[comp.sources.unix] v18i058: Find files using C-style expressions

rsalz@uunet.uu.net (Rich Salz) (03/25/89)

Submitted-by: Kenneth Stauffer <stauffer@cpsc.UCalgary.CA>
Posting-number: Volume 18, Issue 58
Archive-name: rh

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 Unix find(1) command,
partly because most Unix users already know C and party because the syntax
is more consistant. In addition to being easier to use than find(1), its
expressions can be used to select a more diverse subset of files.

#! /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 shell archive."
# Contents:  README Makefile rh.h rh.c rhcmds.c rhdir.c rhparse.c
#   rh.man sample
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(3050 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X							March 6, 1989
X
XINTRODUCTION:
XRh, was written by Ken Stauffer (c)1989, 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
XUnix find(1) command, partly because most Unix users
Xalready know C and party because the syntax is more
Xconsistant. In addition to being easier to use
Xthan find(1), its expressions can be used to select
Xa more diverse subset of files.
X
XPORTABILITY:
XIn the makefile the user must specify what flavour of unix
Xthey are making rh for. For the most part rh is portable
Xto all unix systems, except for the portion of code
Xthat does the directory reading. The code that does this
Xhas been isolated in the file 'rhdir.c'. There are two basic
Xmechanisms I have seen for directory reading:
X
X	XENIX / SYSV:
X	------------
X		This method simply opens the directory as a file
X	and does fread()'s to grab the directory entries. This is
X	the simplest and most obvious way. However on BSD systems
X	a library routine is placed between the programmer and
X	the system call interface for portablility reasons.
X
X	BSD:
X	---
X		Berkley adds the readdir() routine. Which has
X	its own data type, DIR which is used for the
X	reading of directories. However the structure to which these
X	entries are placed is still 'struct direct'. This again differs
X	from the SUN implementation.
X
X	SUN:
X	---
X		Sun OS, is similar to BSD except that an include
X	file is different, and also it uses a 'struct dirent'
X	instead of 'struct direct'.
X
X	All of these differences have been taken into account in
X	the file 'rhdir.c', and hopefully will work on your system.
X	So far 'rh' works on:
X		SCO XENIX, VAX (BSD 4.3) and SUN 4's (and SUN 3's).
X
X	If your system is different from the ones above, then
X	the modifications should be made to 'rhdir.c' ONLY.
X
XCOMPILING:
XTo make rh work on your system, the file Makefile must be
Xedited to match your system. There is a single '-D' option
Xthat must be changed in the Makefile. Define ONE of the
Xfollowing in the definition of CFLAGS:
X
X	-DBSD		- This would be used for
X			  most BSD systems. (VAX's)
X	-DSUN		- This would be used for
X			  SUN computers. (SUN 4 / SUN 3)
X	-DXENIX		- Xenix systems. (Tandy 6000)
X	-DSYSV		- System V systems.
X
XIn addition to the C source there is also a file called
Xrh.man. This is a nroff file and can be created by a command like:
X		nroff -man rh.man > rh.cat
XThe resultant file (rh.cat) is sutable for general viewing.
X
XFinally there is a file called sample. This file contains some
Xsample rh expressions. To use them the user can type:
X
X	% rh -l -f sample -mex1
X
XThis would do a search of the current working directory, and
Xuse the expression 'ex1' from the file 'sample'.
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 a:
X
X	% rh -la -e 1 /
X
XThis will find all files that makes the constant exression '1' true.
XSo unless your not root, all files on the system will be printed.
X
XKen Stauffer.
X
XI can be reached via email at:
X		root@sixk
END_OF_FILE
if test 3050 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(530 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# rh:
X# (c) 1989 Ken Stauffer
X#
X# Place one of the following -D's onto the end of
X# the definition of CFLAGS.
X#
X# -DXENIX
X#	- xenix like OS
X#
X# -DSYSV
X#	- System V/III like OS
X#
X# -DBSD
X#	- BSD like OS
X#
X# -DSUN
X#	- SUN OS
X#
X
XCFLAGS= -DXENIX -O -n
X
Xrh: rhcmds.o rh.o rhparse.o rhdir.o
X	cc $(CFLAGS) -o rh rh.o rhcmds.o rhparse.o rhdir.o
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
END_OF_FILE
if test 530 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
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'\" \(4290 characters\)
sed "s/^X//" >'rh.h' <<'END_OF_FILE'
X
X/* ----------------------------------------------------------------------
X * FILE: rh.h
X * (c) 1989 Ken Stauffer
X * This header contains the #define's for the tokens.
X * It also contains the initialized arrays. By having 
X * rh.c define a symbol MAIN, the space is allocated for these globals
X * only once.
X *
X * ---------------------------------------------------------------------- */
X
X#include <stdio.h>
X
X#define START		256
X#define OR		256	/* || */
X#define AND		257	/* && */
X#define LE		258	/* <= */
X#define LT		259	/* < */
X#define GE		260	/* >= */
X#define GT		261	/* > */
X#define NE		262	/* != */
X#define EQ		263	/* == */
X#define BOR		264	/* | */
X#define BAND		265	/* & */
X#define BXOR		266	/* ^ */
X#define NOT		267	/* ! */
X#define PLUS		268	/* + */
X#define MUL		269	/* * */
X#define MINUS		270	/* - */
X#define DIV		271	/* / */
X#define MOD		272	/* % */
X#define BNOT		273	/* ~ */
X#define UNIMINUS	274	/* - */
X#define SHIFTL		275	/* << */
X#define SHIFTR		276	/* >> */
X#define QM		277	/* ? */
X#define COLON		278	/* : */
X#define NOP		279	/* */
X 
X#define NUMBER		280	/* eg. 1234,NOW,IFDIR */
X#define STAR		281	/* eg. "*.BAK" */
X#define FIELD		282	/* mode, uid */
X#define MACRONAME	283
X#define UNKNOWN		284
X
X#define NOW_INDEX	1	/* where to place the current time */
X#define MACRO_INDEX	0	/* where the macro name goes */
X 
X#define LENGTH		100	/* size of stack program */
X#define MEM		20	/* size of stack */
X#define IDLENGTH	20
X#define STARLEN		1000	/* total chars available for strings */
X
X#if BSD || SUN
X#define DEPTH		getdtablesize()
X#endif
X
X#if XENIX || SYSV
X/* This value was arbitrarily chosen */
X#define DEPTH		24
X#endif
X
Xstruct instr {
X	int		i_type;
X	long		i_value;
X};
X
X#ifdef MAIN
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_star(),
X	c_bnot(),    c_uniminus(), c_lshift(),  c_rshift(),  c_qm(),
X	c_colon(),   c_nop();
X
X#if SUN || BSD
Xchar *identifiers[]={ "", "NOW" , "IFBLK", "IFCHR", "IFDIR", "IFLNK", "IFMT",
X		      "IFREG", "IFSOCK", "ISGID", "ISUID", "ISVTX",
X		      "atime",  "ctime", "dev",   "gid",  "ino",
X		      "mode",  "mtime",  "nlink", "rdev",  "size", "uid",
X		      NULL };
X
Xlong constants[]={ 0,0,
X	S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK,
X	S_ISGID, S_ISUID, S_ISVTX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
X#endif
X
X#if XENIX || SYSV
Xchar *identifiers[]={ "", "NOW", "IFBLK", "IFCHR", "IFDIR", "IFMT",
X		      "IFREG", "IFIFO", "ISGID", "ISUID", "ISVTX",
X		       "atime",  "ctime", "dev",   "gid",  "ino",
X		      "mode",  "mtime",  "nlink", "rdev",  "size", "uid",
X		      NULL };
X
Xlong constants[]={ 0,0,
X	S_IFBLK, S_IFCHR, S_IFDIR, S_IFMT, S_IFREG, S_IFIFO,
X	S_ISGID, S_ISUID, S_ISVTX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
X#endif
X
Xint (*commands[])()= {
X	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_bnot,    c_uniminus,c_lshift,
X	c_rshift,  c_qm,       c_colon,   c_nop,     c_number,
X	c_star,    c_atime,    c_ctime,   c_dev,     c_gid,
X	c_ino,     c_mode,     c_mtime,   c_nlink,   c_rdev,
X	c_size,    c_uid };
X
X	long		tokenval;
X	long		token;
X
X	struct instr	StackProgram[ LENGTH ];
X	int		PC;
X
X	long		Stack[ MEM ];
X	int		SP;
X
X	struct stat	*globuf;
X	char		*fname;
X	int		dashf;
X	int		dashl;
X	int		dashe;
X	int		dashr;
X	int		dashh;
X	int		dashm;
X	int		dasha;
X	char		*expstr;
X	FILE		*expfile;
X	
X	char		Starbuf[ STARLEN ];
X	int		starfree=0;
X
X#else
X	extern long		constants[];
X	extern char		*identifiers[];
X	extern int		(*commands[])();
X
X	extern long		tokenval;
X	extern long		token;
X	extern struct instr	StackProgram[];
X	extern int		PC;
X	extern long		Stack[];
X	extern int		SP;
X	extern struct stat	*globuf;
X	extern char		*fname;
X	extern char		Starbuf[];
X	extern int		starfree;
X	extern int		dashf;
X	extern int		dashl;
X	extern int		dashe;
X	extern int		dashr;
X	extern int		dashh;
X	extern int		dashm;
X	extern int		dasha;
X	extern char		*expstr;
X	extern FILE		*expfile;
X#endif
END_OF_FILE
if test 4290 -ne `wc -c <'rh.h'`; then
    echo shar: \"'rh.h'\" unpacked with wrong size!
fi
# end of 'rh.h'
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'\" \(5063 characters\)
sed "s/^X//" >'rh.c' <<'END_OF_FILE'
X
X/* ----------------------------------------------------------------------
X * FILE: rh.c
X * (c) 1989 Ken Stauffer
X * 
X * printhelp(), execute(), examine(), main()
X *
X *
X * ---------------------------------------------------------------------- */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#define MAIN
X#include "rh.h"
X
Xstatic char usage[]={
X"Usage: %s [-ahlr] [ [-e expr] | [-f filename [-mname]] ] file ...\n"
X};
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
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-a       adds to the long output to make it longer\n");
X	printf("\t-mname   use the named macro 'name' from 'file'\n");
X	printf("\t-e       get expression from the command line\n");
X	printf("\t-f       get expression from file\n\n");
X	printf("VALID EXPRESSIONS CONSIST OF:\n");
X
X	printf("\tCONSTANTS/variables: see stat(2)\n");
X	for(i=0; identifiers[i]; i++ )
X		printf("%s%12s%s", (!(i%5==0)) ? "\t" : "" , 
X			identifiers[i],
X			(i%5==4 || !identifiers[i+1]) ? "\n" : " ");
X
X	printf("\tC OPERATORS (precedence same as C):\n");
X	printf("\t! ~ - * / %% + < <= > >= == != & ^ | << >> && || ?:\n");
X	printf("\tSPECIAL OPERATORS:\n");
X	printf("\t$username , \"*.c\" , [yyyy/mm/dd]\n\n");
X
X}
X
X/* ----------------------------------------------------------------------
X * execute:
X *	Execute the program contained in the StackProgram[]
X *	array.
X *	Indexes the commands[] array to obtain the correct
X *	function/operator. Programs are NULL terminated.
X *	Returns the result of the expression.
X *
X */
X
Xexecute()
X{
X	register long etype,eval;
X	SP=0;
X	for(PC=0; (etype=StackProgram[PC].i_type) ; PC++) {
X		eval=StackProgram[PC].i_value;
X
X    (*commands[ (etype!=FIELD) ? etype-START : etype-START+eval ] ) (eval);
X
X	}
X	return( Stack[0] );
X}
X
X/* ----------------------------------------------------------------------
X * examine:
X *	This function is called for every file that 'rh' examines.
X *	examine() 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
Xexamine(s,buf)
Xchar *s;
Xstruct stat *buf;
X{
X	fname=s;
X	globuf=buf;
X
X	if( execute() )
X		(dashl) ? printentry(globuf,fname) : printf("%s\n",fname);
X}
X
X/* ----------------------------------------------------------------------
X * main:
X *	parse arguments.
X *	two for loops are used to parse the argument list,
X *	the first for loop process the arguments. The second
X *	for loop process the characters within the current argument.
X *	-l, -r, -h options can occur as often as desired.
X *	-f and -e and -m can only occur once and MUST have an argument.
X *
X *	parse expression.
X *	the expression is parsed and if all is ok, then the
X *	recursive hunt is started.
X *
X */
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X	int i,r,skip;
X	char *j;
X
X	/* defaults */
X	dashe = 0;		/* -e option */
X	dashl = 0;		/* -l */
X	dashf = 0;		/* -f */
X	dashr = 1;		/* -r */
X	dashh = 0;		/* -h */
X	dashm = 0;		/* -m */
X	dasha = 0;		/* -a */
X	
X   for(i=1; i<argc; i++ ) {
X	if( *argv[i] != '-' ) break;
X	skip = 0;
X	for(j=argv[i]+1; *j; j++ ) {
X	   switch( *j ) {
X	   case 'l': dashl = 1; break;
X	   case 'r': dashr = 0; break;
X	   case 'h': dashh = 1; break;
X	   case 'a': dashl = dasha = 1; break;
X	   case 'm':
X		if( dashm ) {
X			fprintf(stderr,"%s: too many -m options\n", argv[0]);
X			exit(-1);
X		}
X		dashm = 1;
X		identifiers[ MACRO_INDEX ] = j+1;
X		while(*j) j++; j--;
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 = i+1;
X		skip = 1;
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 = i+1;
X		skip = 1;
X		break;
X	   default:
X		fprintf(stderr,"%s: illegal option -%c, use -h for help\n",
X		argv[0],*j);
X		fprintf(stderr,usage, argv[0]);
X		exit(-1);
X	   }
X	   if( dashf && dashe ) {
X		fprintf(stderr, "%s: cannot have both -e and -f\n",argv[0]);
X		exit(-1);
X	   }
X	}
X	if( skip ) i++;
X   }
X
X	if( dashh ) printhelp(argv[0]);
X
X	if( dashm && !dashf ) {
X		fprintf(stderr,"%s: -m option needs the -f option\n",argv[0]);
X		exit(-1);
X	}
X	if( dashf >= argc ) {
X		fprintf(stderr,"%s: -f missing an argument\n",argv[0]);
X		exit(-1);
X	}
X	if( dashe >= argc ) {
X		fprintf(stderr,"%s: -e missing an argument\n",argv[0]);
X		exit(-1);
X	}
X	
X	if( dashe ) { expstr = argv[dashe]; }
X	else if( dashf ) {
X		expstr = argv[dashf];
X		if( (expfile = fopen(expstr,"r")) == NULL ) {
X			fprintf(stdout,"%s: cannot open %s\n",
X				argv[0],expstr);
X			exit(-1);
X		}
X	} else expfile = stdin;
X	
X	expression();
X
X	if( i >= argc ) {
X		r=ftw(".",examine,(dashr)? DEPTH :1);
X		if(r == -1) perror(".");
X	} else
X		for(; i<argc; i++) {
X			r=ftw( argv[i],examine,(dashr)? DEPTH :1);
X			if( r == -1 ) perror(argv[i]);
X		}
X}
END_OF_FILE
if test 5063 -ne `wc -c <'rh.c'`; then
    echo shar: \"'rh.c'\" unpacked with wrong size!
fi
# end of 'rh.c'
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'\" \(3879 characters\)
sed "s/^X//" >'rhcmds.c' <<'END_OF_FILE'
X
X/* ----------------------------------------------------------------------
X * FILE: rhcmds.c
X * (c) 1989 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 that 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_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--;  }
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--;  }
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; }
Xc_nop(i)   long i; { }
X
X/* operand functions */
X
Xc_number(i) long i; { Stack[SP++] = i;                }
Xc_atime(i)  long i; { Stack[SP++] = globuf->st_atime; }
Xc_ctime(i)  long i; { Stack[SP++] = globuf->st_ctime; }
Xc_dev(i)    long i; { Stack[SP++] = globuf->st_dev;   }
Xc_gid(i)    long i; { Stack[SP++] = globuf->st_gid;   }
Xc_ino(i)    long i; { Stack[SP++] = globuf->st_ino;   }
Xc_mode(i)   long i; { Stack[SP++] = globuf->st_mode;  }
Xc_mtime(i)  long i; { Stack[SP++] = globuf->st_mtime; }
Xc_nlink(i)  long i; { Stack[SP++] = globuf->st_nlink; }
Xc_rdev(i)   long i; { Stack[SP++] = globuf->st_rdev;  }
Xc_size(i)   long i; { Stack[SP++] = globuf->st_size;  }
Xc_uid(i)    long i; { Stack[SP++] = globuf->st_uid;   }
X
X
X/* ----------------------------------------------------------------------
X * star:
X *	This implements the trivial regular expression stuff.
X *	Since people may want to upgrade this, I will explain the
X *	parameter. 'i' is an index into the array Startbuf[]. The
X *	string contained there is the actual '\0' terminated
X *	string that occured in the expression (eg "*.BAK" ), minus
X *	the "'s.
X *	The reasons for the simplistic regular expressions is
X *	because it was easy, because lots of unix systems do
X *	regexp() in lots of ways and this method is fairly fast.
X *
X */
X
Xc_star(i)
Xlong i;
X{
X
X	register int ri,ii;
X	
X	if( Starbuf[i]=='*') {
X		ii=strlen(fname)-1;
X		ri=strlen(Starbuf+i)-1+i;
X		while( fname[ii]==Starbuf[ri] && ri>i ) {
X			ri--; ii--;
X		}
X		Stack[SP++] = (ri==i);
X	}
X	else {
X		int x=0;
X		ii=0;
X		while( fname[x] ) {
X			if(fname[x]=='/') ii=x;
X			x++;
X		}
X		ii++;
X		ri=i;
X		while( fname[ii]==Starbuf[ri] && Starbuf[ri]!='*' 
X			&& fname[ii] && Starbuf[ri]) {
X			ri++; ii++;
X		}
X		Stack[SP++]=!(fname[ii]+Starbuf[ri]) || Starbuf[ri]=='*';
X	}
X}
X
END_OF_FILE
if test 3879 -ne `wc -c <'rhcmds.c'`; then
    echo shar: \"'rhcmds.c'\" unpacked with wrong size!
fi
# end of 'rhcmds.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'\" \(5549 characters\)
sed "s/^X//" >'rhdir.c' <<'END_OF_FILE'
X
X/* ----------------------------------------------------------------------
X * FILE: rhdir.c
X * (c) 1989 Ken Stauffer
X * This file contains the "non portable" stuff dealing with
X * directories.
X * printentry(), ftw(), ftw1()
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
X#if SUN
X
X#include <dirent.h>
X#define MAXPATHLEN	255
X#define islink(b)	((b & S_IFLNK) == S_IFLNK)
X#define isproper(m)	( isdirect(m) && !(((m)&S_IFLNK)==S_IFLNK) )
X
X#endif
X
X#if BSD
X
X#include <sys/dir.h>
X#define MAXPATHLEN	255
X#define islink(b)	((b & S_IFLNK) == S_IFLNK)
X#define isproper(m)	( isdirect(m) && !(((m)&S_IFLNK)==S_IFLNK) )
X
X#endif
X
X#if XENIX || SYSV
X
X#include <sys/dir.h>
X#define MAXPATHLEN	255
X#define islink(b)	(0)
X#define isproper(m)	isdirect(m)
X#define lstat		stat
X
X#endif
X
X
Xstatic int (*func)();
Xstatic char filename[ MAXPATHLEN ];
X
X/* ----------------------------------------------------------------------
X * printentry:
X *	Display filename,permissions and size in a '/bin/ls' like
X *	format. Examines the dasha variable to see if more
X *	info should be printed.
X * uses the macros:
X *	user_index(b)
X *	group_index(b)
X *	all_index(b)
X *	ftype_index(b)
X *
X */
X
Xprintentry(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( dasha ) {
X		t = ctime(&buf->st_mtime);
X		t[24] = '\0';
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	} else {
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 * ftw:
X *	Entry point to do the search, ftw is a front end
X *	to the recursive fwt1.
X *	ftw() places some of the arguments to global variables
X *	so that they arguments would not have to be passed around to
X *	ftw1().
X *
X */
X
Xftw(f,fn,depth)
Xchar *f;
Xint (*fn)();
Xint depth;
X{
X	char *p;
X	struct stat statbuf;
X	int i;
X
X	strcpy(filename,f);
X	if( filename[ strlen(filename)-1 ] != '/' ) {
X		filename[ strlen(f) ] = '/';
X		filename[ strlen(f)+1 ] = '\0';
X	}
X	func=fn;
X	
X	if( lstat(f,&statbuf) < 0 ) return(-1);
X
X	(*func)(f,&statbuf);
X
X	if( isproper( statbuf.st_mode ) ) fwt1(depth);
X
X	return(0);
X}
X
X/* ----------------------------------------------------------------------
X * fwt1:
X *	2 versions of this routine currently live here:
X *	XENIX/SYSV, and SUN/BSD. 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 SUN/BSD version of fwt1()
X *	--------------------------------------
X *
X */
X
X#if SUN || BSD
X
Xstatic fwt1(depth)
Xint depth;
X{
X
X#if SUN
X	DIR *dirp;
X	struct dirent *dp;
X#else
X	DIR *dirp;
X	struct direct *dp;
X#endif
X	char *basep;
X	struct stat statbuf;
X
X	if( !depth ) return;
X	basep=filename+strlen(filename);
X
X	dirp=opendir(filename);
X	if( dirp == NULL ) return;
X	for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
X		if( dp->d_name[0]=='.' && dp->d_name[1]=='\0' ) continue;
X		if( dp->d_name[0]=='.' && dp->d_name[1]=='.' &&
X			dp->d_name[2]=='\0' ) continue;
X		strcpy(basep,dp->d_name);
X		if( lstat(filename,&statbuf) < 0 ) continue;
X		(*func)(filename,&statbuf);
X		if( isproper( statbuf.st_mode ) ) {
X			basep[ strlen(dp->d_name) ] = '/';
X			basep[ strlen(dp->d_name)+1 ] = '\0';
X			fwt1(depth-1);
X		}
X	}
X	closedir(dirp);
X	*basep = '\0';
X}
X#endif
X
X/* ----------------------------------------------------------------------
X * ftw1:
X *	This function does the samething as ftw1() above, but is
X *	meant for XENIX/SYSV type systems that do directory reading
X *	"by hand"
X *
X *	Below is the XENIX/SYSV version of fwt1()
X *	--------------------------------------
X */
X
X#if XENIX || SYSV
X
Xstatic fwt1(depth)
Xint depth;
X{
X	char *basep;
X	FILE *dirp;
X	struct direct dp;
X	struct stat statbuf;
X	int count;
X
X	if( !depth ) return;
X
X	basep=filename+strlen(filename);
X	dirp=fopen(filename,"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( dp.d_name[0]=='.' && dp.d_name[1]=='\0' ) continue;
X		if( dp.d_name[0]=='.' && dp.d_name[1]=='.' &&
X			dp.d_name[2]=='\0' ) continue;
X		strcpy(basep,dp.d_name);
X		if( lstat(filename,&statbuf) < 0 ) continue;
X		(*func)(filename,&statbuf);
X		if( isproper( statbuf.st_mode ) ) {
X			basep[ strlen(dp.d_name) ] = '/';
X			basep[ strlen(dp.d_name)+1 ] = '\0';
X			fwt1(depth-1);
X		}
X	}
X	fclose(dirp);
X	*basep = '\0';
X}
X
X#endif
END_OF_FILE
if test 5549 -ne `wc -c <'rhdir.c'`; then
    echo shar: \"'rhdir.c'\" unpacked with wrong size!
fi
# end of 'rhdir.c'
fi
if test -f 'rhparse.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rhparse.c'\"
else
echo shar: Extracting \"'rhparse.c'\" \(10196 characters\)
sed "s/^X//" >'rhparse.c' <<'END_OF_FILE'
X
X/* ----------------------------------------------------------------------
X * FILE: rhparse.c
X * (c) 1989 Ken Stauffer
X * This contains the parser for the C expressions,
X * gettoken(), getit() and ungetit() routines.
X * sectime(), datespec(), expression(), expr(), exp0(), ... , factor()
X * locatename(), push(), find_macro()
X *
X *
X * ---------------------------------------------------------------------- */
X
X#include "rh.h"
X#include <ctype.h>
X#include <pwd.h>
X
Xstatic int cpos;		/* current character position */
X
X/* ----------------------------------------------------------------------
X * getit:
X *	Return the next character, input of from a file or
X *	a string.
X */
X
Xgetit()
X{
X	cpos++;
X	if( dashe ) return( (*expstr) ? *expstr++ : EOF );
X	else return( getc(expfile) );
X}
X
X/* ----------------------------------------------------------------------
X * ungetit:
X *	Unget a char.
X *
X */
X
Xungetit(c)
Xint c;
X{
X	cpos--;
X	if( dashe ) expstr = (*expstr) ? expstr-1 : expstr;
X	else ungetc(c,expfile);
X}
X
X/* ----------------------------------------------------------------------
X * error:
X *	Print an error message an quit.
X */
X 
Xerror(s)
Xchar *s;
X{
X	if( dashf )
X		fprintf(stderr,"File: %s, ",expstr);
X	else if( dashe )
X		fprintf(stderr,"argv[%d], ",dashe);
X	else
X		fprintf(stderr,"File: stdin, ");
X
X	fprintf(stderr,"char: %d, %s\n",cpos,s);
X	exit(-1);
X}
X
X/* ----------------------------------------------------------------------
X * push:
X *	"assemble" the instruction into the StackProgram[] array.
X *
X */
X
Xpush(t,val)
Xlong t,val;
X{
X	if( PC >= LENGTH ) error("no more space for expression!"); 
X	StackProgram[PC].i_type=t;
X	StackProgram[PC++].i_value=val;
X	return(PC-1);
X}
X
X/* ----------------------------------------------------------------------
X * find_macro:
X *	Routine consumes the input file until it reaches the
X *	macro name followed by a colon. If EOF
X *	is encountered first then the program quits.
X *
X */
X
Xfind_macro()
X{
X	while( token != MACRONAME && token != EOF ) token = gettoken();
X	if( token == EOF ) error("macro name not found");
X	token = gettoken();
X	if( token != COLON ) error("missing ':' after macro name");
X	token = gettoken();
X}
X
X/* ----------------------------------------------------------------------
X * expression()
X *	Parse an expression.
X *	This routine calls exp() and exp() calls exp0() and so on...
X *	If the -m option was specified then the routine find_macro()
X *	is called.
X *	The NOW constant is initialized here.
X *
X */
X	
Xexpression()
X{
X	PC = 0; cpos = 0;
X	constants[ NOW_INDEX ] = time(0);
X	token = gettoken();
X	if( dashm ) find_macro();
X	expr();
X	if( token!= ';' && token!= EOF ) error("missing ';' or EOF");
X	push(0,0);			/* NULL terminated the program */
X}
X
X/* OPERATOR ?: */
Xexpr()
X{
X	int qm,colon,nop;
X
X	expr0();
X	if( token == QM ) {
X		token = gettoken();
X		qm = push(QM,0);
X		expr();
X		if( token != COLON ) error("missing ':'");
X		token = gettoken();
X		colon = push(COLON,0);
X		expr();
X		nop = push(NOP,0);		/* nop */
X
X		StackProgram[qm].i_value = colon;
X		StackProgram[colon].i_value = nop;
X	}
X}		
X
X/* OPERATOR || */ 
Xexpr0()
X{
X	expr1();
X	for(;;)
X		if( token == OR ) {
X			token = gettoken();
X			expr1();
X			push(OR,0);
X	       } else break;
X}
X
X/* OPERATOR && */ 
Xexpr1()
X{
X	expr2();
X	for(;;)
X		if( token == AND ) {
X			token = gettoken();
X			expr2();
X			push(AND,0);
X		} else break;
X}
X
X/* OPERATOR | */
Xexpr2()
X{
X	expr3();
X	for(;;)
X		if( token == BOR ) {
X			token = gettoken();
X			expr3();
X			push(BOR,0);
X		} else break;
X}
X
X/* OPERATOR ^ */
Xexpr3()
X{
X	expr4();
X	for(;;)
X		if( token == BXOR ) {
X			token = gettoken();
X			expr4();
X			push(BXOR,0);
X		} else break;
X}
X
X/* OPERATOR & */
Xexpr4()
X{
X	expr5();
X	for(;;)
X		if( token == BAND ) {
X			token = gettoken();
X			expr5();
X			push(BAND,0);
X		} else break;
X}
X
X/* OPERATOR == != */
Xexpr5()
X{
X	int t;
X	expr6();
X	for(;t=token;)
X		if( t==EQ || t==NE ) {
X			token = gettoken();
X			expr6();
X			push(t,0);
X		} else break;
X}
X
X/* OPERATOR < <= > >= */
Xexpr6()
X{
X	int t;
X	expr7();
X	for(;t=token;)
X		if( t==LE || t==GE || t==GT || t==LT ) {
X			token = gettoken();
X			expr7();
X			push(t,0);
X		} else break;
X}
X
X/* OPERATOR << >> */
Xexpr7()
X{
X	int t;
X	expr8();
X	for(;t=token;)
X		if( t==SHIFTL || t==SHIFTR ) {
X			token = gettoken();
X			expr8();
X			push(t,0);
X		} else break;
X}
X
X/* OPERATOR + - */
Xexpr8()
X{
X	int t;
X	expr9();
X	for(;t=token;)
X		if( t==PLUS || t==MINUS ) {
X			token = gettoken();
X			expr9();
X			push(t,0);
X		} else break;
X}
X
X/* OPERATOR * / % */
Xexpr9()
X{
X	int t;
X	expr10();
X	for(;t=token;)
X		if( t==MUL || t==DIV || t==MOD ) {
X			token = gettoken();
X			expr10();
X			push(t,0);
X		} else break;
X}
X
X/* OPERATOR ~ ! - */ 
Xexpr10()
X{
X	int t;
X	t = token;	
X	if( t==NOT || t==BNOT || t==MINUS ){
X		token = gettoken();
X		expr10();
X		push((t==MINUS)? UNIMINUS : t ,0);
X	} else factor();
X}
X
X/* ----------------------------------------------------------------------
X * factor:
X *	Parse a factor. Could be a number, variable or
X *	string.
X */
X 
Xfactor()
X{
X	long l,datespec();
X
X	switch(token) {
X		case '(':
X			token = gettoken();
X			expr();
X			if( token != ')' )
X				error("missing ')'");
X			token = gettoken();
X			break;
X		case NUMBER:
X			push(NUMBER,tokenval);
X			token = gettoken();
X			break;
X		case FIELD:
X			push(FIELD,tokenval);
X			token = gettoken();
X			break;
X		case '[':
X			token = gettoken();
X			l=datespec();
X			if( token != ']' )
X				error("missing ']'");
X			token = gettoken();
X			push(NUMBER,l);
X			break;
X		case STAR:
X			push(STAR,tokenval);
X			token = gettoken();
X			break;
X		default:
X			error("syntax error");
X	}
X}
X
X/* ----------------------------------------------------------------------
X * sectime:
X *	calculate the number of seconds between January 1, 1970
X *	and year/month/day. Return that value.
X *
X */
X
X#define leap(d)	(((d % 4 == 0) && (d % 100 != 0)) || (d % 400 == 0))
X#define DAYSEC	(3600*24)
X#define YERSEC	(3600*24*365)
X#define TIME0	1970
X
Xlong sectime(year,month,day)
Xint year,month,day;
X{
X
X        static int months[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
X	int yeardays,leapers,x;
X	long seconds;
X
X	if(month>12 || month<1 || year<TIME0 || day<1 || day>months[month]+
X		(month==2 && leap(year)) )
X			return(-1);
X
X	yeardays = leapers = 0;
X
X	for(x=1;x<month;x++)
X		yeardays += months[x];
X	if ((month > 2) && leap(year)) yeardays++;
X
X	for(x=TIME0; x<year; x++)
X		if(leap(x)) leapers++;
X	
X	seconds = yeardays*DAYSEC+(year-TIME0)*YERSEC+7*3600+
X			leapers*DAYSEC + day*DAYSEC;
X
X	return(seconds);
X
X}
X
X/* ----------------------------------------------------------------------
X * datespec:
X *	parse a date. Return the number of seconds from
X *	some date in 1970, until the specified date.
X */
X
Xlong datespec()
X{
X	int year,month,day,seconds;
X
X	if( token != NUMBER ) error("number expected");
X	year = tokenval;
X	token = gettoken();
X	if( token != DIV ) error("missing '/'");
X	token = gettoken();
X	if( token != NUMBER ) error("number expected");
X	month = tokenval;
X	token = gettoken();
X	if( token != DIV ) error("missing '/'");
X	token = gettoken();
X	if( token != NUMBER ) error("number expected");
X	day = tokenval;
X	token = gettoken();
X
X	if( (seconds = sectime(year,month,day)) < 0 ) 
X		error("invalid date");
X
X	return(seconds);
X}
X
X
X/* ----------------------------------------------------------------------
X * locatename:
X *	Does a linear seach for 's' in the array 'identifiers[]'.
X *
X */
X
Xlocatename(s)
Xchar *s;
X{
X	register int i;
X
X	for(i=0; identifiers[i]; i++ )
X		if( !strcmp(s,identifiers[i]) ) return(i);
X	return(-1);
X}
X
X/* ----------------------------------------------------------------------
X * gettoken:
X *	Return the next token.
X *	global variable: tokenval will contain any extra
X *	attribute associated with the returned token, ie
X *	the VALUE of a number, the index of the string etc...
X *
X */
X 
Xgettoken()
X{
X	char buf[IDLENGTH+1],*bufp=buf;
X	int c;
X
X	for(;;) { 
X		while( (c=getit()) == ' ' || c=='\n' || c=='\t' );
X		if( c == '#' ) while( (c=getit())!= '\n' && c!= EOF );
X		else break;
X	}
X
X	if(c=='0') {
X		tokenval=0;
X		while( ( c=getit() ) >= '0' && c <= '7' ) {
X			tokenval <<= 3;
X			tokenval += c-'0';
X		}
X		if( isdigit(c) ) error("bad octal constant");
X		ungetit(c);
X		return(NUMBER);
X	}
X 
X	if(isdigit(c)) {
X		tokenval=c-'0';
X		while(isdigit( (c=getit()) )) {
X			tokenval *=10;
X			tokenval += c-'0';
X		}
X		ungetit(c);
X		return(NUMBER);
X	}
X	
X	if(isalpha(c)) {
X	   int count=0,index;
X	   do {
X		if(count++ < IDLENGTH) *bufp++ = c;
X		c=getit();
X	   } while( isalnum(c) );
X	   ungetit(c);
X	   *bufp='\0';
X	   if( (index=locatename(buf)) < 0 ) return(UNKNOWN);
X	   tokenval = constants[index];
X	   if( index==MACRO_INDEX ) return(MACRONAME);
X	   return( ( isupper( *(identifiers[index]) ) ? NUMBER : FIELD ) );
X	}
X
X	if( c == '"' ) {
X		int index,st=0;
X		index=starfree;
X		while( (c=getit())!= '"' ) {
X			if( starfree > STARLEN )
X				error("no more string space");
X			if(c=='*') st++;
X			if(st>1) error("too many *'s present");
X			Starbuf[starfree++]=c;
X		}
X		Starbuf[starfree++]='\0';
X		tokenval=index;
X		return(STAR);
X	}
X
X	if( c == '=' ) {
X		c=getit();
X		if(c== '=') return(EQ);
X		else {
X			ungetit(c);
X			return('=');
X		}
X	}
X
X	if( c== '$' ) {
X	   int count=0;
X	   struct passwd *info,*getpwnam();
X	   c=getit();
X	   do {
X		if (count++ < IDLENGTH) *bufp++ = c;
X		c=getit();
X	   } while( isalnum(c) );
X	   ungetit(c);
X	   *bufp='\0';
X	   info=getpwnam(buf);
X	   if(info == NULL)
X		error("unknown user name after $");
X	   tokenval = info->pw_uid;
X	   return( NUMBER );
X	}
X	
X	if( c == '!' ) {
X		c=getit();
X		if( c == '=' ) return(NE);
X		ungetit(c);
X		return(NOT);
X	}
X 
X	if( c == '>' ) {
X		c=getit();
X		if( c == '=' ) return(GE);
X		if( c == '>' ) return(SHIFTR);
X		ungetit(c);
X		return(GT);
X	}
X
X	if( c == '<' ) {
X		c=getit();
X		if( c == '=' ) return(LE);
X		if( c == '<' ) return(SHIFTL);
X		ungetit(c);
X		return(LT);
X	}
X
X	if( c == '&' ) {
X		c=getit();
X		if( c == '&' ) return(AND);
X		ungetit(c);
X		return(BAND);
X	}
X
X	if( c == '|' ) {
X		c=getit();
X		if( c == '|' ) return(OR);
X		ungetit(c);
X		return(BOR);
X	}
X	if( c == '^' ) return(BXOR);
X	if( c == '+' ) return(PLUS);
X	if( c == '-' ) return(MINUS);
X	if( c == '*' ) return(MUL);
X	if( c == '/' ) return(DIV);
X	if( c == '%' ) return(MOD);
X	if( c == '~' ) return(BNOT);
X	if( c == '?' ) return(QM);
X	if( c == ':' ) return(COLON);
X	
X	return(c);
X}
X
END_OF_FILE
if test 10196 -ne `wc -c <'rhparse.c'`; then
    echo shar: \"'rhparse.c'\" unpacked with wrong size!
fi
# end of 'rhparse.c'
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'\" \(6020 characters\)
sed "s/^X//" >'rh.man' <<'END_OF_FILE'
X.TH RH 1
X.SH NAME
Xrh - recursive file locater (rawhide)
X.SH SYNOPSIS
X.BI "rh [-rlha] [-f" " filename" " [-m" "name" "] | -e" " expression"
X.BI "] [" "file" " ...]"
X.SH DECRIPTION
X.I Rh
Xrecursively searches the file system starting at
X.I file ...
Xfor files that make a C expression true. If no
X.I files
Xare listed then the current working directory is used.
X.PP
XExpressions for
X.I rh
Xcan come from the command line, a file, or from
Xstandard input (if no
X.I -e
Xor
X.I -f
Xoptions are given).
XThe basic form of a
X.I rh
Xexpression consists of a valid C expression containing
Xinteger constants and variables.
X.PP
X.I Constants
X, are either numeric or symbolic. Symbolic constants refer
Xto the #define's in the file
X.I /usr/include/sys/stat.h.
XOnly the useful constants are implemented. All symbolic
Xconstants are UPPER CASE and the "S_" prefix from the
Xsymbol name is omitted.
X.PP
X.I Variables
X, are lower case symbols that specify a field in the
Xstat structure (eg. st_size,st_mode ). For each file examined by
X.I rh
Xthese internal varibles are updated to match the current
Xfile. For convienience the "st_" prefix is dropped from variable
Xnames.
X.SH OPTIONS
X.B Rh
Xoptions can appear in any order, multiple options can
Xbe together within the same argument. However
Xno options may occur
X.I after
Xthe filename list is given.
X.IP -r
XThis option
X.B disables
X.I rh
Xfrom recursively searching for files.
X.PP
X.IP -l
XNormally
X.I rh
Xprints each matching filename on a line by itself. However,
X.I -l
Xwill cause filenames to be displayed with permission modes and
Xthe file size. Filename printing is similar to that of the
X.I ls
X(1) command. 
X.PP
X.IP -h
XThis tells
X.I rh
Xto display a useful help message. The message
Xexplains the command line usage, a list of
Xavailable constants/variables and a list
Xof valid operators.
X.I Rh
Xthen continues as though the
X.I -h
Xoption was not present.
X.PP
X.IP -f
XExpects that the next argument will be the
X.I filename
Xof a file containing a
X.I rh
Xexpression. It is an error to have both the
X.I -f
Xand
X.I -e
Xoptions together. Expressions should appear
Xin
X.I filename
Xin the same manner as expressions that are
Xentered interactively. The
X.I -m
Xoption expands this to allow labels to appear in the file.
XSee
X.I -m
Xfor more details.
X.PP
X.IP -m
XIs followed immediatly by a
X.I macroname
Xwhich specifies the starting location of a
X.I rh
Xexpression within the file
X.I filename.
XA ':' must appear after
X.I macroname.
XFor example:
X.PP
X.RS 8
Xwritable:
X.RS 5
X(mode & 022) && (uid == $joe );
X.PP
X.RE
X.RE
X.IP
XA
X.I -mwritable
Xoption would cause the expression following
X.I writeable:
Xto be used as the
X.I rh
Xexpression.
X.PP
X.IP -e
XThis option takes the next argument as the
X.I rh
X.I expression
Xthat will be used for the file search. Since many
Xof the operators are also
X.I sh
X(1) meta characters and since expressions may have
Xspaces in them, it is strongly recommended that the
X.I expression
Xbe enclosed in ''.
X.PP
X.IP -a
XThis option
X.I adds
Xmore information to the long output of the
X.I -l
Xoption. Information such as uid, gid, and last modified date
Xare output. The
X.I -a
Xoption implies the
X.I -l
Xoption.
X.PP
X.SH USAGE
X.SS "The following are the valid constants:"
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.PP
X.I
XIFBLK IFDIR IFLNK IFMT IFREG IFSOCK ISGID ISUID ISVTX -
Xsee stat(2).
X.PP
X.SS "The following are the valid variables:"
X.PP
X.I
Xatime ctime dev gid ino mode mtime nlink rdev size uid -
Xsee stat(2).
X.PP
X.SS "Valid C operators are:"
X.PP
X! ~ - * / % + < <= > >= == != & ^ | << >> && || ?:
X.PP
XOperator precedence, associativity and semantics are idenitical
Xto C.
X.PP
X.SS "Special operators:"
X.PP
X.IP $username
XThis operater evaluates to the integer uid of
X.I username.
X.PP
X.IP """*.c"""
XThis operator evaluates to true if the current filename matches
X"expression". The only form of 
X.I expression
Xsupported is, "filename", "*filename", "filename*", "*".
XWhen doing comparisons, only the base name is examined, not
Xpathnames.
X.PP
X.IP [yyyy/mm/dd]
XThe date enclosed in the [], will evaluate to a numeric
Xquantity suitable for comparing with atime,mtime or ctime.
XThe year cannot be abbreviated to say, 89 for 1989.
X.PP
X.IP NOTE:
XThat the
X.I special operators
Xtake on higher precedence than the C operators.
X.PP
X.SS "Lexical conventions:"
X.PP
XNumbers may be entered in octal by preceeding it with
Xa leading zero. Otherwise the number is taken to be
Xdecimal.
X.PP
X.PP
XAll characters after a '#' symbol are ignored upto a
Xnewline or end of file. This allows for comments
Xin rh expression files.
X.PP
XAn expression may be terminated by either
Xa ';' or EOF.
X.PP
X.SH EXAMPLES
XThe following are examples of
X.I rh
Xexpression. All expressions may appear in either a file,
Xinteractively, or on the command line:
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
Xis writable by other people.
X.PP
X.RS 8
X!uid && (mode & ISUID ) && (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 is writable.
X.PP
X.RS 8
X(size > 10*1024) && (mode & 0111) && (atime <= NOW-24*3600);
X.PP
X.RE
XThis expression finds all executable files greater than 10K, that
Xhave not been executed in the last 24 hours.
X.PP
X.RS 8
Xsize < ( ("*.c") ? 4096 : 32*1024 );
X.PP
X.RE
XThis cryptic expression finds C source files less than 4K, or
Xwill find other files if they are smaller than 32K. No other files
Xwill match.
X.PP
X.RS 8
X!(size % 1024);
X.PP
X.RE
XMatches files that are a multiple of 1K.
X.PP
X.RS 8
Xmtime >= [1982/3/1] && mtime <= [1982/3/31];
X.PP
X.RE
XThis exression finds files that were modified during the
Xmonth of march, in the year 1982.
X.PP
X.SH SEE ALSO
Xsh(1), find(1), stat(2), ls(1), chmod(1)
X.PP
XThe C programming language.
X.SH AUTHOR
XKen Stauffer (University of Calgary)
X.PP
Xstauffer@sixk
X.PP
X.SH BUGS
XThe date operator should also allow for time to be entered.
XFull regular exressions should be allowed within the " " operator.
X.PP
END_OF_FILE
if test 6020 -ne `wc -c <'rh.man'`; then
    echo shar: \"'rh.man'\" unpacked with wrong size!
fi
# end of 'rh.man'
fi
if test -f 'sample' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sample'\"
else
echo shar: Extracting \"'sample'\" \(553 characters\)
sed "s/^X//" >'sample' <<'END_OF_FILE'
X# This file contains 'rh' expressions.
X# '#' character causes the rest of the line to
X# be ignored...
X#
X
X# ex1, ex2, ex3, ex4, ex5 come from the manual...
X#
Xex1:
X	(mode & 022) && (uid == $uucp );
X
Xex2:
X	!uid && (mode & ISUID ) && (mode & 02);
X
Xex3:
X	(size > 10*1024) && (mode & 0111) && (atime <= NOW-24*3600);
X
Xex4:
X	size < (("*.c") ? 4096 : 32*1024);
X
Xex5:
X	!(size % 1024);
X
X# find files that have been created in the last hour.
X#
Xnew:
X	ctime > NOW-3600;
X
X# find files who have not been modified in the last 30 days.
X#
Xaged:
X	mtime < NOW-30*24*3600;
X
END_OF_FILE
if test 553 -ne `wc -c <'sample'`; then
    echo shar: \"'sample'\" unpacked with wrong size!
fi
# end of 'sample'
fi
echo shar: End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.