diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..e69de29 diff --git a/META b/META new file mode 100644 index 0000000..2c6cc6c --- /dev/null +++ b/META @@ -0,0 +1,29 @@ +Comp.sources.unix + +From + cluster!munnari.oz.au!uhccux!ames!think!zaphod.mps.ohio-state.edu!tut.cis.ohio-state.edu!snorkelwacker!mit-eddie!bbn!bbn.com!rsalz + Sun Feb 18 13:27:32 EST 1990 + +Submitted-by: Kenneth 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. + + + +From cluster!munnari.oz.au!uhccux!ames!think!mintaka!mit-eddie!bbn!bbn.com!rsalz Sun Feb 18 13:27:33 EST 1990 + +Submitted-by: Kenneth Stauffer +Posting-number: Volume 21, Issue 7 +Archive-name: rh2/part02 + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6b8279c --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# +# rh: +# >>>> VERSION: 2 <<<< +# Written by: Ken Stauffer +# +# Place one of the following -D's onto the end of +# the definition of CFLAGS. +# +# -DSYSV +# - System V/III like OS (including Xenix) +# +# -DBSD +# - BSD like OS (including SunOS) +# +# Also, place one of the following -D's onto the end of +# the definition of CFLAGS if you have an operating system +# in the specified subclass of the classes listed above. +# +# -DSUNOS_4 +# - SunOS 4.x (subclass of BSD) +# +# -DSYSVR3 +# - System V Release 3.x (subclass of SYSV) +# +# getopt.c uses void, so some systems may also need +# -Dvoid=int +# + +CFLAGS= -DBSD -DSUNOS_4 -O + +OBJS= rhcmds.o rh.o rhparse.o rhdir.o rhdata.o getopt.o glob_match.o + +rh: $(OBJS) + cc $(CFLAGS) -o rh $(OBJS) + +rhdir.o: rhdir.c rh.h + cc $(CFLAGS) -c rhdir.c + +rh.o: rh.c rh.h + cc $(CFLAGS) -c rh.c + +rhcmds.o: rhcmds.c rh.h + cc $(CFLAGS) -c rhcmds.c + +rhparse.o: rhparse.c rh.h + cc $(CFLAGS) -c rhparse.c + +rhdata.o: rhdata.c rh.h + cc $(CFLAGS) -c rhdata.c + +getopt.o: getopt.c + cc $(CFLAGS) -Dvoid=int -c getopt.c + +glob_match.o: glob_match.c + cc $(CFLAGS) -Dvoid=int -c glob_match.c + +clean: + rm -f rh core $(OBJS) diff --git a/README b/README new file mode 100644 index 0000000..040bc95 --- /dev/null +++ b/README @@ -0,0 +1,144 @@ + + >>>> V E R S I O N 2 <<<< + +INTRODUCTION: +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. + +CREDITS: + Guy Harris + - Corrected many portability problems. + David MacKenzie + - Manual revisions. Added getopt and regular expressions. + Norm Hutchinson + - Fixed ungetit(). + +COMPILING: +To make rh work on your system, you will need to change +some -D options in the Makefile. Define ONE of the +following in the definition of CFLAGS: + + -DBSD - This would be used for most BSD systems. + -DSYSV - System V systems. + +Also define one of the following: + + -DSUNOS_4 - SunOS 4.x (subclass of BSD) + -DSYSVR3 - System V Release 3.x (subclass of SYSV) + +In addition to the C source there is also a file called rh.man. +This is a nroff file and can be created by a command like: + + nroff -man rh.man > rh.cat + +The resultant file (rh.cat) is sutable for general viewing. + +RUNNING: +There is a file called rhrc. This file contains some +examples of things that can go into a $HOME/.rhrc file. +If the file "rhrc" is moved to your home directory and renamed +to ".rhrc" then a command like: + + % rh -l -e writable + +Will do a search of the current directory, executing the function +"writable()", which finds files that other people have write access to. + +Once rh is made, you can do what you want with it. A good test to +see if it is working is to do: + + % rh -vle 1 / + +This will find all files that makes the constant expression '1' true. +So if your root, all the files on the system will be found. + +PORTABILITY: +The file rhdir.c contains code that does directory reading. +This is most likely where problems will occur. These differences +have been taken into account for most versions of unix +and will hopefully work on your system. +So far 'rh' works on: + SCO XENIX, BSD 4.3, and SUNOS 4.0 + +GRAMMER: +The following is the grammer that describes the input language +recognized by rh: + + ==> EOF + | ; + + ==> + | + | /* empty */ + + ==> { RETURN ; } + + ==> IDENTIFIER + | IDENTIFIER ( ) + | IDENTIFIER ( ) + + ==> IDENTIFIER + ==> , + | /* empty */ + + ==> ? : + + ==> || + + ==> && + + ==> | + + ==> ^ + + ==> & + + ==> == + | != + + ==> < + | > + | <= + | >= + + ==> >> + | << + + ==> + + | - + + ==> * + | / + | % + + ==> ~ + | ! + | - + | + + ==> ( ) + | NUMBER + | + | IDENTIFIER + | [ ] + | STRING + + ==> IDENTIFIER + | IDENTIFIER ( ) + | IDENTIFIER ( ) + + ==> + ==> , + | /* empty */ + + ==> NUMBER / NUMBER / NUMBER + +-------------------------------------------------------------------- +Ken Stauffer. +root@sixk diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..25197b3 --- /dev/null +++ b/getopt.c @@ -0,0 +1,452 @@ + +#undef _POSIX_OPTION_ORDER + +/* Getopt for GNU. + Modified by David MacKenzie to use malloc and free instead of alloca, + and memcpy instead of bcopy under System V. + Copyright (C) 1987 Free Software Foundation, Inc. + + NO WARRANTY + + BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY +NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT +WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, +RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" +WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. +STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY +WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE +LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR +OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR +DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR +A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS +PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. + + GENERAL PUBLIC LICENSE TO COPY + + 1. You may copy and distribute verbatim copies of this source file +as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy a valid copyright notice "Copyright + (C) 1987 Free Software Foundation, Inc."; and include following the +copyright notice a verbatim copy of the above disclaimer of warranty +and of this License. You may charge a distribution fee for the +physical act of transferring a copy. + + 2. You may modify your copy or copies of this source file or +any portion of it, and copy and distribute such modifications under +the terms of Paragraph 1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating + that you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, + that in whole or in part contains or is a derivative of this + program or any part thereof, to be licensed at no charge to all + third parties on terms identical to those contained in this + License Agreement (except that you may choose to grant more + extensive warranty protection to third parties, at your option). + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty + protection in exchange for a fee. + + 3. You may copy and distribute this program or any portion of it in +compiled, executable or object code form under the terms of Paragraphs +1 and 2 above provided that you do the following: + + a) cause each such copy to be accompanied by the + corresponding machine-readable source code, which must + be distributed under the terms of Paragraphs 1 and 2 above; or, + + b) cause each such copy to be accompanied by a + written offer, with no time limit, to give any third party + free (except for a nominal shipping charge) a machine readable + copy of the corresponding source code, to be distributed + under the terms of Paragraphs 1 and 2 above; or, + + c) in the case of a recipient of this program in compiled, executable + or object code form (without the corresponding source code) you + shall cause copies you distribute to be accompanied by a copy + of the written offer of source code which you received along + with the copy you received. + + 4. You may not copy, sublicense, distribute or transfer this program +except as expressly provided under this License Agreement. Any attempt +otherwise to copy, sublicense, distribute or transfer this program is void and +your rights to use the program under this License agreement shall be +automatically terminated. However, parties who have received computer +software programs from you with this License Agreement will not have +their licenses terminated so long as such parties remain in full compliance. + + 5. If you wish to incorporate parts of this program into other free +programs whose distribution conditions are different, write to the Free +Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet +worked out a simple rule that can be stated here, but we will often permit +this. We will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software. + + +In other words, you are welcome to use, share and improve this program. +You are forbidden to forbid anyone else to use, share and improve +what you give them. Help stamp out software-hoarding! */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of `argv' so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable _POSIX_OPTION_ORDER disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include + +#if SYSV || SYSVR3 +#define bcopy(s, d, l) memcpy((d), (s), (l)) +#define index strchr +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + UNSPECIFIED means the caller did not specify anything; + the default is then REQUIRE_ORDER if the environment variable + _OPTIONS_FIRST is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options. + Stop option processing when the first non-option is seen. + This is what Unix does. + + PERMUTE is the default. We permute the contents of `argv' as we scan, + so that eventually all the options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code zero. + Using `-' as the first character of the list of option characters + requests this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; + +char *malloc (); + +/* Allocate memory dynamically, with error checking. */ + +static char * +xmalloc (n) + unsigned n; +{ + char *p; + + p = malloc (n); + if (!p) + { + fprintf (stderr, "Virtual memory exhausted\n"); + exit (1); + } + return p; +} + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size + = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) xmalloc (nonopts_size); + + /* Interchange the two blocks of data in argv. */ + + bcopy (&argv[first_nonopt], temp, nonopts_size); + bcopy (&argv[last_nonopt], &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + bcopy (temp, &argv[first_nonopt + optind - last_nonopt], + nonopts_size); + + free (temp); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + A colon in OPTSTRING means that the previous character is an option + that wants an argument. The argument is taken from the rest of the + current ARGV-element, or from the following ARGV-element, + and returned in `optarg'. + + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg'. + + If OPTSTRING starts with `-', it requests a different method of handling the + non-option ARGV-elements. See the comments about RETURN_IN_ORDER, above. + */ + +int +getopt (argc, argv, optstring) + int argc; + char **argv; + char *optstring; +{ + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = 0; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + ordering = RETURN_IN_ORDER; + else if (getenv ("_POSIX_OPTION_ORDER") != 0) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == 0 || *nextchar == 0) + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' + || argv[optind][1] == 0)) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (argv[optind][0] != '-' || argv[optind][1] == 0) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 0; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = argv[optind] + 1; + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = (char *) index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == 0) + optind++; + + if (temp == 0 || c == ':') + { + if (opterr != 0) + { + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", + argv[0], c); + } + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != 0) + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = 0; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != 0) + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr != 0) + fprintf (stderr, "%s: no argument for `-%c' option\n", + argv[0], c); + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = 0; + } + } + return c; + } +} + +memcpy(s,d,len) +char *s,*d; +int len; +{ + while(len--) *d++ = *s++; +} diff --git a/glob_match.c b/glob_match.c new file mode 100644 index 0000000..57234a7 --- /dev/null +++ b/glob_match.c @@ -0,0 +1,216 @@ +/* File-name wildcard pattern matching for GNU. + Copyright (C) 1985, 1988 Free Software Foundation, Inc. + + NO WARRANTY + + BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY +NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT +WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, +RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" +WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. +STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY +WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE +LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR +OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR +DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR +A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS +PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. + + GENERAL PUBLIC LICENSE TO COPY + + 1. You may copy and distribute verbatim copies of this source file +as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy a valid copyright notice "Copyright +(C) 1988 Free Software Foundation, Inc."; and include following the +copyright notice a verbatim copy of the above disclaimer of warranty +and of this License. + + 2. You may modify your copy or copies of this source file or +any portion of it, and copy and distribute such modifications under +the terms of Paragraph 1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating + that you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, + that in whole or in part contains or is a derivative of this + program or any part thereof, to be licensed at no charge to all + third parties on terms identical to those contained in this + License Agreement (except that you may choose to grant more extensive + warranty protection to some or all third parties, at your option). + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty + protection in exchange for a fee. + +Mere aggregation of another unrelated program with this program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other program under the scope of these terms. + + 3. You may copy and distribute this program (or a portion or derivative +of it, under Paragraph 2) in object code or executable form under the terms +of Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal + shipping charge) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +For an executable file, complete source code means all the source code for +all modules it contains; but, as a special exception, it need not include +source code for modules which are standard libraries that accompany the +operating system on which the executable file runs. + + 4. You may not copy, sublicense, distribute or transfer this program +except as expressly provided under this License Agreement. Any attempt +otherwise to copy, sublicense, distribute or transfer this program is void and +your rights to use the program under this License agreement shall be +automatically terminated. However, parties who have received computer +software programs from you with this License Agreement will not have +their licenses terminated so long as such parties remain in full compliance. + + +In other words, you are welcome to use, share and improve this program. +You are forbidden to forbid anyone else to use, share and improve +what you give them. Help stamp out software-hoarding! */ + +/* Match the pattern PATTERN against the string TEXT; + return 1 if it matches, 0 otherwise. + + A match means the entire string TEXT is used up in matching. + + In the pattern string, `*' matches any sequence of characters, + `?' matches any character, [SET] matches any character in the specified set, + [^SET] matches any character not in the specified set. + + A set is composed of characters or ranges; a range looks like + character hyphen character (as in 0-9 or A-Z). + [0-9a-zA-Z_] is the set of characters allowed in C identifiers. + Any other character in the pattern must be matched exactly. + + To suppress the special syntactic significance of any of `[]*?^-\', + and match the character exactly, precede it with a `\'. + + If DOT_SPECIAL is nonzero, + `*' and `?' do not match `.' at the beginning of TEXT. */ + +int +glob_match (pattern, text, dot_special) + char *pattern, *text; + int dot_special; +{ + register char *p = pattern, *t = text; + register char c; + + while ((c = *p++)) + { + switch (c) + { + case '?': + if (*t == 0 || (dot_special && t == text && *t == '.')) return 0; + else ++t; + break; + + case '\\': + if (*p++ != *t++) return 0; + break; + + case '*': + if (dot_special && t == text && *t == '.') + return 0; + return star_glob_match (p, t); + + case '[': + { + register char c1 = *t++; + register int invert = (*p == '^'); + + if (invert) p++; + + c = *p++; + while (1) + { + register char cstart = c, cend = c; + + if (c == '\\') + { + cstart = *p++; cend = cstart; + } + c = *p++; + if (c == '-') + { cend = *p++; if (cend == '\\') cend = *p++; c = *p++; } + if (c1 >= cstart && c1 <= cend) goto match; + if (c == ']') + break; + } + if (!invert) return 0; + break; + + match: + /* Skip the rest of the [...] construct that already matched. */ + while (c != ']') + { + c = *p++; + if (c == '\\') p++; + } + if (invert) return 0; + break; + } + + default: + if (c != *t++) return 0; + } + } + + if (*t) return 0; + return 1; +} + +/* Like glob_match, but match PATTERN against any final segment of TEXT. */ + +static int +star_glob_match (pattern, text) + char *pattern, *text; +{ + register char *p = pattern, *t = text; + register char c, c1; + + while ((c = *p++) == '?' || c == '*') + { + if (c == '?' && *t++ == 0) + return 0; + } + + if (c == 0) + return 1; + + if (c == '\\') c1 = *p; + else c1 = c; + + for (;;) + { + if ((c == '[' || *t == c1) + && glob_match (p - 1, t, 0)) + return 1; + if (*t++ == 0) return 0; + } +} diff --git a/rh.c b/rh.c new file mode 100644 index 0000000..06e4bc3 --- /dev/null +++ b/rh.c @@ -0,0 +1,267 @@ + +/* ---------------------------------------------------------------------- + * FILE: rh.c + * VERSION: 2 + * Written by: Ken Stauffer + * + * printhelp(), execute(), exam1(), exam2(), exam3(), main() + * + * + * ---------------------------------------------------------------------- */ + +#include +#include +#include "rh.h" + +static char usage[]={ +"Usage: %s [-vhlr] [ [-e expr] | [-f filename ] ] [-x command ] file ...\n" +}; + +/* ---------------------------------------------------------------------- + * printhelp: + * Print out the help screen. The string 's' is argv[0]. + * Called when the -h option is used. + * + */ + +printhelp(s) +char *s; +{ + int i; + struct symbol *p; + + printf(usage,s); + printf("options:\n"); + printf("\t-h show this message\n"); + printf("\t-l long filename output\n"); + printf("\t-r makes %s non-recursive\n",s); + printf("\t-v verbose output\n"); + printf("\t-e get expression from the command line\n"); + printf("\t-f get expression from file\n"); + printf("\t-x execute a unix command for matching files\n\n"); + + printf("\tvalid symbols:\n"); + for(i=1, p=symbols; p; p=p->next, i++) + printf("%12s%s", p->name, + ((i-1)%5==4 || !p->next ) ? "\n" : " "); + + printf("\tC operators:\n"); + printf("\t! ~ - * / %% + < <= > >= == != & ^ | << >> && || ?:\n"); + printf("\tspecial operators:\n"); + printf("\t$username , \"*.c\" , [yyyy/mm/dd]\n\n"); +} + +/* ---------------------------------------------------------------------- + * execute: + * Execute the program contained in the StackProgram[] + * array. Each element of the StackProgram[] array contains + * a pointer to a function. + * Programs are NULL terminated. + * Returns the result of the expression. + * + */ + +execute() +{ + register long eval; + register int (*efunc)(); + + SP=0; + for(PC=startPC; (efunc=StackProgram[PC].func) ; PC++) { + eval=StackProgram[PC].value; + (*efunc)(eval); + if( SP >= MEM ) { + fprintf(stderr,"stack overflow\n"); + exit(1); + } + } + return( Stack[0] ); +} + +/* ---------------------------------------------------------------------- + * exam1: exam2: exam3: + * One of these functions is called for every file that 'rh' examines. + * exam{1,2,3}() first calls execute to see if the + * expression is true, it then prints the file if the expression + * evaluated to true (non-zero). + * + */ + +/* print file out by itself */ +exam1() +{ + if( execute() ) printf("%s\n",attr.fname); +} + +/* long output of file */ +exam2() +{ + if( execute() ) printentry(attr.verbose,attr.buf,attr.fname); +} + +/* do a system(3) call to desired command */ +exam3() +{ + char command[ 2048 + 1 ]; + char *p,*q,*r,*strrchr(); + int rv; + + if( execute() ) { + p=command; + q=attr.command; + while( *q ) { + if( *q != '%' ) *p++ = *q++; + else { + q += 1; + if( *q == 's' ) { + r = attr.fname; + while( *p++ = *r++ ); + p -= 1; + } else if( *q == 'S' ) { + r = strrchr(attr.fname,'/'); + r = (r) ? r+1 : attr.fname; + while( *p++ = *r++ ); + p -= 1; + } else *p++ = '%'; + q += 1; + } + } + *p = '\0'; + rv = system(command); + if( attr.verbose ) printf("%s exit(%d)\n",command,rv); + } +} + + +/* ---------------------------------------------------------------------- + * main: + * parse arguments. + * gnu getopt() is used here. + * -l, -r, -h, -v options can occur as often as desired. + * -f,-x and -e can only occur once and MUST have an argument. + * + * Read and "compile" the $HOME/.rhrc file, if it exists. + * Read and "compile" any -f filename, if present. + * Read and "compile" any -e expression, if present. + * If after all that no start expression is found then read from + * stdin for one. + * Perform the recursive hunt on remaining arguments. + * + */ + +main(argc,argv) +int argc; +char *argv[]; +{ + extern int optind; + extern char *optarg; + char *dashe,*dashf,*strcat(),*getenv(),initfile[ 1024+1 ]; + int i,r; + int dashr,dashh,dashl; + int (*examptr)(); + + /* defaults */ + dashe = NULL; /* -e option */ + dashl = 0; /* -l */ + dashf = NULL; /* -f */ + dashr = 1; /* -r */ + dashh = 0; /* -h */ + attr.verbose = 0; /* -v */ + attr.command = NULL; /* -x */ + examptr = exam1; /* default output function */ + + while ((i = getopt(argc, argv, "lrhvx:e:f:")) != EOF) { + switch( i ) { + case 'l': examptr = exam2; dashl = 1; break; + case 'r': dashr = 0; break; + case 'h': dashh = 1; break; + case 'v': attr.verbose = 1; break; + case 'x': + if( attr.command ) { + fprintf(stderr, "%s: too many -x options\n",argv[0]); + exit(1); + } + examptr = exam3; + attr.command = optarg; + break; + case 'e': + if( dashe ) { + fprintf(stderr, "%s: too many -e options\n",argv[0]); + exit(1); + } + dashe = optarg; + break; + case 'f': + if( dashf ) { + fprintf(stderr, "%s: too many -f options\n",argv[0]); + exit(1); + } + dashf = optarg; + break; + default: + fprintf(stderr,"%s: use -h for help\n", argv[0],i); + fprintf(stderr,usage, argv[0]); + exit(1); + } + if( attr.command && dashl ) { + fprintf(stderr, + "%s: cannot have both -x and -l options\n", + argv[0]); + exit(1); + } + } + + PC = 0; + startPC = -1; + rhinit(); + if( dashh ) printhelp(argv[0]); + + expfname = getenv( HOMEENV ); + if( expfname ) { + strcpy(initfile,expfname); + expfname = strcat(initfile,RHRC); + if( (expfile = fopen(expfname,"r")) != NULL ) { + expstr = NULL; + program(); + } + } + + if( dashf ) { + expstr = NULL; + expfname = dashf; + if( (expfile = fopen(expfname,"r")) == NULL ) { + fprintf(stderr,"%s: ", argv[0]); + perror(expfname); + exit(1); + } + program(); + } + if( dashe ) { + expfile = NULL; + expstr = dashe; + program(); + } + if( startPC == -1 ) { + expstr = NULL; + expfname = "stdin"; + expfile = stdin; + program(); + } + + if( startPC == -1 ) { + fprintf(stderr,"%s: no start expression specified\n", + argv[0] ); + exit(1); + } + rhfinish(); + + if( optind >= argc ) { + r=ftrw(".",examptr,(dashr)? DEPTH :1); + if(r == -1) perror("."); + } else + for(; optind + +#define RHRC "/.rhrc" /* start up file */ +#define RHENV "RH" /* rh environment variable */ +#define HOMEENV "HOME" /* path to your home directory */ + +/* Definition of returned tokens from the lexical analyzer */ + +#define OR 256 /* || */ +#define AND 257 /* && */ +#define LE 258 /* <= */ +#define GE 259 /* >= */ +#define NE 260 /* != */ +#define EQ 261 /* == */ +#define SHIFTL 262 /* << */ +#define SHIFTR 263 /* >> */ +#define NOP 264 /* no-operation */ +#define NUMBER 265 /* literal numbers and symbolic constants */ +#define STR 266 /* regular expression strings */ +#define FIELD 267 /* file fields (size,mode,nlinks) */ +#define FUNCTION 268 /* user defined function */ +#define RETURN 269 /* return keyword */ +#define PARAM 270 /* parameter to functions */ +#define IDENTIFIER 271 + +#define LENGTH 2000 /* size of stack program */ +#define MEM 1000 /* size of stack */ +#define IDLENGTH 20 /* length of an identifier */ +#define STRLEN 200 /* total chars available for strings */ + +#if BSD +#define DEPTH getdtablesize() +#define strrchr rindex +#ifdef SUNOS_4 +#define POSIX_DIRECTORY_LIBRARY +#endif /* SUNOS_4 */ +#endif /* BSD */ + +#ifdef SYSV +#ifdef SYSVR3 +#define DEPTH ulimit(4, 0L) +#define POSIX_DIRECTORY_LIBRARY +#else /* SYSVR3 */ +/* This value was arbitrarily chosen */ +#define DEPTH 20 +#endif /* SYSVR3 */ +#endif /* SYSV */ + +/* + * Structure of a "rh-assembly" instruction. + * + */ + +struct instr { + int (*func)(); + long value; +}; + +/* + * Structure of a symbol. + * + */ + +struct symbol { + char *name; + int type; + long value; + int (*func)(); + struct symbol *next; +}; + +/* + * Structure defining the rh runtime environment. + * + */ + +struct runtime { + struct stat *buf; /* stat info of current file */ + char *fname; /* file name of current file */ + int depth; /* relative depth of current file */ + int (*func)(); /* examination function */ + int prune; /* flag to indicate prunning */ + int verbose; /* used by the (*func)() routine */ + char *command; /* command to exec for current file */ +}; + +#ifndef DATA + extern struct symbol *symbols; + + extern struct symbol *tokensym; + extern long tokenval; + extern long token; + + extern struct instr StackProgram[]; + extern int PC; + extern int startPC; + extern long Stack[]; + extern int SP; + extern int FP; /* frame pointer */ + + extern struct runtime attr; + + extern char Strbuf[]; + extern int strfree; + + extern char *expstr; + extern char *expfname; + extern FILE *expfile; + +#endif + +extern c_or(), c_and(), c_le(), c_lt(), c_ge(), + c_gt(), c_ne(), c_eq(), c_bor(), c_band(), + c_bxor(), c_not(), c_plus(), c_mul(), c_minus(), + c_div(), c_mod(), c_number(), c_atime(), c_ctime(), + c_dev(), c_gid(), c_ino(), c_mode(), c_mtime(), + c_nlink(), c_rdev(), c_size(), c_uid(), c_str(), + c_bnot(), c_uniminus(), c_lshift(), c_rshift(), c_qm(), + c_colon(), c_return(), c_func(), c_param(), c_depth(), + c_baselen(), c_pathlen(), c_prune(); + diff --git a/rh.man b/rh.man new file mode 100644 index 0000000..8e725e4 --- /dev/null +++ b/rh.man @@ -0,0 +1,572 @@ +.TH RH 1L +.SH NAME +rh - recursive file locater (rawhide) >> VERSION 2 << +.SH SYNOPSIS +.B rh +[ +.B \-vhlr +] [ +.B \-f filename +] +.br +.RS +[ +.B \-e expression +] [ +.B \-x command +] +.BR file ... +.RE +.SH DESCRIPTION +.I Rh +recursively searches the file system starting at each given +.IR file +for files that make a C expression true. If no files +are listed, the current working directory is used. +.PP +Expressions for +.I rh +can come from the command line (with the +.I \-e +option), a file (with the +.I \-f +option), or from the standard input (the default). +The basic form of an +.I rh +expression is a C expression which may optionally define and or call +user defined functions. These C expressions may contain +constants, variables, and all the usual C operators. +.PP +.I Constants +are either numeric or symbolic. Symbolic constants are based +on the constants defined in the file +.IR /usr/include/sys/stat.h ; +only the useful constants are implemented. +The ``S_'' prefix from the symbol name is omitted. +(eg. S_IFMT would used as IFMT). +.PP +.I Variables +are symbols that specify a field in the +stat structure (e.g., st_size, st_mode) or some other attribute of the file. +For each file examined by +.IR rh , +these internal variables are updated to match the current +file. For convenience, the ``st_'' prefix is dropped from variable +names. +.PP +.I Functions +are a means of associating a C expression with a function name. +This allows complex expressions to be easily composed from simpler ones. +The value +of a function call is the value of the expression represented by the +function. For example: +.PP +.RS 8 +foo(x) +.br +{ +.br +return( x-1 ); +.br +} +.RE +.PP +If the above function were given to Rh, it would define a function that could be +used later. If +.I foo +were called with 667, then the value of the call to +.I foo +would be equal to 666. +.PP +.SH OPTIONS +.I Rh +options can appear in any order; multiple options can +be given within the same argument. +.TP +.I \-r +Prevents +.I rh +from recursively searching for files. +.TP +.I \-l +Normally +.I rh +prints each matching filename on a line by itself. The +.I \-l +option causes the matching files' permission modes and sizes +to be displayed as well, in a format similar to that of the +.IR ls (1) +command. +.TP +.I \-h +Causes +.I rh +to display a help message. The message +explains the command line usage, a list of +available constants and variables and a list +of valid operators. +.I Rh +then continues as though the +.I \-h +option were not present. +.TP +.I \-f filename +Uses +.I filename +as the name of a file containing a +.I rh +expression. Functions may also be defined in this file. +.TP +.I \-e expression +Uses +.I expression +as the expression +that will be used for the file search. +Since many of the operators are also shell +meta-characters and since rh expressions may contain +spaces, it is strongly recommended that the +.I expression +be enclosed in single quotes, ''. If both the -e and -f options +occur together then the -f option is processed FIRST, followed by the +-e option. This means that an expression specified with the -e option +may use functions defined from the +.I -f file. +.TP +.I \-v +Verbose. Causes the -l option to output more information and +the -x option to print out the command executed and the return value. +.TP +.I \-x command +Execute +.I command +using system(3) for each matching file. The string +.I command +may contain a %s which will be substituted with the full path name. A +%S (uppercase 'S') will be substituted with the base name. For example, +given the file /etc/passwd the values for %s and %S would be: +/etc/passwd and passwd, respectively. +.SH USAGE +.SS "Rh grammer" +This is the grammer that rh will accept. +.PP +.TP + ::= +.RS 6 + EOF +.br + | ; +.RE +.PP +.TP + ::= +.RS 6 + +.br + | +.br + | /* empty */ +.RE +.PP +.TP + ::= +.RS 6 + { RETURN ; } +.RE +.PP +.TP + ::= +.RS 6 + IDENTIFIER +.br + | IDENTIFIER ( ) +.br + | IDENTIFIER ( ) +.RE +.PP +.TP + ::= +.RS 6 + , IDENTIFIER +.br + | IDENTIFIER +.RE +.PP +.TP + ::= +.RS 6 + ? : +.br + | || +.br + | && +.br + | | +.br + | ^ +.br + | & +.br + | == +.br + | != +.br + | < +.br + | > +.br + | <= +.br + | >= +.br + | >> +.br + | << +.br + | + +.br + | - +.br + | * +.br + | / +.br + | % +.br + | ~ +.br + | ! +.br + | - +.br + | +.RE +.PP +.TP + ::= +.RS 6 + ( ) +.br + | NUMBER +.br + | +.br + | IDENTIFIER +.br + | [ ] +.br + | STRING +.RE +.PP +.TP + ::= +.RS 6 + IDENTIFIER +.br + | IDENTIFIER ( ) +.br + | IDENTIFIER ( ) +.RE +.PP +.TP + ::= +.RS 6 + , +.br + | +.RE +.PP +.TP + ::= +.RS 6 + NUMBER / NUMBER / NUMBER +.RE +.PP +.SS "Search order:" +.I Rh +initally looks for a +.I $HOME/.rhrc +and if it exists it will be read in. Next, any file specified by the +.I \-f +option is read followed by any expression specified with the +.I \-e +option. If after all that, an expression, defined outside of a function, +has not been encountered then stdin will be read for such an expression. +An error will result if no expression has been encountered. +.PP +A +.I $HOME/.rhrc +will usually contain function definitions that will be accessable +for the user when they enter in a search expression. +.PP +.SS "The valid constants are:" +.IP NOW +This constant is set to the current time at the start of +.I rh. +It is used to make comparisons with atime, ctime and mtime. +.IP days +This is equal to the number of seconds in a day. +.IP hours +Number of seconds in an hour. +.IP weeks +Number of seconds in a week. +.IP "IFBLK IFDIR IFLNK IFMT IFREG IFSOCK ISGID ISUID ISVTX" +see +.IR stat (2) +for an explanation. +.SS "The valid variables are:" +.PP +.IP depth +This variable is set to the relative depth in the directory search +that the current file is at. +.IP strlen +This is set to the length of the filename. For example strlen +would be equal to 4 given the file: "/tmp/core" because "core" is +4 characters long. +.IP prune +This varible always returns 0, but as a side-effect causes the +search path to be "cut-short" when evaluated. This can be used to prune the +directory search. +.I prune +is usually used with the ?: operator to conditionally evaluate the prune +variable. +.IP "atime,ctime,dev,gid,ino,mode,mtime,nlink,rdev,size,uid" +see +.IR stat (2) +for an explanation. +.SS "The valid C operators are:" +.PP +! ~ - * / % + < <= > >= == != & ^ | << >> && || ?: +.PP +Operator precedence, associativity and semantics are the same as +in C. +.SS "Special operators:" +.IP $username +This operator evaluates to the integer user id of +.I username. +As a special case the symbol $$ evaluates to the +uid of the user currently running +.I rh. +.IP """*.c""" +This operator evaluates to true if the current filename matches +the quoted expression, which is a shell globbing pattern. +The recognized metacharacters are: +.RS +.IP ``*'' +to match any number of characters, including zero (except that, as in +the shell, it does not match a leading ``.''); +.IP ``?'' +to match any single character (except for a leading ``.''); +.IP ``[SET]'' +to match any character in the given set (ranges can be included); +.IP ``[^SET]'' +to match any character not in the given set; +.IP ``\e\e'' +to escape the special meaning of any of the above metacharacters. +.RE +.PP +When doing comparisons, only the base name is examined, not +leading paths. +.IP [yyyy/mm/dd] +The date enclosed in the brackets, ``[]'', will evaluate to a number of +seconds past January 1, 1970, which is +suitable for comparing with atime, mtime or ctime. +The year cannot be abbreviated to its last two digits. +.PP +The special operators +have higher precedence than the C operators. +.SS "Lexical conventions:" +.PP +Numbers may be entered in octal by preceding them with +a leading zero. Otherwise numbers are taken to be in +decimal. +.PP +Text enclosed in /* and */ will be ignored. This can be +used for commenting +.I rh +expression files. +.PP +The start expression may be terminated by either +a ``;'' or the end of the file or argument. +.SH EXAMPLES +The following are examples of +.I rh +expressions. +.PP +.RS 8 +(mode & 022) && (uid == $joe ); +.PP +.RE +Matches all files that have uid equal to username ``joe'' and +are writable by other people. +.PP +.RS 8 +!uid && (mode & ISUID ) && +.br +(mode & 02); +.PP +.RE +Matches all files that are owned by root (uid==0) and that +have set-uid on execution bit set, and are writable. +.PP +.RS 8 +(size > 10*1024) && (mode & 0111) && +.br +(atime <= NOW-24*3600); +.RE +.PP +Finds all executable files larger than 10K that +have not been executed in the last 24 hours. +.PP +.RS 8 +size < ( ("*.c") ? 4096 : 32*1024 ); +.RE +.PP +Finds C source files smaller than 4K and +other files smaller than 32K. No other files will match. +.PP +.RS 8 +!(size % 1024); +.RE +.PP +Matches files that are a multiple of 1K. +.PP +.RS 8 +mtime >= [1982/3/1] && mtime <= [1982/3/31]; +.RE +.PP +Finds files that were modified during March, 1982. +.PP +.RS 8 +strlen >= 4 && strlen <= 10; +.RE +.PP +This expression will print files whose filenames are between +4 and 10 characters in length. +.PP +.RS 8 +depth > 3; +.RE +.PP +Matches files that are at a RELATIVE depth of 3 or more. +.PP +.RS 8 +( "tmp" || "bin" ) ? prune : "*.c"; +.RE +.PP +This expression does a search for all "*.c" files, however it will +not look into any directories called "bin" or "tmp". This is because when +such a filename is encountered the prune variable is evaluated, causing +further searching with the current path to stop. The general form of this +would be: +.PP +.RS 8 +("baddir1" || "baddir2" || ... || "baddirn") ? +.br +.RS 8 +prune : ; +.RE +.RE +.PP +.SH "ADVANCED EXAMPLES" +The following examples show the use of function definitions and other +advanced features of +.I "Rh." + Consider: +.PP +.RS 8 +dir() +.br +{ +.br +return ( (mode & IFMT) == IFDIR ); +.br +} +.br +.RE +.PP +This declares a function that returns true if the current file is a directory +and false otherwise. The function +.PP +.I dir +now may be used in other expressions. +.PP +.RS 8 +dir() && !mine(); +.RE +.PP +This matches files that are directories and are not owned by +the user. This assumes the user has written a mine() function. Since +.I dir +and +.I mine +take no arguments they may be called like: +.PP +.RS 8 +dir && !mine; +.RE +.PP +Also when declaring a function that takes no arguments the parenthesis +may be omitted. For example: +.PP +.RS 8 +mine +.br +{ +.br +return uid == $joe; +.br +} +.br +.RE +.PP +This declares a function mine, that evaluates true when a file +is owned by user name 'joe'. An alternate way to write mine would be: +.PP +.RS 8 +mine(who) +.br +{ +.br +return uid == who; +.br +} +.br +.RE +.PP +This would allow mine to be called with an argument, for example: +.PP +.RS 8 +mine( $sue ) || mine( $joe ); +.RE +.PP +This expression is true of any file owned by user name 'sue' or 'joe'. +Since the parenthesis are optional for functions that take no +arguments, it would be possible to define functions that can be used +exactly like constants, or handy macros. Suppose the above definition +of +.I dir +was placed in a users +.I $HOME/.rhrc +Then the command: +.PP +.RS 8 +rh -e dir +.RE +.PP +would execute the expression 'dir' which will print out all directories. +Rh functions can be recursive. +.SH "FILES" +$HOME/.rhrc +.PP +.SH "SEE ALSO" +chmod(1), find(1), ls(1), stat(2) +.PP +The C programming language. +.SH AUTHOR +Ken Stauffer (University of Calgary) +.PP +stauffer@sixk +.SH BUGS +The date operator should also allow for time to be entered. +The date operator can be off by a day, if the +time on the file is close to midnight. diff --git a/rhcmds.c b/rhcmds.c new file mode 100644 index 0000000..3248df2 --- /dev/null +++ b/rhcmds.c @@ -0,0 +1,127 @@ + +/* ---------------------------------------------------------------------- + * FILE: rhcmds.c + * VERSION: 2 + * Written by: Ken Stauffer + * This file contains the functions that do the evaluation of + * the stack program. + * These functions are simple, and behave like RPN operators, that is + * they use the last two values on the stack, apply an operator + * and push the result. Similarly for unary ops. + * + * ---------------------------------------------------------------------- */ + +#include "rh.h" +#include +#include + +c_or(i) long i; { Stack[SP-2]=Stack[SP-2] || Stack[SP-1]; SP--; } +c_and(i) long i; { Stack[SP-2]=Stack[SP-2] && Stack[SP-1]; SP--; } +c_le(i) long i; { Stack[SP-2]=Stack[SP-2] <= Stack[SP-1]; SP--; } +c_lt(i) long i; { Stack[SP-2]=Stack[SP-2] < Stack[SP-1]; SP--; } +c_ge(i) long i; { Stack[SP-2]=Stack[SP-2] >= Stack[SP-1]; SP--; } +c_gt(i) long i; { Stack[SP-2]=Stack[SP-2] > Stack[SP-1]; SP--; } +c_ne(i) long i; { Stack[SP-2]=Stack[SP-2] != Stack[SP-1]; SP--; } +c_eq(i) long i; { Stack[SP-2]=Stack[SP-2] == Stack[SP-1]; SP--; } +c_bor(i) long i; { Stack[SP-2]=Stack[SP-2] | Stack[SP-1]; SP--; } +c_band(i) long i; { Stack[SP-2]=Stack[SP-2] & Stack[SP-1]; SP--; } +c_bxor(i) long i; { Stack[SP-2]=Stack[SP-2] ^ Stack[SP-1]; SP--; } +c_lshift(i) long i; { Stack[SP-2]=Stack[SP-2] << Stack[SP-1]; SP--; } +c_rshift(i) long i; { Stack[SP-2]=Stack[SP-2] >> Stack[SP-1]; SP--; } +c_plus(i) long i; { Stack[SP-2]=Stack[SP-2] + Stack[SP-1]; SP--; } +c_mul(i) long i; { Stack[SP-2]=Stack[SP-2] * Stack[SP-1]; SP--; } +c_minus(i) long i; { Stack[SP-2]=Stack[SP-2] - Stack[SP-1]; SP--; } +c_div(i) long i; { Stack[SP-2]=Stack[SP-2] / Stack[SP-1]; SP--; } +c_mod(i) long i; { Stack[SP-2]=Stack[SP-2] % Stack[SP-1]; SP--; } + + +/* unary instructions */ + +c_not(i) long i; { Stack[SP-1] = ! Stack[SP-1]; } +c_bnot(i) long i; { Stack[SP-1] = ~ Stack[SP-1]; } +c_uniminus(i) long i; { Stack[SP-1] = - Stack[SP-1]; } + +/* trinary operator ?: */ + +c_qm(i) long i; { PC = (Stack[SP-1]) ? PC : i; SP--; } +c_colon(i) long i; { PC = i; } + +/* accessing a parameter */ + +c_param(i) +long i; +{ + Stack[ SP++ ] = Stack[ FP + i ]; +} + +/* calling a function */ + +c_func(i) +long i; +{ + Stack[ SP++ ] = PC; + Stack[ SP++] = FP; + PC = i; + FP = SP-(StackProgram[PC].value+2); +} + +/* returning from a function */ + +c_return(i) +long i; +{ + PC = Stack[ SP-3 ]; + FP = Stack[ SP-2 ]; + Stack[ SP-(3+i) ] = Stack[ SP-1 ]; + SP -= (2+i); +} + +/* operand functions */ + +c_number(i) long i; { Stack[SP++] = i; } +c_atime(i) long i; { Stack[SP++] = attr.buf->st_atime; } +c_ctime(i) long i; { Stack[SP++] = attr.buf->st_ctime; } +c_dev(i) long i; { Stack[SP++] = attr.buf->st_dev; } +c_gid(i) long i; { Stack[SP++] = attr.buf->st_gid; } +c_ino(i) long i; { Stack[SP++] = attr.buf->st_ino; } +c_mode(i) long i; { Stack[SP++] = attr.buf->st_mode; } +c_mtime(i) long i; { Stack[SP++] = attr.buf->st_mtime; } +c_nlink(i) long i; { Stack[SP++] = attr.buf->st_nlink; } +c_rdev(i) long i; { Stack[SP++] = attr.buf->st_rdev; } +c_size(i) long i; { Stack[SP++] = attr.buf->st_size; } +c_uid(i) long i; { Stack[SP++] = attr.buf->st_uid; } +c_depth(i) long i; { Stack[SP++] = attr.depth; } +c_prune(i) long i; { Stack[SP++] = 0; attr.prune = 1; } + +/* calculate the filename length */ + +c_baselen(i) +long i; +{ + char *c; register int len; + + len = 0; + for(c=attr.fname; *c; c++ ) + if( *c == '/' ) len = 0; + else len += 1; + Stack[SP++] = len; +} + +/* ---------------------------------------------------------------------- + * c_str: + * This implements the regular expression stuff. + * 'i' is an index into the array Strbuf[]. The + * string contained there is the actual '\0' terminated + * string that occured in the expression (eg "*.BAK" ), minus + * the quotes "". + */ + +c_str(i) +long i; +{ + char *tail,*strrchr(); + + tail = strrchr(attr.fname, '/'); + tail = (tail) ? tail+1 : attr.fname; + Stack[SP++] = glob_match(&Strbuf[i], tail, 1); +} diff --git a/rhdata.c b/rhdata.c new file mode 100644 index 0000000..6bd4ec0 --- /dev/null +++ b/rhdata.c @@ -0,0 +1,111 @@ + +/* ---------------------------------------------------------------------- + * FILE: rhdata.c + * VERSION: 2 + * Written by: Ken Stauffer + * This file contains the predefined symbol table, and related data + * structures. + * + * ---------------------------------------------------------------------- */ + +#include +#include +#define DATA +#include "rh.h" + +struct symbol *symbols; + +struct symbol *tokensym; +long tokenval; +long token; + +char Strbuf[ STRLEN+1 ]; +int strfree=0; + +struct instr StackProgram[ LENGTH ]; +int PC; +int startPC; + +long Stack[ MEM+3 ]; +int SP; +int FP; + +struct runtime attr; + +/* + * The following variables specify where the input is comming from. + * If expstr == NULL then the input is certainly not from there, and + * instead is taken from expfile. + * else expstr is used as input. + * + */ + +char *expstr; +FILE *expfile; +char *expfname; + +static struct symbol init_syms[]={ + { "NOW", NUMBER, 0, c_number, NULL }, + { "IFBLK", NUMBER, S_IFBLK, c_number, NULL }, + { "IFCHR", NUMBER, S_IFCHR, c_number, NULL }, + { "IFDIR", NUMBER, S_IFDIR, c_number, NULL }, + { "IFMT", NUMBER, S_IFMT, c_number, NULL }, + { "IFREG", NUMBER, S_IFREG, c_number, NULL }, + { "ISGID", NUMBER, S_ISGID, c_number, NULL }, + { "ISUID", NUMBER, S_ISUID, c_number, NULL }, + { "ISVTX", NUMBER, S_ISVTX, c_number, NULL }, +#ifdef S_IFLNK + { "IFLNK", NUMBER, S_IFLNK, c_number, NULL }, +#endif +#ifdef S_IFSOCK + { "IFSOCK", NUMBER, S_IFSOCK, c_number, NULL }, +#endif +#ifdef S_IFIFO + { "IFIFO", NUMBER, S_IFIFO, c_number, NULL }, +#endif + { "atime", FIELD, 0, c_atime, NULL }, + { "ctime", FIELD, 0, c_ctime, NULL }, + { "dev", FIELD, 0, c_dev, NULL }, + { "gid", FIELD, 0, c_gid, NULL }, + { "ino", FIELD, 0, c_ino, NULL }, + { "mode", FIELD, 0, c_mode, NULL }, + { "mtime", FIELD, 0, c_mtime, NULL }, + { "nlink", FIELD, 0, c_nlink, NULL }, + { "rdev", FIELD, 0, c_rdev, NULL }, + { "size", FIELD, 0, c_size, NULL }, + { "uid", FIELD, 0, c_uid, NULL }, + { "depth", FIELD, 0, c_depth, NULL }, + { "prune", FIELD, 0, c_prune, NULL }, + { "days", NUMBER, 24*3600, c_number, NULL }, + { "weeks", NUMBER, 24*3600*7, c_number, NULL }, + { "hours", NUMBER, 3600, c_number, NULL }, + { "strlen", FIELD, 0, c_baselen, NULL }, + { "return", RETURN, 0, c_return, NULL } +}; + +rhinit() +{ + int i; + struct symbol *s,*locatename(); + + symbols = &init_syms[0]; + + for(i=0; i< sizeof(init_syms)/sizeof(struct symbol)-1; i++ ) + init_syms[i].next = &init_syms[i+1]; + + /* initialize the NOW variable to the time right now */ + s = locatename( "NOW" ); + s->value = time(0); +} + +rhfinish() +{ + struct symbol *s; + + while(symbols->type == PARAM || symbols->type == FUNCTION) { + s = symbols; + symbols = symbols->next; + free(s->name); + free(s); + } +} diff --git a/rhdir.c b/rhdir.c new file mode 100644 index 0000000..af4b331 --- /dev/null +++ b/rhdir.c @@ -0,0 +1,297 @@ + +/* ---------------------------------------------------------------------- + * FILE: rhdir.c + * VERSION: 2 + * Written by: Ken Stauffer + * This file contains the "non portable" stuff dealing with + * directories. + * printentry(), ftrw(), fwt1() + * + * + * ---------------------------------------------------------------------- */ + +#include "rh.h" +#include +#include + +#define user_index(b) ((000777 & (b)) >> 6) + ((b) & S_ISUID ? 8 : 0) +#define group_index(b) ((000077 & b) >> 3) + ((b) & S_ISGID ? 8 : 0) +#define all_index(b) ((000007 & (b)) + (((b) & S_ISVTX) ? 8 : 0)) +#define ftype_index(b) ((b) >> 13) + +#define isdirect(b) (((b)&S_IFMT)==S_IFDIR) +#define isblk(b) (((b)&S_IFMT)==S_IFBLK) +#define ischr(b) (((b)&S_IFMT)==S_IFCHR) + +/* + * Some System do not define these macro's. + * If these macro's are missing, then use these ones: + * + * #define major(b) ((b)>>8) + * #define minor(b) ((b)&0xff) + * + */ + +#define isdot(s) ((s)[1]=='\0' && (s)[0]=='.') +#define isdotdot(s) ((s)[2]=='\0' && (s)[1]=='.' && (s)[0]=='.') + +#ifdef S_IFLNK +#define islink(b) (((b)&S_IFMT) == S_IFLNK) +#else +#define islink(b) (0) +#define lstat stat +#endif + +#define isproper(m) (isdirect(m) && !islink(m) && !attr.prune) + +#ifdef POSIX_DIRECTORY_LIBRARY +#include +#else +#include +#endif + +/* + * XXX - on BSD systems, this is defined in . + * On System V Release 3, it's defined inside "nami.c", and is, + * unfortunately, not in any include file. + * On systems other than those, no such simple limit exists. + * On BSD and S5R3 systems, as distributed by Berkeley and AT&T, + * respectively, it's 1024, so we set it to that. + */ +#define MAXPATHLEN 1024 + +/* ---------------------------------------------------------------------- + * printentry: + * Display filename,permissions and size in a '/bin/ls' like + * format. If verbose is non-zero then more information is + * displayed. + * uses the macros: + * user_index(b) + * group_index(b) + * all_index(b) + * ftype_index(b) + * + */ + +printentry(verbose,buf,name) +struct stat *buf; +char *name; +{ + char *t,*ctime(); + + static char *ftype[]={ "p", "c" , + "d" , "b" , + "-" , "l" , + "s" , "t" }; + + static char *perm[]={ "---", "--x", "-w-", "-wx" , + "r--", "r-x", "rw-", "rwx" , + "--S", "--s", "-wS", "-ws" , + "r-S", "r-s", "rwS", "rws" }; + + static char *perm2[]={ "---", "--x", "-w-", "-wx" , + "r--", "r-x", "rw-", "rwx" , + "--T", "--t", "-wT", "-wt" , + "r-T", "r-t", "rwT", "rwt" }; + + if( verbose ) { + t = ctime(&buf->st_mtime); + t[24] = '\0'; + if( ischr(buf->st_mode) || isblk(buf->st_mode) ) + + printf("%s%s%s%s %4d %4d %3d,%3d %s %-s\n", + ftype[ ftype_index(buf->st_mode) ], + perm[ user_index(buf->st_mode) ], + perm[ group_index(buf->st_mode) ], + perm2[ all_index(buf->st_mode) ], + buf->st_uid, + buf->st_gid, + major(buf->st_rdev), + minor(buf->st_rdev), + t+4, + name ); + + else + + printf("%s%s%s%s %4d %4d %6d %s %-s\n", + ftype[ ftype_index(buf->st_mode) ], + perm[ user_index(buf->st_mode) ], + perm[ group_index(buf->st_mode) ], + perm2[ all_index(buf->st_mode) ], + buf->st_uid, + buf->st_gid, + buf->st_size, + t+4, + name ); + + } else { + + if( ischr(buf->st_mode) || isblk(buf->st_mode) ) + + printf("%s%s%s%s %3d,%3d %-s\n", + ftype[ ftype_index(buf->st_mode) ], + perm[ user_index(buf->st_mode) ], + perm[ group_index(buf->st_mode) ], + perm2[ all_index(buf->st_mode) ], + major(buf->st_rdev), + minor(buf->st_rdev), + name ); + + else + + printf("%s%s%s%s %9d %-s\n", + ftype[ ftype_index(buf->st_mode) ], + perm[ user_index(buf->st_mode) ], + perm[ group_index(buf->st_mode) ], + perm2[ all_index(buf->st_mode) ], + buf->st_size, + name ); + + } +} + +/* ---------------------------------------------------------------------- + * ftrw: + * Entry point to do the search, ftrw is a front end + * to the recursive fwt1. + * ftrw() initializes some global variables and + * builds the initial filename string which is passed to + * ftw1(). + */ + +ftrw(f,fn,depth) +char *f; +int (*fn)(); +int depth; +{ + char *p,filebuf[ MAXPATHLEN+1 ]; + struct stat statbuf; + int last; + + attr.prune = 0; + attr.depth = 0; + attr.func=fn; + attr.fname = filebuf; + attr.buf = &statbuf; + strcpy(attr.fname,f); + + last = 0; + for(p=attr.fname; *p; p++) + if( *p == '/' ) last = 1; + else last = 0; + + if( !last ) { *p++ = '/'; *p = '\0'; } + + if( lstat(attr.fname,attr.buf) < 0 ) return(-1); + + (*(attr.func))(); + + if( isproper( attr.buf->st_mode ) ) fwt1(depth,p); + + return(0); +} + +/* ---------------------------------------------------------------------- + * fwt1: + * 'p' points to the end of the string in attr.fname + * + * 2 versions of this routine currently live here: + * "new-style", for systems with a BSD or POSIX-style + * directory library, and systems without such a + * directory library. They both differ in + * the manner in which they access directories. + * Any chnages needed to work on another system + * should only have to made for this routine. + * + * Below is the "directory library" version of fwt1() + * + */ + +#if defined(POSIX_DIRECTORY_LIBRARY) || defined(BSD) + +static fwt1(depth,p) +int depth; +char *p; +{ + char *q,*s; + DIR *dirp; +#ifdef POSIX_DIRECTORY_LIBRARY + struct dirent *dp; +#else + struct direct *dp; +#endif + if( !depth ) return; + attr.depth += 1; + + dirp=opendir(attr.fname); + if( dirp == NULL ) return; + for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if( isdot(dp->d_name) || isdotdot(dp->d_name) ) continue; + s = p; + q = dp->d_name; + while( *s++ = *q++ ); + s -= 1; + if( lstat(attr.fname,attr.buf) < 0 ) continue; + (*(attr.func))(); + if( isproper( attr.buf->st_mode ) ) { + *s++ = '/'; + *s = '\0'; + fwt1(depth-1,s); + } + } + closedir(dirp); + attr.depth -= 1; + attr.prune = 0; + *p = '\0'; +} +#else + +/* ---------------------------------------------------------------------- + * fwt1: + * This function does the same thing as fwt1() above, but is + * meant for systems without a directory library, that does + * directory reading "by hand". + * + * Below is the "no directory library" version of fwt1() + * + */ + +static fwt1(depth,p) +int depth; +char *p; +{ + char *q,*s; + FILE *dirp; + struct direct dp; + int count; + + if( !depth ) return; + attr.depth += 1; + + dirp=fopen(attr.fname,"r"); + if( dirp == NULL ) return; + for(count = fread(&dp,sizeof(struct direct),1,dirp); count; + count = fread(&dp,sizeof(struct direct),1,dirp) ) { + + if( isdot(dp.d_name) || isdotdot(dp.d_name) || dp.d_ino==0 ) + continue; + s = p; + q = dp.d_name; + while( *s++ = *q++ ); + s -= 1; + + if( lstat(attr.fname,attr.buf) < 0 ) continue; + (*(attr.func))(); + if( isproper( attr.buf->st_mode ) ) { + *s++ = '/'; + *s = '\0'; + fwt1(depth-1,s); + } + } + fclose(dirp); + attr.depth -= 1; + attr.prune = 0; + *p = '\0'; +} + +#endif diff --git a/rhparse.c b/rhparse.c new file mode 100644 index 0000000..94f9b3d --- /dev/null +++ b/rhparse.c @@ -0,0 +1,784 @@ + +/* ---------------------------------------------------------------------- + * FILE: rhparse.c + * VERSION: 2 + * Written by: Ken Stauffer + * This contains the parser for the C expressions, + * gettoken(), getit() and ungetit() routines. + * sectime(), datespec(), expression(), expr(), exp0(), ... , factor() + * locatename(), push(), find_macro() + * + * + * ---------------------------------------------------------------------- */ + +#include "rh.h" +#include +#include + +static int cpos; /* current character position */ +static int lineno; /* current line number */ + +/* ---------------------------------------------------------------------- + * getit: + * Return the next character, input is obtained from a file or + * a string. + * If expstr == NULL then input is from the file called 'expfname' + * with file pointer 'expfile'. + * + * If expstr != NULL then input is from the string 'expstr' + * + */ + +getit() +{ + int c; + + if( expstr ) c = (*expstr) ? *expstr++ : EOF; + else c = getc(expfile); + + if( c == '\n' ) { lineno++; cpos = 0; } + cpos++; + + return(c); +} + +/* ---------------------------------------------------------------------- + * ungetit: + * Unget a char. + * A character is ungotten using the same scheme as stated for + * getit() for determining where input is comming from. + * + */ + +ungetit(c) +int c; +{ + if( c == '\n' ) { lineno--; cpos = 1; } + else cpos--; + if( expstr ) expstr = (c > 0) ? expstr-1 : expstr; + else ungetc(c,expfile); +} + +/* ---------------------------------------------------------------------- + * error: + * Print an error message and quit. + */ + +error(s) +char *s; +{ + if( expstr ) + fprintf(stderr,"Command line: "); + else + fprintf(stderr,"%s: ",expfname); + + fprintf(stderr,"line: %d, char: %d, %s.\n",lineno,cpos,s); + exit(1); +} + +/* ---------------------------------------------------------------------- + * insertname: + * Inserts the symbol named 's' with type 't' and value 'val' + * into the symbol table. Return the a pointer to the symbol + * table entry. The symbol is inserted into the head of the + * linked list. This behavior is relied upon elswhere. + * + */ + +struct symbol *insertname(s,t,val) +char *s; +int t; +long val; +{ + char *p,*malloc(); + struct symbol *sym; + + sym = (struct symbol *) malloc( sizeof(struct symbol) ); + if( sym == NULL ) error("no more memory"); + + p = sym->name = malloc( strlen(s)+1 ); + if( sym->name == NULL ) error("no more memory"); + while( *p++ = *s++ ); + sym->type = t; + sym->value = val; + + sym->next = symbols; + symbols = sym; + + return( sym ); +} + +/* ---------------------------------------------------------------------- + * locatename: + * Do a linear search for 's' in the linked list symbols. + * + */ + +struct symbol *locatename(s) +char *s; +{ + struct symbol *p; + + for(p=symbols; p; p = p->next ) + if( !strcmp(s,p->name) ) return(p); + + return(NULL); +} + +/* ---------------------------------------------------------------------- + * push: + * "assemble" the instruction into the StackProgram[] array. + * + */ + +push(func,val) +int (*func)(); +long val; +{ + if( PC >= LENGTH ) error("program to big"); + StackProgram[PC].func=func; + StackProgram[PC++].value=val; +} + +/* ---------------------------------------------------------------------- + * program: + * Parse a program of the form: + * ==> EOF + * | EOF + * | ; + * + * ==> + * | empty + */ + +program() +{ + cpos = 0; lineno = 1; + + token = gettoken(); + for(;;) { + if( token != IDENTIFIER ) break; + function(); + } + + if( token != EOF ) { + startPC = PC; + expression(); + push(NULL,0); + } + if( token != EOF && token != ';') error("EOF expected"); +} + +/* ---------------------------------------------------------------------- + * function: + * parse a function definition. Grammer for a function is: + * ==> IDENTIFIER { RETURN ; } + * + * ==> ( ) + * | ( ) + * | empty + * + * ==> IDENTIFIER + * + * ==> , + * | empty + * + */ + +function() +{ + struct symbol *s; + + s = tokensym; + tokensym->value = PC; + tokensym->type = FUNCTION; + tokensym->func = c_func; + + token = gettoken(); + + push(NULL, idlist() ); /* save number of args for function */ + + if( token != '{' ) error("expected '{'"); + token = gettoken(); + + if( token != RETURN ) error("expected keyword: return"); + token = gettoken(); + + expression(); + + if( token != ';' ) error("expected ';'"); + token = gettoken(); + + push(c_return,StackProgram[s->value].value); + + /* free up the parameter symbols */ + while( symbols->type == PARAM ) { + s = symbols; + symbols = symbols->next; + free(s->name); + free(s); + } + + if( token != '}' ) error("expected '}'"); + token = gettoken(); +} + +/* ---------------------------------------------------------------------- + * idlist: + * Return the maximum offset obtained in parsing the parameter list. + * ==> ( ) + * | () + * | empty + * + * ==> IDENTIFIER + * ==> , + * | empty + */ + +idlist() +{ + int offset = 0; + + if( token == '(' ) token = gettoken(); + else if( token == '{' ) return(0); + else error("expected '(' or '{'"); + + if( token == ')' ) { + token = gettoken(); + return(0); + } + + for(;;) { + if( token != IDENTIFIER ) error("identifier expected"); + tokensym->type = PARAM; + tokensym->func = c_param; + tokensym->value = offset++; + token = gettoken(); + if( token == ')' ) break; + if( token != ',' ) error("expected ')'"); + token = gettoken(); + } + + token = gettoken(); + return(offset); +} + +/* ---------------------------------------------------------------------- + * expression: + * Parse an expression. (top-level routine) + * OPERATOR ?: + * + */ + +expression() +{ + int qm,colon,nop; + + expr0(); + if( token == '?' ) { + token = gettoken(); + qm = PC; + push(c_qm,0); + expression(); + if( token != ':' ) error("missing ':'"); + token = gettoken(); + colon = PC; + push(c_colon,0); + expression(); + + StackProgram[qm].value = colon; + StackProgram[colon].value = PC-1; + } +} + +/* OPERATOR || */ +expr0() +{ + expr1(); + for(;;) + if( token == OR ) { + token = gettoken(); + expr1(); + push(c_or,0); + } else break; +} + +/* OPERATOR && */ +expr1() +{ + expr2(); + for(;;) + if( token == AND ) { + token = gettoken(); + expr2(); + push(c_and,0); + } else break; +} + +/* OPERATOR | */ +expr2() +{ + expr3(); + for(;;) + if( token == '|' ) { + token = gettoken(); + expr3(); + push(c_bor,0); + } else break; +} + +/* OPERATOR ^ */ +expr3() +{ + expr4(); + for(;;) + if( token == '^' ) { + token = gettoken(); + expr4(); + push(c_bxor,0); + } else break; +} + +/* OPERATOR & */ +expr4() +{ + expr5(); + for(;;) + if( token == '&' ) { + token = gettoken(); + expr5(); + push(c_band,0); + } else break; +} + +/* OPERATOR == != */ +expr5() +{ + int t; + expr6(); + for(;t=token;) + if( t==EQ ) { + token = gettoken(); + expr6(); + push(c_eq,0); + } else if( t==NE ) { + token = gettoken(); + expr6(); + push(c_ne,0); + } else break; +} + +/* OPERATOR < <= > >= */ +expr6() +{ + int t; + expr7(); + for(;t=token;) + if( t==LE ) { + token = gettoken(); + expr7(); + push(c_le,0); + } else if( t==GE ) { + token = gettoken(); + expr7(); + push(c_ge,0); + } else if( t=='>' ) { + token = gettoken(); + expr7(); + push(c_gt,0); + } else if( t=='<' ) { + token = gettoken(); + expr7(); + push(c_lt,0); + } else break; +} + +/* OPERATOR << >> */ +expr7() +{ + int t; + expr8(); + for(;t=token;) + if( t==SHIFTL ) { + token = gettoken(); + expr8(); + push(c_lshift,0); + } else if( t==SHIFTR ) { + token = gettoken(); + expr8(); + push(c_rshift,0); + } else break; +} + +/* OPERATOR + - */ +expr8() +{ + int t; + expr9(); + for(;t=token;) + if( t=='+' ) { + token = gettoken(); + expr9(); + push(c_plus,0); + } else if( t=='-' ) { + token = gettoken(); + expr9(); + push(c_minus,0); + } else break; +} + +/* OPERATOR * / % */ +expr9() +{ + int t; + expr10(); + for(;t=token;) + if( t== '*' ) { + token = gettoken(); + expr10(); + push(c_mul,0); + } else if( t== '/' ) { + token = gettoken(); + expr10(); + push(c_div,0); + } else if( t== '%' ) { + token = gettoken(); + expr10(); + push(c_mod,0); + } else break; +} + +/* OPERATOR ~ ! - */ +expr10() +{ + int t; + t = token; + if( t=='!' ){ + token = gettoken(); + expr10(); + push(c_not,0); + } else if( t== '~' ) { + token = gettoken(); + expr10(); + push(c_bnot,0); + } else if( t== '-' ) { + token = gettoken(); + expr10(); + push(c_uniminus,0); + } else factor(); +} + +/* ---------------------------------------------------------------------- + * explist: + * argc is the number of arguments expected. + * Parse an expression list of the form: + * ==> ( ) + * | ( ) + * | empty + * + * ==> , + * | + * + */ + +explist( argc ) +{ + if( token != '(' && !argc ) return; + + if( token != '(' ) error("missing '('"); + token = gettoken(); + + if( !argc && token == ')' ) { + token = gettoken(); + return; + } + + for(;;) { + expression(); + argc--; + if( token == ')' ) break; + if( token != ',' ) error("missing ','"); + token = gettoken(); + } + + token = gettoken(); + if( argc ) error("wrong number of arguments"); +} + +/* ---------------------------------------------------------------------- + * factor: + * Parse a factor. Could be a number, variable, date, function call or + * regular expression string. + */ + +factor() +{ + long l,datespec(); + int pc; + + switch(token) { + case '(': + token = gettoken(); + expression(); + if( token != ')' ) + error("missing ')'"); + token = gettoken(); + break; + case NUMBER: + push(c_number,tokenval); + token = gettoken(); + break; + case FUNCTION: + pc = tokensym->value; + token = gettoken(); + explist( StackProgram[ pc ].value ); + push(c_func,pc); + break; + case PARAM: + push(c_param,tokensym->value); + token = gettoken(); + break; + case FIELD: + push(tokensym->func,tokenval); + token = gettoken(); + break; + case '[': + token = gettoken(); + l=datespec(); + if( token != ']' ) + error("missing ']'"); + token = gettoken(); + push(c_number,l); + break; + case STR: + push(c_str,tokenval); + token = gettoken(); + break; + case IDENTIFIER: + error("undefined identifier"); + default: + error("syntax error"); + } +} + +/* ---------------------------------------------------------------------- + * sectime: + * calculate the number of seconds between January 1, 1970 + * and year/month/day. Return that value. + * + */ + +#define leap(d) (((d % 4 == 0) && (d % 100 != 0)) || (d % 400 == 0)) +#define DAYSEC (3600*24) +#define YERSEC (3600*24*365) +#define TIME0 1970 + +long sectime(year,month,day) +int year,month,day; +{ + + static int months[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; + int yeardays,leapers,x; + long seconds; + + if(month>12 || month<1 || yearmonths[month]+ + (month==2 && leap(year)) ) + return(-1); + + yeardays = leapers = 0; + + for(x=1;x 2) && leap(year)) yeardays++; + + for(x=TIME0; x= '0' && c <= '7' ) { + tokenval <<= 3; + tokenval += c-'0'; + } + if( isdigit(c) ) error("bad octal constant"); + ungetit(c); + return(NUMBER); + } + + if(isdigit(c)) { + tokenval=c-'0'; + while(isdigit( (c=getit()) )) { + tokenval *=10; + tokenval += c-'0'; + } + ungetit(c); + return(NUMBER); + } + + if(isalpha(c)) { + int count=0; + do { + if(count++ < IDLENGTH) *bufp++ = c; + c=getit(); + } while( isalnum(c) ); + ungetit(c); + *bufp='\0'; + if( (tokensym=locatename(buf)) == NULL ) { + tokensym = insertname(buf,IDENTIFIER,0); + } + tokenval = tokensym->value; + return( tokensym->type ); + } + + if( c == '"' ) { + tokenval=strfree; + while( (c=getit()) != '"' ) { + if( strfree > STRLEN ) + error("no more string space"); + Strbuf[strfree++]= c; + } + Strbuf[strfree++]='\0'; + return(STR); + } + + if( c == '=' ) { + c=getit(); + if(c== '=') return(EQ); + else { + ungetit(c); + return('='); + } + } + + if( c== '$' ) { + int count=0; + struct passwd *info,*getpwnam(); + c=getit(); + if( c=='$' ) { + tokenval = getuid(); + return( NUMBER ); + } + do { + if (count++ < IDLENGTH) *bufp++ = c; + c=getit(); + } while( isalnum(c) ); + ungetit(c); + *bufp='\0'; + if( (info=getpwnam(buf)) == NULL ) + error("no such user"); + tokenval = info->pw_uid; + return( NUMBER ); + } + + if( c == '!' ) { + c=getit(); + if( c == '=' ) return(NE); + ungetit(c); + return('!'); + } + + if( c == '>' ) { + c=getit(); + if( c == '=' ) return(GE); + if( c == '>' ) return(SHIFTR); + ungetit(c); + return('>'); + } + + if( c == '<' ) { + c=getit(); + if( c == '=' ) return(LE); + if( c == '<' ) return(SHIFTL); + ungetit(c); + return('<'); + } + + if( c == '&' ) { + c=getit(); + if( c == '&' ) return(AND); + ungetit(c); + return('&'); + } + + if( c == '|' ) { + c=getit(); + if( c == '|' ) return(OR); + ungetit(c); + return('|'); + } + + return(c); +} + diff --git a/rhrc b/rhrc new file mode 100644 index 0000000..ecd3e83 --- /dev/null +++ b/rhrc @@ -0,0 +1,158 @@ + +/* ---------------------------------------------------------------------- + * FILE: $HOME/.rhrc + * VERSION: 2 + * This file is a sample .rhrc that should live in your home + * directory. + * The contents of this file are read BEFORE any other input is read + * from the user. Functions defined here will be useable elsewhere. + * + */ + +/* + * The dir() function tests a file to see if it is a directory. + * The command "rh -e dir", would be enough to find just directories + * + */ + +dir() +{ + return( (mode & IFMT) == IFDIR ); +} + +/* + * months - This function can be used like a constant. + * It evaluates to the number of seconds in a month (average). + * Expressions like the following are now possible: + * (mtime > NOW-2*months) + * Would find files that have been modified in the last 2 + * months. + * + */ + +months +{ + return days*30; +} + +/* + * This defines a useful "alias" for the 'nlink' variable, + * which may be easier to remember. + * + */ + +nlinks { return nlink; } + +/* + * This function returns the number of seconds passed as its argument + * minus NOW. Used to make some time comparisons "cleaner". + * + */ + +ago(d) +{ + return( NOW - d ); +} + +/* + * returns true if a file is writable by others (and group). + * + */ + +writable() +{ + return mode & 022; +} + +MINE +{ + return( uid == $$ ); +} + +/* + * This is my "bad" function which can be easily invoked by the command: + * % rh -le bad + * This allows me to find files that are not a good idea to have around: + * - core files. + * - old a.out files. + * - files writeable by other people. + * - checkpoint and backup files. + * + */ + +bad() +{ + return( "core" || ("a.out" && mtime <= ago(2*days) ) || + "*.BAK" || "*.CKP" || writable); +} + +/* + * Find C related files. + * + */ + +csrc() +{ + return("*.c" || "*.h" || "[Mm]akefile" ); +} + +/* + * Find files that have been modified in the last + * 1 hour. + * + */ + +changed() +{ + return( mtime > NOW-1*hours ); + + /* ALTERNATELY: + * (using the ago() function) + * + * return( mtime > ago(1*hours) ); + * + */ +} + +/* + * This function can be used as a constant. + * + */ + +K() +{ + return 1024; +} + +/* megs { return K*K; } or ...*/ +megs { return K<<10; } + +/* + * This function shows that recursion is quite possible. + * Call this function with any number, the higher the number + * deeper in recursion it goes. (for large values this function + * overflows the stack). This can be used to impose a delay, but that + * would be useless on my machine, cause it is sooooo slow already. + * Neat mathamatical functions can be calculated this way. + * + */ + +recursion(n) +{ + return (n>0) ? recursion(n-1) : 0; +} + +/* + * sqrt(n), returns the integer square root of the number n. + * Useful for expressions like: + * sqrt(uid) <= gid; + * Which finds files where the square root of the user-id is + * less than the gid. This one will be used rarely if ever. + * sqrt1() is a helper function. + * + */ + +sqrt1(n,odd) { return( (n <= 0 ) ? 0 : sqrt1(n-odd,odd+2)+1 ); } +sqrt(n) { return( sqrt1(n,1) ); } + +fact(n) { return (n<=0) ? 1 : fact(n-1)*n; }