commit 7ea25f9756a5b39d0594a40a159f55862bb4f5c7
parent a8462ca047b5eb8eb5a32ace445c3dee31c2a8f0
Author: tim <tim>
Date: Mon, 2 Jun 1997 09:26:58 +0000
Initial revision
Diffstat:
A | config.h-dist | | | 204 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | hash.c | | | 304 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jbwrap.h | | | 10 | ++++++++++ |
A | lex.c | | | 395 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | var.c | | | 225 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | version.c.in | | | 1 | + |
6 files changed, 1139 insertions(+), 0 deletions(-)
diff --git a/config.h-dist b/config.h-dist
@@ -0,0 +1,204 @@
+/* Copy config.h-dist to config.h and edit config.h, don't edit this file */
+
+/*
+ * Configuration parameters for rc. Suggested defaults are at the bottom
+ * of this file (you should probably look at those first to see if your
+ * system matches one of them; you can search for the beginning of the
+ * defaults section by looking for the string "#ifndef CUSTOM"). If you
+ * want to override the suggested defaults, define the macro CUSTOM.
+#define CUSTOM
+ */
+
+/*
+ * (Note that certain default settings redefine this macro)
+ * DEFAULTPATH the default search path that rc uses when it is started
+ * without either a $PATH or $path environment variable. You must pick
+ * something sensible for your system if you don't like the path shown
+ * below.
+ */
+#define DEFAULTPATH "/usr/ucb", "/usr/bin", "/bin", "."
+
+/*
+ * Define the macro NODIRENT if your system has <sys/dir.h> but not
+ * <dirent.h>. (e.g., NeXT-OS and RISCos)
+#define NODIRENT
+ */
+
+/*
+ * Define the macro SVSIGS if your system has System V signal semantics,
+ * i.e., if "slow" system calls are interrupted rather than resumed
+ * after returning from an interrupt handler. (If you are not sure what
+ * this means, see the man page for signal(2). In any case, it is probably
+ * safe to leave this macro undefined.)
+#define SVSIGS
+ */
+
+/*
+ * Define the macro NOCMDARG if you do not have /dev/fd or fifos on your
+ * system. You may also want to define this if you have broken fifos.
+#define NOCMDARG
+ */
+
+/*
+ * Define TMPDIR if you need to have rc create its fifos in a directory
+ * other than /tmp. For example, if you have a Sun with /tmp mounted
+ * as a ramdisk (type "tmpfs") then you cannot use fifos in /tmp (sigh).
+#define TMPDIR "/var/tmp"
+ */
+
+/*
+ * Define the macro DEVFD if your system supports /dev/fd.
+#define DEVFD
+ */
+
+/*
+ * Define the macro NOLIMITS if your system does not support Berkeley
+ * limits.
+#define NOLIMITS
+ */
+
+/*
+ * Define the macro NOSIGCLD if your system uses SIGCLD in the System
+ * V way. (e.g., sgi's Irix)
+#define NOSIGCLD
+ */
+
+/*
+ * Define the macro READLINE if you want rc to call GNU readline
+ * instead of read(2) on interactive shells.
+#define READLINE
+ */
+
+/*
+ * Define the macro NOEXECVE if your Unix does not interpret #! in the
+ * kernel, and uncomment the EXECVE variable in the Makefile.
+#define NOEXECVE
+ */
+
+/*
+ * If you want rc to default to some interpreter for files which don't
+ * have a legal #! on the first line, define the macro DEFAULTINTERP.
+#define DEFAULTINTERP "/bin/sh"
+ */
+
+/*
+ * If your /bin/sh (or another program you care about) rejects environment
+ * variables with special characters in them (such as ':' or '-'), rc can
+ * put out ugly variable names using [_0-9a-zA-Z] that encode the real name;
+ * define PROTECT_ENV for this hack. (Known offenders: every sh I have tried;
+ * SunOS (silently discards), NeXT (aborts with error), SGI (aborts with
+ * error), Ultrix (sh seems to work, sh5 aborts with error))
+#define PROTECT_ENV
+ */
+
+/*
+ * Define the macro NOECHO if you wish to omit rc's echo builtin from the
+ * compile.
+#define NOECHO
+ */
+
+/*
+ * Define the NOJOB if you do *not* wish rc to perform backgrounding
+ * as if it were a job-control shell; that is, if you do *not* wish
+ * it to put a command spawned in the background into a new process
+ * group. Since most systems support job control and since there are
+ * many broken programs that do not behave correctly when backgrounded
+ * in a v7 non-job-control fashion, rc by default performs a job-
+ * control-like backgrounding.
+#define NOJOB
+ */
+
+/* Beginning of defaults section: */
+
+#ifndef CUSTOM
+
+/*
+ * Suggested settings for Sun, NeXT and sgi (machines here at TAMU):
+ */
+
+#ifdef NeXT /* Used on NextOS 2.1 */
+#define NODIRENT
+#define PROTECT_ENV
+#define NOCMDARG
+#endif
+
+#ifdef sgi /* Used on Irix 3.3.[12] */
+#define SVSIGS
+#define NOSIGCLD
+#define PROTECT_ENV
+#undef DEFAULTPATH
+#define DEFAULTPATH "/usr/bsd", "/usr/sbin", "/usr/bin", "/bin", "."
+#endif
+
+#ifdef sun /* Used on SunOS 4.1.1 */
+#define PROTECT_ENV
+#undef DEFAULTPATH
+#define DEFAULTPATH "/usr/ucb", "/usr/bin", "."
+#endif
+
+/*
+ * Suggested settings for HP300 running 4.3BSD-utah (DWS):
+ */
+
+#if defined(hp300) && !defined(hpux)
+#define NODIRENT
+#define NOCMDARG
+#define DEFAULTINTERP "/bin/sh"
+#define PROTECT_ENV
+#endif
+
+/*
+ * Suggested settings for Ultrix
+ */
+
+#ifdef ultrix
+#define PROTECT_ENV
+#define DEFAULTINTERP "/bin/sh" /* so /bin/true can work */
+#endif
+
+/*
+ * Suggested settings for RISCos 4.52
+ */
+
+/*
+ This doesn't work without interfering with other MIPS-based
+ systems' configuration. Please do it by hand.
+*/
+
+#if defined(host_mips) && defined(MIPSEB) && defined(SYSTYPE_BSD43)
+#define NODIRENT
+#define PROTECT_ENV
+#endif
+
+/*
+ * Suggested settings for AIX
+ */
+
+#ifdef _AIX
+#define PROTECT_ENV
+#endif
+
+/*
+ * Suggested settings for OSF/1 1.0
+ */
+
+#ifdef OSF1
+#define PROTECT_ENV
+#endif
+
+/*
+ * Suggested settings for Unicos XXX
+ */
+
+#ifdef cray
+#define PROTECT_ENV
+#define NOLIMITS
+#define word _word
+#define DEFAULTINTERP "/bin/sh"
+#endif
+
+#endif /* CUSTOM */
+
+#ifndef TMPDIR
+#define TMPDIR "/tmp"
+#endif
diff --git a/hash.c b/hash.c
@@ -0,0 +1,304 @@
+/* hash.c: hash table support for functions and variables. */
+
+/*
+ Functions and variables are cached in both internal and external
+ form for performance. Thus a variable which is never "dereferenced"
+ with a $ is passed on to rc's children untouched. This is not so
+ important for variables, but is a big win for functions, where a call
+ to yyparse() is involved.
+*/
+
+#include "rc.h"
+#include "sigmsgs.h"
+
+static bool var_exportable(char *);
+static bool fn_exportable(char *);
+static int hash(char *, int);
+static int find(char *, Htab *, int);
+static void free_fn(Function *);
+
+Htab *fp;
+Htab *vp;
+static int fused, fsize, vused, vsize;
+static char **env;
+static int bozosize;
+static int envsize;
+static bool env_dirty = TRUE;
+static char *dead = "";
+
+#define HASHSIZE 64 /* rc was debugged with HASHSIZE == 2; 64 is about right for normal use */
+
+extern void inithash() {
+ Htab *fpp, *vpp;
+ int i;
+ fp = ealloc(sizeof(Htab) * HASHSIZE);
+ vp = ealloc(sizeof(Htab) * HASHSIZE);
+ fused = vused = 0;
+ fsize = vsize = HASHSIZE;
+ for (vpp = vp, fpp = fp, i = 0; i < HASHSIZE; i++, vpp++, fpp++)
+ vpp->name = fpp->name = NULL;
+}
+
+#define ADV() {if ((c = *s++) == '\0') break;}
+
+/* hash function courtesy of paul haahr */
+
+static int hash(char *s, int size) {
+ int c, n = 0;
+ while (1) {
+ ADV();
+ n += (c << 17) ^ (c << 11) ^ (c << 5) ^ (c >> 1);
+ ADV();
+ n ^= (c << 14) + (c << 7) + (c << 4) + c;
+ ADV();
+ n ^= (~c << 11) | ((c << 3) ^ (c >> 1));
+ ADV();
+ n -= (c << 16) | (c << 9) | (c << 2) | (c & 3);
+ }
+ if (n < 0)
+ n = ~n;
+ return n & (size - 1); /* need power of 2 size */
+}
+
+static bool rehash(Htab *ht) {
+ int i, j, size;
+ int newsize, newused;
+ Htab *newhtab;
+ if (ht == fp) {
+ if (fsize > 2 * fused)
+ return FALSE;
+ size = fsize;
+ } else {
+ if (vsize > 2 * vused)
+ return FALSE;
+ size = vsize;
+ }
+ newsize = 2 * size;
+ newhtab = ealloc(newsize * sizeof(Htab));
+ for (i = 0; i < newsize; i++)
+ newhtab[i].name = NULL;
+ for (i = newused = 0; i < size; i++)
+ if (ht[i].name != NULL && ht[i].name != dead) {
+ newused++;
+ j = hash(ht[i].name, newsize);
+ while (newhtab[j].name != NULL) {
+ j++;
+ j &= (newsize - 1);
+ }
+ newhtab[j].name = ht[i].name;
+ newhtab[j].p = ht[i].p;
+ }
+ if (ht == fp) {
+ fused = newused;
+ fp = newhtab;
+ fsize = newsize;
+ } else {
+ vused = newused;
+ vp = newhtab;
+ vsize = newsize;
+ }
+ efree(ht);
+ return TRUE;
+}
+
+#define varfind(s) find(s, vp, vsize)
+#define fnfind(s) find(s, fp, fsize)
+
+static int find(char *s, Htab *ht, int size) {
+ int h = hash(s, size);
+ while (ht[h].name != NULL && !streq(ht[h].name, s)) {
+ h++;
+ h &= size - 1;
+ }
+ return h;
+}
+
+extern void *lookup(char *s, Htab *ht) {
+ int h = find(s, ht, ht == fp ? fsize : vsize);
+ return (ht[h].name == NULL) ? NULL : ht[h].p;
+}
+
+extern Function *get_fn_place(char *s) {
+ int h = fnfind(s);
+ env_dirty = TRUE;
+ if (fp[h].name == NULL) {
+ if (rehash(fp))
+ h = fnfind(s);
+ fused++;
+ fp[h].name = ecpy(s);
+ fp[h].p = enew(Function);
+ } else
+ free_fn(fp[h].p);
+ return fp[h].p;
+}
+
+extern Variable *get_var_place(char *s, bool stack) {
+ Variable *new;
+ int h = varfind(s);
+
+ env_dirty = TRUE;
+
+ if (vp[h].name == NULL) {
+ if (rehash(vp))
+ h = varfind(s);
+ vused++;
+ vp[h].name = ecpy(s);
+ vp[h].p = enew(Variable);
+ ((Variable *)vp[h].p)->n = NULL;
+ return vp[h].p;
+ } else {
+ if (stack) { /* increase the stack by 1 */
+ new = enew(Variable);
+ new->n = vp[h].p;
+ return vp[h].p = new;
+ } else { /* trample the top of the stack */
+ new = vp[h].p;
+ efree(new->extdef);
+ listfree(new->def);
+ return new;
+ }
+ }
+}
+
+extern void delete_fn(char *s) {
+ int h = fnfind(s);
+ if (fp[h].name == NULL)
+ return; /* not found */
+ env_dirty = TRUE;
+ free_fn(fp[h].p);
+ efree(fp[h].p);
+ efree(fp[h].name);
+ if (fp[(h+1)&(fsize-1)].name == NULL) {
+ --fused;
+ fp[h].name = NULL;
+ } else {
+ fp[h].name = dead;
+ }
+}
+
+extern void delete_var(char *s, bool stack) {
+ int h = varfind(s);
+ Variable *v;
+ if (vp[h].name == NULL)
+ return; /* not found */
+ env_dirty = TRUE;
+ v = vp[h].p;
+ efree(v->extdef);
+ listfree(v->def);
+ if (v->n != NULL) { /* This is the top of a stack */
+ if (stack) { /* pop */
+ vp[h].p = v->n;
+ efree(v);
+ } else { /* else just empty */
+ v->extdef = NULL;
+ v->def = NULL;
+ }
+ } else { /* needs to be removed from the hash table */
+ efree(v);
+ efree(vp[h].name);
+ if (vp[(h+1)&(vsize-1)].name == NULL) {
+ --vused;
+ vp[h].name = NULL;
+ } else {
+ vp[h].name = dead;
+ }
+ }
+}
+
+static void free_fn(Function *f) {
+ treefree(f->def);
+ efree(f->extdef);
+}
+
+extern void initenv(char **envp) {
+ int n;
+ for (n = 0; envp[n] != NULL; n++)
+ ;
+ n++; /* one for the null terminator */
+ if (n < HASHSIZE)
+ n = HASHSIZE;
+ env = ealloc((envsize = 2 * n) * sizeof (char *));
+ for (; *envp != NULL; envp++)
+ if (strncmp(*envp, "fn_", conststrlen("fn_")) == 0) {
+ if (!dashpee)
+ fnassign_string(*envp);
+ } else {
+ if (!varassign_string(*envp)) /* add to bozo env */
+ env[bozosize++] = *envp;
+ }
+}
+
+static bool var_exportable(char *s) {
+ static char *notforexport[] = {
+ "apid", "pid", "apids", "*", "ifs"
+ };
+ int i;
+ for (i = 0; i < arraysize(notforexport); i++)
+ if (streq(s, notforexport[i]))
+ return FALSE;
+ return TRUE;
+}
+
+static bool fn_exportable(char *s) {
+ int i;
+ if (strncmp(s, "sig", conststrlen("sig")) == 0) { /* small speed hack */
+ for (i = 0; i < NUMOFSIGNALS; i++)
+ if (streq(s, signals[i].name))
+ return FALSE;
+ if (streq(s, "sigexit"))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+extern char **makeenv() {
+ int ep, i;
+ char *v;
+ if (!env_dirty)
+ return env;
+ env_dirty = FALSE;
+ ep = bozosize;
+ if (vsize + fsize + 1 + bozosize > envsize) {
+ envsize = 2 * (bozosize + vsize + fsize + 1);
+ env = erealloc(env, envsize * sizeof(char *));
+ }
+ for (i = 0; i < vsize; i++) {
+ if (vp[i].name == NULL || vp[i].name == dead || !var_exportable(vp[i].name))
+ continue;
+ v = varlookup_string(vp[i].name);
+ if (v != NULL)
+ env[ep++] = v;
+ }
+ for (i = 0; i < fsize; i++) {
+ if (fp[i].name == NULL || fp[i].name == dead || !fn_exportable(fp[i].name))
+ continue;
+ env[ep++] = fnlookup_string(fp[i].name);
+ }
+ env[ep] = NULL;
+ qsort(env, (SIZE_T) ep, sizeof(char *), starstrcmp);
+ return env;
+}
+
+extern void whatare_all_vars(bool showfn, bool showvar) {
+ int i;
+ List *s;
+ if (showvar)
+ for (i = 0; i < vsize; i++)
+ if (vp[i].name != NULL && (s = varlookup(vp[i].name)) != NULL)
+ prettyprint_var(1, vp[i].name, s);
+ if (showfn)
+ for (i = 0; i < fsize; i++)
+ if (fp[i].name != NULL && fp[i].name != dead)
+ prettyprint_fn(1, fp[i].name, fnlookup(fp[i].name));
+}
+
+/* fake getenv() for readline() follows: */
+
+#ifdef READLINE
+extern char *getenv(const char *name) {
+ List *s;
+ if (name == NULL || vp == NULL || (s = varlookup((char *) name)) == NULL)
+ return NULL;
+ return s->w;
+}
+#endif
diff --git a/jbwrap.h b/jbwrap.h
@@ -0,0 +1,10 @@
+/* certain braindamaged environments don't define jmp_buf as an array, so... */
+
+struct Jbwrap {
+ jmp_buf j;
+};
+
+extern Jbwrap slowbuf; /* for getting out of interrupts while performing slow i/o on BSD */
+
+extern int setjmp(jmp_buf);
+extern void longjmp(jmp_buf, int);
diff --git a/lex.c b/lex.c
@@ -0,0 +1,395 @@
+/* lex.c: rc's lexical analyzer */
+
+#include "rc.h"
+#include "y.tab.h"
+
+/*
+ Special characters (i.e., "non-word") in rc:
+ \t \n # ; & | ^ $ = ~ ` ' { } @ ! ( ) < > \
+
+ The lexical analyzer is fairly straightforward. The only really
+ unclean part concerns backslash continuation and "double
+ backslashes". A backslash followed by a newline is treated as a
+ space, otherwise backslash is not a special character (i.e.,
+ it can be part of a word). This introduces a host of unwanted
+ special cases. In our case, \ cannot be a word character, since
+ we wish to read in all word characters in a tight loop.
+
+ Note: to save the trouble of declaring these arrays with TRUEs
+ and FALSEs, I am assuming that FALSE = 0, TRUE = 1. (and so is
+ it declared in rc.h)
+*/
+
+#define BUFSIZE ((SIZE_T) 1000) /* malloc hates power of 2 buffers? */
+#define BUFMAX (8 * BUFSIZE) /* How big the buffer can get before we re-allocate the
+ space at BUFSIZE again. Premature optimization? Maybe.
+ */
+
+typedef enum wordstates {
+ NW, RW, KW /* "nonword", "realword", "keyword" */
+} wordstates;
+
+static void getpair(int);
+
+int lineno;
+
+const char nw[] = {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+const char dnw[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+static SIZE_T bufsize = BUFSIZE;
+static char *realbuf = NULL;
+static bool newline = FALSE;
+static bool errset = FALSE;
+static bool prerror = FALSE;
+static wordstates w = NW;
+static int fd_left, fd_right;
+
+#define checkfreecaret {if (w != NW) { w = NW; ugchar(c); return '^'; }}
+
+enum filedescriptors {
+ UNSET = -9, CLOSED = -1
+};
+
+extern int yylex() {
+ static bool dollar = FALSE;
+ bool saw_meta = FALSE;
+ int c;
+ SIZE_T i; /* The purpose of all these local assignments is to */
+ const char *meta; /* allow optimizing compilers like gcc to load these */
+ char *buf = realbuf; /* values into registers. On a sparc this is a */
+ YYSTYPE *y = &yylval; /* win, in code size *and* execution time */
+ if (errset) {
+ errset = FALSE;
+ return '\n';
+ }
+ /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */
+ meta = (dollar ? dnw : nw);
+ dollar = FALSE;
+ if (newline) {
+ --lineno; /* slight space optimization; print_prompt2() always increments lineno */
+ print_prompt2();
+ newline = FALSE;
+ }
+top: while ((c = gchar()) == ' ' || c == '\t')
+ w = NW;
+ if (c == EOF)
+ return END;
+ if (!meta[(unsigned char) c]) { /* it's a word or keyword. */
+ checkfreecaret;
+ w = RW;
+ i = 0;
+ read: do {
+ buf[i++] = c;
+ if (c == '?' || c == '[' || c == '*')
+ saw_meta = TRUE;
+ if (i >= bufsize)
+ buf = realbuf = erealloc(buf, bufsize *= 2);
+ } while ((c = gchar()) != EOF && !meta[(unsigned char) c]);
+ while (c == '\\') {
+ if ((c = gchar()) == '\n') {
+ print_prompt2();
+ c = ' '; /* Pretend a space was read */
+ break;
+ } else {
+ bs: if (meta != dnw) { /* all words but varnames may have a bslash */
+ buf[i++] = '\\';
+ if (i >= bufsize)
+ buf = realbuf = erealloc(buf, bufsize *= 2);
+ if (!meta[(unsigned char) c])
+ goto read;
+ } else {
+ ugchar(c);
+ c = '\\';
+ break;
+ }
+ }
+ }
+ ugchar(c);
+ buf[i] = '\0';
+ w = KW;
+ if (i == 2) {
+ if (*buf == 'i' && buf[1] == 'f') return IF;
+ if (*buf == 'f' && buf[1] == 'n') return FN;
+ if (*buf == 'i' && buf[1] == 'n') return IN;
+ }
+ if (streq(buf, "for")) return FOR;
+ if (streq(buf, "else")) return ELSE;
+ if (streq(buf, "switch")) return SWITCH;
+ if (streq(buf, "while")) return WHILE;
+ if (streq(buf, "case")) return CASE;
+ w = RW;
+ y->word.w = ncpy(buf);
+ if (saw_meta) {
+ char *r, *s;
+
+ y->word.m = nalloc(strlen(buf) + 1);
+ for (r = buf, s = y->word.m; *r != '\0'; r++, s++)
+ *s = (*r == '?' || *r == '[' || *r == '*');
+ } else {
+ y->word.m = NULL;
+ }
+ return WORD;
+ }
+ if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') {
+ checkfreecaret;
+ if (c == '!' || c == '@' || c == '~')
+ w = KW;
+ }
+ switch (c) {
+ case '!':
+ return BANG;
+ case '@':
+ return SUBSHELL;
+ case '~':
+ return TWIDDLE;
+ case '`':
+ c = gchar();
+ if (c == '`')
+ return BACKBACK;
+ ugchar(c);
+ return '`';
+ case '$':
+ dollar = TRUE;
+ c = gchar();
+ if (c == '#')
+ return COUNT;
+ if (c == '^')
+ return FLAT;
+ ugchar(c);
+ return '$';
+ case '\'':
+ w = RW;
+ i = 0;
+ do {
+ buf[i++] = c;
+ if (c == '\n')
+ print_prompt2();
+ if (c == EOF) {
+ w = NW;
+ scanerror("eof in quoted string");
+ return HUH;
+ }
+ if (i >= bufsize)
+ buf = realbuf = erealloc(buf, bufsize *= 2);
+ } while ((c = gchar()) != '\'' || (c = gchar()) == '\''); /* quote "'" thus: 'how''s it going?' */
+ ugchar(c);
+ buf[i] = '\0';
+ y->word.w = ncpy(buf);
+ y->word.m = NULL;
+ return WORD;
+ case '\\':
+ if ((c = gchar()) == '\n') {
+ print_prompt2();
+ goto top; /* Pretend it was just another space. */
+ }
+ ugchar(c);
+ c = '\\';
+ checkfreecaret;
+ c = gchar();
+ i = 0;
+ goto bs;
+ case '(':
+ if (w == RW) /* SUB's happen only after real words, not keyowrds, so if () and while () work */
+ c = SUB;
+ w = NW;
+ return c;
+ case '#':
+ while ((c = gchar()) != '\n') /* skip comment until newline */
+ if (c == EOF)
+ return END;
+ /* FALLTHROUGH */
+ case '\n':
+ lineno++;
+ newline = TRUE;
+ /* FALLTHROUGH */
+ case ';':
+ case '^':
+ case ')':
+ case '=':
+ case '{': case '}':
+ w = NW;
+ return c;
+ case '&':
+ w = NW;
+ c = gchar();
+ if (c == '&')
+ return ANDAND;
+ ugchar(c);
+ return '&';
+ case '|':
+ w = NW;
+ c = gchar();
+ if (c == '|')
+ return OROR;
+ getpair(c);
+ if (errset)
+ return HUH;
+ if ((y->pipe.left = fd_left) == UNSET)
+ y->pipe.left = 1; /* default to fd 1 */
+ if ((y->pipe.right = fd_right) == UNSET)
+ y->pipe.right = 0; /* default to fd 0 */
+ if (y->pipe.right == CLOSED) {
+ scanerror("expected digit after '='"); /* can't close a pipe */
+ return HUH;
+ }
+ return PIPE;
+ case '>':
+ c = gchar();
+ if (c == '>') {
+ c = gchar();
+ y->redir.type = rAppend;
+ } else
+ y->redir.type = rCreate;
+ y->redir.fd = 1;
+ goto common;
+ case '<':
+ c = gchar();
+ if (c == '<') {
+ c = gchar();
+ if (c == '<') {
+ c = gchar();
+ y->redir.type = rHerestring;
+ } else {
+ y->redir.type = rHeredoc;
+ }
+ } else
+ y->redir.type = rFrom;
+ y->redir.fd = 0;
+ common:
+ w = NW;
+ getpair(c);
+ if (errset)
+ return HUH;
+ if (fd_right == UNSET) { /* redirection, not dup */
+ if (fd_left != UNSET) {
+ y->redir.fd = fd_left;
+ return SREDIR;
+ }
+ return (y->redir.type == rFrom || y->redir.type == rCreate) ? REDIR : SREDIR;
+ } else { /* dup; recast yylval */
+ y->dup.type = y->redir.type;
+ y->dup.left = fd_left;
+ y->dup.right = fd_right;
+ return DUP;
+ }
+ default:
+ w = NW;
+ return c; /* don't know what it is, let yacc barf on it */
+ }
+}
+
+extern void yyerror(const char *s) {
+ char *tok;
+ if (prerror) { /* don't print "syntax error" if there's a more informative scanerror */
+ prerror = FALSE;
+ return;
+ }
+ if (!interactive) {
+ if (w != NW)
+ tok = realbuf;
+ else if (last == EOF)
+ tok = "eof";
+ else if (last == '\n')
+ tok = "end of line";
+ else
+ tok = nprint((last < 32 || last > 126) ? "(decimal %d)" : "'%c'", last);
+ fprint(2, "line %d: %s near %s\n", lineno - (last == '\n'), s, tok);
+ } else
+ fprint(2, "%s\n", s);
+}
+
+extern void scanerror(char *s) {
+ flushu(); /* flush upto newline */
+ yyerror(s);
+ errset = prerror = TRUE;
+}
+
+extern void inityy() {
+ newline = FALSE;
+ w = NW;
+ hq = NULL;
+ /* return memory to the system if the buffer got too large */
+ if (bufsize > BUFMAX && realbuf != NULL) {
+ efree(realbuf);
+ bufsize = BUFSIZE;
+ realbuf = ealloc(bufsize);
+ } else if (realbuf == NULL)
+ realbuf = ealloc(bufsize);
+}
+
+extern void print_prompt2() {
+ lineno++;
+#ifdef READLINE
+ prompt = prompt2;
+#else
+ if (interactive)
+ fprint(2, "%s", prompt2);
+#endif
+}
+
+/*
+ Scan in a pair of integers for redirections like >[2=1]. CLOSED represents a closed file
+ descriptor (i.e., >[2=]) and UNSET represents an undesignated file descriptor (e.g.,
+ >[2] is represented as (2,UNSET).
+
+ This function makes use of unsigned compares to make range tests in one compare operation.
+*/
+
+static void getpair(int c) {
+ int n;
+ fd_left = fd_right = UNSET;
+ if (c != '[') {
+ ugchar(c);
+ return;
+ }
+ if ((unsigned int) (n = gchar() - '0') > 9) {
+ scanerror("expected digit after '['");
+ return;
+ }
+ while ((unsigned int) (c = gchar() - '0') <= 9)
+ n = n * 10 + c;
+ fd_left = n;
+ c += '0';
+ switch (c) {
+ default:
+ scanerror("expected '=' or ']' after digit");
+ return;
+ case ']':
+ return;
+ case '=':
+ if ((unsigned int) (n = gchar() - '0') > 9) {
+ if (n != ']' - '0') {
+ scanerror("expected digit or ']' after '='");
+ return;
+ }
+ fd_right = CLOSED;
+ } else {
+ while ((unsigned int) (c = gchar() - '0') <= 9)
+ n = n * 10 + c;
+ if (c != ']' - '0') {
+ scanerror("expected ']' after digit");
+ return;
+ }
+ fd_right = n;
+ }
+ }
+}
diff --git a/var.c b/var.c
@@ -0,0 +1,225 @@
+/* var.c: provide "public" functions for adding and removing variables from the symbol table */
+
+#include "rc.h"
+
+static void colonassign(char *, List *, bool);
+static void listassign(char *, List *, bool);
+static int hasalias(char *);
+
+static char *const aliases[] = {
+ "home", "HOME", "path", "PATH", "cdpath", "CDPATH"
+};
+
+/* assign a variable in List form to a name, stacking if appropriate */
+
+extern void varassign(char *name, List *def, bool stack) {
+ Variable *new;
+ List *newdef = listcpy(def, ealloc); /* important to do the listcpy first; get_var_place() frees old values */
+ new = get_var_place(name, stack);
+ new->def = newdef;
+ new->extdef = NULL;
+#ifdef READLINE /* need to reset readline() every time TERM or TERMCAP changes */
+ if (interactive && (streq(name, "TERM") || streq(name, "TERMCAP"))) {
+ extern void rl_reset_terminal(char *);
+ rl_reset_terminal(NULL);
+ }
+#endif
+}
+
+/* assign a variable in string form. Check to see if it is aliased (e.g., PATH and path) */
+
+extern bool varassign_string(char *extdef) {
+ static bool aliasset[arraysize(aliases)] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+ char *name = get_name(extdef);
+ Variable *new;
+ int i;
+ if (name == NULL)
+ return FALSE; /* add it to bozo env */
+ if ((i = hasalias(name)) != -1) {
+ aliasset[i] = TRUE;
+ i ^= 1; /* set i to the "opposite" case subscript and */
+ if (i&1 && aliasset[i]) /* don't alias variables that are already set in upper case */
+ return TRUE;
+ }
+ new = get_var_place(name, FALSE);
+ new->def = NULL;
+ new->extdef = ealloc(strlen(extdef) + 1);
+ strcpy(new->extdef, extdef);
+ if (i != -1)
+ alias(name, varlookup(name), FALSE);
+ return TRUE;
+}
+
+/*
+ Return a List based on a name lookup. If the list is in external (string) form,
+ convert it to internal (List) form. Treat $n (n is an integer) specially as $*(n).
+ Also check to see if $status is being dereferenced. (we lazily evaluate the List
+ associated with $status)
+*/
+
+extern List *varlookup(char *name) {
+ Variable *look;
+ List *ret, *l;
+ int sub;
+ if (streq(name, "status"))
+ return sgetstatus();
+ if (streq(name, "apids"))
+ return sgetapids();
+ if (*name != '\0' && (sub = a2u(name)) != -1) { /* handle $1, $2, etc. */
+ for (l = varlookup("*"); l != NULL && sub != 0; --sub)
+ l = l->n;
+ if (l == NULL)
+ return NULL;
+ ret = nnew(List);
+ ret->w = l->w;
+ ret->m = NULL;
+ ret->n = NULL;
+ return ret;
+ }
+ look = lookup_var(name);
+ if (look == NULL)
+ return NULL; /* not found */
+ if (look->def != NULL)
+ return look->def;
+ if (look->extdef == NULL)
+ return NULL; /* variable was set to null, e.g., a=() echo foo */
+ ret = parse_var(name, look->extdef);
+ if (ret == NULL) {
+ look->extdef = NULL;
+ return NULL;
+ }
+ return look->def = ret;
+}
+
+/* lookup a variable in external (string) form, converting if necessary. Used by makeenv() */
+
+extern char *varlookup_string(char *name) {
+ Variable *look;
+ look = lookup_var(name);
+ if (look == NULL)
+ return NULL;
+ if (look->extdef != NULL)
+ return look->extdef;
+ if (look->def == NULL)
+ return NULL;
+ return look->extdef = mprint("%F=%-L", name, look->def, "\001");
+}
+
+/* remove a variable from the symtab. "stack" determines whether a level of scoping is popped or not */
+
+extern void varrm(char *name, bool stack) {
+ int i = hasalias(name);
+ if (streq(name, "*") && !stack) { /* when assigning () to $*, we want to preserve $0 */
+ varassign("*", varlookup("0"), FALSE);
+ return;
+ }
+ delete_var(name, stack);
+ if (i != -1)
+ delete_var(aliases[i^1], stack);
+}
+
+/* assign a value (List) to a variable, using array "a" as input. Used to assign $* */
+
+extern void starassign(char *dollarzero, char **a, bool stack) {
+ List *s, *var;
+ var = nnew(List);
+ var->w = dollarzero;
+ if (*a == NULL) {
+ var->n = NULL;
+ varassign("*", var, stack);
+ return;
+ }
+ var->n = s = nnew(List);
+ while (1) {
+ s->w = *a++;
+ if (*a == NULL) {
+ s->n = NULL;
+ break;
+ } else
+ s = s->n = nnew(List);
+ }
+ varassign("*", var, stack);
+}
+
+/* (ugly name, huh?) assign a colon-separated value to a variable (e.g., PATH) from a List (e.g., path) */
+
+static void colonassign(char *name, List *def, bool stack) {
+ List dud;
+ if (def == NULL) {
+ varassign(name, NULL, stack);
+ return;
+ }
+ dud.w = nprint("%-L", def, ":");
+ dud.n = NULL;
+ varassign(name, &dud, stack);
+}
+
+/* assign a List variable (e.g., path) from a colon-separated string (e.g., PATH) */
+
+static void listassign(char *name, List *def, bool stack) {
+ List *val, *r;
+ char *v, *w;
+ if (def == NULL) {
+ varassign(name, NULL, stack);
+ return;
+ }
+ v = def->w;
+ r = val = nnew(List);
+ while ((w = strchr(v, ':')) != NULL) {
+ *w = '\0';
+ r->w = ncpy(v);
+ *w = ':';
+ v = w + 1;
+ r = r->n = nnew(List);
+ }
+ r->w = ncpy(v);
+ r->n = NULL;
+ varassign(name, val, stack);
+}
+
+/* check to see if a particular variable is aliased; return -1 on failure, or the index */
+
+static int hasalias(char *name) {
+ int i;
+ for (i = 0; i < arraysize(aliases); i++)
+ if (streq(name, aliases[i]))
+ return i;
+ return -1;
+}
+
+/* alias a variable to its lowercase equivalent. function pointers are used to specify the conversion function */
+
+extern void alias(char *name, List *s, bool stack) {
+ static void (*vectors[])(char *, List *, bool) = {
+ varassign, varassign, colonassign, listassign, colonassign, listassign
+ };
+ int i = hasalias(name);
+ if (i != -1)
+ (*vectors[i])(aliases[i^1], s, stack); /* xor hack to reverse case of alias entry */
+}
+
+extern void prettyprint_var(int fd, char *name, List *s) {
+ int i;
+ static const char * const keywords[] = {
+ "if", "in", "fn", "for", "else", "switch", "while", "case"
+ };
+ if (s == NULL) {
+ fprint(fd, "%S=()\n", name);
+ return;
+ }
+ if (streq(name, "*")) {
+ s = s->n;
+ if (s == NULL)
+ return; /* Don't print $0, and if $* is not set, skip it */
+ }
+ for (i = 0; i < arraysize(keywords); i++)
+ if (streq(keywords[i], name)) {
+ fprint(fd, "%#S=", name);
+ goto value;
+ }
+ fprint(fd, "%S=", name);
+value:
+ fprint(fd, s->n == NULL ? "%L\n" : "(%L)\n", s, " ");
+}
diff --git a/version.c.in b/version.c.in
@@ -0,0 +1 @@
+const char id[] = "@(#)rc version 1.5betadev-1, 1/9/94.";