v21i006: Find files using C expressions, Part01/02

Rich Salz rsalz at uunet.uu.net
Thu Feb 8 07:42:29 AEST 1990


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 at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list