text: /*
   * make -- maintain program groups
   * td 80.09.17
   * things done:
   *	20-Oct-82	Made nextc properly translate "\\\n[ 	]*" to ' '.
   *	15-Jan-85	Made portable enough for z-8000, readable enough for
   *			human beings.
   *	06-Nov-85	Added free(t) to make() to free used space.
   *	07-Nov-85	Modified docmd() to call varexp() only if 'cmd'
   *			actually contains macros, for efficiency.
   *	24-Feb-86	Minor fixes by rec to many things.  Deferred
   *			macro expansion in actions until needed, deferred
   *			getmdate() until needed, added canonicalization in
   *			archive searches, allowed ${NAME} in actions for
   *			shell to expand, put macro definitions in malloc,
   *			entered environ into macros.
   *	17-Oct-86	Very minor MS-DOS changes by steve: add _cmdname[],
   *			conditionalize archive code as #if COHERENT || GEMDOS.
   *	 8-Dec-86	Rec makes inpath() search current directory first,
   *			and allows : in dependency list under MSDOS && GEMDOS.
   *	 8-Feb-91	steve: fix comment handling, allow "\#", allow ${VAR}.
   *			Add docmd0() to make $< and $* work in Makefiles.
   *	12-Feb-91	steve: add $SRCPATH source path searching.
   *	 1-Nov-91	steve: fix bug in nextc() to handle "\n\t\n" correctly
   *      29-Sep-92	michael: fix problem with defining a rule that also
   *				exists in the ACTIONFILE.	
   *      08-Oct-92	michael: fix problem with making targets with no
   *				specified actions (empty productions).
   */
  
  #include	"make.h"
  
  char usage[] = "Usage: make [-deinpqrst] [-f file] [macro=value] [target]";
  char nospace[] = "out of space";
  char badmac[] = "bad macro name";
  char incomp[] = "incomplete line at end of file";
  
  /* Command line flags. */
  int iflag;			/* ignore command errors */
  int sflag;			/* don't print command lines */
  int rflag;			/* don't read built-in rules */
  int nflag;			/* don't execute commands */
  int tflag;			/* touch files rather than run commands */
  int qflag;			/* zero exit status if file up to date */
  int pflag;			/* print macro defns, target descriptions */
  int dflag;			/* debug mode -- verbose printout */
  int eflag;			/* make environ macros protected */
  
  /* Globals. */
  unsigned char	backup[NBACKUP];
  int		defining;	/* nonzero => do not expand macros */
  int		instring;	/* Are we in the middle of a string? */
  SYM		*deflt;
  char		*deftarget;
  FILE		*fd;
  int		lastc;
  int		lineno;
  MACRO		*macro;
  char		macroname[NMACRONAME+1];
  char		*mvarval[4];		/* list of macro variable values */
  int		nbackup;
  time_t		now;
  char		*srcpath;
  struct stat	statbuf;
  SYM		*suffixes;
  SYM		*sym;
  char		tokbuf[NTOKBUF];
  char		*token;
  int		toklen;
  char		*tokp;
  int 		inactionfile = 0;
  
  /* cry and die */
  /* VARARGS */
  void die(const char *s, ...)
  {
  	va_list args;
  	va_start(args, s);
  	fflush(stdout);
  	verrx(ERROR, "make: ", args);
  	exit(ERROR);
  }
  
  /* print lineno, cry and die */
  /* VARARGS */
  void doerr(const char *s, ...)
  {
  	va_list args;
  	va_start(args, s);
  	fprintf(stderr, "make: %d: ", lineno);
  	vfprintf(stderr, s, args);
  	exit(ERROR);
  }
  
  /* Malloc nbytes and abort on failure */
  char *mmalloc(size_t n)
  {
  	char *p;
  	if (p = malloc(n))
  		return p;
  	doerr(nospace);
  }
  
  /* Whine about usage and then quit */
  void Usage(void)
  {
  	fprintf(stderr, "%s\n", usage);
  	exit(1);
  }
  
  /* read characters from backup (where macros have been expanded) or from
   * whatever the open file of the moment is. keep track of line #s.
   */
  int readc(void)
  {
  	if (nbackup!=0)
  		return(backup[--nbackup]);
  	if (lastc=='\n')
  		lineno++;
  	lastc=getc(fd);
  	return(lastc);
  }
  
  /* put c into backup[] */
  void putback(int c)
  {
  	if (c==EOF)
  		return;
  	if (nbackup == NBACKUP)
  		doerr("macro definition too long");
  	backup[nbackup++]=c;
  }
  
  /* put s into backup */
  void unreads(register char *s)
  {
  	register char *t;
  
  	t = &s[strlen(s)];
  	while (t > s)
  		putback(*--t);
  }
  
  /* return a pointer to the macro definition assigned to macro name s.
   * return NULL if the macro has not been defined.
   */
  char *mexists(register char *s)
  {
  	register MACRO *i;
  
  	for (i = macro; i != NULL; i=i->next)
  		if (Streq(s, i->name))
  			return (i->value);
  
  	return (NULL);
  }
  
  /* install macro with name name and value value in macro[]. Overwrite an
   * existing value if it is not protected.
   */
  void define(char *name, char *value, int protected)
  {
  	register MACRO *i;
  
  	if (dflag)
  		printf("define %s = %s\n", name, value);
  	for (i = macro; i != NULL; i=i->next)
  		if (Streq(name, i->name)) {
  			if (!i->protected) {
  				free(i->value);
  				i->value = value;
  				i->protected = protected;
  			} else if (dflag)
  				printf("... definition suppressed\n");
  			return;
  		}
  	i = (MACRO *)mmalloc(sizeof(*i));
  	i->name = name;
  	i->value = value;
  	i->protected = protected;
  	i->next = macro;
  	macro = i;
  }
  
  /* Accept single letter user defined macros */
  int ismacro(int c)
  {
  	return ((c>='0'&&c<='9')
  		|| (c>='a'&&c<='z')
  		|| (c>='A'&&c<='Z'));
  }
  
  /*
   * Return the next character from the input file.
   * Eat comments.
   * Return EOS for newline not followed by an action.
   * Return '\n' for newline followed by an action.
   * If not in a macro definition or action specification,
   * then expand macro in backup or complain about the name.
   */
  int nextc(void)
  {
  	register char *s;
  	register int c, endc;
  
  Again:
  	if ((c = readc()) == '\\') {
  		c = readc();
  		if (c == '\n') {		/* escaped newline */
  			while ((c=readc())==' ' || c=='\t')
  				;		/* eat following whitespace */
  			putback(c);
  			return(' ');
  		} else if (c == '#')
  			return c;		/* "\#" means literal '#' */
  		putback(c);
  		return '\\';
  	}
  	if ((c=='#') && !instring) {
  		do
  			c = readc();
  		while (c != '\n' && c != EOF);
  	}
  	if ((c=='"') || (c=='\''))
  	{
  		instring = !instring;
  		return c;
  	}
  	if (c == '\n') {
  		instring = 0;
  Again2:
  		if ((c = readc()) != ' ' && c != '\t') {
  			putback(c);
  			if (c == '#')
  				goto Again;	/* "\n# comment" */
  			return EOS;		/* no action follows */
  		}
  		do
  			c = readc();
  		while (c == ' ' || c == '\t');	/* skip whitespace */
  		if (c == '\n')
  			goto Again2;		/* "\n\t\n" */
  		putback(c);
  		if (c == '#')
  			goto Again;		/* "\n\t# comment" */
  		return '\n';			/* action follows */
  	}
  	if (!defining && c=='$'){
  		c=readc();
  		if (c == '(' || c == '{') {
  			endc = (c == '(') ? ')' : '}';
  			s = macroname;
  			while (' ' < (c = readc()) && c < 0177 && c != endc)
  				if (s != &macroname[NMACRONAME])
  					*s++=c;
  			if (c != endc)
  				doerr(badmac);
  			*s++ = '\0';
  		} else if (ismacro(c)) {
  			macroname[0]=c;
  			macroname[1]='\0';
  		} else
  			doerr(badmac);
  		if ((s=mexists(macroname))!=NULL)
  			unreads(s);
  		goto Again;
  	}
  
  	return(c);
  }
  
  /* Get a block of l0+l1 bytes copy s0 and s1 into it, and return a pointer to
   * the beginning of the block.
   */
  char *extend(char *s0, int l0, char *s1, int l1)
  {
  	register char *t;
  
   	if (s0 == NULL)
   		t = mmalloc(l1);
   	else {
   		if ((t = realloc(s0, l0 + l1)) == NULL)
  			doerr(nospace);
  	}
  	strncpy(t+l0, s1, l1);
  	return(t);
  }
  
  /* Return 1 if c is EOS, EOF, or one of the characters in s */
  int delim(char c, char *s)
  {
  	return (c == EOS || c == EOF || index(s, c) != NULL);
  }
  
  /* Prepare to copy a new token string into the token buffer; if the old value
   * in token wasn't saved, tough matzohs.
   */
  void starttoken(void)
  {
  	token=NULL;
  	tokp=tokbuf;
  	toklen=0;
  }
  
  /* Put c in the token buffer; if the buffer is full, copy its contents into
   * token and start agin at the beginning of the buffer.
   */
  void addtoken(int c)
  {
  	if (tokp==&tokbuf[NTOKBUF]){
  		token=extend(token, toklen-NTOKBUF, tokbuf, NTOKBUF);
  		tokp=tokbuf;
  	}
  	*tokp++=c;
  	toklen++;
  }
  
  /* mark the end of the token in the buffer and save it in token. */
  void endtoken(void)
  {
  	addtoken('\0');
  	token=extend(token, toklen-(tokp-tokbuf), tokbuf, tokp-tokbuf);
  }
  
  /* Install value at the end of the token list which begins with next; return
   * a pointer to the beginning of the list, which is the one just installed if
   * next was NULL.
   */
  TOKEN *listtoken(char *value, TOKEN *next)
  {
  	register TOKEN *p;
  	register TOKEN *t;
  
  	t=(TOKEN *)mmalloc(sizeof *t);	/*Necessaire ou le contraire?*/
  	t->value=value;
  	t->next=NULL;
  	if (next==NULL)
  		return(t);
  	for(p=next;p->next!=NULL;p=p->next);
  	p->next=t;
  	return(next);
  }
  
  /* Free the overhead of a token list */
  TOKEN *freetoken(register TOKEN *t)
  {
  	register TOKEN *tp;
  	while (t != NULL) {
  		tp = t->next;
  		free(t);
  		t = tp;
  	}
  	return t;
  }
  
  /* Read macros, dependencies, and actions from the file with name file, or
   * from whatever file is already open. The first string of tokens is saved
   * in a list pointed to by tp; if it was a macro, the definition goes in
   * token, and we install it in macro[]; if tp points to a string of targets,
   * its depedencies go in a list pointed to by dp, and the action to recreate
   * it in token, and the whole shmear is installed.
   */
  void input(const char *file)
  {
  	TOKEN *tp = NULL, *dp = NULL;
  	register int c;
  	char *action;
  	int twocolons;
  
  	if (file!=NULL && (fd=fopen(file, "r"))==NULL)
  		die("cannot open %s", file);
  	lineno=1;
  	lastc=EOF;
  	for(;;){
  		c=nextc();
  		for(;;){
  			while(c==' ' || c=='\t')
  				c=nextc();
  			if (delim(c, "=:;\n"))
  				break;
  			starttoken();
  			while(!delim(c, " \t\n=:;")){
  				addtoken(c);
  				c=nextc();
  			}
  			endtoken();
  			tp=listtoken(token, tp);
  		}
  		switch(c){
  		case EOF:
  			if (tp!=NULL)
  				doerr(incomp);
  			fclose(fd);
  			return;
  		case EOS:
  			if (tp==NULL)
  				break;
  		case '\n':
  			doerr("newline after target or macroname");
  		case ';':
  			doerr("; after target or macroname");
  		case '=':
  			if (tp==NULL || tp->next!=NULL)
  				doerr("= without macro name or in token list");
  			defining++;
  			while((c=nextc())==' ' || c=='\t');
  			starttoken();
  			while(c!=EOS && c!=EOF) {
  				addtoken(c);
  				c=nextc();
  			}
  			endtoken();
  			define(tp->value, token, 0);
  			defining=0;
  			break;
  		case ':':
  			if (tp==NULL)
  				doerr(": without preceding target");
  			c=nextc();
  			if (c==':'){
  				twocolons=1;
  				c=nextc();
  			} else
  				twocolons=0;
  			for(;;){
  				while(c==' ' || c=='\t')
  					c=nextc();
  				if (delim(c, "=:;\n"))
  					break;
  				starttoken();
  				while(!delim(c, TDELIM)){
  					addtoken(c);
  					c=nextc();
  				}
  				endtoken();
  				dp=listtoken(token, dp);
  			}
  			switch(c){
  			case ':':
  				doerr("::: or : in or after dependency list");
  			case '=':
  				doerr("= in or after dependency");
  			case EOF:
  				doerr(incomp);
  			case ';':
  			case '\n':
  				++defining;
  				starttoken();
  				while((c=nextc())!=EOS && c!=EOF)
  					addtoken(c);
  				endtoken();
  				defining = 0;
  				action=token;
  				break;
  			case EOS:
  				action=NULL;
  			}
  			install(tp, dp, action, twocolons);
  		}
  		tp = freetoken(tp);
  		dp = freetoken(dp);
  		dp = NULL;
  	}
  }
  
  char *path(const char *p1, const char *p2, int mode)
  {
  	static char *pv = NULL;
  	int l1 = strlen(p1);
  	if (pv)
  		free(pv);
  	pv = malloc(strlen(p1) + strlen(p2) + 2);
  	strcpy(pv, p1);
  	pv[l1] = '/';
  	strcpy(pv + l1 + 1, p2);
  	if (access(pv, mode) == 0)
  		return pv;
  	free(pv);
  	return NULL;
  }
  
  /* Input with library lookup */
  void inlib(const char *file)
  {
  	const char *p, *cp;
  	if ((p = getenv("LIBPATH")) == NULL)
  		p = "/lib:/usr/lib";
  	cp = path(p, file, R_OK);
  	input(cp ? cp : file);
  }
  
  /* Input first file in list which is found via SRCPATH. */
  /* Look in current directory first */
  void inpath(char *file, ...)
  {
  	register char **vp, *cp;
  	va_list ap;
  	va_start(ap, file);
  
  	cp = NULL;
  	for (vp = va_arg(ap, char **); *vp != NULL;)
  		if (access(*vp, R_OK) >= 0) {
  			cp = *vp;
  			break;
  		}
  	va_end(ap);
  	if ( ! cp) {
  		va_start(ap, file);
  		for (vp = va_arg(ap, char **); *vp != NULL; vp += 1)
  			if ((cp = path(srcpath, *vp, R_OK)) != NULL)
  				break;
  		va_end(ap);
  	}
  	input(cp ? cp : file);
  }
  
  /* Return the last modified date of file with name name. If it's an archive,
   * open it up and read the insertion date of the pertinent member.
   */
  time_t getmdate(char *name)
  {
  #if	_I386
  	char	*subname;
  	char	*lwa;
  	int	fd, x;
  	char	magic[SARMAG];
  	int	size;
  
  	time_t	result;
  	struct ar_hdr	hdrbuf;
  #endif
  
  	if (stat(name, &statbuf) == 0)
  		return(statbuf.st_mtime);
  
  
  #if	_I386
  	subname = index(name, '(');
  	if (subname == NULL)
  		return (0);
  	lwa = &name[strlen(name) - 1];
  	if (*lwa != ')')
  		return (0);
  	*subname = NUL;
  	fd = open(name, READ);
  	*subname++ = '(';
  	if (fd == EOF)
  		return (0);
  	if (read(fd, magic, SARMAG) != SARMAG)
  	{
  		close(fd);
  		return (0);
  	}
  	if (!strcmp(magic, ARMAG)) {
  		close(fd);
  		return (0);
  	}
  	*lwa = NUL;
  	result = 0;
  	while (read(fd, &hdrbuf, sizeof hdrbuf) == sizeof hdrbuf) {
  		if ((strncmp(hdrbuf.ar_name, subname, x = strlen(subname)) == 0)
  		    && (hdrbuf.ar_name[x] == '/'))
  		{
  			result = atoi(hdrbuf.ar_date);
  			break;
  		}
  		size = atoi(hdrbuf.ar_size);
  		lseek(fd, size, SEEK_CUR);
  	}
  	*lwa = ')';
  
  	return (result);
  #else
  	return 0;
  #endif
  }
  
  
  /* Does file name exist? */
  int fexists(char *name)
  {
  #if 0
  	if (dflag)
  		printf("fexists(%s) = %d getmdate(name) = %d\n", name,
  		getmdate(name) != 0, getmdate(name));
  #endif
  	return getmdate(name) != 0;
  }
  
  /*
   * Find name on srcpath.
   * Return 'name' unchanged if file exists as 'name', 'name' is absolute,
   * or 'name' not found on sourcepath.
   * If successful, return pointer to allocated copy.
   */
  char *fpath(char *name)
  {
  	register char *s;
  
  	if (fexists(name)
  	 || *name == '/'
  	 || srcpath == NULL
  	 || (s = path(srcpath, name, R_OK)) == NULL)
  		return name;
  	starttoken();
  	while (*s)
  		addtoken(*s++);
  	endtoken();
  	return token;
  }
  
  /* Return a pointer to the symbol table entry with name "name", NULL if it's
   * not there.
   */
  SYM *sexists(char *name)
  {
  	register SYM *sp;
  
  	for(sp=sym;sp!=NULL;sp=sp->next)
  		if (Streq(name, sp->name))
  			return(sp);
  	return(NULL);
  }
  
  /*
   * Return a pointer to the member of deplist which has name as the last
   * part of it's pathname, otherwise return NULL.
   */
  SYM *dexists(char *name, DEP *dp)
  {
  	register char *p;
  	while (dp != NULL) {
  		if ((p = rindex(dp->symbol->name, '/')) && Streq(name, p+1))
  			return dp->symbol;
  		else
  			dp = dp->next;
  	}
  	return NULL;
  }
  
  /* Look for symbol with name "name" in the symbol table; install it if it's
   * not there; initialize the action and dependency lists to NULL, the type to
   * unknown, zero the modification date, and return a pointer to the entry.
   */
  SYM *lookup(char *name)
  {
  	register SYM *sp;
  
  	if ((sp=sexists(name))!=NULL)
  		return(sp);
  	sp = (SYM *)mmalloc(sizeof (*sp));	/*necessary?*/
  	sp->name=name;
  	sp->filename=fpath(name);
  	sp->action=NULL;
  	sp->deplist=NULL;
  	sp->type=T_UNKNOWN;
  	sp->moddate=0;
  	sp->next=sym;
  	sym=sp;
  	return(sp);
  }
  
  /* Install a dependency with symbol having name "name", action "action" in
   * the end of the dependency list pointed to by next. If s has already
   * been noted as a file in the dependency list, install action. Return a
   * pointer to the beginning of the dependency list.
   */
  DEP *adddep(char *name, char *action, DEP *next)
  {
  	register DEP *v;
  	register SYM *s;
  	DEP *dp;
  
  	s=lookup(name);
  	for(v=next;v!=NULL;v=v->next)
  		if (s==v->symbol){
  			if (action != NULL) {
  				if (v->action!=NULL)
  					doerr("multiple detailed actions for %s",
  						s->name);
  				v->action=action;
  			}
  			return(next);
  		}
  	v = (DEP *)malloc(sizeof (*v));	/*necessary?*/
  	v->symbol=s;
  	v->action=action;
  	v->next=NULL;
  	if (next==NULL)
  		return(v);
  	for(dp=next;dp->next!=NULL;dp=dp->next);
  	dp->next=v;
  	return(next);
  }
  
  /* Do everything for a dependency with left-hand side cons, r.h.s. ante,
   * action "action", and one or two colons. If cons is the first target in the
   * file, it becomes the default target. Mark each target in cons as detailed
   * if twocolons, undetailed if not, and install action in the symbol table
   * action slot for cons in the latter case. Call adddep() to actually create
   * the dependency list.
   */
  
  void install(TOKEN *cons, TOKEN *ante, char *action, int twocolons)
  {
  	SYM *cp;
  	TOKEN *ap;
  
  	if (deftarget==NULL && cons->value[0]!='.')
  		deftarget=cons->value;
  	if (dflag){
  		printf("Ante:");
  		ap=ante;
  		while(ap!=NULL){
  			printf(" %s", ap->value);
  			ap=ap->next;
  		}
  		printf("\nCons:");
  		ap=cons;
  		while(ap!=NULL){
  			printf(" %s", ap->value);
  			ap=ap->next;
  		}
  		printf("\n");
  		if (action!=NULL)
  			printf("Action: '%s'\n", action);
  		if (twocolons)
  			printf("two colons\n");
  	}
  	for (; cons != NULL; cons = cons->next) {
  		cp=lookup(cons->value);
  		if (cp==suffixes && ante==NULL)
  			cp->deplist=NULL;
  		else{
  			if (twocolons){
  				if (cp->type==T_UNKNOWN)
  					cp->type=T_DETAIL;
  				else if (cp->type!=T_DETAIL)
  					doerr("'::' not allowed for %s",
  						cp->name);
  			} else {
  				if (cp->type==T_UNKNOWN)
  					cp->type=T_NODETAIL;
  				else if (cp->type!=T_NODETAIL)
  					doerr("must use '::' for %s", cp->name);
  				if (action != NULL) {
  					if (cp->action != NULL)
  					{
  						if (!inactionfile)
  							doerr("multiple action"
  							"s for %s", cp->name);
  					}
  					else
  						cp->action = action;
  				}
  			}
  			for(ap=ante;ap!=NULL;ap=ap->next)
  				cp->deplist=adddep(ap->value,
  					twocolons?action:NULL, cp->deplist);
  		}
  	}
  }
  
  /* Make s; first, make everything s depends on; if the target has detailed
   * actions, execute any implicit actions associated with it, then execute
   * the actions associated with the dependencies which are newer than s.
   * Otherwise, put the dependencies that are newer than s in token ($?),
   * make s if it doesn't exist, and call docmd.
   */
  void make(SYM *s)
  {
  	register DEP *dep;
  	register char *t, *name;
  	int update;
  	int type;
  
  	if (s->type==T_DONE)
  		return;
  	name = s->filename;
  	if (dflag) {
  		if (s->name == name)
  			printf("Making %s\n", name);
  		else
  			printf("Making %s (file %s)\n", s->name, name);
  	}
  	type=s->type;
  	s->type=T_DONE;
  	s->moddate=getmdate(name);
  	for(dep=s->deplist;dep!=NULL;dep=dep->next)		
  		make(dep->symbol);
  	if (type==T_DETAIL){
  		implicit(s, "", 0);
  		for(dep=s->deplist;dep!=NULL;dep=dep->next)
  			if (dep->symbol->moddate>s->moddate)
  				docmd0(s, dep->action, name, dep->symbol->filename);
  	} else {
  		update=0;
  		starttoken();
  		for(dep=s->deplist;dep!=NULL;dep=dep->next){
  			if (dflag)
  				printf("%s time=%ld %s time=%ld\n",
  				    dep->symbol->filename, dep->symbol->moddate,
  				    name, s->moddate);
  			if (dep->symbol->moddate>s->moddate){
  				update++;
  				addtoken(' ');
  				for(t=dep->symbol->filename;*t;t++)
  					addtoken(*t);
  			}
  		}
  		endtoken();
  		t = token;
  		if (!update && !fexists(name)) {
  			update = TRUE;
  			if (dflag)
  				printf("'%s' made due to non-existence\n",
  					name);
  		}
  		if (s->action==NULL)
  			implicit(s, t, update);
  		else if (update)
  			docmd0(s, s->action, name, t);
  		free(t);
  	}
  }
  
  /*
   * Expand substitutes the macros in actions and returns the string.
   */
  void expand(char *str)
  {
  	register int c;
  	register char *p;
  	int endc;
  
  	while (c = *str++) {
  		if (c == '$') {
  			c = *str++;
  			switch (c) {
  			case '\0':	doerr(badmac);
  			case '$':	addtoken(c);	continue;
  			case '@':	p = mvarval[0]; break;
  			case '?':	p = mvarval[1]; break;
  			case '<':	p = mvarval[2]; break;
  			case '*':	p = mvarval[3]; break;
  			case '{':
  			case '(':
  				endc = (c == '(') ? ')' : '}';
  				c = '(';
  				p = str;
  				do c = *str++; while (c != 0 && c != endc);
  				if (c == 0)
  					doerr(badmac);
  				*--str = 0;
  				p = mexists(p);
  				*str++ = endc;
  				break;
  			default:
  				if ( ! ismacro(c))
  					doerr(badmac);
  				c = *str;
  				*str = 0;
  				p = mexists(str-1);
  				*str = c;
  				break;
  			}
  			if (p != NULL)
  				expand(p);
  		} else
  			addtoken(c);
  	}
  }
  
  /* Like docmd(), except builds its own dependency list and prefix args. */
  void docmd0(SYM *s, char *cmd, char *at, char *ques)
  {
  	register char *cp;
  	register DEP *dep;
  	char *less, *prefix;
  
  	/* Build dependency list. */
  	starttoken();
  	for (dep = s->deplist; dep != NULL; dep = dep->next) {
  		addtoken(' ');
  		for (cp = dep->symbol->filename; *cp; cp++)
  			addtoken(*cp);
  	}
  	endtoken();
  	less = token;
  
  	/* Build prefix. */
  	starttoken();
  	for (cp = s->name; *cp; cp++)
  		addtoken(*cp);
  	endtoken();
  	prefix = token;
  
  	if ((cp = rindex(prefix, '.')) != NULL)
  		*cp = '\0';
  	docmd(s, cmd, at, ques, less, prefix);
  	free(less);
  	free(prefix);
  }
  
  /* Mark s as modified; if tflag, touch s, otherwise execute the necessary
   * commands.
   */
  void docmd(SYM *s, char *cmd, char *at, char *ques, char *less, char *star)
  {
  	if (dflag)
  		printf("ex '%s'\n\t$@='%s'\n\t$?='%s'\n\t$<='%s'\n\t$*='%s'\n",
  			cmd, at, ques, less, star);
  	if (qflag)
  		exit(NOTUTD);
  	s->moddate = now;
  	if (tflag)
  		cmd = "touch $@";
  	if (cmd == NULL)
  		return;
  	mvarval[0] = at;
  	mvarval[1] = ques;
  	mvarval[2] = less;
  	mvarval[3] = star;
  	starttoken();
  	expand(cmd);
  	endtoken();
  	doit(token);
  	free(token);
  }
  
  
  /* look for '-' (ignore errors) and '@' (silent) in cmd, then execute it
   * and note the return status.
   */
  void doit(char *cmd)
  {
  	register char *mark;
  	int sflg, iflg, rstat;
  
  	if (nflag) {
  		printf("%s\n", cmd);
  		return;
  	}
  	do {
  		mark = index(cmd, '\n');
  		if (mark != NULL)
  			*mark = NUL;
  		if (*cmd == '-') {
  			++cmd;
  			iflg = TRUE;
  		} else
  			iflg = iflag;
  		if (*cmd == '@') {
  			++cmd;
  			sflg = TRUE;
  		} else
  			sflg = sflag;
  		if (!sflg)
  			printf("%s\n", cmd);
  		fflush(stdout);
  		rstat = system(cmd);
  		if (rstat != 0 && !iflg)
  			if (sflg)
  				die("%s	exited with status %d",
  					cmd, rstat);
  			else
  				die("	exited with status %d", rstat);
  		cmd = mark + 1;
  	} while (mark != NULL && *cmd != NUL);
  }
  
  
  /* Find the implicit rule to generate obj and execute it. Put the name of
   * obj up to '.' in prefix, and look for the rest in the dependency list
   * of .SUFFIXES. Find the file "prefix.foo" upon which obj depends, where
   * foo appears in the dependency list of suffixes after the suffix of obj.
   * Then make obj according to the rule from makeactions. If we can't find
   * any rules, use .DEFAULT, provided we're definite.
   */
  
  void implicit(SYM *obj, char *ques, int definite)
  {
  	register char *s;
  	register DEP *d;
  	char *prefix, *file, *rulename, *suffix;
  	SYM *rule;
  	SYM *subj;
  
  	if (dflag)
  		printf("Implicit %s (%s)\n", obj->name, ques);
  	if ((suffix=rindex(obj->name, '.')) == NULL
  	 || suffix==obj->name) {
  		if (definite)
  			defalt(obj, ques);
  		return;
  	}
  	starttoken();
  	for(s=obj->name; s<suffix; s++)
  		addtoken(*s);
  	endtoken();
  	prefix=token;
  	for(d=suffixes->deplist;d!=NULL;d=d->next)
  		if (Streq(suffix, d->symbol->name))
  			break;
  	if (d==NULL){
  		free(prefix);
  		if (definite)
  			defalt(obj, ques);
  		return;
  	}
  	while((d=d->next)!=NULL){
  		starttoken();
  		for(s=obj->name; s!=suffix; s++)
  			addtoken(*s);
  		for(s=d->symbol->name;*s;s++)
  			addtoken(*s);
  		endtoken();
  		file=token;
  		if ((s = fpath(file)) != file) {
  			free(file);
  			file = s;
  		}
  		subj=NULL;
  		if (fexists(file) || (subj=dexists(file, obj->deplist))){
  			starttoken();
  			for(s=d->symbol->filename;*s!='\0';s++)
  				addtoken(*s);
  			for(s=suffix;*s!='\0';s++)
  				addtoken(*s);
  			endtoken();
  			rulename=token;
  			if ((rule=sexists(rulename))!=NULL){
  				if (subj != NULL || (subj=sexists(file))) {
  					free(file);
  					file=subj->name;
  				} else
  					subj=lookup(file);
  				make(subj);
  				if (definite || subj->moddate>obj->moddate)
  					docmd(obj, rule->action,
  						obj->name, ques, file, prefix);
  				free(prefix);
  				free(rulename);
  				return;
  			}
  			free(rulename);
  		}
  		free(file);
  	}
  	free(prefix);
  	if (definite)
  		defalt(obj, ques);
  }
  
  /*
   * Deflt uses the commands associated to '.DEFAULT' to make the object
   * 'obj'.
   */
  
  void defalt(SYM *obj, char *ques)
  {
  	if (deflt == NULL)
  	{
  		if (obj->deplist == NULL)
  			die("do not know how to make %s", obj->name);
  	}
  	else
  		docmd0(obj, deflt->action, obj->name, ques);
  }
  
  int main(int argc, char *argv[])
  {
  	register char	*s, *value;
  	register char	*namesave;
  	register int c;
  	int	len, numtargets = 0;
  	char 	**dtarget;
  	TOKEN	*fp = NULL;
  	SYM	*sp;
  	DEP	*d;
  	MACRO	*mp;
  	extern char **environ;
  	char	**envp = environ;
  
  
  	if ((dtarget = malloc(argc * sizeof(char *))) == NULL)
  		doerr(nospace);
  
  	time(&now);
  	++argv;
  	--argc;
  
  	while (argc > 0)
  	{
  		if (argv[0][0] == '-')
  		{
  			for (--argc, s = *argv++; *++s != NUL;)
  				switch (*s) {
  				case 'd': dflag++; break;
  				case 'e': eflag++; break;
  				case 'i': iflag++; break;
  				case 'n': nflag++; break;
  				case 'p': pflag++; break;
  				case 'q': qflag++; break;
  				case 'r': rflag++; break;
  				case 's': sflag++; break;
  				case 't': tflag++; break;
  				case 'f':
  					if (--argc < 0)
  						Usage();
  					fp=listtoken(*argv++, fp);
  					break;
  				default:
  					Usage();
  				}
  		}
  		else if ((value = index(*argv, '=')) != NULL)
  		{
  			s = *argv;
  			while (*s != ' ' && *s != '\t' && *s != '=')
  				++s;
  			*s = '\0';
  			define(*argv++, value+1, 1);
  			--argc;
  		}
  		else
  		{
  			dtarget[numtargets++] = *argv++;
  			--argc;
  		}
  	}
  	while (*envp != NULL) {
  		if ((value = index(*envp, '=')) != NULL
  		 && index(value, '$') == NULL) {
  			s = *envp;
  			while ((c=*s) != ' ' && c != '\t' && c != '=')
  				++s;
  
  			len = s - *envp;
  			namesave=mmalloc(len+1);
  			strncpy(namesave, *envp, len);
  			namesave[len] = '\0';
  
  			if (eflag)
  				define(namesave, value+1, 1);
  			else {
  				starttoken();
  				while (*++value) addtoken(*value);
  				endtoken();
  				define(namesave, token, 0);
  			}
  		}
  		++envp;
  	}
  	srcpath = mexists("SRCPATH");
  	suffixes=lookup(".SUFFIXES");
  	if (!rflag)
  		inlib(MACROFILE);
  	deftarget = NULL;
  	if (fp == NULL)
  		inpath("makefile", "Makefile", NULL);
  	else {
  		fd = stdin;
  		do {
  			input( strcmp(fp->value, "-") == 0 ? NULL : fp->value);
  			fp = fp->next;
  		} while (fp != NULL);
  	}
  	if (!rflag)
  	{
  		inactionfile = 1;
  		inlib(ACTIONFILE);
  		inactionfile = 0;
  	}
  
  	if (sexists(".IGNORE") != NULL)
  		++iflag;
  	if (sexists(".SILENT") != NULL)
  		++sflag;
  	deflt = sexists(".DEFAULT");
  	if (pflag){
  		if (macro != NULL) {
  			printf("Macros:\n");
  			for (mp = macro; mp != NULL; mp=mp->next)
  				printf("%s=%s\n", mp->name, mp->value);
  		}
  		printf("Rules:\n");
  		for(sp=sym;sp!=NULL;sp=sp->next){
  			if (sp->type!=T_UNKNOWN){
  				printf("%s:", sp->name);
  				if (sp->type==T_DETAIL)
  					putchar(':');
  				for(d=sp->deplist;d!=NULL;d=d->next)
  					printf(" %s", d->symbol->name);
  				printf("\n");
  				if (sp->action)
  					printf("\t%s\n", sp->action);
  			}
  		}
  	}
  	if (numtargets)
  
  	{
  		int i;
  
  		for (i=0;i<numtargets;i++)
  			make(lookup(dtarget[i]));		
  	} else
  		make(lookup(deftarget));
  
  	exit(ALLOK);
  }
  
  
  /* end of make.c */
  