static char *RcsId = 
    "$Header: /src/bin/make/m/doname.c,v 4.11 84/04/24 21:10:33 crl Exp $";

/* static char *sccsid = "@(#)doname.c	4.1 (Berkeley) 81/02/28"; */

/*
 * $Log:	/src/bin/make/m/doname.c,v $
 * Revision 4.11  84/04/24  21:10:33  crl
 * Added some casts for portability and have error mesgs print the prog name
 * 
 * Revision 4.10  83/12/11  23:43:08  crl
 * Modified to pass through lint better.
 * 
 * Revision 4.9  83/10/31  01:10:13  crl
 * Defer setting variables $* and $< until after possible checkouts,
 * 	since co() sets $<
 * Now uses srchRCS() to search RCS directories.
 * 
 * Revision 4.8  83/10/22  19:08:21  crl
 * Do co()'s in doname() rather than docom().  This allows co() to use
 * 	docom().
 * It is now an error to have both a rule and an RCS file.
 * 
 * Revision 4.7.1.2  83/10/17  21:30:21  crl
 * Fixed bug where targets with no shell lines would not get automatically
 * 	checked out.
 * 
 * Revision 4.7.1.1  83/10/10  21:09:28  crl
 * Fixed bug where make would try RCS/o/foo.o when searching for implicit
 * 	dependents of o/foo.o.  For now, do searching of RCS for implicit
 * 	dependents only in the current directory.
 * 
 * Revision 4.7  83/07/25  11:06:41  crl
 * Fixed possible bug in doname() with isrcs being assigned to a time_t.
 * 
 * Revision 4.6  83/07/24  16:10:52  crl
 * Fixed bug where files wouldn't be marked for check-out once they had
 * 	been done once.
 * 
 * Revision 4.5  83/07/24  16:04:03  crl
 * If RCSdir is non-null, call srchdir() with it prepended so that implicit
 * 	rules can find entries without specific mention in the makefile.
 * 
 * Revision 4.4  83/07/23  16:00:50  crl
 * Fixed bug where doname() called getrcs() without checking coflag.
 * 
 * Revision 4.3  83/07/23  14:42:48  crl
 * Defer checkout of RCS files until needed.  In doname(), cochain is
 * 	used to keep a list of files to checkout for the file being made.
 * 	ochain is the addr of the previous cochain, so that if the current
 * 	file doesn't exist, it will append itself to the previous one.
 * Docom() gets another arg--a chain of files to co.  It calls co() to do
 * 	this.
 * TIMETYPE replaced with time_t.
 * 
 * Revision 4.2  83/07/22  14:14:10  crl
 * Made to work transparently with RCS.
 * 
 * Revision 4.1  83/03/01  00:42:18  crl
 * Vanilla 4.1 version
 */

#include "defs.h"

/*  BASIC PROCEDURE.  RECURSIVE.  */

/*
 * p->done = 0   don't know what to do yet
 * p->done = 1   file in process of being updated
 * p->done = 2   file already exists in current state
 * p->done = 3   file make failed
 */
int
doname(p, reclevel, tval, ochain)
register struct nameblock *p;
int reclevel;
time_t *tval;
struct chain **ochain;
{
	int errstat;
	int okdel1;
	int didwork;
	int setimpl;			/* set implicit vars */
	time_t td, td1, tdep, ptime, ptime1;
	register struct depblock *q;
	struct depblock *qtemp, *suffp, *suffp1;
	struct nameblock *p1, *p2;
	struct shblock *implcom, *explcom;
	register struct lineblock *lp;
	struct lineblock *lp1, *lp2;
	char sourcename[MAXPATH], prefix[MAXPATH], temp[MAXPATH];
	char concsuff[20];
	char *pnamep, *p1namep;
	struct chain *qchain;
	struct chain *cochain;

	if(p == 0) {
		*tval = 0;
		return(0);
	}

	if(dbgflag) {
		printf("doname(%s,%d)\n", p->namep, reclevel);
		fflush(stdout);
	}

	if(p->done != 0) {
#ifdef RCS
		/*
		 * if we want to check-out RCS files, and we have previously
		 * determined that we can, then append it to the previous 
		 * level's cochain.
		 */
		if (p->RCSnamep)
			*ochain = appendq(*ochain, p->namep);
#endif
		*tval = p->modtime;
		return(p->done == 3);
	}

	cochain = NULL;
	errstat = 0;
	tdep = 0;
	implcom = 0;
	explcom = 0;
	ptime = exists(p, ochain);
	ptime1 = 0;
	didwork = NO;
	p->done = 1;	/* avoid infinite loops */

	qchain = NULL;

	/* Expand any names that have embedded metacharaters */

	for(lp = p->linep ; lp ; lp = lp->nxtlineblock)
		for(q = lp->depp ; q ; q=qtemp ) {
			qtemp = q->nxtdepblock;
			expand(q);
		}

	/* make sure all dependents are up to date */

	for(lp = p->linep ; lp ; lp = lp->nxtlineblock)	{
		td = 0;
		for(q = lp->depp ; q ; q = q->nxtdepblock) {
			errstat += doname(q->depname, reclevel+1, &td1, &cochain);
			if(dbgflag)
				printf("TIME(%s)=%s", q->depname->namep,
							ctime(&td1)+4);
			if(td1 > td) 
				td = td1;
			if(ptime < td1)
				qchain = appendq(qchain, q->depname->namep);
		}
		if(p->septype == SOMEDEPS) {
			if(lp->shp!=0)
				if( ptime < td || (ptime == 0 && td == 0)
							|| lp->depp == 0) {
					okdel1 = okdel;
					okdel = NO;
					if( !questflag ) {
						if (cochain)
							co(cochain);
						setvar("@", p->namep);
						setvar("?", mkqlist(qchain) );
						errstat += docom(lp->shp);
						setvar("@", (char *) NULL);
					}
					qchain = NULL;
					cochain = NULL;
					okdel = okdel1;
					ptime1 = time((time_t *) NULL);
					didwork = YES;
				}
		} else	{
			if(lp->shp != 0) {
				if(explcom)
					fprintf(stderr, "%s: Too many command lines for `%s'\n",
					progname, p->namep);
				else
					explcom = lp->shp;
			}
			if(td > tdep)
				tdep = td;
		}
	}

	/* Look for implicit dependents, using suffix rules */

	setimpl = 0;
	for(lp = sufflist ; lp ; lp = lp->nxtlineblock)
		for(suffp = lp->depp; suffp; suffp = suffp->nxtdepblock) {
			pnamep = suffp->depname->namep;
			if(suffix(p->namep , pnamep , prefix)) {
				srchdir(concat(prefix,"*",temp), NO,
					(struct depblock *) NULL, YES);
#ifdef RCS
				srchRCS(temp);
#endif
				for(lp1 = sufflist; lp1; lp1 = lp1->nxtlineblock)
					for(suffp1=lp1->depp ; suffp1 ; suffp1 = suffp1->nxtdepblock) {
						p1namep = suffp1->depname->namep;
						if( (p1=srchname(concat(p1namep, pnamep ,concsuff)))
						    && (p2=srchname(concat(prefix, p1namep ,sourcename))) ) {
							if (dbgflag)
								printf("Found implicit match\n");
							errstat += doname(p2, reclevel+1, &td, &cochain);
							if(ptime < td)
								qchain = appendq(qchain, p2->namep);
							if(dbgflag) {
								printf("TIME(%s)=%s", p2->namep, ctime(&td)+4);
								fflush(stdout);
							}
							if(td > tdep)
								tdep = td;
							setimpl++;
							for(lp2=p1->linep ; lp2 ; lp2 = lp2->nxtlineblock)
								if(implcom = lp2->shp)
									break;
							goto endloop;
						}
					}
			}
		}

endloop:

	if (dbgflag) {
		printf("CO(%s): %s\n", p->namep, mkqlist(cochain));
		fflush(stdout);
	}
#ifdef RCS
	if (p->RCSnamep && (explcom || implcom)) {
		if (keepgoing) {
			errstat++;
			printf("%s: %s has both an RCS file and rules\n",
				progname, p->namep);
		} else
			fatal1("%s has both an RCS file and rules", p->namep);
	}
#endif
	if(errstat==0 && (ptime<tdep || (ptime==0 && tdep==0) ) ) {
		ptime = (tdep > 0 ? tdep : time((time_t *) NULL) );
#ifdef RCS
		if (cochain)
			co(cochain);
#endif
		if (setimpl) {
			setvar("*", prefix);
			setvar("<", sourcename);
		}
		setvar("@", p->namep);
		setvar("?", mkqlist(qchain) );
		if(explcom)
			errstat += docom(explcom);
		else if(implcom)
			errstat += docom(implcom);
		else if(p->septype == 0)
			if(p1=srchname(".DEFAULT")) {
				setvar("<", p->namep);
				for(lp2=p1->linep; lp2; lp2=lp2->nxtlineblock)
					if(implcom = lp2->shp) {
						errstat += docom(implcom);
						break;
					}
			} else if(keepgoing) {
				printf("%s: Don't know how to make %s\n", 
						progname, p->namep);
				++errstat;
			} else
				fatal1("Don't know how to make %s", p->namep);

		setvar("@", (char *) NULL);
		if(noexflag || (ptime = exists(p, (struct chain **) NULL)) == 0)
			ptime = time((time_t *) NULL);
	} else if(errstat!=0 && reclevel==0)
		printf("%s: `%s' not remade because of errors\n", 
				progname, p->namep);
	else if(!questflag && reclevel == 0 &&  didwork == NO)
		printf("%s: `%s' is up to date.\n", progname, p->namep);

	if(questflag && reclevel==0)
		quit(ndocoms>0 ? -1 : 0);

	p->done = (errstat ? 3 : 2);
	if(ptime1 > ptime)
		ptime = ptime1;
	p->modtime = ptime;
	*tval = ptime;
	if (dbgflag) {
		printf("%s: errstat=%d\n", p->namep, errstat);
		fflush(stdout);
	}
	return(errstat);
}

docom(q)
struct shblock *q;
{
	char *s;
	int ign, nopr;
	char string[OUTMAX];

	++ndocoms;
	if(questflag)
		return(NO);

	if(touchflag) {
		s = varptr("@")->varval;
		if(!silflag)
			printf("%s: touch(%s)\n", progname, s);
		if(!noexflag)
			touch(s);
	} else {
		for( ; q ; q = q->nxtshblock ) {
			subst(q->shbp,string);
			ign = ignerr;
			nopr = NO;
			for(s = string ; *s=='-' || *s=='@' ; ++s)
				if(*s == '-')
					ign = YES;
				else
					nopr = YES;
			if( docom1(s, ign, nopr) && !ign)
				if(keepgoing)
					return(YES);
				else
					fatal((char *) NULL);
		}
	}
	return(NO);
}

docom1(comstring, nohalt, noprint)
register char *comstring;
int nohalt, noprint;
{
	register int status;

	if(comstring[0] == '\0')
		return(0);
	if(!silflag && (!noprint || noexflag) )	{
		printf("%s%s\n", (noexflag ? "" : prompt), comstring);
		fflush(stdout);
	}

	if(noexflag)
		return(0);

	if( status = dosys(comstring, nohalt) ) {
		if( status>>8 )
			printf("*** Error code %d", status>>8 );
		else
			printf("*** Termination code %d", status );

		if(nohalt)
			printf(" (ignored)\n");
		else
			printf("\n");
		fflush(stdout);
	}

	return(status);
}

/*
 * If there are any Shell meta characters in the name,
 * expand into a list, after searching directory
 */

expand(q)
register struct depblock *q;
{
	register char *s;
	char *s1;
	struct depblock *p;

	s1 = q->depname->namep;
	for(s = s1 ; ;)
		switch(*s++) {
		case '\0':
			return;

		case '*':
		case '?':
		case '[':
			if( p = srchdir(s1 , YES, q->nxtdepblock, YES) ) {
				q->nxtdepblock = p;
				q->depname = 0;
			}
			return;
		}
}
