rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit 5556dde4d3ad4e34267320b1411fc090b18a24b0
parent 26bb34cf036027dfa22dfae8d8fc2f6bfd6acb75
Author: Byron Rakitzis <byron@archone.tamu.edu>
Date:   Mon,  7 Mar 1994 00:00:01 +0000

beta: rc-1.5betadev1

Diffstat:
MCHANGES | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MMakefile | 2+-
MREADME | 13++++++++++++-
Mbuiltins.c | 60+++++++++++++++++++++++++++++++++++++++++++-----------------
Mexcept.c | 2+-
Mexec.c | 13++++++++++++-
Mfn.c | 33++++++++++++---------------------
Mfootobar.c | 175+++++++++++++++++++++++++++++++++----------------------------------------------
Mglob.c | 50++++++++++++++++++++++++++++++++++++++++----------
Mglom.c | 20+++++++++++++-------
Mhash.c | 16+++++++++-------
Mhistory/history.c | 12++++++++++--
Minput.c | 83++++++++++++++++++++++++++++++++++---------------------------------------------
Mlex.c | 5+----
Mmain.c | 18++++++++++++------
Mnalloc.c | 2++
Mprint.c | 20+++++++++++++++-----
Mproto.h | 1+
Mrc.1 | 7++++---
Mrc.h | 8+++-----
Msignal.c | 4+---
Mstatus.c | 34+++++++++++++++++++++-------------
Mtrip.rc | 0
Mutils.c | 11+----------
Mvar.c | 11+++++------
Mversion.c | 2+-
Mwait.c | 7++++---
Mwalk.c | 30++++++++++++++++++++++--------
28 files changed, 446 insertions(+), 285 deletions(-)

diff --git a/CHANGES b/CHANGES @@ -169,3 +169,95 @@ on. A memory leak in treefree() was plugged up --- the root node of a function was not getting freed. + +Changes since rc-1.4: + +General changes: + + Some small memory leaks/uninit references revealed by Purify. + + $bqstatus for querying the exit status of a backquote. + + Globbing through unreadable directories. + + More options to whatis. + + History append which always re-opens the file (avoids + inconsistencies over multiple NFS-mounted accesses to + $history). + + Support "rc -s". + +--------- + +Makefile: Added comment referring to README for yacc/malloc problem. + +uiltins.c: Added more options to whatis, protected rlimit prototypes + with #ifndef SYSVR4, renamed SIGCHK to sigchk. + +except.c: Patched nl_on_intr printing to happen only in interactive + shells. + +exec.c: Added comment explaining nl_on_intr variable, renamed SIGCHK + to sigchk. + +fn.c: Replaced by-hand consing of exception stack etc. for signal + handler execution with call to funcall(). Replaced fun2str + call with call on print routines. + +footobar.c: Got rid of memory leak in get_name(), parenthesize count, + flat and var nodes for correctness in unparsing, removed + list2str, made get_name use nalloc space, merge in a + better parse_var from es. + +glob.c: Split out a test so that closedir is called correctly, renamed + SIGCHK to sigchk. + +glom.c: Added bqstatus, renamed SIGCHK to sigchk, removed spurious + setsigdefaults, patched uninit memory reference, rename + "clear" to "memzero", wait for bq subproc to finish on EINTR. + +hash.c: Added options to function/variable print code. + +history/history.c: Added '@' to the list of chars which can precede an + ignored '-'. + +input.c: Got rid of tiny memory leak in closefds, got rid of uninit + memory reference in popinput, moved nul ignored check into + realgchar(), changed history append to always reopen the + history file, replaced SIGCHK with sigchk. Freed memory + returned by call to readline(). + +lex.c: Corrected typo in comment, moved nul character ignore code + to input routines. + +main.c: Added -s flag. + +nalloc.c: Added convenience feature to erealloc. (Allow NULL parameter) + +print.c: Changed use of va_start so broken compilers can compile + print code. + +proto.h: Added fake memset. + +rc.h: Replaced clear() function prototype with a macro call on + memset(), removed SIGCHK, fixed prototypes. + +signal.c: Removed unconditional exit in catcher code, renamed SIGCHK + to sigchk. + +status.c: Rewrite sgetstatus breaking out strstatus so that bqstatus + can be set from glom routines. + +utils.c: Got rid of clear() (use memset from C library), rename SIGCHK + to sigchk. + +var.c: Got rid of memory leak in listassign(), converted list2str() + call to something using the print routines. + +version.c: New version string. + +wait.c: Got rid of memory leak in rc_fork, renamed SIGCHK to sigchk. + +walk.c: Fixed pre-redirection bug, removed spurious setsigdefaults(), + renamed SIGCHK to sigchk. diff --git a/Makefile b/Makefile @@ -21,7 +21,7 @@ CFLAGS= LDFLAGS= # You may substitute "bison -y" for yacc. (You want to choose the one that -# makes a smaller y.tab.c.) +# makes a smaller y.tab.c. Also see the README about Sun's yacc.) YACC=yacc OBJS=$(ADDON) builtins.o except.o exec.o $(EXECVE) fn.o footobar.o getopt.o \ diff --git a/README b/README @@ -1,4 +1,4 @@ -This is release 1.4 of rc. +This is release 1.5 of rc. Read COPYRIGHT for copying information. All files are @@ -63,6 +63,17 @@ to get rc to work in a reasonable fashion on a real (i.e., commercial, non-Labs) UNIX system; a few were changes motivated by concern about some inadequacies in the original design. +YACC + +The yacc that Sun ships with SunOS 4.1.1 calls malloc() to allocate +space for the state stack, and requires a call to YYACCEPT or YYABORT +to free this memory. This means that if rc takes an interrupt while +parsing a command (say, if ^C is typed), yacc will leak away this +memory. The solution is to use a yacc which statically allocates +this array, such as the yacc in the BSD distribution. Berkeley yacc- +generated y.tab.c and y.tab.h are shipped with rc in case you cannot +get hold of Berkeley yacc. + OLD C If you need to convert rc's source into K&R C, you need to run the diff --git a/builtins.c b/builtins.c @@ -12,6 +12,7 @@ #include <errno.h> #include "rc.h" #include "jbwrap.h" +#include "sigmsgs.h" #ifndef NOLIMITS #include <sys/time.h> #include <sys/resource.h> @@ -266,7 +267,7 @@ static void b_wait(char **av) { setstatus(pid, stat); else set(FALSE); - SIGCHK; + sigchk(); } /* @@ -274,46 +275,69 @@ static void b_wait(char **av) { is defined as a variable, function or pathname. */ +#define not(b) ((b)^TRUE) +#define show(b) (not(eff|vee|pee|bee|ess)|(b)) + +static bool issig(char *s) { + int i; + for (i = 0; i < NUMOFSIGNALS; i++) + if (streq(s, signals[i].name)) + return TRUE; + return FALSE; +} + static void b_whatis(char **av) { + bool ess, eff, vee, pee, bee; bool f, found; - bool ess = FALSE; int i, ac, c; List *s; Node *n; char *e; for (rc_optind = ac = 0; av[ac] != NULL; ac++) ; /* count the arguments for getopt */ - while ((c = rc_getopt(ac, av, "s")) == 's') - ess = TRUE; - if (c != -1) { - set(FALSE); - return; - } + ess = eff = vee = pee = bee = FALSE; + while ((c = rc_getopt(ac, av, "sfvpb")) != -1) + switch (c) { + default: set(FALSE); return; + case 's': ess = TRUE; break; + case 'f': eff = TRUE; break; + case 'v': vee = TRUE; break; + case 'p': pee = TRUE; break; + case 'b': bee = TRUE; break; + } av += rc_optind; - if (*av == NULL && !ess) { - whatare_all_vars(); + if (*av == NULL) { + if (vee|eff) + whatare_all_vars(eff, vee); + if (ess) + whatare_all_signals(); + if (bee) + for (i = 0; i < arraysize(builtins); i++) + fprint(1, "builtin %s\n", builtins[i].name); + if (pee) + fprint(2, "whatis -p: must specify argument\n"); + if (show(FALSE)) /* no options? */ + whatare_all_vars(TRUE, TRUE); set(TRUE); return; } - if (ess) - whatare_all_signals(); found = TRUE; for (i = 0; av[i] != NULL; i++) { f = FALSE; errno = ENOENT; - if ((s = varlookup(av[i])) != NULL) { + if (show(vee) && (s = varlookup(av[i])) != NULL) { f = TRUE; prettyprint_var(1, av[i], s); } - if ((n = fnlookup(av[i])) != NULL) { + if (((show(ess)&&issig(av[i])) || show(eff)) && (n = fnlookup(av[i])) != NULL) { f = TRUE; prettyprint_fn(1, av[i], n); - } else if (isbuiltin(av[i]) != NULL) { + } else if (show(bee) && isbuiltin(av[i]) != NULL) { f = TRUE; fprint(1, "builtin %s\n", av[i]); - } else if ((e = which(av[i], FALSE)) != NULL) { + } else if (show(pee) && (e = which(av[i], FALSE)) != NULL) { f = TRUE; - fprint(1, "%s\n", e); + fprint(1, "%S\n", e); } if (!f) { found = FALSE; @@ -440,8 +464,10 @@ static const Limit limits[] = { { NULL, 0, NULL } }; +#ifndef SYSVR4 extern int getrlimit(int, struct rlimit *); extern int setrlimit(int, struct rlimit *); +#endif static void printlimit(const Limit *limit, bool hard) { struct rlimit rlim; diff --git a/except.c b/except.c @@ -131,7 +131,7 @@ extern void sigint(int s) { if (s != SIGINT) panic("s != SIGINT in sigint catcher"); /* this is the newline you see when you hit ^C while typing a command */ - if (nl_on_intr) + if (interactive && nl_on_intr) fprint(2, "\n"); nl_on_intr = TRUE; redirq = NULL; diff --git a/exec.c b/exec.c @@ -110,9 +110,20 @@ extern void exec(List *s, bool parent) { redirq = NULL; rc_wait4(pid, &stat, TRUE); setstatus(-1, stat); + /* + There is a very good reason for having this weird + nl_on_intr variable: when rc and its child both + process a SIGINT, (i.e., the child has a SIGINT + catcher installed) then you don't want rc to print + a newline when the child finally exits. Here's an + example: ed, <type ^C>, <type "q">. rc does not + and should not print a newline before the next + prompt, even though there's a SIGINT in its signal + vector. + */ if ((stat & 0xff) == 0) nl_on_intr = FALSE; - SIGCHK; + sigchk(); nl_on_intr = TRUE; pop_cmdarg(TRUE); } diff --git a/fn.c b/fn.c @@ -12,9 +12,9 @@ static void fn_handler(int), dud_handler(int); static bool runexit = FALSE; static Node *handlers[NUMOFSIGNALS], null; -static void (*def_sigint)(int) = SIG_DFL, - (*def_sigquit)(int) = SIG_DFL, - (*def_sigterm)(int) = SIG_DFL; +static void (*def_sigint)(int) = SIG_DFL; +static void (*def_sigquit)(int) = SIG_DFL; +static void (*def_sigterm)(int) = SIG_DFL; /* Set signals to default values for rc. This means that interactive @@ -106,13 +106,12 @@ extern void setsigdefaults(bool sysvbackground) { /* rc's exit. if runexit is set, run the sigexit function. */ extern void rc_exit(int stat) { - static char *sigexit[2] = { - "sigexit", - NULL - }; if (runexit) { + char *sig[2]; + sig[0] = "sigexit"; + sig[1] = NULL; runexit = FALSE; - funcall(sigexit); + funcall(sig); stat = getstatus(); } exit(stat); @@ -121,22 +120,14 @@ extern void rc_exit(int stat) { /* The signal handler for all functions. calls walk() */ static void fn_handler(int s) { - List *dollarzero; - Estack e; - Edata star; + char *sig[2]; int olderrno; if (s < 1 || s >= NUMOFSIGNALS) panic("unknown signal"); olderrno = errno; - dollarzero = nnew(List); - dollarzero->w = signals[s].name; - dollarzero->n = NULL; - varassign("*", dollarzero, TRUE); - star.name = "*"; - except(eVarstack, star, &e); - walk(handlers[s], TRUE); - varrm("*", TRUE); - unexcept(); /* eVarstack */ + sig[0] = signals[s].name; + sig[1] = NULL; + funcall(sig); errno = olderrno; } @@ -217,7 +208,7 @@ extern char *fnlookup_string(char *name) { return NULL; if (look->extdef != NULL) return look->extdef; - return look->extdef = fun2str(name, look->def); + return look->extdef = mprint("fn_%F={%T}", name, look->def); } /* diff --git a/footobar.c b/footobar.c @@ -5,15 +5,12 @@ #include "rc.h" -#define FSCHAR '\1' -#define FSSTRING "\1" - -static char *getenvw(char *, bool); +/* protect an exported name from brain-dead shells */ #ifdef PROTECT_ENV -static bool Fconv(Format *f, int ignore) { /* protect an exported name from brain-dead shells */ - int c; +static bool Fconv(Format *f, int ignore) { unsigned const char *s = va_arg(f->args, unsigned const char *); + int c; while ((c = *s++) != '\0') if (dnw[c] || c == '*' || (c == '_' && *s == '_')) @@ -24,12 +21,6 @@ static bool Fconv(Format *f, int ignore) { /* protect an exported name from brai } #endif -/* used to turn a function in Node * form into something we can export to the environment */ - -extern char *fun2str(char *name, Node *n) { - return mprint("fn_%F={%T}", name, n); -} - /* convert a redirection to a printable form */ static bool Dconv(Format *f, int ignore) { @@ -63,14 +54,11 @@ static bool Tconv(Format *f, int ignore) { switch (n->type) { case nWord: fmtprint(f, "%S", n->u[0].s); break; case nQword: fmtprint(f, "%#S", n->u[0].s); break; - case nBang: fmtprint(f, "! %T", n->u[0].p); break; + case nBang: fmtprint(f, "!%T", n->u[0].p); break; case nCase: fmtprint(f, "case %T", n->u[0].p); break; case nNowait: fmtprint(f, "%T&", n->u[0].p); break; - case nCount: fmtprint(f, "$#%T", n->u[0].p); break; - case nFlat: fmtprint(f, "$^%T", n->u[0].p); break; case nRmfn: fmtprint(f, "fn %T", n->u[0].p); break; case nSubshell: fmtprint(f, "@ %T", n->u[0].p); break; - case nVar: fmtprint(f, "$%T", n->u[0].p); break; case nAndalso: fmtprint(f, "%T&&%T", n->u[0].p, n->u[1].p); break; case nAssign: fmtprint(f, "%T=%T", n->u[0].p, n->u[1].p); break; case nConcat: fmtprint(f, "%T^%T", n->u[0].p, n->u[1].p); break; @@ -81,10 +69,25 @@ static bool Tconv(Format *f, int ignore) { case nArgs: fmtprint(f, "%T %T", n->u[0].p, n->u[1].p); break; case nSwitch: fmtprint(f, "switch(%T){%T}", n->u[0].p, n->u[1].p); break; case nMatch: fmtprint(f, "~ %T %T", n->u[0].p, n->u[1].p); break; - case nVarsub: fmtprint(f, "$%T(%T)", n->u[0].p, n->u[1].p); break; case nWhile: fmtprint(f, "while(%T)%T", n->u[0].p, n->u[1].p); break; case nLappend: fmtprint(f, "(%T %T)", n->u[0].p, n->u[1].p); break; case nForin: fmtprint(f, "for(%T in %T)%T", n->u[0].p, n->u[1].p, n->u[2].p); break; + case nVarsub: fmtprint(f, "$%T(%T)", n->u[0].p, n->u[1].p); break; + case nCount: case nFlat: case nVar: { + char *lp = "", *rp = ""; + Node *n0 = n->u[0].p; + + if (n0->type != nWord && n0->type != nQword) + lp = "(", rp = ")"; + + switch (n->type) { + default: panic("this can't happen"); break; + case nCount: fmtprint(f, "$#%s%T%s", lp, n0, rp); break; + case nFlat: fmtprint(f, "$^%s%T%s", lp, n0, rp); break; + case nVar: fmtprint(f, "$%s%T%s", lp, n0, rp); break; + } + break; + } case nDup: if (n->u[2].i != -1) fmtprint(f, "%D[%d=%d]", n->u[0].i, n->u[1].i, n->u[2].i); @@ -154,65 +157,41 @@ static bool Tconv(Format *f, int ignore) { return FALSE; } -/* convert a List to a string, separating it with ^A characters. Used for exporting variables to the environment */ - -extern char *list2str(char *name, List *s) { - SIZE_T size, step; - List *t; - char *w, *x; - name = nprint("%F", name); - size = strlen(name) + listlen(s); - w = ealloc(size + 2); - t = s; - x = w; - strcpy(x, name); - strcpy(x += strlen(name), "="); - strcpy(x += conststrlen("="), t->w); - for (x += strlen(t->w), s = s->n; s != NULL; s = s->n) { - memcpy(x, FSSTRING, step = conststrlen(FSSTRING)); - x += step; - memcpy(x, s->w, step = strlen(s->w)); - x += step; - } - *x = '\0'; - return w; -} - /* convert a List to an array, for execve() */ extern char **list2array(List *s, bool print) { - char **av; - int i; + char **argv, **av; - /* 4 == 1 for the null terminator + 2 for the fake execve() + 1 for defaulting to sh */ - av = nalloc((listnel(s) + 4) * sizeof (char *)); - av += 3; /* hide the two free spots from rc (two for #! emulation, one for defaulting to sh) */ if (print) fprint(2, "%L\n", s, " "); - for (i = 0; s != NULL; i++) { - av[i] = s->w; + /* + Allocate 3 extra spots (2 for the fake execve & 1 for defaulting to + sh) and hide these from exec(). + */ + argv = av = (char **) nalloc((listnel(s) + 4) * sizeof *av) + 3; + while (s != NULL) { + *av++ = s->w; s = s->n; } - av[i] = NULL; - return av; + *av = NULL; + return argv; } -/* figure out the name of a variable given an environment string. copy this into malloc space */ +/* figure out the name of a variable given an environment string. */ extern char *get_name(char *s) { + char *eq = strchr(s, '='); + char *r, *result; int c; - SIZE_T i; - char *r, *namebuf; - for (i = 0; s[i] != '\0' && s[i] != '='; i++) - ; - if (s[i] == '\0') + + if (eq == NULL) return NULL; - r = namebuf = ealloc(i + 1); + r = result = nalloc(eq - s + 1); while (1) switch (c = *s++) { case '=': *r++ = '\0'; - return namebuf; + return result; #ifdef PROTECT_ENV case '_': if (*s == '_') { @@ -233,55 +212,45 @@ extern char *get_name(char *s) { } } -/* get the next word from a variable's value as represented in the environment. */ - -static char *getenvw(char *s, bool saw_alpha) { - SIZE_T i; - char *r; - for (i = 0; s[i] != '\0' && s[i] != FSCHAR; i++) - ; - if (i == 0) { - if (s[i] == '\0' && !saw_alpha) - return NULL; - else - return clear(enew(char), (SIZE_T) 1); - } - r = strncpy(ealloc(i + 1), s, i); - r[i] = '\0'; - return r; -} +/* + Hacky routine to split a ^A-separated environment variable. Scans + backwards. Be VERY CAREFUL with the loop boundary conditions. e.g., + notice what happens when there is no ^A in the extdef. (It does + the right thing, of course :-) +*/ -/* take an environment entry for a variable (elements ^A separated) and turn it into a List */ +#define skipleft(p) do --p; while (*p != '\0' && *p != '\001'); extern List *parse_var(char *name, char *extdef) { - List *r, *top; - char *f; - bool saw_alpha; - top = r = enew(List); - extdef = strchr(extdef, '=') + 1; - if ((f = getenvw(extdef, FALSE)) == NULL) { - r->w = ""; - r->m = NULL; - r->n = NULL; - } else { - while (1) { - r->w = f; - r->m = NULL; - extdef += strlen(f); - if (*extdef == FSCHAR) { - extdef++; - saw_alpha = TRUE; - } else { - saw_alpha = FALSE; - } - if ((f = getenvw(extdef, saw_alpha)) == NULL) { - r->n = NULL; - break; - } - r = r->n = enew(List); - } + char *endp, *realend, *sepp; + List *tailp, *new; + + extdef = strchr(extdef, '='); + *extdef++ = '\0'; /* null "initiate" the string for rtol scanning */ + + sepp = realend = strchr(extdef, '\0'); + tailp = NULL; + + while (1) { + endp = sepp; /* set endp to end of current mebmer */ + skipleft(sepp); /* advance sepp to the previous \1, */ + *endp = '\0'; /* and null terminate the member. */ + + new = enew(List); + new->w = ecpy(sepp+1); + new->m = NULL; + new->n = tailp; + tailp = new; + + if (sepp < extdef) /* break when sepp hits the null "initiator" */ + break; + if (endp != realend) /* else restore the \1 in between members */ + *endp = '\001'; } - return top; + if (endp != realend) + *endp = '\001'; + *--extdef = '='; /* restore extdef '=' */ + return tailp; } /* get an environment entry for a function and have rc parse it. */ diff --git a/glob.c b/glob.c @@ -94,11 +94,39 @@ static List *dmatch(char *d, char *p, char *m) { extern DIR *opendir(const char *); extern struct dirent *readdir(DIR *); /*extern int closedir(DIR *);*/ + int i; + + /* + return a match if there are no metacharacters; allows globbing through + directories with no read permission. make sure the file exists, though. + */ + matched = TRUE; + if (m != NULL) + for (i = 0; p[i] != '\0'; i++) + if (m[i]) { + matched = FALSE; + break; + } + + if (matched) { + char *path = nprint("%s/%s", d, p); + if (stat(path, &s) < 0) + return NULL; + r = nnew(List); + r->w = ncpy(p); + r->m = NULL; + r->n = NULL; + return r; + } top = r = NULL; + if ((dirp = opendir(d)) == NULL) + return NULL; /* opendir succeeds on regular files on some systems, so the stat() call is necessary (sigh) */ - if (stat(d, &s) < 0 || (s.st_mode & S_IFMT) != S_IFDIR || (dirp = opendir(d)) == NULL) + if (stat(d, &s) < 0 || (s.st_mode & S_IFMT) != S_IFDIR) { + closedir(dirp); return NULL; + } while ((dp = readdir(dirp)) != NULL) if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */ matched = TRUE; @@ -125,15 +153,17 @@ static List *dmatch(char *d, char *p, char *m) { static List *lglob(List *s, char *p, char *m, SIZE_T slashcount) { List *q, *r, *top, foo; - static List slash; - static SIZE_T slashsize = 0; - if (slashcount + 1 > slashsize) { - slash.w = ealloc(slashcount + 1); - slashsize = slashcount; + static struct { + List l; + SIZE_T size; + } slash; + if (slashcount+1 > slash.size) { + slash.size = 2*(slashcount+1); + slash.l.w = erealloc(slash.l.w, slash.size); } - slash.w[slashcount] = '\0'; + slash.l.w[slashcount] = '\0'; while (slashcount > 0) - slash.w[--slashcount] = '/'; + slash.l.w[--slashcount] = '/'; for (top = r = NULL; s != NULL; s = s->n) { q = dmatch(s->w, p, m); if (q != NULL) { @@ -141,7 +171,7 @@ static List *lglob(List *s, char *p, char *m, SIZE_T slashcount) { foo.m = NULL; foo.n = NULL; if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */ - q = concat(&slash, q); /* dir/name with slash */ + q = concat(&slash.l, q); /* dir/name with slash */ q = concat(&foo, q); if (r == NULL) top = r = q; @@ -209,7 +239,7 @@ static List *doglob(char *w, char *m) { } do { SIZE_T slashcount; - SIGCHK; + sigchk(); for (slashcount = 0; *s == '/'; s++, m++) slashcount++; /* skip slashes */ while (*s != '/' && *s != '\0') diff --git a/glom.c b/glom.c @@ -3,6 +3,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <signal.h> +#include <errno.h> #include "rc.h" #if !defined(S_IFIFO) && !defined(DEVFD) #define NOCMDARG @@ -68,11 +69,11 @@ extern List *concat(List *s1, List *s2) { } else { r->m = nalloc(z); if (s1->m == NULL) - clear(r->m, x); + memzero(r->m, x); else memcpy(r->m, s1->m, x); if (s2->m == NULL) - clear(&r->m[x], y); + memzero(&r->m[x], y); else memcpy(&r->m[x], s2->m, y); r->m[z] = 0; @@ -183,7 +184,7 @@ static List *bqinput(List *ifs, int fd) { char isifs[256]; int n, state; /* a simple FSA is used to read in data */ - clear(isifs, sizeof isifs); + memzero(isifs, sizeof isifs); for (isifs['\0'] = TRUE; ifs != NULL; ifs = ifs->n) for (s = ifs->w; *s != '\0'; s++) isifs[*(unsigned char *)s] = TRUE; @@ -209,8 +210,12 @@ static List *bqinput(List *ifs, int fd) { if ((n = rc_read(fd, end, remain)) <= 0) { if (n == 0) /* break */ break; - uerror("backquote read"); - rc_error(NULL); + else if (errno == EINTR) + return NULL; /* interrupted, wait for subproc */ + else { + uerror("backquote read"); + rc_error(NULL); + } } remain -= n; for (bufend = &end[n]; end < bufend; end++) @@ -218,6 +223,7 @@ static List *bqinput(List *ifs, int fd) { if (!isifs[*(unsigned char *)end]) { state = 1; r->w = end; + r->m = NULL; } } else { if (isifs[*(unsigned char *)end]) { @@ -252,7 +258,6 @@ static List *backq(Node *ifs, Node *n) { rc_error(NULL); } if ((pid = rc_fork()) == 0) { - setsigdefaults(FALSE); mvfd(p[1], 1); close(p[0]); redirq = NULL; @@ -264,7 +269,8 @@ static List *backq(Node *ifs, Node *n) { close(p[0]); rc_wait4(pid, &sp, TRUE); statprint(-1, sp); - SIGCHK; + varassign("bqstatus", word(strstatus(sp), NULL), FALSE); + sigchk(); return bq; } diff --git a/hash.c b/hash.c @@ -279,15 +279,17 @@ extern char **makeenv() { return env; } -extern void whatare_all_vars() { +extern void whatare_all_vars(bool showfn, bool showvar) { int i; List *s; - for (i = 0; i < vsize; i++) - if (vp[i].name != NULL && (s = varlookup(vp[i].name)) != NULL) - prettyprint_var(1, vp[i].name, s); - 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)); + 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: */ diff --git a/history/history.c b/history/history.c @@ -246,7 +246,7 @@ again: s = last; /* * if the command contains the "me" character at the start of the line - * or after any of [`{|()] then try again + * or after any of [`{|()@] then try again */ for (t = s; *t != '\0'; t++) @@ -254,8 +254,16 @@ again: s = last; char *u = t - 1; while (u >= s && (*u == ' ' || *u == '\t')) --u; - if (u < s || *u == '`' || *u == '{' || *u == '|' || *u == '(' || *u == ')') + if (u < s) goto again; + switch (*u) { + case '`': case '@': + case '(': case ')': + case '{': case '|': + goto again; + default: + break; + } } return s; } diff --git a/input.c b/input.c @@ -40,8 +40,6 @@ static char *inbuf; static SIZE_T istacksize, chars_out, chars_in; static bool eofread = FALSE, save_lineno = TRUE; static Input *istack, *itop; -static char *histstr; -static int histfd; static int (*realgchar)(void); static void (*realugchar)(int); @@ -49,11 +47,17 @@ static void (*realugchar)(int); int last; extern int gchar() { + int c; + if (eofread) { eofread = FALSE; return last = EOF; } - return (*realgchar)(); + + while ((c = (*realgchar)()) == '\0') + pr_error("warning: null character ignored"); + + return c; } extern void ugchar(int c) { @@ -99,7 +103,7 @@ static char *rc_readline(char *prompt) { slow = FALSE; if (r == NULL) errno = EINTR; - SIGCHK; + sigchk(); return r; } #else @@ -128,14 +132,14 @@ static int fdgchar() { inbuf = ealloc(chars_in + 3); strcpy(inbuf+2, rlinebuf); strcat(inbuf+2, "\n"); + efree(rlinebuf); } } else #endif { long /*ssize_t*/ r = rc_read(istack->fd, inbuf + 2, BUFSIZE); + sigchk(); if (r < 0) { - if (errno == EINTR) - continue; /* Suppose it was interrupted by a signal */ uerror("read"); rc_exit(1); } @@ -214,7 +218,7 @@ extern void popinput() { efree(inbuf); --istack; realgchar = (istack->t == iString ? stringgchar : fdgchar); - if (istack->fd == -1) { /* top of input stack */ + if (istack->t == iFd && istack->fd == -1) { /* top of input stack */ realgchar = dead; realugchar = ugdead; } @@ -258,7 +262,7 @@ extern Node *doit(bool execit) { Edata block; block.b = newblock(); except(eArena, block, &e2); - SIGCHK; + sigchk(); if (dashell) { char *fname[3]; fname[1] = concat(varlookup("home"), word("/.rcrc", NULL))->w; @@ -314,57 +318,42 @@ extern Node *parseline(char *extdef) { return fun; } -/* write last command out to a file. Check to see if $history has changed, also */ +/* write last command out to a file if interactive && $history is set */ static void history() { - List *histlist; + List *hist; SIZE_T a; - if (!interactive) - return; - if ((histlist = varlookup("history")) == NULL) { - if (histstr != NULL) { - efree(histstr); - close(histfd); - histstr = NULL; - } + + if (!interactive || (hist = varlookup("history")) == NULL) return; - } - if (histstr == NULL || !streq(histstr, histlist->w)) { /* open new file */ - if (histstr != NULL) { - efree(histstr); - close(histfd); - } - histstr = ecpy(histlist->w); - histfd = rc_open(histstr, rAppend); - if (histfd < 0) { - uerror(histstr); - efree(histstr); - histstr = NULL; - varrm("history", FALSE); - } - } - /* - Small unix hack: since read() reads only up to a newline - from a terminal, then presumably this write() will write at - most only one input line at a time. - */ - for (a = 2; a < chars_in + 2; a++) { /* skip empty lines and comments in history. */ - if (inbuf[a] == '#' || inbuf[a] == '\n') - return; - if (inbuf[a] != ' ' && inbuf[a] != '\t') + + for (a = 0; a < chars_in; a++) { + char c = inbuf[a+2]; + + /* skip empty lines and comments */ + if (c == '#' || c == '\n') break; + + /* line matches [ \t]*[^#\n] so it's ok to write out */ + if (c != ' ' && c != '\t') { + char *name = hist->w; + int fd = rc_open(name, rAppend); + if (fd < 0) { + uerror(name); + varrm(name, TRUE); + } else { + writeall(fd, inbuf + 2, chars_in); + close(fd); + } + break; + } } - writeall(histfd, inbuf + 2, chars_in); } /* close file descriptors after a fork() */ extern void closefds() { Input *i; - if (histstr != NULL) { /* Close an open history file */ - close(histfd); - histstr = NULL; /* But prevent re-closing of the same file-descriptor */ - } for (i = istack; i != itop; --i) /* close open scripts */ if (i->t == iFd && i->fd > 2) { close(i->fd); diff --git a/lex.c b/lex.c @@ -10,7 +10,7 @@ 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 characeter (i.e., + 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. @@ -155,9 +155,6 @@ top: while ((c = gchar()) == ' ' || c == '\t') w = KW; } switch (c) { - case '\0': - pr_error("warning: null character ignored"); - goto top; case '!': return BANG; case '@': diff --git a/main.c b/main.c @@ -3,7 +3,7 @@ #include "rc.h" bool dashdee, dashee, dashvee, dashex, dashell, dasheye, - dashen, dashpee, interactive; + dashen, dashpee, dashess, interactive; int rc_pid; static bool dashoh; @@ -19,7 +19,7 @@ extern void main(int argc, char *argv[], char *envp[]) { dollarzero = argv[0]; rc_pid = getpid(); dashell = (*argv[0] == '-'); /* Unix tradition */ - while ((c = rc_getopt(argc, argv, "nolpeivdxc:")) != -1) + while ((c = rc_getopt(argc, argv, "nolpeivdxsc:")) != -1) switch (c) { case 'l': dashell = TRUE; @@ -39,6 +39,9 @@ extern void main(int argc, char *argv[], char *envp[]) { case 'd': dashdee = TRUE; break; + case 's': + dashess = dasheye = interactive = TRUE; + break; case 'c': dashsee[0] = rc_optarg; goto quitopts; @@ -56,8 +59,8 @@ extern void main(int argc, char *argv[], char *envp[]) { } quitopts: argv += rc_optind; - /* use isatty() iff -i is not set, and iff the input is not from a script or -c flag */ - if (!dasheye && dashsee[0] == NULL && *argv == NULL) + /* use isatty() iff -i is not set, and iff the input is not from a script or -c or -s flags */ + if (!dasheye && !dashess && dashsee[0] == NULL && *argv == NULL) interactive = isatty(0); if (!dashoh) { checkfd(0, rFrom); @@ -78,10 +81,13 @@ quitopts: null[0] = NULL; starassign(dollarzero, null, FALSE); /* assign $0 to $* */ inithandler(); - if (dashsee[0] != NULL) { /* input from the -c flag? */ + if (dashsee[0] != NULL || dashess) { /* input from -c or -s? */ if (*argv != NULL) starassign(dollarzero, argv, FALSE); - pushstring(dashsee, TRUE); + if (dashess) + pushfd(0); + else + pushstring(dashsee, TRUE); } else if (*argv != NULL) { /* else from a file? */ b_dot(--argv); rc_exit(getstatus()); diff --git a/nalloc.c b/nalloc.c @@ -122,6 +122,8 @@ extern void *ealloc(SIZE_T n) { extern void *erealloc(void *p, SIZE_T n) { extern void *realloc(void *, SIZE_T); + if (p == NULL) /* convenience feature */ + return ealloc(n); if ((p = realloc(p, n)) == NULL) { uerror("realloc"); rc_exit(1); diff --git a/print.c b/print.c @@ -270,9 +270,11 @@ extern int printfmt(Format *format, const char *fmt) { extern int fmtprint(Format *format, const char *fmt,...) { int n = -format->flushed; - va_list saveargs = format->args; + va_list ap, saveargs; - va_start(format->args, fmt); + va_start(ap, fmt); + saveargs = format->args; + format->args = ap; n += printfmt(format, fmt); va_end(format->args); format->args = saveargs; @@ -292,6 +294,7 @@ static void fprint_flush(Format *format, SIZE_T more) { extern int fprint(int fd, const char *fmt,...) { char buf[1024]; Format format; + va_list ap; format.buf = buf; format.bufbegin = buf; @@ -300,7 +303,8 @@ extern int fprint(int fd, const char *fmt,...) { format.flushed = 0; format.u.n = fd; - va_start(format.args, fmt); + va_start(ap, fmt); + format.args = ap; printfmt(&format, fmt); va_end(format.args); @@ -340,8 +344,11 @@ static char *memprint(Format *format, const char *fmt, char *buf, SIZE_T len) { extern char *mprint(const char *fmt,...) { Format format; char *result; + va_list ap; + format.u.n = 1; - va_start(format.args, fmt); + va_start(ap, fmt); + format.args = ap; result = memprint(&format, fmt, ealloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE); va_end(format.args); return result; @@ -350,8 +357,11 @@ extern char *mprint(const char *fmt,...) { extern char *nprint(const char *fmt,...) { Format format; char *result; + va_list ap; + format.u.n = 0; - va_start(format.args, fmt); + va_start(ap, fmt); + format.args = ap; result = memprint(&format, fmt, nalloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE); va_end(format.args); return result; diff --git a/proto.h b/proto.h @@ -47,6 +47,7 @@ extern char *strncpy(char *, const char *, SIZE_T); extern char *strcat(char *, const char *); extern char *strncat(char *, const char *, SIZE_T); extern void *memcpy(void *, const void *, SIZE_T); +extern void *memset(void *, int, SIZE_T); /* fake unistd.h */ diff --git a/rc.1 b/rc.1 @@ -490,6 +490,7 @@ This allows the unambiguous use of variables adjacent to text, as in .Ds .Cr $variable^follow .De +.PP To include a literal .Cr $ in a here document when an unquoted end-of-file marker is being used, @@ -683,8 +684,8 @@ the first command exits with a zero exit status (``true'' in Unix). .Cr "command || command" .De .PP -executes the first command executing the second command if and only if -the second command exits with a nonzero exit status (``false'' in Unix). +executes the first command and then executes the second command if and only if +the first command exits with a nonzero exit status (``false'' in Unix). .Ds .Cr "! command" .De @@ -1625,7 +1626,7 @@ If no .I pid is specified, .I rc -waits for any child process to exit. +waits for all child processes to exit. .TP \fBwhatis \fR[\fB\-s\fR] [\fB\-\|\-\fR] [\fIname ...\fR] Prints a definition of the named objects. diff --git a/rc.h b/rc.h @@ -150,6 +150,7 @@ enum { #define a2u(x) n2u(x, 10) #define o2u(x) n2u(x, 8) #define arraysize(a) ((int)(sizeof(a)/sizeof(*a))) +#define memzero(s, n) memset(s, 0, n) #define enew(x) ((x *) ealloc(sizeof(x))) #define ecpy(x) strcpy((char *) ealloc(strlen(x) + 1), x) #define lookup_fn(s) ((Function *) lookup(s, fp)) @@ -192,8 +193,6 @@ extern void exec(List *, bool); extern void doredirs(void); /* footobar.c */ -extern char *fun2str(char *, Node *); -extern char *list2str(char *, List *); extern char **list2array(List *, bool); extern char *get_name(char *); extern List *parse_var(char *, char *); @@ -245,7 +244,7 @@ extern void setsigdefaults(bool); extern void inithandler(void); extern void varassign(char *, List *, bool); extern void varrm(char *, bool); -extern void whatare_all_vars(void); +extern void whatare_all_vars(bool, bool); extern void whatare_all_signals(void); extern void prettyprint_var(int, char *, List *); extern void prettyprint_fn(int, char *, Node *); @@ -338,7 +337,6 @@ extern void sigchk(void); extern void (*rc_signal(int, void (*)(int)))(int); extern void (*sighandlers[])(int); extern volatile SIG_ATOMIC_T slow, interrupt_happened; -#define SIGCHK sigchk() /* status.c */ extern int istrue(void); @@ -349,6 +347,7 @@ extern List *sgetstatus(void); extern void setpipestatus(int [], int); extern void statprint(int, int); extern void ssetstatus(char **); +extern char *strstatus(int s); /* tree.c */ extern Node *mk(int /*nodetype*/,...); @@ -362,7 +361,6 @@ extern int rc_read(int, char *, SIZE_T); extern int mvfd(int, int); extern int starstrcmp(const void *, const void *); extern void pr_error(char *); -extern char *clear(char *, SIZE_T); extern void panic(char *); extern void uerror(char *); extern void writeall(int, char *, SIZE_T); diff --git a/signal.c b/signal.c @@ -13,8 +13,6 @@ void (*sighandlers[NUMOFSIGNALS])(int); static volatile SIG_ATOMIC_T sigcount, caught[NUMOFSIGNALS]; extern void catcher(int s) { - if (forked) - exit(1); /* exit unconditionally on a signal in a child process */ if (caught[s] == 0) { sigcount++; caught[s] = 1; @@ -52,7 +50,7 @@ extern void sigchk() { extern void (*rc_signal(int s, void (*h)(int)))(int) { void (*old)(int); - SIGCHK; + sigchk(); old = sighandlers[s]; if (h == SIG_DFL || h == SIG_IGN) { signal(s, h); diff --git a/status.c b/status.c @@ -80,27 +80,35 @@ extern void statprint(int pid, int i) { /* prepare a list to be passed back. Used whenever $status is dereferenced */ extern List *sgetstatus() { - List *r; + List *r = NULL; int i; - for (r = NULL, i = 0; i < pipelength; i++) { + + for (i = 0; i < pipelength; i++) { List *q = nnew(List); - int s = statuses[i]; - int t; + q->w = strstatus(statuses[i]); + q->m = NULL; q->n = r; r = q; - if ((t = s & 0x7f) != 0) { - const char *core = (s & 0x80) ? "+core" : ""; - if (t < NUMOFSIGNALS && *signals[t].name != '\0') - r->w = nprint("%s%s", signals[t].name, core); - else - r->w = nprint("-%d%s", t, core); /* unknown signals are negated */ - } else - r->w = nprint("%d", (s >> 8) & 0xff); - r->m = NULL; } + return r; } +/* return status as a string (used above and for bqstatus) */ + +extern char *strstatus(int s) { + int t = s & 0x7f; + + if (t != 0) { + const char *core = (s & 0x80) ? "+core" : ""; + if (t < NUMOFSIGNALS && *signals[t].name != '\0') + return nprint("%s%s", signals[t].name, core); + else + return nprint("-%d%s", t, core); /* unknown signals are negated */ + } else + return nprint("%d", (s >> 8) & 0xff); +} + extern void ssetstatus(char **av) { int i, j, k, l; bool found; diff --git a/trip.rc b/trip.rc Binary files differ. diff --git a/utils.c b/utils.c @@ -82,7 +82,7 @@ extern void writeall(int fd, char *buf, SIZE_T remain) { slow = FALSE; } slow = FALSE; - SIGCHK; + sigchk(); } extern int rc_read(int fd, char *buf, SIZE_T n) { @@ -101,18 +101,9 @@ extern int rc_read(int fd, char *buf, SIZE_T n) { errno = EINTR; r = -1; } - SIGCHK; return r; } -/* clear out z bytes from character string s */ - -extern char *clear(char *s, SIZE_T z) { - while (z != 0) - s[--z] = 0; - return s; -} - /* duplicate a fd and close the old one only if necessary */ extern int mvfd(int i, int j) { diff --git a/var.c b/var.c @@ -104,7 +104,7 @@ extern char *varlookup_string(char *name) { return look->extdef; if (look->def == NULL) return NULL; - return look->extdef = list2str(name, look->def); + 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 */ @@ -166,16 +166,15 @@ static void listassign(char *name, List *def, bool stack) { return; } v = def->w; - r = val = enew(List); + r = val = nnew(List); while ((w = strchr(v, ':')) != NULL) { *w = '\0'; - r->w = ecpy(v); + r->w = ncpy(v); *w = ':'; v = w + 1; - r->n = enew(List); - r = r->n; + r = r->n = nnew(List); } - r->w = ecpy(v); + r->w = ncpy(v); r->n = NULL; varassign(name, val, stack); } diff --git a/version.c b/version.c @@ -1 +1 @@ -const char id[] = "@(#)rc version 1.4, 5/26/92."; +const char id[] = "@(#)rc version 1.5betadev-1, 1/9/94."; diff --git a/wait.c b/wait.c @@ -16,7 +16,7 @@ static struct Pid { } *plist = NULL; extern int rc_fork() { - Pid *new = enew(Pid); + Pid *new; int pid = fork(); switch (pid) { case -1: @@ -25,9 +25,10 @@ extern int rc_fork() { /* NOTREACHED */ case 0: forked = TRUE; - SIGCHK; + sigchk(); return 0; default: + new = enew(Pid); new->pid = pid; new->alive = TRUE; new->n = plist; @@ -98,7 +99,7 @@ extern void waitforall() { setstatus(pid, stat); else set(FALSE); - SIGCHK; + sigchk(); } } diff --git a/walk.c b/walk.c @@ -11,6 +11,7 @@ */ bool cond = FALSE; +static bool haspreredir(Node *); static bool isallpre(Node *); static bool dofork(bool); static void dopipe(Node *); @@ -22,7 +23,7 @@ static void dopipe(Node *); /* walk the parse-tree. "obvious". */ extern bool walk(Node *n, bool parent) { -top: SIGCHK; +top: sigchk(); if (n == NULL) { if (!parent) exit(0); @@ -147,7 +148,6 @@ top: SIGCHK; } case nSubshell: if (dofork(TRUE)) { - setsigdefaults(FALSE); walk(n->u[0].p, FALSE); rc_exit(getstatus()); } @@ -214,8 +214,14 @@ top: SIGCHK; case nPre: { List *v; if (n->u[0].p->type == nRedir || n->u[0].p->type == nDup) { + if (redirq == NULL && !dofork(parent)) /* subshell on first preredir */ + break; qredir(n->u[0].p); - walk(n->u[1].p, parent); + if (!haspreredir(n->u[1].p)) + doredirs(); /* no more preredirs, empty queue */ + walk(n->u[1].p, FALSE); + rc_exit(getstatus()); + /* NOTREACHED */ } else if (n->u[0].p->type == nAssign) { if (isallpre(n->u[1].p)) { walk(n->u[0].p, TRUE); @@ -239,7 +245,6 @@ top: SIGCHK; if (n->u[1].p == NULL) { WALK(n->u[0].p, parent); } else if (dofork(parent)) { - setsigdefaults(FALSE); walk(n->u[1].p, TRUE); /* Do redirections */ redirq = NULL; /* Reset redirection queue */ walk(n->u[0].p, FALSE); /* Do commands */ @@ -265,6 +270,17 @@ top: SIGCHK; return istrue(); } +/* checks to see whether there are any pre-redirections left in the tree */ + +static bool haspreredir(Node *n) { + while (n != NULL && n->type == nPre) { + if (n->u[0].p->type == nDup || n->u[0].p->type == nRedir) + return TRUE; + n = n->u[1].p; + } + return FALSE; +} + /* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */ static bool isallpre(Node *n) { @@ -286,7 +302,7 @@ static bool dofork(bool parent) { redirq = NULL; /* clear out the pre-redirection queue in the parent */ rc_wait4(pid, &sp, TRUE); setstatus(-1, sp); - SIGCHK; + sigchk(); return FALSE; } @@ -304,7 +320,6 @@ static void dopipe(Node *n) { rc_error(NULL); } if ((pid = rc_fork()) == 0) { - setsigdefaults(FALSE); redirq = NULL; /* clear preredir queue */ mvfd(p[0], r->u[1].i); if (fd_prev != 1) @@ -321,7 +336,6 @@ static void dopipe(Node *n) { close(p[0]); } if ((pid = rc_fork()) == 0) { - setsigdefaults(FALSE); mvfd(fd_prev, fd_out); walk(r, FALSE); exit(getstatus()); @@ -340,5 +354,5 @@ static void dopipe(Node *n) { intr |= (sp == SIGINT); } setpipestatus(stats, i); - SIGCHK; + sigchk(); }