1/*	$NetBSD: scan.c,v 1.29 2011/01/04 10:23:40 wiz Exp $	*/
2
3/*
4 * Copyright (c) 1992 Carnegie Mellon University
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify and distribute this software and its
8 * documentation is hereby granted, provided that both the copyright
9 * notice and this permission notice appear in all copies of the
10 * software, derivative works or modified versions, and any portions
11 * thereof, and that both notices appear in supporting documentation.
12 *
13 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
14 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
15 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
16 *
17 * Carnegie Mellon requests users of this software to return to
18 *
19 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
20 *  School of Computer Science
21 *  Carnegie Mellon University
22 *  Pittsburgh PA 15213-3890
23 *
24 * any improvements or extensions that they make and grant Carnegie Mellon
25 * the rights to redistribute these changes.
26 */
27/*
28 * scan.c - sup list file scanner
29 *
30 **********************************************************************
31 * HISTORY
32 * Revision 1.8  92/08/11  12:04:28  mrt
33 * 	Brad's changes: delinted, added forward declarations of static
34 * 	functions.Added Copyright.
35 * 	[92/07/24            mrt]
36 *
37 * 18-Mar-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
38 *	Added host=<hostfile> support to releases file.
39 *
40 * 11-Mar-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
41 *	Added "rsymlink" recursive symbolic link quoting directive.
42 *
43 * 28-Jun-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
44 *	Added code for "release" support.
45 *
46 * 26-May-87  Doug Philips (dwp) at Carnegie-Mellon University
47 *	Lets see if we'll be able to write the scan file BEFORE
48 *	we collect the data for it.  Include sys/file.h and use
49 *	new definitions for access check codes.
50 *
51 * 20-May-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
52 *	Added type casting information for lint.
53 *
54 * 21-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
55 *	Added check for newonly upgrade when lasttime is the same as
56 *	scantime.  This will save us the trouble of parsing the scanfile
57 *	when the client has successfully received everything in the
58 *	scanfile already.
59 *
60 * 16-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
61 *	Clear Texec pointers in execT so that Tfree of execT will not
62 *	free command trees associated with files in listT.
63 *
64 * 06-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
65 *	Added code to omit scanned files from list if we want new files
66 *	only and they are old.
67 *
68 * 29-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
69 *	Major rewrite for protocol version 4.  Added version numbers to
70 *	scan file.  Also added mode of file in addition to flags.
71 *	Execute commands are now immediately after file information.
72 *
73 * 13-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
74 *	Added comments to list file format.
75 *
76 * 08-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
77 *	Added code to implement omitany.  Currently doesn't know about
78 *	{a,b,c} patterns.
79 *
80 * 07-Oct-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
81 *	Created.
82 *
83 **********************************************************************
84 */
85
86#ifdef HAS_VIS
87#include <vis.h>
88#endif
89#include <sys/types.h>
90#include <sys/param.h>
91#include <sys/time.h>
92#include <sys/stat.h>
93#ifdef HAS_POSIX_DIR
94#include <dirent.h>
95#else
96#include <sys/dir.h>
97#endif
98#include <sys/file.h>
99#include <limits.h>
100#include <unistd.h>
101#include "supcdefs.h"
102#include "supextern.h"
103#include "libc.h"
104#include "c.h"
105
106/*************************
107 ***    M A C R O S    ***
108 *************************/
109
110#define SPECNUMBER 1000
111 /* number of filenames produced by a single spec in the list file */
112
113/*******************************************
114 ***    D A T A   S T R U C T U R E S    ***
115 *******************************************/
116
117typedef enum {			/* release options */
118	ONEXT, OPREFIX, OLIST, OSCAN,
119	OHOST
120}    OPTION;
121
122static char *options[] = {
123	"next", "prefix", "list", "scan",
124	"host",
125	0
126};
127
128typedef enum {			/* <collection>/list file lines */
129	LUPGRADE, LOMIT, LBACKUP, LEXECUTE,
130	LINCLUDE, LNOACCT, LOMITANY, LALWAYS,
131	LSYMLINK, LRSYMLINK
132}    LISTTYPE;
133
134static char *ltname[] = {
135	"upgrade", "omit", "backup", "execute",
136	"include", "noaccount", "omitany", "always",
137	"symlink", "rsymlink",
138	0
139};
140#define FALWAYS		FUPDATE
141
142/* list file lines */
143static TREE *upgT;		/* files to upgrade */
144static TREE *flagsT;		/* special flags: BACKUP NOACCT */
145static TREE *omitT;		/* recursize file omition list */
146static TREE *omanyT;		/* non-recursize file omition list */
147static TREE *symT;		/* symbolic links to quote */
148static TREE *rsymT;		/* recursive symbolic links to quote */
149static TREE *execT;		/* execute command list */
150
151/*************************
152 ***    E X T E R N    ***
153 *************************/
154
155extern char _argbreak;		/* break character from nxtarg */
156
157extern TREELIST *listTL;	/* list of trees for scanning */
158extern TREE *listT;		/* final list of files in collection */
159extern TREE *refuseT;		/* files refused by client */
160
161extern char *collname;		/* collection name */
162extern char *basedir;		/* base directory name */
163extern char *prefix;		/* collection pathname prefix */
164extern time_t lasttime;		/* time of last upgrade */
165extern time_t scantime;		/* time of this scan */
166extern int trace;		/* trace directories */
167extern int newonly;		/* new files only */
168
169/*************************************************
170 ***   STATIC   R O U T I N E S    ***
171 *************************************************/
172static void passdelim(char **, char);
173static char *parserelease(TREELIST **, char *, char *);
174static int scanone(TREE *, void *);
175static void makescan(char *, char *);
176static void getscan(char *, char *);
177static void doscan(char *);
178static void readlistfile(char *);
179static void expTinsert(char *, TREE **, int, char *);
180static int listone(TREE *, void *);
181static void listentry(char *, char *, char *, int);
182static void listname(char *, struct stat *);
183static void listdir(char *, int);
184static int omitanyone(TREE *, void *);
185static int anyglob(char *, char *);
186static int getscanfile(char *);
187static void chkscanfile(char *);
188static void makescanfile(char *);
189static int recordone(TREE *, void *);
190static int recordexec(TREE *, void *);
191
192
193/*************************************************
194 ***    L I S T   S C A N   R O U T I N E S    ***
195 *************************************************/
196
197static void
198passdelim(char **ptr, char delim)
199{				/* skip over delimiter */
200	*ptr = skipover(*ptr, " \t");
201	if (_argbreak != delim && **ptr == delim) {
202		(*ptr)++;
203		*ptr = skipover(*ptr, " \t");
204	}
205}
206
207static char *
208parserelease(TREELIST ** tlp, char *relname, char *args)
209{
210	TREELIST *tl;
211	char *arg;
212	OPTION option;
213	int opno;
214	char *nextrel;
215
216	tl = (TREELIST *) malloc(sizeof(TREELIST));
217	if ((*tlp = tl) == NULL)
218		goaway("Couldn't allocate TREELIST");
219	tl->TLnext = NULL;
220	tl->TLname = estrdup(relname);
221	tl->TLprefix = NULL;
222	tl->TLlist = NULL;
223	tl->TLscan = NULL;
224	tl->TLhost = NULL;
225	nextrel = NULL;
226	args = skipover(args, " \t");
227	while (*(arg = nxtarg(&args, " \t="))) {
228		for (opno = 0; options[opno] != NULL; opno++)
229			if (strcmp(arg, options[opno]) == 0)
230				break;
231		if (options[opno] == NULL)
232			goaway("Invalid release option %s for release %s",
233			    arg, relname);
234		option = (OPTION) opno;
235		switch (option) {
236		case ONEXT:
237			passdelim(&args, '=');
238			arg = nxtarg(&args, " \t");
239			if (nextrel)
240				free(nextrel);
241			nextrel = estrdup(arg);
242			break;
243		case OPREFIX:
244			passdelim(&args, '=');
245			arg = nxtarg(&args, " \t");
246			tl->TLprefix = estrdup(arg);
247			break;
248		case OLIST:
249			passdelim(&args, '=');
250			arg = nxtarg(&args, " \t");
251			tl->TLlist = estrdup(arg);
252			break;
253		case OSCAN:
254			passdelim(&args, '=');
255			arg = nxtarg(&args, " \t");
256			tl->TLscan = estrdup(arg);
257			break;
258		case OHOST:
259			passdelim(&args, '=');
260			arg = nxtarg(&args, " \t");
261			tl->TLhost = estrdup(arg);
262			break;
263		}
264	}
265	return (nextrel);
266}
267
268int
269getrelease(char *release)
270{
271	TREELIST *tl;
272	char buf[STRINGLENGTH];
273	char *p, *q;
274	int rewound;
275	char *frelease = NULL;
276	FILE *f;
277
278	if (release == NULL)
279		frelease = release = estrdup(DEFRELEASE);
280	listTL = NULL;
281
282	(void) sprintf(buf, FILERELEASES, collname);
283	f = fopen(buf, "r");
284	if (f != NULL) {
285		rewound = TRUE;
286		for (;;) {
287			p = fgets(buf, sizeof(buf), f);
288			if (p == NULL) {
289				if (rewound)
290					break;
291				rewind(f);
292				rewound = TRUE;
293				continue;
294			}
295			q = strchr(p, '\n');
296			if (q)
297				*q = 0;
298			if (strchr("#;:", *p))
299				continue;
300			q = nxtarg(&p, " \t");
301			if (strcmp(q, release) != 0)
302				continue;
303			release = parserelease(&tl, release, p);
304			if (tl->TLprefix == NULL)
305				tl->TLprefix = prefix;
306			else if (chdir(tl->TLprefix) < 0) {
307				free(tl);
308				fclose(f);
309				if (frelease)
310					free(frelease);
311				return (FALSE);
312			} else
313				(void) chdir(basedir);
314			tl->TLnext = listTL;
315			listTL = tl;
316			if (release == NULL)
317				break;
318			rewound = FALSE;
319		}
320		(void) fclose(f);
321	}
322	if (release == NULL) {
323		if (frelease)
324			free(frelease);
325		return (TRUE);
326	}
327	if (strcmp(release, DEFRELEASE) != 0) {
328		if (frelease)
329			free(frelease);
330		return (FALSE);
331	}
332	(void) parserelease(&tl, release, "");
333	tl->TLprefix = prefix;
334	tl->TLnext = listTL;
335	listTL = tl;
336	if (frelease)
337		free(frelease);
338	return (TRUE);
339}
340
341void
342makescanlists(void)
343{
344	TREELIST *tl;
345	char buf[STRINGLENGTH];
346	char *p, *q;
347	FILE *f;
348	char *saveprefix = prefix;
349	int count = 0;
350
351	(void) sprintf(buf, FILERELEASES, collname);
352	f = fopen(buf, "r");
353	if (f != NULL) {
354		while ((p = fgets(buf, sizeof(buf), f)) != NULL) {
355			q = strchr(p, '\n');
356			if (q)
357				*q = 0;
358			if (strchr("#;:", *p))
359				continue;
360			q = nxtarg(&p, " \t");
361			(void) parserelease(&tl, q, p);
362			if ((prefix = tl->TLprefix) == NULL)
363				prefix = saveprefix;
364			if (prefix != NULL) {
365				if (chdir(prefix) < 0)
366					goaway("Can't chdir to %s", prefix);
367				(void) chdir(basedir);
368			}
369			makescan(tl->TLlist, tl->TLscan);
370			free(tl);
371			count++;
372		}
373		(void) fclose(f);
374	}
375	if (count == 0)
376		makescan(NULL, NULL);
377}
378
379static int
380scanone(TREE * t, void *v __unused)
381{
382	TREE *newt;
383
384	if (newonly && (t->Tflags & FNEW) == 0)
385		return (SCMOK);
386	newt = Tinsert(&listT, t->Tname, FALSE);
387	if (newt == NULL)
388		return (SCMOK);
389	newt->Tmode = t->Tmode;
390	newt->Tflags = t->Tflags;
391	newt->Tmtime = t->Tmtime;
392	return (SCMOK);
393}
394
395void
396getscanlists(void)
397{
398	TREELIST *tl, *stl;
399
400	stl = listTL;
401	listTL = NULL;
402	while ((tl = stl) != NULL) {
403		prefix = tl->TLprefix;
404		getscan(tl->TLlist, tl->TLscan);
405		tl->TLtree = listT;
406		stl = tl->TLnext;
407		tl->TLnext = listTL;
408		listTL = tl;
409	}
410	listT = NULL;
411	for (tl = listTL; tl != NULL; tl = tl->TLnext)
412		(void) Tprocess(tl->TLtree, scanone, NULL);
413}
414
415static void
416makescan(char *listfile, char *scanfile)
417{
418	listT = NULL;
419	chkscanfile(scanfile);	/* can we can write a scan file? */
420	doscan(listfile);	/* read list file and scan disk */
421	makescanfile(scanfile);	/* record names in scan file */
422	Tfree(&listT);		/* free file list tree */
423}
424
425static void
426getscan(char *listfile, char *scanfile)
427{
428	listT = NULL;
429	if (!getscanfile(scanfile)) {	/* check for pre-scanned file list */
430		scantime = time(NULL);
431		doscan(listfile);	/* read list file and scan disk */
432	}
433}
434
435static void
436doscan(char *listfile)
437{
438	char buf[STRINGLENGTH];
439
440	upgT = NULL;
441	flagsT = NULL;
442	omitT = NULL;
443	omanyT = NULL;
444	execT = NULL;
445	symT = NULL;
446	rsymT = NULL;
447	if (listfile == NULL)
448		listfile = FILELISTDEF;
449	(void) sprintf(buf, FILELIST, collname, listfile);
450	readlistfile(buf);	/* get contents of list file */
451	(void) Tprocess(upgT, listone, NULL);	/* build list of files
452						 * specified */
453	cdprefix(NULL);
454	Tfree(&upgT);
455	Tfree(&flagsT);
456	Tfree(&omitT);
457	Tfree(&omanyT);
458	Tfree(&execT);
459	Tfree(&symT);
460	Tfree(&rsymT);
461}
462
463static void
464readlistfile(char *fname)
465{
466	char buf[STRINGLENGTH + MAXPATHLEN * 4 + 1], *p;
467	char *q, *r;
468	FILE *f;
469	int ltn, n, i, flags;
470	TREE **t = NULL;
471	LISTTYPE lt;
472	char *speclist[SPECNUMBER];
473
474	f = fopen(fname, "r");
475	if (f == NULL)
476		goaway("Can't read list file %s", fname);
477	cdprefix(prefix);
478	while ((p = fgets(buf, sizeof(buf), f)) != NULL) {
479		if ((q = strchr(p, '\n')) != NULL)
480			*q = '\0';
481		if (strchr("#;:", *p))
482			continue;
483		q = nxtarg(&p, " \t");
484		if (*q == '\0')
485			continue;
486		for (ltn = 0; ltname[ltn] && strcmp(q, ltname[ltn]) != 0; ltn++);
487		if (ltname[ltn] == NULL)
488			goaway("Invalid list file keyword %s", q);
489		lt = (LISTTYPE) ltn;
490		flags = 0;
491		switch (lt) {
492		case LUPGRADE:
493			t = &upgT;
494			break;
495		case LBACKUP:
496			t = &flagsT;
497			flags = FBACKUP;
498			break;
499		case LNOACCT:
500			t = &flagsT;
501			flags = FNOACCT;
502			break;
503		case LSYMLINK:
504			t = &symT;
505			break;
506		case LRSYMLINK:
507			t = &rsymT;
508			break;
509		case LOMIT:
510			t = &omitT;
511			break;
512		case LOMITANY:
513			t = &omanyT;
514			break;
515		case LALWAYS:
516			t = &upgT;
517			flags = FALWAYS;
518			break;
519		case LINCLUDE:
520			while (*(q = nxtarg(&p, " \t"))) {
521				cdprefix(NULL);
522				n = expand(q, speclist, SPECNUMBER);
523				for (i = 0; i < n && i < SPECNUMBER; i++) {
524					readlistfile(speclist[i]);
525					cdprefix(NULL);
526					free(speclist[i]);
527				}
528				cdprefix(prefix);
529			}
530			continue;
531		case LEXECUTE:
532			r = p = q = skipover(p, " \t");
533			do {
534				q = p = skipto(p, " \t(");
535				p = skipover(p, " \t");
536			} while (*p != '(' && *p != '\0');
537			if (*p++ == '(') {
538				*q = '\0';
539				do {
540					q = nxtarg(&p, " \t)");
541					if (*q == 0)
542						_argbreak = ')';
543					else
544						expTinsert(q, &execT, 0, r);
545				} while (_argbreak != ')');
546				continue;
547			}
548			/* FALLTHROUGH */
549		default:
550			goaway("Error in handling list file keyword %d", ltn);
551		}
552		while (*(q = nxtarg(&p, " \t"))) {
553			if (lt == LOMITANY)
554				(void) Tinsert(t, q, FALSE);
555			else
556				expTinsert(q, t, flags, NULL);
557		}
558	}
559	(void) fclose(f);
560}
561
562static void
563expTinsert(char *p, TREE ** t, int flags, char *exec)
564{
565	int n, i;
566	TREE *newt;
567	char *speclist[SPECNUMBER];
568	char buf[STRINGLENGTH];
569
570	n = expand(p, speclist, SPECNUMBER);
571	for (i = 0; i < n && i < SPECNUMBER; i++) {
572		newt = Tinsert(t, speclist[i], TRUE);
573		newt->Tflags |= flags;
574		if (exec) {
575			(void) sprintf(buf, exec, speclist[i]);
576			(void) Tinsert(&newt->Texec, buf, FALSE);
577		}
578		free(speclist[i]);
579	}
580}
581
582static int
583listone(TREE * t, void *v __unused)
584{				/* expand and add one name from upgrade list */
585	listentry(t->Tname, t->Tname, NULL, (t->Tflags & FALWAYS) != 0);
586	return (SCMOK);
587}
588
589static void
590listentry(char *name, char *fullname, char *updir, int always)
591{
592	struct stat statbuf;
593	int linkcount = 0;
594
595	if (Tlookup(refuseT, fullname))
596		return;
597	if (!always) {
598		if (Tsearch(omitT, fullname))
599			return;
600		if (Tprocess(omanyT, omitanyone, fullname) != SCMOK)
601			return;
602	}
603	if (lstat(name, &statbuf) < 0)
604		return;
605	if (S_ISLNK(statbuf.st_mode)) {
606		if (Tsearch(symT, fullname)) {
607			listname(fullname, &statbuf);
608			return;
609		}
610		if (Tlookup(rsymT, fullname)) {
611			listname(fullname, &statbuf);
612			return;
613		}
614		if (updir)
615			linkcount++;
616		if (stat(name, &statbuf) < 0)
617			return;
618	}
619	if (S_ISDIR(statbuf.st_mode)) {
620		if (access(name, R_OK | X_OK) < 0)
621			return;
622		if (chdir(name) < 0)
623			return;
624		listname(fullname, &statbuf);
625		if (trace) {
626			printf("Scanning directory %s\n", fullname);
627			(void) fflush(stdout);
628		}
629		listdir(fullname, always);
630		if (updir == 0 || linkcount) {
631			(void) chdir(basedir);
632			if (prefix)
633				(void) chdir(prefix);
634			if (updir && *updir)
635				(void) chdir(updir);
636		} else
637			(void) chdir("..");
638		return;
639	}
640	if (access(name, R_OK) < 0)
641		return;
642	listname(fullname, &statbuf);
643}
644
645static void
646listname(char *name, struct stat * st)
647{
648	TREE *t, *ts;
649	int new;
650	TREELIST *tl;
651
652	new = st->st_ctime > lasttime;
653	if (newonly && !new) {
654		for (tl = listTL; tl != NULL; tl = tl->TLnext)
655			if ((ts = Tsearch(tl->TLtree, name)) != NULL)
656				ts->Tflags &= ~FNEW;
657		return;
658	}
659	t = Tinsert(&listT, name, FALSE);
660	if (t == NULL)
661		return;
662	t->Tmode = st->st_mode;
663	t->Tctime = st->st_ctime;
664	t->Tmtime = st->st_mtime;
665	if (new)
666		t->Tflags |= FNEW;
667	if ((ts = Tsearch(flagsT, name)) != NULL)
668		t->Tflags |= ts->Tflags;
669	if ((ts = Tsearch(execT, name)) != NULL) {
670		t->Texec = ts->Texec;
671		ts->Texec = NULL;
672	}
673}
674
675static void
676listdir(char *name, int always)
677{				/* expand directory */
678#ifdef HAS_POSIX_DIR
679	struct dirent *dentry;
680#else
681	struct direct *dentry;
682#endif
683	DIR *dirp;
684	char newname[STRINGLENGTH], filename[STRINGLENGTH];
685	char *p, *newp;
686
687	dirp = opendir(".");
688	if (dirp == 0)
689		return;		/* unreadable: probably protected */
690
691	p = name;		/* punt leading ./ and trailing / */
692	newp = newname;
693	if (p[0] == '.' && p[1] == '/') {
694		p += 2;
695		while (*p == '/')
696			p++;
697	}
698	while ((*newp++ = *p++) != '\0');	/* copy string */
699	--newp;			/* trailing null */
700	while (newp > newname && newp[-1] == '/')
701		--newp;		/* trailing / */
702	*newp = 0;
703	if (strcmp(newname, ".") == 0)
704		newname[0] = 0;	/* "." ==> "" */
705
706	while ((dentry = readdir(dirp)) != NULL) {
707		if (dentry->d_ino == 0)
708			continue;
709		if (strcmp(dentry->d_name, ".") == 0)
710			continue;
711		if (strcmp(dentry->d_name, "..") == 0)
712			continue;
713		if (*newname) {
714			(void)snprintf(filename, sizeof(filename), "%s/%s",
715			    newname, dentry->d_name);
716		} else {
717			(void)strncpy(filename, dentry->d_name,
718			    sizeof(filename) - 1);
719			filename[sizeof(filename) - 1] = '\0';
720		}
721		listentry(dentry->d_name, filename, newname, always);
722	}
723	closedir(dirp);
724}
725
726static int
727omitanyone(TREE * t, void *fv)
728{
729	char *filename = fv;
730	if (anyglob(t->Tname, filename))
731		return (SCMERR);
732	return (SCMOK);
733}
734
735static int
736anyglob(char *pattern, char *match)
737{
738	char *p, *m;
739	char *pb, *pe;
740
741	p = pattern;
742	m = match;
743	while (*m && *p == *m) {
744		p++;
745		m++;
746	}
747	if (*p == '\0' && *m == '\0')
748		return (TRUE);
749	switch (*p++) {
750	case '*':
751		for (;;) {
752			if (*p == '\0')
753				return (TRUE);
754			if (*m == '\0')
755				return (*p == '\0');
756			if (anyglob(p, ++m))
757				return (TRUE);
758		}
759	case '?':
760		return (anyglob(p, ++m));
761	case '[':
762		pb = p;
763		while (*(++p) != ']')
764			if (*p == '\0')
765				return (FALSE);
766		pe = p;
767		for (p = pb + 1; p != pe; p++) {
768			switch (*p) {
769			case '-':
770				if (p == pb && *m == '-') {
771					p = pe + 1;
772					return (anyglob(p, ++m));
773				}
774				if (p == pb)
775					continue;
776				if ((p + 1) == pe)
777					return (FALSE);
778				if (*m > *(p - 1) &&
779				    *m <= *(p + 1)) {
780					p = pe + 1;
781					return (anyglob(p, ++m));
782				}
783				continue;
784			default:
785				if (*m == *p) {
786					p = pe + 1;
787					return (anyglob(p, ++m));
788				}
789			}
790		}
791		return (FALSE);
792	default:
793		return (FALSE);
794	}
795}
796/*****************************************
797 ***    R E A D   S C A N   F I L E    ***
798 *****************************************/
799
800static int
801getscanfile(char *scanfile)
802{
803	char buf[STRINGLENGTH];
804#ifdef HAS_VIS
805	char fname[MAXPATHLEN];
806#else
807	char *fname;
808#endif
809	struct stat sbuf;
810	FILE *f;
811	TREE ts;
812	char *p, *q;
813	TREE *tmp, *t = NULL;
814	int notwanted;
815	TREELIST *tl;
816
817	if (scanfile == NULL)
818		scanfile = FILESCANDEF;
819	(void) sprintf(buf, FILESCAN, collname, scanfile);
820	if (stat(buf, &sbuf) < 0)
821		return (FALSE);
822	if ((f = fopen(buf, "r")) == NULL)
823		return (FALSE);
824	if ((p = fgets(buf, sizeof(buf), f)) == NULL) {
825		(void) fclose(f);
826		return (FALSE);
827	}
828	if ((q = strchr(p, '\n')) != NULL)
829		*q = '\0';
830	if (*p++ != 'V') {
831		(void) fclose(f);
832		return (FALSE);
833	}
834	if (atoi(p) != SCANVERSION) {
835		(void) fclose(f);
836		return (FALSE);
837	}
838	scantime = sbuf.st_mtime;	/* upgrade time is time of supscan,
839					 * i.e. time of creation of scanfile */
840	if (newonly && scantime == lasttime) {
841		(void) fclose(f);
842		return (TRUE);
843	}
844	notwanted = FALSE;
845	while ((p = fgets(buf, sizeof(buf), f)) != NULL) {
846		q = strchr(p, '\n');
847		if (q)
848			*q = 0;
849		ts.Tflags = 0;
850		if (*p == 'X') {
851			if (notwanted)
852				continue;
853			if (t == NULL)
854				goaway("scanfile format inconsistent");
855			(void) Tinsert(&t->Texec, ++p, FALSE);
856			continue;
857		}
858		notwanted = FALSE;
859		if (*p == 'B') {
860			p++;
861			ts.Tflags |= FBACKUP;
862		}
863		if (*p == 'N') {
864			p++;
865			ts.Tflags |= FNOACCT;
866		}
867		if ((q = strchr(p, ' ')) == NULL)
868			goaway("scanfile format inconsistent");
869		*q++ = '\0';
870		ts.Tmode = atoo(p);
871		p = q;
872		if ((q = strchr(p, ' ')) == NULL)
873			goaway("scanfile format inconsistent");
874		*q++ = '\0';
875		ts.Tctime = atoi(p);
876		p = q;
877		if ((q = strchr(p, ' ')) == NULL)
878			goaway("scanfile format inconsistent");
879		*q++ = 0;
880		ts.Tmtime = atoi(p);
881#ifdef HAS_VIS
882		(void) strunvis(fname, q);
883#else
884		fname = q;
885#endif
886		if (ts.Tctime > lasttime)
887			ts.Tflags |= FNEW;
888		else if (newonly) {
889			for (tl = listTL; tl != NULL; tl = tl->TLnext)
890				if ((tmp = Tsearch(tl->TLtree, fname)) != NULL)
891					tmp->Tflags &= ~FNEW;
892			notwanted = TRUE;
893			continue;
894		}
895		if (Tlookup(refuseT, fname)) {
896			notwanted = TRUE;
897			continue;
898		}
899		t = Tinsert(&listT, fname, TRUE);
900		t->Tmode = ts.Tmode;
901		t->Tflags = ts.Tflags;
902		t->Tctime = ts.Tctime;
903		t->Tmtime = ts.Tmtime;
904	}
905	(void) fclose(f);
906	return (TRUE);
907}
908/*******************************************
909 ***    W R I T E   S C A N   F I L E    ***
910 *******************************************/
911
912static void
913chkscanfile(char *scanfile)
914{
915	char tname[STRINGLENGTH], fname[STRINGLENGTH];
916	FILE *f;
917
918	if (scanfile == NULL)
919		scanfile = FILESCANDEF;
920	(void) sprintf(fname, FILESCAN, collname, scanfile);
921	(void) sprintf(tname, "%s.temp", fname);
922	if (NULL == (f = fopen(tname, "w")))
923		goaway("Can't test scan file temp %s for %s", tname, collname);
924	else {
925		(void) unlink(tname);
926		(void) fclose(f);
927	}
928}
929
930static void
931makescanfile(char *scanfile)
932{
933	char tname[STRINGLENGTH], fname[STRINGLENGTH];
934	struct timeval tbuf[2];
935	FILE *scanF;		/* output file for scanned file list */
936
937	if (scanfile == NULL)
938		scanfile = FILESCANDEF;
939	(void) sprintf(fname, FILESCAN, collname, scanfile);
940	(void) sprintf(tname, "%s.temp", fname);
941	scanF = fopen(tname, "w");
942	if (scanF == NULL)
943		goto out;
944	if (fprintf(scanF, "V%d\n", SCANVERSION) < 0)
945		goto closeout;
946	if (Tprocess(listT, recordone, scanF) != SCMOK)
947		goto closeout;
948	if (fclose(scanF) != 0)
949		goto out;
950	if (rename(tname, fname) < 0) {
951		(void)unlink(tname);
952		goaway("Can't change %s to %s", tname, fname);
953	}
954	tbuf[0].tv_sec = time(NULL);
955	tbuf[0].tv_usec = 0;
956	tbuf[1].tv_sec = scantime;
957	tbuf[1].tv_usec = 0;
958	(void) utimes(fname, tbuf);
959	return;
960closeout:
961	(void) fclose(scanF);
962out:
963	goaway("Can't write scan file temp %s for %s", tname, collname);
964}
965
966static int
967recordone(TREE * t, void *v)
968{
969	FILE *scanF = v;
970#ifdef HAS_VIS
971	char fname[MAXPATHLEN * 4 + 1];
972	strvis(fname, t->Tname, VIS_WHITE);
973#else
974	char *fname = t->Tname;
975#endif
976
977	if (t->Tflags & FBACKUP)
978		if (fprintf(scanF, "B") < 0)
979			return SCMERR;
980	if (t->Tflags & FNOACCT)
981		if (fprintf(scanF, "N") < 0)
982			return SCMERR;
983	if (fprintf(scanF, "%o %d %d %s\n",
984	    t->Tmode, t->Tctime, t->Tmtime, fname) < 0)
985		return SCMERR;
986	return Tprocess(t->Texec, recordexec, scanF);
987}
988
989static int
990recordexec(TREE * t, void *v)
991{
992	FILE *scanF = v;
993#ifdef HAS_VIS
994	char fname[MAXPATHLEN * 4 + 1];
995	strvis(fname, t->Tname, VIS_WHITE);
996#else
997	char *fname = t->Tname;
998#endif
999	if (fprintf(scanF, "X%s\n", fname) < 0)
1000		return SCMERR;
1001	return (SCMOK);
1002}
1003
1004void
1005cdprefix(char *prefix)
1006{
1007	static char *curprefix = NULL;
1008
1009	if (curprefix == NULL) {
1010		if (prefix == NULL)
1011			return;
1012		(void) chdir(prefix);
1013		curprefix = prefix;
1014		return;
1015	}
1016	if (prefix == NULL) {
1017		(void) chdir(basedir);
1018		curprefix = NULL;
1019		return;
1020	}
1021	if (prefix == curprefix)
1022		return;
1023	if (strcmp(prefix, curprefix) == 0) {
1024		curprefix = prefix;
1025		return;
1026	}
1027	(void) chdir(basedir);
1028	(void) chdir(prefix);
1029	curprefix = prefix;
1030}
1031