ast@cs.vu.nl (Andy Tanenbaum) (06/15/87)
Here is a version of find written by Alistair Crooks. Andy Tanenbaum (ast@cs.vu.nl) ------------------------ cut here for manual -------------------------------- NAME find - find files SYNOPSIS find pathname-list expression find name DESCRIPTION find can be used to find a (number of) file(s) in a file system It is modelled after 4.3BSD's find, but has certain limitations. These are listed at the end of this page. There are two forms that the find command can take - the shortform, which is basically find / -name "*name*" -print in the longer form. The longer form has the following valid expressions : -type x where x is bcdfgu for block special file, character special file, directory, regular file, setgid file and setuid file. -name s where the name of the file is s, which can contain wile cards, same as sh(1). -inum n where n is the inode number of the file. -mtime [+-]n where the modification time of the file is n. +n means more than n, -n means less than n, and n means exactly n. n is the number of days. -atime [+-]n where the access time of the file is n. +n means more than n, -n means less than n, and n means exactly n. n is the number of days. -ctime [+-]n where the change time of the file is n. +n means more than n, -n means less than n, and n means exactly n. n is the number of days. -size [+-]n[c] where the size of the file is n. Default radix is blocks. If the number is followed by the character 'c', the size unit of bytes is used. +n means more than n (blocks), -n means less than n (blocks), n means exactly n (blocks). -links [+-]n where the number of links possessed by a file is n. +n means more than n, -n means less than n, and n means exactly n. -depth Does a depth first search of the file system. Default is to search a directory's entries after processing the directory itself. This expression means the directory entries are processed before the directory. -prune Does not progress the search into directories. -newer f where f is a file. Finds files that are newer than f. -perm [-]n where n is an octal number of the file permission form. If the number is preceded by '-', a long permission mode is used. -user u where u is a login name, or a uid. Finds files belonging to user u. -nouser finds bastard files (files whose owners are not in /etc/passwd). -group a where g is a group name or gid. Finds files belonging to group g. -nogroup finds group-delinquent files. (files whose group is no longer in /etc/group). In addition, the expressions can have the following Boolean operators: -a Boolean and operator. -o Boolean or operator. ( and ) group expressions together ! Boolean not operator Find also has the following actions: -print print the file name on standard output. -exec execute a Minix command. The file name can be interpreted here by a pair of braces ("{}"). The command should (but not necessarily be concluded by a quoted semi-colon ("\;"). -ok prints the command on standard output, and waits for the user to enter a character from the keyboard. If the character is 'y', the command will be executed, otherwise it will not. -ls provides a long listing of all salient points about the file, and is equivalent to the 4.3BSD Unix ls -gilds. BUGS AND ENHANCEMENTS 1. Only one directory can be used as the starting point for the search. This is relatively simple to cure. 2. Only one action can be specified (in contrast to the BSD version) which views everything as an expression, and actions as true expressions. This should be simple to cure as well. 3. The building of the internal search tree leaves a little to be desired. I'm not sure bracketed groups are done in the right way, and the code is a mess. 4. Build to run under many more different flavours of Unix. AUTHOR Alistair G. Crooks, Joypace Ltd, London, UK, 1987. (agc@ist.co.uk, until July). ------------------------ cut here for find.c -------------------------------- /* * find.c 1.1 7/6/87 agc Joypace Ltd. * * Copyright 1987, Joypace Ltd., London UK. All rights reserved. * This code may be freely distributed, provided that this notice * remains attached. * * find - a public domain interpretation of find(1). * * Compile-time flags: -DMINIX for Minix (V7 file system, etc) * -DSUN for SunOS (dp_ino or dp_fileno) * default env: BSD 4.3 */ #include <stdio.h> #ifdef MINIX #include <blocksize.h> #include <stat.h> struct passwd { char *pw_name; char *pw_passwd; int pw_uid; int pw_gid; char *pw_gecos; char *pw_dir; char *pw_shell; }; struct group { char *gr_name; char *gr_passwd; int gr_gid; }; #else #include <sys/types.h> #include <sys/stat.h> #include <sys/dir.h> #include <pwd.h> #include <grp.h> #define BLOCK_SIZE 1024 #endif p #define LINELEN 256 /* max no of chars in a line */ #define SHORTLINE 80 /* a shorter line */ #define DIRNAMELEN 14 /* max no of chars in a dir entry */ #define MAXCRIT 10 /* max no of search criteria */ #define MODELEN 11 /* length of mode readout */ #define NAMELEN 8 /* length of a user name readout */ #define SHORTMASK 0777 /* short file permission mask */ #define LONGMASK 017777 /* long file permission mask */ #define ISDIGIT(x) ((x) >= '0' && (x) <= '9') #define ISOCT(c) ((c) >= '0' && (c) <= '7') #define CR_TYPE 1 /* -type criterion */ #define CR_NAME 2 /* -name criterion */ #define CR_INUM 3 /* -inum criterion */ #define CR_MTIME 4 /* -mtime criterion */ #define CR_ATIME 5 /* -atime criterion */ #define CR_CTIME 6 /* -ctime criterion */ #define CR_SIZE 7 /* -size criterion */ #define CR_LINKS 8 /* -links criterion */ #define CR_DEPTH 9 /* -depth criterion */ #define CR_PRUNE 10 /* -prune criterion */ #define CR_NEWER 11 /* -newer criterion */ #define CR_PERM 12 /* -perm criterion */ #define CR_USER 13 /* -user criterion */ #define CR_NOUSER 14 /* -nouser criterion */ #define CR_GROUP 15 /* -group criterion */ #define CR_NOGROUP 16 /* -nogroup criterion */ #define ACT_PRINT 17 /* -print action */ #define ACT_EXEC 18 /* -exec action */ #define ACT_OK 19 /* -ok action */ #define ACT_LS 20 /* -ls action */ #define AR_LT 21 /* arithmetical < */ #define AR_EQ 22 /* arithmetical == */ #define AR_GT 23 /* arithmetical > */ #define OP_AND 24 /* and operator for node */ #define OP_OR 25 /* or operator for node */ #define OP_OPENB 26 /* open bracket operator for node */ #define OP_CLOSEB 27 /* close bracket operator for node */ #define OP_NOT 28 /* ! operator for node */ #define ND_OP 29 /* operator node */ #define ND_CRIT 30 /* criterion node */ p typedef struct num { long nm_num; char nm_op; } NUM; typedef struct _critstr { char cr_type; char cr_not; union { char *cr_name; NUM cr_n; } cr_un; } CRIT; typedef struct _nodestr { char nd_type; union { CRIT *nd_crit; int nd_op; } nd_un; struct _nodestr *nd_left; struct _nodestr *nd_right; } NODE; typedef struct _argstr { char *ar_name; int ar_argc; int ar_ndtype; int ar_attr; } ARG; #ifdef MINIX typedef struct dir { short inum; char d_name[DIRNAMELEN]; } DIR; DIR dir; #endif p char *prog; /* program name */ char *startdir; /* dir to start from */ char cmd[LINELEN]; /* command to be executed */ char expanded[LINELEN]; /* command with expanded {}'s */ int act; /* type of action to take */ int bracec = 0; /* no of braces in cmd */ NODE *root; /* root of search criteria tree */ CRIT criteria[MAXCRIT]; /* search criteria */ int critc = 0; /* number of criteria */ int depthfirst = 0; /* depth first search of tree */ int prune = 0; /* stop at directories */ extern long time(); /* no see */ extern char *malloc(); /* shut lint up */ extern char *ctime(); /* ditto */ extern long atol(); /* nearly there */ p ARG argtab[] = { {"-o", 1, ND_OP, OP_OR}, {"-a", 1, ND_OP, OP_AND}, {"(", 1, ND_OP, OP_OPENB}, {")", 1, ND_OP, OP_CLOSEB}, {"!", 1, ND_OP, OP_NOT}, {"-name", 2, ND_CRIT, CR_NAME}, {"-type", 2, ND_CRIT, CR_TYPE}, {"-inum", 2, ND_CRIT, CR_INUM}, {"-mtime", 2, ND_CRIT, CR_MTIME}, {"-ctime", 2, ND_CRIT, CR_CTIME}, {"-atime", 2, ND_CRIT, CR_ATIME}, {"-size", 2, ND_CRIT, CR_SIZE}, {"-depth", 1, ND_CRIT, CR_DEPTH}, {"-prune", 1, ND_CRIT, CR_PRUNE}, {"-links", 2, ND_CRIT, CR_LINKS}, {"-newer", 2, ND_CRIT, CR_NEWER}, {"-perm", 2, ND_CRIT, CR_PERM}, {"-user", 2, ND_CRIT, CR_USER}, {"-nouser", 1, ND_CRIT, CR_NOUSER}, {"-group", 2, ND_CRIT, CR_GROUP}, {"-nogroup", 1, ND_CRIT, CR_NOGROUP}, {NULL, 0, 0, 0} }; p /* * myindex - index gets corrupted by that used in scanf. */ char * myindex(s, c) register char *s; register char c; { for ( ; *s ; s++) if (*s == c) return(s); return(NULL); } p #ifdef MINIX /* * system - re-implemented for Minix. Possibly quicker to use execl * rather than splitting the command line, but seems to be quick * enough. */ int system(s) char *s; { int pid; int status; switch(pid = fork()) { case -1: (void) fprintf(stderr, "%s: fork failed\n", prog); return(127); case 0 : execl("/bin/sh", "sh", "-c", s, 0); (void) fprintf(stderr, "%s: exec failed\n", prog); return(127); default : while (wait(&status) != pid) ; } return(status); } #endif p #ifdef MINIX /* * getpwuid - get the passwd file entry with the same uid as usrid. * Returns the address in static area if found, NULL otherwise. */ struct passwd * getpwuid(usrid) int usrid; { static struct passwd pw; static char lbuf[100]; char *ptr; int bin; FILE *fp, *fopen(); if ((fp = fopen("/etc/passwd", "r")) == (FILE *) NULL) return(NULL); while (fgets(lbuf, sizeof(lbuf), fp) != NULL) { for (ptr = pw.pw_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; for (*ptr++ = 0, pw.pw_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++) bin = 10*bin + (*ptr - '0'); if (bin != usrid) continue; pw.pw_uid = bin; for (bin = 0, ++ptr; *ptr && ISDIGIT(*ptr) ; ptr++) bin = 10*bin + (*ptr - '0'); pw.pw_gid = bin; for (pw.pw_gecos = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++) ; for (*ptr = 0, pw.pw_dir = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++) ; *ptr = 0 ; for (pw.pw_shell = ++ptr; *ptr && *ptr != '\n' ; ptr++) ; *ptr = '\0'; fclose(fp); return(&pw); } fclose(fp); return(NULL); } #endif p #ifdef MINIX /* * getpwnam - get passwd entry with same login name as s. * Returns address in static area if found, NULL otherwise. */ struct passwd * getpwnam(s) char *s; { static struct passwd pw; static char lbuf[100]; char *ptr; int bin; FILE *fp, *fopen(); if ((fp = fopen("/etc/passwd", "r")) == (FILE *) NULL) return(NULL); while (fgets(lbuf, sizeof(lbuf), fp) != NULL) { for (ptr = pw.pw_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; *ptr++ = 0; if (strcmp(s, pw.pw_name) != 0) continue; for (pw.pw_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++) bin = 10*bin + (*ptr - '0'); pw.pw_uid = bin; for (bin = 0, ++ptr; *ptr && ISDIGIT(*ptr) ; ptr++) bin = 10*bin + (*ptr - '0'); pw.pw_gid = bin; for (pw.pw_gecos = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++) ; for (*ptr = 0, pw.pw_dir = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++) ; *ptr = 0 ; pw.pw_shell = ++ptr; fclose(fp); return(&pw); } fclose(fp); return(NULL); } #endif p #ifdef MINIX /* * getgrgid - get group entry with same uid as passed to it. * Returns address of entry in static area if found, NULL otherwise. */ struct group * getgrgid(gid) int gid; { static struct group gp; static char lbuf[100]; char *ptr; int bin; FILE *fp, *fopen(); if ((fp = fopen("/etc/group", "r")) == (FILE *) NULL) return(NULL); while (fgets(lbuf, sizeof(lbuf), fp) != NULL) { for (ptr = gp.gr_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; for (*ptr++ = 0, gp.gr_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++) bin = 10*bin + (*ptr - '0'); if (bin != gid) continue; gp.gr_gid = bin; fclose(fp); return(&gp); } fclose(fp); return(NULL); } #endif p #ifdef MINIX /* * getgrnam - get the group file entry corresponding to group name s. * Returns address of entry in static store if found, NULL otherwise. */ struct group * getgrnam(s) char *s; { static struct group gp; static char lbuf[100]; char *ptr; int bin; FILE *fp, *fopen(); if ((fp = fopen("/etc/group", "r")) == (FILE *) NULL) return(NULL); while (fgets(lbuf, sizeof(lbuf), fp) != NULL) { for (ptr = gp.gr_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; *ptr++ = 0; if (strcmp(s, gp.gr_name) != 0) continue; for (gp.gr_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++) ; if (*ptr == 0) continue; for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++) bin = 10*bin + (*ptr - '0'); gp.gr_gid = bin; fclose(fp); return(&gp); } fclose(fp); return(NULL); } #endif #ifdef MINIX /* * ctime - doesn't seem to be in libc on Minix. Used to format * time, passed as pointer to long (number of seconds). Returns * pointer to static char string containing formatted time. Code * stolen and hacked from date.c. */ int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char *days[] = { "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed" }; struct { int year, month, day, hour, min, sec; } tm; #define S_P_MIN 60L #define S_P_HOUR 60L * 60L #define S_P_DAY 24L * 60L * 60L #define S_P_YEAR 365L * 24L * 60L * 60L char * ctime(tp) long *tp; { static char out[26]; long t = *tp; tm.year = 0; tm.month = 0; tm.day = 1; tm.hour = 0; tm.min = 0; tm.sec = 0; while (t >= S_P_YEAR) { if (((tm.year + 2) % 4) == 0) t -= S_P_DAY; tm.year += 1; t -= S_P_YEAR; } if (((tm.year + 2) % 4) == 0) days_per_month[1]++; tm.year += 1970; while (t >= (days_per_month[tm.month] * S_P_DAY)) t -= days_per_month[tm.month++] * S_P_DAY; while (t >= S_P_DAY) { t -= S_P_DAY; tm.day++; } while (t >= S_P_HOUR) { t -= S_P_HOUR; tm.hour++; } while (t >= S_P_MIN) { t -= S_P_MIN; tm.min++; } tm.sec = (int) t; sprintf(out, "%s %s %2d %02d:%02d:%02d %d\n", days[(int)((t / S_P_DAY) % 7)], months[tm.month], tm.day, tm.hour, tm.min, tm.sec, tm.year); return(out); } #endif p #ifdef MINIX /* * LSTAT - Minix's V7 File system has no symbolic links, * so a crude work around is used here. */ #define LSTAT stat #else #define LSTAT lstat #endif p /* * fatal - report an error, and exit */ void fatal(s) char *s; { (void) fprintf(stderr, "%s: %s\n", prog, s); exit(1); } p /* * newnode - create a new node, with the arguments passed to it. */ NODE * newnode(typ, op, crp, left, right) int typ; int op; CRIT *crp; NODE *left; NODE *right; { NODE *np; if ((np = (NODE *) malloc(sizeof(NODE))) == (NODE *) NULL) fatal("can't malloc a node"); np->nd_type = typ; switch (typ) { case ND_CRIT: np->nd_un.nd_crit = crp; break; case ND_OP: np->nd_un.nd_op = op; break; } np->nd_left = left; np->nd_right = right; return(np); } p /* * dotim - calculate the time stamp desired. */ void dotim(s, np) char *s; NUM *np; { np->nm_op = (*s == '+') ? AR_LT : (*s == '-') ? AR_GT : AR_EQ; (void) time(&(np->nm_num)); np->nm_num -= (atol(++s) * 24L * 60L *60L); } p /* * nextcrit - return the address of the next criterion, assuming * that we have some left. */ CRIT * nextcrit() { if (critc == MAXCRIT) fatal("too many search criteria"); return(&criteria[critc++]); } p /* * octal - convert s to an octal number. Used for file permissions. * Returns the long octal number derived from s. */ long octal(s) char *s; { long l; for (l = 0; *s && ISOCT(*s) ; s++) l = l * 8L + *s - '0'; return(l); } p /* * finduid - get the uid corresponding to the argument string s. * Could be numeric or username. */ long finduid(s) char *s; { register char *cp; struct passwd *pwp; int numeric; for (cp = s, numeric = 1 ; *cp && numeric ; cp++) if (!ISDIGIT(*cp)) numeric = 0; if (numeric) return(atol(s)); if ((pwp = getpwnam(s)) == NULL) fatal("user not found"); return((long) pwp->pw_uid); } p /* * findgid - get the gid corresponding to the argument string s. * Could be numeric or group name. */ long findgid(s) char *s; { register char *cp; struct group *grp; int numeric; for (cp = s, numeric = 1 ; *cp ; cp++) if (!ISDIGIT(*cp)) numeric = 0; if (numeric) return(atol(s)); if ((grp = getgrnam(s)) == NULL) fatal("group not found"); return((long) grp->gr_gid); } p /* * fillcmd - fill the string cmd with the command to be executed, * starting at argv[i], until all arguments done. */ void fillcmd(argc, argv, i) int argc; char **argv; int i; { char *cp = cmd; char *cp2 = argv[i]; for ( ; i < argc ; cp2 = argv[++i], *cp++ = ' ') while (*cp2) if ((*cp++ = *cp2++) == '{') bracec++; while (*(cp-1) == ' ') cp--; *cp = '\0'; } p /* * argfind - takes the string s and returns its place in * the argument table. NULL if not found. */ ARG * argfind(s) char *s; { ARG *ap; for (ap = argtab ; ap && ap->ar_name ; ap++) if (strcmp(s, ap->ar_name) == 0) return(ap); return(NULL); } p /* * getcrit - recognise one criterion from the command line. * pi will be incremented by number of arguments used in the * criterion. Returns the address of the new node, if the * criterion is not an operator, otherwise NULL, and status * is filled with the operator type. */ NODE * getcrit(pi, argv) int *pi; char **argv; { struct stat s; CRIT *crp; char *cp; long l; ARG *ap; if ((ap = argfind(argv[++*pi])) == NULL) { *pi--; return(NULL); } if (ap->ar_ndtype != ND_CRIT) return(NULL); switch(ap->ar_attr) { case CR_NAME: crp = nextcrit(); crp->cr_type = CR_NAME; crp->cr_un.cr_name = argv[++*pi]; break; case CR_TYPE: crp = nextcrit(); crp->cr_type = CR_TYPE; crp->cr_un.cr_name = argv[++*pi]; if (myindex("bcdfgu", *argv[*pi]) == NULL) fatal("bad type identifier"); break; case CR_INUM: crp = nextcrit(); crp->cr_type = CR_INUM; if ((crp->cr_un.cr_n.nm_num = atoi(argv[++*pi])) <= 0) fatal("Bad inode number"); break; case CR_MTIME: crp = nextcrit(); crp->cr_type = CR_MTIME; dotim(argv[++*pi], &(crp->cr_un.cr_n)); break; case CR_CTIME: crp = nextcrit(); crp->cr_type = CR_CTIME; dotim(argv[++*pi], &(crp->cr_un.cr_n)); break; case CR_ATIME: crp = nextcrit(); crp->cr_type = CR_ATIME; dotim(argv[++*pi], &(crp->cr_un.cr_n)); break; case CR_SIZE: crp = nextcrit(); crp->cr_type = CR_SIZE; cp = argv[++*pi]; crp->cr_un.cr_n.nm_op = (*cp == '+') ? AR_GT : (*cp == '-') ? AR_LT : AR_EQ; if (*cp == '+' || *cp == '-') cp++; for (l = 0L ; *cp && ISDIGIT(*cp);) l = l * 10L + (long) (*cp++ - '0'); crp->cr_un.cr_n.nm_num = (*cp && *cp == 'c') ? l : l * (long) BLOCK_SIZE; break; case CR_DEPTH: depthfirst = 1; break; case CR_PRUNE: prune = 1; break; case CR_LINKS: crp = nextcrit(); crp->cr_type = CR_LINKS; cp = argv[++*pi]; crp->cr_un.cr_n.nm_op = (*cp == '+') ? AR_GT : (*cp == '-') ? AR_LT : AR_EQ; if (*cp == '+' || *cp == '-') cp++; if ((crp->cr_un.cr_n.nm_num = atol(cp)) <= 0) fatal("Bad number of links"); break; case CR_NEWER: if (LSTAT(argv[++*pi], &s) < 0) fatal("cannot stat file"); crp = nextcrit(); crp->cr_type = CR_NEWER; crp->cr_un.cr_n.nm_num = s.st_ctime; crp->cr_un.cr_n.nm_op = AR_GT; break; case CR_PERM: crp = nextcrit(); crp->cr_type = CR_PERM; cp = argv[++*pi]; crp->cr_un.cr_n.nm_op = (*cp == '-') ? AR_LT : AR_EQ; if (*cp == '-') cp++; crp->cr_un.cr_n.nm_num = octal(cp); break; case CR_USER: crp = nextcrit(); crp->cr_type = CR_USER; crp->cr_un.cr_n.nm_num = finduid(argv[++*pi]); break; case CR_NOUSER: crp = nextcrit(); crp->cr_type = CR_NOUSER; break; case CR_GROUP: crp = nextcrit(); crp->cr_type = CR_GROUP; crp->cr_un.cr_n.nm_num = findgid(argv[++*pi]); break; case CR_NOGROUP: crp = nextcrit(); crp->cr_type = CR_NOGROUP; break; } return(newnode(ND_CRIT, 0, crp, (NODE *)NULL, (NODE *)NULL)); } p /* * getacts - recognise the actions from the command line. */ void getacts(i, argc, argv) int i; int argc; char **argv; { if (strcmp(argv[++i], "-print") == 0) act = ACT_PRINT; else if (strcmp(argv[i], "-exec") == 0) { act = ACT_EXEC; fillcmd(argc, argv, ++i); } else if (strcmp(argv[i], "-ls") == 0) { act = ACT_LS; fillcmd(argc, argv, ++i); } else if (strcmp(argv[i], "-ok") == 0) { act = ACT_OK; fillcmd(argc, argv, ++i); } else fatal("bad action"); } p /* * getncrit - recognise n criteria, up to the start of the * actions, or until a closing bracket received. Returns the * top of the tree created by it. */ NODE * getncrit(ip, argc, argv) int *ip; int argc; char **argv; { NODE *new; NODE *top; char prev = 0; ARG *ap; for (top = NULL ; (ap = argfind(argv[*ip+1])) != NULL ; ) { if ((new = getcrit(ip, argv)) == NULL) { switch(ap->ar_attr) { case OP_OPENB : top = getncrit(ip, argc, argv); break; case OP_CLOSEB: return(top); case OP_NOT: prev = OP_NOT; break; case OP_AND: case OP_OR: if (argfind(argv[*ip+1]) == NULL) fatal("missing search criterion"); top = newnode(ND_OP, ap->ar_attr, (CRIT *)NULL, top, (NODE *)NULL); } } else { if (prev == OP_NOT) { new->nd_un.nd_crit->cr_not = 1; prev = 0; } if (top == NULL) top = new; else if (top->nd_type == ND_CRIT) top = newnode(ND_OP, OP_AND, (CRIT *)NULL, top, new); else top->nd_right = new; } } return(top); } p /* * doargs - parse the command line arguments. */ void doargs(argc, argv) int argc; char **argv; { int i = 1; startdir = argv[i]; if (access(startdir, 01) < 0) fatal("cannot access start directory"); root = getncrit(&i, argc, argv); getacts(i, argc, argv); } p /* * expand - take the command line, and replace all the occurrences * of braces with the file name s. */ char * expand(s) char *s; { char *cp; char *cp2; char *fn; for (cp2 = cmd, cp = expanded ; *cp2 && *cp2 != ';' ; ) { switch(*cp2) { case '{' : if (*(cp2+1) == '}') { for (fn = s ; *fn ; *cp++ = *fn++) ; cp2 += 2; } else *cp++ = *cp2++; break; default : *cp++ = *cp2++; } } while (*(cp-1) == ' ') cp--; *cp = '\0'; return(expanded); } p /* * prompt - prompt the user for input, and return char presented. */ int prompt(s) char *s; { int c; (void) printf("%s [y/n]? ", s); (void) fflush(stdout); while ((c = getchar()) == '\n') ; return(c); } p /* * fmtmode - format file permission mode (bit mask s) and print it. * Returns 1 if a [char,block] special file, 0 otherwise. */ char fmtmode(s) int s; { char ret = 0; switch(s & S_IFMT) { case S_IFDIR : putchar('d'); break; case S_IFCHR : putchar('c'); ret = 1; break; case S_IFBLK : putchar('b'); ret = 1; break; default : putchar('-'); break; } putchar((s & S_IREAD) ? 'r' : '-'); putchar((s & S_IWRITE) ? 'w' : '-'); putchar((s & S_ISUID) ? 's' : (s & S_IEXEC) ? 'x' : '-'); putchar((s & (S_IREAD >> 3)) ? 'r' : '-'); putchar((s & (S_IWRITE >> 3)) ? 'w' : '-'); putchar((s & S_ISGID) ? 's' : (s & (S_IEXEC >> 3)) ? 'x' : '-'); putchar((s & (S_IREAD >> 6)) ? 'r' : '-'); putchar((s & (S_IWRITE >> 6)) ? 'w' : '-'); putchar((s & (S_IEXEC >> 6)) ? 'x' : '-'); return(ret); } p /* * ls - perform the -ls function */ void ls(fname) char *fname; { struct stat s; struct passwd *pwp; struct group *grp; char special; if (LSTAT(fname, &s) < 0) { (void) fprintf(stderr, "Bad status <%s>\n", fname); return; } (void) printf("%5d %4d ", s.st_ino, s.st_size/BLOCK_SIZE); special = fmtmode((int)s.st_mode); (void) printf(" %2d ", s.st_nlink); if ((pwp = getpwuid(s.st_uid)) == NULL) (void) printf("%-8d ", s.st_uid); else (void) printf("%-8.8s ", pwp->pw_name); if ((grp = getgrgid(s.st_gid)) == NULL) (void) printf("%-8d ", s.st_gid); else (void) printf("%-8.8s ", grp->gr_name); if (special) (void) printf("%2d, %4d ", s.st_rdev / 256, s.st_rdev % 256); else (void) printf("%8D ", s.st_size); (void) printf("%.12s %s\n", &(ctime(&s.st_mtime))[4], fname); #ifdef MINIX (void) fflush(stdout); /* otherwise nothing comes out??? */ #endif } p /* * action - take the action (held in act). Possible expansion of * braces necessary. */ void action(fname) char *fname; { char *cp; switch(act) { case ACT_PRINT : (void) printf("%s\n", fname); return; case ACT_EXEC : cp = (bracec > 0) ? expand(fname) : cmd; (void) system(cp); return; case ACT_OK : cp = (bracec > 0) ? expand(fname) : cmd; switch(prompt(cp)) { case 'y' : case 'Y' : (void) system(cp); return; default : return; } case ACT_LS : ls(fname); return; default : return; } } p /* * arithcmp - do an arithmetical comparison of the number held * in the number struct, and the parameter n. Note long integers used. * Returns 1 if comparison true, 0 otherwise. */ int arithcmp(np, n) NUM *np; long n; { switch(np->nm_op) { case AR_LT : return(n < np->nm_num); case AR_EQ : return(n == np->nm_num); case AR_GT : return(n > np->nm_num); default : return(0); } } p /* * singlematch - does the file with (complete) file name fname, * and inode number inum meet the search criterion pointed to * by crp. Returns 1 if so, 0 otherwise. */ int singlematch(fname, inum, crp) char *fname; int inum; CRIT *crp; { struct stat s; char *cp; char *cp2; char *cp3; int fnd; switch(crp->cr_type) { case CR_INUM : return(crp->cr_un.cr_n.nm_num == inum); case CR_NAME : for (cp = &fname[strlen(fname)-1] ; *cp && *cp != '/' ; cp--) ; cp++; if (myindex(crp->cr_un.cr_name, '*') != NULL || myindex(crp->cr_un.cr_name, '?') != NULL || myindex(crp->cr_un.cr_name, '[') != NULL) { if (strcmp(crp->cr_un.cr_name, "*") == 0) return(1); for (cp2 = crp->cr_un.cr_name ; *cp2 ; ) { switch(*cp2) { case '?' : cp2++ ; break; case '*' : if (*++cp2 == '\0') return(1); while (*cp && *cp != *cp2) cp++; if (*cp == '\0') return(0); break; case '[' : if ((cp3 = myindex(++cp2, ']')) == NULL) return(0); *cp3 = '\0'; fnd = myindex(cp2, *cp++)!=NULL? 1 : 0; cp2 = cp3 + 1; *cp3 = ']'; if (!fnd) return(0); break; default : if (*cp++ != *cp2++) return(0); } } return(*cp == '\0' ? 1 : 0); } return(strcmp(crp->cr_un.cr_name, cp) == 0 ? 1 : 0); case CR_MTIME : case CR_CTIME : case CR_ATIME : case CR_SIZE : case CR_TYPE : case CR_LINKS : case CR_NEWER : case CR_PERM : case CR_USER : case CR_NOUSER : case CR_GROUP : case CR_NOGROUP : if (LSTAT(fname, &s) < 0) { (void) fprintf(stderr, "Bad status <%s>\n", fname); return(0); } switch(crp->cr_type) { case CR_ATIME : return(arithcmp(&(crp->cr_un.cr_n), s.st_atime)); case CR_CTIME : return(arithcmp(&(crp->cr_un.cr_n), s.st_ctime)); case CR_MTIME : return(arithcmp(&(crp->cr_un.cr_n), s.st_mtime)); case CR_SIZE : return(arithcmp(&(crp->cr_un.cr_n), (long) s.st_size)); case CR_LINKS : return(arithcmp(&(crp->cr_un.cr_n), (long) s.st_nlink)); case CR_NEWER : return(arithcmp(&(crp->cr_un.cr_n), s.st_ctime)); case CR_PERM : fnd = crp->cr_un.cr_n.nm_op == AR_LT ? LONGMASK : SHORTMASK; return((s.st_mode & fnd) == crp->cr_un.cr_n.nm_num); case CR_USER : return(s.st_uid == (short) crp->cr_un.cr_n.nm_num); case CR_NOUSER : return(getpwuid(s.st_uid) == NULL); case CR_GROUP : return(s.st_gid == (short) crp->cr_un.cr_n.nm_num); case CR_NOGROUP : return(getgrgid(s.st_gid) == NULL); case CR_TYPE : switch(*(crp->cr_un.cr_name)) { case 'b' : return((s.st_mode & S_IFMT) == S_IFBLK); case 'c' : return((s.st_mode & S_IFMT) == S_IFCHR); case 'd' : return((s.st_mode & S_IFMT) == S_IFDIR); case 'f' : return((s.st_mode & S_IFMT) == S_IFREG); case 'g' : return(s.st_mode & S_ISGID); case 'u' : return(s.st_mode & S_ISUID); } } default : return(0); } } p /* * match - does the file fname, inode inum match the criteria tree * staarting at np? Returns 1 for yes, 0 otherwise. */ int match(fname, inum, np) char *fname; int inum; NODE *np; { int ret; if (np == NULL) return(0); switch(np->nd_type) { case ND_CRIT : ret = singlematch(fname, inum, np->nd_un.nd_crit); return(np->nd_un.nd_crit->cr_not ? 1 - ret : ret); case ND_OP : switch(np->nd_un.nd_op) { case OP_AND : return(match(fname, inum, np->nd_left) && match(fname, inum, np->nd_right)); case OP_OR : return(match(fname, inum, np->nd_left) || match(fname, inum, np->nd_right)); case OP_CLOSEB : fatal("close bracket in match operator"); } default : (void) printf("unusual type %d\n", np->nd_type); } return(0); } p /* * makedname - make the full file name from the directory name, * file name, and place it in out. If this would overflow, 0 * is returned, 1 otherwise. */ int makedname(d, f, out, outlen) char *d; char *f; char *out; int outlen; { register char *cp; register char *dp; if ((strlen(d) + strlen(f) + 2) > outlen) return(0); for (cp = out, dp = d ; *dp ; *cp++ = *dp++) ; if (d[strlen(d)-1] != '/') *cp++ = '/'; while (*f) *cp++ = *f++; *cp = '\0'; return(1); } p #ifdef MINIX /* * dodir - process the directory dname. Minix uses the V7 file * system, hence the hacked-in ifdefs. */ void dodir(dname) char *dname; { struct stat s; char dirent[LINELEN]; int fd; #define ERR {\ (void) fprintf(stderr, "Bad status <%s>\n", dname);\ return;\ } if (LSTAT(dname, &s) < 0) ERR; if ((s.st_mode & S_IFMT) != S_IFDIR) ERR; if ((fd = open(dname, 0)) < 0) ERR; while (read(fd, &dir, sizeof(dir)) > 0) { if (strcmp(dir.d_name, ".") == 0 || strcmp(dir.d_name, "..") == 0) continue; if (dir.inum == 0) continue; if (!makedname(dname, dir.d_name, dirent, sizeof(dirent))) continue; if (LSTAT(dirent, &s) < 0) { close(fd); ERR; } if (((s.st_mode & S_IFMT) == S_IFDIR) && depthfirst && !prune) dodir(dirent); if (root == NULL || match(dirent, (int) dir.inum, root)) action(dirent); if (((s.st_mode & S_IFMT) == S_IFDIR) && !depthfirst && !prune) dodir(dirent); } close(fd); } p #else /* * dodir - process the directory dname. This function uses the * directory processing functions. */ void dodir(dname) char *dname; { struct direct *dp; struct stat s; char dent[LINELEN]; DIR *dirp; #define ERR {\ (void) fprintf(stderr, "Bad status <%s>\n", dname);\ return;\ } if (LSTAT(dname, &s) < 0) ERR; if ((s.st_mode & S_IFMT) != S_IFDIR) ERR; if ((dirp = opendir(dname)) == NULL) ERR; while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) continue; if (!makedname(dname, dp->d_name, dent, sizeof(dent))) continue; if (LSTAT(dent, &s) < 0) { closedir(dirp); ERR; } if (((s.st_mode & S_IFMT) == S_IFDIR) && depthfirst && !prune) dodir(dent); #ifdef SUN if (root == NULL || match(dent, (int)dp->d_fileno, root)) #else if (root == NULL || match(dent, (int)dp->d_ino, root)) #endif action(dent); if (((s.st_mode & S_IFMT) == S_IFDIR) && !depthfirst && !prune) dodir(dent); } closedir(dirp); } #endif p /* * shortform - 4.3 BSD has a shortform of find, "find name" which is * "find / -name "*name*" -print. */ void shortform(s) char *s; { CRIT *crp; char name[SHORTLINE]; critc = 1; crp = criteria; crp->cr_type = CR_NAME; (void) sprintf(name, "*%s*", s); crp->cr_un.cr_name = name; act = ACT_PRINT; root = newnode(ND_CRIT, 0, crp, (NODE *)NULL, (NODE *)NULL); dodir("/"); exit(0); } p /* * At last... */ main(argc, argv) int argc; char **argv; { prog = argv[0]; if (argc == 1) { (void) fprintf(stderr, "Usage: %s name, or %s path-list predicate-list\n", prog, prog); exit(1); } else if (argc == 2) shortform(argv[1]); doargs(argc, argv); dodir(startdir); exit(0); }