+/*
+
+ * Copyright (c) 1984, 1985, 1986 AT&T
+ * All Rights Reserved
+
+ * THIS IS UNPUBLISHED PROPRIETARY SOURCE
+ * CODE OF AT&T.
+ * The copyright notice above does not
+ * evidence any actual or intended
+ * publication of such source code.
+
+ */
+/* @(#)cmd.c 1.1 */
+/*
+ * UNIX shell
+ *
+ * S. R. Bourne
+ * Rewritten by David Korn
+ * AT&T Bell Laboratories
+ *
+ */
+
+#include "defs.h"
+#include "sym.h"
+#include "flags.h"
+#include "name.h"
+#include "io.h"
+#include "history.h"
+#include "mode.h"
+#include "stak.h"
+#include "shtype.h"
+#include "brkincr.h"
+#include "builtins.h"
+
+
+/* These routines are defined by this module */
+void synbad();
+TREPTR cmd();
+TREPTR makefork();
+
+/* These routines are referenced by this module */
+extern void addblok();
+extern void chkpr();
+extern void exitsh();
+extern void free();
+extern STKPTR getstak();
+extern void hist_cancel();
+extern void hist_flush();
+extern long hist_position();
+extern char *malloc();
+extern char *movstr();
+extern void p_setout();
+extern void p_str();
+extern void p_prp();
+extern void p_num();
+
+static TREPTR makelist();
+static ARGPTR qscan();
+static IOPTR inout();
+static void chkword();
+static void chkflags();
+static void chksym();
+static TREPTR term();
+static TREPTR list();
+static REGPTR syncase();
+static TREPTR item();
+static int skipnl();
+static void prsym();
+
+
+static int heredoc;
+
+/*
+ * ======== command line decoding ========
+ *
+ * This is the parser for a shell command line
+ */
+
+
+
+
+/*
+ * Make a node which will cause the shell to fork
+ */
+
+TREPTR makefork(flgs, i)
+int flgs;
+TREPTR i;
+{
+ register FORKPTR t;
+ t=(FORKPTR) getstak(FORKTYPE);
+ t->forktyp = flgs|TFORK;
+ t->forktre = i;
+ t->forkio = 0;
+ return((TREPTR)t);
+}
+
+/*
+ * Make a node corresponding to a command list
+ */
+
+static TREPTR makelist(type,i,r)
+int type;
+TREPTR i, r;
+{
+ register LSTPTR t;
+ if(i==0 || r==0)
+ synbad();
+ else
+ {
+ t = (LSTPTR) getstak(LSTTYPE);
+ t->lsttyp = type;
+ t->lstlef = i;
+ t->lstrit = r;
+ }
+ return((TREPTR)t);
+}
+
+/*
+ * cmd
+ * empty
+ * list
+ * list & [ cmd ]
+ * list [ ; cmd ]
+ */
+
+TREPTR cmd(sym,flg)
+register int sym;
+int flg;
+{
+ register int flag = FINT|FPRS|FAMP;
+ register TREPTR i, e;
+ IOPTR saviotemp = iotemp;
+ /* parser output goes on standard error */
+ p_setout(stderr);
+ i = list(flg);
+ if(wdval==NL)
+ {
+ if(flg&NLFLG)
+ {
+ wdval=';';
+ chkpr(0);
+ }
+ }
+ else if(i==0 && (flg&MTFLG)==0)
+ synbad();
+ switch(wdval)
+ {
+ case COOPSYM: /* set up a cooperating process */
+ flag |= FPIN|FPOU;
+ case '&':
+ if(i)
+ {
+ if(saviotemp!=iotemp || heredoc)
+ flag |= FTMP;
+ i = (TREPTR)makefork(flag, i);
+ }
+ else
+ synbad();
+
+ case ';':
+ if(e=cmd(sym,flg|MTFLG))
+ i=(TREPTR)makelist(TLST, i, e);
+ break;
+
+ case EOFSYM:
+ if(sym==NL)
+ break;
+
+ default:
+ if(sym)
+ chksym(sym);
+
+ }
+ /* restore output stream */
+ return(i);
+}
+
+/*
+ * list
+ * term
+ * list && term
+ * list || term
+ */
+
+static TREPTR list(flg)
+{
+ register TREPTR r;
+ register int b;
+ r = term(flg);
+ while(r && ((b=(wdval==ANDFSYM)) || wdval==ORFSYM))
+ {
+ r = makelist((b ? TAND : TORF), r, term(NLFLG));
+ }
+ return(r);
+}
+
+/*
+ * term
+ * item
+ * item |^ term
+ */
+
+static TREPTR term(flg)
+register int flg;
+{
+ register TREPTR t;
+ register PARPTR p = NULL;
+ heredoc = 0;
+ reserv++;
+ if(flg&NLFLG)
+ skipnl();
+ else
+ word();
+ /* check to see if pipeline is to be timed */
+ if(wdval==TIMSYM)
+ {
+ p=(PARPTR) getstak(PARTYPE);
+ p->partyp=TTIME;
+ reserv++;
+ word();
+ }
+ if((t=item(NLFLG|MTFLG)) && wdval=='|')
+ {
+ flg = heredoc|FPOU;
+ t=makelist(TFIL,makefork(flg,t),makefork(FPIN|FPCL,term(NLFLG)));
+ }
+ if(p)
+ {
+ p->partre= t;
+ return((TREPTR)p);
+ }
+ else
+ return(t);
+}
+
+/*
+ * case statement
+ */
+
+static REGPTR syncase(esym)
+register int esym;
+{
+ wdset |= E_FLAG; /* set to avoid aliasing expressions */
+ skipnl();
+ if(wdval==esym)
+ {
+ wdset &= ~E_FLAG;
+ return(0);
+ }
+ else
+ {
+ register REGPTR r=(REGPTR) getstak(REGTYPE);
+ r->regptr=0;
+ while(1)
+ {
+ chkflags(wdarg,1);
+ wdarg->argnxt=r->regptr;
+ r->regptr=wdarg;
+ if(wdval==')' || wdval=='|' || ( word()!=')' && wdval!='|' ))
+ synbad();
+ if(wdval=='|')
+ word();
+ else
+ break;
+ }
+ wdset &= ~E_FLAG;
+ r->regcom=cmd(0,NLFLG|MTFLG);
+ if(wdval==ECSYM)
+ r->regnxt=syncase(esym);
+ else
+ {
+ chksym(esym);
+ r->regnxt=0;
+ }
+ return(r);
+ }
+}
+
+/*
+ * item
+ *
+ * ( cmd ) [ < in ] [ > out ]
+ * word word* [ < in ] [ > out ]
+ * if ... then ... else ... fi
+ * for ... while ... do ... done
+ * case ... in ... esac
+ * begin ... end
+ */
+
+static TREPTR item(flag)
+BOOL flag;
+{
+ register TREPTR t;
+ register IOPTR io;
+ if(flag)
+ io=inout((IOPTR)0,1);
+ else
+ io=0;
+ switch(wdval)
+ {
+ /* case statement */
+ case CASYM:
+ {
+ t=(TREPTR) getstak(SWTYPE);
+ chkword();
+ ((SWPTR) t)->swarg=wdarg->argval;
+ skipnl();
+ chksym(INSYM|BRSYM);
+ ((SWPTR) t)->swlst=syncase(wdval==INSYM?ESSYM:KTSYM);
+ ((SWPTR) t)->swtyp=TSW;
+ break;
+ }
+
+ /* if statement */
+ case IFSYM:
+ {
+ register int w;
+ t=(TREPTR) getstak(IFTYPE);
+ ((IFPTR) t)->iftyp=TIF;
+ ((IFPTR) t)->iftre=cmd(THSYM,NLFLG);
+ ((IFPTR) t)->thtre=cmd(ELSYM|FISYM|EFSYM,NLFLG);
+ ((IFPTR) t)->eltre=((w=wdval)==ELSYM?cmd(FISYM,NLFLG):
+ (w==EFSYM?(wdval=IFSYM, item(0)):0));
+ if(w==EFSYM)
+ return(t);
+ break;
+ }
+
+ /* for and select statement */
+ case FORSYM:
+ case SELSYM:
+ {
+ t=(TREPTR) getstak(FORTYPE);
+ ((FORPTR) t)->fortyp=(wdval==FORSYM?TFOR:TSELECT);
+ ((FORPTR) t)->forlst=0;
+ chkword();
+ ((FORPTR) t)->fornam=(char*) wdarg->argval;
+ if(skipnl()==INSYM)
+ {
+ chkword();
+ ((FORPTR) t)->forlst=(COMPTR) item(0);
+ if(wdval!=NL && wdval!=';')
+ synbad();
+ if(wdval==NL)
+ chkpr(0);
+ skipnl();
+ }
+ /* 'for i;do cmd' is valid syntax */
+ else if(wdval==';')
+ {
+ reserv = 1;
+ word();
+ }
+ chksym(DOSYM|BRSYM);
+ ((FORPTR) t)->fortre=cmd(wdval==DOSYM?ODSYM:KTSYM,NLFLG);
+ break;
+ }
+
+ /* This is the code for parsing function definitions */
+ case PROCSYM:
+ funct_5_2:
+ {
+ TREPTR cmdptr;
+ BLKPTR blokptr;
+ int savstates = states;
+ int saveline = firstline;
+ register FILE *fd = NULL;
+ IOPTR saviotemp = iotemp;
+ t=(TREPTR) getstak(PROCTYPE);
+ ((PROCPTR) t)->proctyp=TPROC;
+ ((PROCPTR) t)->procloc = -1;
+ firstline = standin->flin;
+ if(wdval == PROCSYM)
+ chkword();
+ ((PROCPTR) t)->procnam=(char *) wdarg->argval;
+ skipnl();
+ chksym(BRSYM);
+ /* force a new stak frame to compile the command */
+ addblok(-1);
+ if(is_option(INTFLG))
+ {
+ /* just in case history file not open yet */
+ hist_open();
+ if(fc_fix)
+ {
+ fd = fc_fix->fixfd;
+ states |= FIXFLG;
+ ((PROCPTR)t)->procloc =
+ hist_position(fc_fix->fixind) +
+ fd->_ptr - fd->_base;
+ }
+ }
+ cmdptr = cmd(KTSYM,NLFLG);
+ /* force another stak frame to save the command */
+ addblok(-1);
+ blokptr = stakbsy;
+ stakbsy = stakbsy->word;
+ /* save the entry point in block */
+ blokptr->word = BLK(cmdptr);
+ ((PROCPTR) t)->proctre = blokptr;
+ if(iotemp != saviotemp)
+ {
+ iotemp = saviotemp;
+ states |= RM_TMP;
+ }
+ if(fd && (savstates&FIXFLG)==0)
+ {
+ hist_flush();
+ hist_cancel();
+ states &= ~FIXFLG;
+ }
+ firstline = saveline;
+ break;
+ }
+
+ /* while and until */
+ case WHSYM:
+ case UNSYM:
+ {
+ t=(TREPTR) getstak(WHTYPE);
+ ((WHPTR) t)->whtyp=(wdval==WHSYM ? TWH : TUN);
+ ((WHPTR) t)->whtre = cmd(DOSYM,NLFLG);
+ ((WHPTR) t)->dotre = cmd(ODSYM,NLFLG);
+ break;
+ }
+
+ /* command group with { */
+ case BRSYM:
+ t=cmd(KTSYM,NLFLG);
+ break;
+
+ case '(':
+ {
+ register PARPTR p;
+ p=(PARPTR) getstak(PARTYPE);
+ p->partre=cmd(')',NLFLG);
+ p->partyp=TPAR;
+ t=makefork(0,(TREPTR)p);
+ break;
+ }
+
+ default:
+ if(io==0)
+ return(0);
+
+ /* simple command */
+ case 0:
+ {
+ register ARGPTR argp;
+ register ARGPTR *argtail;
+ register ARGPTR *argset=0;
+ int keywd=KEYFLG;
+ int argno = 0;
+ int bltin = 0;
+ t=(TREPTR) getstak(COMTYPE);
+ ((COMPTR)t)->comio=io; /*initial io chain*/
+ /* set command line number for error messages */
+ ((COMPTR)t)->comline = (exec_flag?cmdline:
+ standin->flin-firstline-1);
+ argtail = &(((COMPTR)t)->comarg);
+ while(wdval==0)
+ {
+ argp = wdarg;
+ argp->argchn = 0;
+ /* test for keyword argument */
+ if(wdset&keywd)
+ {
+ chkflags(argp,0);
+ argp->argnxt=(ARGPTR) argset;
+ argset=(ARGPTR *) argp;
+ /* alias substitutions allowed */
+ wdset |= (KEYFLG|S_FLAG);
+ }
+ else
+ {
+ wdset = 0; /* don't hunt for aliases*/
+ chkflags(argp,1);
+ if((argp->argflag&A_RAW) == 0)
+ argno = -1;
+ if(argno>=0 && argno++==0)
+ {
+ /* check for builtin command */
+ bltin=syslook(argp->argval,commands);
+ }
+ *argtail = argp;
+ argtail = &(argp->argnxt);
+ wdset = keywd=is_option(KEYFLG);
+ }
+#ifdef DEVFD
+ retry:
+ word();
+ if((wdval&STRIP)=='(')
+ {
+ TREPTR t;
+ int flag = (wdval==OPROC);
+ t = cmd(')',NLFLG|(argno==1&&wdval=='('?MTFLG:0));
+ if(t == NULL)
+ {
+ wdarg = argp;
+ goto funct_5_2;
+ }
+ argp = (ARGPTR)locstak();
+ argno = -1;
+ *argtail = argp;
+ argtail = &(argp->argnxt);
+ endstak(movstr(nullstr,argp->argval));
+ argp->argchn = (ARGPTR)makefork(flag?FPIN|FAMP|FPCL:FPOU,t);
+ argp->argflag = (A_EXP|flag);
+ goto retry;
+ }
+#else
+ word();
+ if(argno==1 && argset==NULL && wdval== '(')
+ {
+ /* SVR2 style function */
+ word();
+ if(wdval == ')')
+ {
+ wdarg = argp;
+ goto funct_5_2;
+ }
+ wdval = '(';
+ }
+#endif /* DEVFD */
+ if(flag)
+ {
+ if(io)
+ {
+ while(io->ionxt)
+ io = io->ionxt;
+ io->ionxt = inout((IOPTR)0,0);
+ }
+ else
+ ((COMPTR)t)->comio = io = inout((IOPTR)0,0);
+ }
+ }
+ *argtail = 0;
+ ((COMPTR)t)->comtyp = (TCOM|(bltin<<(COMBITS+1)));
+ /* expand argument list if possible */
+ if(argno>0)
+ ((COMPTR)t)->comarg = qscan(t,argno);
+ else if(((COMPTR)t)->comarg)
+ ((COMPTR)t)->comtyp |= COMSCAN;
+ ((COMPTR)t)->comset=(ARGPTR) argset;
+ wdset &= ~S_FLAG;
+ return(t);
+ }
+ }
+ reserv++;
+ word();
+ if(io=inout(io,0))
+ {
+ int type = t->tretyp&COMMSK;
+ t=makefork(0,t);
+ t->treio=io;
+ if(type != TFORK)
+ t->tretyp = TSETIO;
+ }
+ return(t);
+}
+
+
+/*
+ * skip past newlines but issue prompt if interactive
+ */
+
+static int skipnl()
+{
+ while((reserv++, word()==NL))
+ chkpr(0);
+ return(wdval);
+}
+
+/*
+ * check for and process and i/o redirections
+ * if flag is set then an alias can be in the next word
+ */
+
+static IOPTR inout(lastio,flag)
+IOPTR lastio;
+{
+ register int iof;
+ register IOPTR iop;
+ register int c;
+ iof=wdnum;
+ switch(wdval)
+ {
+ case DOCSYM: /* << */
+ iof |= IODOC;
+ heredoc = FTMP;
+ break;
+
+ case APPSYM: /* >> */
+ case '>':
+ if(wdnum==0)
+ iof |= 1;
+ iof |= IOPUT;
+ if(wdval==APPSYM)
+ {
+ iof |= IOAPP;
+ break;
+ }
+
+ case '<':
+ if((c=nextc())=='&')
+ iof |= IOMOV;
+ else if(c=='>')
+ /* <> is open for read and write */
+ /* unadvertised feature */
+ iof |= IORDW;
+ else
+ peekn=c|MARK;
+ break;
+
+ default:
+ return(lastio);
+ }
+ chkword();
+ iop=(IOPTR) getstak(IOTYPE);
+ iop->ioname=wdarg->argval;
+ iop->iofile=iof;
+ if(iof&IODOC)
+ {
+ iop->iolst=iopend;
+ iopend=iop;
+ }
+ word();
+ iop->ionxt=inout(lastio,0);
+ /* allow alias substitutions */
+ if(flag)
+ wdset |= S_FLAG;
+ return(iop);
+}
+
+/*
+ * get next token and make sure that it is not a keyword or meta-character
+ */
+
+static void chkword()
+{
+ if(word())
+ synbad();
+}
+
+/*
+ * see if this token is syntactically correct
+ */
+
+static void chksym(sym)
+register int sym;
+{
+ register int x = sym&wdval;
+ if(((x&SYMFLG) ? x : sym) != wdval)
+ synbad();
+}
+
+/*
+ * print the name of a syntactic token
+ */
+
+static void prsym(sym)
+register int sym;
+{
+ if(sym&SYMFLG)
+ {
+ register SYSPTR sp=reserved;
+ while(sp->sysval && sp->sysval!=sym)
+ sp++;
+ fputs(sp->sysnam,output);
+ }
+ else if(sym==EOFSYM)
+ fputs(endoffile,output);
+ else
+ {
+ if(sym&SYMREP)
+ putc(sym,output);
+ if(sym==NL)
+ fputs("newline or ;",output);
+ else
+ putc(sym,output);
+ }
+ putc('\'',output);
+}
+
+/*
+ * print a bad syntax message
+ */
+
+void synbad()
+{
+ register char *cp = unexpected;
+ register int w = wdval;
+ p_setout(stderr);
+ p_prp(synmsg,0);
+ if((states&TTYFLG)==0)
+ {
+ fputs(atline,output);
+ p_num((int)standin->flin,SP);
+ }
+ p_str(colon,'`');
+ if(w)
+ prsym(w);
+ else
+ p_str(wdarg->argval,'\'');
+ if((w&EOFSYM) && w!=EOFSYM)
+ cp = unmatched;
+ p_str(cp,NL);
+ hist_flush();
+ exitsh(SYNBAD);
+}
+
+/*
+ * check argument for possible optimizations
+ * in many cases we can skip macro and file name expansion
+ * The fexp flag is set when file expansion is possible
+ */
+
+#define EXP_MACRO 2 /* macro expansion needed */
+#define EXP_TRIM 4 /* quoted characters in string */
+#define EXP_FILE 8 /* file expansion characters*/
+#define EXP_QUOTE 16 /* string contains " character */
+
+static void chkflags(argp,fexp)
+register ARGPTR argp;
+{
+ register int c;
+ argp->argflag = 0;
+ {
+ register int flag = 0;
+ char nquote = 0;
+ char *sp=argp->argval;
+ while(c= *sp++)
+ {
+ if(c==ESCAPE)
+ {
+ flag |= EXP_TRIM;
+ sp++;
+ }
+ else if(isexp(c))
+ {
+ if(c == '$' || c == '`')
+ {
+ flag |= EXP_MACRO;
+ if(c=='`')
+ subflag++;
+ }
+ else if(nquote==0)
+ {
+ /* special case of '[' */
+ if(*sp || c!='[')
+ flag |= EXP_FILE;
+ }
+ }
+ else if(c == '"')
+ {
+ /* toggle the quote count */
+ nquote = 1 - nquote;
+ flag |= EXP_QUOTE;
+ }
+ }
+ if(fexp==0)
+ flag &= ~EXP_FILE;
+ /* return if no macro expansion, file expansion or trimming required */
+ if(flag==0)
+ {
+ argp->argflag |= A_RAW;
+ return;
+ }
+ /* return if macro or command substitution needed */
+ if(flag&EXP_MACRO)
+ {
+ argp->argflag |= (A_MAC|A_EXP);
+ return;
+ }
+ /* check to see if file expansion is required */
+ if(flag&EXP_FILE)
+ {
+ argp->argflag|= A_EXP;
+ /* return if no quotes otherwise don't optimize */
+ if(flag&(EXP_QUOTE|EXP_TRIM))
+ {
+ argp->argflag= A_MAC;
+ return;
+ }
+ return;
+ }
+ argp->argflag |= A_RAW;
+ }
+ /* just get rid of quoting stuff and consider argument as expanded */
+ {
+ register char *dp,*sp;
+ char nquote = 0; /* set within quoted string */
+ dp = sp = argp->argval;
+ while(c= *sp++)
+ {
+ if(c != '"')
+ {
+ if(c==ESCAPE)
+ {
+ /* strip escchar's in double quotes */
+ c = *sp++;
+ if(nquote && !escchar(c) && c!='"')
+ *dp++ = ESCAPE;
+ }
+ *dp++ = c;
+ }
+ else /* toggle quote marker */
+ nquote = 1-nquote;
+ }
+ *dp = 0;
+ }
+}
+
+/*
+ * convert argument chain to argument list when no special arguments
+ */
+
+static ARGPTR qscan(ac,argn)
+COMPTR ac;
+int argn;
+{
+ register char **cp;
+ register ARGPTR ap;
+ register DOLPTR dp;
+ /* leave space for an extra argument at the front */
+ dp = (DOLPTR)getstak((unsigned)DOLTYPE + sizeof(char*) + argn*sizeof(char*));
+ cp = dp->dolarg+1;
+ dp->doluse = argn;
+ ap = ac->comarg;
+ while(ap)
+ {
+ *cp++ = ap->argval;
+ ap = ap->argnxt;
+ }
+ *cp = NULL;
+ return((ARGPTR)dp);
+}
+