/*
 *     Parse a makefile
 */


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


struct name			namehead;			/* head of name list */
struct name			*firstname;

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


/* create a symbol table for a name, if one does not already exist */

/*
 *     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;
	extern TimeStamp time_zero;						/* a null timestamp */

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

		/* else make a new name, and link it in */

	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(strlen(name)+1)) == (char *)0)
		fatal("No memory for name");
	strcpy(cp, name);

	/* initialize name structure */

	rp->n_name = cp;
	rp->n_line = NULL;
	rp->n_time = time_zero;
	rp->n_flag = 0;

	/* return pointer to new name */

	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;

	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(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			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(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()
{
	char				*p;		/*  General  */
	char 				*q;
	struct name			*np;
	struct depend		*dp;
	struct cmd			*cp;
	bool				dbl;

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

	for(;;)
	{	if (*str1 == '\t')      /*  Rules without targets  */
			error("Command found with no target/dependancy line");

		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)) 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) == FALSE)		/*  Get commands  */
		{
			while (*str2 == '\t')
			{
				cp = newcmd(&str2[0], cp);
				if (getline(str2))
					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 (nestlvl < 0)			/*  EOF?  */
			return;

		strcpy(str1, str2);
	}
}
