home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume25
/
pdksh
/
part04
< prev
next >
Wrap
Text File
|
1991-11-12
|
56KB
|
2,637 lines
Newsgroups: comp.sources.misc
From: sjg@zen.void.oz.au (Simon J. Gerraty)
Subject: v25i050: pdksh - Public Domain Korn Shell, v4, Part04/09
Message-ID: <1991Nov13.031114.15918@sparky.imd.sterling.com>
X-Md4-Signature: 39dc3d95402c3f04617242cea892a466
Date: Wed, 13 Nov 1991 03:11:14 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: sjg@zen.void.oz.au (Simon J. Gerraty)
Posting-number: Volume 25, Issue 50
Archive-name: pdksh/part04
Environment: UNIX
#! /bin/sh
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents: Changes.mlj sh/c_ksh.c sh/eval.c sh/lex.c sh/syn.c
# Wrapped by kent@sparky on Tue Nov 12 20:44:32 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 4 (of 9)."'
if test -f 'Changes.mlj' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Changes.mlj'\"
else
echo shar: Extracting \"'Changes.mlj'\" \(1777 characters\)
sed "s/^X//" >'Changes.mlj' <<'END_OF_FILE'
XI got the pd-ksh from John MacMillan after he indicated that he
Xhad a version of it that had vi editing (I'd seen various versions
Xwith emacs-editing, but none with vi).
X
XIt had a few bugs and areas which were not quite complete. I fixed
X(or at least tried) to fix several; there are still some things
Xwhich I plan on doing (or at least looking into).
X
XBugs fixed (or at least abated):
X
X vi-mode changes:
X - Changed memcpy() to memmove(), which fixed the trashing of
X the end of the edit buffer while inserting in the middle
X of a line or with use of '#'
X - using 'r' replacing the current character with ^@
X - typing ctrl-c resulting in next command being garbled
X - lack of support for '-' and '+' (pretty trivial)
X - finish adding support for '*' (not entirely sure I'm freeing
X malloc'ed memory correctly here, but I haven't had any problems)
X - treats '_' as end of a word
X
X general changes:
X - reporting "not found" when a file actually doesn't have
X the appropriate execute bit set (now says "cannot execute"
X or "not found", as appropriate)
X
X
XStill to do:
X
X vi changes:
X - fix ctrl-r (I've come up with a hack, but it involves
X redrawing the screen a lot when it isn't necessary; I
X really wouldn't consider this a fix)
X - add support for 'v'
X
X general changes:
X - seems to be a memory leak when executing shells in the
X current shell; repeatedly executing ". /etc/profile"
X increased the size of the program as reported in the
X "SZ" field of "ps -l"
X - don't give a file its complete pathname in argv[0]; only
X its filename (religious issue?)
X - history recall should start at the previous command, not
X the current one (typing "r r" causes an infinite loop)
END_OF_FILE
if test 1777 -ne `wc -c <'Changes.mlj'`; then
echo shar: \"'Changes.mlj'\" unpacked with wrong size!
fi
# end of 'Changes.mlj'
fi
if test -f 'sh/c_ksh.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sh/c_ksh.c'\"
else
echo shar: Extracting \"'sh/c_ksh.c'\" \(11631 characters\)
sed "s/^X//" >'sh/c_ksh.c' <<'END_OF_FILE'
X/*
X * built-in Korn commands: c_*
X */
X
Xstatic char *RCSid = "$Id: c_ksh.c,v 3.4 89/03/27 15:47:16 egisin Exp $";
X
X#include <stddef.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X#include "table.h"
X
Xint
Xc_hash(wp)
X register char **wp;
X{
X register int i;
X register struct tbl *tp, **p;
X
X wp++;
X if (*wp == NULL) {
X for (p = tsort(&commands); (tp = *p++) != NULL; )
X if ((tp->flag&ISSET))
X printf("%s\n", tp->val.s);
X return 0;
X }
X
X if (strcmp(*wp, "-r") == 0)
X flushcom(1);
X while (*wp != NULL)
X findcom(*wp++, 1);
X return 0;
X}
X
Xint
Xc_cd(wp)
X register char **wp;
X{
X char path [PATH];
X char newd [PATH];
X register char *cp;
X register char *dir;
X register char *cdpath;
X register char *rep;
X register char *pwd = NULL, *oldpwd = NULL;
X register int done = 0;
X register int prt = 0;
X register struct tbl *v_pwd = NULL, *v_oldpwd = NULL;
X extern Void cleanpath();
X
X if ((dir = wp[1]) == NULL && (dir = strval(global("HOME"))) == NULL)
X errorf("no home directory");
X
X v_pwd = global("PWD");
X if ((pwd = strval(v_pwd)) == null) {
X setstr(v_pwd, getcwd(path, (size_t)PATH));
X pwd = strval(v_pwd);
X }
X
X if (wp[1] != NULL && (rep = wp[2]) != NULL) {
X /*
X * Two arg version: cd pat rep
X */
X if (strlen(pwd) - strlen(dir) + strlen(rep) >= PATH)
X errorf("substitution too long\n");
X cp = strstr(pwd, dir);
X if (cp == NULL)
X errorf("substitution failed\n");
X strncpy(path, pwd, cp - pwd); /* first part */
X strcpy(path + (cp - pwd), rep); /* replacement */
X strcat(path, cp + strlen(dir)); /* last part */
X dir = strsave(path, ATEMP);
X prt = 1;
X } else if (dir[0] == '-' && dir[1] == '\0') {
X /*
X * Change to previous dir: cd -
X */
X dir = strval(v_oldpwd = global("OLDPWD"));
X prt = 1;
X }
X if (dir[0] == '/' || (dir[0] == '.' && (dir[1] == '/' ||
X (dir[1] == '.' && dir[2] == '/')))) {
X /*
X * dir is an explicitly named path, so no CDPATH search
X */
X cleanpath(pwd, dir, newd);
X if (chdir(newd) < 0)
X errorf("%s: bad directory\n", newd);
X else if (prt)
X shellf("%s\n", newd);
X flushcom(0);
X } else {
X /*
X * search CDPATH for dir
X */
X cdpath = strval(global("CDPATH"));
X while ( !done && cdpath != NULL ) {
X cp = path;
X while (*cdpath && *cdpath != ':')
X *cp++ = *cdpath++;
X if (*cdpath == '\0')
X cdpath = NULL;
X else
X cdpath++;
X if (prt = (cp > path)) {
X *cp++ = '/';
X (void) strcpy( cp, dir );
X cp = path;
X } else
X cp = dir;
X
X cleanpath(pwd, cp, newd);
X if (chdir(newd) == 0)
X done = 1;
X } while (!done && cdpath != NULL);
X if (!done)
X errorf("%s: bad directory\n", dir);
X if (prt)
X shellf("%s\n", newd);
X flushcom(0);
X }
X
X /*
X * Keep track of OLDPWD and PWD
X */
X oldpwd = pwd;
X pwd = newd;
X if (!v_oldpwd)
X v_oldpwd = global("OLDPWD");
X if (oldpwd && *oldpwd)
X setstr(v_oldpwd, oldpwd);
X else
X unset(v_oldpwd);
X if (*pwd)
X setstr(v_pwd, pwd);
X else
X unset(v_pwd);
X
X return 0;
X}
X
Xint
Xc_print(wp)
X register char **wp;
X{
X int nl = 1;
X int expand = 1;
X FILE *f = stdout;
X
X for (wp++; *wp != NULL && **wp == '-'; wp++) {
X register char *s = *wp + 1;
X if (*s == '\0') {
X wp++;
X break;
X }
X while (*s) switch (*s++) {
X case 'n':
X nl = 0;
X break;
X case 'e':
X expand = 1;
X break;
X case 'r':
X expand = 0;
X break;
X case 'u':
X if (!digit(*s) || (f = shf[*s++-'0']) == NULL)
X errorf("bad -u argument\n");
X break;
X }
X }
X
X while (*wp != NULL) {
X register char *s = *wp;
X register int c;
X while ((c = *s++) != '\0')
X if (expand && c == '\\') {
X switch ((c = *s++)) {
X case 'b': c = '\b'; break;
X case 'c': nl = 0; continue; /* AT&T brain damage */
X case 'f': c = '\f'; break;
X case 'n': c = '\n'; break;
X case 'r': c = '\r'; break;
X case 't': c = '\t'; break;
X case 'v': c = 0x0B; break;
X case '0': case '1': case '2': case '3':
X case '4': case '5': case '6': case '7':
X c = c - '0';
X if (*s >= '0' && *s <= '7')
X c = 8*c + *s++ - '0';
X if (*s >= '0' && *s <= '7')
X c = 8*c + *s++ - '0';
X break;
X case '\\': break;
X default:
X putc('\\', f);
X }
X putc(c, f);
X } else
X putc(c, f);
X if (*++wp != NULL)
X putc(' ', f);
X }
X if (nl)
X putc('\n', f);
X return 0;
X}
X
X/* todo: handle case where id is both lexical and command */
Xint
Xc_whence(wp)
X register char **wp;
X{
X register struct tbl *tp;
X char *id;
X int vflag = 0;
X int ret = 0;
X
X for (wp++; (id = *wp) != NULL && *id == '-'; wp++)
X if (id[1] == 'v')
X vflag = 1;
X
X while ((id = *wp++) != NULL) {
X tp = tsearch(&lexicals, id, hash(id));
X if (tp == NULL)
X tp = findcom(id, 0);
X if (vflag)
X switch ((tp == NULL) ? CNONE : tp->type) {
X case CNONE:
X printf("%s is unknown\n", id);
X ret = 1;
X break;
X case CSHELL:
X printf("%s is a shell builtin\n", id);
X break;
X case CFUNC:
X printf("%s is a function\n", id);
X fptreef(stdout, "function %s %T\n", id, tp->val.t);
X break;
X case CEXEC:
X printf("%s is %s\n", id,
X (tp->flag&ISSET) ? tp->val.s : "unknown");
X if (!(tp->flag&ISSET))
X ret = 1;
X break;
X case CALIAS:
X printf("%s is the alias '%s'\n", id, tp->val.s);
X break;
X case CKEYWD:
X printf("%s is a shell keyword\n", id);
X break;
X default:
X printf("%s is *GOK*\n", id);
X break;
X }
X else
X switch ((tp == NULL) ? CNONE : tp->type) {
X case CNONE:
X printf("\n");
X ret = 1;
X break;
X case CSHELL:
X printf("builtin %s\n", id);
X break;
X case CFUNC:
X printf("%s\n", id);
X break;
X case CEXEC:
X printf("%s\n", (tp->flag&ISSET) ? tp->val.s : "");
X if (!(tp->flag&ISSET))
X ret = 1;
X break;
X case CALIAS:
X printf("%s\n", tp->val.s);
X break;
X case CKEYWD:
X printf("%s\n", id);
X break;
X default:
X printf("*GOK*\n");
X break;
X }
X }
X return ret;
X}
X
X/* typeset, export, and readonly */
Xint
Xc_typeset(wp)
X register char **wp;
X{
X register char *id;
X struct block *l = e.loc;
X register struct tbl *vp, **p;
X int fset = 0, fclr = 0;
X int thing = 0, func = 0, local = 0;
X
X switch (**wp) {
X case 'e': /* export */
X fset |= EXPORT;
X break;
X case 'r': /* readonly */
X fset |= RDONLY;
X break;
X case 't': /* typeset */
X local = 1;
X break;
X }
X
X for (wp++; (id = *wp) != NULL && (*id == '-' || *id == '+'); wp++) {
X int flag = 0;
X thing = *id;
X while (*++id != '\0') switch (*id) {
X case 'f':
X flag |= FUNCT;
X func = 1;
X break;
X case 'i':
X flag |= INTEGER;
X break;
X case 'r':
X flag |= RDONLY;
X break;
X case 'x':
X flag |= EXPORT;
X break;
X case 't':
X flag |= TRACE;
X break;
X default:
X errorf("unknown flag -%c\n", *id);
X }
X if (flag != 0) { /* + or - with options */
X if (thing == '-')
X fset |= flag;
X else
X fclr |= flag;
X thing = 0;
X }
X }
X
X /* list variables and attributes */
X if (*wp == NULL) {
X for (l = e.loc; l != NULL; l = l->next) {
X for (p = tsort((func==0) ? &l->vars : &l->funs);
X (vp = *p++) != NULL; )
X if ((vp->flag&ISSET))
X if (thing == 0 && fclr == 0 && fset == 0) {
X printf("typeset ");
X if ((vp->flag&INTEGER))
X printf("-i ");
X if ((vp->flag&EXPORT))
X printf("-x ");
X if ((vp->flag&RDONLY))
X printf("-r ");
X if ((vp->flag&TRACE))
X printf("-t ");
X printf("%s\n", vp->name);
X } else
X if (thing == '+' ||
X fclr && (vp->flag&fclr) == fclr) {
X printf("%s\n", vp->name);
X } else
X if (thing == '-' ||
X fset && (vp->flag&fset) == fset) {
X if (fset&FUNCT)
X printf("function %s\n", vp->name);
X else
X printf("%s=%s\n", vp->name, strval(vp));
X }
X }
X return (0);
X }
X
X if (local)
X fset |= LOCAL;
X for (; *wp != NULL; wp++)
X#if 0
X if (func) {
X } else
X#endif
X if (typeset(*wp, fset, fclr) == NULL)
X errorf("%s: not identifier\n", *wp);
X return 0;
X}
X
Xint
Xc_alias(wp)
X register char **wp;
X{
X register struct table *t = &lexicals;
X register struct tbl *ap, **p;
X register int i;
X int rv = 0;
X
X if (*++wp != NULL && strcmp(*wp, "-d") == 0) {
X t = &homedirs;
X wp++;
X }
X
X if (*wp == NULL)
X for (p = tsort(t); (ap = *p++) != NULL; )
X if (ap->type == CALIAS && (ap->flag&DEFINED))
X printf("%s='%s'\n", ap->name, ap->val.s);
X
X for (; *wp != NULL; wp++) {
X register char *id = *wp;
X register char *val = strchr(id, '=');
X
X if (val == NULL) {
X ap = tsearch(t, id, hash(id));
X if (ap != NULL && ap->type == CALIAS && (ap->flag&DEFINED))
X printf("%s='%s'\n", ap->name, ap->val.s);
X else
X rv = 1;
X } else {
X *val++ = '\0';
X ap = tenter(t, id, hash(id));
X if (ap->type == CKEYWD)
X errorf("cannot alias keyword\n");
X if ((ap->flag&ALLOC)) {
X afree((Void*)ap->val.s, APERM);
X ap->flag &= ~(ALLOC|ISSET);
X }
X ap->type = CALIAS;
X ap->val.s = strsave(val, APERM);
X ap->flag |= DEFINED|ALLOC|ISSET;
X }
X }
X return rv;
X}
X
Xint
Xc_unalias(wp)
X register char **wp;
X{
X register struct table *t = &lexicals;
X register struct tbl *ap;
X
X if (*++wp != NULL && strcmp(*wp, "-d") == 0) {
X t = &homedirs;
X wp++;
X }
X
X for (; *wp != NULL; wp++) {
X ap = tsearch(t, *wp, hash(*wp));
X if (ap == NULL || ap->type != CALIAS)
X continue;
X if ((ap->flag&ALLOC))
X afree((Void*)ap->val.s, APERM);
X ap->flag &= ~(DEFINED|ISSET|ALLOC);
X }
X return 0;
X}
X
Xint
Xc_let(wp)
X char **wp;
X{
X int rv = 1;
X
X for (wp++; *wp; wp++)
X rv = evaluate(*wp) == 0;
X return rv;
X}
X
Xint
Xc_jobs(wp)
X char **wp;
X{
X j_jobs();
X return 0;
X}
X
X#ifdef JOBS
Xint
Xc_fgbg(wp)
X register char **wp;
X{
X int bg = strcmp(*wp, "bg") == 0;
X
X if (!flag[FMONITOR])
X errorf("Job control not enabled\n");
X wp++;
X j_resume(j_lookup((*wp == NULL) ? "%" : *wp), bg);
X return 0;
X}
X#endif
X
Xint
Xc_kill(wp)
X register char **wp;
X{
X register char *cp;
X int sig = 15; /* SIGTERM */
X int rv = 0;
X int n;
X int gotsig = FALSE;
X
X if (*++wp == NULL)
X errorf("Usage: kill [-l] [-signal] {pid|job} ...\n");
X if (strcmp(*wp, "-l") == 0) {
X register struct trap *p = sigtraps;
X for (sig = 0; sig < SIGNALS; sig++, p++)
X if (p->signal)
X printf("%2d %8s %s\n", p->signal, p->name, p->mess);
X return 0;
X }
X
X for (; (cp = *wp) != NULL; wp++)
X if (*cp == '-' && gotsig == FALSE && *(wp+1) != NULL) {
X struct trap *p;
X gotsig = FALSE;
X if (digit(*(cp+1))) {
X if ((n = atoi(cp+1)) < SIGNALS) {
X sig = n;
X gotsig = TRUE;
X } else if (kill(n, sig) < 0) {
X shellf("%s: %s\n", cp, strerror(errno));
X rv++;
X }
X } else {
X p = gettrap(cp+1);
X if (p == NULL)
X errorf("bad signal %s\n", cp+1);
X sig = p->signal;
X gotsig = TRUE;
X }
X } else {
X gotsig = FALSE;
X if (digit(*cp) || (*cp == '-' && digit(*(cp+1)))) {
X if (kill(atoi(cp), sig) < 0) {
X shellf("%s: %s\n", cp, strerror(errno));
X rv++;
X }
X } else
X if (*cp == '%')
X j_kill(j_lookup(cp), sig);
X else
X errorf("invalid argument\n");
X }
X return rv;
X}
X
Xint
Xc_bind(wp)
X register char **wp;
X{
X int macro = 0;
X register char *cp;
X
X for (wp++; (cp = *wp) != NULL && *cp == '-'; wp++)
X if (cp[1] == 'm')
X macro = 1;
X
X if (*wp == NULL) /* list all */
X x_bind((char*)NULL, (char*)NULL, 0);
X
X for (; *wp != NULL; wp++) {
X cp = strchr(*wp, '=');
X if (cp != NULL)
X *cp++ = 0;
X x_bind(*wp, cp, macro);
X }
X
X return 0;
X}
X
Xextern c_fc();
Xextern c_getopts();
X
XConst struct builtin kshbuiltins [] = {
X {"cd", c_cd},
X {"print", c_print},
X {"getopts", c_getopts},
X {"=typeset", c_typeset},
X {"=export", c_typeset},
X {"=readonly", c_typeset},
X {"whence", c_whence},
X {"=alias", c_alias},
X {"unalias", c_unalias},
X {"hash", c_hash},
X {"let", c_let},
X {"fc", c_fc},
X {"jobs", c_jobs},
X {"kill", c_kill},
X#ifdef JOBS
X {"fg", c_fgbg},
X {"bg", c_fgbg},
X#endif
X#ifdef EMACS
X {"bind", c_bind},
X#endif
X {NULL, NULL}
X};
X
END_OF_FILE
if test 11631 -ne `wc -c <'sh/c_ksh.c'`; then
echo shar: \"'sh/c_ksh.c'\" unpacked with wrong size!
fi
# end of 'sh/c_ksh.c'
fi
if test -f 'sh/eval.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sh/eval.c'\"
else
echo shar: Extracting \"'sh/eval.c'\" \(14245 characters\)
sed "s/^X//" >'sh/eval.c' <<'END_OF_FILE'
X/*
X * Expansion - quoting, separation, substitution, globbing
X */
X
Xstatic char *RCSid = "$Id: eval.c,v 3.4 89/03/27 15:49:55 egisin Exp $";
X
X#include <stddef.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <unistd.h>
X#include <sys/types.h>
X#include <dirent.h>
X#include <pwd.h>
X#include "sh.h"
X#include "lex.h"
X#include "tree.h"
X#include "table.h"
X#include "expand.h"
X
X/*
X * string expansion
X *
X * first pass: quoting, IFS separation, ${} and $() substitution.
X * second pass: filename expansion (*?[]~).
X */
X
X/* expansion generator state */
Xtypedef struct Expand {
X /* int type; */ /* see expand() */
X char *str; /* string */
X union {
X char **strv; /* string[] */
X FILE *file; /* file */
X } u; /* source */
X short split; /* split "$@"*/
X} Expand;
X
X#define XBASE 0 /* scanning original */
X#define XSUB 1 /* expanding ${} string */
X#define XARGSEP 2 /* ifs0 between "$@" */
X#define XARG 3 /* expanding $*, $@ */
X#define XCOM 4 /* expanding $() */
X
Xstatic void expand ARGS((char *, XPtrV *, int));
Xstatic int comsub ARGS((Expand *, char *comm));
Xstatic int varsub ARGS((Expand *, char *name, int stype));
Xstatic void glob ARGS((char *cp, XPtrV *wp));
Xstatic void globit ARGS((char *ds, char *dp, char *sp, XPtrV *wp, int check));
Xstatic char *tilde ARGS((char *wp));
Xstatic char *trimsub ARGS((char *str, char *pat, int how));
X
Xint ifs0 = ' '; /* todo: first char of $IFS */
X
X/* compile and expand word */
Xchar *
Xsubstitute(cp, f)
X char Const *cp;
X int f;
X{
X struct source *s, *sold;
X
X sold = source;
X s = pushs(SWSTR);
X s->str = (char *) cp;
X source = s;
X if (yylex(ONEWORD) != LWORD)
X errorf("eval:substitute error\n");
X source = sold;
X return evalstr(yylval.cp, f);
X}
X
X/*
X * expand arg-list
X */
Xchar **
Xeval(ap, f)
X register char **ap;
X{
X XPtrV w;
X
X if (*ap == NULL)
X return ap;
X XPinit(w, 32);
X XPput(w, NULL); /* space for shell name */
X#ifdef SHARPBANG
X XPput(w, NULL); /* and space for one arg */
X#endif
X while (*ap != NULL)
X expand(*ap++, &w, f);
X XPput(w, NULL);
X#ifdef SHARPBANG
X return (char **) XPclose(w) + 2;
X#else
X return (char **) XPclose(w) + 1;
X#endif
X}
X
X/*
X * expand string
X */
Xchar *
Xevalstr(cp, f)
X register char *cp;
X int f;
X{
X XPtrV w;
X
X XPinit(w, 1);
X expand(cp, &w, f);
X cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w);
X XPfree(w);
X return cp;
X}
X
X/* for nested substitution: ${var:=$var2} */
Xtypedef struct SubType {
X short type; /* [=+-?%#] action after expanded word */
X short base; /* begin position of expanded word */
X char *name; /* name for ${var=word} */
X} SubType;
X
Xstatic void
Xexpand(cp, wp, f)
X char *cp; /* input word */
X register XPtrV *wp; /* output words */
X int f; /* DO* flags */
X{
X register int c;
X register int type = XBASE; /* expansion type */
X register int quote = 0; /* quoted */
X int quotestack[11]; /* Keep this bigger than the subtype stack */
X register int *qst = quotestack + 11; /* This too, of course */
X XString ds; /* destination string */
X register char *dp, *sp; /* dest., source */
X int fdo, word, combase; /* second pass flags; have word */
X Expand x; /* expansion variables */
X SubType subtype [10]; /* substitution type stack */
X register SubType *st = subtype + 10;
X int newlines; /* For trailing newlines in COMSUB */
X
X if (cp == NULL)
X errorf("eval:expand(NULL)\n");
X if (flag[FNOGLOB])
X f &= ~ DOGLOB;
X
X Xinit(ds, dp, 128); /* init dest. string */
X type = XBASE;
X sp = cp;
X fdo = 0;
X word = !(f&DOBLANK);
X
X while (1) {
X Xcheck(ds, dp);
X
X switch (type) {
X case XBASE: /* original prefixed string */
X c = *sp++;
X switch (c) {
X case EOS:
X c = 0;
X break;
X case CHAR:
X c = *sp++;
X break;
X case QCHAR:
X quote |= 2; /* temporary quote */
X c = *sp++;
X break;
X case OQUOTE:
X word = quote = 1;
X continue;
X case CQUOTE:
X quote = 0;
X continue;
X case COMSUB:
X type = comsub(&x, sp);
X sp = strchr(sp, 0) + 1;
X combase = Xsavepos(ds, dp);
X newlines = 0;
X continue;
X case OSUBST: /* ${var{:}[=+-?]word} */
X cp = sp; /* variable */
X sp = strchr(sp, 0) + 1; /* skip variable */
X c = (*sp == CSUBST) ? 0 : *sp++;
X if ((c&0x7F) == '#' || (c&0x7F) == '%') {
X if (flag[FNOUNSET] &&
X strval(global(cp)) == null)
X errorf("%s: unset variable\n", cp);
X f |= DOPAT;
X type = XBASE;
X *--qst = quote;
X quote = 0;
X } else
X type = varsub(&x, cp, c);
X if (type == XBASE) { /* expand? */
X if (st == subtype)
X errorf("ridiculous ${} nesting\n");
X --st;
X st->type = c;
X st->base = Xsavepos(ds, dp);
X st->name = cp;
X } else
X sp = wdscan(sp, CSUBST); /* skip word */
X continue;
X case CSUBST: /* only get here if expanding word */
X *dp = 0;
X if (f&DOGLOB)
X f &= ~DOPAT;
X switch (st->type&0x7F) {
X case '#':
X case '%':
X *dp = 0;
X dp = Xrestpos(ds, dp, st->base);
X quote = *qst++;
X x.str = trimsub(strval(global(st->name)),
X dp, st->type);
X type = XSUB;
X continue;
X case '=':
X#if 0
X if ((x.u.vp->flag&RDONLY))
X errorf("cannot set readonly %s\n", cp);
X#endif
X setstr(global(st->name), Xrestpos(ds, dp, st->base));
X break;
X case '?':
X if (dp == Xrestpos(ds, dp, st->base))
X errorf("missing value for %s\n", cp);
X else
X errorf("%s\n", Xrestpos(ds, dp, st->base));
X }
X st++;
X type = XBASE;
X continue;
X }
X break;
X
X case XSUB:
X if ((c = *x.str++) == 0) {
X type = XBASE;
X continue;
X }
X break;
X
X case XARGSEP:
X type = XARG;
X quote = 1;
X case XARG:
X if ((c = *x.str++) == 0) {
X if ((x.str = *x.u.strv++) == NULL) {
X type = XBASE;
X continue;
X } else if (quote && x.split) {
X /* terminate word for "$@" */
X type = XARGSEP;
X quote = 0;
X }
X c = ifs0;
X }
X break;
X
X case XCOM:
X if (newlines) { /* Spit out saved nl's */
X c = '\n';
X --newlines;
X } else {
X while ((c = getc(x.u.file)) == '\n')
X newlines++; /* Save newlines */
X if (newlines && c != EOF) {
X ungetc(c, x.u.file);
X c = '\n';
X --newlines;
X }
X }
X if (c == EOF) {
X cp = Xrestpos(ds, sp, combase);
X newlines = 0;
X fclose(x.u.file);
X if (x.split)
X waitlast();
X type = XBASE;
X continue;
X }
X break;
X }
X
X /* check for end of word or IFS separation */
X if (c == 0 || !quote && (f&DOBLANK) && ctype(c, C_IFS)) {
X if (word) {
X *dp++ = 0;
X cp = Xclose(ds, dp);
X if (fdo&DOTILDE)
X cp = tilde(cp);
X if (fdo&DOGLOB)
X glob(cp, wp);
X else
X {XPput(*wp, cp);}
X fdo = word = 0;
X if (c != 0)
X Xinit(ds, dp, 128);
X } else
X ; /* ignore IFS */
X if (c == 0)
X return;
X } else {
X /* mark any special second pass chars */
X if (!quote)
X switch (c) {
X case '*':
X case '?':
X case '[':
X if (f&(DOPAT|DOGLOB)) {
X fdo |= (f&DOGLOB);
X *dp++ = MAGIC;
X }
X break;
X case NOT:
X if ((f&(DOPAT|DOGLOB)) &&
X dp[-1] == '[' && dp[-2] == MAGIC) {
X *dp++ = MAGIC;
X }
X break;
X case '~':
X if ((f&DOTILDE) && dp == Xstring(ds, dp) ||
X !(f&DOBLANK) &&
X (dp[-1] == '=' || dp[-1] == ':')) {
X fdo |= DOTILDE;
X *dp++ = MAGIC;
X }
X break;
X }
X else
X quote &= ~2; /* undo temporary */
X
X word = 1;
X *dp++ = c; /* save output char */
X }
X }
X}
X
X/*
X * Prepare to generate the string returned by ${} substitution.
X */
Xstatic int
Xvarsub(xp, sp, stype)
X register Expand *xp;
X register char *sp;
X int stype;
X{
X register int c;
X int type;
X
X /* ${#var}, string length or argc */
X if (sp[0] == '#' && (c = sp[1]) != 0) {
X c = (c == '*' || c == '@') ? e.loc->argc :
X strlen(strval(global(sp+1)));
X xp->str = strsave(ulton((unsigned long)c, 10), ATEMP);
X return XSUB;
X }
X
X c = sp[0];
X if (c == '*' || c == '@') {
X if (e.loc->argc == 0) {
X xp->str = null;
X type = XSUB;
X } else {
X xp->u.strv = e.loc->argv + 1;
X xp->str = *xp->u.strv++;
X xp->split = c == '@'; /* $@ */
X type = XARG;
X }
X } else {
X xp->str = strval(global(sp));
X type = XSUB;
X }
X
X c = stype&0x7F;
X /* test the compiler's code generator */
X if (c == '%' || c == '#' ||
X (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
X c == '=' || c == '-' || c == '?' : c == '+'))
X type = XBASE; /* expand word instead of variable value */
X if (type != XBASE && flag[FNOUNSET] && xp->str == null && c != '+')
X errorf("%s: unset variable\n", sp);
X return type;
X}
X
X/*
X * Run the command in $(...) and read its output.
X */
Xstatic int
Xcomsub(xp, cp)
X register Expand *xp;
X char *cp;
X{
X Source *s;
X register struct op *t;
X FILE *fi;
X
X s = pushs(SSTRING);
X s->str = cp;
X t = compile(s);
X
X if (t != NULL && t->type == TCOM && /* $(<file) */
X *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
X register struct ioword *io = *t->ioact;
X
X if ((io->flag&IOTYPE) != IOREAD)
X errorf("funny $() command\n");
X fi = fopen(evalstr(io->name, DOTILDE), "r");
X if (fi != NULL)
X fileno(fi) = savefd(fileno(fi));
X xp->split = 0; /* no waitlast() */
X } else {
X int ofd1, pv[2];
X openpipe(pv);
X fi = fdopen(pv[0], "r");
X ofd1 = savefd(1);
X dup2(pv[1], 1);
X close(pv[1]);
X#if 0
X exchild(t, XXCOM|XPIPEO);
X#else
X execute(t, XFORK|XXCOM|XPIPEO);
X#endif
X dup2(ofd1, 1);
X close(ofd1);
X xp->split = 1; /* waitlast() */
X }
X
X if (fi == NULL)
X errorf("cannot open $() input\n");
X setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ);
X xp->u.file = fi;
X return XCOM;
X}
X
X/*
X * perform #pattern and %pattern substitution in ${}
X */
X
Xstatic char *
Xtrimsub(str, pat, how)
X register char *str;
X char *pat;
X int how;
X{
X register char *end = strchr(str, 0);
X register char *p, c, *match;
X
X switch (how&0xff) { /* UCHAR_MAX maybe? */
X case '#': /* shortest at begin */
X for (p = str; p <= end; p++) {
X c = *p; *p = '\0';
X if (gmatch(str, pat)) {
X *p = c;
X return p;
X }
X *p = c;
X }
X break;
X case '#'|0x80: /* longest match at begin */
X for (p = end; p >= str; p--) {
X c = *p; *p = '\0';
X if (gmatch(str, pat)) {
X *p = c;
X return p;
X }
X *p = c;
X }
X break;
X case '%': /* shortest match at end */
X for (p = end; p >= str; p--) {
X if (gmatch(p, pat)) {
X c = *p; *p = '\0';
X match = strsave( str, APERM ); /* APERM? */
X *p = c;
X return match;
X }
X }
X break;
X case '%'|0x80: /* longest match at end */
X for (p = str; p <= end; p++) {
X if (gmatch(p, pat)) {
X c = *p; *p = '\0';
X match = strsave( str, ATEMP ); /* APERM? */
X *p = c;
X return match;
X }
X }
X break;
X }
X
X return str; /* no match, return string */
X}
X
X/*
X * glob
X * Name derived from V6's /etc/glob, the program that expanded filenames.
X */
X
Xstatic char *debunk();
X
Xstatic void
Xglob(cp, wp)
X char *cp;
X register XPtrV *wp;
X{
X char path [PATH];
X register char *sp = cp;
X int oldsize;
X
X oldsize = XPsize(*wp);
X globit(path, path, sp, wp, 0);
X
X if (XPsize(*wp) == oldsize)
X {XPput(*wp, debunk(cp));}
X else
X qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
X}
X
Xstatic void
Xglobit(ds, dp, sp, wp, check)
X char *ds; /* dest path */
X char *dp; /* dest end */
X char *sp; /* source path */
X register XPtrV *wp; /* output list */
X int check; /* check dest existence */
X{
X register char *np; /* next source component */
X register char *tsp, *tdp;
X
X if (sp == NULL) { /* end of source path */
X if (check && eaccess(ds, 0) < 0)
X return;
X XPput(*wp, strsave(ds, ATEMP));
X return;
X }
X
X if (dp > ds)
X *dp++ = '/';
X while (*sp == '/')
X *dp++ = *sp++;
X np = strchr(sp, '/');
X if (np != NULL)
X *np++ = 0;
X
X *dp = 0;
X if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */
X tdp = dp; tsp = sp;
X while ((*tdp++ = *tsp++) != 0)
X ;
X --tdp;
X globit(ds, tdp, np, wp, check);
X } else {
X DIR *dirp;
X struct dirent *d;
X
X /* ToDo:
X * should not attemp to open() special files: /dev/ttyd0/*
X * opendir should do this check, but Doug Gwyn's does not.
X */
X dirp = opendir((*ds == 0) ? "." : ds);
X if (dirp == NULL)
X goto Nodir;
X while ((d = readdir(dirp)) != NULL) {
X tsp = d->d_name;
X if (tsp[0] == '.' &&
X (tsp[1] == 0 || tsp[1] == '.' && tsp[2] == 0))
X continue; /* always ignore . and .. */
X if (*tsp == '.' && *sp != '.' || !gmatch(tsp, sp))
X continue;
X
X tdp = dp;
X while ((*tdp++ = *tsp++) != 0)
X ;
X --tdp;
X globit(ds, tdp, np, wp, np != NULL);
X }
X closedir(dirp);
X Nodir:;
X }
X
X if (np != NULL)
X *--np = '/';
X}
X
X/* remove MAGIC from string */
Xstatic char *
Xdebunk(cp)
X char *cp;
X{
X register char *dp, *sp;
X
X for (dp = sp = cp; *sp != 0; sp++)
X if (*sp != MAGIC)
X *dp++ = *sp;
X *dp = 0;
X return cp;
X}
X
X/*
X * tilde expansion
X *
X * based on a version by Arnold Robbins
X */
X
Xstatic char *homedir();
X
Xstatic char *
Xtilde(acp)
X char *acp;
X{
X register int c;
X char path [PATH+1];
X register char *cp = acp, *wp = path, *dp;
X char userid [16+1];
X
X Again:
X while (1) {
X if ((c = *cp++) == 0) {
X *wp = 0;
X afree((Void*)acp, ATEMP);
X return strsave(path, ATEMP);
X } else if (c == MAGIC && *cp == '~')
X break;
X else
X *wp++ = c;
X }
X
X dp = NULL; /* no output substitution */
X if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */
X dp = strval(global("HOME")), cp += 1;
X else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
X dp = strval(global("PWD")), cp += 2;
X else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
X dp = strval(global("OLDPWD")), cp += 2;
X else if (letter(cp[1])) {
X char *save = cp;
X for (dp = userid, cp++; letnum(*cp) && dp < userid+16; )
X *dp++ = *cp++;
X *dp = 0;
X dp = homedir(userid);
X if (dp == NULL)
X cp = save;
X }
X /* substitute */
X if (dp != NULL)
X while (*dp != 0)
X *wp++ = *dp++;
X goto Again;
X}
X
X/*
X * map userid to user's home directory.
X * todo: implement a cache with the "homedirs" table.
X * note that 4.3's getpw adds more than 6K to the shell,
X * and the YP version probably adds much more.
X * we might consider our own version of getpwnam() to keep the size down.
X */
X
Xstatic char *
Xhomedir(name)
X char *name;
X{
X register struct tbl *ap;
X register struct passwd *pw;
X extern struct passwd *getpwnam();
X
X ap = tsearch(&homedirs, name, hash(name));
X if ((ap != NULL && (ap->flag&ISSET)))
X return ap->val.s;
X pw = getpwnam(name);
X if (pw == NULL)
X return NULL;
X return pw->pw_dir;
X}
END_OF_FILE
if test 14245 -ne `wc -c <'sh/eval.c'`; then
echo shar: \"'sh/eval.c'\" unpacked with wrong size!
fi
# end of 'sh/eval.c'
fi
if test -f 'sh/lex.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sh/lex.c'\"
else
echo shar: Extracting \"'sh/lex.c'\" \(12416 characters\)
sed "s/^X//" >'sh/lex.c' <<'END_OF_FILE'
X/*
X * lexical analysis and source input
X */
X
X#ifndef lint
Xstatic char *RCSid = "$Id: lex.c,v 3.6 89/03/27 15:51:20 egisin Exp $";
Xstatic char *sccs_id = "@(#)lex.c 1.3 91/11/09 15:35:19 (sjg)";
X#endif
X
X#include <stddef.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <unistd.h>
X#include <assert.h>
X#include "sh.h"
X#include "lex.h"
X#include "tree.h"
X#include "table.h"
X#include "expand.h"
X
X int ttyfd = -1; /* tty fd for edit and jobs */
X char *history[HISTORY]; /* saved commands */
X char **histptr = history - 1; /* last history item */
X int histpush; /* number of pushed fc commands */
X
X/* we set s->str to NULLSTR instead of "", so that ungetsc() works */
Xstatic char nullstr [] = {0, 0};
X#define NULLSTR (nullstr + 1)
X
Xstatic int expanding_alias;
Xstatic int alias;
Xstatic int getsc_ ARGS((void));
X
X/* optimized getsc_() */
X#define getsc() ((*source->str != 0) ? *source->str++ : getsc_())
X#define ungetsc() (source->str--)
X
X/*
X * Lexical analyzer
X *
X * tokens are not regular expressions, they are LL(1).
X * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
X * hence the state stack.
X */
X
Xint
Xyylex(cf)
X int cf;
X{
X register int c, state;
X char states [64], *statep = states;
X XString ws; /* expandable output word */
X register char *wp; /* output word pointer */
X register char *sp, *dp;
X int istate;
X int c2;
X static int rec_alias_cnt = 0;
X static struct tbl *rec_alias_table[20];
X
X
X if (expanding_alias) {
X expanding_alias = 0;
X while (rec_alias_cnt-- > 0)
X rec_alias_table[rec_alias_cnt]->flag &= ~EXPALIAS;
X rec_alias_cnt = 0;
X }
X Again:
X Xinit(ws, wp, 64);
X
X if (cf&ONEWORD)
X istate = SWORD;
X else if (cf&LETEXPR)
X istate = SDPAREN;
X else { /* normal lexing */
X istate = SBASE;
X while ((c = getsc()) == ' ' || c == '\t')
X ;
X if (c == '#')
X while ((c = getsc()) != 0 && c != '\n')
X ;
X ungetsc();
X }
X if (alias) { /* trailing ' ' in alias definition */
X alias = 0;
X cf |= ALIAS;
X }
X
X /* collect non-special or quoted characters to form word */
X for (*statep = state = istate;
X !((c = getsc()) == 0 || state == SBASE && ctype(c, C_LEX1)); ) {
X Xcheck(ws, wp);
X switch (state) {
X case SBASE:
X Sbase:
X switch (c) {
X case '\\':
X c = getsc();
X if (c != '\n')
X *wp++ = QCHAR, *wp++ = c;
X else
X if (wp == Xstring(ws, wp))
X goto Again;
X break;
X case '\'':
X *++statep = state = SSQUOTE;
X *wp++ = OQUOTE;
X break;
X case '"':
X *++statep = state = SDQUOTE;
X *wp++ = OQUOTE;
X break;
X default:
X goto Subst;
X }
X break;
X
X Subst:
X switch (c) {
X case '\\':
X c = getsc();
X switch (c) {
X case '\n':
X break;
X case '"': case '\\':
X case '$': case '`':
X *wp++ = QCHAR, *wp++ = c;
X break;
X default:
X Xcheck(ws, wp);
X *wp++ = CHAR, *wp++ = '\\';
X *wp++ = CHAR, *wp++ = c;
X break;
X }
X break;
X case '$':
X c = getsc();
X if (c == '(') {
X *++statep = state = SPAREN;
X *wp++ = COMSUB;
X } else
X if (c == '{') {
X *++statep = state = SBRACE;
X *wp++ = OSUBST;
X c = getsc();
X do {
X Xcheck(ws, wp);
X *wp++ = c;
X c = getsc();
X } while (ctype(c, C_ALPHA|C_DIGIT));
X *wp++ = 0;
X /* todo: more compile-time checking */
X if (c == '}')
X ungetsc();
X else if (c == '#' || c == '%') {
X /* Korn pattern trimming */
X if (getsc() == c)
X c |= 0x80;
X else
X ungetsc();
X *wp++ = c;
X } else if (c == ':')
X *wp++ = 0x80|getsc();
X else
X *wp++ = c;
X } else if (ctype(c, C_ALPHA)) {
X *wp++ = OSUBST;
X do {
X Xcheck(ws, wp);
X *wp++ = c;
X c = getsc();
X } while (ctype(c, C_ALPHA|C_DIGIT));
X *wp++ = 0;
X *wp++ = CSUBST;
X ungetsc();
X } else if (ctype(c, C_DIGIT|C_VAR1)) {
X Xcheck(ws, wp);
X *wp++ = OSUBST;
X *wp++ = c;
X *wp++ = 0;
X *wp++ = CSUBST;
X } else {
X *wp++ = CHAR, *wp++ = '$';
X ungetsc();
X }
X break;
X case '`':
X *++statep = state = SBQUOTE;
X *wp++ = COMSUB;
X break;
X default:
X *wp++ = CHAR, *wp++ = c;
X }
X break;
X
X case SSQUOTE:
X if (c == '\'') {
X state = *--statep;
X *wp++ = CQUOTE;
X } else
X *wp++ = QCHAR, *wp++ = c;
X break;
X
X case SDQUOTE:
X if (c == '"') {
X state = *--statep;
X *wp++ = CQUOTE;
X } else
X goto Subst;
X break;
X
X case SPAREN:
X if (c == '(')
X *++statep = state;
X else if (c == ')')
X state = *--statep;
X if (state == SPAREN)
X *wp++ = c;
X else
X *wp++ = 0; /* end of COMSUB */
X break;
X
X case SBRACE:
X if (c == '}') {
X state = *--statep;
X *wp++ = CSUBST;
X } else
X goto Sbase;
X break;
X
X case SBQUOTE:
X if (c == '`') {
X *wp++ = 0;
X state = *--statep;
X } else if (c == '\\') {
X switch (c = getsc()) {
X case '\n':
X break;
X case '\\':
X case '$': case '`':
X *wp++ = c;
X break;
X default:
X *wp++ = '\\';
X *wp++ = c;
X break;
X }
X } else
X *wp++ = c;
X break;
X
X case SWORD: /* ONEWORD */
X goto Subst;
X
X case SDPAREN: /* LETEXPR */
X if (c == ')') {
X if (getsc() == ')') {
X c = 0;
X goto Done;
X } else
X ungetsc();
X }
X goto Subst;
X }
X }
XDone:
X Xcheck(ws, wp);
X if (state != istate)
X yyerror("no closing quote");
X
X if (c == '<' || c == '>') {
X char *cp = Xstring(ws, wp);
X if (wp > cp && cp[0] == CHAR && digit(cp[1])) {
X wp = cp; /* throw away word */
X c2/*unit*/ = cp[1] - '0';
X } else
X c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */
X }
X
X if (wp == Xstring(ws, wp) && state == SBASE) {
X Xfree(ws, sp); /* free word */
X /* no word, process LEX1 character */
X switch (c) {
X default:
X return c;
X
X case '|':
X case '&':
X case ';':
X if (getsc() == c)
X c = (c == ';') ? BREAK :
X (c == '|') ? LOGOR :
X (c == '&') ? LOGAND :
X YYERRCODE;
X else
X ungetsc();
X return c;
X
X case '>':
X case '<': {
X register struct ioword *iop;
X
X iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
X iop->unit = c2/*unit*/;
X
X c2 = getsc();
X if (c2 == '>' || c2 == '<') {
X iop->flag = c != c2 ? IORDWR : c == '>' ? IOCAT : IOHERE;
X c2 = getsc();
X } else
X iop->flag = c == '>' ? IOWRITE : IOREAD;
X
X if (iop->flag == IOHERE)
X if (c2 == '-')
X iop->flag |= IOSKIP;
X else
X ungetsc();
X else
X if (c2 == '&')
X iop->flag = IODUP;
X else if (c2 == '!' && iop->flag == IOWRITE)
X iop->flag |= IOCLOB;
X else
X ungetsc();
X yylval.iop = iop;
X return REDIR;
X }
X case '\n':
X gethere();
X if (cf & CONTIN)
X goto Again;
X return c;
X
X case '(':
X c2 = getsc();
X if (c2 == ')')
X c = MPAREN;
X else if (c2 == '(')
X c = MDPAREN;
X else
X ungetsc();
X case ')':
X return c;
X }
X }
X
X *wp++ = EOS; /* terminate word */
X yylval.cp = Xclose(ws, wp);
X if (state == SWORD || state == SDPAREN) /* ONEWORD? */
X return LWORD;
X ungetsc(); /* unget terminator */
X
X /* copy word to unprefixed string ident */
X for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
X *dp++ = *sp++;
X /* Make sure the ident array stays '\0' padded */
X while (dp <= ident+IDENT)
X *dp++ = '\0';
X#if 0
X if (*ident == '~' || (dp = strchr(ident, '=')) != NULL && dp[1] == '~')
X "Tilde expansion";
X#endif
X if (c != EOS)
X *ident = 0; /* word is not unquoted */
X
X if (*ident != 0 && (cf&(KEYWORD|ALIAS))) {
X register struct tbl *p;
X
X p = tsearch(&lexicals, ident, hash(ident));
X if (p != NULL && (p->flag&ISSET))
X if (p->type == CKEYWD && (cf&KEYWORD)) {
X afree(yylval.cp, ATEMP);
X return p->val.i;
X } else if (p->type == CALIAS && (cf&ALIAS) &&
X !(p->flag&EXPALIAS)) {
X register Source *s;
X
X if (rec_alias_cnt == sizeof(rec_alias_table)/sizeof(rec_alias_table[0]))
X yyerror("excessive recusrsive aliasing");
X else
X rec_alias_table[rec_alias_cnt++] = p;
X p->flag |= EXPALIAS;
X /* check for recursive aliasing */
X for (s = source; s->type == SALIAS; s = s->next)
X if (s->u.tblp == p)
X return LWORD;
X afree(yylval.cp, ATEMP);
X
X /* push alias expansion */
X s = pushs(SALIAS);
X s->str = p->val.s;
X s->u.tblp = p;
X s->next = source;
X source = s;
X goto Again;
X }
X }
X
X return LWORD;
X}
X
Xstatic void readhere();
X
Xgethere()
X{
X register struct ioword **p;
X
X for (p = heres; p < herep; p++)
X readhere(*p);
X herep = heres;
X}
X
X/*
X * read "<<word" text into temp file
X * todo: set up E_ERR to fclose(f) on unwind
X */
X
Xstatic void
Xreadhere(iop)
X register struct ioword *iop;
X{
X register FILE *f;
X struct temp *h;
X register int c;
X char *eof;
X register char *cp;
X char line [LINE+1];
X
X eof = evalstr(iop->name, 0);
X
X h = maketemp(ATEMP);
X h->next = e.temps; e.temps = h;
X iop->name = h->name;
X f = fopen(h->name, "w");
X if (f == NULL)
X errorf("Cannot create temporary file\n");
X setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
X
X for (;;) {
X cp = line;
X while ((c = getsc()) != '\n') {
X if (c == 0)
X errorf("here document `%s' unclosed\n", eof);
X if (cp >= line+LINE)
X break;
X *cp++ = c;
X }
X ungetsc();
X *cp = 0;
X for (cp = line; iop->flag&IOSKIP && *cp == '\t'; cp++)
X ;
X if (strcmp(eof, cp) == 0 || c == 0)
X break;
X while ((c = *cp++) != '\0')
X putc(c, f);
X while ((c = getsc()) != '\n') {
X if (c == 0)
X errorf("here document `%s' unclosed\n", eof);
X putc(c, f);
X }
X putc(c, f);
X }
X fclose(f);
X}
X
Xvoid
Xyyerror(msg)
X Const char *msg;
X{
X yynerrs++;
X while (source->type == SALIAS) /* pop aliases */
X source = source->next;
X if (source->file != NULL)
X shellf("%s[%d]: ", source->file, source->line);
X source->str = NULLSTR; /* zap pending input */
X errorf("%s\n", msg);
X}
X
X/*
X * input for yylex with alias expansion
X */
X
XSource *
Xpushs(type)
X int type;
X{
X register Source *s;
X
X s = (Source *) alloc(sizeof(Source), ATEMP);
X s->type = type;
X s->str = NULLSTR;
X s->line = 0;
X s->file = NULL;
X s->echo = 0;
X s->next = NULL;
X return s;
X}
X
Xstatic int
Xgetsc_()
X{
X register Source *s = source;
X register int c;
X extern void mprint();
X
X while ((c = *s->str++) == 0) {
X s->str = NULL; /* return 0 for EOF by default */
X switch (s->type) {
X case SEOF:
X s->str = NULLSTR;
X return 0;
X
X case STTY:
X if (histpush < 0) { /* commands pushed by dofc */
X s->type = SHIST;
X s->str = NULLSTR;
X continue;
X }
X s->line++;
X s->str = line;
X line[0] = '\0';
X mprint();
X pprompt(prompt);
X flushshf(1); flushshf(2);
X#ifdef EDIT
X#ifdef EMACS
X if (flag[FEMACS])
X c = x_read(ttyfd, line, LINE);
X else
X#endif
X#ifdef VI
X if (flag[FVI])
X c = x_read(ttyfd, line, LINE);
X else
X#endif
X#endif
X c = read(ttyfd, line, LINE);
X if (c < 0) /* read error */
X c = 0;
X line[c] = '\0';
X prompt = strval(global("PS2"));
X if (c == 0) { /* EOF */
X s->str = NULL;
X s->line--;
X } else {
X c = 0;
X while(line[c] && ctype(line[c], C_IFS))
X c++;
X if (!line[c]) {
X s->str = &line[c - 1];
X s->line--;
X } else {
X s->str = &line[c];
X histsave(s->str);
X }
X }
X break;
X
X case SHIST:
X if (histpush == 0) {
X s->type = STTY;
X s->str = NULLSTR;
X continue;
X }
X s->line++;
X s->str = histptr[++histpush];
X#if 0
X pprompt("!< "); /* todo: PS9 */
X#endif
X shellf("%s\n", s->str);
X strcpy(line, s->str);
X s->str = strchr(line, 0);
X *s->str++ = '\n';
X *s->str = 0;
X s->str = line;
X break;
X
X case SFILE:
X s->line++;
X s->str = fgets(line, LINE, s->u.file);
X if (s->str == NULL)
X if (s->u.file != stdin)
X fclose(s->u.file);
X break;
X
X case SWSTR:
X break;
X
X case SSTRING:
X s->str = "\n";
X s->type = SEOF;
X break;
X
X case SWORDS:
X s->str = *s->u.strv++;
X s->type = SWORDSEP;
X break;
X
X case SWORDSEP:
X if (*s->u.strv == NULL) {
X s->str = "\n";
X s->type = SEOF;
X } else {
X s->str = " ";
X s->type = SWORDS;
X }
X break;
X
X case SALIAS:
X s->str = s->u.tblp->val.s;
X if (s->str[0] != 0) {
X c = strchr(s->str, 0)[-1];
X if (c == ' ' || c == '\t')
X alias = 1; /* trailing ' ' */
X }
X source = s = s->next;
X expanding_alias = 1;
X continue;
X }
X if (s->str == NULL) {
X s->type = SEOF;
X s->str = NULLSTR;
X return 0;
X }
X if (s->echo)
X fputs(s->str, shlout);
X }
X return c;
X}
X
Xpprompt(cp)
X register char *cp;
X{
X while (*cp != 0)
X if (*cp != '!')
X putc(*cp++, shlout);
X else
X if (*++cp == '!')
X putc(*cp++, shlout);
X else
X shellf("%d", source->line);
X fflush(shlout);
X}
END_OF_FILE
if test 12416 -ne `wc -c <'sh/lex.c'`; then
echo shar: \"'sh/lex.c'\" unpacked with wrong size!
fi
# end of 'sh/lex.c'
fi
if test -f 'sh/syn.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sh/syn.c'\"
else
echo shar: Extracting \"'sh/syn.c'\" \(9735 characters\)
sed "s/^X//" >'sh/syn.c' <<'END_OF_FILE'
X/*
X * shell parser (C version)
X */
X
Xstatic char *RCSid = "$Id: syn.c,v 3.3 89/03/27 15:51:51 egisin Exp $";
X
X#include <stddef.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X#include "lex.h"
X#include "tree.h"
X#include "table.h"
X#include "expand.h"
X
Xstatic void zzerr();
Xstatic struct op *block(), *newtp();
Xstatic struct op *pipeline(), *andor(), *command();
Xstatic struct op *nested(), *c_list();
Xstatic struct op *dogroup(), *thenpart(), *casepart(), *caselist();
Xstatic struct op *elsepart();
Xstatic char **wordlist();
Xstatic void musthave();
Xstatic struct ioword *synio(), *io();
X
Xstatic struct op *outtree; /* yyparse output */
X
Xstatic int reject; /* token(cf) gets symbol again */
Xstatic int symbol; /* yylex value */
X
X#define REJECT (reject = 1)
X#define ACCEPT (reject = 0)
X#define token(cf) \
X ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
X#define tpeek(cf) \
X ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
X
Xint
Xyyparse()
X{
X ACCEPT;
X yynerrs = 0;
X if ((tpeek(KEYWORD|ALIAS)) == 0) { /* EOF */
X outtree = newtp(TEOF);
X return 0;
X }
X outtree = c_list();
X musthave('\n', 0);
X return (yynerrs != 0);
X}
X
Xstatic struct op *
Xpipeline(cf)
X int cf;
X{
X register struct op *t, *p, *tl = NULL;
X register int c;
X
X t = command(cf);
X if (t != NULL) {
X while ((c = token(0)) == '|') {
X if ((p = command(CONTIN)) == NULL)
X SYNTAXERR;
X if (tl == NULL)
X t = tl = block(TPIPE, t, p, NOWORDS);
X else
X tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
X /*t = block(TPIPE, t, p, NOWORDS);*/
X }
X REJECT;
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 = token(0)) == LOGAND || c == LOGOR) {
X if ((p = pipeline(CONTIN)) == NULL)
X SYNTAXERR;
X t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
X }
X REJECT;
X }
X return (t);
X}
X
Xstatic struct op *
Xc_list()
X{
X register struct op *t, *p, *tl = NULL;
X register int c;
X
X t = andor();
X if (t != NULL) {
X while ((c = token(0)) == ';' || c == '&' ||
X (multiline || source->type == SSTRING
X || source->type == SALIAS) && c == '\n') {
X if (c == '&') {
X if (tl)
X tl->right = block(TASYNC, tl->right, NOBLOCK, NOWORDS);
X else
X t = block(TASYNC, t, NOBLOCK, NOWORDS);
X }
X if ((p = andor()) == NULL)
X return (t);
X if (tl == NULL)
X t = tl = block(TLIST, t, p, NOWORDS);
X else
X tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
X }
X REJECT;
X }
X return (t);
X}
X
Xstatic struct ioword *
Xsynio(cf)
X int cf;
X{
X register struct ioword *iop;
X
X if (tpeek(cf) != REDIR)
X return NULL;
X ACCEPT;
X iop = yylval.iop;
X musthave(LWORD, 0);
X iop->name = yylval.cp;
X if ((iop->flag&IOTYPE) == IOHERE) {
X if (*ident != 0) /* unquoted */
X iop->flag |= IOEVAL;
X if (herep >= &heres[HERES])
X errorf("too many <<'s\n");
X *herep++ = iop;
X }
X return iop;
X}
X
Xstatic void
Xmusthave(c, cf)
X int c, cf;
X{
X if ((token(cf)) != c)
X SYNTAXERR;
X}
X
Xstatic struct op *
Xnested(type, mark)
X int type, mark;
X{
X register struct op *t;
X
X multiline++;
X t = c_list();
X musthave(mark, KEYWORD);
X multiline--;
X return (block(type, t, NOBLOCK, NOWORDS));
X}
X
Xstatic struct op *
Xcommand(cf)
X int cf;
X{
X register struct op *t;
X register int c, iopn = 0;
X struct ioword *iop, **iops;
X XPtrV args, vars;
X
X iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1), ATEMP);
X XPinit(args, 16);
X XPinit(vars, 16);
X
X if (multiline)
X cf = CONTIN;
X cf |= KEYWORD|ALIAS;
X
X while ((iop = synio(cf)) != NULL) {
X if (iopn >= NUFILE)
X yyerror("too many redirections");
X iops[iopn++] = iop;
X cf &=~ CONTIN;
X }
X
X switch (c = token(cf)) {
X case 0:
X yyerror("unexpected EOF");
X return NULL;
X
X default:
X REJECT;
X if (iopn == 0)
X return NULL; /* empty line */
X t = newtp(TCOM);
X break;
X
X case LWORD:
X case MDPAREN:
X REJECT;
X t = newtp(TCOM);
X if (c == MDPAREN) {
X ACCEPT;
X XPput(args,"let");
X musthave(LWORD,LETEXPR);
X XPput(args,yylval.cp);
X }
X while (1)
X switch (tpeek(0)) {
X case REDIR:
X if (iopn >= NUFILE)
X yyerror("too many redirections");
X iops[iopn++] = synio(0);
X break;
X
X case LWORD:
X ACCEPT;
X if ((XPsize(args) == 0 || flag[FKEYWORD])
X && strchr(ident+1, '='))
X {XPput(vars, yylval.cp);}
X else
X {XPput(args, yylval.cp);}
X break;
X
X case MPAREN:
X ACCEPT;
X if (XPsize(args) != 1)
X SYNTAXERR;
X if (*ident == 0)
X yyerror("invalid function name\n");
X t = newtp(TFUNCT);
X t->str = strsave(ident, ATEMP);
X musthave('{', CONTIN|KEYWORD);
X t->left = nested(TBRACE, '}');
X return t;
X
X default:
X goto Leave;
X }
X Leave:
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(TFOR);
X musthave(LWORD, 0);
X t->str = strsave(ident, ATEMP);
X multiline++;
X t->vars = wordlist();
X t->left = dogroup(0);
X multiline--;
X break;
X
X case WHILE:
X case UNTIL:
X multiline++;
X t = newtp((c == WHILE) ? TWHILE: TUNTIL);
X t->left = c_list();
X t->right = dogroup(1);
X multiline--;
X break;
X
X case CASE:
X t = newtp(TCASE);
X musthave(LWORD, 0);
X t->str = yylval.cp;
X multiline++;
X musthave(IN, KEYWORD|CONTIN);
X t->left = caselist();
X musthave(ESAC, KEYWORD);
X multiline--;
X break;
X
X case IF:
X multiline++;
X t = newtp(TIF);
X t->left = c_list();
X t->right = thenpart();
X musthave(FI, KEYWORD);
X multiline--;
X break;
X
X case TIME:
X t = pipeline(CONTIN);
X t = block(TTIME, t, NOBLOCK, NOWORDS);
X break;
X
X case FUNCTION:
X t = newtp(TFUNCT);
X musthave(LWORD, 0);
X t->str = strsave(ident, ATEMP);
X musthave('{', CONTIN|KEYWORD);
X t->left = nested(TBRACE, '}');
X break;
X
X#if 0
X case MDPAREN:
X t = newtp(TCOM);
X XPput(args, "let");
X musthave(LWORD, LETEXPR);
X XPput(args, yylval.cp);
X while (tpeek(0) == REDIR) {
X if (iopn >= NUFILE)
X yyerror("too many redirections");
X iops[iopn++] = synio(0);
X }
X break;
X#endif
X }
X
X while ((iop = synio(0)) != NULL) {
X if (iopn >= NUFILE)
X yyerror("too many redirections");
X iops[iopn++] = iop;
X }
X
X if (iopn == 0) {
X afree((Void*) iops, ATEMP);
X t->ioact = NULL;
X } else {
X iops[iopn++] = NULL;
X aresize((Void*) iops, sizeofN(struct ioword *, iopn), ATEMP);
X t->ioact = iops;
X }
X
X if (t->type == TCOM) {
X XPput(args, NULL);
X t->args = (char **) XPclose(args);
X XPput(vars, NULL);
X t->vars = (char **) XPclose(vars);
X } else {
X XPfree(args);
X XPfree(vars);
X }
X
X return t;
X}
X
Xstatic struct op *
Xdogroup(onlydone)
X int onlydone;
X{
X register int c;
X register struct op *list;
X
X c = token(CONTIN|KEYWORD);
X if (c == DONE && onlydone)
X return NULL;
X if (c != DO)
X SYNTAXERR;
X list = c_list();
X musthave(DONE, KEYWORD);
X return list;
X}
X
Xstatic struct op *
Xthenpart()
X{
X register int c;
X register struct op *t;
X
X if ((c = token(0)) != THEN) {
X REJECT;
X return NULL;
X }
X t = newtp(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 = token(0)) {
X case ELSE:
X if ((t = c_list()) == NULL)
X SYNTAXERR;
X return (t);
X
X case ELIF:
X t = newtp(TELIF);
X t->left = c_list();
X t->right = thenpart();
X return (t);
X
X default:
X REJECT;
X return NULL;
X }
X}
X
Xstatic struct op *
Xcaselist()
X{
X register struct op *t, *tl;
X
X t = tl = NULL;
X while ((tpeek(CONTIN|KEYWORD)) != ESAC) {
X struct op *tc = casepart();
X if (tl == NULL)
X t = tl = tc, tl->right = NULL;
X else
X tl->right = tc, tl = tc;
X }
X return (t);
X}
X
Xstatic struct op *
Xcasepart()
X{
X register struct op *t;
X register int c, cf;
X XPtrV ptns;
X
X XPinit(ptns, 16);
X t = newtp(TPAT);
X cf = CONTIN|KEYWORD;
X c = token(cf);
X if (c != '(')
X REJECT;
X else
X cf = 0;
X do {
X musthave(LWORD, cf);
X XPput(ptns, yylval.cp);
X cf = 0;
X } while ((c = token(0)) == '|');
X REJECT;
X XPput(ptns, NULL);
X t->vars = (char **) XPclose(ptns);
X musthave(')', 0);
X
X t->left = c_list();
X if ((tpeek(CONTIN|KEYWORD)) != ESAC)
X musthave(BREAK, CONTIN|KEYWORD);
X return (t);
X}
X
Xstatic char **
Xwordlist()
X{
X register int c;
X XPtrV args;
X
X XPinit(args, 16);
X if ((c = token(CONTIN|KEYWORD)) != IN) {
X REJECT;
X return NULL;
X }
X while ((c = token(0)) == LWORD)
X XPput(args, yylval.cp);
X if (c != '\n' && c != ';')
X SYNTAXERR;
X if (XPsize(args) == 0) {
X XPfree(args);
X return NULL;
X } else {
X XPput(args, NULL);
X return (char **) XPclose(args);
X }
X}
X
X/*
X * supporting functions
X */
X
Xstatic struct op *
Xblock(type, t1, t2, wp)
X struct op *t1, *t2;
X char **wp;
X{
X register struct op *t;
X
X t = newtp(type);
X t->left = t1;
X t->right = t2;
X t->vars = wp;
X return (t);
X}
X
XConst struct res {
X char *name;
X int 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 "function", FUNCTION,
X "time", TIME,
X "{", '{',
X "}", '}',
X 0
X};
X
Xkeywords()
X{
X register struct res Const *rp;
X register struct tbl *p;
X
X for (rp = restab; rp->name; rp++) {
X p = tenter(&lexicals, rp->name, hash(rp->name));
X p->flag |= DEFINED|ISSET;
X p->type = CKEYWD;
X p->val.i = rp->val;
X }
X}
X
Xstatic struct op *
Xnewtp(type)
X int type;
X{
X register struct op *t;
X
X t = (struct op *) alloc(sizeof(*t), ATEMP);
X t->type = type;
X t->args = t->vars = NULL;
X t->ioact = NULL;
X t->left = t->right = NULL;
X t->str = NULL;
X return (t);
X}
X
Xstatic void
Xzzerr()
X{
X yyerror("syntax error");
X}
X
Xstruct op *
Xcompile(s)
X Source *s;
X{
X yynerrs = 0;
X multiline = 0;
X herep = heres;
X source = s;
X if (yyparse())
X unwind();
X if (s->type == STTY || s->type == SFILE || s->type == SHIST)
X s->str = null; /* line is not preserved */
X return outtree;
X}
X
END_OF_FILE
if test 9735 -ne `wc -c <'sh/syn.c'`; then
echo shar: \"'sh/syn.c'\" unpacked with wrong size!
fi
# end of 'sh/syn.c'
fi
echo shar: End of archive 4 \(of 9\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 9 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.