/*
 *	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;
}


/*
 *	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 (;;) {
#ifdef os9
	if (*str1 == ' ')	/* Rules without targets  */
# else
	    if (*str1 == '\t')	/* Rules without targets  */
# endif
		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;

	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++);
	}

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

	*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  */
#ifdef os9
	    while (*str2 == ' ')
#else
	    while (*str2 == '\t')
#endif
	    {
		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);
    }
}
