v20i012: rc - A Plan 9 shell reimplementation, Part03/04
Byron Rakitzis
byron at archone.tamu.edu
Thu May 23 01:43:05 AEST 1991
Submitted-by: Byron Rakitzis <byron at 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 at 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 at 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 at 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 at uunet.uu.net.
More information about the Comp.sources.misc
mailing list