# to unbundle, sh this file (in an empty directory) echo ftw.body 1>&2 sed >ftw.body <<'//GO.SYSIN DD ftw.body' 's/^-//' -/* This is the real ftw source code. It assumes that the following - * includes have been given: - * - * #include - * #include - * #include "ftw.h" - * - * Struct FTW (whose definition starts at the end of ftw.h) must - * must include at least the integers quit, base, and level. - */ - -#define FTW_PATHLEN0 1000 -#define FTW_PATHINC 1000 -#ifndef S_IFLNK -#define lstat stat -#endif -#ifdef S_IFSOCK -#include -#else -#ifdef S_ICCTYP -#include "ndir.h" -#else -#include "dirent.h" -#define direct dirent -#endif -#endif -#ifndef ENOMEM -#include -#endif - - extern int errno; - -/* - * Each generation of ftw1 (the real ftw) allocates one copy, R, of the - * following structure; it passes a pointer to this structure when it - * recursively invokes itself. These structures are chained together, - * so that if it becomes necessary to recycle file descriptors, then - * the oldest descriptor (the one at the shallowest depth still open) - * can be recycled. - */ - - struct FTW_rec { - struct FTW_rec *prev; - long here; /* seek to here when reopening at this level */ - DIR *fd; /* file descriptor at this level */ - }; - -/* - * One instance, T, of the following structure is allocated by ftw; a - * pointer to it is passed to all generations of ftw1 (the real ftw). - * T could often be a global variable, but this way the parameter fn - * can invoke ftw for an independent tree walk. - * Component T->path points to storage for the object path-names; - * this storage may be relocated by realloc if T->path needs to be - * more than T->pathlast characters long. - * T->path[T->pathnext] is the next free character in the pathnames. - * T->depth = parameter depth to ftw. T->lastout is the deepest level at - * which a file descriptor has been recycled. - */ - - struct FTW_top { - int (*fn)(); - char *path; - unsigned pathlast, pathnext; - int lastout; - int depth; - }; - -static ftw_1_(); - -int -ftw (path, fn, depth) - char *path; - int (*fn)(); - int depth; -{ - struct FTW_top T; - struct FTW_rec R; - struct FTW S; - int rc; - char *malloc(), *strcpy(); - - T.depth = depth; - T.lastout = -1; - T.fn = fn; - S.quit = 0; - S.level = -1; - - /* initialize S.base, T.pathnext... */ - { - register char c, *p, *q; - for (p = q = path; c = *p; p++) if (c == '/') q = p + 1; - S.base = q - path; - T.pathnext = p - path; - } - - T.pathlast = T.pathnext + FTW_PATHLEN0; - T.path = malloc(T.pathlast); - if (!T.path) { errno = ENOMEM; return -1; } - strcpy(T.path, path); - rc = ftw_1_(&R, &T, 0, &S); - free(T.path); - return rc; -} - -static int -ftw_1_ (R, T, level, S1) - register struct FTW_rec *R; - register struct FTW_top *T; - int level; - struct FTW *S1; -{ - int rc, n; - DIR *fd; - struct direct *dirp; - char *component, *path; - struct stat sb; - struct FTW_rec mr; - unsigned nextsave; - struct FTW S; - char *realloc(); - long lseek(); - - mr.prev = R; - path = T->path; - S.level = level; - S.quit = 0; - S.base = S1->base; - - /* Try to get file status. If unsuccessful, errno will say why. */ - if (lstat(path, &sb) < 0) { - rc = (*T->fn) (path, &sb, FTW_NS, &S); - S1->quit = S.quit; - return rc; - }; - - /* - * The stat succeeded, so we know the object exists. - * If not a directory, call the user function and return. - */ -#ifdef S_IFLNK - if ((sb.st_mode & S_IFMT) == S_IFLNK) { - rc = (*T->fn) (path, &sb, FTW_SL, &S); - S1->quit = S.quit; - if (rc || S.quit == FTW_SKR) return rc; - if (S.quit != FTW_FOLLOW) return 0; - S1->quit = S.quit = 0; - if (stat(path, &sb) < 0) { - rc = (*T->fn) (path, &sb, FTW_NSL, &S); - S1->quit = S.quit; - return rc; - }; - } -#endif - - if ((sb.st_mode & S_IFMT) != S_IFDIR) { - rc = (*T->fn) (path, &sb, FTW_F, &S); - S1->quit = S.quit; - return rc; - } - - /* - * The object was a directory. - * - * Open a file to read the directory - */ - mr.fd = fd = opendir(path); - - /* - * Call the user function, telling it whether - * the directory can be read. If it can't be read - * call the user function or indicate an error, - * depending on the reason it couldn't be read. - */ - if (!fd) { - rc = (*T->fn) (path, &sb, FTW_DNR, &S); - S1->quit = S.quit; - return rc; - } - - /* We could read the directory. Call user function. */ - rc = (*T->fn) (path, &sb, FTW_D, &S); - if (rc != 0) - goto rtrn; - if (S.quit == FTW_SKD) goto rtrn; - if (S.quit == FTW_SKR) {S1->quit = FTW_SKR; goto rtrn;} - - /* Make sure path is big enough to hold generated pathnames. */ - - n = nextsave = T->pathnext; - if (n + MAXNAMLEN + 1 >= T->pathlast) { - T->pathlast += FTW_PATHINC; - path = T->path = realloc(T->path, T->pathlast); - if (!path) { - errno = ENOMEM; - rc = -1; - goto rtrn; - } - } - - /* Create a prefix to which we will append component names */ - - if (n > 0 && path[n-1] != '/') path[n++] = '/'; - component = path + n; - - /* - * Read the directory one component at a time. - * We must ignore "." and "..", but other than that, - * just create a path name and call self to check it out. - */ - while (dirp = readdir(fd)) { - if (dirp->d_ino != 0 - && strcmp (dirp->d_name, ".") != 0 - && strcmp (dirp->d_name, "..") != 0) { - int i; - struct FTW_rec *pr; - - /* Append the component name to the working path */ - strcpy(component, dirp->d_name); - T->pathnext = n + strlen(dirp->d_name); - - /* - * If we are about to exceed our depth, - * remember where we are and close the file. - */ - if (level - T->lastout >= T->depth) { - pr = &mr; - i = T->lastout++; - while (++i < level) pr = pr->prev; - pr->here = telldir(pr->fd); - closedir(pr->fd); - } - - /* - * Do a recursive call to process the file. - */ - S.quit = 0; - S.base = n; - rc = ftw_1_(&mr, T, level+1, &S); - if (rc != 0 || S.quit == FTW_SKR) { - if (level > T->lastout) closedir(fd); - T->pathnext = nextsave; - return rc; - } - - /* - * If we closed the file, try to reopen it. - */ - if (level <= T->lastout) { - char c = path[nextsave]; - path[nextsave] = 0; - T->lastout = level - 1; - mr.fd = fd = opendir(path); - if (!fd) { - rc = (*T->fn) (path, &sb, FTW_DNR, &S); - S1->quit = S.quit; - T->pathnext = nextsave; - return rc; - } - path[nextsave] = c; - seekdir(fd, mr.here); - } - } - } - T->pathnext = nextsave; - path[nextsave] = 0; - - /* - * We got out of the subdirectory loop. Call the user - * function again at the end and clean up. - */ - - rc = (*T->fn) (path, &sb, FTW_DP, &S); - S1->quit = S.quit; -rtrn: - closedir(fd); - return rc; -} //GO.SYSIN DD ftw.body echo ftw.c 1>&2 sed >ftw.c <<'//GO.SYSIN DD ftw.c' 's/^-//' -/* - * ftw - file tree walk - * - * int ftw (path, fn, depth) char *path; int (*fn)(); int depth; - * - * Given a path name, ftw starts from the file given by that path - * name and visits each file and directory in the tree beneath - * that file. If a single file has multiple links within the - * structure, it will be visited once for each such link. - * For each object visited, fn is called with four arguments. - * The fourth can often be ignored; it is a pointer, say S, - * declared "struct FTW *S", discussed in more detail below. - * The first contains the path name of the object, the second - * contains a pointer to a stat buffer which will usually hold - * appropriate information for the object and the third contains - * an integer value giving additional information about the - * object, as follows: - * - * FTW_F The object is a file for which stat was - * successful. It does not guarantee that the - * file can actually be read. - * - * FTW_D The object is a directory for which stat and - * open for read were both successful. This is - * a preorder visit -- objects in the directory - * are yet to be visited. - * - * FTW_DNR The object is a directory for which stat - * succeeded, but which cannot be read. Because - * the directory cannot be read, fn will not be - * called for any descendants of this directory. - * - * FTW_DP The object is a directory for which stat and - * open for read were both successful. This is - * a postorder visit -- everything in the directory - * has already been visited. - * - * FTW_NS Lstat failed on the object. If errno is EACCES, - * then the failure stems from lack of - * appropriate permission. This indication will - * be given, for example, for each file in a directory - * with read but no execute permission. Whenever - * stat fails, it is not possible to determine - * whether this object is a file or a directory. - * The stat buffer passed to fn will contain garbage. - * - * FTW_SL The object is a symbolic link. Set S->quit - * (a component of the structure pointed to by - * the fourth parameter to fn) to FTW_FOLLOW to - * have the link followed and the object to which - * it points visited. - * - * FTW_NSL Lstat succeeded, but stat failed on the object. - * This is only possible when following a symbolic - * link. - * - * Among the components of the structure to which the fourth - * parameter, S, to fn points is S->quit. If the caller sets - * S->quit to FTW_SKR, then no more files in the current directory - * will be visited. (The current directory is the one containing - * the object being visited.) If the third parameter to fn is - * FTW_D and the caller sets S->quit to FTW_SKD, then this directory - * (the one named in the first parameter to fn) will be skipped. - * - * Other components pointed to by the fourth parameter S are - * the current recursion level S->level (top level = 0) and - * the offset S->base in the pathname of the current object - * (the first parameter to fn) of the object's base name. - * By expanding the definition of struct FTW given below and - * including the files included below, one can arrange for - * S to point to a larger structure, components of which can - * be initialized (for example) on calls to fn with third - * parameter FTW_D. - * - * If fn returns nonzero, ftw stops and returns the same value - * to its caller. Ftw only initiates a nonzero return if malloc - * fails; in this case ftw sets errno to ENOMEM and returns -1. - * - * The third argument to ftw does not limit the depth to which - * ftw will go. Rather, it limits the depth to which ftw will - * go before it starts recycling file descriptors. In general, - * it is necessary to use a file descriptor for each level of the - * tree, but they can be recycled for deep trees by saving the position, - * closing, re-opening, and seeking. It is possible to start - * recycling file descriptors by sensing when we have run out, but - * in general this will not be terribly useful if fn expects to be - * able to open files. We could also figure out how many file descriptors - * are available and guarantee a certain number to fn, but we would not - * know how many to guarantee, and we do not want to impose the extra - * overhead on a caller who knows how many are available without - * having to figure it out. - * - * It is possible for ftw to die with a memory fault in the event - * of a file system so deeply nested that the stack overflows. - */ - -#include -#include -#include "ftw.h" -#include "ftw.body" //GO.SYSIN DD ftw.c echo ftw.h 1>&2 sed >ftw.h <<'//GO.SYSIN DD ftw.h' 's/^-//' -/* - * Codes for the third argument to the user-supplied function - * which is passed as the second argument to ftw... - */ - -#define FTW_F 0 /* file */ -#define FTW_D 1 /* directory */ -#define FTW_DNR 2 /* directory without read permission */ -#define FTW_NS 3 /* unknown type, stat failed */ -#define FTW_DP 4 /* directory, postorder visit */ -#define FTW_SL 5 /* symbolic link */ -#define FTW_NSL 6 /* stat failed (errno = ENOENT) on symbolic link */ - -/* Values the user-supplied function may wish to assign to - component quit of struct FTW... - */ - -#define FTW_SKD 1 /* skip this directory (2nd par = FTW_D) */ -#define FTW_SKR 2 /* skip rest of current directory */ -#define FTW_FOLLOW 3 /* follow symbolic link */ - -struct FTW { int quit, base, level; -#ifndef FTW_more_to_come - }; -#endif //GO.SYSIN DD ftw.h echo getpats.c 1>&2 sed >getpats.c <<'//GO.SYSIN DD getpats.c' 's/^-//' -#include "patlist.h" - -void -getpats(argc, argv, keep, drop) -int argc; -char **argv; -struct patlist **keep, **drop; -{ -int i, j, m; -char c, *malloc(), *s, *t; -struct patlist *d, *k, **p, *q; - -d = (struct patlist *) drop; -k = (struct patlist *) keep; -for (i = 1; i < argc; i++) { - s = argv[i]; - for (j = 0;;) { - for (; (c=s[j]) == ' ' || c == '\t' || c == '\n'; j++); - if (!c) break; - if (c == '!') {p = &d; s++;} - else p = &k; - for (m = j; (c=s[m]) && c != ' ' && c != '\t' && - c != '\n'; m++); - if (m == j) continue; - *p = (*p)->nextpat = q = (struct patlist *) - malloc((unsigned) (m-j+sizeof(struct patlist))); - t = q->namepat; - while (j < m) *t++ = s[j++]; - *t = 0; - } - } -d->nextpat = k->nextpat = 0; -} - -int -skipit(name, keep, drop) -char *name; -struct patlist *keep, *drop; -{ -if (!keep) goto kept; -for (; keep; keep = keep->nextpat) - {if (match(keep->namepat, name)) goto kept;} -return 1; - -kept: -for (; drop; drop = drop->nextpat) - {if (match(drop->namepat, name)) return 1;} -return 0; -} //GO.SYSIN DD getpats.c echo makefile 1>&2 sed >makefile <<'//GO.SYSIN DD makefile' 's/^-//' -.SUFFIXES: .c .o -u = ustree.o -p = prune.o -m = getpats.o match.o -h = patlist.h -mtest = matchtst.o match.o -g = -O -s = stree.o ftw.o -b = ftw.body ftw.c ftw.h getpats.c makefile match.c matchtst.c ok.c \ - patlist.h prune.c stree.1 stree.c ustree.c - -all: stree ustree prune -stree: $s $m $h - cc $g -o stree $s $m -stree.o: stree.c ok.c - cc -c $g stree.c -ustree: $u $m $h - cc $g -o ustree $u $m -prune: $p $m - cc $g -o prune $p $m -mtest: $(mtest) - cc $g -o mtest $(mtest) -lint: - lint stree.c getpats.c match.c ftw.c -stree.t: stree.1 - troff -man stree.1 >stree.t -.c.o: $h - cc -c $g $*.c - -bundle: $b - stree -u $b >bundle -clean: - rm -f *.[to] bundle //GO.SYSIN DD makefile echo match.c 1>&2 sed >match.c <<'//GO.SYSIN DD match.c' 's/^-//' -#include -int -match(pat, s) /* return 1 if s matches pat, 0 ow; match in the sense of */ -char *pat, *s; /* UNIX file names, plus ``~'' = empty | '/' | '/'.*'/' */ -{ /* with `empty' possible only at ends. */ - -#define STMAX 50 -int i, j, k, l, pst[STMAX], sst[STMAX]; -char c, clast, p, tst[STMAX]; - -k = -1; -c = '/'; -for(i=j=0;; j++){ - p = pat[j]; - clast = c; - c = s[i]; - switch(p) { - case '?': - if (!c || c == '/' || c == '.' && clast == '/') goto fail; - i++; break; - - case '[': - if (c == '/') goto fail; - for (l = ++j; (p = pat[l]) != ']'; l++) { - if (!p) {fprintf(stderr, - "match: missing ``]'' in ``%s''\n", - pat); - exit(1); - } - } - if (pat[j] == '^') { - for (j++; j < l; j++) { - p = pat[j]; - if (p == c) goto fail; - if (pat[j+1] == '-' && j+2 < l) { - j += 2; - if ((c >= p && c <= pat[j]) || - (c <= p && c >= pat[j])) goto fail; - } - } - if (!c || clast == '/' && c == '.') goto fail; - goto havit; - } - else for (; j < l; j++) { - p = pat[j]; - if (p == c) goto havit; - if (pat[j+1] == '-' && j+2 < l) { - j += 2; - if ((c >= p && c <= pat[j]) || - (c <= p && c >= pat[j])) goto havit; - } - } - goto fail; - - havit: j = l; - i++; break; - - case '~': - if (!pat[j+1] && (c == '/' || !c)) return 1; - if (j > 0 && c != '/') goto fail; - if (c == '/') i++; - else c = '/'; - if (++k >= STMAX) { -toomany: - fprintf(stderr, - "match: more than %d *'s and ~'s in ``%s''\n", - STMAX, pat); - exit(1); - } - pst[k] = j; - sst[k] = i; - tst[k] = '~'; - break; - - case '*': - if (++k >= STMAX) goto toomany; - pst[k] = j; - sst[k] = i; - tst[k] = '*'; - break; - - default: - if (p != c) goto fail; - if (!c) return 1; - i++; break; - - fail: - if (k < 0) return 0; - j = pst[k]; - i = sst[k]++; - c = s[i++]; - if (!c) {--k; goto fail;} - if (tst[k] == '~') { - while (c = s[i++]) {if (c == '/') goto loop;} - --i; - } - else if (c == '/') {--k; goto fail;} - } -loop:; - } -} //GO.SYSIN DD match.c echo matchtst.c 1>&2 sed >matchtst.c <<'//GO.SYSIN DD matchtst.c' 's/^-//' -#include - -main(){ -char s[256], pat[256]; -int i; - -while(fgets(pat,sizeof(pat),stdin)) { - if ((i = strlen(pat)) && pat[--i] == '\n') - pat[i] = 0; - printf("pattern ``%s''\n", pat); - for(;;){ - if (!fgets(s,sizeof(s),stdin)) exit(0); - if ((i = strlen(s)) && s[--i] == '\n') - s[i] = 0; - if (!s[0]) break; - printf("``%s'' %s\n", s, match(pat,s) ? "matches" : "fails"); - } - } -exit(0); -} //GO.SYSIN DD matchtst.c echo ok.c 1>&2 sed >ok.c <<'//GO.SYSIN DD ok.c' 's/^-//' -/* #include */ -#include -#include - -#define OK return(1) -#define NOT_OK return(0) -int -ok(file) -char *file; -{ - char *s; - register char *Buf = buf; - - s = strrchr(file,'/'); if (!s) s = file - 1; - if(!strcmp(s+1,"core")){ NOT_OK; } - if (in <= 1) OK; - if( (((unsigned char)Buf[0])==247) && (Buf[1]==2) ) NOT_OK; /* Tex dvi */ - -#ifdef ZMAGIC -#ifdef mips - switch(*(unsigned short *)Buf) { - case 0x0160: - case 0x0162: - case 0x6001: - case 0x6201: - case 0x0180: - case 0x0182: - case 070707: /* cpio data */ - case 0135246: /* andrew/ehg */ /* view2d input file */ - case 0135256: /* andrew */ /* apl file */ - case 0164200: /* td */ /* Lucasfilm picture */ - NOT_OK; - } - if(Buf[0] == 0x7f && Buf[1] == 'E' && Buf[2] == 'L' && Buf[3] == 'F') - NOT_OK; /* ELF object */ - if( !strncmp(Buf, "!\n",8) && - (Buf[8] == '/' /* ELF object file archive */ - || !strncmp(Buf+8,"__________EBEB_ ", 16))) /* random archive */ - NOT_OK; - if( !strncmp(Buf, "#!/bin/echo ", 12) /* cyntax object file */ ) NOT_OK; -#else -#ifdef sun - switch(*(int *)Buf & 0xffff) { -#else - switch(*(int *)Buf) { - case 0600560: /* mux downloadable file */ -#endif - case 0413: /* demand paged */ - case 0410: /* pure */ - case 0411: /* executable */ - case 0407: /* executable */ - case 0406: /* mpx 68000 */ - case 0177555: /* very old archive */ - case 0177545: /* old archive */ - case 070707: /* cpio data */ - case 0135246: /* andrew/ehg */ /* view2d input file */ - case 0135256: /* andrew */ /* apl file */ - case 0164200: /* td */ /* Lucasfilm picture */ - NOT_OK; - } - if( !strncmp(Buf, "!\n__.SYMDEF", 17) /* archive random */ ) NOT_OK; - if( !strncmp(Buf, "#!/bin/echo ", 12) /* cyntax object file */ ) NOT_OK; -#endif -#else -#ifdef __hpux - switch(((MAGIC *)Buf)->file_type) { - case RELOC_MAGIC: - case SHARE_MAGIC: - case EXEC_MAGIC: - NOT_OK; - } - if( !strncmp(Buf, "!\n/ ", 24) /* archive random */ ) - NOT_OK; -#else -/* # if u3b || vax || M32 || u3b15 || u3b5 || u3b2 */ - switch(*(short *)Buf) { - case 0407: /* pdp11/pre System V vax executable */ - case 0401: /* unix-rt ldp */ - case 0405: /* pdp11 overlay */ - case 0410: /* pdp11/pre System V vax pure executable */ - case 0411: /* pdp11 separate I&D */ - case 0437: /* pdp11 kernel overlay */ - case 0570: /* vax executable */ - case 0575: /* vax pure executable */ - case 0502: /* basic-16 executable */ - case 0503: /* basic-16 executable (TV) */ - case 0510: /* x86 executable */ - case 0511: /* x86 executable (TV) */ - case 0520: /* mc68k executable */ - case 0521: /* mc68k executable */ - case 0522: /* mc68k executable (shared) */ - case 0550: /* 3b20 executable */ - case 0551: /* 3b20 executable (TV) */ - case 0560: /* WE32000 executable */ - case 0561: /* WE32000 executable (TV) */ - NOT_OK; - } - if ( !strncmp(Buf,"!\n",8) || !strncmp(Buf,"",4) ) NOT_OK; -#endif -#endif - if ( troffint(Buf,in) /* new troff intermediate format */ ) NOT_OK; - if ( Buf[0]=='\100' && Buf[1]=='\357' ) - /* troff CAT output */ NOT_OK; - if (in >= 32 && Buf[0] == 0 && Buf[1] == 0 && Buf[3] == 7 - && (Buf[2] == 1 || Buf[2] == 4)) - NOT_OK; /* plan9 executables */ - OK; -} - -troffint(buf, n) /* taken from file.c and made stricter */ -register char *buf; -register n; -{ - register i, k; - - i = 0; - for (k = 0; k < 8 && i < n; k++, i++) { - if (buf[i] == 'x' && buf[i+1] == ' ') { - i += 2; - if (!strncmp(buf+i, "T ", 2) - || !strncmp(buf+i, "init\n", 5) - || !strncmp(buf+i, "font ", 5) - || !strncmp(buf+i, "res ", 4)) - /* x [T|init|font|res] */ - while (++i < n && buf[i] != '\n') ; - else return 0; - } - else if (buf[i] == 'f' || buf[i] == 's' || - buf[i] == 'p' || buf[i] == 'V' || buf[i] == 'H') { - /* p1 | f2 | s3 | V4... */ - while (++i < n && buf[i] != '\n') - if (!isdigit(buf[i])) return 0; - } - else - return k >= 4; - } - return 1; -} - -lookup(I, tab) -int *I; -register char *tab[]; -{ - register char *B, *b, r, *s; - static char goodnext[256]; - - if (!goodnext[' ']) - for(s = " \t\n\t{/"; r = *s++;) goodnext[r] = 1; - - B = buf + *I; - while((r = *B) == ' ' || r == '\t' || r == '\n') B++; - if (r == 'p' && (B == buf || B[-1] == '\n')) { - while((r = *++B) >= '0' && r <= '9'); - if (r == '\n') { *I = B - buf; return 1; } - return 0; - } - while(s = *tab++) { - for(b = B; (r = *s++) && r == *b; b++); - if (!r && goodnext[*b]) { *I = b - buf; return 1; } - } - return 0; - } //GO.SYSIN DD ok.c echo patlist.h 1>&2 sed >patlist.h <<'//GO.SYSIN DD patlist.h' 's/^-//' -struct patlist { - struct patlist *nextpat; - char namepat[1]; - }; //GO.SYSIN DD patlist.h echo prune.c 1>&2 sed >prune.c <<'//GO.SYSIN DD prune.c' 's/^-//' -/* This reads output from stree on stdin and passes a subset to stdout. - Invocation is: prune pat1 pat2 ... - Arguments to prune are pathname shell pathname patterns, possibly - preceded by a ! . The patterns may also contain the metacharacter ~ , - which matches an arbitrary string that must start with / unless ~ is - at the beginning of the pattern and must end with / unless ~ is at - the end of the pattern. - An excluding pattern is one that starts with ! , a selecting - pattern one that does not. A file is passed to stdout if its pathname - matches one of the selecting patterns but not one of the excluding ones. - Giving no selecting patterns is the same as giving the single selecting - pattern ~ (which matches all names). -*/ -#include -#include "patlist.h" -#define BUFLEN 800 - -main(argc, argv) -int argc; -char **argv; -{ -struct patlist *drop, *keep; -int copy, endup, i; -char buf[BUFLEN+2], c; - -endup = copy = 0; -buf[BUFLEN] = '\n'; -buf[BUFLEN+1] = 0; -getpats(argc, argv, &keep, &drop); -while(fgets(buf, BUFLEN, stdin)) { - endup = 0; - if (!strncmp(buf, "./ ADD NAME=", 12)) { - for (i=12; (c=buf[i]) && c != ' ' && c != '\n'; i++); - buf[i] = 0; - copy = !skipit(buf+12, keep, drop); - buf[i] = c; - } - else if (!strncmp(buf, "./ ENDUP", 8)) {copy = 0; endup = 1;} - if (copy) fputs(buf, stdout); - } -if (!endup) fprintf(stderr, "./ ENDUP line added\n"); -fputs(endup ? buf : "./ ENDUP\n", stdout); -} - -fputs(s, f) -char *s; -FILE *f; -{ - register char *s1 = s; - while(*s1++ != '\n'); - fwrite(s, s1-s, 1, f); - } //GO.SYSIN DD prune.c echo stree.1 1>&2 sed >stree.1 <<'//GO.SYSIN DD stree.1' 's/^-//' -.if n .ds ~ ~ -.if t .ds ~ \v'7p'\s+6~\s-6\v'-7p' -.TH STREE 1 -.SH NAME -stree, ustree, prune \(em collapse (restore) file trees into (from) a single file -.SH SYNOPSIS -.B stree -[\fIoptions\fR] \fIfile1\fR [\fIoptions\fR] \fIfile2\fR ... -.br -.B ustree -[!]\fIpat1 \fR[!]\fIpat2 \fR... -.br -.B prune -[!]\fIpat1 \fR[!]\fIpat2 \fR... -.SH DESCRIPTION -.I Stree -copies the -.IR file s -(or, in the case of directories, -all descendants) to the standard output, -along with control information. -Piping the output of -.I stree -into -.I ustree -will recreate the files, -restoring the last-modified date -and executable mode and honoring link information saved by stree. -.PP -Executable and troff output files, files named "core", and -archives on which ranlib has been run -are skipped by default; shell scripts and packed and encrypted files, however, -.I are -saved. When it encounters a symbolic -link, stree merely records the name of the file to which the -link points. Options for stree: -.TP -.BI "\-a\ " oldfile -Save only files with \fImtime\fR newer than that of \fIoldfile\fR. -.BI "\-A\ " oldfile -Save only files with \fIctime\fR newer than that of \fIoldfile\fR. -(The \fIctime\fR changes whenever one changes a file or -its permissions or \fImtime\fR; it cannot be faked. -The \fImtime\fR changes whenever -the bits in the file change, but it can also be set -arbitrarily, e.g. to an old value when a file is restored.) -.TP -.BI "\-b\ " nnn -Save only files of length < \fInnn\fR. (\fInnn\fR may include a -trailing k to denote kilobytes, m to denote megabytes, or b -to denote 512-byte blocks.) -.TP -.B \(pll -(default) Save the text of a file (inode) the first time it is seen; -if it is seen again, record only that it should be linked to its -first occurrence. -.TP -.B \-l -Ignore link information: save the text of a file each time it is seen. -.TP -.B \(plo -(default) Apply default omission tests described above. -.TP -.B \-o -Skip the default omission tests; honor only -the !\fIpat\fR omissions described below. -.TP -.B \-u -save files in -.IR bundle (1) -format. -.TP -.BI ! pat -Do not save files whose name matches \fIpat\fR. The pattern \fIpat\fR -may contain shell metacharacters (\(**, ?, and [...], which may have to -be quoted). It may also contain the metacharacter \*~ described below. -.PP -!\fIpat\fR options are global; the others apply when seen. -Quoted white space is treated as though unquoted: the single -argument \(fm!\*~\(**.o !\*~junk\(fm is treated the same as the two -arguments \(fm!\*~\(**.o\(fm and \(fm!\*~junk\(fm . -.PP -\*~ works as follows: at the beginning of a pattern, -\*~ matches the null string and strings that end with / ; in the middle -of a pattern, \*~ matches an arbitrary string -that starts and ends with / ; at the end of a pattern, \*~ matches the -null string and strings that start with / . Examples: -.in +.3i -.ll -.3i -.sp .5 - \(fm\*~nohup.out\(fm -matches \(fmnohup.out\(fm, \(fmtest/nohup.out\(fm, and -\(fm/usr/me/src/nohup.out\(fm, but matches neither \(fmxnohup.out\(fm -nor \(fm/tmp/ynohup.out. -.sp .5 - \(fmdoc\*~junk\(fm matches \(fmdoc/junk\(fm and -\(fmdoc/some/old/junk\(fm, but not \(fmdoc1/junk\(fm. -.sp .5 - \(fmzap\*~\(fm matches \(fmzap\(fm, -\(fmzap/zip\(fm, and \(fmzap/zot/zork\(fm but not \(fmzap1\(fm. -.sp .5 - \(fm\*~*out\*~\(fm matches \(fmout\(fm, \(fmtest/nohup.out\(fm, -\(fmcur/testout/stuff\(fm, but not \(fmout1\(fm. -.ll -.in -.PP -The format was chosen so that normally all characters are printing ascii -characters and the file contents are unchanged. -ASCII escape and newline characters are added -as necessary to make each file appear to be a sequence of reasonably -sized lines, none starting with "./"; -but if any escapes have to be added, a warning message is printed. -The control lines are of the form: -.br - ./ ADD NAME=pathname/of/file TIME=123456789 X -.br - ./ ADD NAME=pathname/of/file LINK=pathname/to/link/to -.br - ./ ADD NAME=pathname/of/file SLNK=pathname/to/link/to/symbolically -.br -and -.br - ./ ENDUP -.br -This format was chosen for compatibility with -the IEBUPDTE utility on some IBM systems. -.PP -.B Ustree -reads stree output on its standard input and restores -some or all of the files it sees. The arguments -.RI [!] pat1 ", [!]" pat2 ", ..." -select the files to be restored. Patterns preceded by ! are -\fIrejecting\fR patterns; the others are \fIselecting\fR patterns. -These patterns are like the \fIpat\fR in !\fIpat\fR arguments to \fIstree\fR. -Only files whose names match at least one selecting pattern and do not match -any rejecting patterns are restored. If there are no selecting patterns, -then the selecting pattern \*~ (matching all filenames) is assumed. -.PP -.B Prune -is like ustree, except that it passes selected files to its standard -output (in \fIstree\fR format) instead of restoring them. -.SH "SEE ALSO" -.IR tar (1), -.IR cpio (1) //GO.SYSIN DD stree.1 echo stree.c 1>&2 sed >stree.c <<'//GO.SYSIN DD stree.c' 's/^-//' -/* version of stree that saves link information by adding LINK= or - SLNK= field after NAME= field of ./ ADD line. LINK is for ``hard'' - links, SLNK for symbolic links. - options: - -a limitfile copy only files modified after limitfile. - -A limitfile (for wmc) like -a, but times are ctime rather than mtime. - -b limitsize copy only files smaller than limitsize. - +l (default) supply LINK field, don't copy file. - -l do not supply LINK field and copy entire file instead. - +o (default) apply normal omission tests (ok()) - -o consider all files ok - ! skip files whose names match pat - -u use Unix "bundle" format for output - - Options and pathnames can be intermixed. ! options are global: they - apply to all files; the other options take effect when seen. - Patterns are the same as for the shell, with the addition of the - metacharacter ~ , which matches an arbitrary string that must start - with / unless ~ is at the beginning of the pattern and must end with / - unless ~ is at the end of the pattern. - - Last changed Sun Aug 9 18:59:40 EDT 1987. - This was written at AT&T Bell Laboratories by David Gay and Eric Grosse for - personal use. It is not copyrighted, and people may copy it at will - if they retain this notice. This program is not guaranteed either! But - since we depend on it for our own backups, we would welcome bug reports - (to research!dmg or research!ehg). - -*/ - -#include -#include -#include -#include "patlist.h" -#include "ftw.h" -#include "string.h" - -struct stat stbuf; -struct patlist *drop; - -char buf[BUFSIZ]; -char *progname; -int copyall; /* -o option: omit ok() checks */ -int bundle, nla, nonascii, npa; /* counters for funny characters in bundle */ -int linemax; -int in; /* number of chars in buf */ -ino_t outino; /* inode number of output file */ -dev_t outdev; /* device numbers of output file */ -int escap; /* number of escapes added on output */ -int Linkct; /* number of links seen more than once */ -int Slnkct; /* number of symbolic nondirectory links saved */ -int Slnkdir; /* number of symbolic directories skipped */ -int abspath; /* number of absolute path names saved */ -#define MAXLEVEL 96 -#define isdir(b) (((b).st_mode & S_IFMT) == S_IFDIR) -int ifile; -extern void exit(); - -off_t bsize; - -long thresh; /* "old file" time threshold */ -long cthresh; /* "old file" time for -A */ -#ifndef ESC -#define ESC '\033' /* ascii escape character */ -#endif -#ifndef MLINLEN /* max line length, plus or minus a few */ -#define MLINLEN 590 -#endif - -#define IHTABLEN 287 -#define MAXNAME 1023 - -char linkname[MAXNAME+1]; - -struct inodetab { - struct inodetab *nextint; - ino_t i_st_ino; - dev_t i_st_dev; - char iname[1]; - }; - -struct inodetab *ihtab[IHTABLEN]; -char Link = 'o'; - -options(rc) -{ - static char *opt[] = { - "-a limitfile {copy only files modified after limitfile}", - "-A limitfile {like -a, but relative to ctime rather than mtime}", - "-b limitsize {copy only files smaller than limitsize}", - "+o {(default) apply normal omission checks\n\ - (to exclude object files, binary executables, etc.)}", - "-o {omit normal omission checks:\n\ - only exclude files matching !'s}", - "+l {(default) supply LINK fields instead of\n\ - making new copies of linked files}", - "-l {copy entire file rather than supply LINK field}", - "! {(global) skip files whose names match pat}", - "-u {(global) use \"bundle\" format for output}", - 0}; - char **o = opt; - fprintf(stderr, - "usage: %s [options] file1 [[options] file2 ...]\noptions:\n", - progname); - while(*o) fprintf(stderr, "\t%s\n", *o++); - exit(rc); - } - -main(argc, argv) - int argc; - char **argv; -{ - char c, *s; - int i; - long time(); - struct patlist *k, *keep; - int copy(), ftw(); - void getpats(); - - if(fstat(fileno(stdout),&stbuf) != 0){ - fprintf(stderr,"%s: can't stat output\n", progname); - exit(1); - } - outino = stbuf.st_ino; - outdev = stbuf.st_dev; - - progname = *argv; - getpats(argc, argv, &keep, &drop); - - for (i=0; inextpat) - if (!strncmp(k->namepat, "-u",2)) { - bundle = 1; - printf("# to unbundle, sh this file (in an empty directory)\n"); - } - for (; keep; keep = keep->nextpat){ - s = keep->namepat; - if ( *s == '-' ) switch(s[1]) { - case 'A': - if (keep = keep->nextpat) { - if (stat(s=keep->namepat,&stbuf)<0) - scream("can't stat %s",s); - cthresh = stbuf.st_ctime; - thresh = 0; - } - else goto missing; - break; - case 'a': - if (keep = keep->nextpat) { - if (stat(s=keep->namepat,&stbuf)<0) - scream("can't stat %s",s); - thresh = stbuf.st_mtime; - cthresh = 0; - } - else { -missing: - fprintf(stderr, "%s: trailing %s ignored\n", progname, s); - goto done; - } - break; - case 'b': - if (!(keep = keep->nextpat)) goto missing; - bsize = 0; - s = keep->namepat; - while(c = *s++) { - if (c >= '0' && c <= '9') bsize = 10*bsize + c - '0'; - else if (c == 'b' || c == 'B') { bsize <<= 9; break; } - else if (c == 'k' || c == 'K') { bsize <<= 10; break; } - else if (c == 'm' || c == 'M') { bsize <<= 20; break; } - else scream("bad limit size in \"-b %s\"", keep->namepat); - } - break; - case 'l': - Link = 0; - break; - case 'o': - copyall = 1; - break; - case 'u': - break; /* already checked for -u */ - case '?': - options(0); - default: - fprintf(stderr, "unknown option %s",s); - options(1); - } - else if (!strcmp(s, "+l")) Link = 'o'; - else if (!strcmp(s, "+o")) copyall = 0; - else { - if(stat(s, &stbuf)) - fprintf(stderr, "%s: can't stat %s\n", progname, s); - else if (ftw(s,copy,15)) exit(1); - } - } - -done: - if (bundle) { - squawk(nla, "newlines added"); - squawk(nonascii, "non-ASCII characters"); - squawk(npa, "non-printing ASCII characters"); - if (linemax > 121) squawk(linemax-1, "characters in longest line"); - } - else { - printf("./ ENDUP\n"); - squawk(escap, "escapes were added"); - } - squawk(Linkct, "files saved as (hard) links"); - squawk(Slnkdir, "directories skipped and saved as symbolic links"); - squawk(Slnkct, "nondirectories saved as symbolic links"); - squawk(abspath, "absolute path names saved"); - exit(0); -} - -squawk(n, msg) -char *msg; -{ if (n) fprintf(stderr, "%s: %d %s\n", progname, n, msg); } - -int -copy(source, sbuf, info, follow) - char *source; - struct stat *sbuf; - int info, *follow; -{ - static int level = 0; - long time; - int n; - - if (sbuf->st_ino == outino && sbuf->st_dev == outdev) { - fprintf(stderr,"skipped {outputfile} %s\n",source); - return 0; - } - - switch(info) { - - case FTW_F: /* ordinary file */ - if (strchr(source,' ')) { - fprintf(stderr, "Skipping \"%s\" because of blanks in name.\n",source); - break; - } - if (skipit(source, (struct patlist *) 0, drop)) break; - if ( (time = sbuf->st_mtime) > thresh && sbuf->st_ctime > cthresh) { - if( (ifile = open(source,0)) < 0) { - fprintf(stderr, "can't open %s\n", source); - break; - } - if ( ( in = read(ifile,buf,BUFSIZ) ) < 0 ) - scream("input error on %s", source); - if (copyall || ok(source)) { - if (!bsize || sbuf->st_size < bsize) - copyfil(sbuf, source, time); - else - fprintf(stderr, "Skipped big (%ld) %s\n", sbuf->st_size, source); - } - else fprintf(stderr,"skipped %s\n",source); - if (close(ifile) < 0) scream("can't close %s", source); - } - break; - - case FTW_D: /* preorder directory visit */ - if (skipit(source, (struct patlist *) 0, drop)) - *follow = FTW_SKD; - else if (++level > MAXLEVEL) - scream("directories nested too deep",""); - if(bundle&&(strcmp(".",source)!=0)) printf("mkdir %s\n",source); - break; - - case FTW_DNR: /* unreadable directory */ - fprintf(stderr,"skipped unreadable directory %s\n",source); - break; - - case FTW_NS: /* unstatable file */ - fprintf(stderr,"skipped unstatable file %s\n", source); - break; - - case FTW_DP: /* postorder directory visit */ - level--; - break; - -#ifdef S_IFLNK - case FTW_SL: /* symbolic link */ - if ( sbuf->st_mtime <= thresh ) break; - if ( sbuf->st_ctime <= cthresh ) break; - if (!stat(source, &stbuf) && isdir(stbuf)) Slnkdir++; - else Slnkct++; - n = readlink(source, linkname, MAXNAME); - if (n > MAXNAME) - scream("Symbolic path to %s too long.\n", source); - linkname[n] = 0; - if(bundle) printf("ln -s %s %s\n", linkname, source); - else printf("./ ADD NAME=%s SLNK=%s\n", source, linkname); - break; -#endif - - default: - fprintf(stderr, "stree: ftw called copy with info = %d\n", - info); - exit(1); - } - return 0; -} - -copyfil( sbuf, source, time ) - char *source; - long time; - struct stat *sbuf; -{ - register int i; - register int j=0; /* number of chars output on current line */ - register int p; - register char *Buf = buf; - char *x; - char *s=source; - int execut; - - if (*source == '/') abspath++; - if (bundle) { - if (linkchk(linkname, sbuf, source)) { - printf("ln %s %s\n", linkname,s); - return; - } - printf("echo %s 1>&2\nsed >%s <<'//GO.SYSIN DD %s' 's/^-//'\n",s,s,s); - execut = sbuf->st_mode & S_IEXEC; - for(i=0; i 0x7f) nonascii++; - } - if (++i >= in) { - if( (in = read (ifile, Buf, BUFSIZ)) < 0) - scream("input error on %s", source); - if(!in && j) /* ensure each file is terminated by a newline */ - { putchar('\n'); nla++; } - i=0; - } - } - printf("//GO.SYSIN DD %s\n",s); - if(execut) printf("chmod +x %s\n",s); - } - - else { /* not bundle */ - printf("./ ADD NAME=%s", source); - if (linkchk(linkname, sbuf, source)) { - printf(" LINK=%s\n", linkname); - return; - } - p = 0; /* p == 1 ==> just saw a . at the start of a line */ - x = (sbuf->st_mode & S_IEXEC) ? " X" : ""; - printf(" TIME=%ld%s\n", time, x); - for(i=0; i MLINLEN-2 ) - { putchar(ESC); putchar('\n'); j = 0; escap++; } - i++; - if( i>=in ){ - if ( (in = read (ifile, Buf, BUFSIZ)) < 0) - scream("input error on %s", source); - if (in == 0 && j) { - /* ensure that each file is terminated by a newline */ - putchar(ESC); putchar('\n'); - escap++; - } - i=0; - } - } - } -} - -scream (s1, s2) - char *s1, *s2; -{ - fprintf (stderr, "stree: "); - fprintf (stderr, s1, s2); - putc ('\n', stderr); - exit(1); -} - -int linkchk(ln,sbuf,source) -char *ln, *source; -struct stat *sbuf; -{ - struct inodetab *p, *p0; - char *malloc(); - int i; - register char c, *s; - - if (!Link) return (0); - if (sbuf->st_nlink < 2) return(0); - p0 = (struct inodetab *) - (((sbuf->st_dev + sbuf->st_ino) % IHTABLEN) + ihtab); - while ((p = p0->nextint) && - (p->i_st_ino != sbuf->st_ino || - p->i_st_dev != sbuf->st_dev)) p0 = p; - - if (p) {/* If this is a link to a file already seen... */ - /* then set ln to the overall path name... */ - - for (s = p->iname, i = 0; (c = *s++) && i < MAXNAME; ) ln[i++] = c; - if (i >= MAXNAME) scream("Path name to link too long.\n",""); - ln[i] = '\0'; - Linkct++; - return 1; - } - - /* This file has more than one link to it -- add it to the list of files */ - /* that may be seen later. */ - - p = (struct inodetab *) - malloc((unsigned) (sizeof(struct inodetab) + strlen(source))); - p0->nextint = p; - p->nextint = 0; - p->i_st_ino = sbuf->st_ino; - p->i_st_dev = sbuf->st_dev; - strcpy(p->iname, source); - return (0); - } - -#include "ok.c" //GO.SYSIN DD stree.c echo ustree.c 1>&2 sed >ustree.c <<'//GO.SYSIN DD ustree.c' 's/^-//' -/* version of ustree that restores links (including symbolic links) - and ignores extra ./ ENDUP lines. This ustree also takes arguments: - it reads output from stree on stdin and restores a subset. - - Invocation is: ustree pat1 pat2 ... - - Arguments to ustree are pathname shell pathname patterns, possibly - preceded by a ! . The patterns may also contain the metacharacter ~ , - which matches an arbitrary string that must start with / unless ~ is - at the beginning of the pattern and must end with / unless ~ is at - the end of the pattern. - An excluding pattern is one that starts with ! , a selecting - pattern one that does not. A file is restored if its pathname - matches one of the selecting patterns but not one of the excluding ones. - Giving no selecting patterns is the same as giving the single selecting - pattern ~ (which matches all names). -*/ - -#include "patlist.h" -#include -#include -#include -#include -#include -#include "string.h" -#ifndef ESC -#define ESC '\033' -#endif -#define LMAX 600 -#define MAXNAME 1000 -char buf[LMAX]; -char *getline(); -FILE *ofile = 0; -struct stat stbuf; -char olddir[MAXNAME]; - -main(argc,argv) -int argc; -char **argv; -{ - char linkname[MAXNAME], name[MAXNAME], nname[MAXNAME]; - long time, ntime; - int execut, nexecut; - time_t timep[2]; - int canend=0, copytext=0, cntl, endup=0, rc=0; - struct patlist *keep, *drop; - struct stat sbuf; - - /* body */ - - getpats(argc, argv, &keep, &drop); - olddir[0] = '\0'; - name[0] = '\0'; - for(;;){ - if(!getline(buf,LMAX)) { - if (endup > 1) printf("ustree: %d ./ ENDUP lines seen\n", endup); - if (canend) exit(rc); - scream("missing `./ ENDUP' after `%s'?", name); - } - canend = 0; - if( (cntl=dotslash(linkname,nname,&ntime,&nexecut)) != 0 ){ - if(ofile){ - if (execut) { - fstat(fileno(ofile), &sbuf); - /* could use fchmod, but that's less portable */ - chmod(name, sbuf.st_mode | 0111); - } - fclose(ofile); - if(time){ timep[0]=time; timep[1]=time; utime(name,timep); } - ofile = 0; - } - copytext = 0; - if(cntl==2) { /* ./ ENDUP */ - endup++; - canend = 1; - continue; - } - strcpy(name,nname); - if (skipit(name,keep,drop)) continue; - dirchk(name); - if (cntl==1) /* non-link */ { - time = ntime; - execut = nexecut; - if (ofile = fopen(name,"w")) - copytext = 1; - else - printf("ustree: can't create %s\n",name); - } - else /*link*/ { - unlink(name); /* get rid of old copy, if any */ - if (cntl==3 && !link(linkname,name)) continue; -#ifdef S_IFLNK - if (symlink(linkname,name)) - {printf("can't link %s to %s\n",name,linkname); exit(1);} - if (cntl==3) printf("hard link %s to %s fails; symbolic link made\n", - name, linkname); -#else - printf("Can't symbolically link %s to %s\n", name, linkname); - rc = 1; -#endif - } - } - else if (copytext) putline(buf); - } -} - -int -dotslash (linkname,name,time,execut) - char *linkname, name[]; - long *time; - int *execut; -{ - int i; - char c; - - name[0]='\0'; - *time=0; - if( (buf[0]=='.') && (buf[1]=='/') ){ - if(!strncmp(buf+2," ADD NAME=",10)){ - for(i=12; i