Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!seismo!mcvax!botter!ast
From: a...@cs.vu.nl (Andy Tanenbaum)
Newsgroups: comp.os.minix
Subject: MINIX 1.2 shell (part 1 of 2)
Message-ID: <1554@botter.cs.vu.nl>
Date: Wed, 5-Aug-87 11:16:36 EDT
Article-I.D.: botter.1554
Posted: Wed Aug 5 11:16:36 1987
Date-Received: Sat, 8-Aug-87 08:52:11 EDT
Reply-To: a...@cs.vu.nl (Andy Tanenbaum)
Distribution: world
Organization: VU Informatica, Amsterdam
Lines: 2145
Here is the 1.2 shell in 2 parts. It has a number of improvements over the
1.1 shell, and should be fairly close to the shell to be used on the Atari
MINIX system.
I have just noticed a bug in it that I can't find. The bug may well also
have been in the 1.1 shell, but I can't check it out because 1.1 doesn't
run on the hard disk I used to test 1.2. Anyway, I made a shell script
'doit' consisting of:
ck /usr/ast/minix/kernel
ck /usr/ast/minix/kernel/MINIX
ck /usr/ast/minix/lib
...
In short, ck is run with each directory on the hard disk as an argument.
The shell script 'ck' is:
ls -l * */* >/dev/null 2>&1
Finally, from the console I started the command
while true
do sh -v doit
done >log 2>&1
This command generates quite a few shells floating around. It runs fine
for a few minutes, but when doit hits the line containing ck /usr/ast/minix/lib
it hangs. You can't kill it with DEL or CTRL-\. F1 works, and from looking
at several samples, the shell seems to be looping in getcell or sometimes in
execve. I suspect that it ran out of memory somehow. When I chmem'd from the
standard 8000 bytes to 16000, the problem remained.
After trying this test several times and having to reboot after each test, I
made a small change to tty.c and mm/signal.c. Now the F9 key does the
equivalent of the super-user typing: kill -1 9. In other words, it sends a
signal 9 to every process in the system. While this is definitely a sledge
hammer approach, it de-hangs the system instantly. It also kills your shell
and logs you out. So be it. I would prefer to have found the bug in the
shell, but not having done so, this will have to do for the time being.
The F9 key will be in 1.2, which I am almost done with. I will post the
definitive diff listing between 1.1 and 1.2 in perhaps a week. If anyone
can reproduce, find, and fix the shell bug, please post it.
Andy Tanenbaum
------------------------------MINIX 1.2 shell (part 1 of 2) -------------
: 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
echo Extracting \m\a\k\e\f\i\l\e
sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e
XCFLAGS=-F
Xl=../../lib
X
Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s
Xsh: $(shobj) sh.h
X @asld -i -o sh -T. $l/crtso.s $(shobj) $l/libc.a $l/end.s
X @chmem =8000 sh
X
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e
set `sum \m\a\k\e\f\i\l\e`
sum=$1
case $sum in
45952) :;;
*) echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2
esac
echo Extracting \s\h\.\h
sed 's/^X//' > \s\h\.\h << '+ END-OF-FILE '\s\h\.\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 union {
X char *io_name; /* file name */
X struct block *io_here; /* here structure pointer */
X } io_un;
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 4 /* file IO */
X#define XHERE 0x80 /* Any of the above inside a here document */
X#define XMASK 0x7f /* Get the actual task */
X
X/* in substitution */
X#define INSUB() ((e.iop->task&XMASK)==XGRAVE||(e.iop->task&XMASK)==XDOLL)
X
X/*
X * input generators for IO structure
X */
Xint nlchar();
Xint strchar();
Xint filechar();
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 */);
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' \s\h\.\h
set `sum \s\h\.\h`
sum=$1
case $sum in
29038) :;;
*) echo 'Bad sum in '\s\h\.\h >&2
esac
echo Extracting \s\h\1\.\c
sed 's/^X//' > \s\h\1\.\c << '+ END-OF-FILE '\s\h\1\.\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[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-1));
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 freearea(areanum = 1);
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 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
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' \s\h\1\.\c
set `sum \s\h\1\.\c`
sum=$1
case $sum in
04453) :;;
*) echo 'Bad sum in '\s\h\1\.\c >&2
esac
echo Extracting \s\h\2\.\c
sed 's/^X//' > \s\h\2\.\c << '+ END-OF-FILE '\s\h\2\.\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 while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') {
X if (c == '&')
X t = block(TASYNC, t, NOBLOCK, NOWORDS);
X if ((p = andor()) == NULL)
X return(t);
X t = list(t, p);
X }
X peeksym = c;
X }
X return(t);
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_un.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' \s\h\2\.\c
set `sum \s\h\2\.\c`
sum=$1
case $sum in
34787) :;;
*) echo 'Bad sum in '\s\h\2\.\c >&2
esac
exit 0
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!husc6!seismo!mcvax!botter!ast
From: a...@cs.vu.nl (Andy Tanenbaum)
Newsgroups: comp.os.minix
Subject: MINIX 1.2 shell (part 2 of 2: instant replay)
Message-ID: <1567@botter.cs.vu.nl>
Date: Mon, 10-Aug-87 07:17:19 EDT
Article-I.D.: botter.1567
Posted: Mon Aug 10 07:17:19 1987
Date-Received: Tue, 11-Aug-87 02:06:46 EDT
Reply-To: a...@cs.vu.nl (Andy Tanenbaum)
Distribution: world
Organization: VU Informatica, Amsterdam
Lines: 2484
: 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
echo Extracting \s\h\3\.\c
sed 's/^X//' > \s\h\3\.\c << '+ END-OF-FILE '\s\h\3\.\c
X#define Extern extern
X#include "signal.h"
X#include "errno.h"
X#include "setjmp.h"
X#include "sh.h"
X
X/* -------- exec.c -------- */
X/* #include "sh.h" */
X
X/*
X * execute tree
X */
X
Xstatic char *signame[] = {
X "Signal 0",
X "Hangup",
X NULL, /* interrupt */
X "Quit",
X "Illegal instruction",
X "Trace/BPT trap",
X "abort",
X "EMT trap",
X "Floating exception",
X "Killed",
X "Bus error",
X "Memory fault",
X "Bad system call",
X NULL, /* broken pipe */
X "Alarm clock",
X "Terminated",
X};
X#define NSIGNAL (sizeof(signame)/sizeof(signame[0]))
X
Xstatic struct op *findcase();
Xstatic void brkset();
Xstatic void echo();
Xstatic int forkexec();
Xstatic int parent();
X
Xint
Xexecute(t, pin, pout, act)
Xregister struct op *t;
Xint *pin, *pout;
Xint act;
X{
X register struct op *t1;
X int i, pv[2], rv, child, a;
X char *cp, **wp, **wp2;
X struct var *vp;
X struct brkcon bc;
X
X if (t == NULL)
X return(0);
X rv = 0;
X a = areanum++;
X wp = (wp2 = t->words) != NULL? eval(wp2, DOALL): NULL;
X
X switch(t->type) {
X case TPAREN:
X case TCOM:
X rv = forkexec(t, pin, pout, act, wp, &child);
X if (child) {
X exstat = rv;
X leave();
X }
X break;
X
X case TPIPE:
X if ((rv = openpipe(pv)) < 0)
X break;
X pv[0] = remap(pv[0]);
X pv[1] = remap(pv[1]);
X (void) execute(t->left, pin, pv, 0);
X rv = execute(t->right, pv, pout, 0);
X break;
X
X case TLIST:
X (void) execute(t->left, pin, pout, 0);
X rv = execute(t->right, pin, pout, 0);
X break;
X
X case TASYNC:
X i = parent();
X if (i != 0) {
X if (i != -1) {
X if (pin != NULL)
X closepipe(pin);
X if (talking) {
X prs(putn(i));
X prs("\n");
X }
X } else
X rv = -1;
X setstatus(rv);
X } else {
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X if (talking)
X signal(SIGTERM, SIG_DFL);
X talking = 0;
X if (pin == NULL) {
X close(0);
X open("/dev/null", 0);
X }
X exit(execute(t->left, pin, pout, FEXEC));
X }
X break;
X
X case TOR:
X case TAND:
X rv = execute(t->left, pin, pout, 0);
X if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND))
X rv = execute(t1, pin, pout, 0);
X break;
X
X case TFOR:
X if (wp == NULL) {
X wp = dolv+1;
X if ((i = dolc-1) < 0)
X i = 0;
X } else
X i = -1;
X vp = lookup(t->str);
X while (setjmp(bc.brkpt))
X if (isbreak)
X goto broken;
X brkset(&bc);
X for (t1 = t->left; i-- && *wp != NULL;) {
X setval(vp, *wp++);
X rv = execute(t1, pin, pout, 0);
X }
X brklist = brklist->nextlev;
X break;
X
X case TWHILE:
X case TUNTIL:
X while (setjmp(bc.brkpt))
X if (isbreak)
X goto broken;
X brkset(&bc);
X t1 = t->left;
X while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
X rv = execute(t->right, pin, pout, 0);
X brklist = brklist->nextlev;
X break;
X
X case TIF:
X case TELIF:
X rv = !execute(t->left, pin, pout, 0)?
X execute(t->right->left, pin, pout, 0):
X execute(t->right->right, pin, pout, 0);
X break;
X
X case TCASE:
X if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0)
X cp = "";
X if ((t1 = findcase(t->left, cp)) != NULL)
X rv = execute(t1, pin, pout, 0);
X break;
X
X case TBRACE:
X/*
X if (iopp = t->ioact)
X while (*iopp)
X if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
X rv = -1;
X break;
X }
X*/
X if (rv >= 0 && (t1 = t->left))
X rv = execute(t1, pin, pout, 0);
X break;
X }
X
Xbroken:
X t->words = wp2;
X isbreak = 0;
X freearea(areanum);
X areanum = a;
X if (intr) {
X closeall();
X fail();
X }
X return(rv);
X}
X
Xstatic int
Xforkexec(t, pin, pout, act, wp, pforked)
Xregister struct op *t;
Xint *pin, *pout;
Xint act;
Xchar **wp;
Xint *pforked;
X{
X int i, rv, (*shcom)();
X int doexec();
X register int f;
X char *cp;
X struct ioword **iopp;
X int resetsig;
X
X resetsig = 0;
X *pforked = 0;
X shcom = NULL;
X rv = -1; /* system-detected error */
X if (t->type == TCOM) {
X /* strip all initial assignments */
X /* not correct wrt PATH=yyy command etc */
X if (flag['x'])
X echo(wp);
X while ((cp = *wp++) != NULL && assign(cp, COPYV))
X ;
X wp--;
X if (cp == NULL && t->ioact == NULL)
X return(setstatus(0));
X else
X shcom = inbuilt(cp);
X }
X t->words = wp;
X f = act;
X if (shcom == NULL && (f & FEXEC) == 0) {
X i = parent();
X if (i != 0) {
X if (i == -1)
X return(rv);
X if (pin != NULL)
X closepipe(pin);
X return(pout==NULL? setstatus(waitfor(i,0)): 0);
X }
X if (talking) {
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X resetsig = 1;
X }
X talking = 0;
X intr = 0;
X (*pforked)++;
X brklist = 0;
X execflg = 0;
X }
X#ifdef COMPIPE
X if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
X err("piping to/from shell builtins not yet done");
X return(-1);
X }
X#endif
X if (pin != NULL) {
X dup2(pin[0], 0);
X closepipe(pin);
X }
X if (pout != NULL) {
X dup2(pout[1], 1);
X closepipe(pout);
X }
X if ((iopp = t->ioact) != NULL) {
X if (shcom != NULL && shcom != doexec) {
X prs(cp);
X err(": cannot redirect shell command");
X return(-1);
X }
X while (*iopp)
X if (iosetup(*iopp++, pin!=NULL, pout!=NULL))
X return(rv);
X }
X if (shcom)
X return(setstatus((*shcom)(t)));
X /* should use FIOCEXCL */
X for (i=FDBASE; i<NOFILE; i++)
X close(i);
X if (t->type == TPAREN)
X exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
X if (resetsig) {
X signal(SIGINT, SIG_DFL);
X signal(SIGQUIT, SIG_DFL);
X }
X if (wp[0] == NULL)
X exit(0);
X cp = rexecve(wp[0], wp, makenv(wp));
X prs(wp[0]); prs(": "); warn(cp);
X if (!execflg)
X trap[0] = NULL;
X leave();
X /* NOTREACHED */
X}
X
X/*
X * common actions when creating a new child
X */
Xstatic int
Xparent()
X{
X register int i;
X
X i = fork();
X if (i != 0) {
X if (i == -1)
X warn("try again");
X setval(lookup("!"), putn(i));
X }
X return(i);
X}
X
X/*
X * 0< 1> are ignored as required
X * within pipelines.
X */
Xiosetup(iop, pipein, pipeout)
Xregister struct ioword *iop;
Xint pipein, pipeout;
X{
X register u;
X char *cp, *msg;
X
X if (iop->io_unit == IODEFAULT) /* take default */
X iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1;
X if (pipein && iop->io_unit == 0)
X return(0);
X if (pipeout && iop->io_unit == 1)
X return(0);
X msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create";
X if ((iop->io_flag & IOHERE) == 0) {
X cp = iop->io_un.io_name;
X if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL)
X return(1);
X }
X if (iop->io_flag & IODUP) {
X if (cp[1] || !digit(*cp) && *cp != '-') {
X prs(cp);
X err(": illegal >& argument");
X return(1);
X }
X if (*cp == '-')
X iop->io_flag = IOCLOSE;
X iop->io_flag &= ~(IOREAD|IOWRITE);
X }
X switch (iop->io_flag) {
X case IOREAD:
X u = open(cp, 0);
X break;
X
X case IOHERE:
X case IOHERE|IOXHERE:
X u = herein(iop->io_un.io_here, iop->io_flag&IOXHERE);
X cp = "here file";
X break;
X
X case IOWRITE|IOCAT:
X if ((u = open(cp, 1)) >= 0) {
X lseek(u, (long)0, 2);
X break;
X }
X case IOWRITE:
X u = creat(cp, 0666);
X break;
X
X case IODUP:
X u = dup2(*cp-'0', iop->io_unit);
X break;
X
X case IOCLOSE:
X close(iop->io_unit);
X return(0);
X }
X if (u < 0) {
X prs(cp);
X prs(": cannot ");
X warn(msg);
X return(1);
X } else {
X if (u != iop->io_unit) {
X dup2(u, iop->io_unit);
X close(u);
X }
X }
X return(0);
X}
X
Xstatic void
Xecho(wp)
Xregister char **wp;
X{
X register i;
X
X prs("+");
X for (i=0; wp[i]; i++) {
X if (i)
X prs(" ");
X prs(wp[i]);
X }
X prs("\n");
X}
X
Xstatic struct op **
Xfind1case(t, w)
Xstruct op *t;
Xchar *w;
X{
X register struct op *t1;
X struct op **tp;
X register char **wp, *cp;
X
X if (t == NULL)
X return(NULL);
X if (t->type == TLIST) {
X if ((tp = find1case(t->left, w)) != NULL)
X return(tp);
X t1 = t->right; /* TPAT */
X } else
X t1 = t;
X for (wp = t1->words; *wp;)
X if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp))
X return(&t1->left);
X return(NULL);
X}
X
Xstatic struct op *
Xfindcase(t, w)
Xstruct op *t;
Xchar *w;
X{
X register struct op **tp;
X
X return((tp = find1case(t, w)) != NULL? *tp: NULL);
X}
X
X/*
X * Enter a new loop level (marked for break/continue).
X */
Xstatic void
Xbrkset(bc)
Xstruct brkcon *bc;
X{
X bc->nextlev = brklist;
X brklist = bc;
X}
X
X/*
X * Wait for the last process created.
X * Print a message for each process found
X * that was killed by a signal.
X * Ignore interrupt signals while waiting
X * unless `canintr' is true.
X */
Xint
Xwaitfor(lastpid, canintr)
Xregister int lastpid;
Xint canintr;
X{
X register int pid, rv;
X int s;
X
X rv = 0;
X do {
X pid = wait(&s);
X if (pid == -1) {
X if (errno != EINTR || canintr)
X break;
X } else {
X if ((rv = WAITSIG(s)) != 0) {
X if (rv < NSIGNAL) {
X if (signame[rv] != NULL) {
X if (pid != lastpid) {
X prn(pid);
X prs(": ");
X }
X prs(signame[rv]);
X }
X } else {
X if (pid != lastpid) {
X prn(pid);
X prs(": ");
X }
X prs("Signal "); prn(rv); prs(" ");
X }
X if (WAITCORE(s))
X prs(" - core dumped");
X prs("\n");
X rv = -1;
X } else
X rv = WAITVAL(s);
X }
X/* Special patch for MINIX: sync before each command */
X sync();
X } while (pid != lastpid);
X return(rv);
X}
X
Xint
Xsetstatus(s)
Xregister int s;
X{
X exstat = s;
X setval(lookup("?"), putn(s));
X return(s);
X}
X
X/*
X * PATH-searching interface to execve.
X * If getenv("PATH") were kept up-to-date,
X * execvp might be used.
X */
Xchar *
Xrexecve(c, v, envp)
Xchar *c, **v, **envp;
X{
X register int i;
X register char *sp, *tp;
X int eacces = 0, asis = 0;
X extern int errno;
X
X sp = any('/', c)? "": path->value;
X asis = *sp == '\0';
X while (asis || *sp != '\0') {
X asis = 0;
X tp = e.linep;
X for (; *sp != '\0'; tp++)
X if ((*tp = *sp++) == ':') {
X asis = *sp == '\0';
X break;
X }
X if (tp != e.linep)
X *tp++ = '/';
X for (i = 0; (*tp++ = c[i++]) != '\0';)
X ;
X execve(e.linep, v, envp);
X switch (errno) {
X case ENOEXEC:
X *v = e.linep;
X tp = *--v;
X *v = e.linep;
X execve("/bin/sh", v, envp);
X *v = tp;
X return("no Shell");
X
X case ENOMEM:
X return("program too big");
X
X case E2BIG:
X return("argument list too long");
X
X case EACCES:
X eacces++;
X break;
X }
X }
X return(errno==ENOENT ? "not found" : "cannot execute");
X}
X
X/*
X * Run the command produced by generator `f'
X * applied to stream `arg'.
X */
Xrun(arg, f)
Xstruct ioarg arg;
Xint (*f)();
X{
X struct op *otree;
X struct wdblock *swdlist;
X struct wdblock *siolist;
X jmp_buf ev, rt;
X xint *ofail;
X int rv;
X
X areanum++;
X swdlist = wdlist;
X siolist = iolist;
X otree = outtree;
X ofail = failpt;
X rv = -1;
X if (newenv(setjmp(errpt = ev)) == 0) {
X wdlist = 0;
X iolist = 0;
X pushio(arg, f);
X e.iobase = e.iop;
X yynerrs = 0;
X if (setjmp(failpt = rt) == 0 && yyparse() == 0)
X rv = execute(outtree, NOPIPE, NOPIPE, 0);
X quitenv();
X }
X wdlist = swdlist;
X iolist = siolist;
X failpt = ofail;
X outtree = otree;
X freearea(areanum--);
X return(rv);
X}
X
X/* -------- do.c -------- */
X/* #include "sh.h" */
X
X/*
X * built-in commands: doX
X */
X
Xstatic void rdexp();
Xstatic void badid();
Xstatic int brkcontin();
X
Xdolabel()
X{
X return(0);
X}
X
Xdochdir(t)
Xregister struct op *t;
X{
X register char *cp, *er;
X
X if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL)
X er = ": no home directory";
X else if(chdir(cp) < 0)
X er = ": bad directory";
X else
X return(0);
X prs(cp != NULL? cp: "cd");
X err(er);
X return(1);
X}
X
Xdoshift(t)
Xregister struct op *t;
X{
X register n;
X
X n = t->words[1]? getn(t->words[1]): 1;
X if(dolc < n) {
X err("nothing to shift");
X return(1);
X }
X dolv[n] = dolv[0];
X dolv += n;
X dolc -= n;
X setval(lookup("#"), putn(dolc));
X return(0);
X}
X
X/*
X * execute login and newgrp directly
X */
Xdologin(t)
Xstruct op *t;
X{
X register char *cp;
X
X if (talking) {
X signal(SIGINT, SIG_DFL);
X signal(SIGQUIT, SIG_DFL);
X }
X cp = rexecve(t->words[0], t->words, makenv(t->words));
X prs(t->words[0]); prs(": "); err(cp);
X return(1);
X}
X
Xdoumask(t)
Xregister struct op *t;
X{
X register int i, n;
X register char *cp;
X
X if ((cp = t->words[1]) == NULL) {
X i = umask(0);
X umask(i);
X for (n=3*4; (n-=3) >= 0;)
X putc('0'+((i>>n)&07));
X putc('\n');
X } else {
X for (n=0; *cp>='0' && *cp<='9'; cp++)
X n = n*8 + (*cp-'0');
X umask(n);
X }
X return(0);
X}
X
Xdoexec(t)
Xregister struct op *t;
X{
X register i;
X jmp_buf ex;
X xint *ofail;
X
X t->ioact = NULL;
X for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++)
X ;
X if (i == 0)
X return(1);
X execflg = 1;
X ofail = failpt;
X if (setjmp(failpt = ex) == 0)
X execute(t, NOPIPE, NOPIPE, FEXEC);
X failpt = ofail;
X execflg = 0;
X return(1);
X}
X
Xdodot(t)
Xstruct op *t;
X{
X register i;
X register char *sp, *tp;
X char *cp;
X
X if ((cp = t->words[1]) == NULL)
X return(0);
X sp = any('/', cp)? ":": path->value;
X while (*sp) {
X tp = e.linep;
X while (*sp && (*tp = *sp++) != ':')
X tp++;
X if (tp != e.linep)
X *tp++ = '/';
X for (i = 0; (*tp++ = cp[i++]) != '\0';)
X ;
X if ((i = open(e.linep, 0)) >= 0) {
X exstat = 0;
X next(remap(i));
X return(exstat);
X }
X }
X prs(cp);
X err(": not found");
X return(-1);
X}
X
Xdowait(t)
Xstruct op *t;
X{
X register i;
X register char *cp;
X
X if ((cp = t->words[1]) != NULL) {
X i = getn(cp);
X if (i == 0)
X return(0);
X } else
X i = -1;
X if (talking)
X signal(SIGINT, onintr);
X setstatus(waitfor(i, 1));
X if (talking)
X signal(SIGINT, SIG_IGN);
X return(0);
X}
X
Xdoread(t)
Xstruct op *t;
X{
X register char *cp, **wp;
X register nb;
X
X if (t->words[1] == NULL) {
X err("Usage: read name ...");
X return(1);
X }
X for (wp = t->words+1; *wp; wp++) {
X for (cp = e.linep; cp < elinep-1; cp++)
X if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
X *cp == '\n' ||
X wp[1] && any(*cp, ifs->value))
X break;
X *cp = 0;
X if (nb <= 0)
X break;
X setval(lookup(*wp), e.linep);
X }
X return(nb <= 0);
X}
X
Xdoeval(t)
Xregister struct op *t;
X{
X int wdchar();
X
X return(RUN(awordlist, t->words+1, wdchar));
X}
X
Xdotrap(t)
Xregister struct op *t;
X{
X register char *s;
X register n, i;
X
X if (t->words[1] == NULL) {
X for (i=0; i<NSIG; i++)
X if (trap[i]) {
X prn(i);
X prs(": ");
X prs(trap[i]);
X prs("\n");
X }
X return(0);
X }
X n = getsig((s = t->words[2])!=NULL? s: t->words[1]);
X xfree(trap[n]);
X trap[n] = 0;
X if (s != NULL) {
X if ((i = strlen(s = t->words[1])) != 0) {
X trap[n] = strsave(s, 0);
X setsig(n, sig);
X } else
X setsig(n, SIG_IGN);
X } else
X setsig(n, (n == SIGINT || n == SIGQUIT) && talking? SIG_IGN: SIG_DFL);
X return(0);
X}
X
Xgetsig(s)
Xchar *s;
X{
X register int n;
X
X if ((n = getn(s)) < 0 || n >= NSIG) {
X err("trap: bad signal number");
X n = 0;
X }
X return(n);
X}
X
Xsetsig(n, f)
Xregister n;
Xint (*f)();
X{
X if (n == 0)
X return;
X if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
X ourtrap[n] = 1;
X signal(n, f);
X }
X}
X
Xgetn(as)
Xchar *as;
X{
X register char *s;
X register n, m;
X
X s = as;
X m = 1;
X if (*s == '-') {
X m = -1;
X s++;
X }
X for (n = 0; digit(*s); s++)
X n = (n*10) + (*s-'0');
X if (*s) {
X prs(as);
X err(": bad number");
X }
X return(n*m);
X}
X
Xdobreak(t)
Xstruct op *t;
X{
X return(brkcontin(t->words[1], 1));
X}
X
Xdocontinue(t)
Xstruct op *t;
X{
X return(brkcontin(t->words[1], 0));
X}
X
Xstatic int
Xbrkcontin(cp, val)
Xregister char *cp;
X{
X register struct brkcon *bc;
X register nl;
X
X nl = cp == NULL? 1: getn(cp);
X if (nl <= 0)
X nl = 999;
X do {
X if ((bc = brklist) == NULL)
X break;
X brklist = bc->nextlev;
X } while (--nl);
X if (nl) {
X err("bad break/continue level");
X return(1);
X }
X isbreak = val;
X longjmp(bc->brkpt, 1);
X /* NOTREACHED */
X}
X
Xdoexit(t)
Xstruct op *t;
X{
X register char *cp;
X
X execflg = 0;
X if ((cp = t->words[1]) != NULL)
X exstat = getn(cp);
X leave();
X}
X
Xdoexport(t)
Xstruct op *t;
X{
X rdexp(t->words+1, export, EXPORT);
X return(0);
X}
X
Xdoreadonly(t)
Xstruct op *t;
X{
X rdexp(t->words+1, ronly, RONLY);
X return(0);
X}
X
Xstatic void
Xrdexp(wp, f, key)
Xregister char **wp;
Xvoid (*f)();
Xint key;
X{
X if (*wp != NULL) {
X for (; *wp != NULL; wp++)
X if (checkname(*wp))
X (*f)(lookup(*wp));
X else
X badid(*wp);
X } else
X putvlist(key, 1);
X}
X
Xstatic void
Xbadid(s)
Xregister char *s;
X{
X prs(s);
X err(": bad identifier");
X}
X
Xdoset(t)
Xregister struct op *t;
X{
X register struct var *vp;
X register char *cp;
X register n;
X
X if ((cp = t->words[1]) == NULL) {
X for (vp = vlist; vp; vp = vp->next)
X varput(vp->name, 1);
X return(0);
X }
X if (*cp == '-') {
X t->words++;
X if (*++cp == 0)
X flag['x'] = flag['v'] = 0;
X else
X for (; *cp; cp++)
X switch (*cp) {
X case 'e':
X if (!talking)
X flag['e']++;
X break;
X
X default:
X if (*cp>='a' && *cp<='z')
X flag[*cp]++;
X break;
X }
X setdash();
X }
X if (t->words[1]) {
X t->words[0] = dolv[0];
X for (n=1; t->words[n]; n++)
X setarea((char *)t->words[n], 0);
X dolc = n-1;
X dolv = t->words;
X setval(lookup("#"), putn(dolc));
X setarea((char *)(dolv-1), 0);
X }
X return(0);
X}
X
Xvarput(s, out)
Xregister char *s;
X{
X if (letnum(*s)) {
X write(out, s, strlen(s));
X write(out, "\n", 1);
X }
X}
X
X
Xstruct builtin {
X char *command;
X int (*fn)();
X};
Xstatic struct builtin builtin[] = {
X ":", dolabel,
X "cd", dochdir,
X "shift", doshift,
X "exec", doexec,
X "wait", dowait,
X "read", doread,
X "eval", doeval,
X "trap", dotrap,
X "break", dobreak,
X "continue", docontinue,
X "exit", doexit,
X "export", doexport,
X "readonly", doreadonly,
X "set", doset,
X ".", dodot,
X "umask", doumask,
X "login", dologin,
X "newgrp", dologin,
X 0,
X};
X
Xint (*inbuilt(s))()
Xregister char *s;
X{
X register struct builtin *bp;
X
X for (bp = builtin; bp->command != NULL; bp++)
X if (strcmp(bp->command, s) == 0)
X return(bp->fn);
X return(NULL);
X}
X
+ END-OF-FILE sh3.c
chmod 'u=rw,g=r,o=r' \s\h\3\.\c
set `sum \s\h\3\.\c`
sum=$1
case $sum in
63046) :;;
*) echo 'Bad sum in '\s\h\3\.\c >&2
esac
echo Extracting \s\h\4\.\c
sed 's/^X//' > \s\h\4\.\c << '+ END-OF-FILE '\s\h\4\.\c
X#define Extern extern
X#include "signal.h"
X#include "errno.h"
X#include "setjmp.h"
X#include "stat.h"
X#include "sh.h"
X
X/* -------- eval.c -------- */
X/* #include "sh.h" */
X/* #include "word.h" */
X
X/*
X * ${}
X * `command`
X * blank interpretation
X * quoting
X * glob
X */
X
Xstatic char *blank();
Xstatic int grave();
Xstatic int expand();
Xstatic int dollar();
X
Xchar **
Xeval(ap, f)
Xregister char **ap;
X{
X struct wdblock *wb;
X char **wp;
X jmp_buf ev;
X
X inword++;
X wp = NULL;
X wb = NULL;
X if (newenv(setjmp(errpt = ev)) == 0) {
X wb = addword((char *)0, wb); /* space for shell name, if command file */
X while (expand(*ap++, &wb, f))
X ;
X wb = addword((char *)0, wb);
X wp = getwords(wb) + 1;
X quitenv();
X } else
X gflg = 1;
X inword--;
X return(gflg? NULL: wp);
X}
X
X/*
X * Make the exported environment from the exported
X * names in the dictionary. Keyword assignments
X * ought to be taken from wp (the list of words on the command line)
X * but aren't, yet. Until then: ARGSUSED
X */
Xchar **
Xmakenv(wp)
Xchar **wp;
X{
X register struct wdblock *wb;
X register struct var *vp;
X
X wb = NULL;
X for (vp = vlist; vp; vp = vp->next)
X if (vp->status & EXPORT)
X wb = addword(vp->name, wb);
X wb = addword((char *)0, wb);
X return(getwords(wb));
X}
X
Xchar *
Xevalstr(cp, f)
Xregister char *cp;
Xint f;
X{
X struct wdblock *wb;
X
X inword++;
X wb = NULL;
X if (expand(cp, &wb, f)) {
X if (wb == NULL || wb->w_nword == 0 || (cp = wb->w_words[0]) == NULL)
X cp = "";
X DELETE(wb);
X } else
X cp = NULL;
X inword--;
X return(cp);
X}
X
Xstatic int
Xexpand(cp, wbp, f)
Xregister char *cp;
Xregister struct wdblock **wbp;
X{
X jmp_buf ev;
X
X gflg = 0;
X if (cp == NULL)
X return(0);
X if (!anys("$`'\"", cp) &&
X !anys(ifs->value, cp) &&
X ((f&DOGLOB)==0 || !anys("[*?", cp))) {
X cp = strsave(cp, areanum);
X if (f & DOTRIM)
X unquote(cp);
X *wbp = addword(cp, *wbp);
X return(1);
X }
X if (newenv(setjmp(errpt = ev)) == 0) {
X PUSHIO(aword, cp, strchar);
X e.iobase = e.iop;
X while ((cp = blank(f)) && gflg == 0) {
X e.linep = cp;
X cp = strsave(cp, areanum);
X if ((f&DOGLOB) == 0) {
X if (f & DOTRIM)
X unquote(cp);
X *wbp = addword(cp, *wbp);
X } else
X *wbp = glob(cp, *wbp);
X }
X quitenv();
X } else
X gflg = 1;
X return(gflg == 0);
X}
X
X/*
X * Blank interpretation and quoting
X */
Xstatic char *
Xblank(f)
X{
X register c, c1;
X register char *sp;
X
X sp = e.linep;
X
Xloop:
X switch (c = subgetc('"', 0)) {
X case 0:
X if (sp == e.linep)
X return(0);
X *e.linep++ = 0;
X return(sp);
X
X default:
X if (f & DOBLANK && any(c, ifs->value))
X goto loop;
X break;
X
X case '"':
X case '\'':
X if (INSUB())
X break;
X for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
X if (c == 0)
X break;
X if (c == '\'' || !any(c, "$`\""))
X c |= QUOTE;
X *e.linep++ = c;
X }
X c = 0;
X }
X unget(c);
X for (;;) {
X c = subgetc('"', 0);
X if (c == 0 ||
X f & DOBLANK && any(c, ifs->value) ||
X !INSUB() && any(c, "\"'`")) {
X unget(c);
X if (any(c, "\"'`"))
X goto loop;
X break;
X }
X *e.linep++ = c;
X }
X *e.linep++ = 0;
X return(sp);
X}
X
X/*
X * Get characters, substituting for ` and $
X */
Xint
Xsubgetc(ec, quoted)
Xregister char ec;
Xint quoted;
X{
X register char c;
X
Xagain:
X c = getc(ec);
X if (!INSUB() && ec != '\'') {
X if (c == '`') {
X if (grave(quoted) == 0)
X return(0);
X e.iop->task = XGRAVE;
X goto again;
X }
X if (c == '$' && (c = dollar(quoted)) == 0) {
X e.iop->task = XDOLL;
X goto again;
X }
X }
X return(c);
X}
X
X/*
X * Prepare to generate the string returned by ${} substitution.
X */
Xstatic int
Xdollar(quoted)
Xint quoted;
X{
X int otask;
X struct io *oiop;
X char *dolp;
X register char *s, c, *cp;
X struct var *vp;
X
X c = readc();
X s = e.linep;
X if (c != '{') {
X *e.linep++ = c;
X if (letter(c)) {
X while ((c = readc())!=0 && letnum(c))
X if (e.linep < elinep)
X *e.linep++ = c;
X unget(c);
X }
X c = 0;
X } else {
X oiop = e.iop;
X otask = e.iop->task;
X e.iop->task = XOTHER;
X while ((c = subgetc('"', 0))!=0 && c!='}' && c!='\n')
X if (e.linep < elinep)
X *e.linep++ = c;
X if (oiop == e.iop)
X e.iop->task = otask;
X if (c != '}') {
X err("unclosed ${");
X gflg++;
X return(c);
X }
X }
X if (e.linep >= elinep) {
X err("string in ${} too long");
X gflg++;
X e.linep -= 10;
X }
X *e.linep = 0;
X if (*s)
X for (cp = s+1; *cp; cp++)
X if (any(*cp, "=-+?")) {
X c = *cp;
X *cp++ = 0;
X break;
X }
X if (s[1] == 0 && (*s == '*' || *s == '@')) {
X if (dolc > 1) {
X /* currently this does not distinguish $* and $@ */
X /* should check dollar */
X e.linep = s;
X PUSHIO(awordlist, dolv+1, dolchar);
X return(0);
X } else { /* trap the nasty ${=} */
X s[0] = '1';
X s[1] = 0;
X }
X }
X vp = lookup(s);
X if ((dolp = vp->value) == null) {
X switch (c) {
X case '=':
X if (digit(*s)) {
X err("cannot use ${...=...} with $n");
X gflg++;
X break;
X }
X setval(vp, cp);
X dolp = vp->value;
X break;
X
X case '-':
X dolp = strsave(cp, areanum);
X break;
X
X case '?':
X if (*cp == 0) {
X prs("missing value for ");
X err(s);
X } else
X err(cp);
X gflg++;
X break;
X }
X } else if (c == '+')
X dolp = strsave(cp, areanum);
X if (flag['u'] && dolp == null) {
X prs("unset variable: ");
X err(s);
X gflg++;
X }
X e.linep = s;
X PUSHIO(aword, dolp, strchar);
X return(0);
X}
X
X/*
X * Run the command in `...` and read its output.
X */
Xstatic int
Xgrave(quoted)
Xint quoted;
X{
X register char *cp;
X register int i;
X int pf[2];
X
X for (cp = e.iop->arg.aword; *cp != '`'; cp++)
X if (*cp == 0) {
X err("no closing `");
X return(0);
X }
X if (openpipe(pf) < 0)
X return(0);
X if ((i = fork()) == -1) {
X closepipe(pf);
X err("try again");
X return(0);
X }
X if (i != 0) {
X e.iop->arg.aword = ++cp;
X close(pf[1]);
X PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar);
X return(1);
X }
X *cp = 0;
X /* allow trapped signals */
X for (i=0; i<NSIG; i++)
X if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN)
X signal(i, SIG_DFL);
X dup2(pf[1], 1);
X closepipe(pf);
X flag['e'] = 0;
X flag['v'] = 0;
X flag['n'] = 0;
X cp = strsave(e.iop->arg.aword, 0);
X
X /* jrp debug */
X scraphere();
X
X freearea(areanum = 1); /* free old space */
X e.oenv = NULL;
X e.iop = (e.iobase = iostack) - 1;
X unquote(cp);
X talking = 0;
X PUSHIO(aword, cp, nlchar);
X onecommand();
X exit(1);
X}
X
Xchar *
Xunquote(as)
Xregister char *as;
X{
X register char *s;
X
X if ((s = as) != NULL)
X while (*s)
X *s++ &= ~QUOTE;
X return(as);
X}
X
X/* -------- glob.c -------- */
X/* #include "sh.h" */
X
X#define DIRSIZ 14
Xstruct direct
X{
X unsigned short d_ino;
X char d_name[DIRSIZ];
X};
X/*
X * glob
X */
X
X#define scopy(x) strsave((x), areanum)
X#define BLKSIZ 512
X#define NDENT ((BLKSIZ+sizeof(struct direct)-1)/sizeof(struct direct))
X
Xstatic struct wdblock *cl, *nl;
Xstatic char spcl[] = "[?*";
Xstatic int xstrcmp();
Xstatic char *generate();
Xstatic int anyspcl();
X
Xstruct wdblock *
Xglob(cp, wb)
Xchar *cp;
Xstruct wdblock *wb;
X{
X register i;
X register char *pp;
X
X if (cp == 0)
X return(wb);
X i = 0;
X for (pp = cp; *pp; pp++)
X if (any(*pp, spcl))
X i++;
X else if (!any(*pp & ~QUOTE, spcl))
X *pp &= ~QUOTE;
X if (i != 0) {
X for (cl = addword(scopy(cp), (struct wdblock *)0); anyspcl(cl); cl = nl) {
X nl = newword(cl->w_nword*2);
X for(i=0; i<cl->w_nword; i++) { /* for each argument */
X for (pp = cl->w_words[i]; *pp; pp++)
X if (any(*pp, spcl)) {
X globname(cl->w_words[i], pp);
X break;
X }
X if (*pp == '\0')
X nl = addword(scopy(cl->w_words[i]), nl);
X }
X for(i=0; i<cl->w_nword; i++)
X DELETE(cl->w_words[i]);
X DELETE(cl);
X }
X for(i=0; i<cl->w_nword; i++)
X unquote(cl->w_words[i]);
X glob0((char *)cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
X if (cl->w_nword) {
X for (i=0; i<cl->w_nword; i++)
X wb = addword(cl->w_words[i], wb);
X DELETE(cl);
X return(wb);
X }
X }
X wb = addword(unquote(cp), wb);
X return(wb);
X}
X
Xglobname(we, pp)
Xchar *we;
Xregister char *pp;
X{
X register char *np, *cp;
X char *name, *gp, *dp;
X int dn, j, n, k;
X struct direct ent[NDENT];
X char dname[DIRSIZ+1];
X struct stat dbuf;
X
X for (np = we; np != pp; pp--)
X if (pp[-1] == '/')
X break;
X for (dp = cp = space((int)(pp-np)+3); np < pp;)
X *cp++ = *np++;
X *cp++ = '.';
X *cp = '\0';
X for (gp = cp = space(strlen(pp)+1); *np && *np != '/';)
X *cp++ = *np++;
X *cp = '\0';
X dn = open(dp, 0);
X if (dn < 0) {
X DELETE(dp);
X DELETE(gp);
X return;
X }
X dname[DIRSIZ] = '\0';
X while ((n = read(dn, (char *)ent, sizeof(ent))) >= sizeof(*ent)) {
X n /= sizeof(*ent);
X for (j=0; j<n; j++) {
X if (ent[j].d_ino == 0)
X continue;
X strncpy(dname, ent[j].d_name, DIRSIZ);
X if (dname[0] == '.' &&
X (dname[1] == '\0' || dname[1] == '.' && dname[2] == '\0'))
X if (*gp != '.')
X continue;
X for(k=0; k<DIRSIZ; k++)
X if (any(dname[k], spcl))
X dname[k] |= QUOTE;
X if (gmatch(dname, gp)) {
X name = generate(we, pp, dname, np);
X if (*np && !anys(np, spcl)) {
X if (stat(name,&dbuf)) {
X DELETE(name);
X continue;
X }
X }
X nl = addword(name, nl);
X }
X }
X }
X close(dn);
X DELETE(dp);
X DELETE(gp);
X}
X
X/*
X * generate a pathname as below.
X * start..end1 / middle end
X * the slashes come for free
X */
Xstatic char *
Xgenerate(start1, end1, middle, end)
Xchar *start1;
Xregister char *end1;
Xchar *middle, *end;
X{
X char *p;
X register char *op, *xp;
X
X p = op = space((int)(end1-start1)+strlen(middle)+strlen(end)+2);
X for (xp = start1; xp != end1;)
X *op++ = *xp++;
X for (xp = middle; (*op++ = *xp++) != '\0';)
X ;
X op--;
X for (xp = end; (*op++ = *xp++) != '\0';)
X ;
X return(p);
X}
X
Xstatic int
Xanyspcl(wb)
Xregister struct wdblock *wb;
X{
X register i;
X register char **wd;
X
X wd = wb->w_words;
X for (i=0; i<wb->w_nword; i++)
X if (anys(spcl, *wd++))
X return(1);
X return(0);
X}
X
Xstatic int
Xxstrcmp(p1, p2)
Xchar *p1, *p2;
X{
X return(strcmp(*(char **)p1, *(char **)p2));
X}
X
X/* -------- word.c -------- */
X/* #include "sh.h" */
X/* #include "word.h" */
Xchar *memcpy();
X
X#define NSTART 16 /* default number of words to allow for initially */
X
Xstruct wdblock *
Xnewword(nw)
Xregister nw;
X{
X register struct wdblock *wb;
X
X wb = (struct wdblock *) space(sizeof(*wb) + nw*sizeof(char *));
X wb->w_bsize = nw;
X wb->w_nword = 0;
X return(wb);
X}
X
Xstruct wdblock *
Xaddword(wd, wb)
Xchar *wd;
Xregister struct wdblock *wb;
X{
X register struct wdblock *wb2;
X register nw;
X
X if (wb == NULL)
X wb = newword(NSTART);
X if ((nw = wb->w_nword) >= wb->w_bsize) {
X wb2 = newword(nw * 2);
X memcpy((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *));
X wb2->w_nword = nw;
X DELETE(wb);
X wb = wb2;
X }
X wb->w_words[wb->w_nword++] = wd;
X return(wb);
X}
X
Xchar **
Xgetwords(wb)
Xregister struct wdblock *wb;
X{
X register char **wd;
X register nb;
X
X if (wb == NULL)
X return(NULL);
X if (wb->w_nword == 0) {
X DELETE(wb);
X return(NULL);
X }
X wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
X memcpy((char *)wd, (char *)wb->w_words, nb);
X DELETE(wb); /* perhaps should done by caller */
X return(wd);
X}
X
Xint (*func)();
Xint globv;
X
Xglob0(a0, a1, a2, a3)
Xchar *a0;
Xunsigned a1;
Xint a2;
Xint (*a3)();
X{
X func = a3;
X globv = a2;
X glob1(a0, a0 + a1 * a2);
X}
X
Xglob1(base, lim)
Xchar *base, *lim;
X{
X register char *i, *j;
X int v2;
X char **k;
X char *lptr, *hptr;
X int c;
X unsigned n;
X
X
X v2 = globv;
X
Xtop:
X if ((n=(int)(lim-base)) <= v2)
X return;
X n = v2 * (n / (2*v2));
X hptr = lptr = base+n;
X i = base;
X j = lim-v2;
X for(;;) {
X if (i < lptr) {
X if ((c = (*func)(i, lptr)) == 0) {
X glob2(i, lptr -= v2);
X continue;
X }
X if (c < 0) {
X i += v2;
X continue;
X }
X }
X
Xbegin:
X if (j > hptr) {
X if ((c = (*func)(hptr, j)) == 0) {
X glob2(hptr += v2, j);
X goto begin;
X }
X if (c > 0) {
X if (i == lptr) {
X glob3(i, hptr += v2, j);
X i = lptr += v2;
X goto begin;
X }
X glob2(i, j);
X j -= v2;
X i += v2;
X continue;
X }
X j -= v2;
X goto begin;
X }
X
X
X if (i == lptr) {
X if (lptr-base >= lim-hptr) {
X glob1(hptr+v2, lim);
X lim = lptr;
X } else {
X glob1(base, lptr);
X base = hptr+v2;
X }
X goto top;
X }
X
X
X glob3(j, lptr -= v2, i);
X j = hptr -= v2;
X }
X}
X
Xglob2(i, j)
Xchar *i, *j;
X{
X register char *index1, *index2, c;
X int m;
X
X m = globv;
X index1 = i;
X index2 = j;
X do {
X c = *index1;
X *index1++ = *index2;
X *index2++ = c;
X } while(--m);
X}
X
Xglob3(i, j, k)
Xchar *i, *j, *k;
X{
X register char *index1, *index2, *index3;
X int c;
X int m;
X
X m = globv;
X index1 = i;
X index2 = j;
X index3 = k;
X do {
X c = *index1;
X *index1++ = *index3;
X *index3++ = *index2;
X *index2++ = c;
X } while(--m);
X}
+ END-OF-FILE sh4.c
chmod 'u=rw,g=r,o=r' \s\h\4\.\c
set `sum \s\h\4\.\c`
sum=$1
case $sum in
59682) :;;
*) echo 'Bad sum in '\s\h\4\.\c >&2
esac
echo Extracting \s\h\5\.\c
sed 's/^X//' > \s\h\5\.\c << '+ END-OF-FILE '\s\h\5\.\c
X#define Extern extern
X#include "signal.h"
X#include "errno.h"
X#include "setjmp.h"
X#include "sh.h"
X
X/* -------- io.c -------- */
X/* #include "sh.h" */
X
X/*
X * shell IO
X */
X
X
Xint
Xgetc(ec)
Xregister int ec;
X{
X register int c;
X
X if(e.linep > elinep) {
X while((c=readc()) != '\n' && c)
X ;
X err("input line too long");
X gflg++;
X return(c);
X }
X c = readc();
X if ((ec != '"') && (ec != '\'')) {
X if(c == '\\') {
X c = readc();
X if (c == '\n' && ec != '\"')
X return(getc(ec));
X c |= QUOTE;
X }
X }
X return(c);
X}
X
Xvoid
Xunget(c)
X{
X if (e.iop >= e.iobase)
X e.iop->peekc = c;
X}
X
Xint
Xreadc()
X{
X register c;
X static int eofc;
X
X for (; e.iop >= e.iobase; e.iop--)
X if ((c = e.iop->peekc) != '\0') {
X e.iop->peekc = 0;
X return(c);
X } else if ((c = (*e.iop->iofn)(&e.iop->arg, e.iop)) != '\0') {
X if (c == -1) {
X e.iop++;
X continue;
X }
X if (e.iop == iostack)
X ioecho(c);
X return(c);
X }
X if (e.iop >= iostack ||
X multiline && eofc++ < 3)
X return(0);
X leave();
X /* NOTREACHED */
X}
X
Xvoid
Xioecho(c)
Xchar c;
X{
X if (flag['v'])
X write(2, &c, sizeof c);
X}
X
Xvoid
Xpushio(arg, fn)
Xstruct ioarg arg;
Xint (*fn)();
X{
X if (++e.iop >= &iostack[NPUSH]) {
X e.iop--;
X err("Shell input nested too deeply");
X gflg++;
X return;
X }
X e.iop->iofn = fn;
X e.iop->arg = arg;
X e.iop->peekc = 0;
X e.iop->xchar = 0;
X e.iop->nlcount = 0;
X if (fn == filechar || fn == linechar || fn == nextchar)
X e.iop->task = XIO;
X else if (fn == gravechar || fn == qgravechar)
X e.iop->task = XGRAVE;
X else
X e.iop->task = XOTHER;
X}
X
Xstruct io *
Xsetbase(ip)
Xstruct io *ip;
X{
X register struct io *xp;
X
X xp = e.iobase;
X e.iobase = ip;
X return(xp);
X}
X
X/*
X * Input generating functions
X */
X
X/*
X * Produce the characters of a string, then a newline, then EOF.
X */
Xint
Xnlchar(ap)
Xregister struct ioarg *ap;
X{
X register int c;
X
X if (ap->aword == NULL)
X return(0);
X if ((c = *ap->aword++) == 0) {
X ap->aword = NULL;
X return('\n');
X }
X return(c);
X}
X
X/*
X * Given a list of words, produce the characters
X * in them, with a space after each word.
X */
Xint
Xwdchar(ap)
Xregister struct ioarg *ap;
X{
X register char c;
X register char **wl;
X
X if ((wl = ap->awordlist) == NULL)
X return(0);
X if (*wl != NULL) {
X if ((c = *(*wl)++) != 0)
X return(c & 0177);
X ap->awordlist++;
X return(' ');
X }
X ap->awordlist = NULL;
X return('\n');
X}
X
X/*
X * Return the characters of a list of words,
X * producing a space between them.
X */
Xstatic int xxchar(), qqchar();
X
Xint
Xdolchar(ap)
Xregister struct ioarg *ap;
X{
X register char *wp;
X
X if ((wp = *ap->awordlist++) != NULL) {
X PUSHIO(aword, wp, *ap->awordlist == NULL? qqchar: xxchar);
X return(-1);
X }
X return(0);
X}
X
Xstatic int
Xxxchar(ap)
Xregister struct ioarg *ap;
X{
X register int c;
X
X if (ap->aword == NULL)
X return(0);
X if ((c = *ap->aword++) == '\0') {
X ap->aword = NULL;
X return(' ');
X }
X return(c);
X}
X
Xstatic int
Xqqchar(ap)
Xregister struct ioarg *ap;
X{
X register int c;
X
X if (ap->aword == NULL || (c = *ap->aword++) == '\0')
X return(0);
X return(c);
X}
X
X/*
X * Produce the characters from a single word (string).
X */
Xint
Xstrchar(ap)
Xregister struct ioarg *ap;
X{
X register int c;
X
X if (ap->aword == 0 || (c = *ap->aword++) == 0)
X return(0);
X return(c);
X}
X
X/*
X * Return the characters from a file.
X */
Xint
Xfilechar(ap)
Xregister struct ioarg *ap;
X{
X register int i;
X char c;
X extern int errno;
X
X do {
X i = read(ap->afile, &c, sizeof(c));
X } while (i < 0 && errno == EINTR);
X return(i == sizeof(c)? c&0177: (closef(ap->afile), 0));
X}
X
X/*
X * Return the characters produced by a process (`...`).
X * Quote them if required, and remove any trailing newline characters.
X */
Xint
Xgravechar(ap, iop)
Xstruct ioarg *ap;
Xstruct io *iop;
X{
X register int c;
X
X if ((c = qgravechar(ap, iop)&~QUOTE) == '\n')
X c = ' ';
X return(c);
X}
X
Xint
Xqgravechar(ap, iop)
Xregister struct ioarg *ap;
Xstruct io *iop;
X{
X register int c;
X
X if (iop->xchar) {
X if (iop->nlcount) {
X iop->nlcount--;
X return('\n'|QUOTE);
X }
X c = iop->xchar;
X iop->xchar = 0;
X } else if ((c = filechar(ap)) == '\n') {
X iop->nlcount = 1;
X while ((c = filechar(ap)) == '\n')
X iop->nlcount++;
X iop->xchar = c;
X if (c == 0)
X return(c);
X iop->nlcount--;
X c = '\n';
X }
X return(c!=0? c|QUOTE: 0);
X}
X
X/*
X * Return a single command (usually the first line) from a file.
X */
Xint
Xlinechar(ap)
Xregister struct ioarg *ap;
X{
X register int c;
X
X if ((c = filechar(ap)) == '\n') {
X if (!multiline) {
X closef(ap->afile);
X ap->afile = -1; /* illegal value */
X }
X }
X return(c);
X}
X
X/*
X * Return the next character from the command source,
X * prompting when required.
X */
Xint
Xnextchar(ap)
Xregister struct ioarg *ap;
X{
X register int c;
X
X if ((c = filechar(ap)) != 0)
X return(c);
X if (talking && e.iop <= iostack+1)
X prs(prompt->value);
X return(0);
X}
X
Xvoid
Xprs(s)
Xregister char *s;
X{
X if (*s)
X write(2, s, strlen(s));
X}
X
Xvoid
Xputc(c)
Xchar c;
X{
X write(2, &c, sizeof c);
X}
X
Xvoid
Xprn(u)
Xunsigned u;
X{
X prs(itoa(u, 0));
X}
X
Xvoid
Xclosef(i)
Xregister i;
X{
X if (i > 2)
X close(i);
X}
X
Xvoid
Xcloseall()
X{
X register u;
X
X for (u=NUFILE; u<NOFILE;)
X close(u++);
X}
X
X/*
X * remap fd into Shell's fd space
X */
Xint
Xremap(fd)
Xregister int fd;
X{
X register int i;
X int map[NOFILE];
X
X if (fd < e.iofd) {
X for (i=0; i<NOFILE; i++)
X map[i] = 0;
X do {
X map[fd] = 1;
X fd = dup(fd);
X } while (fd >= 0 && fd < e.iofd);
X for (i=0; i<NOFILE; i++)
X if (map[i])
X close(i);
X if (fd < 0)
X err("too many files open in shell");
X }
X return(fd);
X}
X
Xint
Xopenpipe(pv)
Xregister int *pv;
X{
X register int i;
X
X if ((i = pipe(pv)) < 0)
X err("can't create pipe - try again");
X return(i);
X}
X
Xvoid
Xclosepipe(pv)
Xregister int *pv;
X{
X if (pv != NULL) {
X close(*pv++);
X close(*pv);
X }
X}
X
X/* -------- here.c -------- */
X/* #include "sh.h" */
X
X/*
X * here documents
X */
X
Xstruct here {
X char *h_tag;
X int h_dosub;
X struct ioword *h_iop;
X struct here *h_next;
X} *herelist;
X
Xstruct block {
X char *b_linebuf;
X char *b_next;
X char b_tmpfile[50];
X int b_fd;
X};
X
Xstatic struct block *readhere();
X
X#define NCPB 2048 /* here text block allocation unit */
X
Xmarkhere(s, iop)
Xregister char *s;
Xstruct ioword *iop;
X{
X register struct here *h, *lh;
X
X h = (struct here *) space(sizeof(struct here));
X if (h == 0)
X return;
X h->h_tag = evalstr(s, DOSUB);
X if (h->h_tag == 0)
X return;
X h->h_iop = iop;
X h->h_iop->io_un.io_here = NULL;
X h->h_next = NULL;
X if (herelist == 0)
X herelist = h;
X else
X for (lh = herelist; lh!=NULL; lh = lh->h_next)
X if (lh->h_next == 0) {
X lh->h_next = h;
X break;
X }
X iop->io_flag |= IOHERE|IOXHERE;
X for (s = h->h_tag; *s; s++)
X if (*s & QUOTE) {
X iop->io_flag &= ~ IOXHERE;
X *s &= ~ QUOTE;
X }
X h->h_dosub = iop->io_flag & IOXHERE;
X}
X
Xgethere()
X{
X register struct here *h;
X
X for (h = herelist; h != NULL; h = h->h_next) {
X h->h_iop->io_un.io_here =
X readhere(h->h_tag, h->h_dosub? 0: '\'',
X h->h_iop->io_flag & IOXHERE);
X }
X herelist = NULL;
X}
X
Xstatic struct block *
Xreadhere(s, ec, nolit)
Xregister char *s;
X{
X register struct block *bp;
X register c;
X jmp_buf ev;
X
X bp = (struct block *) space(sizeof(*bp));
X if (bp == 0)
X return(0);
X bp->b_linebuf = (char *)space(NCPB);
X if (bp->b_linebuf == 0) {
X /* jrp - should release bp here... */
X return(0);
X }
X if (newenv(setjmp(errpt = ev)) == 0) {
X if (e.iop == iostack && e.iop->iofn == filechar) {
X pushio(e.iop->arg, filechar);
X e.iobase = e.iop;
X }
X
X /* jrp changes */
X bp->b_linebuf[0] = 0;
X bp->b_next = bp->b_linebuf;
X bp->b_tmpfile[0] = 0;
X bp->b_fd = -1;
X for (;;) {
X while ((c = getc(ec)) != '\n' && c) {
X if (ec == '\'')
X c &= ~ QUOTE;
X if (savec(c, bp, nolit) == 0) {
X c = 0;
X break;
X }
X }
X savec(0, bp, nolit);
X if (strcmp(s, bp->b_linebuf) == 0 || c == 0)
X break;
X savec('\n', bp, nolit);
X }
X *bp->b_linebuf = 0;
X if (c == 0) {
X prs("here document `"); prs(s); err("' unclosed");
X }
X quitenv();
X }
X return(bp);
X}
X
Xstatic
Xsavec(c, bp, nolit)
Xregister struct block *bp;
X{
X /* jrp - gutted routine completely, modified to use temp file. */
X
X /* If the file is not open, see if a filename needs to be
X * created. If so, create one. Then create the file.
X */
X char * lp;
X char * cp;
X static int inc;
X int len;
X
X if(bp->b_fd < 0) {
X if(bp->b_tmpfile[0] == 0) {
X /* Key this by the PID plus a tag... */
X for (cp = bp->b_tmpfile, lp = "/tmp/shtm";
X (*cp = *lp++) != '\0'; cp++)
X ;
X
X inc = (inc + 1) % 100;
X lp = putn(getpid()*100 + inc);
X for (; (*cp = *lp++) != '\0'; cp++)
X ;
X }
X
X /* Create the file, then open it for
X * read/write access. After opening the
X * file, unlink it to it'll go away when
X * we're through using it.
X */
X bp->b_fd = creat(bp->b_tmpfile, 0600);
X close(bp->b_fd);
X bp->b_fd = open(bp->b_tmpfile, 2);
X unlink(bp->b_tmpfile);
X if(bp->b_fd < 0) {
X return(0);
X }
X }
X
X /* Stuff the character into the line buffer. If it's a
X * newline, then insert it before the trailing null, write
X * out the line, and reset the line buffer.
X */
X if(c == '\n') {
X bp->b_next[-1] = '\n';
X bp->b_next[0] = '\0';
X len = strlen(bp->b_linebuf);
X
X /* Write this out, unless the line ended
X * with a backslash...
X */
X if((len > 1) && (bp->b_next[-2] != '\\')) {
X write_linebuf(bp, nolit);
X }
X
X return(1);
X }
X else {
X if(bp->b_next == &(bp->b_linebuf[NCPB - 1])) {
X prs("here: line buffer full\n");
X return(0);
X }
X *(bp->b_next++) = c;
X return(1);
X }
X}
X
Xwrite_linebuf(bp, nolit)
Xstruct block * bp;
X{
X
X char c;
X jmp_buf ev;
X
X if(nolit) {
X if (newenv(setjmp(errpt = ev)) == 0) {
X PUSHIO(aword, bp->b_linebuf, strchar);
X setbase(e.iop);
X e.iop->task |= XHERE;
X while ((c = subgetc(0, 0)) != 0) {
X c &= ~ QUOTE;
X write(bp->b_fd, &c, sizeof c);
X }
X quitenv();
X
X }
X }
X else {
X write(bp->b_fd, bp->b_linebuf, strlen(bp->b_linebuf));
X }
X
X /* Zap the line buffer for next time... */
X bp->b_next = bp->b_linebuf;
X bp->b_linebuf[0] = 0;
X}
X
Xherein(bp, xdoll)
Xstruct block *bp;
X{
X int ret_fd;
X
X if (bp == 0)
X return(-1);
X
X /* If we have a temp file, then rewind it to the beginning */
X if(bp->b_fd < 0) {
X return(-1);
X }
X
X lseek(bp->b_fd, 0L, 0);
X
X /* Free up this block pointer, as we're
X * not going to need it anymore.
X */
X xfree(bp->b_linebuf);
X xfree(bp);
X
X return(bp->b_fd);
X}
X
Xscraphere()
X{
X struct here * h;
X struct here * nexth;
X struct block * bp;
X
X
X /* Close and unlink any files associated with
X * heres in progress, and free up all the
X * associated structures.
X */
X h = herelist;
X while(h != NULL) {
X nexth = h->h_next;
X bp = (struct block *)h->h_iop->io_un.io_here;
X if(bp != NULL) {
X if(bp->b_fd >= 0) { close(bp->b_fd); }
X if(*bp->b_tmpfile) { unlink(bp->b_tmpfile); }
X xfree(bp->b_linebuf);
X xfree(bp);
X }
X xfree(h);
X h = nexth;
X }
X
X herelist = NULL;
X}
X
Xchar *
Xmemcpy(ato, from, nb)
Xregister char *ato, *from;
Xregister int nb;
X{
X register char *to;
X
X to = ato;
X while (--nb >= 0)
X *to++ = *from++;
X return(ato);
X}
+ END-OF-FILE sh5.c
chmod 'u=rw,g=r,o=r' \s\h\5\.\c
set `sum \s\h\5\.\c`
sum=$1
case $sum in
38678) :;;
*) echo 'Bad sum in '\s\h\5\.\c >&2
esac
echo Extracting \s\h\6\.\c
sed 's/^X//' > \s\h\6\.\c << '+ END-OF-FILE '\s\h\6\.\c
X#define Extern
X
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X
+ END-OF-FILE sh6.c
chmod 'u=rw,g=r,o=r' \s\h\6\.\c
set `sum \s\h\6\.\c`
sum=$1
case $sum in
30298) :;;
*) echo 'Bad sum in '\s\h\6\.\c >&2
esac
exit 0
|