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