/*
 *	Parse a makefile
 */


#include <stdio.h>
#include <ctype.h>
#include "h.h"


struct name	namehead;
struct name    *firstname;

char		str1[LZ];	/* General store  */
char		str2[LZ];


/*
 *	Intern a name.	Return a pointer to the name struct
 */
struct name    *
newname(name)
    char	   *name;
{
    register struct name *rp;
    register struct name *rrp;
    register char  *cp;


    for
	(
	 rp = namehead.n_next, rrp = &namehead;
	 rp;
	 rp = rp->n_next, rrp = rrp->n_next
	)
	if (strcmp(name, rp->n_name) == 0)
	    return rp;

    if ((rp = (struct name *) malloc(sizeof(struct name)))
	== (struct name *) 0)
	fatal("No memory for name");
    rrp->n_next = rp;
    rp->n_next = (struct name *) 0;
    if ((cp = malloc((unsigned) strlen(name) + 1)) == (char *) 0)
	fatal("No memory for name");
    strcpy(cp, name);
    rp->n_name = cp;
    rp->n_line = (struct line *) 0;
    rp->n_time = (time_t) 0;
    rp->n_flag = 0;

    return rp;
}

/*
 *  Delete the last created name.
 */

delname(np)
struct name *np;
{
    if (namehead.n_next == np) {
	namehead.n_next = np->n_next;
	free(np);
    }
}

/*
 *	Add a dependant to the end of the supplied list of dependants.
 *	Return the new head pointer for that list.
 */
struct depend  *
newdep(np, dp)
    struct name    *np;
    struct depend  *dp;
{
    register struct depend *rp;
    register struct depend *rrp;


    if ((rp = (struct depend *) malloc(sizeof(struct depend)))
	== (struct depend *) 0)
	fatal("No memory for dependant");
    rp->d_next = (struct depend *) 0;
    rp->d_name = np;

    if (dp == (struct depend *) 0)
	return rp;

    for (rrp = dp; rrp->d_next; rrp = rrp->d_next);

    rrp->d_next = rp;

    return dp;
}


/*
 *	Add a command to the end of the supplied list of commands.
 *	Return the new head pointer for that list.
 */
struct cmd     *
newcmd(str, cp)
    char	   *str;
    struct cmd	   *cp;
{
    register struct cmd *rp;
    register struct cmd *rrp;
    register char  *rcp;


    if (rcp = rindex(str, '\n'))
	*rcp = '\0';            /* Loose newline  */

    while (isspace(*str))
	str++;

    if (*str == '\0')           /* If nothing left, the exit  */
	return (struct cmd *) 0;

    if ((rp = (struct cmd *) malloc(sizeof(struct cmd)))
	== (struct cmd *) 0)
	fatal("No memory for command");
    rp->c_next = (struct cmd *) 0;
    if ((rcp = malloc((unsigned) strlen(str) + 1)) == (char *) 0)
	fatal("No memory for command");
    strcpy(rcp, str);
    rp->c_cmd = rcp;

    if (cp == (struct cmd *) 0)
	return rp;

    for (rrp = cp; rrp->c_next; rrp = rrp->c_next);

    rrp->c_next = rp;

    return cp;
}


/*
 *	Add a new 'line' of stuff to a target.  This check to see
 *	if commands already exist for the target.  If flag is set,
 *	the line is a double colon target.
 *
 *	Kludges:
 *	i)  If the new name begins with a '.', and there are no dependents,
 *	    then the target must cease to be a target.	This is for .SUFFIXES.
 *	ii) If the new name begins with a '.', with no dependents and has
 *	    commands, then replace the current commands.  This is for
 *	    redefining commands for a default rule.
 *	Neither of these free the space used by dependents or commands,
 *	since they could be used by another target.
 */
void
newline(np, dp, cp, flag)
    struct name    *np;
    struct depend  *dp;
    struct cmd	   *cp;
    bool	    flag;
{
    bool	    hascmds = FALSE;	/* Target has commands	*/
    register struct line *rp;
    register struct line *rrp;


    /* Handle the .SUFFIXES case */
    if (np->n_name[0] == '.' && !dp && !cp) {
	for (rp = np->n_line; rp; rp = rrp) {
	    rrp = rp->l_next;
	    free((char *) rp);
	}
	np->n_line = (struct line *) 0;
	np->n_flag &= ~N_TARG;
	return;
    }
    /* This loop must happen since rrp is used later. */
    for
	(
	 rp = np->n_line, rrp = (struct line *) 0;
	 rp;
	 rrp = rp, rp = rp->l_next
	)
	if (rp->l_cmd)
	    hascmds = TRUE;

    if (hascmds && cp && !(np->n_flag & N_DOUBLE))
	/* Handle the implicit rules redefinition case */
	if (np->n_name[0] == '.' && dp == (struct depend *) 0) {
	    np->n_line->l_cmd = cp;
	    return;
	} else
	    error("Commands defined twice for target %s", np->n_name);
    if (np->n_flag & N_TARG)
	if (!(np->n_flag & N_DOUBLE) != !flag)  /* like xor */
	    error("Inconsistent rules for target %s", np->n_name);

    if ((rp = (struct line *) malloc(sizeof(struct line)))
	== (struct line *) 0)
	fatal("No memory for line");
    rp->l_next = (struct line *) 0;
    rp->l_dep = dp;
    rp->l_cmd = cp;

    if (rrp)
	rrp->l_next = rp;
    else
	np->n_line = rp;

    np->n_flag |= N_TARG;
    if (flag)
	np->n_flag |= N_DOUBLE;
}


/*
 *	Parse input from the makefile, and construct a tree structure
 *	of it.
 */
void
input(fd)
    FILE	   *fd;
{
    char	   *p;		/* General  */
    char	   *q;
    struct name    *np;
    struct depend  *dp;
    struct cmd	   *cp;
    bool	    dbl, getline();


    if (getline(str1, fd))      /* Read the first line  */
	return;

    for (;;) {
	if (str1[0] == '\t' || str1[0] == ' ')  /* Rules without targets  */
		error("Rules not allowed here");

	p = str1;

	while (isspace(*p))     /* Find first target  */
	    p++;

	while (((q = index(p, '=')) != (char *) 0) &&
	       (p != q) && (q[-1] == '\\')) {   /* Find value */
	    register char  *a;

	    a = q - 1;		/* Del \ chr; move rest back  */
	    p = q;
	    while (*a++ = *q++);
	}

	if (q != (char *) 0) {
	    register char  *a;

	    *q++ = '\0';        /* Separate name and val  */
	    while (isspace(*q))
		q++;
	    if (p = rindex(q, '\n'))
		*p = '\0';

	    p = str1;
	    if ((a = gettok(&p)) == (char *) 0)
		error("No macro name");

	    setmacro(a, q);

	    if (getline(str1, fd))
		return;
	    continue;
	}
	expand(str1);
	p = str1;

#if 0
	while (((q = index(p, ':')) != (char *) 0) &&
	       (p != q) && (q[-1] == '\\')) {   /* Find dependents  */
	    register char  *a;

	    a = q - 1;		/* Del \ chr; move rest back  */
	    p = q;
	    while (*a++ = *q++);
	}
#else
	q = index(p, ':');
#endif

	if (q == (char *) 0)
	    error("No targets provided");

	/* Added by OIS to allow at most one : in target names */

	{
	    register char *a = q;

	    while (*++a) {
		if (*a == ' ' || *a == '\t') {
		    /* Found no more colons. Must have been first one. */
		    break;
		}
		if (*a == ':') {
		    /* Found second colon. This must be the separator. */
		    if (a != q+1)   /* No double colon */
			q = a;
		    break;
		}
	    }
	}

	*q++ = '\0';            /* Separate targets and dependents  */

	if (*q == ':') {        /* Double colon */
	    dbl = 1;
	    q++;
	} else
	    dbl = 0;

	for (dp = (struct depend *) 0; ((p = gettok(&q)) != (char *) 0);)
	    /* get list of dep's */
	{
	    np = newname(p);    /* Intern name  */
	    dp = newdep(np, dp);/* Add to dep list */
	}

	*((q = str1) + strlen(str1) + 1) = '\0';
	/* Need two nulls for gettok (Remember separation)  */

	cp = (struct cmd *) 0;
	if (getline(str2, fd) == FALSE) {       /* Get commands  */
	    while (str2[0] == '\t' || str2[0] == ' ') { /*OIS*0.80*/
		cp = newcmd(&str2[0], cp);
		if (getline(str2, fd))
		    break;
	    }
	}
	while ((p = gettok(&q)) != (char *) 0) {        /* Get list of targ's */
	    np = newname(p);    /* Intern name  */
	    newline(np, dp, cp, dbl);
	    if (!firstname && p[0] != '.')
		firstname = np;
	}

	if (feof(fd))           /* EOF?  */
	    return;

	strcpy(str1, str2);
    }
}
