evas@euraiv1.UUCP (Eelco van Asperen) (10/02/87)
Here is part 7 of 9: Eelco van Asperen. # This is a shar archive. Extract with sh, not csh. # This archive ends with exit, so do not worry about trailing junk. # --------------------------- cut here -------------------------- echo Extracting commands/find.c sed 's/^X//' > commands/find.c << '+ END-OF-FILE 'commands/find.c X/* find - look for files satisfying a predicat Author: Erik Baalbergen */ X X/* X *** Check the switches in the SWITCHES section below. *** X X Differences from UNIX version 7 find(1): X * -name: no name allowed; only uid X e.g. find all core files: "find . -name core -a -print" X X The "-atime" may not work well on Minix. X Please report bugs and suggestions to erikb@cs.vu.nl X*/ X X#include "stdio.h" X X/*######################## SWITCHES ##############################*/ X#include "stat.h" X#define SHELL "/usr/bin/sh" X X#define PLEN 256 /* maximum path length; overflows are not detected */ X#define DIRSIZ 16 /* size of a directory entry */ X#define MAXARG 256 /* maximum length for an argv */ X#define NPATHS 256 /* maximum number of paths in path-list */ X#define BSIZE 1024 /* bytes per block */ X X/*######################## DEFINITIONS ##############################*/ X#define SECS_PER_DAY (24L * 60L * 60L) /* check your planet */ X Xstruct exec { X int e_cnt; X char *e_vec[MAXARG]; X}; X X#define OP_NAME 1 X#define OP_PERM 2 X#define OP_TYPE 3 X#define OP_LINKS 4 X#define OP_USER 5 X#define OP_GROUP 6 X#define OP_SIZE 7 X#define OP_INUM 8 X#define OP_ATIME 9 X#define OP_MTIME 10 X#define OP_EXEC 11 X#define OP_OK 12 X#define OP_PRINT 13 X#define OP_NEWER 14 X#define OP_AND 15 X#define OP_OR 16 X Xstruct oper { X char *op_str; X int op_val; X} ops[] = { X {"name", OP_NAME}, X {"perm", OP_PERM}, X {"type", OP_TYPE}, X {"links", OP_LINKS}, X {"user", OP_USER}, X {"group", OP_GROUP}, X {"size", OP_SIZE}, X {"inum", OP_INUM}, X {"atime", OP_ATIME}, X {"mtime", OP_MTIME}, X {"exec", OP_EXEC}, X {"ok", OP_OK}, X {"print", OP_PRINT}, X {"newer", OP_NEWER}, X {"a", OP_AND}, X {"o", OP_OR}, X {0, 0} X}; X X#define EOI -1 X#define NONE 0 X#define LPAR 20 X#define RPAR 21 X#define NOT 22 X Xchar *prog, *strcpy(), *Malloc(), *find_bin(); X Xstruct node { X int n_type; /* any OP_ or NOT */ X union { X char *n_str; X struct { X long n_val; X int n_sign; X } n_int; X struct exec *n_exec; X struct { X struct node *n_left, *n_right; X } n_opnd; X } n_info; X}; Xstruct node *expr(); X Xchar **ipp; Xint tty; /* fd for /dev/tty when using -ok */ Xlong current_time; X Xchar * XMalloc(n) X{ X char *malloc(), *m; X X if ((m = malloc(n)) == 0) X fatal("out of memory", ""); X return m; X} X Xchar * XSalloc(s) X char *s; X{ X return strcpy(Malloc(strlen(s) + 1), s); X} X Xmain(argc, argv) X char *argv[]; X{ X char *pathlist[NPATHS]; X int pathcnt = 0; X register i; X struct node *pred; X X prog = *argv++; X while (--argc > 0 && lex(*argv) == NONE) X pathlist[pathcnt++] = *argv++; X if (pathcnt == 0 || argc == 0) X fatal("Usage: path-list predicate-list", ""); X ipp = argv; X time(¤t_time); X pred = expr(lex(*ipp)); X if (lex(*++ipp) != EOI) X fatal("syntax error: garbage at end of predicate", ""); X for (i = 0; i < pathcnt; i++) X find(pathlist[i], pred, ""); X exit(0); X} X Xfind(path, pred, last) X char *path, *last; X struct node *pred; X{ X char spath[PLEN], ent[DIRSIZ + 1]; X struct stat st; X register char *send = spath; X FILE *fp, *fopen(); X X if (path[1] == '\0' && *path == '/') { X *send++ = '/'; X *send = '\0'; X } X else X while (*send++ = *path++) {} X if (stat(spath, &st) == -1) X nonfatal("can't get status of ", spath); X else { X (void) check(spath, &st, pred, last); X if ((st.st_mode & S_IFMT) == S_IFDIR) { X if ((fp = fopen(spath, "r")) == NULL) { X nonfatal("can't read directory ", spath); X return; X } X send[-1] = '/'; X ent[DIRSIZ] = '\0'; X while (fread(ent, DIRSIZ, 1, fp) == 1) { X if (!((*ent == '\0' && ent[1] == '\0') X || (ent[2] == '.') && X (ent[3] == '\0' X || (ent[3] == '.' && ent[4] == '\0')) X )) { X strcpy(send, ent + 2); X find(spath, pred, ent + 2); X } X } X fclose(fp); X } X } X} X Xcheck(path, st, n, last) X char *path, *last; X register struct stat *st; X register struct node *n; X{ X switch (n->n_type) { X case OP_AND: X return check(path, st, n->n_info.n_opnd.n_left, last) && X check(path, st, n->n_info.n_opnd.n_right, last); X case OP_OR: X return check(path, st, n->n_info.n_opnd.n_left, last) || X check(path, st, n->n_info.n_opnd.n_right, last); X case NOT: X return !check(path, st, n->n_info.n_opnd.n_left, last); X case OP_NAME: X return smatch(last, n->n_info.n_str); X case OP_PERM: X if (n->n_info.n_int.n_sign < 0) X return st->st_mode == (int) n->n_info.n_int.n_val; X return (st->st_mode & 0777) == (int) n->n_info.n_int.n_val; X case OP_NEWER: X return st->st_mtime > n->n_info.n_int.n_val; X case OP_TYPE: X return (st->st_mode & S_IFMT) == n->n_info.n_int.n_val; X case OP_LINKS: X return ichk((long)(st->st_nlink), n); X case OP_USER: X return st->st_uid == n->n_info.n_int.n_val; X case OP_GROUP: X return st->st_gid == n->n_info.n_int.n_val; X case OP_SIZE: X return ichk((st->st_size == 0) ? 0L : X ((st->st_size - 1) / BSIZE + 1), n); X case OP_INUM: X return ichk((long)(st->st_ino), n); X case OP_ATIME: X return ichk(st->st_atime, n); X case OP_MTIME: X return ichk(st->st_mtime, n); X case OP_EXEC: X case OP_OK: X return execute(n->n_type, n->n_info.n_exec, path); X case OP_PRINT: X prints("%s\n", path); X return 1; X } X fatal("ILLEGAL NODE", ""); X} X Xichk(val, n) X long val; X struct node *n; X{ X switch (n->n_info.n_int.n_sign) { X case 0: X return val == n->n_info.n_int.n_val; X case 1: X return val > n->n_info.n_int.n_val; X case -1: X return val < n->n_info.n_int.n_val; X } X fatal("internal: bad n_sign", ""); X} X Xlex(str) X char *str; X{ X if (str == 0) X return EOI; X if (*str == '-') { X register struct oper *op; X X str++; X for (op = ops; op->op_str; op++) X if (strcmp(str, op->op_str) == 0) X break; X return op->op_val; X } X if (str[1] == 0) { X switch(*str) { X case '(': X return LPAR; X case ')': X return RPAR; X case '!': X return NOT; X } X } X return NONE; X} X Xstruct node * Xnewnode(t) X{ X struct node *n = (struct node*) Malloc(sizeof(struct node)); X X n->n_type = t; X return n; X} X X/*########################### PARSER ###################################*/ X/* grammar: X expr : primary | primary OR expr; X primary : secondary | secondary AND primary | secondary primary; X secondary : NOT secondary | LPAR expr RPAR | simple; X simple : -OP args... X*/ Xstruct node *expr(), *primary(), *secondary(), *simple(); X Xnumber(str, base, pl, ps) X char *str; X long *pl; X int *ps; X{ X int up = '0' + base - 1; X long val = 0; X X *ps = ((*str == '-' || *str == '+') ? ((*str++ == '-') ? -1 : 1) : 0); X while (*str >= '0' && *str <= up) X val = base * val + *str++ - '0'; X if (*str) X fatal("syntax error: illegal numeric value", ""); X *pl = val; X} X Xstruct node * Xexpr(t) X{ X struct node *nd, *p, *nd2; X X nd = primary(t); X if ((t = lex(*++ipp)) == OP_OR) { X nd2 = expr(lex(*++ipp)); X p = newnode(OP_OR); X p->n_info.n_opnd.n_left = nd; X p->n_info.n_opnd.n_right = nd2; X return p; X } X ipp--; X return nd; X} X Xstruct node * Xprimary(t) X{ X struct node *nd, *p, *nd2; X X nd = secondary(t); X if ((t = lex(*++ipp)) != OP_AND) { X ipp--; X if (t == EOI || t == RPAR || t == OP_OR) X return nd; X } X nd2 = primary(lex(*++ipp)); X p = newnode(OP_AND); X p->n_info.n_opnd.n_left = nd; X p->n_info.n_opnd.n_right = nd2; X return p; X} X Xstruct node * Xsecondary(t) X{ X struct node *n, *p; X X if (t == LPAR) { X n = expr(lex(*++ipp)); X if (lex(*++ipp) != RPAR) X fatal("syntax error, ) expected", ""); X return n; X } X if (t == NOT) { X n = secondary(lex(*++ipp)); X p = newnode(NOT); X p->n_info.n_opnd.n_left = n; X return p; X } X return simple(t); X} X Xcheckarg(arg) X char *arg; X{ X if (arg == 0) X fatal("syntax error, argument expected", ""); X} X Xstruct node * Xsimple(t) X{ X struct node *p = newnode(t); X struct exec *e; X struct stat est; X long l; X X switch (t) { X case OP_TYPE: X checkarg(*++ipp); X switch (**ipp) { X case 'b': X p->n_info.n_int.n_val = S_IFBLK; X break; X case 'c': X p->n_info.n_int.n_val = S_IFCHR; X break; X case 'd': X p->n_info.n_int.n_val = S_IFDIR; X break; X case 'f': X p->n_info.n_int.n_val = S_IFREG; X break; X default: X fatal("-type needs b, c, d or f", ""); X } X break; X case OP_LINKS: X case OP_USER: X case OP_GROUP: X case OP_SIZE: X case OP_INUM: X case OP_PERM: X checkarg(*++ipp); X number(*ipp, (t == OP_PERM) ? 8 : 10, &(p->n_info.n_int.n_val), X &(p->n_info.n_int.n_sign)); X break; X case OP_ATIME: X case OP_MTIME: X checkarg(*++ipp); X number(*ipp, 10, &l, &(p->n_info.n_int.n_sign)); X p->n_info.n_int.n_val = current_time - l * SECS_PER_DAY; X /* more than n days old means less than the absolute time */ X p->n_info.n_int.n_sign *= -1; X break; X case OP_EXEC: X case OP_OK: X checkarg(*++ipp); X e = (struct exec *)Malloc(sizeof(struct exec)); X e->e_cnt = 2; X e->e_vec[0] = SHELL; X p->n_info.n_exec = e; X while (*ipp) { X if (**ipp == ';' && (*ipp)[1] == '\0') { X e->e_vec[e->e_cnt] = 0; X break; X } X e->e_vec[(e->e_cnt)++] = X (**ipp == '{' && (*ipp)[1] == '}' X && (*ipp)[2] == '\0') ? (char *)(-1) : *ipp; X ipp++; X } X if (*ipp == 0) X fatal("-exec/-ok: ; missing", ""); X if ((e->e_vec[1] = find_bin(e->e_vec[2])) == 0) X fatal("can't find program ", e->e_vec[2]); X if (t == OP_OK) X if ((tty = open("/dev/tty", 2)) < 0) X fatal("can't open /dev/tty", ""); X break; X case OP_NEWER: X checkarg(*++ipp); X if (stat(*ipp, &est) == -1) X fatal("-newer: can't get status of ", *ipp); X p->n_info.n_int.n_val = est.st_mtime; X break; X case OP_NAME: X checkarg(*++ipp); X p->n_info.n_str = *ipp; X break; X case OP_PRINT: X break; X default: X fatal("syntax error, operator expected", ""); X } X return p; X} X X/*######################## DIAGNOSTICS ##############################*/ X Xnonfatal(s1, s2) X char *s1, *s2; X{ X std_err(prog); X std_err(": "); X std_err(s1); X std_err(s2); X std_err("\n"); X} X Xfatal(s1, s2) X char *s1, *s2; X{ X nonfatal(s1, s2); X exit(1); X} X X/*################### SMATCH #########################*/ X/* Don't try to understand the following one... */ Xsmatch(s, t) /* shell-like matching */ X char *s, *t; X{ X register n; X X if (*t == '\0') X return *s == '\0'; X if (*t == '*') { X ++t; X do X if (smatch(s,t)) X return 1; X while (*s++ != '\0'); X return 0; X } X if (*s == '\0') X return 0; X if (*t == '\\') X return (*s == *++t) ? smatch(++s, ++t) : 0; X if (*t == '?') X return smatch(++s, ++t); X if (*t == '[') { X while (*++t != ']') { X if (*t == '\\') X ++t; X if (*(t+1) != '-') X if (*t == *s) { X while (*++t != ']') X if (*t == '\\') X ++t; X return smatch(++s, ++t); X } X else X continue; X if (*(t+2) == ']') X return (*s == *t || *s == '-'); X n = (*(t+2) == '\\') ? 3 : 2; X if (*s >= *t && *s <= *(t+n)) { X while (*++t != ']') X if (*t == '\\') X ++t; X return smatch(++s, ++t); X } X t += n; X } X return 0; X } X return (*s == *t) ? smatch(++s, ++t) : 0; X} X X/*####################### EXECUTE ###########################*/ X/* do -exec or -ok */ X Xchar *epath = 0; X Xchar * Xgetpath() X{ X extern char **environ; X register char **e = environ; X X if (epath) /* retrieve PATH only once */ X return epath; X while (*e) { X if (strncmp("PATH=", *e, 5) == 0) { X return epath = *e + 5; X } X e++; X } X fatal("can't get PATH from environment", ""); X} X Xchar * Xfind_bin(s) X char *s; X{ X char *f, *l, buf[PLEN]; X X if (*s == '/') /* absolute path name */ X return (access(s, 1) == 0) ? s : 0; X l = f = getpath(); X for (;;) { X if (*l == ':' || *l == 0) { X if (l == f) { X if (access(s, 1) == 0) X return Salloc(s); X f++; X } X else { X register char *p = buf, *q = s; X X while (f != l) X *p++ = *f++; X f++; X *p++ = '/'; X while (*p++ = *q++) {} X if (access(buf, 1) == 0) X return Salloc(buf); X } X if (*l == 0) X break; X } X l++; X } X return 0; X} X Xexecute(op, e, path) X struct exec *e; X char *path; X{ X int s, pid; X char *argv[MAXARG]; X register char **p, **q; X X for (p = e->e_vec, q = argv; *p;) /* replace the {}s */ X if ((*q++ = *p++) == (char *)-1) X q[-1] = path; X *q = '\0'; X if (op == OP_OK) { X char answer[10]; X X for (p = &argv[2]; *p; p++) { X write(tty, *p, strlen(*p)); X write(tty, " ", 1); X } X write(tty, "? ", 2); X if (read(tty, answer, 10) < 2 || *answer != 'y') X return 0; X } X if ((pid = fork()) == -1) X fatal("can't fork", ""); X if (pid == 0) { X register i = 3; X X while (close(i++) == 0) {} /* wow!!! */ X execv(argv[1], &argv[2]); /* binary itself? */ X execv(argv[0], &argv[1]); /* shell command? */ X fatal("exec failure: ", argv[1]); /* none of them! */ X exit(127); X } X return wait(&s) == pid && s == 0; X} + END-OF-FILE commands/find.c chmod u=rw,g=r,o=r commands/find.c set `sum commands/find.c` sum=$1 case $sum in 25453) :;; *) echo 'Bad sum in 'commands/find.c >&2 esac echo Extracting commands/fix.c sed 's/^X//' > commands/fix.c << '+ END-OF-FILE 'commands/fix.c X/* fix - combine file and diff listing Author: Erik Baalbergen */ X X/* Notes: X * files old and old.fix are equal after the following commands X diff old new > difflist X fix old difflist > old.fix X * the diff output is assumed to be produced by my diff program. X * the difflist has the following form: X difflist ::= chunk* X chunk ::= append | delete | change ; X append ::= n1 'a' n2 [',' n3]? '\n' ['> ' line '\n'](n3 - n2 + 1) X delete ::= n1 [',' n2]? 'd' n3 '\n' ['< ' line '\n'](n2 - n1 + 1) X change ::= n1 [',' n2]? 'c' n3 [',' n4]? '\n' X ['< ' line '\n'](n2 - n1 + 1) X '---\n' X ['> ' line '\n'](n4 - n3 + 1) X where X - n[1234] is an unsigned integer X - "[pat](expr)" means "(expr) occurences of pat" X - "[pat]?" means "either pat or nothing" X * the information in the diff listing is checked against the file to which X it is applied; an error is printed if there is a conflict X*/ X X#include <stdio.h> X Xextern char *fgets(); Xextern FILE *fopen(); X#define LINELEN 1024 X Xchar *prog = 0; X Xchar * Xgetline(fp, b) X FILE *fp; X char *b; X{ X if (fgets(b, LINELEN, fp) == NULL) X fatal("unexpected eof"); X return b; X} X X#define copy(str) printf("%s", str) X Xmain(argc, argv) X char **argv; X{ X char cmd, *fl, *fd, obuf[LINELEN], nbuf[LINELEN]; X int o1, o2, n1, n2, here; X FILE *fpf, *fpd; X X prog = argv[0]; X if (argc != 3) X fatal("use: %s original-file diff-list-file", prog); X if ((fpf = fopen(argv[1], "r")) == NULL) X fatal("can't read %s", argv[1]); X if ((fpd = fopen(argv[2], "r")) == NULL) X fatal("can't read %s", argv[2]); X here = 0; X while (getcommand(fpd, &o1, &o2, &cmd, &n1, &n2)) { X while (here < o1 - 1) { X here++; X copy(getline(fpf, obuf)); X } X switch (cmd) { X case 'c': X case 'd': X if (cmd == 'd' && n1 != n2) X fatal("delete count conflict"); X while (o1 <= o2) { X fl = getline(fpf, obuf); X here++; X fd = getline(fpd, nbuf); X if (strncmp(fd, "< ", 2)) X fatal("illegal delete line"); X if (strcmp(fl, fd + 2)) X fatal("delete line conflict"); X o1++; X } X if (cmd == 'd') X break; X if (strcmp(getline(fpd, nbuf), "---\n")) X fatal("illegal separator in chunk"); X /*FALLTHROUGH*/ X case 'a': X if (cmd == 'a') { X if (o1 != o2) X fatal("append count conflict"); X copy(getline(fpf, obuf)); X here++; X } X while (n1 <= n2) { X if (strncmp(getline(fpd, nbuf), "> ", 2)) X fatal("illegal append line"); X copy(nbuf + 2); X n1++; X } X break; X } X } X while (fgets(obuf, LINELEN, fpf) != NULL) X copy(obuf); X exit(0); X} X Xisdigit(c) X char c; X{ X return c >= '0' && c <= '9'; X} X Xchar * Xrange(s, p1, p2) X char *s; X int *p1, *p2; X{ X register int v1 = 0, v2; X X while (isdigit(*s)) X v1 = 10 * v1 + *s++ - '0'; X v2 = v1; X if (*s == ',') { X s++; X v2 = 0; X while (isdigit(*s)) X v2 = 10 * v2 + *s++ - '0'; X } X if (v1 == 0 || v2 == 0 || v1 > v2) X fatal("illegal range"); X *p1 = v1; X *p2 = v2; X return s; X} X Xgetcommand(fp, o1, o2, pcmd, n1, n2) X FILE *fp; X int *o1, *o2, *n1, *n2; X char *pcmd; X{ X char buf[LINELEN]; X register char *s; X char cmd; X X if ((s = fgets(buf, LINELEN, fp)) == NULL) X return 0; X s = range(s, o1, o2); X if ((cmd = *s++) != 'a' && cmd != 'c' && cmd != 'd') X fatal("illegal command"); X s = range(s, n1, n2); X if (*s != '\n' && s[1] != '\0') X fatal("extra characters at end of command: %s", s); X *pcmd = cmd; X return 1; X} X Xfatal(s, a) X char *s, *a; X{ X fprintf(stderr, "%s: fatal: ", prog); X fprintf(stderr, s, a); X fprintf(stderr, "\n"); X exit(1); X} X X X X + END-OF-FILE commands/fix.c chmod u=rw,g=r,o=r commands/fix.c set `sum commands/fix.c` sum=$1 case $sum in 50220) :;; *) echo 'Bad sum in 'commands/fix.c >&2 esac echo Extracting commands/head.dif sed 's/^X//' > commands/head.dif << '+ END-OF-FILE 'commands/head.dif X1a2,3 X> /* change to use putc() instead of prints() -- Dean Long 3/7/87 */ X> X8d9 X< char lbuf[256]; X43,44c44,47 X< else X< do_file(n); X--- X> else { X> do_file(n); X> fflush(stdout); X> } X48d50 X< fflush(stdout); X57,58c59,70 X< /* Print the first 'n' lines of a file. */ X< while (n--) do_line(); X--- X> int c; X> X> /* Print the first 'n' lines of a file. */ X> while(n) X> switch (c = getc(stdin)) { X> case EOF : X> return; X> case '\n': X> --n; X> default : X> putc((char)c, stdout); X> } X62,80c74,78 X< do_line() X< { X< /* Print one line. */ X< X< char c, *cp; X< cp = lbuf; X< while ( (c = getc(stdin)) != '\n') *cp++ = c; X< *cp++ = '\n'; X< *cp++ = 0; X< prints("%s",lbuf); X< } X< X< X< usage() X< { X< std_err("Usage: head [-n] [file ...]\n"); X< exit(1); X< } X< X--- X> usage() X> { X> std_err("Usage: head [-n] [file ...]\n"); X> exit(1); X> } + END-OF-FILE commands/head.dif chmod u=rw,g=r,o=r commands/head.dif set `sum commands/head.dif` sum=$1 case $sum in 60912) :;; *) echo 'Bad sum in 'commands/head.dif >&2 esac exit 0