ast@cs.vu.nl (Andy Tanenbaum) (10/06/88)
: This is a shar archive. Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'makefile'
sed 's/^X//' > 'makefile' << '+ END-OF-FILE ''makefile'
XCFLAGS=-F
Xl=/lib
X
Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s
Xsh: $(shobj) sh.h
X @cc -i -o sh -T. $(shobj)
X @chmem =13000 sh
X
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' 'makefile'
set `wc -c 'makefile'`
count=$1
case $count in
127) :;;
*) echo 'Bad character count in ''makefile' >&2
echo 'Count should be 127' >&2
esac
echo Extracting 'sh.h'
sed 's/^X//' > 'sh.h' << '+ END-OF-FILE ''sh.h'
X/* -------- sh.h -------- */
X/*
X * shell
X */
X
X#define NULL 0
X#define LINELIM 1000
X#define NPUSH 8 /* limit to input nesting */
X
X#define NOFILE 20 /* Number of open files */
X#define NUFILE 10 /* Number of user-accessible files */
X#define FDBASE 10 /* First file usable by Shell */
X
X/*
X * values returned by wait
X */
X#define WAITSIG(s) ((s)&0177)
X#define WAITVAL(s) (((s)>>8)&0377)
X#define WAITCORE(s) (((s)&0200)!=0)
X
X/*
X * library and system defintions
X */
Xtypedef int xint; /* base type of jmp_buf, for broken compilers */
X
X/*
X * shell components
X */
X/* #include "area.h" */
X/* #include "word.h" */
X/* #include "io.h" */
X/* #include "var.h" */
X
X#define QUOTE 0200
X
X#define NOBLOCK ((struct op *)NULL)
X#define NOWORD ((char *)NULL)
X#define NOWORDS ((char **)NULL)
X#define NOPIPE ((int *)NULL)
X
X/*
X * Description of a command or an operation on commands.
X * Might eventually use a union.
X */
Xstruct op {
X int type; /* operation type, see below */
X char **words; /* arguments to a command */
X struct ioword **ioact; /* IO actions (eg, < > >>) */
X struct op *left;
X struct op *right;
X char *str; /* identifier for case and for */
X};
X
X#define TCOM 1 /* command */
X#define TPAREN 2 /* (c-list) */
X#define TPIPE 3 /* a | b */
X#define TLIST 4 /* a [&;] b */
X#define TOR 5 /* || */
X#define TAND 6 /* && */
X#define TFOR 7
X#define TDO 8
X#define TCASE 9
X#define TIF 10
X#define TWHILE 11
X#define TUNTIL 12
X#define TELIF 13
X#define TPAT 14 /* pattern in case */
X#define TBRACE 15 /* {c-list} */
X#define TASYNC 16 /* c & */
X
X/*
X * actions determining the environment of a process
X */
X#define BIT(i) (1<<(i))
X#define FEXEC BIT(0) /* execute without forking */
X
X/*
X * flags to control evaluation of words
X */
X#define DOSUB 1 /* interpret $, `, and quotes */
X#define DOBLANK 2 /* perform blank interpretation */
X#define DOGLOB 4 /* interpret [?* */
X#define DOKEY 8 /* move words with `=' to 2nd arg. list */
X#define DOTRIM 16 /* trim resulting string */
X
X#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
X
XExtern char **dolv;
XExtern int dolc;
XExtern int exstat;
XExtern char gflg;
XExtern int talking; /* interactive (talking-type wireless) */
XExtern int execflg;
XExtern int multiline; /* \n changed to ; */
XExtern struct op *outtree; /* result from parser */
X
XExtern xint *failpt;
XExtern xint *errpt;
X
Xstruct brkcon {
X jmp_buf brkpt;
X struct brkcon *nextlev;
X} ;
XExtern struct brkcon *brklist;
XExtern int isbreak;
X
X/*
X * redirection
X */
Xstruct ioword {
X short io_unit; /* unit affected */
X short io_flag; /* action (below) */
X char *io_name; /* file name */
X};
X#define IOREAD 1 /* < */
X#define IOHERE 2 /* << (here file) */
X#define IOWRITE 4 /* > */
X#define IOCAT 8 /* >> */
X#define IOXHERE 16 /* ${}, ` in << */
X#define IODUP 32 /* >&digit */
X#define IOCLOSE 64 /* >&- */
X
X#define IODEFAULT (-1) /* token for default IO unit */
X
XExtern struct wdblock *wdlist;
XExtern struct wdblock *iolist;
X
X/*
X * parsing & execution environment
X */
Xextern struct env {
X char *linep;
X struct io *iobase;
X struct io *iop;
X xint *errpt;
X int iofd;
X struct env *oenv;
X} e;
X
X/*
X * flags:
X * -e: quit on error
X * -k: look for name=value everywhere on command line
X * -n: no execution
X * -t: exit after reading and executing one command
X * -v: echo as read
X * -x: trace
X * -u: unset variables net diagnostic
X */
Xextern char *flag;
X
Xextern char *null; /* null value for variable */
Xextern int intr; /* interrupt pending */
X
XExtern char *trap[NSIG];
XExtern char ourtrap[NSIG];
XExtern int trapset; /* trap pending */
X
Xextern int inword; /* defer traps and interrupts */
X
XExtern int yynerrs; /* yacc */
X
XExtern char line[LINELIM];
Xextern char *elinep;
X
X/*
X * other functions
X */
Xint (*inbuilt())(); /* find builtin command */
Xchar *rexecve();
Xchar *space();
Xchar *getwd();
Xchar *strsave();
Xchar *evalstr();
Xchar *putn();
Xchar *itoa();
Xchar *unquote();
Xstruct var *lookup();
Xstruct wdblock *add2args();
Xstruct wdblock *glob();
Xchar **makenv();
Xstruct ioword *addio();
Xchar **eval();
Xint setstatus();
Xint waitfor();
X
Xint onintr(); /* SIGINT handler */
X
X/*
X * error handling
X */
Xvoid leave(); /* abort shell (or fail in subshell) */
Xvoid fail(); /* fail but return to process next command */
Xint sig(); /* default signal handler */
X
X/*
X * library functions and system calls
X */
Xlong lseek();
Xchar *strncpy();
Xint strlen();
Xextern int errno;
X
X/* -------- var.h -------- */
X
Xstruct var {
X char *value;
X char *name;
X struct var *next;
X char status;
X};
X#define COPYV 1 /* flag to setval, suggesting copy */
X#define RONLY 01 /* variable is read-only */
X#define EXPORT 02 /* variable is to be exported */
X#define GETCELL 04 /* name & value space was got with getcell */
X
XExtern struct var *vlist; /* dictionary */
X
XExtern struct var *homedir; /* home directory */
XExtern struct var *prompt; /* main prompt */
XExtern struct var *cprompt; /* continuation prompt */
XExtern struct var *path; /* search path for commands */
XExtern struct var *shell; /* shell to interpret command files */
XExtern struct var *ifs; /* field separators */
X
Xstruct var *lookup(/* char *s */);
Xvoid setval(/* struct var *, char * */);
Xvoid nameval(/* struct var *, char *val, *name */);
Xvoid export(/* struct var * */);
Xvoid ronly(/* struct var * */);
Xint isassign(/* char *s */);
Xint checkname(/* char *name */);
Xint assign(/* char *s, int copyflag */);
Xvoid putvlist(/* int key, int fd */);
Xint eqname(/* char *n1, char *n2 */);
X
X/* -------- io.h -------- */
X/* possible arguments to an IO function */
Xstruct ioarg {
X char *aword;
X char **awordlist;
X int afile; /* file descriptor */
X};
X
X/* an input generator's state */
Xstruct io {
X int (*iofn)();
X struct ioarg arg;
X int peekc;
X char nlcount; /* for `'s */
X char xchar; /* for `'s */
X char task; /* reason for pushed IO */
X};
XExtern struct io iostack[NPUSH];
X#define XOTHER 0 /* none of the below */
X#define XDOLL 1 /* expanding ${} */
X#define XGRAVE 2 /* expanding `'s */
X#define XIO 3 /* file IO */
X
X/* in substitution */
X#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL)
X
X/*
X * input generators for IO structure
X */
Xint nlchar();
Xint strchar();
Xint filechar();
Xint herechar();
Xint linechar();
Xint nextchar();
Xint gravechar();
Xint qgravechar();
Xint dolchar();
Xint wdchar();
X
X/*
X * IO functions
X */
Xint getc();
Xint readc();
Xvoid unget();
Xvoid ioecho();
Xvoid prs();
Xvoid putc();
Xvoid prn();
Xvoid closef();
Xvoid closeall();
X
X/*
X * IO control
X */
Xvoid pushio(/* struct ioarg arg, int (*gen)() */);
Xint remap();
Xint openpipe();
Xvoid closepipe();
Xstruct io *setbase(/* struct io * */);
X
XExtern struct ioarg temparg; /* temporary for PUSHIO */
X#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(temparg,(gen)))
X#define RUN(what,arg,gen) ((temparg.what = (arg)), run(temparg,(gen)))
X
X/* -------- word.h -------- */
X#ifndef WORD_H
X#define WORD_H 1
Xstruct wdblock {
X short w_bsize;
X short w_nword;
X /* bounds are arbitrary */
X char *w_words[1];
X};
X
Xstruct wdblock *addword();
Xstruct wdblock *newword();
Xchar **getwords();
X#endif
X
X/* -------- area.h -------- */
X
X/*
X * storage allocation
X */
Xchar *getcell(/* unsigned size */);
Xvoid garbage();
Xvoid setarea(/* char *obj, int to */);
Xint getarea(/* char *obj */);
Xvoid freearea(/* int area */);
Xvoid freecell(/* char *obj */);
X
XExtern int areanum; /* current allocation area */
X
X#define NEW(type) (type *)getcell(sizeof(type))
X#define DELETE(obj) freecell((char *)obj)
X
+ END-OF-FILE sh.h
chmod 'u=rw,g=r,o=r' 'sh.h'
set `wc -c 'sh.h'`
count=$1
case $count in
7321) :;;
*) echo 'Bad character count in ''sh.h' >&2
echo 'Count should be 7321' >&2
esac
echo Extracting 'sh1.c'
sed 's/^X//' > 'sh1.c' << '+ END-OF-FILE ''sh1.c'
X#define Extern extern
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X/* -------- sh.c -------- */
X/*
X * shell
X */
X
X/* #include "sh.h" */
X
Xint intr;
Xint inparse;
Xchar flags['z'-'a'+1];
Xchar *flag = flags-'a';
Xchar *elinep = line+sizeof(line)-5;
Xchar *null = "";
Xint inword =1;
Xstruct env e ={line, iostack, iostack-1, NULL, FDBASE, NULL};
X
Xextern char **environ; /* environment pointer */
X
X/*
X * default shell, search rules
X */
Xchar shellname[] = "/bin/sh";
Xchar search[] = ":/bin:/usr/bin";
X
Xint (*qflag)() = SIG_IGN;
X
Xmain(argc, argv)
Xint argc;
Xregister char **argv;
X{
X register int f;
X register char *s;
X int cflag;
X char *name, **ap;
X int (*iof)();
X
X initarea();
X if ((ap = environ) != NULL) {
X while (*ap)
X assign(*ap++, !COPYV);
X for (ap = environ; *ap;)
X export(lookup(*ap++));
X }
X closeall();
X areanum = 1;
X
X shell = lookup("SHELL");
X if (shell->value == null)
X setval(shell, shellname);
X export(shell);
X
X homedir = lookup("HOME");
X if (homedir->value == null)
X setval(homedir, "/");
X export(homedir);
X
X setval(lookup("$"), itoa(getpid(), 5));
X
X path = lookup("PATH");
X if (path->value == null)
X setval(path, search);
X export(path);
X
X ifs = lookup("IFS");
X if (ifs->value == null)
X setval(ifs, " \t\n");
X
X prompt = lookup("PS1");
X if (prompt->value == null)
X#ifndef UNIXSHELL
X setval(prompt, "$ ");
X#else
X setval(prompt, "% ");
X#endif
X if (geteuid() == 0) {
X setval(prompt, "# ");
X prompt->status &= ~EXPORT;
X }
X cprompt = lookup("PS2");
X if (cprompt->value == null)
X setval(cprompt, "> ");
X
X iof = filechar;
X cflag = 0;
X name = *argv++;
X if (--argc >= 1) {
X if(argv[0][0] == '-' && argv[0][1] != '\0') {
X for (s = argv[0]+1; *s; s++)
X switch (*s) {
X case 'c':
X prompt->status &= ~EXPORT;
X cprompt->status &= ~EXPORT;
X setval(prompt, "");
X setval(cprompt, "");
X cflag = 1;
X if (--argc > 0)
X PUSHIO(aword, *++argv, iof = nlchar);
X break;
X
X case 'q':
X qflag = SIG_DFL;
X break;
X
X case 's':
X /* standard input */
X break;
X
X case 't':
X prompt->status &= ~EXPORT;
X setval(prompt, "");
X iof = linechar;
X break;
X
X case 'i':
X talking++;
X default:
X if (*s>='a' && *s<='z')
X flag[*s]++;
X }
X } else {
X argv--;
X argc++;
X }
X if (iof == filechar && --argc > 0) {
X setval(prompt, "");
X setval(cprompt, "");
X prompt->status &= ~EXPORT;
X cprompt->status &= ~EXPORT;
X if (newfile(*++argv))
X exit(1);
X }
X }
X setdash();
X if (e.iop < iostack) {
X PUSHIO(afile, 0, iof);
X if (isatty(0) && isatty(1) && !cflag)
X talking++;
X }
X signal(SIGQUIT, qflag);
X if (name && name[0] == '-') {
X talking++;
X if ((f = open("/etc/profile", 0)) >= 0)
X next(remap(f));
X if ((f = open(".profile", 0)) >= 0)
X next(remap(f));
X }
X if (talking) {
X signal(SIGTERM, sig);
X signal(SIGINT, SIG_IGN);
X }
X dolv = argv;
X dolc = argc;
X dolv[0] = name;
X if (dolc > 1)
X for (ap = ++argv; --argc > 0;)
X if (assign(*ap = *argv++, !COPYV))
X dolc--; /* keyword */
X else
X ap++;
X setval(lookup("#"), putn(--dolc));
X
X for (;;) {
X if (talking && e.iop <= iostack)
X prs(prompt->value);
X onecommand();
X }
X}
X
Xsetdash()
X{
X register char *cp, c;
X char m['z'-'a'+1];
X
X cp = m;
X for (c='a'; c<='z'; c++)
X if (flag[c])
X *cp++ = c;
X *cp = 0;
X setval(lookup("-"), m);
X}
X
Xnewfile(s)
Xregister char *s;
X{
X register f;
X
X if (strcmp(s, "-") != 0) {
X f = open(s, 0);
X if (f < 0) {
X prs(s);
X err(": cannot open");
X return(1);
X }
X } else
X f = 0;
X next(remap(f));
X return(0);
X}
X
Xonecommand()
X{
X register i;
X jmp_buf m1;
X
X inword++;
X while (e.oenv)
X quitenv();
X areanum = 1;
X freehere(areanum);
X freearea(areanum);
X garbage();
X wdlist = 0;
X iolist = 0;
X e.errpt = 0;
X e.linep = line;
X yynerrs = 0;
X multiline = 0;
X inparse = 1;
X setjmp(failpt = m1); /* Bruce Evans' fix */
X if (talking)
X signal(SIGINT, onintr);
X if (setjmp(failpt = m1) || yyparse() || intr) {
X while (e.oenv)
X quitenv();
X scraphere();
X inparse = 0;
X intr = 0;
X return;
X }
X inparse = 0;
X inword = 0;
X if ((i = trapset) != 0) {
X trapset = 0;
X runtrap(i);
X }
X brklist = 0;
X intr = 0;
X execflg = 0;
X if (!flag['n']) {
X if (talking)
X signal(SIGINT, onintr);
X execute(outtree, NOPIPE, NOPIPE, 0);
X intr = 0;
X if (talking)
X signal(SIGINT, SIG_IGN);
X }
X}
X
Xvoid
Xfail()
X{
X longjmp(failpt, 1);
X /* NOTREACHED */
X}
X
Xvoid
Xleave()
X{
X if (execflg)
X fail();
X runtrap(0);
X sync();
X exit(exstat);
X /* NOTREACHED */
X}
X
Xwarn(s)
Xregister char *s;
X{
X if(*s) {
X prs(s);
X exstat = -1;
X }
X prs("\n");
X if (flag['e'])
X leave();
X}
X
Xerr(s)
Xchar *s;
X{
X warn(s);
X if (flag['n'])
X return;
X if (!talking)
X leave();
X if (e.errpt)
X longjmp(e.errpt, 1);
X closeall();
X e.iop = e.iobase = iostack;
X}
X
Xnewenv(f)
X{
X register struct env *ep;
X
X if (f) {
X quitenv();
X return(1);
X }
X ep = (struct env *) space(sizeof(*ep));
X if (ep == NULL) {
X while (e.oenv)
X quitenv();
X fail();
X }
X *ep = e;
X e.oenv = ep;
X e.errpt = errpt;
X return(0);
X}
X
Xquitenv()
X{
X register struct env *ep;
X register fd;
X
X if ((ep = e.oenv) != NULL) {
X fd = e.iofd;
X e = *ep;
X /* should close `'d files */
X DELETE(ep);
X while (--fd >= e.iofd)
X close(fd);
X }
X}
X
X/*
X * Is any character from s1 in s2?
X */
Xint
Xanys(s1, s2)
Xregister char *s1, *s2;
X{
X while (*s1)
X if (any(*s1++, s2))
X return(1);
X return(0);
X}
X
X/*
X * Is character c in s?
X */
Xint
Xany(c, s)
Xregister int c;
Xregister char *s;
X{
X while (*s)
X if (*s++ == c)
X return(1);
X return(0);
X}
X
Xchar *
Xputn(n)
Xregister n;
X{
X return(itoa(n, -1));
X}
X
Xchar *
Xitoa(u, n)
Xregister unsigned u;
X{
X register char *cp;
X static char s[20];
X int m;
X
X m = 0;
X if (n < 0 && (int) u < 0) {
X m++;
X u = -u;
X }
X cp = s+sizeof(s);
X *--cp = 0;
X do {
X *--cp = u%10 + '0';
X u /= 10;
X } while (--n > 0 || u);
X if (m)
X *--cp = '-';
X return(cp);
X}
X
Xnext(f)
X{
X PUSHIO(afile, f, nextchar);
X}
X
Xonintr()
X{
X signal(SIGINT, SIG_IGN);
X if (inparse) {
X prs("\n");
X fail();
X }
X intr++;
X}
X
Xletter(c)
Xregister c;
X{
X return(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
X}
X
Xdigit(c)
Xregister c;
X{
X return(c >= '0' && c <= '9');
X}
X
Xletnum(c)
Xregister c;
X{
X return(letter(c) || digit(c));
X}
X
Xchar *
Xspace(n)
Xint n;
X{
X register char *cp;
X
X inword++;
X if ((cp = getcell(n)) == 0)
X err("out of string space");
X inword--;
X return(cp);
X}
X
Xchar *
Xstrsave(s, a)
Xregister char *s;
X{
X register char *cp, *xp;
X
X if ((cp = space(strlen(s)+1)) != NULL) {
X setarea((char *)cp, a);
X for (xp = cp; (*xp++ = *s++) != '\0';)
X ;
X return(cp);
X }
X return("");
X}
X
X/*
X * if inword is set, traps
X * are delayed, avoiding
X * having two people allocating
X * at once.
X */
Xxfree(s)
Xregister char *s;
X{
X inword++;
X DELETE(s);
X inword--;
X}
X
X/*
X * trap handling
X */
Xsig(i)
Xregister i;
X{
X if (inword == 0) {
X signal(i, SIG_IGN);
X runtrap(i);
X } else
X trapset = i;
X signal(i, sig);
X}
X
Xruntrap(i)
X{
X char *trapstr;
X
X if ((trapstr = trap[i]) == NULL)
X return;
X if (i == 0)
X trap[i] = 0;
X RUN(aword, trapstr, nlchar);
X}
X
X/* -------- var.c -------- */
X/* #include "sh.h" */
X
Xstatic char *findeq();
X
X/*
X * Find the given name in the dictionary
X * and return its value. If the name was
X * not previously there, enter it now and
X * return a null value.
X */
Xstruct var *
Xlookup(n)
Xregister char *n;
X{
X register struct var *vp;
X register char *cp;
X register int c;
X static struct var dummy;
X
X if (digit(*n)) {
X dummy.name = n;
X for (c = 0; digit(*n) && c < 1000; n++)
X c = c*10 + *n-'0';
X dummy.status = RONLY;
X dummy.value = c <= dolc? dolv[c]: null;
X return(&dummy);
X }
X for (vp = vlist; vp; vp = vp->next)
X if (eqname(vp->name, n))
X return(vp);
X cp = findeq(n);
X vp = (struct var *)space(sizeof(*vp));
X if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) {
X dummy.name = dummy.value = "";
X return(&dummy);
X }
X for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++)
X ;
X if (*cp == 0)
X *cp = '=';
X *++cp = 0;
X setarea((char *)vp, 0);
X setarea((char *)vp->name, 0);
X vp->value = null;
X vp->next = vlist;
X vp->status = GETCELL;
X vlist = vp;
X return(vp);
X}
X
X/*
X * give variable at `vp' the value `val'.
X */
Xvoid
Xsetval(vp, val)
Xstruct var *vp;
Xchar *val;
X{
X nameval(vp, val, (char *)NULL);
X}
X
X/*
X * if name is not NULL, it must be
X * a prefix of the space `val',
X * and end with `='.
X * this is all so that exporting
X * values is reasonably painless.
X */
Xvoid
Xnameval(vp, val, name)
Xregister struct var *vp;
Xchar *val, *name;
X{
X register char *cp, *xp;
X char *nv;
X int fl;
X
X if (vp->status & RONLY) {
X for (xp = vp->name; *xp && *xp != '=';)
X putc(*xp++);
X err(" is read-only");
X return;
X }
X fl = 0;
X if (name == NULL) {
X xp = space(strlen(vp->name)+strlen(val)+2);
X if (xp == 0)
X return;
X /* make string: name=value */
X setarea((char *)xp, 0);
X name = xp;
X for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++)
X ;
X if (*xp++ == 0)
X xp[-1] = '=';
X nv = xp;
X for (cp = val; (*xp++ = *cp++) != '\0';)
X ;
X val = nv;
X fl = GETCELL;
X }
X if (vp->status & GETCELL)
X xfree(vp->name); /* form new string `name=value' */
X vp->name = name;
X vp->value = val;
X vp->status |= fl;
X}
X
Xvoid
Xexport(vp)
Xstruct var *vp;
X{
X vp->status |= EXPORT;
X}
X
Xvoid
Xronly(vp)
Xstruct var *vp;
X{
X if (letter(vp->name[0])) /* not an internal symbol ($# etc) */
X vp->status |= RONLY;
X}
X
Xint
Xisassign(s)
Xregister char *s;
X{
X if (!letter(*s))
X return(0);
X for (; *s != '='; s++)
X if (*s == 0 || !letnum(*s))
X return(0);
X return(1);
X}
X
Xint
Xassign(s, cf)
Xregister char *s;
Xint cf;
X{
X register char *cp;
X struct var *vp;
X
X if (!letter(*s))
X return(0);
X for (cp = s; *cp != '='; cp++)
X if (*cp == 0 || !letnum(*cp))
X return(0);
X vp = lookup(s);
X nameval(vp, ++cp, cf == COPYV? NULL: s);
X if (cf != COPYV)
X vp->status &= ~GETCELL;
X return(1);
X}
X
Xint
Xcheckname(cp)
Xregister char *cp;
X{
X if (!letter(*cp++))
X return(0);
X while (*cp)
X if (!letnum(*cp++))
X return(0);
X return(1);
X}
X
Xvoid
Xputvlist(f, out)
Xregister int f, out;
X{
X register struct var *vp;
X
X for (vp = vlist; vp; vp = vp->next)
X if (vp->status & f && letter(*vp->name)) {
X if (vp->status & EXPORT)
X write(out, "export ", 7);
X if (vp->status & RONLY)
X write(out, "readonly ", 9);
X write(out, vp->name, (int)(findeq(vp->name) - vp->name));
X write(out, "\n", 1);
X }
X}
X
Xint
Xeqname(n1, n2)
Xregister char *n1, *n2;
X{
X for (; *n1 != '=' && *n1 != 0; n1++)
X if (*n2++ != *n1)
X return(0);
X return(*n2 == 0 || *n2 == '=');
X}
X
Xstatic char *
Xfindeq(cp)
Xregister char *cp;
X{
X while (*cp != '\0' && *cp != '=')
X cp++;
X return(cp);
X}
X
X/* -------- gmatch.c -------- */
X/*
X * int gmatch(string, pattern)
X * char *string, *pattern;
X *
X * Match a pattern as in sh(1).
X */
X
X#define NULL 0
X#define CMASK 0377
X#define QUOTE 0200
X#define QMASK (CMASK&~QUOTE)
X#define NOT '!' /* might use ^ */
X
Xstatic char *cclass();
X
Xint
Xgmatch(s, p)
Xregister char *s, *p;
X{
X register int sc, pc;
X
X if (s == NULL || p == NULL)
X return(0);
X while ((pc = *p++ & CMASK) != '\0') {
X sc = *s++ & QMASK;
X switch (pc) {
X case '[':
X if ((p = cclass(p, sc)) == NULL)
X return(0);
X break;
X
X case '?':
X if (sc == 0)
X return(0);
X break;
X
X case '*':
X s--;
X do {
X if (*p == '\0' || gmatch(s, p))
X return(1);
X } while (*s++ != '\0');
X return(0);
X
X default:
X if (sc != (pc&~QUOTE))
X return(0);
X }
X }
X return(*s == 0);
X}
X
Xstatic char *
Xcclass(p, sub)
Xregister char *p;
Xregister int sub;
X{
X register int c, d, not, found;
X
X if ((not = *p == NOT) != 0)
X p++;
X found = not;
X do {
X if (*p == '\0')
X return(NULL);
X c = *p & CMASK;
X if (p[1] == '-' && p[2] != ']') {
X d = p[2] & CMASK;
X p++;
X } else
X d = c;
X if (c == sub || c <= sub && sub <= d)
X found = !not;
X } while (*++p != ']');
X return(found? p+1: NULL);
X}
X
X/* -------- area.c -------- */
X#define REGSIZE sizeof(struct region)
X#define GROWBY 256
X#undef SHRINKBY 64
X#define FREE 32767
X#define BUSY 0
X#define ALIGN (sizeof(int)-1)
X
X/* #include "area.h" */
X#define NULL 0
X
Xstruct region {
X struct region *next;
X int area;
X};
X
X/*
X * All memory between (char *)areabot and (char *)(areatop+1) is
X * exclusively administered by the area management routines.
X * It is assumed that sbrk() and brk() manipulate the high end.
X */
Xstatic struct region *areabot; /* bottom of area */
Xstatic struct region *areatop; /* top of area */
Xstatic struct region *areanxt; /* starting point of scan */
Xchar *sbrk();
Xchar *brk();
X
Xinitarea()
X{
X while ((int)sbrk(0) & ALIGN)
X sbrk(1);
X areabot = (struct region *)sbrk(REGSIZE);
X areabot->next = areabot;
X areabot->area = BUSY;
X areatop = areabot;
X areanxt = areabot;
X}
X
Xchar *
Xgetcell(nbytes)
Xunsigned nbytes;
X{
X register int nregio;
X register struct region *p, *q;
X register i;
X
X if (nbytes == 0)
X abort(); /* silly and defeats the algorithm */
X /*
X * round upwards and add administration area
X */
X nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1;
X for (p = areanxt;;) {
X if (p->area > areanum) {
X /*
X * merge free cells
X */
X while ((q = p->next)->area > areanum)
X p->next = q->next;
X /*
X * exit loop if cell big enough
X */
X if (q >= p + nregio)
X goto found;
X }
X p = p->next;
X if (p == areanxt)
X break;
X }
X i = nregio >= GROWBY ? nregio : GROWBY;
X p = (struct region *)sbrk(i * REGSIZE);
X if ((int)p == -1)
X return(NULL);
X p--;
X if (p != areatop)
X abort(); /* allocated areas are contiguous */
X q = p + i;
X p->next = q;
X p->area = FREE;
X q->next = areabot;
X q->area = BUSY;
X areatop = q;
Xfound:
X /*
X * we found a FREE area big enough, pointed to by 'p', and up to 'q'
X */
X areanxt = p + nregio;
X if (areanxt < q) {
X /*
X * split into requested area and rest
X */
X if (areanxt+1 > q)
X abort(); /* insufficient space left for admin */
X areanxt->next = q;
X areanxt->area = FREE;
X p->next = areanxt;
X }
X p->area = areanum;
X return((char *)(p+1));
X}
X
Xvoid
Xfreecell(cp)
Xchar *cp;
X{
X register struct region *p;
X
X if ((p = (struct region *)cp) != NULL) {
X p--;
X if (p < areanxt)
X areanxt = p;
X p->area = FREE;
X }
X}
X
Xvoid
Xfreearea(a)
Xregister int a;
X{
X register struct region *p, *top;
X
X top = areatop;
X for (p = areabot; p != top; p = p->next)
X if (p->area >= a)
X p->area = FREE;
X}
X
Xvoid
Xsetarea(cp,a)
Xchar *cp;
Xint a;
X{
X register struct region *p;
X
X if ((p = (struct region *)cp) != NULL)
X (p-1)->area = a;
X}
X
Xint
Xgetarea(cp)
Xchar *cp;
X{
X return ((struct region*)cp-1)->area;
X}
X
Xvoid
Xgarbage()
X{
X register struct region *p, *q, *top;
X
X top = areatop;
X for (p = areabot; p != top; p = p->next) {
X if (p->area > areanum) {
X while ((q = p->next)->area > areanum)
X p->next = q->next;
X areanxt = p;
X }
X }
X#ifdef SHRINKBY
X if (areatop >= q + SHRINKBY && q->area > areanum) {
X brk((char *)(q+1));
X q->next = areabot;
X q->area = BUSY;
X areatop = q;
X }
X#endif
X}
+ END-OF-FILE sh1.c
chmod 'u=rw,g=r,o=r' 'sh1.c'
set `wc -c 'sh1.c'`
count=$1
case $count in
14561) :;;
*) echo 'Bad character count in ''sh1.c' >&2
echo 'Count should be 14561' >&2
esac
echo Extracting 'sh2.c'
sed 's/^X//' > 'sh2.c' << '+ END-OF-FILE ''sh2.c'
X#define Extern extern
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X
X/* -------- csyn.c -------- */
X/*
X * shell: syntax (C version)
X */
X
Xtypedef union {
X char *cp;
X char **wp;
X int i;
X struct op *o;
X} YYSTYPE;
X#define WORD 256
X#define LOGAND 257
X#define LOGOR 258
X#define BREAK 259
X#define IF 260
X#define THEN 261
X#define ELSE 262
X#define ELIF 263
X#define FI 264
X#define CASE 265
X#define ESAC 266
X#define FOR 267
X#define WHILE 268
X#define UNTIL 269
X#define DO 270
X#define DONE 271
X#define IN 272
X#define YYERRCODE 300
X
X/* flags to yylex */
X#define CONTIN 01 /* skip new lines to complete command */
X
X/* #include "sh.h" */
X#define SYNTAXERR zzerr()
Xstatic int startl = 1;
Xstatic int peeksym = 0;
Xstatic void zzerr();
Xstatic void word();
Xstatic char **copyw();
Xstatic struct op *block(), *namelist(), *list(), *newtp();
Xstatic struct op *pipeline(), *andor(), *command();
Xstatic struct op *nested(), *simple(), *c_list();
Xstatic struct op *dogroup(), *thenpart(), *casepart(), *caselist();
Xstatic struct op *elsepart();
Xstatic char **wordlist(), **pattern();
Xstatic void musthave();
Xstatic int yylex();
Xstatic struct ioword *io();
Xstatic struct ioword **copyio();
Xstatic char *tree();
Xstatic void diag();
Xstatic int nlseen;
Xstatic int iounit = IODEFAULT;
Xstatic struct op *tp;
Xstruct op *newtp();
X
Xstatic YYSTYPE yylval;
X
Xint
Xyyparse()
X{
X peeksym = 0;
X yynerrs = 0;
X outtree = c_list();
X musthave('\n', 0);
X return(yynerrs!=0);
X}
X
Xstatic struct op *
Xpipeline(cf)
Xint cf;
X{
X register struct op *t, *p;
X register int c;
X
X t = command(cf);
X if (t != NULL) {
X while ((c = yylex(0)) == '|') {
X if ((p = command(CONTIN)) == NULL)
X SYNTAXERR;
X if (t->type != TPAREN && t->type != TCOM) {
X /* shell statement */
X t = block(TPAREN, t, NOBLOCK, NOWORDS);
X }
X t = block(TPIPE, t, p, NOWORDS);
X }
X peeksym = c;
X }
X return(t);
X}
X
Xstatic struct op *
Xandor()
X{
X register struct op *t, *p;
X register int c;
X
X t = pipeline(0);
X if (t != NULL) {
X while ((c = yylex(0)) == LOGAND || c == LOGOR) {
X if ((p = pipeline(CONTIN)) == NULL)
X SYNTAXERR;
X t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
X }
X peeksym = c;
X }
X return(t);
X}
X
Xstatic struct op *
Xc_list()
X{
X register struct op *t, *p;
X register int c;
X
X t = andor();
X if (t != NULL) {
X if((peeksym = yylex(0)) == '&')
X t = block(TASYNC, t, NOBLOCK, NOWORDS);
X while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') {
X if ((p = andor()) == NULL)
X return(t);
X if((peeksym = yylex(0)) == '&')
X p = block(TASYNC, p, NOBLOCK, NOWORDS);
X t = list(t, p);
X }
X peeksym = c;
X }
X return(t);
X}
X
X
Xstatic int
Xsynio(cf)
Xint cf;
X{
X register struct ioword *iop;
X register int i;
X register int c;
X
X if ((c = yylex(cf)) != '<' && c != '>') {
X peeksym = c;
X return(0);
X }
X i = yylval.i;
X musthave(WORD, 0);
X iop = io(iounit, i, yylval.cp);
X iounit = IODEFAULT;
X if (i & IOHERE)
X markhere(yylval.cp, iop);
X}
X
Xstatic void
Xmusthave(c, cf)
Xint c, cf;
X{
X if ((peeksym = yylex(cf)) != c)
X SYNTAXERR;
X peeksym = 0;
X}
X
Xstatic struct op *
Xsimple()
X{
X register struct op *t;
X
X t = NULL;
X for (;;) {
X switch (peeksym = yylex(0)) {
X case '<':
X case '>':
X (void) synio(0);
X break;
X
X case WORD:
X if (t == NULL) {
X t = newtp();
X t->type = TCOM;
X }
X peeksym = 0;
X word(yylval.cp);
X break;
X
X default:
X return(t);
X }
X }
X}
X
Xstatic struct op *
Xnested(type, mark)
Xint type, mark;
X{
X register struct op *t;
X
X multiline++;
X t = c_list();
X musthave(mark, 0);
X multiline--;
X return(block(type, t, NOBLOCK, NOWORDS));
X}
X
Xstatic struct op *
Xcommand(cf)
Xint cf;
X{
X register struct ioword *io;
X register struct op *t;
X struct wdblock *iosave;
X register int c;
X
X iosave = iolist;
X iolist = NULL;
X if (multiline)
X cf |= CONTIN;
X while (synio(cf))
X cf = 0;
X switch (c = yylex(cf)) {
X default:
X peeksym = c;
X if ((t = simple()) == NULL) {
X if (iolist == NULL)
X return(NULL);
X t = newtp();
X t->type = TCOM;
X }
X break;
X
X case '(':
X t = nested(TPAREN, ')');
X break;
X
X case '{':
X t = nested(TBRACE, '}');
X break;
X
X case FOR:
X t = newtp();
X t->type = TFOR;
X musthave(WORD, 0);
X startl = 1;
X t->str = yylval.cp;
X multiline++;
X t->words = wordlist();
X if ((c = yylex(0)) != '\n' && c != ';')
X SYNTAXERR;
X t->left = dogroup(0);
X multiline--;
X break;
X
X case WHILE:
X case UNTIL:
X multiline++;
X t = newtp();
X t->type = c == WHILE? TWHILE: TUNTIL;
X t->left = c_list();
X t->right = dogroup(1);
X t->words = NULL;
X multiline--;
X break;
X
X case CASE:
X t = newtp();
X t->type = TCASE;
X musthave(WORD, 0);
X t->str = yylval.cp;
X startl++;
X multiline++;
X musthave(IN, CONTIN);
X startl++;
X t->left = caselist();
X musthave(ESAC, 0);
X multiline--;
X break;
X
X case IF:
X multiline++;
X t = newtp();
X t->type = TIF;
X t->left = c_list();
X t->right = thenpart();
X musthave(FI, 0);
X multiline--;
X break;
X }
X while (synio(0))
X ;
X t = namelist(t);
X iolist = iosave;
X return(t);
X}
X
Xstatic struct op *
Xdogroup(onlydone)
Xint onlydone;
X{
X register int c;
X register struct op *list;
X
X c = yylex(CONTIN);
X if (c == DONE && onlydone)
X return(NULL);
X if (c != DO)
X SYNTAXERR;
X list = c_list();
X musthave(DONE, 0);
X return(list);
X}
X
Xstatic struct op *
Xthenpart()
X{
X register int c;
X register struct op *t;
X
X if ((c = yylex(0)) != THEN) {
X peeksym = c;
X return(NULL);
X }
X t = newtp();
X t->type = 0;
X t->left = c_list();
X if (t->left == NULL)
X SYNTAXERR;
X t->right = elsepart();
X return(t);
X}
X
Xstatic struct op *
Xelsepart()
X{
X register int c;
X register struct op *t;
X
X switch (c = yylex(0)) {
X case ELSE:
X if ((t = c_list()) == NULL)
X SYNTAXERR;
X return(t);
X
X case ELIF:
X t = newtp();
X t->type = TELIF;
X t->left = c_list();
X t->right = thenpart();
X return(t);
X
X default:
X peeksym = c;
X return(NULL);
X }
X}
X
Xstatic struct op *
Xcaselist()
X{
X register struct op *t;
X register int c;
X
X t = NULL;
X while ((peeksym = yylex(CONTIN)) != ESAC)
X t = list(t, casepart());
X return(t);
X}
X
Xstatic struct op *
Xcasepart()
X{
X register struct op *t;
X register int c;
X
X t = newtp();
X t->type = TPAT;
X t->words = pattern();
X musthave(')', 0);
X t->left = c_list();
X if ((peeksym = yylex(CONTIN)) != ESAC)
X musthave(BREAK, CONTIN);
X return(t);
X}
X
Xstatic char **
Xpattern()
X{
X register int c, cf;
X
X cf = CONTIN;
X do {
X musthave(WORD, cf);
X word(yylval.cp);
X cf = 0;
X } while ((c = yylex(0)) == '|');
X peeksym = c;
X word(NOWORD);
X return(copyw());
X}
X
Xstatic char **
Xwordlist()
X{
X register int c;
X
X if ((c = yylex(0)) != IN) {
X peeksym = c;
X return(NULL);
X }
X startl = 0;
X while ((c = yylex(0)) == WORD)
X word(yylval.cp);
X word(NOWORD);
X peeksym = c;
X return(copyw());
X}
X
X/*
X * supporting functions
X */
Xstatic struct op *
Xlist(t1, t2)
Xregister struct op *t1, *t2;
X{
X if (t1 == NULL)
X return(t2);
X if (t2 == NULL)
X return(t1);
X return(block(TLIST, t1, t2, NOWORDS));
X}
X
Xstatic struct op *
Xblock(type, t1, t2, wp)
Xstruct op *t1, *t2;
Xchar **wp;
X{
X register struct op *t;
X
X t = newtp();
X t->type = type;
X t->left = t1;
X t->right = t2;
X t->words = wp;
X return(t);
X}
X
Xstruct res {
X char *r_name;
X int r_val;
X} restab[] = {
X "for", FOR,
X "case", CASE,
X "esac", ESAC,
X "while", WHILE,
X "do", DO,
X "done", DONE,
X "if", IF,
X "in", IN,
X "then", THEN,
X "else", ELSE,
X "elif", ELIF,
X "until", UNTIL,
X "fi", FI,
X
X ";;", BREAK,
X "||", LOGOR,
X "&&", LOGAND,
X "{", '{',
X "}", '}',
X
X 0,
X};
X
Xrlookup(n)
Xregister char *n;
X{
X register struct res *rp;
X
X for (rp = restab; rp->r_name; rp++)
X if (strcmp(rp->r_name, n) == 0)
X return(rp->r_val);
X return(0);
X}
X
Xstatic struct op *
Xnewtp()
X{
X register struct op *t;
X
X t = (struct op *)tree(sizeof(*t));
X t->type = 0;
X t->words = NULL;
X t->ioact = NULL;
X t->left = NULL;
X t->right = NULL;
X t->str = NULL;
X return(t);
X}
X
Xstatic struct op *
Xnamelist(t)
Xregister struct op *t;
X{
X if (iolist) {
X iolist = addword((char *)NULL, iolist);
X t->ioact = copyio();
X } else
X t->ioact = NULL;
X if (t->type != TCOM) {
X if (t->type != TPAREN && t->ioact != NULL) {
X t = block(TPAREN, t, NOBLOCK, NOWORDS);
X t->ioact = t->left->ioact;
X t->left->ioact = NULL;
X }
X return(t);
X }
X word(NOWORD);
X t->words = copyw();
X return(t);
X}
X
Xstatic char **
Xcopyw()
X{
X register char **wd;
X
X wd = getwords(wdlist);
X wdlist = 0;
X return(wd);
X}
X
Xstatic void
Xword(cp)
Xchar *cp;
X{
X wdlist = addword(cp, wdlist);
X}
X
Xstatic struct ioword **
Xcopyio()
X{
X register struct ioword **iop;
X
X iop = (struct ioword **) getwords(iolist);
X iolist = 0;
X return(iop);
X}
X
Xstatic struct ioword *
Xio(u, f, cp)
Xchar *cp;
X{
X register struct ioword *iop;
X
X iop = (struct ioword *) tree(sizeof(*iop));
X iop->io_unit = u;
X iop->io_flag = f;
X iop->io_name = cp;
X iolist = addword((char *)iop, iolist);
X return(iop);
X}
X
Xstatic void
Xzzerr()
X{
X yyerror("syntax error");
X}
X
Xyyerror(s)
Xchar *s;
X{
X yynerrs++;
X if (talking) {
X if (multiline && nlseen)
X unget('\n');
X multiline = 0;
X while (yylex(0) != '\n')
X ;
X }
X err(s);
X fail();
X}
X
Xstatic int
Xyylex(cf)
Xint cf;
X{
X register int c, c1;
X int atstart;
X
X if ((c = peeksym) > 0) {
X peeksym = 0;
X if (c == '\n')
X startl = 1;
X return(c);
X }
X nlseen = 0;
X e.linep = line;
X atstart = startl;
X startl = 0;
X yylval.i = 0;
X
Xloop:
X while ((c = getc(0)) == ' ' || c == '\t')
X ;
X switch (c) {
X default:
X if (any(c, "0123456789")) {
X unget(c1 = getc(0));
X if (c1 == '<' || c1 == '>') {
X iounit = c - '0';
X goto loop;
X }
X *e.linep++ = c;
X c = c1;
X }
X break;
X
X case '#':
X while ((c = getc(0)) != 0 && c != '\n')
X ;
X unget(c);
X goto loop;
X
X case 0:
X return(c);
X
X case '$':
X *e.linep++ = c;
X if ((c = getc(0)) == '{') {
X if ((c = collect(c, '}')) != '\0')
X return(c);
X goto pack;
X }
X break;
X
X case '`':
X case '\'':
X case '"':
X if ((c = collect(c, c)) != '\0')
X return(c);
X goto pack;
X
X case '|':
X case '&':
X case ';':
X if ((c1 = dual(c)) != '\0') {
X startl = 1;
X return(c1);
X }
X startl = 1;
X return(c);
X case '^':
X startl = 1;
X return('|');
X case '>':
X case '<':
X diag(c);
X return(c);
X
X case '\n':
X nlseen++;
X gethere();
X startl = 1;
X if (multiline || cf & CONTIN) {
X if (talking && e.iop <= iostack)
X prs(cprompt->value);
X if (cf & CONTIN)
X goto loop;
X }
X return(c);
X
X case '(':
X case ')':
X startl = 1;
X return(c);
X }
X
X unget(c);
X
Xpack:
X while ((c = getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n"))
X if (e.linep >= elinep)
X err("word too long");
X else
X *e.linep++ = c;
X unget(c);
X if(any(c, "\"'`$"))
X goto loop;
X *e.linep++ = '\0';
X if (atstart && (c = rlookup(line))!=0) {
X startl = 1;
X return(c);
X }
X yylval.cp = strsave(line, areanum);
X return(WORD);
X}
X
Xint
Xcollect(c, c1)
Xregister c, c1;
X{
X char s[2];
X
X *e.linep++ = c;
X while ((c = getc(c1)) != c1) {
X if (c == 0) {
X unget(c);
X s[0] = c1;
X s[1] = 0;
X prs("no closing "); yyerror(s);
X return(YYERRCODE);
X }
X if (talking && c == '\n' && e.iop <= iostack)
X prs(cprompt->value);
X *e.linep++ = c;
X }
X *e.linep++ = c;
X return(0);
X}
X
Xint
Xdual(c)
Xregister c;
X{
X char s[3];
X register char *cp = s;
X
X *cp++ = c;
X *cp++ = getc(0);
X *cp = 0;
X if ((c = rlookup(s)) == 0)
X unget(*--cp);
X return(c);
X}
X
Xstatic void
Xdiag(ec)
Xregister int ec;
X{
X register int c;
X
X c = getc(0);
X if (c == '>' || c == '<') {
X if (c != ec)
X zzerr();
X yylval.i = ec == '>'? IOWRITE|IOCAT: IOHERE;
X c = getc(0);
X } else
X yylval.i = ec == '>'? IOWRITE: IOREAD;
X if (c != '&' || yylval.i == IOHERE)
X unget(c);
X else
X yylval.i |= IODUP;
X}
X
Xstatic char *
Xtree(size)
Xunsigned size;
X{
X register char *t;
X
X if ((t = getcell(size)) == NULL) {
X prs("command line too complicated\n");
X fail();
X /* NOTREACHED */
X }
X return(t);
X}
X
X/* VARARGS1 */
X/* ARGSUSED */
Xprintf(s) /* yyparse calls it */
Xchar *s;
X{
X}
X
+ END-OF-FILE sh2.c
chmod 'u=rw,g=r,o=r' 'sh2.c'
set `wc -c 'sh2.c'`
count=$1
case $count in
11571) :;;
*) echo 'Bad character count in ''sh2.c' >&2
echo 'Count should be 11571' >&2
esac
exit 0