static char *RcsId = 
    "$Header: /src/bin/make/m/files.c,v 4.15 84/04/24 21:16:02 crl Exp $";

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

/*
 * $Log:	/src/bin/make/m/files.c,v $
 * Revision 4.15  84/04/24  21:16:02  crl
 * Cleaned up error mesgs and added some portability casts.
 * 
 * Revision 4.14  83/12/11  23:46:25  crl
 * Modified to use new directory routines.
 * 
 * Revision 4.13  83/12/04  16:07:02  crl
 * Fixed bug where RCS files in subdirs that were not named RCS were not
 * 	found.
 * 
 * Revision 4.12  83/11/25  13:51:52  crl
 * In srchdir(), also make sure dirf != NULL before fseeking
 * 
 * Revision 4.11  83/11/21  12:48:10  crl
 * In fixing the problem mentioned below (4.10), I broke the directory search
 * 	for implicits.  The directory was not searched the first time it
 * 	was opened.
 * 
 * Revision 4.10  83/11/18  23:10:12  crl
 * Fixed bug in srchdir():  if the RCS dir wasn't present, the dir was
 * 	entered in the open dir chain anyways, causing the second reference
 * 	to the directory to think it was already open.
 * 
 * Revision 4.9  83/11/02  20:52:48  crl
 * Fixed bug in srchdir where RCS files in . wouldn't be handled properly.
 * 
 * Revision 4.8  83/10/31  01:32:15  crl
 * srchdir():
 * 	Added 4th arg that says whether to die on open failure.
 * 	Reimplemented RCS and ,v stripping to work on a multi-directory
 * 		filename
 * getrcs():
 * 	1st arg now a nameblock *
 * 	Try to find an RCS file by appending ,v and possibly inserting
 * 		RCS/ in the right place, rather than at the front always
 * 	sets nameblock.RCSnamep
 * co():
 * 	now sets @=working file and <=RCS file.
 * srchRCS():
 * 	Added to search proper directory for RCS files.
 * 
 * Revision 4.7  83/10/22  20:02:40  crl
 * Removed a (now) unused array in co().
 * 
 * Revision 4.6  83/10/22  19:17:02  crl
 * Changed exists() to take a nameblock as its 1st arg so it can set isrcs.
 * Changed co() to use docom() and ".CO" target.
 * Changed rm() to use docom() and ".CLEANUP" target.
 * Removed rules (placed in rules.c).
 * 
 * Revision 4.5  83/07/24  16:02:11  crl
 * Have srchdir() try to strip off a leading RCSdir and a trailing RCS_SUF
 * 	when calling srchname() and makename().  This is so that implicit
 * 	rules can find the entries without specific mention in the makefile.
 * 
 * Revision 4.4  83/07/23  15:33:44  crl
 * Implemented automatic deletion of files that are automatically checked out.
 * Added rm() to do the above.
 * 
 * Revision 4.3  83/07/23  15:10:46  crl
 * Include types.h in defs.h instead
 * The 2nd arg to exists() changed to a chain of files to co.
 * getrcs() gets a 2nd arg--a chain of filenames to co that will be 
 * 	appended to if we find the RCS file.  This is to defer co'ing
 * 	until docom().
 * New function co() added that co's a chain of files.
 * TIMETYPE replaced by time_t
 * 
 * Revision 4.2  83/07/22  14:14:33  crl
 * Made to work transparently with RCS.
 * 
 * Revision 4.1  83/03/01  00:43:35  crl
 * Vanilla 4.1 version
 */

#include "defs.h"
#include <sys/stat.h>
#include <ar.h>
#include <a.out.h>

/*
 * Check to see if 'filename' exists and return its modified time.
 * If it doesn't, then if coflag is true and ch is non-null, try to
 *	find the RCS file, returning the modified time of the RCS file
 */
time_t
exists(p, ch)
register struct nameblock *p;
struct chain **ch;
{
	struct stat buf;
	register char *s;
	register char *filename;

	filename = p->namep;
	for(s = filename ; *s!='\0' && *s!='(' ; ++s)
		;

	if(*s == '(')
		return(lookarch(filename));

	if(stat(filename,&buf) < 0) {
#ifdef RCS
		if (coflag && ch) 
			return(getrcs(p, ch));
		else
			return(0);
#else
		return(0);
#endif
	} else
		return(buf.st_mtime);
}

FSTATIC char nbuf[MAXNAMLEN];
FSTATIC char *nbufend	= &nbuf[MAXNAMLEN];

struct depblock *
srchdir(pat, mkchain, nextdbl, die)
register char *pat;		/* pattern to be matched in directory */
int mkchain;			/* nonzero if results to be remembered */
struct depblock *nextdbl;	/* final value for chain */
int die;			/* fatal error if we can't open dir */
{
	DIR * dirf;
	int cldir;
	char *dirname, *dirpref, *endir, *filepat, *p1, *p2;
	char fullname[MAXPATH], temp[MAXPATH];
	struct nameblock *q;
	struct depblock *thisdbl;
	struct opendir *od;
	struct pattern *patp;
	struct direct *dptr;
#ifdef RCS
	char temp2[MAXPATH], *RCSpref;
#endif

	thisdbl = 0;

	if(mkchain == NO)
		for(patp=firstpat ; patp ; patp = patp->nxtpattern)
			if(! unequal(pat, patp->patval))
				return(0);

	patp = ALLOC(pattern);
	patp->nxtpattern = firstpat;
	firstpat = patp;
	patp->patval = copys(pat);

	if((endir = rindex(pat, '/')) == NULL) {
		dirname = ".";
		dirpref = "";
		filepat = pat;
	} else {
		dirname = pat;
		*endir = '\0';
		dirpref = concat(dirname, "/", temp);
		filepat = endir+1;
	}
#ifdef RCS
	RCSpref = NULL;
	if (coflag && !mkchain) {
		if (suffix(dirname, RCSdir, temp2))
			RCSpref = temp2;
		else
			RCSpref = dirpref;
	}
#endif
	dirf = NULL;
	cldir = NO;

	for(od = firstod; od; od = od->nxtopendir)
		if(! unequal(dirname, od->dirn) ) {
			if ((dirf = od->dirfc) != NULL)
				rewinddir(dirf); 	/* start over  */
			break;
		}

	if(dirf == NULL) {
		if ((dirf = opendir(dirname)) == NULL) {
			if (die)
				fatal1("Directory %s: Cannot open", dirname);
			if (endir)
				*endir = '/';
			return(NULL);
		}
		if(nopdir >= MAXDIR)
			cldir = YES;
		else if (dirf != NULL) {
			++nopdir;
			od = ALLOC(opendir);
			od->nxtopendir = firstod;
			firstod = od;
			od->dirfc = dirf;
			od->dirn = copys(dirname);
		}
	}
	while ((dptr = readdir(dirf)) != NULL) {
		p1 = dptr->d_name;
		p2 = nbuf;
		while((p2<nbufend) && (*p2++ = *p1++) != '\0' )
			;
		if (amatch(nbuf,filepat)) {
			concat(dirpref,nbuf,fullname);
			if ((q=srchname(fullname)) == NULL)
				q = makename(copys(fullname));
			if(mkchain) {
				thisdbl = ALLOC(depblock);
				thisdbl->nxtdepblock=nextdbl;
				thisdbl->depname = q;
				nextdbl = thisdbl;
			}
#ifdef RCS
			/*
			 * if RCSpref is non-null, it is the dirpref
			 * without the "RCS/".  This, along with RCS_SUF
			 * is stripped so implicit rules can find the
			 * corresponding files
			 */
			if (!suffix(nbuf, RCSsuf, nbuf))
				continue;
			if (RCSpref)
				p1=ncat(fullname, RCSpref, -1);
			else
				p1 = fullname;
			ncat(p1, nbuf, -1);
			if (srchname(fullname) == NULL)
				makename(copys(fullname));
#endif
		}
	}

	if (endir != 0)
		*endir = '/';

	if (cldir)
		closedir(dirf);
	return(thisdbl);
}

/* stolen from glob through find */

static
amatch(s, p)
char *s, *p;
{
	register int cc, scc, k;
	int c, lc;

	scc = *s;
	lc = 077777;
	switch (c = *p) {
	case '[':
		k = 0;
		while (cc = *++p) {
			switch (cc) {
			case ']':
				if (k)
					return(amatch(++s, ++p));
				else
					return(0);
			case '-':
				k |= (lc <= scc)  & (scc <= (cc=p[1]) ) ;
			}
			if (scc == (lc = cc))
				k++;
		}
		return(0);

	case '?':
caseq:
		if(scc)
			return(amatch(++s, ++p));
		return(0);
	case '*':
		return(umatch(s, ++p));
	case 0:
		return(!scc);
	}
	if (c==scc)
		goto caseq;
	return(0);
}

static
umatch(s, p)
char *s, *p;
{
	if(*p == 0)
		return(1);
	while(*s)
		if (amatch(s++, p))
			return(1);
	return(0);
}

#ifdef METERFILE
#include <pwd.h>
int meteron	= 1;	/* default: metering off */

meter(file)
char *file;
{
	time_t tvec;
	char *p;
	FILE * mout;
	struct passwd *pwd, *getpwuid();

	if(file==0 || meteron==0)
		return;

	pwd = getpwuid(getuid());

	time(&tvec);

	if( (mout=fopen(file,"a")) != NULL ) {
		p = ctime(&tvec);
		p[16] = '\0';
		fprintf(mout,"User %s, %s\n",pwd->pw_name,p+4);
		fclose(mout);
	}
}
#endif


/* look inside archives for notations a(b) and a((b))
 *	a(b)	is file member   b   in archive a
 *	a((b))	is entry point  _b  in object archive a
 */

FILE *arfd;
long int arpos, arlen;
static char arfname[16];
static long arfdate;
static long arflen;
static struct exec objhead;
static struct nlist objentry;

time_t 
lookarch(filename)
char *filename;
{
	char *p, *q, *send, s[15];
	int i, nc, nsym, objarch;

	for(p = filename; *p!= '(' ; ++p)
		;
	*p = '\0';
	openarch(filename);
	*p++ = '(';

	if(*p == '(') {
		objarch = YES;
		nc = 8;
		++p;
	} else {
		objarch = NO;
		nc = 14;
	}
	send = s + nc;

	for( q = s ; q<send && *p!='\0' && *p!=')' ; *q++ = *p++ )
		;
	while(q < send)
		*q++ = '\0';
	while(getarch()) {
		if(objarch) {
			getobj();
			nsym = objhead.a_syms / sizeof(objentry);
			for(i = 0; i<nsym ; ++i) {
				fread( (char *) &objentry, sizeof(objentry),1,arfd);
				if( (objentry.n_type & N_EXT)
				    && ((objentry.n_type & ~N_EXT) || objentry.n_value)
#ifdef vax
				    && eqstr(objentry.n_un.n_name,s,nc)) {
#else
				    && eqstr(objentry.n_name,s,nc)) {
#endif
					clarch();
					return(arfdate);
				}
			}
		} else if( eqstr(arfname, s, nc)) {
			clarch();
			return(arfdate);
		}
	}

	clarch();
	return( 0L);
}

clarch()
{
	fclose( arfd );
}


openarch(f)
register char *f;
{
#ifdef ASCARCH
	char magic[SARMAG];
#endif
	int word;
	struct stat buf;

	stat(f, &buf);
	arlen = buf.st_size;

	arfd = fopen(f, "r");
	if(arfd == NULL)
		fatal1("cannot open %s", f);

	fread( (char *) &word, sizeof(word), 1, arfd);
#ifdef ASCARCH
	fseek(arfd, 0L, 0);
	fread(magic, SARMAG, 1, arfd);
	arpos = SARMAG;
	if( ! eqstr(magic, ARMAG, SARMAG) )
#else
		arpos = sizeof(word);
	if(word != ARMAG)
#endif
		fatal1("%s is not an archive", f);

	arflen = 0;
}

getarch()
{
	struct ar_hdr arhead;

	arpos += (arflen + 1) & ~1L;	/* round archived file length up to even */
	if(arpos >= arlen)
		return(0);
	fseek(arfd, arpos, 0);

	fread( (char *) &arhead, sizeof(arhead), 1, arfd);
	arpos += sizeof(arhead);
#ifdef ASCARCH
	arflen = atol(arhead.ar_size);
	arfdate = atol(arhead.ar_date);
#else
	arflen = arhead.ar_size;
	arfdate = arhead.ar_date;
#endif
	strncpy(arfname, arhead.ar_name, sizeof(arhead.ar_name));
	return(1);
}

getobj()
{
	long int skip;

	fread( (char *) &objhead, sizeof(objhead), 1, arfd);
	if (N_BADMAG(objhead))
		fatal1("%s is not an object module", arfname);
	skip = objhead.a_text + objhead.a_data;
#ifdef vax
	skip += objhead.a_trsize + objhead.a_drsize;
#else
	if(! objhead.a_flag )
		skip *= 2;
#endif
	fseek(arfd, skip, 1);
}

eqstr(a,b,n)
register char *a, *b;
register int n;
{
	do
		if(*a++ != *b++)
			return(NO);
	while (--n);
	return(YES);
}

#ifdef RCS

/*
 * Try to find an RCS file corresponding to 'filename', and return
 *	the modified time of the RCS file.
 * If ch is non-null, then append it on the chain for later check-out
 */
time_t
getrcs(p, ch)
register struct nameblock *p;
struct chain **ch;
{
	struct stat sbuf;
	char temp[MAXPATH];
	register char *tail;
	register char *s;
	int headlen;

	if ((tail = rindex(p->namep, '/')) == NULL) {
		headlen = 0;
		tail = p->namep;
	} else
		headlen = ++tail - p->namep;
	s = ncat(temp, p->namep, headlen);
	s = ncat(s, RCSdir, -1);
	*s++ = '/';
	s = ncat(s, tail, -1);
	ncat(s, RCSsuf, -1);
	if (stat(temp, &sbuf) < 0) {
		concat(p->namep, RCSsuf, temp);
		if (stat(temp, &sbuf) < 0)
			return(0);
	}
	p->RCSnamep = copys(temp);
	if (ch)
		*ch = appendq(*ch, p->namep);
	return(sbuf.st_mtime);
}

/*
 * Try to check-out the files specified in ch, if they do not
 *	already exist.  If rmflag is true, mark successful attempts
 *	for automatic deletion.
 * Try to make the modified time of the file the same as that of the
 *	RCS file.
 */
co(ch)
register struct chain *ch;
{
	register struct nameblock *p;
	register char *file;
	char *RCSfile;
	struct stat sbuf;
	int i;
	time_t tm[2];
	
	for ( ; ch; ch = ch->nextp) {
		if ((file=ch->datap) == NULL || (p=srchname(file)) == NULL)
			continue;
		if (stat(file, &sbuf) == 0)
			continue;	/* don't do it again */
		RCSfile = p->RCSnamep;
		p->RCSnamep = NULL;
		setvar("@", file);
		setvar("<", RCSfile);
		i = docom(co_cmd);
		setvar("@", (char *) NULL);  /* so it doesn't get deleted */
		if (i)
			continue;	/* docom() failed */
		/*
		 * since we succeeded, mark it for later deletion
		 */
		if (rmflag && !isprecious(file))
			rmchain = appendq(rmchain, file);
		/*
		 * try to set modified time on file
		 */
		if (stat(RCSfile, &sbuf) == 0) {
			tm[0] = time((time_t *) NULL);
			tm[1] = sbuf.st_mtime;
			utime(file, tm);
		}
	}
}

/*
 * delete the files listed in rmchain
 */

rm ()
{
	register struct chain *q;
	struct nameblock *p;
	register struct lineblock *lp;
	register struct shblock *sp;
	static int once = 0;

	if (once || (q = rmchain) == NULL)  /* only if we should */
		return;
	once = 1;
	if ((p = srchname(".CLEANUP")) == NULL)
		return;
	sp = NULL;
	for (lp = p->linep; lp; lp = lp->nxtlineblock)
		if (sp = lp->shp)
			break;
	if (sp == NULL)			/* no or NULL .CLEANUP ? */
		return;
	setvar("?", mkqlist(q));
	docom(sp);	
}

/*
 * Do the srchdir for RCS files.  For a pattern a/b, it searches 
 * a/RCS/b, without generating an error if it doesn't exist
 */
srchRCS(pat)
register char *pat;
{
	char temp[MAXPATH];
	int headlen;
	register char *tail, *s;

	if ((tail = rindex(pat, '/')) == NULL) {
					/* quick search for '.' */
		if (dotRCS) {
			s = ncat(temp, RCSdir, -1);
			*s++ = '/';
			ncat(s, pat, -1);
			srchdir(temp, NO, (struct depblock *) NULL, NO);
		}
		return;
	} else
		tail++;
	headlen = tail - pat;
	s = ncat(temp, pat, headlen);
	s = ncat(s, RCSdir, -1);
	*s++ = '/';
	ncat(s, tail, -1);
	srchdir(temp, NO, (struct depblock *) NULL, NO);
}
#endif
