rsalz@uunet.uu.net (Rich Salz) (02/08/90)
Submitted-by: Kenneth Stauffer <cpsc.UCalgary.CA!stauffer> Posting-number: Volume 21, Issue 6 Archive-name: rh2/part01 Rh was written by Ken Stauffer to make the job of finding files easier by allowing the user to enter real C expressions. This notation is much easier to master than the notation used by the find(1) command, because most Unix users already know C. In addition to being easier to use than find(1), rh expressions can be used to select the desired files. This version provides a fairly powerful mini-language for writing search predicates in. It's not unlike the "tw" file walker presented at the Baltimore 89 Usenix. #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 2)." # Contents: MANIFEST Makefile README glob_match.c rh.c rh.h rh.man # rhcmds.c rhdata.c rhdir.c rhrc # Wrapped by rsalz@litchi.bbn.com on Wed Feb 7 15:35:29 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(0 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' END_OF_FILE if test 0 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(1127 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# X# rh: X# >>>> VERSION: 2 <<<< X# Written by: Ken Stauffer X# X# Place one of the following -D's onto the end of X# the definition of CFLAGS. X# X# -DSYSV X# - System V/III like OS (including Xenix) X# X# -DBSD X# - BSD like OS (including SunOS) X# X# Also, place one of the following -D's onto the end of X# the definition of CFLAGS if you have an operating system X# in the specified subclass of the classes listed above. X# X# -DSUNOS_4 X# - SunOS 4.x (subclass of BSD) X# X# -DSYSVR3 X# - System V Release 3.x (subclass of SYSV) X# X# getopt.c uses void, so some systems may also need X# -Dvoid=int X# X XCFLAGS= -DBSD -DSUNOS_4 -O X XOBJS= rhcmds.o rh.o rhparse.o rhdir.o rhdata.o getopt.o glob_match.o X Xrh: $(OBJS) X cc $(CFLAGS) -o rh $(OBJS) X Xrhdir.o: rhdir.c rh.h X cc $(CFLAGS) -c rhdir.c X Xrh.o: rh.c rh.h X cc $(CFLAGS) -c rh.c X Xrhcmds.o: rhcmds.c rh.h X cc $(CFLAGS) -c rhcmds.c X Xrhparse.o: rhparse.c rh.h X cc $(CFLAGS) -c rhparse.c X Xrhdata.o: rhdata.c rh.h X cc $(CFLAGS) -c rhdata.c X Xgetopt.o: getopt.c X cc $(CFLAGS) -Dvoid=int -c getopt.c X Xglob_match.o: glob_match.c X cc $(CFLAGS) -Dvoid=int -c glob_match.c X Xclean: X rm -f rh core $(OBJS) END_OF_FILE if test 1127 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(3646 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' X X >>>> V E R S I O N 2 <<<< X XINTRODUCTION: XRh was written by Ken Stauffer to make the Xjob of finding files easier by allowing the Xuser to enter real C expressions. This notation is Xmuch easier to master than the notation used by the Xfind(1) command, because most Unix users Xalready know C. In addition to being easier to use Xthan find(1), rh expressions can be used to select Xthe desired files. X XCREDITS: X Guy Harris X - Corrected many portability problems. X David MacKenzie X - Manual revisions. Added getopt and regular expressions. X Norm Hutchinson X - Fixed ungetit(). X XCOMPILING: XTo make rh work on your system, you will need to change Xsome -D options in the Makefile. Define ONE of the Xfollowing in the definition of CFLAGS: X X -DBSD - This would be used for most BSD systems. X -DSYSV - System V systems. X XAlso define one of the following: X X -DSUNOS_4 - SunOS 4.x (subclass of BSD) X -DSYSVR3 - System V Release 3.x (subclass of SYSV) X XIn addition to the C source there is also a file called rh.man. XThis is a nroff file and can be created by a command like: X X nroff -man rh.man > rh.cat X XThe resultant file (rh.cat) is sutable for general viewing. X XRUNNING: XThere is a file called rhrc. This file contains some Xexamples of things that can go into a $HOME/.rhrc file. XIf the file "rhrc" is moved to your home directory and renamed Xto ".rhrc" then a command like: X X % rh -l -e writable X XWill do a search of the current directory, executing the function X"writable()", which finds files that other people have write access to. X XOnce rh is made, you can do what you want with it. A good test to Xsee if it is working is to do: X X % rh -vle 1 / X XThis will find all files that makes the constant expression '1' true. XSo if your root, all the files on the system will be found. X XPORTABILITY: XThe file rhdir.c contains code that does directory reading. XThis is most likely where problems will occur. These differences Xhave been taken into account for most versions of unix Xand will hopefully work on your system. XSo far 'rh' works on: X SCO XENIX, BSD 4.3, and SUNOS 4.0 X XGRAMMER: XThe following is the grammer that describes the input language Xrecognized by rh: X X <program> ==> <function list> <expression> EOF X | <function list> <expression> ; X X <function list> ==> <function> X | <function list> <function> X | /* empty */ X X <function> ==> <function heading> { RETURN <expression> ; } X X <function heading> ==> IDENTIFIER X | IDENTIFIER ( ) X | IDENTIFIER ( <idlist> ) X X <idlist> ==> IDENTIFIER <idtail> X <idtail> ==> , <idlist> X | /* empty */ X X <expression> ==> <expr0> ? <expression> : <expression> X X <expr0> ==> <expr1> || <expr1> X X <expr1> ==> <expr2> && <expr2> X X <expr2> ==> <expr3> | <expr3> X X <expr3> ==> <expr4> ^ <expr4> X X <expr4> ==> <expr5> & <expr5> X X <expr5> ==> <expr6> == <expr6> X | <expr6> != <expr6> X X <expr6> ==> <expr7> < <expr7> X | <expr7> > <expr7> X | <expr7> <= <expr7> X | <expr7> >= <expr7> X X <expr7> ==> <expr8> >> <expr8> X | <expr8> << <expr8> X X <expr8> ==> <expr9> + <expr9> X | <expr9> - <expr9> X X <expr9> ==> <expr10> * <expr10> X | <expr10> / <expr10> X | <expr10> % <expr10> X X <expr10> ==> ~ <expr10> X | ! <expr10> X | - <expr10> X | <factor> X X <factor> ==> ( <expression> ) X | NUMBER X | <function call> X | IDENTIFIER X | [ <date spec> ] X | STRING X X <function call> ==> IDENTIFIER X | IDENTIFIER ( <exprlist> ) X | IDENTIFIER ( ) X X <exprlist> ==> <expression> <exprtail> X <exprtail> ==> , <exprlist> X | /* empty */ X X <datespec> ==> NUMBER / NUMBER / NUMBER X X-------------------------------------------------------------------- XKen Stauffer. Xroot@sixk END_OF_FILE if test 3646 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'glob_match.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'glob_match.c'\" else echo shar: Extracting \"'glob_match.c'\" \(7660 characters\) sed "s/^X//" >'glob_match.c' <<'END_OF_FILE' X/* File-name wildcard pattern matching for GNU. X Copyright (C) 1985, 1988 Free Software Foundation, Inc. X X NO WARRANTY X X BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND XFITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY XAND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR XCORRECTION. X X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. X X GENERAL PUBLIC LICENSE TO COPY X X 1. You may copy and distribute verbatim copies of this source file Xas you receive it, in any medium, provided that you conspicuously and Xappropriately publish on each copy a valid copyright notice "Copyright X(C) 1988 Free Software Foundation, Inc."; and include following the Xcopyright notice a verbatim copy of the above disclaimer of warranty Xand of this License. X X 2. You may modify your copy or copies of this source file or Xany portion of it, and copy and distribute such modifications under Xthe terms of Paragraph 1 above, provided that you also do the following: X X a) cause the modified files to carry prominent notices stating X that you changed the files and the date of any change; and X X b) cause the whole of any work that you distribute or publish, X that in whole or in part contains or is a derivative of this X program or any part thereof, to be licensed at no charge to all X third parties on terms identical to those contained in this X License Agreement (except that you may choose to grant more extensive X warranty protection to some or all third parties, at your option). X X c) You may charge a distribution fee for the physical act of X transferring a copy, and you may at your option offer warranty X protection in exchange for a fee. X XMere aggregation of another unrelated program with this program (or its Xderivative) on a volume of a storage or distribution medium does not bring Xthe other program under the scope of these terms. X X 3. You may copy and distribute this program (or a portion or derivative Xof it, under Paragraph 2) in object code or executable form under the terms Xof Paragraphs 1 and 2 above provided that you also do one of the following: X X a) accompany it with the complete corresponding machine-readable X source code, which must be distributed under the terms of X Paragraphs 1 and 2 above; or, X X b) accompany it with a written offer, valid for at least three X years, to give any third party free (except for a nominal X shipping charge) a complete machine-readable copy of the X corresponding source code, to be distributed under the terms of X Paragraphs 1 and 2 above; or, X X c) accompany it with the information you received as to where the X corresponding source code may be obtained. (This alternative is X allowed only for noncommercial distribution and only if you X received the program in object code or executable form alone.) X XFor an executable file, complete source code means all the source code for Xall modules it contains; but, as a special exception, it need not include Xsource code for modules which are standard libraries that accompany the Xoperating system on which the executable file runs. X X 4. You may not copy, sublicense, distribute or transfer this program Xexcept as expressly provided under this License Agreement. Any attempt Xotherwise to copy, sublicense, distribute or transfer this program is void and Xyour rights to use the program under this License agreement shall be Xautomatically terminated. However, parties who have received computer Xsoftware programs from you with this License Agreement will not have Xtheir licenses terminated so long as such parties remain in full compliance. X X XIn other words, you are welcome to use, share and improve this program. XYou are forbidden to forbid anyone else to use, share and improve Xwhat you give them. Help stamp out software-hoarding! */ X X/* Match the pattern PATTERN against the string TEXT; X return 1 if it matches, 0 otherwise. X X A match means the entire string TEXT is used up in matching. X X In the pattern string, `*' matches any sequence of characters, X `?' matches any character, [SET] matches any character in the specified set, X [^SET] matches any character not in the specified set. X X A set is composed of characters or ranges; a range looks like X character hyphen character (as in 0-9 or A-Z). X [0-9a-zA-Z_] is the set of characters allowed in C identifiers. X Any other character in the pattern must be matched exactly. X X To suppress the special syntactic significance of any of `[]*?^-\', X and match the character exactly, precede it with a `\'. X X If DOT_SPECIAL is nonzero, X `*' and `?' do not match `.' at the beginning of TEXT. */ X Xint Xglob_match (pattern, text, dot_special) X char *pattern, *text; X int dot_special; X{ X register char *p = pattern, *t = text; X register char c; X X while ((c = *p++)) X { X switch (c) X { X case '?': X if (*t == 0 || (dot_special && t == text && *t == '.')) return 0; X else ++t; X break; X X case '\\': X if (*p++ != *t++) return 0; X break; X X case '*': X if (dot_special && t == text && *t == '.') X return 0; X return star_glob_match (p, t); X X case '[': X { X register char c1 = *t++; X register int invert = (*p == '^'); X X if (invert) p++; X X c = *p++; X while (1) X { X register char cstart = c, cend = c; X X if (c == '\\') X { X cstart = *p++; cend = cstart; X } X c = *p++; X if (c == '-') X { cend = *p++; if (cend == '\\') cend = *p++; c = *p++; } X if (c1 >= cstart && c1 <= cend) goto match; X if (c == ']') X break; X } X if (!invert) return 0; X break; X X match: X /* Skip the rest of the [...] construct that already matched. */ X while (c != ']') X { X c = *p++; X if (c == '\\') p++; X } X if (invert) return 0; X break; X } X X default: X if (c != *t++) return 0; X } X } X X if (*t) return 0; X return 1; X} X X/* Like glob_match, but match PATTERN against any final segment of TEXT. */ X Xstatic int Xstar_glob_match (pattern, text) X char *pattern, *text; X{ X register char *p = pattern, *t = text; X register char c, c1; X X while ((c = *p++) == '?' || c == '*') X { X if (c == '?' && *t++ == 0) X return 0; X } X X if (c == 0) X return 1; X X if (c == '\\') c1 = *p; X else c1 = c; X X for (;;) X { X if ((c == '[' || *t == c1) X && glob_match (p - 1, t, 0)) X return 1; X if (*t++ == 0) return 0; X } X} END_OF_FILE if test 7660 -ne `wc -c <'glob_match.c'`; then echo shar: \"'glob_match.c'\" unpacked with wrong size! fi # end of 'glob_match.c' fi if test -f 'rh.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rh.c'\" else echo shar: Extracting \"'rh.c'\" \(6063 characters\) sed "s/^X//" >'rh.c' <<'END_OF_FILE' X X/* ---------------------------------------------------------------------- X * FILE: rh.c X * VERSION: 2 X * Written by: Ken Stauffer X * X * printhelp(), execute(), exam1(), exam2(), exam3(), main() X * X * X * ---------------------------------------------------------------------- */ X X#include <sys/types.h> X#include <sys/stat.h> X#include "rh.h" X Xstatic char usage[]={ X"Usage: %s [-vhlr] [ [-e expr] | [-f filename ] ] [-x command ] file ...\n" X}; X X/* ---------------------------------------------------------------------- X * printhelp: X * Print out the help screen. The string 's' is argv[0]. X * Called when the -h option is used. X * X */ X Xprinthelp(s) Xchar *s; X{ X int i; X struct symbol *p; X X printf(usage,s); X printf("options:\n"); X printf("\t-h show this message\n"); X printf("\t-l long filename output\n"); X printf("\t-r makes %s non-recursive\n",s); X printf("\t-v verbose output\n"); X printf("\t-e get expression from the command line\n"); X printf("\t-f get expression from file\n"); X printf("\t-x execute a unix command for matching files\n\n"); X X printf("\tvalid symbols:\n"); X for(i=1, p=symbols; p; p=p->next, i++) X printf("%12s%s", p->name, X ((i-1)%5==4 || !p->next ) ? "\n" : " "); X X printf("\tC operators:\n"); X printf("\t! ~ - * / %% + < <= > >= == != & ^ | << >> && || ?:\n"); X printf("\tspecial operators:\n"); X printf("\t$username , \"*.c\" , [yyyy/mm/dd]\n\n"); X} X X/* ---------------------------------------------------------------------- X * execute: X * Execute the program contained in the StackProgram[] X * array. Each element of the StackProgram[] array contains X * a pointer to a function. X * Programs are NULL terminated. X * Returns the result of the expression. X * X */ X Xexecute() X{ X register long eval; X register int (*efunc)(); X X SP=0; X for(PC=startPC; (efunc=StackProgram[PC].func) ; PC++) { X eval=StackProgram[PC].value; X (*efunc)(eval); X if( SP >= MEM ) { X fprintf(stderr,"stack overflow\n"); X exit(1); X } X } X return( Stack[0] ); X} X X/* ---------------------------------------------------------------------- X * exam1: exam2: exam3: X * One of these functions is called for every file that 'rh' examines. X * exam{1,2,3}() first calls execute to see if the X * expression is true, it then prints the file if the expression X * evaluated to true (non-zero). X * X */ X X/* print file out by itself */ Xexam1() X{ X if( execute() ) printf("%s\n",attr.fname); X} X X/* long output of file */ Xexam2() X{ X if( execute() ) printentry(attr.verbose,attr.buf,attr.fname); X} X X/* do a system(3) call to desired command */ Xexam3() X{ X char command[ 2048 + 1 ]; X char *p,*q,*r,*strrchr(); X int rv; X X if( execute() ) { X p=command; X q=attr.command; X while( *q ) { X if( *q != '%' ) *p++ = *q++; X else { X q += 1; X if( *q == 's' ) { X r = attr.fname; X while( *p++ = *r++ ); X p -= 1; X } else if( *q == 'S' ) { X r = strrchr(attr.fname,'/'); X r = (r) ? r+1 : attr.fname; X while( *p++ = *r++ ); X p -= 1; X } else *p++ = '%'; X q += 1; X } X } X *p = '\0'; X rv = system(command); X if( attr.verbose ) printf("%s exit(%d)\n",command,rv); X } X} X X X/* ---------------------------------------------------------------------- X * main: X * parse arguments. X * gnu getopt() is used here. X * -l, -r, -h, -v options can occur as often as desired. X * -f,-x and -e can only occur once and MUST have an argument. X * X * Read and "compile" the $HOME/.rhrc file, if it exists. X * Read and "compile" any -f filename, if present. X * Read and "compile" any -e expression, if present. X * If after all that no start expression is found then read from X * stdin for one. X * Perform the recursive hunt on remaining arguments. X * X */ X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X extern int optind; X extern char *optarg; X char *dashe,*dashf,*strcat(),*getenv(),initfile[ 1024+1 ]; X int i,r; X int dashr,dashh,dashl; X int (*examptr)(); X X /* defaults */ X dashe = NULL; /* -e option */ X dashl = 0; /* -l */ X dashf = NULL; /* -f */ X dashr = 1; /* -r */ X dashh = 0; /* -h */ X attr.verbose = 0; /* -v */ X attr.command = NULL; /* -x */ X examptr = exam1; /* default output function */ X X while ((i = getopt(argc, argv, "lrhvx:e:f:")) != EOF) { X switch( i ) { X case 'l': examptr = exam2; dashl = 1; break; X case 'r': dashr = 0; break; X case 'h': dashh = 1; break; X case 'v': attr.verbose = 1; break; X case 'x': X if( attr.command ) { X fprintf(stderr, "%s: too many -x options\n",argv[0]); X exit(1); X } X examptr = exam3; X attr.command = optarg; X break; X case 'e': X if( dashe ) { X fprintf(stderr, "%s: too many -e options\n",argv[0]); X exit(1); X } X dashe = optarg; X break; X case 'f': X if( dashf ) { X fprintf(stderr, "%s: too many -f options\n",argv[0]); X exit(1); X } X dashf = optarg; X break; X default: X fprintf(stderr,"%s: use -h for help\n", argv[0],i); X fprintf(stderr,usage, argv[0]); X exit(1); X } X if( attr.command && dashl ) { X fprintf(stderr, X "%s: cannot have both -x and -l options\n", X argv[0]); X exit(1); X } X } X X PC = 0; X startPC = -1; X rhinit(); X if( dashh ) printhelp(argv[0]); X X expfname = getenv( HOMEENV ); X if( expfname ) { X strcpy(initfile,expfname); X expfname = strcat(initfile,RHRC); X if( (expfile = fopen(expfname,"r")) != NULL ) { X expstr = NULL; X program(); X } X } X X if( dashf ) { X expstr = NULL; X expfname = dashf; X if( (expfile = fopen(expfname,"r")) == NULL ) { X fprintf(stderr,"%s: ", argv[0]); X perror(expfname); X exit(1); X } X program(); X } X if( dashe ) { X expfile = NULL; X expstr = dashe; X program(); X } X if( startPC == -1 ) { X expstr = NULL; X expfname = "stdin"; X expfile = stdin; X program(); X } X X if( startPC == -1 ) { X fprintf(stderr,"%s: no start expression specified\n", X argv[0] ); X exit(1); X } X rhfinish(); X X if( optind >= argc ) { X r=ftrw(".",examptr,(dashr)? DEPTH :1); X if(r == -1) perror("."); X } else X for(; optind<argc; optind++) { X r=ftrw(argv[optind],examptr,(dashr)? DEPTH :1); X if( r == -1 ) perror(argv[optind]); X } X exit(0); X} END_OF_FILE if test 6063 -ne `wc -c <'rh.c'`; then echo shar: \"'rh.c'\" unpacked with wrong size! fi # end of 'rh.c' fi if test -f 'rh.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rh.h'\" else echo shar: Extracting \"'rh.h'\" \(3361 characters\) sed "s/^X//" >'rh.h' <<'END_OF_FILE' X X/* ---------------------------------------------------------------------- X * FILE: rh.h X * VERSION: 2 X * Written by: Ken Stauffer X * This header contains the #define's for the tokens. X * It also contains structure definitions. X * X * ---------------------------------------------------------------------- */ X X#include <stdio.h> X X#define RHRC "/.rhrc" /* start up file */ X#define RHENV "RH" /* rh environment variable */ X#define HOMEENV "HOME" /* path to your home directory */ X X/* Definition of returned tokens from the lexical analyzer */ X X#define OR 256 /* || */ X#define AND 257 /* && */ X#define LE 258 /* <= */ X#define GE 259 /* >= */ X#define NE 260 /* != */ X#define EQ 261 /* == */ X#define SHIFTL 262 /* << */ X#define SHIFTR 263 /* >> */ X#define NOP 264 /* no-operation */ X#define NUMBER 265 /* literal numbers and symbolic constants */ X#define STR 266 /* regular expression strings */ X#define FIELD 267 /* file fields (size,mode,nlinks) */ X#define FUNCTION 268 /* user defined function */ X#define RETURN 269 /* return keyword */ X#define PARAM 270 /* parameter to functions */ X#define IDENTIFIER 271 X X#define LENGTH 2000 /* size of stack program */ X#define MEM 1000 /* size of stack */ X#define IDLENGTH 20 /* length of an identifier */ X#define STRLEN 200 /* total chars available for strings */ X X#if BSD X#define DEPTH getdtablesize() X#define strrchr rindex X#ifdef SUNOS_4 X#define POSIX_DIRECTORY_LIBRARY X#endif /* SUNOS_4 */ X#endif /* BSD */ X X#ifdef SYSV X#ifdef SYSVR3 X#define DEPTH ulimit(4, 0L) X#define POSIX_DIRECTORY_LIBRARY X#else /* SYSVR3 */ X/* This value was arbitrarily chosen */ X#define DEPTH 20 X#endif /* SYSVR3 */ X#endif /* SYSV */ X X/* X * Structure of a "rh-assembly" instruction. X * X */ X Xstruct instr { X int (*func)(); X long value; X}; X X/* X * Structure of a symbol. X * X */ X Xstruct symbol { X char *name; X int type; X long value; X int (*func)(); X struct symbol *next; X}; X X/* X * Structure defining the rh runtime environment. X * X */ X Xstruct runtime { X struct stat *buf; /* stat info of current file */ X char *fname; /* file name of current file */ X int depth; /* relative depth of current file */ X int (*func)(); /* examination function */ X int prune; /* flag to indicate prunning */ X int verbose; /* used by the (*func)() routine */ X char *command; /* command to exec for current file */ X}; X X#ifndef DATA X extern struct symbol *symbols; X X extern struct symbol *tokensym; X extern long tokenval; X extern long token; X X extern struct instr StackProgram[]; X extern int PC; X extern int startPC; X extern long Stack[]; X extern int SP; X extern int FP; /* frame pointer */ X X extern struct runtime attr; X X extern char Strbuf[]; X extern int strfree; X X extern char *expstr; X extern char *expfname; X extern FILE *expfile; X X#endif X Xextern c_or(), c_and(), c_le(), c_lt(), c_ge(), X c_gt(), c_ne(), c_eq(), c_bor(), c_band(), X c_bxor(), c_not(), c_plus(), c_mul(), c_minus(), X c_div(), c_mod(), c_number(), c_atime(), c_ctime(), X c_dev(), c_gid(), c_ino(), c_mode(), c_mtime(), X c_nlink(), c_rdev(), c_size(), c_uid(), c_str(), X c_bnot(), c_uniminus(), c_lshift(), c_rshift(), c_qm(), X c_colon(), c_return(), c_func(), c_param(), c_depth(), X c_baselen(), c_pathlen(), c_prune(); X END_OF_FILE if test 3361 -ne `wc -c <'rh.h'`; then echo shar: \"'rh.h'\" unpacked with wrong size! fi # end of 'rh.h' fi if test -f 'rh.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rh.man'\" else echo shar: Extracting \"'rh.man'\" \(12150 characters\) sed "s/^X//" >'rh.man' <<'END_OF_FILE' X.TH RH 1L X.SH NAME Xrh - recursive file locater (rawhide) >> VERSION 2 << X.SH SYNOPSIS X.B rh X[ X.B \-vhlr X] [ X.B \-f filename X] X.br X.RS X[ X.B \-e expression X] [ X.B \-x command X] X.BR file ... X.RE X.SH DESCRIPTION X.I Rh Xrecursively searches the file system starting at each given X.IR file Xfor files that make a C expression true. If no files Xare listed, the current working directory is used. X.PP XExpressions for X.I rh Xcan come from the command line (with the X.I \-e Xoption), a file (with the X.I \-f Xoption), or from the standard input (the default). XThe basic form of an X.I rh Xexpression is a C expression which may optionally define and or call Xuser defined functions. These C expressions may contain Xconstants, variables, and all the usual C operators. X.PP X.I Constants Xare either numeric or symbolic. Symbolic constants are based Xon the constants defined in the file X.IR /usr/include/sys/stat.h ; Xonly the useful constants are implemented. XThe ``S_'' prefix from the symbol name is omitted. X(eg. S_IFMT would used as IFMT). X.PP X.I Variables Xare symbols that specify a field in the Xstat structure (e.g., st_size, st_mode) or some other attribute of the file. XFor each file examined by X.IR rh , Xthese internal variables are updated to match the current Xfile. For convenience, the ``st_'' prefix is dropped from variable Xnames. X.PP X.I Functions Xare a means of associating a C expression with a function name. XThis allows complex expressions to be easily composed from simpler ones. XThe value Xof a function call is the value of the expression represented by the Xfunction. For example: X.PP X.RS 8 Xfoo(x) X.br X{ X.br Xreturn( x-1 ); X.br X} X.RE X.PP XIf the above function were given to Rh, it would define a function that could be Xused later. If X.I foo Xwere called with 667, then the value of the call to X.I foo Xwould be equal to 666. X.PP X.SH OPTIONS X.I Rh Xoptions can appear in any order; multiple options can Xbe given within the same argument. X.TP X.I \-r XPrevents X.I rh Xfrom recursively searching for files. X.TP X.I \-l XNormally X.I rh Xprints each matching filename on a line by itself. The X.I \-l Xoption causes the matching files' permission modes and sizes Xto be displayed as well, in a format similar to that of the X.IR ls (1) Xcommand. X.TP X.I \-h XCauses X.I rh Xto display a help message. The message Xexplains the command line usage, a list of Xavailable constants and variables and a list Xof valid operators. X.I Rh Xthen continues as though the X.I \-h Xoption were not present. X.TP X.I \-f filename XUses X.I filename Xas the name of a file containing a X.I rh Xexpression. Functions may also be defined in this file. X.TP X.I \-e expression XUses X.I expression Xas the expression Xthat will be used for the file search. XSince many of the operators are also shell Xmeta-characters and since rh expressions may contain Xspaces, it is strongly recommended that the X.I expression Xbe enclosed in single quotes, ''. If both the -e and -f options Xoccur together then the -f option is processed FIRST, followed by the X-e option. This means that an expression specified with the -e option Xmay use functions defined from the X.I -f file. X.TP X.I \-v XVerbose. Causes the -l option to output more information and Xthe -x option to print out the command executed and the return value. X.TP X.I \-x command XExecute X.I command Xusing system(3) for each matching file. The string X.I command Xmay contain a %s which will be substituted with the full path name. A X%S (uppercase 'S') will be substituted with the base name. For example, Xgiven the file /etc/passwd the values for %s and %S would be: X/etc/passwd and passwd, respectively. X.SH USAGE X.SS "Rh grammer" XThis is the grammer that rh will accept. X.PP X.TP X<program> ::= X.RS 6 X <function list> <expression> EOF X.br X | <function list> <expression> ; X.RE X.PP X.TP X<function list> ::= X.RS 6 X <function list> <function> X.br X | <function> X.br X | /* empty */ X.RE X.PP X.TP X<function> ::= X.RS 6 X <function heading> { RETURN <expression> ; } X.RE X.PP X.TP X<function heading> ::= X.RS 6 X IDENTIFIER X.br X | IDENTIFIER ( ) X.br X | IDENTIFIER ( <idlist> ) X.RE X.PP X.TP X<idlist> ::= X.RS 6 X <idlist> , IDENTIFIER X.br X | IDENTIFIER X.RE X.PP X.TP X<expression> ::= X.RS 6 X <expression> ? <expression> : <expression> X.br X | <expression> || <expression> X.br X | <expression> && <expression> X.br X | <expression> | <expression> X.br X | <expression> ^ <expression> X.br X | <expression> & <expression> X.br X | <expression> == <expression> X.br X | <expression> != <expression> X.br X | <expression> < <expression> X.br X | <expression> > <expression> X.br X | <expression> <= <expression> X.br X | <expression> >= <expression> X.br X | <expression> >> <expression> X.br X | <expression> << <expression> X.br X | <expression> + <expression> X.br X | <expression> - <expression> X.br X | <expression> * <expression> X.br X | <expression> / <expression> X.br X | <expression> % <expression> X.br X | ~ <expression> X.br X | ! <expression> X.br X | - <expression> X.br X | <factor> X.RE X.PP X.TP X<factor> ::= X.RS 6 X ( <expression> ) X.br X | NUMBER X.br X | <function call> X.br X | IDENTIFIER X.br X | [ <date spec> ] X.br X | STRING X.RE X.PP X.TP X<function call> ::= X.RS 6 X IDENTIFIER X.br X | IDENTIFIER ( <exprlist> ) X.br X | IDENTIFIER ( ) X.RE X.PP X.TP X<exprlist> ::= X.RS 6 X <exprlist> , <expression> X.br X | <expression> X.RE X.PP X.TP X<datespec> ::= X.RS 6 X NUMBER / NUMBER / NUMBER X.RE X.PP X.SS "Search order:" X.I Rh Xinitally looks for a X.I $HOME/.rhrc Xand if it exists it will be read in. Next, any file specified by the X.I \-f Xoption is read followed by any expression specified with the X.I \-e Xoption. If after all that, an expression, defined outside of a function, Xhas not been encountered then stdin will be read for such an expression. XAn error will result if no expression has been encountered. X.PP XA X.I $HOME/.rhrc Xwill usually contain function definitions that will be accessable Xfor the user when they enter in a search expression. X.PP X.SS "The valid constants are:" X.IP NOW XThis constant is set to the current time at the start of X.I rh. XIt is used to make comparisons with atime, ctime and mtime. X.IP days XThis is equal to the number of seconds in a day. X.IP hours XNumber of seconds in an hour. X.IP weeks XNumber of seconds in a week. X.IP "IFBLK IFDIR IFLNK IFMT IFREG IFSOCK ISGID ISUID ISVTX" Xsee X.IR stat (2) Xfor an explanation. X.SS "The valid variables are:" X.PP X.IP depth XThis variable is set to the relative depth in the directory search Xthat the current file is at. X.IP strlen XThis is set to the length of the filename. For example strlen Xwould be equal to 4 given the file: "/tmp/core" because "core" is X4 characters long. X.IP prune XThis varible always returns 0, but as a side-effect causes the Xsearch path to be "cut-short" when evaluated. This can be used to prune the Xdirectory search. X.I prune Xis usually used with the ?: operator to conditionally evaluate the prune Xvariable. X.IP "atime,ctime,dev,gid,ino,mode,mtime,nlink,rdev,size,uid" Xsee X.IR stat (2) Xfor an explanation. X.SS "The valid C operators are:" X.PP X! ~ - * / % + < <= > >= == != & ^ | << >> && || ?: X.PP XOperator precedence, associativity and semantics are the same as Xin C. X.SS "Special operators:" X.IP $username XThis operator evaluates to the integer user id of X.I username. XAs a special case the symbol $$ evaluates to the Xuid of the user currently running X.I rh. X.IP """*.c""" XThis operator evaluates to true if the current filename matches Xthe quoted expression, which is a shell globbing pattern. XThe recognized metacharacters are: X.RS X.IP ``*'' Xto match any number of characters, including zero (except that, as in Xthe shell, it does not match a leading ``.''); X.IP ``?'' Xto match any single character (except for a leading ``.''); X.IP ``[SET]'' Xto match any character in the given set (ranges can be included); X.IP ``[^SET]'' Xto match any character not in the given set; X.IP ``\e\e'' Xto escape the special meaning of any of the above metacharacters. X.RE X.PP XWhen doing comparisons, only the base name is examined, not Xleading paths. X.IP [yyyy/mm/dd] XThe date enclosed in the brackets, ``[]'', will evaluate to a number of Xseconds past January 1, 1970, which is Xsuitable for comparing with atime, mtime or ctime. XThe year cannot be abbreviated to its last two digits. X.PP XThe special operators Xhave higher precedence than the C operators. X.SS "Lexical conventions:" X.PP XNumbers may be entered in octal by preceding them with Xa leading zero. Otherwise numbers are taken to be in Xdecimal. X.PP XText enclosed in /* and */ will be ignored. This can be Xused for commenting X.I rh Xexpression files. X.PP XThe start expression may be terminated by either Xa ``;'' or the end of the file or argument. X.SH EXAMPLES XThe following are examples of X.I rh Xexpressions. X.PP X.RS 8 X(mode & 022) && (uid == $joe ); X.PP X.RE XMatches all files that have uid equal to username ``joe'' and Xare writable by other people. X.PP X.RS 8 X!uid && (mode & ISUID ) && X.br X(mode & 02); X.PP X.RE XMatches all files that are owned by root (uid==0) and that Xhave set-uid on execution bit set, and are writable. X.PP X.RS 8 X(size > 10*1024) && (mode & 0111) && X.br X(atime <= NOW-24*3600); X.RE X.PP XFinds all executable files larger than 10K that Xhave not been executed in the last 24 hours. X.PP X.RS 8 Xsize < ( ("*.c") ? 4096 : 32*1024 ); X.RE X.PP XFinds C source files smaller than 4K and Xother files smaller than 32K. No other files will match. X.PP X.RS 8 X!(size % 1024); X.RE X.PP XMatches files that are a multiple of 1K. X.PP X.RS 8 Xmtime >= [1982/3/1] && mtime <= [1982/3/31]; X.RE X.PP XFinds files that were modified during March, 1982. X.PP X.RS 8 Xstrlen >= 4 && strlen <= 10; X.RE X.PP XThis expression will print files whose filenames are between X4 and 10 characters in length. X.PP X.RS 8 Xdepth > 3; X.RE X.PP XMatches files that are at a RELATIVE depth of 3 or more. X.PP X.RS 8 X( "tmp" || "bin" ) ? prune : "*.c"; X.RE X.PP XThis expression does a search for all "*.c" files, however it will Xnot look into any directories called "bin" or "tmp". This is because when Xsuch a filename is encountered the prune variable is evaluated, causing Xfurther searching with the current path to stop. The general form of this Xwould be: X.PP X.RS 8 X("baddir1" || "baddir2" || ... || "baddirn") ? X.br X.RS 8 Xprune : <search expr>; X.RE X.RE X.PP X.SH "ADVANCED EXAMPLES" XThe following examples show the use of function definitions and other Xadvanced features of X.I "Rh." X Consider: X.PP X.RS 8 Xdir() X.br X{ X.br Xreturn ( (mode & IFMT) == IFDIR ); X.br X} X.br X.RE X.PP XThis declares a function that returns true if the current file is a directory Xand false otherwise. The function X.PP X.I dir Xnow may be used in other expressions. X.PP X.RS 8 Xdir() && !mine(); X.RE X.PP XThis matches files that are directories and are not owned by Xthe user. This assumes the user has written a mine() function. Since X.I dir Xand X.I mine Xtake no arguments they may be called like: X.PP X.RS 8 Xdir && !mine; X.RE X.PP XAlso when declaring a function that takes no arguments the parenthesis Xmay be omitted. For example: X.PP X.RS 8 Xmine X.br X{ X.br Xreturn uid == $joe; X.br X} X.br X.RE X.PP XThis declares a function mine, that evaluates true when a file Xis owned by user name 'joe'. An alternate way to write mine would be: X.PP X.RS 8 Xmine(who) X.br X{ X.br Xreturn uid == who; X.br X} X.br X.RE X.PP XThis would allow mine to be called with an argument, for example: X.PP X.RS 8 Xmine( $sue ) || mine( $joe ); X.RE X.PP XThis expression is true of any file owned by user name 'sue' or 'joe'. XSince the parenthesis are optional for functions that take no Xarguments, it would be possible to define functions that can be used Xexactly like constants, or handy macros. Suppose the above definition Xof X.I dir Xwas placed in a users X.I $HOME/.rhrc XThen the command: X.PP X.RS 8 Xrh -e dir X.RE X.PP Xwould execute the expression 'dir' which will print out all directories. XRh functions can be recursive. X.SH "FILES" X$HOME/.rhrc X.PP X.SH "SEE ALSO" Xchmod(1), find(1), ls(1), stat(2) X.PP XThe C programming language. X.SH AUTHOR XKen Stauffer (University of Calgary) X.PP Xstauffer@sixk X.SH BUGS XThe date operator should also allow for time to be entered. XThe date operator can be off by a day, if the Xtime on the file is close to midnight. END_OF_FILE if test 12150 -ne `wc -c <'rh.man'`; then echo shar: \"'rh.man'\" unpacked with wrong size! fi # end of 'rh.man' fi if test -f 'rhcmds.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rhcmds.c'\" else echo shar: Extracting \"'rhcmds.c'\" \(4062 characters\) sed "s/^X//" >'rhcmds.c' <<'END_OF_FILE' X X/* ---------------------------------------------------------------------- X * FILE: rhcmds.c X * VERSION: 2 X * Written by: Ken Stauffer X * This file contains the functions that do the evaluation of X * the stack program. X * These functions are simple, and behave like RPN operators, that is X * they use the last two values on the stack, apply an operator X * and push the result. Similarly for unary ops. X * X * ---------------------------------------------------------------------- */ X X#include "rh.h" X#include <sys/types.h> X#include <sys/stat.h> X Xc_or(i) long i; { Stack[SP-2]=Stack[SP-2] || Stack[SP-1]; SP--; } Xc_and(i) long i; { Stack[SP-2]=Stack[SP-2] && Stack[SP-1]; SP--; } Xc_le(i) long i; { Stack[SP-2]=Stack[SP-2] <= Stack[SP-1]; SP--; } Xc_lt(i) long i; { Stack[SP-2]=Stack[SP-2] < Stack[SP-1]; SP--; } Xc_ge(i) long i; { Stack[SP-2]=Stack[SP-2] >= Stack[SP-1]; SP--; } Xc_gt(i) long i; { Stack[SP-2]=Stack[SP-2] > Stack[SP-1]; SP--; } Xc_ne(i) long i; { Stack[SP-2]=Stack[SP-2] != Stack[SP-1]; SP--; } Xc_eq(i) long i; { Stack[SP-2]=Stack[SP-2] == Stack[SP-1]; SP--; } Xc_bor(i) long i; { Stack[SP-2]=Stack[SP-2] | Stack[SP-1]; SP--; } Xc_band(i) long i; { Stack[SP-2]=Stack[SP-2] & Stack[SP-1]; SP--; } Xc_bxor(i) long i; { Stack[SP-2]=Stack[SP-2] ^ Stack[SP-1]; SP--; } Xc_lshift(i) long i; { Stack[SP-2]=Stack[SP-2] << Stack[SP-1]; SP--; } Xc_rshift(i) long i; { Stack[SP-2]=Stack[SP-2] >> Stack[SP-1]; SP--; } Xc_plus(i) long i; { Stack[SP-2]=Stack[SP-2] + Stack[SP-1]; SP--; } Xc_mul(i) long i; { Stack[SP-2]=Stack[SP-2] * Stack[SP-1]; SP--; } Xc_minus(i) long i; { Stack[SP-2]=Stack[SP-2] - Stack[SP-1]; SP--; } Xc_div(i) long i; { Stack[SP-2]=Stack[SP-2] / Stack[SP-1]; SP--; } Xc_mod(i) long i; { Stack[SP-2]=Stack[SP-2] % Stack[SP-1]; SP--; } X X X/* unary instructions */ X Xc_not(i) long i; { Stack[SP-1] = ! Stack[SP-1]; } Xc_bnot(i) long i; { Stack[SP-1] = ~ Stack[SP-1]; } Xc_uniminus(i) long i; { Stack[SP-1] = - Stack[SP-1]; } X X/* trinary operator ?: */ X Xc_qm(i) long i; { PC = (Stack[SP-1]) ? PC : i; SP--; } Xc_colon(i) long i; { PC = i; } X X/* accessing a parameter */ X Xc_param(i) Xlong i; X{ X Stack[ SP++ ] = Stack[ FP + i ]; X} X X/* calling a function */ X Xc_func(i) Xlong i; X{ X Stack[ SP++ ] = PC; X Stack[ SP++] = FP; X PC = i; X FP = SP-(StackProgram[PC].value+2); X} X X/* returning from a function */ X Xc_return(i) Xlong i; X{ X PC = Stack[ SP-3 ]; X FP = Stack[ SP-2 ]; X Stack[ SP-(3+i) ] = Stack[ SP-1 ]; X SP -= (2+i); X} X X/* operand functions */ X Xc_number(i) long i; { Stack[SP++] = i; } Xc_atime(i) long i; { Stack[SP++] = attr.buf->st_atime; } Xc_ctime(i) long i; { Stack[SP++] = attr.buf->st_ctime; } Xc_dev(i) long i; { Stack[SP++] = attr.buf->st_dev; } Xc_gid(i) long i; { Stack[SP++] = attr.buf->st_gid; } Xc_ino(i) long i; { Stack[SP++] = attr.buf->st_ino; } Xc_mode(i) long i; { Stack[SP++] = attr.buf->st_mode; } Xc_mtime(i) long i; { Stack[SP++] = attr.buf->st_mtime; } Xc_nlink(i) long i; { Stack[SP++] = attr.buf->st_nlink; } Xc_rdev(i) long i; { Stack[SP++] = attr.buf->st_rdev; } Xc_size(i) long i; { Stack[SP++] = attr.buf->st_size; } Xc_uid(i) long i; { Stack[SP++] = attr.buf->st_uid; } Xc_depth(i) long i; { Stack[SP++] = attr.depth; } Xc_prune(i) long i; { Stack[SP++] = 0; attr.prune = 1; } X X/* calculate the filename length */ X Xc_baselen(i) Xlong i; X{ X char *c; register int len; X X len = 0; X for(c=attr.fname; *c; c++ ) X if( *c == '/' ) len = 0; X else len += 1; X Stack[SP++] = len; X} X X/* ---------------------------------------------------------------------- X * c_str: X * This implements the regular expression stuff. X * 'i' is an index into the array Strbuf[]. The X * string contained there is the actual '\0' terminated X * string that occured in the expression (eg "*.BAK" ), minus X * the quotes "". X */ X Xc_str(i) Xlong i; X{ X char *tail,*strrchr(); X X tail = strrchr(attr.fname, '/'); X tail = (tail) ? tail+1 : attr.fname; X Stack[SP++] = glob_match(&Strbuf[i], tail, 1); X} END_OF_FILE if test 4062 -ne `wc -c <'rhcmds.c'`; then echo shar: \"'rhcmds.c'\" unpacked with wrong size! fi # end of 'rhcmds.c' fi if test -f 'rhdata.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rhdata.c'\" else echo shar: Extracting \"'rhdata.c'\" \(2739 characters\) sed "s/^X//" >'rhdata.c' <<'END_OF_FILE' X X/* ---------------------------------------------------------------------- X * FILE: rhdata.c X * VERSION: 2 X * Written by: Ken Stauffer X * This file contains the predefined symbol table, and related data X * structures. X * X * ---------------------------------------------------------------------- */ X X#include <sys/types.h> X#include <sys/stat.h> X#define DATA X#include "rh.h" X Xstruct symbol *symbols; X Xstruct symbol *tokensym; Xlong tokenval; Xlong token; X Xchar Strbuf[ STRLEN+1 ]; Xint strfree=0; X Xstruct instr StackProgram[ LENGTH ]; Xint PC; Xint startPC; X Xlong Stack[ MEM+3 ]; Xint SP; Xint FP; X Xstruct runtime attr; X X/* X * The following variables specify where the input is comming from. X * If expstr == NULL then the input is certainly not from there, and X * instead is taken from expfile. X * else expstr is used as input. X * X */ X Xchar *expstr; XFILE *expfile; Xchar *expfname; X Xstatic struct symbol init_syms[]={ X { "NOW", NUMBER, 0, c_number, NULL }, X { "IFBLK", NUMBER, S_IFBLK, c_number, NULL }, X { "IFCHR", NUMBER, S_IFCHR, c_number, NULL }, X { "IFDIR", NUMBER, S_IFDIR, c_number, NULL }, X { "IFMT", NUMBER, S_IFMT, c_number, NULL }, X { "IFREG", NUMBER, S_IFREG, c_number, NULL }, X { "ISGID", NUMBER, S_ISGID, c_number, NULL }, X { "ISUID", NUMBER, S_ISUID, c_number, NULL }, X { "ISVTX", NUMBER, S_ISVTX, c_number, NULL }, X#ifdef S_IFLNK X { "IFLNK", NUMBER, S_IFLNK, c_number, NULL }, X#endif X#ifdef S_IFSOCK X { "IFSOCK", NUMBER, S_IFSOCK, c_number, NULL }, X#endif X#ifdef S_IFIFO X { "IFIFO", NUMBER, S_IFIFO, c_number, NULL }, X#endif X { "atime", FIELD, 0, c_atime, NULL }, X { "ctime", FIELD, 0, c_ctime, NULL }, X { "dev", FIELD, 0, c_dev, NULL }, X { "gid", FIELD, 0, c_gid, NULL }, X { "ino", FIELD, 0, c_ino, NULL }, X { "mode", FIELD, 0, c_mode, NULL }, X { "mtime", FIELD, 0, c_mtime, NULL }, X { "nlink", FIELD, 0, c_nlink, NULL }, X { "rdev", FIELD, 0, c_rdev, NULL }, X { "size", FIELD, 0, c_size, NULL }, X { "uid", FIELD, 0, c_uid, NULL }, X { "depth", FIELD, 0, c_depth, NULL }, X { "prune", FIELD, 0, c_prune, NULL }, X { "days", NUMBER, 24*3600, c_number, NULL }, X { "weeks", NUMBER, 24*3600*7, c_number, NULL }, X { "hours", NUMBER, 3600, c_number, NULL }, X { "strlen", FIELD, 0, c_baselen, NULL }, X { "return", RETURN, 0, c_return, NULL } X}; X Xrhinit() X{ X int i; X struct symbol *s,*locatename(); X X symbols = &init_syms[0]; X X for(i=0; i< sizeof(init_syms)/sizeof(struct symbol)-1; i++ ) X init_syms[i].next = &init_syms[i+1]; X X /* initialize the NOW variable to the time right now */ X s = locatename( "NOW" ); X s->value = time(0); X} X Xrhfinish() X{ X struct symbol *s; X X while(symbols->type == PARAM || symbols->type == FUNCTION) { X s = symbols; X symbols = symbols->next; X free(s->name); X free(s); X } X} END_OF_FILE if test 2739 -ne `wc -c <'rhdata.c'`; then echo shar: \"'rhdata.c'\" unpacked with wrong size! fi # end of 'rhdata.c' fi if test -f 'rhdir.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rhdir.c'\" else echo shar: Extracting \"'rhdir.c'\" \(6851 characters\) sed "s/^X//" >'rhdir.c' <<'END_OF_FILE' X X/* ---------------------------------------------------------------------- X * FILE: rhdir.c X * VERSION: 2 X * Written by: Ken Stauffer X * This file contains the "non portable" stuff dealing with X * directories. X * printentry(), ftrw(), fwt1() X * X * X * ---------------------------------------------------------------------- */ X X#include "rh.h" X#include <sys/types.h> X#include <sys/stat.h> X X#define user_index(b) ((000777 & (b)) >> 6) + ((b) & S_ISUID ? 8 : 0) X#define group_index(b) ((000077 & b) >> 3) + ((b) & S_ISGID ? 8 : 0) X#define all_index(b) ((000007 & (b)) + (((b) & S_ISVTX) ? 8 : 0)) X#define ftype_index(b) ((b) >> 13) X X#define isdirect(b) (((b)&S_IFMT)==S_IFDIR) X#define isblk(b) (((b)&S_IFMT)==S_IFBLK) X#define ischr(b) (((b)&S_IFMT)==S_IFCHR) X X/* X * Some System do not define these macro's. X * If these macro's are missing, then use these ones: X * X * #define major(b) ((b)>>8) X * #define minor(b) ((b)&0xff) X * X */ X X#define isdot(s) ((s)[1]=='\0' && (s)[0]=='.') X#define isdotdot(s) ((s)[2]=='\0' && (s)[1]=='.' && (s)[0]=='.') X X#ifdef S_IFLNK X#define islink(b) (((b)&S_IFMT) == S_IFLNK) X#else X#define islink(b) (0) X#define lstat stat X#endif X X#define isproper(m) (isdirect(m) && !islink(m) && !attr.prune) X X#ifdef POSIX_DIRECTORY_LIBRARY X#include <dirent.h> X#else X#include <sys/dir.h> X#endif X X/* X * XXX - on BSD systems, this is defined in <sys/param.h>. X * On System V Release 3, it's defined inside "nami.c", and is, X * unfortunately, not in any include file. X * On systems other than those, no such simple limit exists. X * On BSD and S5R3 systems, as distributed by Berkeley and AT&T, X * respectively, it's 1024, so we set it to that. X */ X#define MAXPATHLEN 1024 X X/* ---------------------------------------------------------------------- X * printentry: X * Display filename,permissions and size in a '/bin/ls' like X * format. If verbose is non-zero then more information is X * displayed. X * uses the macros: X * user_index(b) X * group_index(b) X * all_index(b) X * ftype_index(b) X * X */ X Xprintentry(verbose,buf,name) Xstruct stat *buf; Xchar *name; X{ X char *t,*ctime(); X X static char *ftype[]={ "p", "c" , X "d" , "b" , X "-" , "l" , X "s" , "t" }; X X static char *perm[]={ "---", "--x", "-w-", "-wx" , X "r--", "r-x", "rw-", "rwx" , X "--S", "--s", "-wS", "-ws" , X "r-S", "r-s", "rwS", "rws" }; X X static char *perm2[]={ "---", "--x", "-w-", "-wx" , X "r--", "r-x", "rw-", "rwx" , X "--T", "--t", "-wT", "-wt" , X "r-T", "r-t", "rwT", "rwt" }; X X if( verbose ) { X t = ctime(&buf->st_mtime); X t[24] = '\0'; X if( ischr(buf->st_mode) || isblk(buf->st_mode) ) X X printf("%s%s%s%s %4d %4d %3d,%3d %s %-s\n", X ftype[ ftype_index(buf->st_mode) ], X perm[ user_index(buf->st_mode) ], X perm[ group_index(buf->st_mode) ], X perm2[ all_index(buf->st_mode) ], X buf->st_uid, X buf->st_gid, X major(buf->st_rdev), X minor(buf->st_rdev), X t+4, X name ); X X else X X printf("%s%s%s%s %4d %4d %6d %s %-s\n", X ftype[ ftype_index(buf->st_mode) ], X perm[ user_index(buf->st_mode) ], X perm[ group_index(buf->st_mode) ], X perm2[ all_index(buf->st_mode) ], X buf->st_uid, X buf->st_gid, X buf->st_size, X t+4, X name ); X X } else { X X if( ischr(buf->st_mode) || isblk(buf->st_mode) ) X X printf("%s%s%s%s %3d,%3d %-s\n", X ftype[ ftype_index(buf->st_mode) ], X perm[ user_index(buf->st_mode) ], X perm[ group_index(buf->st_mode) ], X perm2[ all_index(buf->st_mode) ], X major(buf->st_rdev), X minor(buf->st_rdev), X name ); X X else X X printf("%s%s%s%s %9d %-s\n", X ftype[ ftype_index(buf->st_mode) ], X perm[ user_index(buf->st_mode) ], X perm[ group_index(buf->st_mode) ], X perm2[ all_index(buf->st_mode) ], X buf->st_size, X name ); X X } X} X X/* ---------------------------------------------------------------------- X * ftrw: X * Entry point to do the search, ftrw is a front end X * to the recursive fwt1. X * ftrw() initializes some global variables and X * builds the initial filename string which is passed to X * ftw1(). X */ X Xftrw(f,fn,depth) Xchar *f; Xint (*fn)(); Xint depth; X{ X char *p,filebuf[ MAXPATHLEN+1 ]; X struct stat statbuf; X int last; X X attr.prune = 0; X attr.depth = 0; X attr.func=fn; X attr.fname = filebuf; X attr.buf = &statbuf; X strcpy(attr.fname,f); X X last = 0; X for(p=attr.fname; *p; p++) X if( *p == '/' ) last = 1; X else last = 0; X X if( !last ) { *p++ = '/'; *p = '\0'; } X X if( lstat(attr.fname,attr.buf) < 0 ) return(-1); X X (*(attr.func))(); X X if( isproper( attr.buf->st_mode ) ) fwt1(depth,p); X X return(0); X} X X/* ---------------------------------------------------------------------- X * fwt1: X * 'p' points to the end of the string in attr.fname X * X * 2 versions of this routine currently live here: X * "new-style", for systems with a BSD or POSIX-style X * directory library, and systems without such a X * directory library. They both differ in X * the manner in which they access directories. X * Any chnages needed to work on another system X * should only have to made for this routine. X * X * Below is the "directory library" version of fwt1() X * X */ X X#if defined(POSIX_DIRECTORY_LIBRARY) || defined(BSD) X Xstatic fwt1(depth,p) Xint depth; Xchar *p; X{ X char *q,*s; X DIR *dirp; X#ifdef POSIX_DIRECTORY_LIBRARY X struct dirent *dp; X#else X struct direct *dp; X#endif X if( !depth ) return; X attr.depth += 1; X X dirp=opendir(attr.fname); X if( dirp == NULL ) return; X for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { X if( isdot(dp->d_name) || isdotdot(dp->d_name) ) continue; X s = p; X q = dp->d_name; X while( *s++ = *q++ ); X s -= 1; X if( lstat(attr.fname,attr.buf) < 0 ) continue; X (*(attr.func))(); X if( isproper( attr.buf->st_mode ) ) { X *s++ = '/'; X *s = '\0'; X fwt1(depth-1,s); X } X } X closedir(dirp); X attr.depth -= 1; X attr.prune = 0; X *p = '\0'; X} X#else X X/* ---------------------------------------------------------------------- X * fwt1: X * This function does the same thing as fwt1() above, but is X * meant for systems without a directory library, that does X * directory reading "by hand". X * X * Below is the "no directory library" version of fwt1() X * X */ X Xstatic fwt1(depth,p) Xint depth; Xchar *p; X{ X char *q,*s; X FILE *dirp; X struct direct dp; X int count; X X if( !depth ) return; X attr.depth += 1; X X dirp=fopen(attr.fname,"r"); X if( dirp == NULL ) return; X for(count = fread(&dp,sizeof(struct direct),1,dirp); count; X count = fread(&dp,sizeof(struct direct),1,dirp) ) { X X if( isdot(dp.d_name) || isdotdot(dp.d_name) || dp.d_ino==0 ) X continue; X s = p; X q = dp.d_name; X while( *s++ = *q++ ); X s -= 1; X X if( lstat(attr.fname,attr.buf) < 0 ) continue; X (*(attr.func))(); X if( isproper( attr.buf->st_mode ) ) { X *s++ = '/'; X *s = '\0'; X fwt1(depth-1,s); X } X } X fclose(dirp); X attr.depth -= 1; X attr.prune = 0; X *p = '\0'; X} X X#endif END_OF_FILE if test 6851 -ne `wc -c <'rhdir.c'`; then echo shar: \"'rhdir.c'\" unpacked with wrong size! fi # end of 'rhdir.c' fi if test -f 'rhrc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rhrc'\" else echo shar: Extracting \"'rhrc'\" \(2972 characters\) sed "s/^X//" >'rhrc' <<'END_OF_FILE' X X/* ---------------------------------------------------------------------- X * FILE: $HOME/.rhrc X * VERSION: 2 X * This file is a sample .rhrc that should live in your home X * directory. X * The contents of this file are read BEFORE any other input is read X * from the user. Functions defined here will be useable elsewhere. X * X */ X X/* X * The dir() function tests a file to see if it is a directory. X * The command "rh -e dir", would be enough to find just directories X * X */ X Xdir() X{ X return( (mode & IFMT) == IFDIR ); X} X X/* X * months - This function can be used like a constant. X * It evaluates to the number of seconds in a month (average). X * Expressions like the following are now possible: X * (mtime > NOW-2*months) X * Would find files that have been modified in the last 2 X * months. X * X */ X Xmonths X{ X return days*30; X} X X/* X * This defines a useful "alias" for the 'nlink' variable, X * which may be easier to remember. X * X */ X Xnlinks { return nlink; } X X/* X * This function returns the number of seconds passed as its argument X * minus NOW. Used to make some time comparisons "cleaner". X * X */ X Xago(d) X{ X return( NOW - d ); X} X X/* X * returns true if a file is writable by others (and group). X * X */ X Xwritable() X{ X return mode & 022; X} X XMINE X{ X return( uid == $$ ); X} X X/* X * This is my "bad" function which can be easily invoked by the command: X * % rh -le bad X * This allows me to find files that are not a good idea to have around: X * - core files. X * - old a.out files. X * - files writeable by other people. X * - checkpoint and backup files. X * X */ X Xbad() X{ X return( "core" || ("a.out" && mtime <= ago(2*days) ) || X "*.BAK" || "*.CKP" || writable); X} X X/* X * Find C related files. X * X */ X Xcsrc() X{ X return("*.c" || "*.h" || "[Mm]akefile" ); X} X X/* X * Find files that have been modified in the last X * 1 hour. X * X */ X Xchanged() X{ X return( mtime > NOW-1*hours ); X X /* ALTERNATELY: X * (using the ago() function) X * X * return( mtime > ago(1*hours) ); X * X */ X} X X/* X * This function can be used as a constant. X * X */ X XK() X{ X return 1024; X} X X/* megs { return K*K; } or ...*/ Xmegs { return K<<10; } X X/* X * This function shows that recursion is quite possible. X * Call this function with any number, the higher the number X * deeper in recursion it goes. (for large values this function X * overflows the stack). This can be used to impose a delay, but that X * would be useless on my machine, cause it is sooooo slow already. X * Neat mathamatical functions can be calculated this way. X * X */ X Xrecursion(n) X{ X return (n>0) ? recursion(n-1) : 0; X} X X/* X * sqrt(n), returns the integer square root of the number n. X * Useful for expressions like: X * sqrt(uid) <= gid; X * Which finds files where the square root of the user-id is X * less than the gid. This one will be used rarely if ever. X * sqrt1() is a helper function. X * X */ X Xsqrt1(n,odd) { return( (n <= 0 ) ? 0 : sqrt1(n-odd,odd+2)+1 ); } Xsqrt(n) { return( sqrt1(n,1) ); } X Xfact(n) { return (n<=0) ? 1 : fact(n-1)*n; } END_OF_FILE if test 2972 -ne `wc -c <'rhrc'`; then echo shar: \"'rhrc'\" unpacked with wrong size! fi # end of 'rhrc' fi echo shar: End of archive 1 \(of 2\). cp /dev/null ark1isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.