byron@archone.tamu.edu (Byron Rakitzis) (05/22/91)
Submitted-by: Byron Rakitzis <byron@archone.tamu.edu> Posting-number: Volume 20, Issue 12 Archive-name: rc/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: CHANGES glob.c hash.c heredoc.c parse.y redir.c tree.c # utils.c var.c which.c # Wrapped by kent@sparky on Wed May 22 01:21:49 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 3 (of 4)."' if test -f 'CHANGES' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'CHANGES'\" else echo shar: Extracting \"'CHANGES'\" \(2395 characters\) sed "s/^X//" >'CHANGES' <<'END_OF_FILE' XBugs and changes since version 0.9: X X----- XA pipe with a null first command caused rc to dump core. e.g., X X |; X----- XWhen fifo redirection was used with builtin commands, rc would hang: X X diff <{echo hi} <{echo bye} X Xa missing exit() was the cause of all this X----- XA double backquote operator was added to make inlining of ifs-type material Xpossible; thus X X `` ($ifs / :) command X Xis the same as X X ifs=($ifs / :) X ` command X Xonly without the assignment. X----- XAn "rc error" (e.g., trying to evaluate $()) inside a function messed up Xrc's interactive state, i.e., it stopped printing prompts. X X fn foo { echo $() } X Xthis was fixed by adding full-fledged exception handling, with a separate Xexception stack X----- Xrc did not raise sigexit from the builtin "exit". X----- Xrc assumed the existence of getgroups(). Also, a typo in Xthe getgroups() code in which.c was fixed. X----- Xrc now sets $0 to the name of the function being called or the file being Xinterpreted by dot. X----- Xif - else statements were incorrectly exported into the environment X----- Xrc now has stubs for GNU readline compatability. (compile -DREADLINE) X----- Xrc used to use a single arena for temporary space which got clobbered if there Xwas a command of the form: X X eval foo ; bar X Xsince the eval would cause rc to prematurely recycle that memory. I fixed Xthis by adding support for multiple arenas. X----- Xan assignment inside a compound command clobbered the global definition Xof a variable: X X foo=a X foo=b { stuff; foo=c; stuff } X X$foo was set to () after this. X----- Xrc -e now is much more sh-like. In particular X X false || echo bletch X Xnow does the right thing. X----- Xrc did not print $prompt(2) while scanning heredocs X----- Xexec with a redirection but without other arguments now does the Xsame thing as sh, i.e., it alters rc's own file descriptors X----- Xthe command X X eval eval eval [200 times or so] echo hi X Xwould cause rc to allocate obnoxious amounts of memory. This has Xbeen cleaned up. X----- Xrc how has full support for *all* signals, not just sigquit, sigint Xand sighup and sigterm. Additionally, the signal code is generated via Xan awk script operating on /usr/include/signal.h, so that rc "learns" Xabout the signals of a particular machine at compile time. X----- Xrc now supports "fn prompt", a function that gets executed before Xeach $prompt(1) is printed. X----- X~ () '' incorrectly returned a true status. X----- END_OF_FILE if test 2395 -ne `wc -c <'CHANGES'`; then echo shar: \"'CHANGES'\" unpacked with wrong size! fi # end of 'CHANGES' fi if test -f 'glob.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'glob.c'\" else echo shar: Extracting \"'glob.c'\" \(6192 characters\) sed "s/^X//" >'glob.c' <<'END_OF_FILE' X/* glob.c: rc's (ugly) globber. This code is not elegant, but it works */ X X#include <sys/types.h> X#include "rc.h" X#include "glob.h" X#include "glom.h" X#include "nalloc.h" X#include "utils.h" X#include "match.h" X#include "footobar.h" X#include "list.h" X#ifdef NODIRENT X#include <sys/dir.h> X#define dirent direct /* need to get the struct declaraction right */ X#else X#include <dirent.h> X#endif X Xstatic List *dmatch(char *, char *, char *); Xstatic List *doglob(char *, char *); Xstatic List *lglob(List *, char *, char *, SIZE_T); Xstatic List *sort(List *); X X/* X matches a list of words s against a list of patterns p. Returns true iff X a pattern in p matches a word in s. null matches null, but otherwise null X patterns match nothing. X*/ X Xboolean lmatch(List *s, List *p) { X List *q; X int i; X boolean okay; X X if (s == NULL) { X if (p == NULL) /* null matches null */ X return TRUE; X for (; p != NULL; p = p->n) { /* one or more stars match null */ X if (*p->w != '\0') { /* the null string is a special case; it does *not* match () */ X okay = TRUE; X for (i = 0; p->w[i] != '\0'; i++) X if (p->w[i] != '*' || p->m[i] != 1) { X okay = FALSE; X break; X } X if (okay) X return TRUE; X } X } X return FALSE; X } X X for (; s != NULL; s = s->n) X for (q = p; q != NULL; q = q->n) X if (match(q->w, q->m, s->w)) X return TRUE; X return FALSE; X} X X/* Matches a pattern p against the contents of directory d */ X Xstatic List *dmatch(char *d, char *p, char *m) { X boolean matched = FALSE; X List *top, *r; X DIR *dirp; X struct dirent *dp; X /* prototypes for XXXdir functions. comment out if necessary */ X extern DIR *opendir(const char *); X extern struct dirent *readdir(DIR *); X extern int closedir(DIR *); X X if ((dirp = opendir(d)) == NULL) X return NULL; X X top = r = NULL; X X while ((dp = readdir(dirp)) != NULL) X if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */ X matched = TRUE; X if (top == NULL) { X top = r = nnew(List); X } else { X r->n = nnew(List); X r = r->n; X } X r->w = ncpy(dp->d_name); X r->m = NULL; X } X X closedir(dirp); X X if (!matched) X return NULL; X X r->n = NULL; X return top; X} X X/* X lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*" X will return a list with all the files in /tmp, /usr, and /bin. NULL on no match. X slashcount indicates the number of slashes to stick between the directory and the X matched name. e.g., for matching ////tmp/////foo* X*/ X Xstatic List *lglob(List *s, char *p, char *m, SIZE_T slashcount) { X List *q, *r, *top, foo; X static List slash; X static SIZE_T slashsize = 0; X X if (slashcount + 1 > slashsize) { X slash.w = ealloc(slashcount + 1); X slashsize = slashcount; X } X slash.w[slashcount] = '\0'; X while (slashcount > 0) X slash.w[--slashcount] = '/'; X X for (top = r = NULL; s != NULL; s = s->n) { X q = dmatch(s->w, p, m); X if (q != NULL) { X foo.w = s->w; X foo.m = NULL; X foo.n = NULL; X if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */ X q = concat(&slash, q); /* dir/name with slash */ X q = concat(&foo, q); X if (r == NULL) X top = r = q; X else X r->n = q; X while (r->n != NULL) X r = r->n; X } X } X return top; X} X X/* X Doglob globs a pathname in pattern form against a unix path. Returns the original X pattern (cleaned of metacharacters) on failure, or the globbed string(s). X*/ X Xstatic List *doglob(char *w, char *m) { X static char *dir = NULL, *pattern = NULL, *metadir = NULL, *metapattern = NULL; X static SIZE_T dsize = 0; X char *d, *p, *md, *mp; X SIZE_T psize; X char *s = w; X List firstdir; X List *matched; X int slashcount; X X if ((psize = strlen(w) + 1) > dsize || dir == NULL) { X efree(dir); efree(pattern); efree(metadir); efree(metapattern); X dir = ealloc(psize); X pattern = ealloc(psize); X metadir = ealloc(psize); X metapattern = ealloc(psize); X dsize = psize; X } X X d = dir; X p = pattern; X md = metadir; X mp = metapattern; X X if (*s == '/') { X while (*s == '/') X *d++ = *s++, *md++ = *m++; X } else { X while (*s != '/' && *s != '\0') X *d++ = *s++, *md++ = *m++; /* get first directory component */ X } X *d = '\0'; X X /* X Special case: no slashes in the pattern, i.e., open the current directory. X Remember that w cannot consist of slashes alone (the other way *s could be X zero) since doglob gets called iff there's a metacharacter to be matched X */ X if (*s == '\0') { X matched = dmatch(".", dir, metadir); X goto end; X } X X if (*w == '/') { X firstdir.w = dir; X firstdir.m = metadir; X firstdir.n = NULL; X matched = &firstdir; X } else { X /* X we must glob against current directory, X since the first character is not a slash. X */ X matched = dmatch(".", dir, metadir); X } X X do { X for (slashcount = 0; *s == '/'; s++, m++) X slashcount++; /* skip slashes */ X X while (*s != '/' && *s != '\0') X *p++ = *s++, *mp++ = *m++; /* get pattern */ X *p = '\0'; X X matched = lglob(matched, pattern, metapattern, slashcount); X X p = pattern, mp = metapattern; X } while (*s != '\0'); X Xend: if (matched == NULL) { X matched = nnew(List); X matched->w = w; X matched->m = NULL; X matched->n = NULL; X } X return matched; X} X X/* X Globs a list; checks to see if each element in the list has a metacharacter. If it X does, it is globbed, and the output is sorted. X*/ X XList *glob(List *s) { X List *top, *r; X boolean meta; X X for (r = s, meta = FALSE; r != NULL; r = r->n) X if (r->m != NULL) X meta = TRUE; X X if (!meta) X return s; /* don't copy lists with no metacharacters in them */ X X for (top = r = NULL; s != NULL; s = s->n) { X if (s->m == NULL) { /* no metacharacters; just tack on to the return list */ X if (top == NULL) { X top = r = nnew(List); X } else { X r->n = nnew(List); X r = r->n; X } X r->w = s->w; X } else { X if (top == NULL) X top = r = sort(doglob(s->w, s->m)); X else X r->n = sort(doglob(s->w, s->m)); X while (r->n != NULL) X r = r->n; X } X } X X r->n = NULL; X return top; X} X Xstatic List *sort(List *s) { X SIZE_T nel = listnel(s); X X if (nel > 1) { X char **a; X List *t; X X qsort(a = list2array(s, FALSE), nel, sizeof(char *), starstrcmp); X X for (t = s; t != NULL; t = t->n) X t->w = *a++; X } X X return s; X} END_OF_FILE if test 6192 -ne `wc -c <'glob.c'`; then echo shar: \"'glob.c'\" unpacked with wrong size! fi # end of 'glob.c' fi if test -f 'hash.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'hash.c'\" else echo shar: Extracting \"'hash.c'\" \(6588 characters\) sed "s/^X//" >'hash.c' <<'END_OF_FILE' X/* hash.c: hash table support for functions and variables. */ X X/* X functions and variables are cached in both internal and external X form for performance. Thus a variable which is never "dereferenced" X with a $ is passed on to rc's children untouched. This is not so X important for variables, but is a big win for functions, where a call X to yyparse() is involved. X*/ X X#include "rc.h" X#include "utils.h" X#include "hash.h" X#include "list.h" X#include "tree.h" X#include "footobar.h" X Xstatic boolean exportable(char *); Xstatic int hash(char *, int); Xstatic int find(char *, Htab *, int); Xstatic void free_fn(Function *); X XHtab *fp; XHtab *vp; Xstatic int fused, fsize, vused, vsize; Xstatic char **env; Xstatic int bozosize; Xstatic int envsize; Xstatic boolean env_dirty = TRUE; Xstatic char *dead = ""; X X#define HASHSIZE 64 /* rc was debugged with HASHSIZE == 2; 64 is about right for normal use */ X Xvoid inithash(void) { X int i; X Htab *fpp, *vpp; X X fp = ealloc(sizeof(Htab) * HASHSIZE); X vp = ealloc(sizeof(Htab) * HASHSIZE); X fused = vused = 0; X fsize = vsize = HASHSIZE; X X for (vpp = vp, fpp = fp, i = 0; i < HASHSIZE; i++, vpp++, fpp++) X vpp->name = fpp->name = NULL; X} X X#define ADV() {if ((c = *s++) == '\0') break;} X X/* hash function courtesy of paul haahr */ X Xstatic int hash(char *s, int size) { X int n = 0; X int c; X X while (1) { X ADV(); X n += (c << 17) ^ (c << 11) ^ (c << 5) ^ (c >> 1); X ADV(); X n ^= (c << 14) + (c << 7) + (c << 4) + c; X ADV(); X n ^= (~c << 11) | ((c << 3) ^ (c >> 1)); X ADV(); X n -= (c << 16) | (c << 9) | (c << 2) | (c & 3); X } X X if (n < 0) X n = ~n; X X return n & (size - 1); /* need power of 2 size */ X} X Xstatic boolean rehash(Htab *ht) { X int i,j,size; X int newsize,newused; X Htab *newhtab; X X if (ht == fp) { X if (fsize > 2 * fused) X return FALSE; X size = fsize; X } else { X if (vsize > 2 * vused) X return FALSE; X size = vsize; X } X X X newsize = 2 * size; X newhtab = ealloc(newsize * sizeof(Htab)); X for (i = 0; i < newsize; i++) X newhtab[i].name = NULL; X X for (i = newused = 0; i < size; i++) X if (ht[i].name != NULL && ht[i].name != dead) { X newused++; X j = hash(ht[i].name, newsize); X while (newhtab[j].name != NULL) { X j++; X j &= (newsize - 1); X } X newhtab[j].name = ht[i].name; X newhtab[j].p = ht[i].p; X } X X if (ht == fp) { X fused = newused; X fp = newhtab; X fsize = newsize; X } else { X vused = newused; X vp = newhtab; X vsize = newsize; X } X X efree(ht); X return TRUE; X} X X#define varfind(s) find(s,vp,vsize) X#define fnfind(s) find(s,fp,fsize) X Xstatic int find(char *s, Htab *ht, int size) { X int h = hash(s, size); X X while (ht[h].name != NULL && !streq(ht[h].name,s)) { X h++; X h &= size - 1; X } X X return h; X} X Xvoid *lookup(char *s, Htab *ht) { X int h = find(s, ht, ht == fp ? fsize : vsize); X X return (ht[h].name == NULL) ? NULL : ht[h].p; X} X XFunction *get_fn_place(char *s) { X int h = fnfind(s); X X env_dirty = TRUE; X X if (fp[h].name == NULL) { X if (rehash(fp)) X h = fnfind(s); X fused++; X fp[h].name = ecpy(s); X fp[h].p = enew(Function); X } else X free_fn(fp[h].p); X X return fp[h].p; X} X XVariable *get_var_place(char *s, boolean stack) { X Variable *new; X int h = varfind(s); X X env_dirty = TRUE; X X if (vp[h].name == NULL) { X if (rehash(vp)) X h = varfind(s); X vused++; X vp[h].name = ecpy(s); X strcpy(vp[h].name, s); X vp[h].p = enew(Variable); X ((Variable *)vp[h].p)->n = NULL; X return vp[h].p; X } else { X if (stack) { /* increase the stack by 1 */ X new = enew(Variable); X new->n = vp[h].p; X return vp[h].p = new; X } else { /* trample the top of the stack */ X new = vp[h].p; X efree(new->extdef); X listfree(new->def); X return new; X } X } X} X Xvoid delete_fn(char *s) { X int h = fnfind(s); X X if (fp[h].name == NULL) X return; /* not found */ X X env_dirty = TRUE; X X free_fn(fp[h].p); X efree(fp[h].p); X efree(fp[h].name); X if (fp[h+1].name == NULL) { X --fused; X fp[h].name = NULL; X } else { X fp[h].name = dead; X } X} X Xvoid delete_var(char *s, boolean stack) { X int h = varfind(s); X Variable *v; X X if (vp[h].name == NULL) X return; /* not found */ X X env_dirty = TRUE; X X v = vp[h].p; X efree(v->extdef); X listfree(v->def); X X if (v->n != NULL) { /* This is the top of a stack */ X if (stack) { /* pop */ X vp[h].p = v->n; X efree(v); X } else { /* else just empty */ X v->extdef = NULL; X v->def = NULL; X } X } else { /* needs to be removed from the hash table */ X efree(v); X efree(vp[h].name); X if (vp[h+1].name == NULL) { X --vused; X vp[h].name = NULL; X } else { X vp[h].name = dead; X } X } X} X Xstatic void free_fn(Function *f) { X treefree(f->def); X efree(f->extdef); X} X Xvoid initenv(char **envp) { X int n; X X for (n = 0; envp[n] != NULL; n++) X ; X n++; /* one for the null terminator */ X X if (n < HASHSIZE) X n = HASHSIZE; X X env = ealloc((envsize = 2 * n) * sizeof (char *)); X X for (; *envp != NULL; envp++) X if (strncmp(*envp, "fn_", sizeof("fn_") - 1) == 0) X fnassign_string(*envp); X else X if (!varassign_string(*envp)) /* add to bozo env */ X env[bozosize++] = *envp; X} X Xstatic boolean exportable(char *s) { X int i; X static char *notforexport[] = { X "sighup", "sigint", "sigquit", "sigterm", "sigexit", X "apid", "pid", "apid", "*", "ifs" X }; X X for (i = 0; i < arraysize(notforexport); i++) X if (streq(s,notforexport[i])) X return FALSE; X return TRUE; X} X Xchar **makeenv(void) { X int ep, i; X char *v; X X if (!env_dirty) X return env; X X env_dirty = FALSE; X ep = bozosize; X X if (vsize + fsize + 1 + bozosize > envsize) { X envsize = 2 * (bozosize + vsize + fsize + 1); X env = erealloc(env, envsize * sizeof(char *)); X } X X for (i = 0; i < vsize; i++) { X if (vp[i].name == NULL || !exportable(vp[i].name)) X continue; X v = varlookup_string(vp[i].name); X if (v != NULL) X env[ep++] = v; X } X for (i = 0; i < fsize; i++) { X if (fp[i].name == NULL || !exportable(fp[i].name)) X continue; X env[ep++] = fnlookup_string(fp[i].name); X } X env[ep] = NULL; X return env; X} X Xvoid whatare_all_vars(void) { X int i; X List *s,*t; X X for (i = 0; i < vsize; i++) X if (vp[i].name != NULL && (s = varlookup(vp[i].name)) != NULL) { X fprint(1,"%s=%s", vp[i].name, (s->n == NULL ? "" : "(")); X for (t = s; t->n != NULL; t = t->n) X fprint(1,"%s ",strprint(t->w, FALSE, TRUE)); X fprint(1,"%s%s\n",strprint(t->w, FALSE, TRUE), (s->n == NULL ? "" : ")")); X } X for (i = 0; i < fsize; i++) X if (fp[i].name != NULL) X fprint(1,"fn %s {%s}\n", fp[i].name, ptree(fnlookup(fp[i].name))); X} X X/* fake getenv() for readline() follows: */ X X#ifdef READLINE Xchar *getenv(char *name) { X List *s; X X if (name == NULL || (s = varlookup(name)) == NULL) X return NULL; X X return s->w; X} X#endif X END_OF_FILE if test 6588 -ne `wc -c <'hash.c'`; then echo shar: \"'hash.c'\" unpacked with wrong size! fi # end of 'hash.c' fi if test -f 'heredoc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'heredoc.c'\" else echo shar: Extracting \"'heredoc.c'\" \(3252 characters\) sed "s/^X//" >'heredoc.c' <<'END_OF_FILE' X/* heredoc.c: heredoc slurping is done here */ X X#include "rc.h" X#include "lex.h" X#include "utils.h" X#include "nalloc.h" X#include "heredoc.h" X#include "tree.h" X#include "input.h" X#include "hash.h" X#include "glom.h" X Xstruct Hq { X Node *doc; X char *name; X Hq *n; X} *hq; X Xstatic SIZE_T j, bufsize; X X/* a stupid realloc for nalloc */ X Xstatic char *renalloc(char *buf, SIZE_T n) { X X while (n >= bufsize - 2) X bufsize *= 2; X X return memcpy(nalloc(bufsize), buf, j); X} X X/* X read in a heredocument. A clever trick: skip over any partially matched end-of-file X marker storing only the number of characters matched. If the whole marker is matched, X return from readheredoc(). If only part of the marker is matched, copy that part into X the heredocument. X*/ X Xstatic char *readheredoc(char *eof) { X int c; X SIZE_T i, markerlen, varsize = 0; X char *buf, *varname = NULL, *newvarname; X boolean quoted = FALSE; X List *var; X X if (*eof == '\'') { X quoted = TRUE; X eof++; X } X X markerlen = strlen(eof); X X buf = nalloc(bufsize = 512); X j = 0; X X while (1) { X print_prompt2(); X X for (i = 0, c = gchar(); eof[i] == c && i < markerlen; i++) /* match the eof marker */ X c = gchar(); X X if (i == markerlen && c == '\n') { /* if the whole marker was matched, return */ X buf[j] = 0; X return buf; X } X X if (i > 0) { /* else copy the partially matched marker into the heredocument */ X if (i + j >= bufsize - 2) X buf = renalloc(buf, i + j); X memcpy(buf + j, eof, i); X j += i; X } X X for (; c != '\n'; c = gchar()) { X if (c == EOF) X scanerror("EOF inside heredoc"); X if (j >= bufsize - 2) X buf = renalloc(buf, j); X if (quoted || c != '$') { X buf[j++] = c; X continue; /* avoid an extra level of indentation */ X } X if ((c = gchar()) == '$' || dnw[c]) { X if (c != '$') /* $$ quotes $, but $<nonalnum> is transcribed literally */ X buf[j++] = '$'; X buf[j++] = c; X } else { X if (varsize == 0) X varname = nalloc(varsize = 16); X for (i = 0; !dnw[c]; i++, c = gchar()) { X if (i >= varsize - 1) { X newvarname = nalloc(varsize *= 4); X memcpy(newvarname, varname, i); X varname = newvarname; X } X varname[i] = c; X } X varname[i] = '\0'; X if (c != '^') X ugchar(c); X var = varlookup(varname); X if (varname[0] == '*' && varname[1] == '\0') /* skip $0 */ X var = var->n; X if ((var = flatten(var)) != NULL) { X i = strlen(var->w); X while (j + i >= bufsize - 2) X buf = renalloc(buf, i + j); X memcpy(buf + j, var->w, i); X j += i; X } X } X } X buf[j++] = c; X } X} X X/* read in heredocs when yyparse hits a newline. called from yyparse */ X Xvoid heredoc(int end) { X if (end && hq != NULL) X rc_error("EOF on command line with heredoc"); X X for (; hq != NULL; hq = hq->n) { X hq->doc->u[0].i = HERESTRING; X hq->doc->u[2].p = newnode(rWORD,readheredoc(hq->name), NULL); X } X} X X/* queue pending heredocs into a queue. called from yyparse */ X Xvoid qdoc(Node *name, Node *n) { X Hq *new; X X if (name->type != rWORD) X scanerror("eof-marker must be a single word"); X X if (hq == NULL) { X new = hq = nnew(Hq); X } else { X for (new = hq; new->n != NULL; new = new->n) X ; X new->n = nnew(Hq); X new = new->n; X } X X new->name = name->u[0].s; X new->doc = n; X new->n = NULL; X} END_OF_FILE if test 3252 -ne `wc -c <'heredoc.c'`; then echo shar: \"'heredoc.c'\" unpacked with wrong size! fi # end of 'heredoc.c' fi if test -f 'parse.y' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'parse.y'\" else echo shar: Extracting \"'parse.y'\" \(5173 characters\) sed "s/^X//" >'parse.y' <<'END_OF_FILE' X/* parse.y */ X X/* X * Adapted from rc grammar, v10 manuals, volume 2. X */ X X%{ X#include "stddef.h" X#include "node.h" X#include "lex.h" X#include "tree.h" X#include "heredoc.h" X#include "parse.h" X#include "utils.h" X#undef NULL X#define NULL 0 X#undef lint X#define lint /* hush up gcc -Wall, leave out the dumb sccsid's. */ Xstatic Node *star, *nolist; XNode *parsetree; /* not using yylval because bison declares it as an auto */ X%} X X%term BANG DUP ELSE END FN FOR HUH IF IN LBRACK PIPE RBRACK X%term REDIR STAR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD X X%left IF WHILE FOR SWITCH ')' ELSE X%left ANDAND OROR X%left BANG SUBSHELL X%left PIPE X%left '^' X%right '$' COUNT FLAT X%left SUB X%left '`' BACKBACK X X%union { X struct Node *node; X struct Redir redir; X struct Pipe pipe; X struct Dup dup; X struct Word word; X char *keyword; X} X X%type <redir> REDIR X%type <pipe> PIPE X%type <dup> DUP X%type <word> WORD X%type <keyword> keyword X%type <node> assign body brace cmd cmdsa cmdsan comword epilog X first line paren redir simple iftail word words X X%start rc X X%% X Xrc : line end { parsetree = $1; return 1; } X | error end { yyerrok; parsetree = NULL; return -1; } X X/* an rc line may end in end-of-file as well as newline, e.g., rc -c 'ls' */ Xend : END /* EOF */ { heredoc(1); } /* flag error if there is a heredoc in the queue */ X | '\n' { heredoc(0); } /* get heredoc on \n */ X X/* a cmdsa is a command followed by ampersand or newline (used in "line" and "body") */ Xcmdsa : cmd ';' X | cmd '&' { $$ = ($1 != NULL ? newnode(NOWAIT,$1) : $1); } X X/* a line is a single command, or a command terminated by ; or & followed by a line (recursive) */ Xline : cmd X | cmdsa line { $$ = ($1 != NULL ? newnode(BODY,$1,$2) : $2); } X X/* a body is like a line, only commands may also be terminated by newline */ Xbody : cmd X | cmdsan body { $$ = ($1 != NULL ? newnode(BODY,$1,$2) : $2); } X Xcmdsan : cmdsa X | cmd '\n' { $$ = $1; heredoc(0); } /* get h.d. on \n */ X Xbrace : '{' body '}' { $$ = $2; } X Xparen : '(' body ')' { $$ = $2; } X Xassign : first '=' word { $$ = newnode(ASSIGN,$1,$3); } X Xepilog : { $$ = NULL; } X | redir epilog { $$ = newnode(EPILOG,$1,$2); } X X/* a redirection is a dup (e.g., >[1=2]) or a file redirection. (e.g., > /dev/null) */ Xredir : DUP { $$ = newnode(rDUP,$1.type,$1.left,$1.right); } X | REDIR word { $$ = newnode(rREDIR,$1.type,$1.fd,$2); X if ($1.type == HEREDOC) qdoc($2, $$); /* queue heredocs up */ X } X X/* the tail of an if statement can be a command, or a braced command followed by else on the same line */ Xiftail : cmd %prec IF X | brace ELSE cmd { $$ = newnode(rELSE,$1,$3); } X X/* skipnl() skips newlines and comments between an operator and its operand */ Xcmd : { $$ = NULL; } X | simple X | brace epilog { $$ = ($2 == NULL ? $1 : newnode(BRACE,$1,$2)); } X | IF paren { skipnl(); } iftail { $$ = newnode(rIF,$2,$4); } X | FOR '(' word IN words ')' { skipnl(); } cmd { $$ = newnode(FORIN,$3,$5,$8); } X | FOR '(' word ')' { skipnl(); } cmd { $$ = newnode(FORIN,$3,star,$6); } X | WHILE paren { skipnl(); } cmd { $$ = newnode(rWHILE,$2,$4); } X | SWITCH '(' word ')' { skipnl(); } brace { $$ = newnode(rSWITCH,$3,$6); } X | TWIDDLE word words { $$ = newnode(MATCH,$2,$3); } X | cmd ANDAND { skipnl(); } cmd { $$ = ($1 != NULL ? newnode(rANDAND,$1,$4) : $4); } X | cmd OROR { skipnl(); } cmd { $$ = ($1 != NULL ? newnode(rOROR,$1,$4) : $4); } X | cmd PIPE { skipnl(); } cmd { $$ = newnode(rPIPE,$2.left,$2.right,$1,$4); } X | redir cmd %prec BANG { $$ = ($2 != NULL ? newnode(PRE,$1,$2) : $1); } X | assign cmd %prec BANG { $$ = ($2 != NULL ? newnode(PRE,$1,$2) : $1); } X | BANG cmd { $$ = newnode(rBANG,$2); } X | SUBSHELL cmd { $$ = newnode(rSUBSHELL,$2); } X | FN words brace { $$ = newnode(NEWFN,$2,$3); } X | FN words { $$ = newnode(RMFN,$2); } X Xsimple : first X | simple word { $$ = ($2 != NULL ? newnode(ARGS,$1,$2) : $1); } X | simple redir { $$ = newnode(ARGS,$1,$2); } X Xfirst : comword X | first '^' word { $$ = newnode(CONCAT,$1,$3); } X Xword : comword X | keyword { $$ = newnode(rWORD,$1, NULL); } X | word '^' word { $$ = newnode(CONCAT,$1,$3); } X Xcomword : '$' word { $$ = newnode(VAR,$2); } X | '$' word SUB words ')' { $$ = newnode(VARSUB,$2,$4); } X | COUNT word { $$ = newnode(rCOUNT,$2); } X | FLAT word { $$ = newnode(rFLAT, $2); } X | WORD { $$ = newnode(rWORD,$1.w, $1.m); } X | '`' word { $$ = newnode(BACKQ,nolist,$2); } X | '`' brace { $$ = newnode(BACKQ,nolist,$2); } X | BACKBACK word brace { $$ = newnode(BACKQ,$2,$3); } X | BACKBACK word word { $$ = newnode(BACKQ,$2,$3); } X | '(' words ')' { $$ = $2; } X | REDIR brace { $$ = newnode(NMPIPE,$1.type,$1.fd,$2); } X Xkeyword : FOR { $$ = "for"; } X | IN { $$ = "in"; } X | WHILE { $$ = "while"; } X | IF { $$ = "if"; } X | SWITCH { $$ = "switch"; } X | FN { $$ = "fn"; } X | ELSE { $$ = "else"; } X | TWIDDLE { $$ = "~"; } X | BANG { $$ = "!"; } X | SUBSHELL { $$ = "@"; } X Xwords : { $$ = NULL; } X | words word { $$ = ($1 != NULL ? ($2 != NULL ? newnode(LAPPEND,$1,$2) : $1) : $2); } X X%% X Xvoid initparse() { X star = treecpy(newnode(VAR,newnode(rWORD,"*",NULL)), ealloc); X nolist = treecpy(newnode(VAR,newnode(rWORD,"ifs",NULL)), ealloc); X} END_OF_FILE if test 5173 -ne `wc -c <'parse.y'`; then echo shar: \"'parse.y'\" unpacked with wrong size! fi # end of 'parse.y' fi if test -f 'redir.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'redir.c'\" else echo shar: Extracting \"'redir.c'\" \(2480 characters\) sed "s/^X//" >'redir.c' <<'END_OF_FILE' X/* redir.c: code for opening files and piping heredocs after fork but before exec. */ X X#include "rc.h" X#include "lex.h" X#include "glom.h" X#include "glob.h" X#include "open.h" X#include "exec.h" X#include "utils.h" X#include "redir.h" X#include "hash.h" X X/* X Walk the redirection queue, and open files and dup2 to them. Also, here-documents are treated X here by dumping them down a pipe. (this should make here-documents fast on systems with lots X of memory which do pipes right. Under sh, a file is copied to /tmp, and then read out of /tmp X again. I'm interested in knowing how much faster, say, shar runs when unpacking when invoked X with rc instead of sh. On my sun4/280, it runs in about 60-75% of the time of sh for unpacking X the rc source distribution.) X*/ X Xvoid doredirs() { X List *fname; X int fd, p[2]; X Rq *r; X X for (r = redirq; r != NULL; r = r->n) { X switch(r->r->type) { X default: X fprint(2,"%d: bad node in doredirs\n", r->r->type); X exit(1); X /* NOTREACHED */ X case rREDIR: X if (r->r->u[0].i == HERESTRING) { X fname = flatten(glom(r->r->u[2].p)); /* fname is really a string */ X if (fname == NULL) { X close(r->r->u[1].i); /* feature? */ X break; X } X if (pipe(p) < 0) { X uerror("pipe"); X exit(1); X } X switch (fork()) { X case -1: X uerror("fork"); X exit(1); X /* NOTREACHED */ X case 0: /* child writes to pipe */ X setsigdefaults(); X close(p[0]); X writeall(p[1], fname->w, strlen(fname->w)); X exit(0); X /* NOTREACHED */ X default: X close(p[1]); X if (dup2(p[0], r->r->u[1].i) < 0) { X uerror("dup"); X exit(1); X } X close(p[0]); X } X } else { X fname = glob(glom(r->r->u[2].p)); X if (fname == NULL) { X fprint(2,"null filename in redirection\n"); X exit(1); X } X if (fname->n != NULL) { X fprint(2,"multi-word filename in redirection\n"); X exit(1); X } X switch (r->r->u[0].i) { X default: X fprint(2,"doredirs: this can't happen\n"); X exit(1); X /* NOTREACHED */ X case CREATE: case APPEND: case FROM: X fd = rc_open(fname->w, r->r->u[0].i); X break; X } X if (fd < 0) { X uerror(fname->w); X exit(1); X } X if (dup2(fd, r->r->u[1].i) < 0) { X uerror("dup"); X exit(1); X } X close(fd); X } X break; X case rDUP: X if (r->r->u[2].i == -1) X close(r->r->u[1].i); X else if (dup2(r->r->u[2].i, r->r->u[1].i) < 0) { X uerror("dup"); X exit(1); X } X } X } X redirq = NULL; X} END_OF_FILE if test 2480 -ne `wc -c <'redir.c'`; then echo shar: \"'redir.c'\" unpacked with wrong size! fi # end of 'redir.c' fi if test -f 'tree.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tree.c'\" else echo shar: Extracting \"'tree.c'\" \(4613 characters\) sed "s/^X//" >'tree.c' <<'END_OF_FILE' X/* tree.c: functions for manipulating parse-trees. (create, copy, delete) */ X X#include <stdarg.h> X#include "rc.h" X#include "tree.h" X#include "utils.h" X#include "nalloc.h" X X/* make a new node, pass it back to yyparse. Used to generate the parsetree. */ X XNode *newnode(int /*enum nodetype*/ t,...) { X va_list ap; X Node *n; X X va_start(ap,t); X X switch (t) { X default: X fprint(2,"newnode: this can't happen\n"); X exit(1); X /* NOTREACHED */ X case rDUP: X n = nalloc(offsetof(Node, u[3])); X n->u[0].i = va_arg(ap, int); X n->u[1].i = va_arg(ap, int); X n->u[2].i = va_arg(ap, int); X break; X case rWORD: X n = nalloc(offsetof(Node, u[2])); X n->u[0].s = va_arg(ap, char *); X n->u[1].s = va_arg(ap, char *); X break; X case rBANG: case NOWAIT: X case rCOUNT: case rFLAT: case RMFN: case rSUBSHELL: X case VAR: X n = nalloc(offsetof(Node, u[1])); X n->u[0].p = va_arg(ap, Node *); X break; X case rANDAND: case ASSIGN: case BACKQ: case BODY: case BRACE: case CONCAT: X case rELSE: case EPILOG: case rIF: case NEWFN: X case rOROR: case PRE: case ARGS: case rSWITCH: X case MATCH: case VARSUB: case rWHILE: case LAPPEND: X n = nalloc(offsetof(Node, u[2])); X n->u[0].p = va_arg(ap, Node *); X n->u[1].p = va_arg(ap, Node *); X break; X case FORIN: X n = nalloc(offsetof(Node, u[3])); X n->u[0].p = va_arg(ap, Node *); X n->u[1].p = va_arg(ap, Node *); X n->u[2].p = va_arg(ap, Node *); X break; X case rPIPE: X n = nalloc(offsetof(Node, u[4])); X n->u[0].i = va_arg(ap, int); X n->u[1].i = va_arg(ap, int); X n->u[2].p = va_arg(ap, Node *); X n->u[3].p = va_arg(ap, Node *); X break; X case rREDIR: X case NMPIPE: X n = nalloc(offsetof(Node, u[3])); X n->u[0].i = va_arg(ap, int); X n->u[1].i = va_arg(ap, int); X n->u[2].p = va_arg(ap, Node *); X break; X } X n->type = t; X va_end(ap); X return n; X} X X/* copy a tree to malloc space. Used when storing the definition of a function */ X XNode *treecpy(Node *s, void (*alloc(SIZE_T))) { X Node *n; X X if (s == NULL) X return NULL; X X switch (s->type) { X default: X fprint(2,"treecpy: this can't happen\n"); X exit(1); X /* NOTREACHED */ X case rDUP: X n = alloc(offsetof(Node, u[3])); X n->u[0].i = s->u[0].i; X n->u[1].i = s->u[1].i; X n->u[2].i = s->u[2].i; X break; X case rWORD: X n = alloc(offsetof(Node, u[2])); X n->u[0].s = ecpy(s->u[0].s); X if (s->u[1].s != NULL) { X SIZE_T i = strlen(s->u[0].s); X X n->u[1].s = alloc(i); X memcpy(n->u[1].s, s->u[1].s, i); X } else X n->u[1].s = NULL; X break; X case rBANG: case NOWAIT: X case rCOUNT: case rFLAT: case RMFN: case rSUBSHELL: case VAR: X n = alloc(offsetof(Node, u[1])); X n->u[0].p = treecpy(s->u[0].p, alloc); X break; X case rANDAND: case ASSIGN: case BACKQ: case BODY: case BRACE: case CONCAT: X case rELSE: case EPILOG: case rIF: case NEWFN: X case rOROR: case PRE: case ARGS: case rSWITCH: X case MATCH: case VARSUB: case rWHILE: case LAPPEND: X n = alloc(offsetof(Node, u[2])); X n->u[0].p = treecpy(s->u[0].p, alloc); X n->u[1].p = treecpy(s->u[1].p, alloc); X break; X case FORIN: X n = alloc(offsetof(Node, u[3])); X n->u[0].p = treecpy(s->u[0].p, alloc); X n->u[1].p = treecpy(s->u[1].p, alloc); X n->u[2].p = treecpy(s->u[2].p, alloc); X break; X case rPIPE: X n = alloc(offsetof(Node, u[4])); X n->u[0].i = s->u[0].i; X n->u[1].i = s->u[1].i; X n->u[2].p = treecpy(s->u[2].p, alloc); X n->u[3].p = treecpy(s->u[3].p, alloc); X break; X case rREDIR: X case NMPIPE: X n = alloc(offsetof(Node, u[3])); X n->u[0].i = s->u[0].i; X n->u[1].i = s->u[1].i; X n->u[2].p = treecpy(s->u[2].p, alloc); X break; X } X n->type = s->type; X return n; X} X X/* free a function definition that is no longer needed */ X Xvoid treefree(Node *s) { X if (s == NULL) X return; X switch (s->type) { X case rDUP: X break; X case rWORD: X efree(s->u[0].s); X efree(s->u[1].s); X break; X case rBANG: case NOWAIT: X case rCOUNT: case rFLAT: case RMFN: X case rSUBSHELL: case VAR: X treefree(s->u[0].p); X efree(s->u[0].p); X break; X case rANDAND: case ASSIGN: case BACKQ: case BODY: case BRACE: case CONCAT: X case rELSE: case EPILOG: case rIF: case NEWFN: X case rOROR: case PRE: case ARGS: X case rSWITCH: case MATCH: case VARSUB: case rWHILE: X case LAPPEND: X treefree(s->u[1].p); X treefree(s->u[0].p); X efree(s->u[1].p); X efree(s->u[0].p); X break; X case FORIN: X treefree(s->u[2].p); X treefree(s->u[1].p); X treefree(s->u[0].p); X efree(s->u[2].p); X efree(s->u[1].p); X efree(s->u[0].p); X break; X case rPIPE: X treefree(s->u[2].p); X treefree(s->u[3].p); X efree(s->u[2].p); X efree(s->u[3].p); X break; X case rREDIR: X case NMPIPE: X treefree(s->u[2].p); X efree(s->u[2].p); X break; X default: X fprint(2,"treefree: this can't happen\n"); X exit(1); X } X} END_OF_FILE if test 4613 -ne `wc -c <'tree.c'`; then echo shar: \"'tree.c'\" unpacked with wrong size! fi # end of 'tree.c' fi if test -f 'utils.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'utils.c'\" else echo shar: Extracting \"'utils.c'\" \(5186 characters\) sed "s/^X//" >'utils.c' <<'END_OF_FILE' X/* utils.c: general utility functions like fprint, ealloc etc. */ X X#include <stdarg.h> X#include <errno.h> X#include <setjmp.h> X#include <signal.h> X#include "rc.h" X#include "utils.h" X#include "nalloc.h" X#include "status.h" X#include "input.h" X#include "except.h" X#include "lex.h" /* import char nw[]; used by strprint to see if it needs to quote a word */ X#include "walk.h" X Xstatic void dprint(va_list, char *, char *); Xstatic int n2u(char *, int); X X/* exception handlers */ X Xvoid rc_error(char *s) { X if (s != NULL) { X if (interactive) X fprint(2,"%s\n",s); X else X fprint(2,"line %d: %s\n", lineno - 1, s); X } X set(FALSE); X redirq = NULL; X cond = FALSE; /* no longer inside conditional */ X empty_fifoq(); X rc_raise(ERROR); X} X Xvoid sig(int s) { X signal(SIGINT, sig); /* some unices require re-signaling */ X X if (errno == EINTR) X return; /* allow wait() to complete */ X X fprint(2,"\n"); /* this is the newline you see when you hit ^C while typing a command */ X redirq = NULL; X cond = FALSE; X empty_fifoq(); X rc_raise(ERROR); X} X X/* our perror */ X Xvoid uerror(char *s) { X extern int sys_nerr; X extern char *sys_errlist[]; X X if (errno > sys_nerr) X return; X X if (s != NULL) X fprint(2,"%s: %s\n",s,sys_errlist[errno]); X else X fprint(2,"%s\n",sys_errlist[errno]); X} X X/* printing functions */ X Xvoid fprint(int fd, char *f,...) { X va_list ap; X char str[FPRINT_SIZE]; X X va_start(ap,f); X dprint(ap, str, f); X va_end(ap); X writeall(fd,str,strlen(str)); X} X Xchar *sprint(char *b, char *f,...) { X va_list ap; X X va_start(ap, f); X dprint(ap, b, f); X va_end(ap); X return b; X} X Xstatic void dprint(va_list ap, char *strbuf, char *f) { X int i; X X for (i = 0; *f != '\0'; f++) { X if (*f != '%') { X strbuf[i++] = *f; X continue; /* avoid an ugly extra level of indentation */ X } X switch (*++f) { X case 'a': { X char **a = va_arg(ap, char **); X X if (*a == NULL) X break; X strcpy(strbuf + i, *a); X i += strlen(*a); X while (*++a != NULL) { X strbuf[i++] = ' '; X strcpy(strbuf + i, *a); X i += strlen(*a); X } X break; X } X case 'c': X strbuf[i++] = va_arg(ap, int); X break; X case 'd': case 'o': { X int v = va_arg(ap, int); X int j = 0; X int base = (*f == 'd' ? 10 : 8); X char num[16]; X X if (v == 0) X num[j++] = '0'; X while (v != 0) { X num[j++] = (v % base) + '0'; X v /= base; X } X while (--j >= 0) X strbuf[i++] = num[j]; X break; X } X case 's': { X char *s = va_arg(ap, char *); X while (*s != '\0') X strbuf[i++] = *s++; X break; X } X default: /* on format error, just print the bad format */ X strbuf[i++] = '%'; X /* FALLTHROUGH */ X case '%': X strbuf[i++] = *f; X } X } X strbuf[i] = '\0'; X} X X/* prints a string in rc-quoted form. e.g., a string with spaces in it must be quoted */ X Xchar *strprint(char *s, int quotable, int metaquote) { /* really boolean, but y.tab.c includes utils.h */ X SIZE_T i,j; X char *t; X X if (*s == '\0') X return "''"; X X for (i = 0; s[i] != '\0'; i++) X if (nw[s[i]] == 1 && (metaquote || (s[i] != '*' && s[i] != '?' && s[i] != '['))) X quotable = TRUE; X X if (!quotable) X return s; X X for(i = j = 0; s[i] != '\0'; i++, j++) X if (s[i] == '\'') X j++; X X t = nalloc(j + 3); X X t[0] = '\''; X X for (j = 1, i = 0; s[i] != '\0'; i++, j++) { X t[j] = s[i]; X if (s[i] == '\'') X t[++j] = '\''; X } X X t[j++] = '\''; X t[j] = '\0'; X X return t; X} X X/* ascii -> unsigned conversion routines. -1 indicates conversion error. */ X Xstatic int n2u(char *s, int base) { X int i; X X for (i = 0; *s != '\0'; s++) { X /* small hack with unsigned ints -- one compare for range test */ X if (((unsigned int) *s) - '0' >= (unsigned int) base) X return -1; X i = (i * base) + (*s - '0'); X } X return i; X} X X/* decimal -> uint */ X Xint a2u(char *s) { X return n2u(s, 10); X} X X/* octal -> uint */ X Xint o2u(char *s) { X return n2u(s, 8); X} X X/* memory allocation functions */ X Xvoid *ealloc(SIZE_T n) { X char *p = malloc(n); X X if (p == NULL) { X uerror("malloc"); X rc_exit(1); X } X X return p; X} X Xvoid *erealloc(void *p, SIZE_T n) { X p = realloc(p, n); X X if (p == NULL) { X uerror("realloc"); X rc_exit(1); X } X X return p; X} X Xvoid efree(void *p) { X if (p != NULL) X free(p); X} X X/* useful functions */ X X/* The last word in portable ANSI: a strcmp wrapper for qsort */ X Xint starstrcmp(const void *s1, const void *s2) { X return strcmp(*(char **)s1, *(char **)s2); X} X X/* tests to see if pathname begins with "/", "./", or "../" */ X Xint isabsolute(char *path) { X return path[0] == '/' || (path[0] == '.' && (path[1] == '/' || (path[1] == '.' && path[2] == '/'))); X} X X/* write a given buffer allowing for partial writes from write(2) */ X Xvoid writeall(int fd, char *buf, SIZE_T remain) { X int i; X X for (i = 0; remain > 0; buf += i, remain -= i) X i = write(fd, buf, remain); X} X X/* clear out z bytes from character string s */ X Xvoid clear(char *s, SIZE_T z) { X while (z != 0) X s[--z] = 0; X} X X/* zero out the fifo queue, removing the fifos from /tmp as you go (also prints errors arising from signals) */ X Xvoid empty_fifoq() { X int sp; X X while (fifoq != NULL) { X unlink(fifoq->w); X wait(&sp); X statprint(sp); X fifoq = fifoq->n; X } X} X XSIZE_T strarraylen(char **a) { X SIZE_T i; X X for (i = 0; *a != NULL; a++) X i += strlen(*a) + 1; X X return i; X} END_OF_FILE if test 5186 -ne `wc -c <'utils.c'`; then echo shar: \"'utils.c'\" unpacked with wrong size! fi # end of 'utils.c' fi if test -f 'var.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'var.c'\" else echo shar: Extracting \"'var.c'\" \(5802 characters\) sed "s/^X//" >'var.c' <<'END_OF_FILE' X/* var.c: provide "public" functions for adding and removing variables from the symbol table */ X X#include "rc.h" X#include "utils.h" X#include "hash.h" X#include "list.h" X#include "footobar.h" X#include "nalloc.h" X#include "status.h" X#include "glom.h" X X#ifdef READLINE /* need to reset readline() every time TERM or TERMCAP changes */ Xextern void rl_reset_terminal(char *); X#endif X Xstatic void colonassign(char *, List *, boolean); Xstatic void listassign(char *, List *, boolean); Xstatic int hasalias(char *); X Xstatic char *const aliases[] = { X "home", "HOME", "path", "PATH", "cdpath", "CDPATH" X}; X X/* assign a variable in List form to a name, stacking if appropriate */ X Xvoid varassign(char *name, List *def, boolean stack) { X Variable *new; X List *newdef = listcpy(def); /* important to do the listcpy first; get_var_place() frees old values */ X X new = get_var_place(name, stack); X new->def = newdef; X new->extdef = NULL; X X#ifdef READLINE /* need to reset readline() every time TERM or TERMCAP changes */ X if (interactive && streq(name, "TERM") || streq(name, "TERMCAP")) X rl_reset_terminal(NULL); X#endif X} X X/* assign a variable in string form. Check to see if it is aliased (e.g., PATH and path) */ X Xboolean varassign_string(char *extdef) { X char *name = get_name(extdef); X Variable *new; X int i; X static boolean aliasset[arraysize(aliases)] = { X FALSE, FALSE, FALSE, FALSE, FALSE, FALSE X }; X X if (name == NULL) X return FALSE; /* add it to bozo env */ X X i = hasalias(name); X if (i >= 0) { X aliasset[i] = TRUE; X if ((i & 1 == 0) && aliasset[i^1]) X return TRUE; /* don't alias variables that are already set in upper case */ X } X new = get_var_place(name, FALSE); X new->def = NULL; X new->extdef = ealloc(strlen(extdef) + 1); X strcpy(new->extdef, extdef); X if (hasalias(name) != -1) X alias(name, varlookup(name), FALSE); X return TRUE; X} X X/* X Return a List based on a name lookup. If the list is in external (string) form, X convert it to internal (List) form. Treat $n (n is an integer) specially as $*(n). X Also check to see if $status is being dereferenced. (we lazily evaluate the List X associated with $status) X*/ X XList *varlookup(char *name) { X Variable *look; X List *ret, *l; X int sub; X X if (streq(name, "status")) X return sgetstatus(); X X if (*name != '\0' && (sub = a2u(name)) != -1) { /* handle $1, $2, etc. */ X for (l = varlookup("*"); l != NULL && sub != 0; --sub) X l = l->n; X if (l == NULL) X return NULL; X ret = nnew(List); X ret->w = l->w; X ret->m = NULL; X ret->n = NULL; X return ret; X } X X look = lookup_var(name); X X if (look == NULL) X return NULL; /* not found */ X if (look->def != NULL) X return look->def; X if (look->extdef == NULL) X return NULL; /* variable was set to null, e.g., a=() echo foo */ X X ret = parse_var(name, look->extdef); X X if (ret == NULL) { X look->extdef = NULL; X return NULL; X } X return look->def = ret; X} X X/* lookup a variable in external (string) form, converting if necessary. Used by makeenv() */ X Xchar *varlookup_string(char *name) { X Variable *look; X X look = lookup_var(name); X X if (look == NULL) X return NULL; X if (look->extdef != NULL) X return look->extdef; X if (look->def == NULL) X return NULL; X return look->extdef = list2str(name, look->def); X} X X/* remove a variable from the symtab. "stack" determines whether a level of scoping is popped or not */ X Xvoid varrm(char *name, boolean stack) { X int i = hasalias(name); X X if (streq(name, "*") && !stack) { /* when assigning () to $*, we want to preserve $0 */ X varassign("*", varlookup("0"), FALSE); X return; X } X X delete_var(name, stack); X if (i != -1) X delete_var(aliases[i^1], stack); X} X X/* assign a value (List) to a variable, using array "a" as input. Used to assign $* */ X Xvoid starassign(char *dollarzero, char **a, boolean stack) { X List *s, *var; X X var = nnew(List); X var->w = dollarzero; X X if (*a == NULL) { X var->n = NULL; X varassign("*", var, stack); X return; X } X X var->n = s = nnew(List); X X while (1) { X s->w = *a++; X if (*a == NULL) { X s->n = NULL; X break; X } else { X s->n = nnew(List); X s = s->n; X } X } X varassign("*", var, stack); X} X X/* (ugly name, huh?) assign a colon-separated value to a variable (e.g., PATH) from a List (e.g., path) */ X Xstatic void colonassign(char *name, List *def, boolean stack) { X char *colondef; X List dud; X SIZE_T deflen; X List *r; X X if (def == NULL) { X varassign(name, NULL, stack); X return; X } X X deflen = listlen(def) + 1; /* one for the null terminator */ X X colondef = nalloc(deflen); X strcpy(colondef, def->w); X X for (r = def->n; r != NULL; r = r->n) { X strcat(colondef, ":"); X strcat(colondef, r->w); X } X X dud.w = colondef; X dud.n = NULL; X varassign(name, &dud, stack); X} X X/* assign a List variable (e.g., path) from a colon-separated string (e.g., PATH) */ X Xstatic void listassign(char *name, List *def, boolean stack) { X List *val, *r; X char *v, *w; X X if (def == NULL) { X varassign(name, NULL, stack); X return; X } X X v = def->w; X X r = val = enew(List); X X while((w = strchr(v,':')) != NULL) { X *w = '\0'; X r->w = ecpy(v); X *w = ':'; X v = w + 1; X r->n = enew(List); X r = r->n; X } X r->w = ecpy(v); X r->n = NULL; X X varassign(name, val, stack); X} X X/* check to see if a particular variable is aliased; return -1 on failure, or the index */ X Xstatic int hasalias(char *name) { X int i; X X for (i = 0; i < arraysize(aliases); i++) X if (streq(name, aliases[i])) X return i; X return -1; X} X X/* alias a variable to its lowercase equivalent. function pointers are used to specify the conversion function */ X Xvoid alias(char *name, List *s, boolean stack) { X int i = hasalias(name); X static void (*vectors[])(char *, List *, boolean) = { X varassign, varassign, colonassign, listassign, colonassign, listassign X }; X X if (i != -1) X vectors[i](aliases[i^1], s, stack); /* xor hack to reverse case of alias entry */ X} END_OF_FILE if test 5802 -ne `wc -c <'var.c'`; then echo shar: \"'var.c'\" unpacked with wrong size! fi # end of 'var.c' fi if test -f 'which.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'which.c'\" else echo shar: Extracting \"'which.c'\" \(2968 characters\) sed "s/^X//" >'which.c' <<'END_OF_FILE' X/* which.c: check to see if a file is executable. X X This function is isolated from the rest because of #include trouble with X ANSI compilers. X X This function was originally written with Maarten Litmaath's which.c as X a template, but was changed in order to accomodate the possibility of X rc's running setuid or the possibility of executing files not in the X primary group. Much of this file has been re-vamped by Paul Haahr. X I re-re-vamped the functions that Paul supplied to correct minor bugs X and to strip out unneeded functionality. X*/ X X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/param.h> X#include "builtins.h" X X#define M_USR 0700 X#define M_GRP 0070 X#define M_OTH 0007 X#define X_ALL 0111 X X#ifndef NULL X#define NULL 0 X#endif X Xtypedef struct List { X char *w; X char *m; X struct List *n; X} List; X Xextern int stat(const char *, struct stat *); Xextern int geteuid(void); Xextern int getegid(void); Xextern int getgroups(int, int *); Xextern char *strcpy(char *, char *); Xextern char *strcat(char *, char *); Xextern int strlen(char *); X Xextern List *varlookup(char *); Xextern void *ealloc(int); Xextern void efree(void *); Xextern int isabsolute(char *); X Xstatic int initialized = 0; Xstatic int uid, gid; X X#ifdef NGROUPS Xstatic int ngroups, gidset[NGROUPS]; X X/* determine whether gid lies in gidset */ X Xstatic int ingidset(int gid) { X int i; X for (i = 0; i < ngroups; i++) X if (gid == gidset[i]) X return 1; X return 0; X} X#endif X X/* X A home-grown access/stat. Does the right thing for group-executable files. X Returns a boolean result instead of this -1 nonsense. X*/ X Xstatic int rc_access(char *path) { X struct stat st; X int mask; X X if (stat(path, &st) != 0) X return 0; X X mask = X_ALL; X X if (uid != 0) { X if (uid == st.st_uid) X mask &= M_USR; X#ifdef NGROUPS X else if (gid == st.st_gid || ingidset(st.st_gid)) X#else X else if (gid == st.st_gid) X#endif X mask &= M_GRP; X else X mask &= M_OTH; X } X X return (st.st_mode & S_IFMT) == S_IFREG && (st.st_mode & mask) == mask; X} X X/* return a full pathname by searching $path, and by checking the status of the file */ X Xchar *which(char *name) { X static char *test = NULL; X static int testlen = 0; X List *path; X int len; X X if (name == NULL) /* no filename? can happen with "> foo" as a command */ X return NULL; X X if (!initialized) { X initialized = 1; X uid = geteuid(); X gid = getegid(); X#ifdef NGROUPS X ngroups = getgroups(NGROUPS, gidset); X#endif X } X X if (isabsolute(name)) /* absolute pathname? */ X return rc_access(name) ? name : NULL; X X len = strlen(name); X for (path = varlookup("path"); path != NULL; path = path->n) { X int need = strlen(path->w) + len + 2; /* one for null terminator, one for the '/' */ X if (testlen < need) { X efree(test); X test = ealloc(testlen = need); X } X if (*path->w == '\0') { X strcpy(test, name); X } else { X strcpy(test, path->w); X strcat(test, "/"); X strcat(test, name); X } X if (rc_access(test)) X return test; X } X return NULL; X} END_OF_FILE if test 2968 -ne `wc -c <'which.c'`; then echo shar: \"'which.c'\" unpacked with wrong size! fi # end of 'which.c' fi echo shar: End of archive 3 \(of 4\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.