From 4e590aafc3cdbba677ecb596f76ae588aa396d5e Mon Sep 17 00:00:00 2001 From: raf Date: Sun, 18 Feb 1990 13:40:40 +1100 Subject: [PATCH] Initial commit (from Comp.sources.unix by Ken Stauffer) --- MANIFEST | 0 META | 29 ++ Makefile | 58 ++++ README | 144 ++++++++++ getopt.c | 452 +++++++++++++++++++++++++++++ glob_match.c | 216 ++++++++++++++ rh.c | 267 ++++++++++++++++++ rh.h | 131 +++++++++ rh.man | 572 +++++++++++++++++++++++++++++++++++++ rhcmds.c | 127 +++++++++ rhdata.c | 111 ++++++++ rhdir.c | 297 +++++++++++++++++++ rhparse.c | 784 +++++++++++++++++++++++++++++++++++++++++++++++++++ rhrc | 158 +++++++++++ 14 files changed, 3346 insertions(+) create mode 100644 MANIFEST create mode 100644 META create mode 100644 Makefile create mode 100644 README create mode 100644 getopt.c create mode 100644 glob_match.c create mode 100644 rh.c create mode 100644 rh.h create mode 100644 rh.man create mode 100644 rhcmds.c create mode 100644 rhdata.c create mode 100644 rhdir.c create mode 100644 rhparse.c create mode 100644 rhrc 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; }