1 /* $NetBSD: scan.c,v 1.26 2007/12/20 20:15:59 christos Exp $ */
4 * Copyright (c) 1992 Carnegie Mellon University
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.
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.
17 * Carnegie Mellon requests users of this software to return to
19 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
20 * School of Computer Science
21 * Carnegie Mellon University
22 * Pittsburgh PA 15213-3890
24 * any improvements or extensions that they make and grant Carnegie Mellon
25 * the rights to redistribute these changes.
28 * scan.c - sup list file scanner
30 **********************************************************************
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.
37 * 18-Mar-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
38 * Added host=<hostfile> support to releases file.
40 * 11-Mar-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
41 * Added "rsymlink" recursive symbolic link quoting directive.
43 * 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
44 * Added code for "release" support.
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.
51 * 20-May-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
52 * Added type casting information for lint.
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
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.
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.
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.
73 * 13-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
74 * Added comments to list file format.
76 * 08-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
77 * Added code to implement omitany. Currently doesn't know about
80 * 07-Oct-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
83 **********************************************************************
89 #include <sys/types.h>
90 #include <sys/param.h>
101 #include "supcdefs.h"
102 #include "supextern.h"
106 /*************************
108 *************************/
110 #define SPECNUMBER 1000
111 /* number of filenames produced by a single spec in the list file */
113 /*******************************************
114 *** D A T A S T R U C T U R E S ***
115 *******************************************/
117 typedef enum { /* release options */
118 ONEXT
, OPREFIX
, OLIST
, OSCAN
,
122 static char *options
[] = {
123 "next", "prefix", "list", "scan",
128 typedef enum { /* <collection>/list file lines */
129 LUPGRADE
, LOMIT
, LBACKUP
, LEXECUTE
,
130 LINCLUDE
, LNOACCT
, LOMITANY
, LALWAYS
,
134 static char *ltname
[] = {
135 "upgrade", "omit", "backup", "execute",
136 "include", "noaccount", "omitany", "always",
137 "symlink", "rsymlink",
140 #define FALWAYS FUPDATE
142 /* list file lines */
143 static TREE
*upgT
; /* files to upgrade */
144 static TREE
*flagsT
; /* special flags: BACKUP NOACCT */
145 static TREE
*omitT
; /* recursize file omition list */
146 static TREE
*omanyT
; /* non-recursize file omition list */
147 static TREE
*symT
; /* symbolic links to quote */
148 static TREE
*rsymT
; /* recursive symbolic links to quote */
149 static TREE
*execT
; /* execute command list */
151 /*************************
153 *************************/
155 extern char _argbreak
; /* break character from nxtarg */
157 extern TREELIST
*listTL
; /* list of trees for scanning */
158 extern TREE
*listT
; /* final list of files in collection */
159 extern TREE
*refuseT
; /* files refused by client */
161 extern char *collname
; /* collection name */
162 extern char *basedir
; /* base directory name */
163 extern char *prefix
; /* collection pathname prefix */
164 extern time_t lasttime
; /* time of last upgrade */
165 extern time_t scantime
; /* time of this scan */
166 extern int trace
; /* trace directories */
167 extern int newonly
; /* new files only */
170 extern char *rcs_branch
;
174 /*************************************************
175 *** STATIC R O U T I N E S ***
176 *************************************************/
177 static void passdelim(char **, char);
178 static char *parserelease(TREELIST
**, char *, char *);
179 static int scanone(TREE
*, void *);
180 static void makescan(char *, char *);
181 static void getscan(char *, char *);
182 static void doscan(char *);
183 static void readlistfile(char *);
184 static void expTinsert(char *, TREE
**, int, char *);
185 static int listone(TREE
*, void *);
186 static void listentry(char *, char *, char *, int);
187 static void listname(char *, struct stat
*);
188 static void listdir(char *, int);
189 static int omitanyone(TREE
*, void *);
190 static int anyglob(char *, char *);
191 static int getscanfile(char *);
192 static void chkscanfile(char *);
193 static void makescanfile(char *);
194 static int recordone(TREE
*, void *);
195 static int recordexec(TREE
*, void *);
198 /*************************************************
199 *** L I S T S C A N R O U T I N E S ***
200 *************************************************/
203 passdelim(char **ptr
, char delim
)
204 { /* skip over delimiter */
205 *ptr
= skipover(*ptr
, " \t");
206 if (_argbreak
!= delim
&& **ptr
== delim
) {
208 *ptr
= skipover(*ptr
, " \t");
213 parserelease(TREELIST
** tlp
, char *relname
, char *args
)
221 tl
= (TREELIST
*) malloc(sizeof(TREELIST
));
222 if ((*tlp
= tl
) == NULL
)
223 goaway("Couldn't allocate TREELIST");
225 tl
->TLname
= estrdup(relname
);
231 args
= skipover(args
, " \t");
232 while (*(arg
= nxtarg(&args
, " \t="))) {
233 for (opno
= 0; options
[opno
] != NULL
; opno
++)
234 if (strcmp(arg
, options
[opno
]) == 0)
236 if (options
[opno
] == NULL
)
237 goaway("Invalid release option %s for release %s",
239 option
= (OPTION
) opno
;
242 passdelim(&args
, '=');
243 arg
= nxtarg(&args
, " \t");
246 nextrel
= estrdup(arg
);
249 passdelim(&args
, '=');
250 arg
= nxtarg(&args
, " \t");
251 tl
->TLprefix
= estrdup(arg
);
254 passdelim(&args
, '=');
255 arg
= nxtarg(&args
, " \t");
256 tl
->TLlist
= estrdup(arg
);
259 passdelim(&args
, '=');
260 arg
= nxtarg(&args
, " \t");
261 tl
->TLscan
= estrdup(arg
);
264 passdelim(&args
, '=');
265 arg
= nxtarg(&args
, " \t");
266 tl
->TLhost
= estrdup(arg
);
274 getrelease(char *release
)
277 char buf
[STRINGLENGTH
];
280 char *frelease
= NULL
;
284 frelease
= release
= estrdup(DEFRELEASE
);
287 (void) sprintf(buf
, FILERELEASES
, collname
);
292 p
= fgets(buf
, sizeof(buf
), f
);
303 if (strchr("#;:", *p
))
305 q
= nxtarg(&p
, " \t");
306 if (strcmp(q
, release
) != 0)
308 release
= parserelease(&tl
, release
, p
);
309 if (tl
->TLprefix
== NULL
)
310 tl
->TLprefix
= prefix
;
311 else if (chdir(tl
->TLprefix
) < 0) {
318 (void) chdir(basedir
);
327 if (release
== NULL
) {
332 if (strcmp(release
, DEFRELEASE
) != 0) {
337 (void) parserelease(&tl
, release
, "");
338 tl
->TLprefix
= prefix
;
350 char buf
[STRINGLENGTH
];
353 char *saveprefix
= prefix
;
356 (void) sprintf(buf
, FILERELEASES
, collname
);
359 while ((p
= fgets(buf
, sizeof(buf
), f
)) != NULL
) {
363 if (strchr("#;:", *p
))
365 q
= nxtarg(&p
, " \t");
366 (void) parserelease(&tl
, q
, p
);
367 if ((prefix
= tl
->TLprefix
) == NULL
)
369 if (prefix
!= NULL
) {
370 if (chdir(prefix
) < 0)
371 goaway("Can't chdir to %s", prefix
);
372 (void) chdir(basedir
);
374 makescan(tl
->TLlist
, tl
->TLscan
);
381 makescan((char *) NULL
, (char *) NULL
);
385 scanone(TREE
* t
, void *v __unused
)
389 if (newonly
&& (t
->Tflags
& FNEW
) == 0)
391 newt
= Tinsert(&listT
, t
->Tname
, FALSE
);
394 newt
->Tmode
= t
->Tmode
;
395 newt
->Tflags
= t
->Tflags
;
396 newt
->Tmtime
= t
->Tmtime
;
407 while ((tl
= stl
) != NULL
) {
408 prefix
= tl
->TLprefix
;
409 getscan(tl
->TLlist
, tl
->TLscan
);
416 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
)
417 (void) Tprocess(tl
->TLtree
, scanone
, NULL
);
421 makescan(char *listfile
, char *scanfile
)
424 chkscanfile(scanfile
); /* can we can write a scan file? */
425 doscan(listfile
); /* read list file and scan disk */
426 makescanfile(scanfile
); /* record names in scan file */
427 Tfree(&listT
); /* free file list tree */
431 getscan(char *listfile
, char *scanfile
)
434 if (!getscanfile(scanfile
)) { /* check for pre-scanned file list */
435 scantime
= time((time_t *) NULL
);
436 doscan(listfile
); /* read list file and scan disk */
441 doscan(char *listfile
)
443 char buf
[STRINGLENGTH
];
452 if (listfile
== NULL
)
453 listfile
= FILELISTDEF
;
454 (void) sprintf(buf
, FILELIST
, collname
, listfile
);
455 readlistfile(buf
); /* get contents of list file */
456 (void) Tprocess(upgT
, listone
, NULL
); /* build list of files
458 cdprefix((char *) NULL
);
469 readlistfile(char *fname
)
471 char buf
[STRINGLENGTH
+ MAXPATHLEN
* 4 + 1], *p
;
474 int ltn
, n
, i
, flags
;
477 char *speclist
[SPECNUMBER
];
479 f
= fopen(fname
, "r");
481 goaway("Can't read list file %s", fname
);
483 while ((p
= fgets(buf
, sizeof(buf
), f
)) != NULL
) {
484 if ((q
= strchr(p
, '\n')) != NULL
)
486 if (strchr("#;:", *p
))
488 q
= nxtarg(&p
, " \t");
491 for (ltn
= 0; ltname
[ltn
] && strcmp(q
, ltname
[ltn
]) != 0; ltn
++);
492 if (ltname
[ltn
] == NULL
)
493 goaway("Invalid list file keyword %s", q
);
525 while (*(q
= nxtarg(&p
, " \t"))) {
526 cdprefix((char *) NULL
);
527 n
= expand(q
, speclist
, SPECNUMBER
);
528 for (i
= 0; i
< n
&& i
< SPECNUMBER
; i
++) {
529 readlistfile(speclist
[i
]);
530 cdprefix((char *) NULL
);
537 r
= p
= q
= skipover(p
, " \t");
539 q
= p
= skipto(p
, " \t(");
540 p
= skipover(p
, " \t");
541 } while (*p
!= '(' && *p
!= '\0');
545 q
= nxtarg(&p
, " \t)");
549 expTinsert(q
, &execT
, 0, r
);
550 } while (_argbreak
!= ')');
555 goaway("Error in handling list file keyword %d", ltn
);
557 while (*(q
= nxtarg(&p
, " \t"))) {
559 (void) Tinsert(t
, q
, FALSE
);
561 expTinsert(q
, t
, flags
, (char *) NULL
);
568 expTinsert(char *p
, TREE
** t
, int flags
, char *exec
)
572 char *speclist
[SPECNUMBER
];
573 char buf
[STRINGLENGTH
];
575 n
= expand(p
, speclist
, SPECNUMBER
);
576 for (i
= 0; i
< n
&& i
< SPECNUMBER
; i
++) {
577 newt
= Tinsert(t
, speclist
[i
], TRUE
);
578 newt
->Tflags
|= flags
;
580 (void) sprintf(buf
, exec
, speclist
[i
]);
581 (void) Tinsert(&newt
->Texec
, buf
, FALSE
);
588 listone(TREE
* t
, void *v __unused
)
589 { /* expand and add one name from upgrade list */
590 listentry(t
->Tname
, t
->Tname
, (char *) NULL
, (t
->Tflags
& FALWAYS
) != 0);
595 listentry(char *name
, char *fullname
, char *updir
, int always
)
600 if (Tlookup(refuseT
, fullname
))
603 if (Tsearch(omitT
, fullname
))
605 if (Tprocess(omanyT
, omitanyone
, fullname
) != SCMOK
)
608 if (lstat(name
, &statbuf
) < 0)
610 if (S_ISLNK(statbuf
.st_mode
)) {
611 if (Tsearch(symT
, fullname
)) {
612 listname(fullname
, &statbuf
);
615 if (Tlookup(rsymT
, fullname
)) {
616 listname(fullname
, &statbuf
);
621 if (stat(name
, &statbuf
) < 0)
624 if (S_ISDIR(statbuf
.st_mode
)) {
625 if (access(name
, R_OK
| X_OK
) < 0)
629 listname(fullname
, &statbuf
);
631 printf("Scanning directory %s\n", fullname
);
632 (void) fflush(stdout
);
634 listdir(fullname
, always
);
635 if (updir
== 0 || linkcount
) {
636 (void) chdir(basedir
);
638 (void) chdir(prefix
);
645 if (access(name
, R_OK
) < 0)
649 char rcs_release
[STRINGLENGTH
];
651 if (rcs_branch
!= NULL
)
653 sprintf(rcs_release
, "-r %s", rcs_branch
);
655 sprintf(rcs_release
, "-r%s", rcs_branch
);
658 rcs_release
[0] = '\0';
660 sprintf(sys_com
, "cvs -d %s -r -l -Q co -p %s %s > %s\n", cvs_root
, rcs_release
, name
, rcs_file
);
662 status
= runp("rcsstat", "rcsstat", "-q", rcs_release
, name
, 0);
668 listname(fullname
, &statbuf
);
672 listname(char *name
, struct stat
* st
)
678 new = st
->st_ctime
> lasttime
;
679 if (newonly
&& !new) {
680 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
)
681 if ((ts
= Tsearch(tl
->TLtree
, name
)) != NULL
)
685 t
= Tinsert(&listT
, name
, FALSE
);
688 t
->Tmode
= st
->st_mode
;
689 t
->Tctime
= st
->st_ctime
;
690 t
->Tmtime
= st
->st_mtime
;
693 if ((ts
= Tsearch(flagsT
, name
)) != NULL
)
694 t
->Tflags
|= ts
->Tflags
;
695 if ((ts
= Tsearch(execT
, name
)) != NULL
) {
696 t
->Texec
= ts
->Texec
;
702 listdir(char *name
, int always
)
703 { /* expand directory */
705 struct dirent
*dentry
;
707 struct direct
*dentry
;
710 char newname
[STRINGLENGTH
], filename
[STRINGLENGTH
];
715 return; /* unreadable: probably protected */
717 p
= name
; /* punt leading ./ and trailing / */
719 if (p
[0] == '.' && p
[1] == '/') {
724 while ((*newp
++ = *p
++) != '\0'); /* copy string */
725 --newp
; /* trailing null */
726 while (newp
> newname
&& newp
[-1] == '/')
727 --newp
; /* trailing / */
729 if (strcmp(newname
, ".") == 0)
730 newname
[0] = 0; /* "." ==> "" */
732 while ((dentry
= readdir(dirp
)) != NULL
) {
733 if (dentry
->d_ino
== 0)
735 if (strcmp(dentry
->d_name
, ".") == 0)
737 if (strcmp(dentry
->d_name
, "..") == 0)
740 (void)snprintf(filename
, sizeof(filename
), "%s/%s",
741 newname
, dentry
->d_name
);
743 (void)strncpy(filename
, dentry
->d_name
,
744 sizeof(filename
) - 1);
745 filename
[sizeof(filename
) - 1] = '\0';
747 listentry(dentry
->d_name
, filename
, newname
, always
);
753 omitanyone(TREE
* t
, void *fv
)
756 if (anyglob(t
->Tname
, filename
))
762 anyglob(char *pattern
, char *match
)
769 while (*m
&& *p
== *m
) {
773 if (*p
== '\0' && *m
== '\0')
786 return (anyglob(p
, ++m
));
789 while (*(++p
) != ']')
793 for (p
= pb
+ 1; p
!= pe
; p
++) {
796 if (p
== pb
&& *m
== '-') {
798 return (anyglob(p
, ++m
));
807 return (anyglob(p
, ++m
));
813 return (anyglob(p
, ++m
));
822 /*****************************************
823 *** R E A D S C A N F I L E ***
824 *****************************************/
827 getscanfile(char *scanfile
)
829 char buf
[STRINGLENGTH
];
831 char fname
[MAXPATHLEN
];
839 TREE
*tmp
, *t
= NULL
;
843 if (scanfile
== NULL
)
844 scanfile
= FILESCANDEF
;
845 (void) sprintf(buf
, FILESCAN
, collname
, scanfile
);
846 if (stat(buf
, &sbuf
) < 0)
848 if ((f
= fopen(buf
, "r")) == NULL
)
850 if ((p
= fgets(buf
, sizeof(buf
), f
)) == NULL
) {
854 if ((q
= strchr(p
, '\n')) != NULL
)
860 if (atoi(p
) != SCANVERSION
) {
864 scantime
= sbuf
.st_mtime
; /* upgrade time is time of supscan,
865 * i.e. time of creation of scanfile */
866 if (newonly
&& scantime
== lasttime
) {
871 while ((p
= fgets(buf
, sizeof(buf
), f
)) != NULL
) {
880 goaway("scanfile format inconsistent");
881 (void) Tinsert(&t
->Texec
, ++p
, FALSE
);
887 ts
.Tflags
|= FBACKUP
;
891 ts
.Tflags
|= FNOACCT
;
893 if ((q
= strchr(p
, ' ')) == NULL
)
894 goaway("scanfile format inconsistent");
898 if ((q
= strchr(p
, ' ')) == NULL
)
899 goaway("scanfile format inconsistent");
903 if ((q
= strchr(p
, ' ')) == NULL
)
904 goaway("scanfile format inconsistent");
908 (void) strunvis(fname
, q
);
912 if (ts
.Tctime
> lasttime
)
915 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
)
916 if ((tmp
= Tsearch(tl
->TLtree
, fname
)) != NULL
)
917 tmp
->Tflags
&= ~FNEW
;
921 if (Tlookup(refuseT
, fname
)) {
925 t
= Tinsert(&listT
, fname
, TRUE
);
927 t
->Tflags
= ts
.Tflags
;
928 t
->Tctime
= ts
.Tctime
;
929 t
->Tmtime
= ts
.Tmtime
;
934 /*******************************************
935 *** W R I T E S C A N F I L E ***
936 *******************************************/
939 chkscanfile(char *scanfile
)
941 char tname
[STRINGLENGTH
], fname
[STRINGLENGTH
];
944 if (scanfile
== NULL
)
945 scanfile
= FILESCANDEF
;
946 (void) sprintf(fname
, FILESCAN
, collname
, scanfile
);
947 (void) sprintf(tname
, "%s.temp", fname
);
948 if (NULL
== (f
= fopen(tname
, "w")))
949 goaway("Can't test scan file temp %s for %s", tname
, collname
);
951 (void) unlink(tname
);
957 makescanfile(char *scanfile
)
959 char tname
[STRINGLENGTH
], fname
[STRINGLENGTH
];
960 struct timeval tbuf
[2];
961 FILE *scanF
; /* output file for scanned file list */
963 if (scanfile
== NULL
)
964 scanfile
= FILESCANDEF
;
965 (void) sprintf(fname
, FILESCAN
, collname
, scanfile
);
966 (void) sprintf(tname
, "%s.temp", fname
);
967 scanF
= fopen(tname
, "w");
970 if (fprintf(scanF
, "V%d\n", SCANVERSION
) < 0)
972 if (Tprocess(listT
, recordone
, scanF
) != SCMOK
)
974 if (fclose(scanF
) != 0)
976 if (rename(tname
, fname
) < 0) {
978 goaway("Can't change %s to %s", tname
, fname
);
980 tbuf
[0].tv_sec
= time((time_t *) NULL
);
982 tbuf
[1].tv_sec
= scantime
;
984 (void) utimes(fname
, tbuf
);
987 goaway("Can't write scan file temp %s for %s", tname
, collname
);
991 recordone(TREE
* t
, void *v
)
995 char fname
[MAXPATHLEN
* 4 + 1];
996 strvis(fname
, t
->Tname
, VIS_WHITE
);
998 char *fname
= t
->Tname
;
1001 if (t
->Tflags
& FBACKUP
)
1002 if (fprintf(scanF
, "B") < 0)
1004 if (t
->Tflags
& FNOACCT
)
1005 if (fprintf(scanF
, "N") < 0)
1007 if (fprintf(scanF
, "%o %d %d %s\n",
1008 t
->Tmode
, t
->Tctime
, t
->Tmtime
, fname
) < 0)
1010 return Tprocess(t
->Texec
, recordexec
, scanF
);
1014 recordexec(TREE
* t
, void *v
)
1018 char fname
[MAXPATHLEN
* 4 + 1];
1019 strvis(fname
, t
->Tname
, VIS_WHITE
);
1021 char *fname
= t
->Tname
;
1023 if (fprintf(scanF
, "X%s\n", fname
) < 0)
1029 cdprefix(char *prefix
)
1031 static char *curprefix
= NULL
;
1033 if (curprefix
== NULL
) {
1036 (void) chdir(prefix
);
1040 if (prefix
== NULL
) {
1041 (void) chdir(basedir
);
1045 if (prefix
== curprefix
)
1047 if (strcmp(prefix
, curprefix
) == 0) {
1051 (void) chdir(basedir
);
1052 (void) chdir(prefix
);