diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/parse.c | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/sh/parse.c')
-rw-r--r-- | src/cmd/ksh93/sh/parse.c | 2082 |
1 files changed, 2082 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/parse.c b/src/cmd/ksh93/sh/parse.c new file mode 100644 index 0000000..6ef3ef9 --- /dev/null +++ b/src/cmd/ksh93/sh/parse.c @@ -0,0 +1,2082 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2012 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * UNIX shell + * + * S. R. Bourne + * Rewritten by David Korn + * AT&T Labs + * + * This is the parser for a shell language + */ + +#if KSHELL +#include "defs.h" +#else +#include <shell.h> +#include <ctype.h> +#endif +#include <fcin.h> +#include <error.h> +#include "shlex.h" +#include "history.h" +#include "builtins.h" +#include "test.h" +#include "history.h" + +#define HERE_MEM SF_BUFSIZE /* size of here-docs kept in memory */ + +#if CDT_VERSION < 20111111L +#define hash nvlink.hl._hash +#else +#define hash nvlink.lh.__hash +#endif + +/* These routines are local to this module */ + +static Shnode_t *makeparent(Lex_t*, int, Shnode_t*); +static Shnode_t *makelist(Lex_t*, int, Shnode_t*, Shnode_t*); +static struct argnod *qscan(struct comnod*, int); +static struct ionod *inout(Lex_t*,struct ionod*, int); +static Shnode_t *sh_cmd(Lex_t*,int,int); +static Shnode_t *term(Lex_t*,int); +static Shnode_t *list(Lex_t*,int); +static struct regnod *syncase(Lex_t*,int); +static Shnode_t *item(Lex_t*,int); +static Shnode_t *simple(Lex_t*,int, struct ionod*); +static int skipnl(Lex_t*,int); +static Shnode_t *test_expr(Lex_t*,int); +static Shnode_t *test_and(Lex_t*); +static Shnode_t *test_or(Lex_t*); +static Shnode_t *test_primary(Lex_t*); + +#define sh_getlineno(lp) (lp->lastline) + +#ifndef NIL +# define NIL(type) ((type)0) +#endif /* NIL */ +#define CNTL(x) ((x)&037) + + +#if !KSHELL +static struct stdata +{ + struct slnod *staklist; + int cmdline; +} st; +#endif + +static int opt_get; +static int loop_level; +static struct argnod *label_list; +static struct argnod *label_last; + +#define getnode(type) ((Shnode_t*)stakalloc(sizeof(struct type))) + +#if SHOPT_KIA +#include "path.h" +/* + * write out entities for each item in the list + * type=='V' for variable assignment lists + * Otherwise type is determined by the command */ +static unsigned long writedefs(Lex_t *lexp,struct argnod *arglist, int line, int type, struct argnod *cmd) +{ + register struct argnod *argp = arglist; + register char *cp; + register int n,eline; + int width=0; + unsigned long r=0; + static char atbuff[20]; + int justify=0; + char *attribute = atbuff; + unsigned long parent=lexp->script; + if(type==0) + { + parent = lexp->current; + type = 'v'; + switch(*argp->argval) + { + case 'a': + type='p'; + justify = 'a'; + break; + case 'e': + *attribute++ = 'x'; + break; + case 'r': + *attribute++ = 'r'; + break; + case 'l': + break; + } + while(argp = argp->argnxt.ap) + { + if((n= *(cp=argp->argval))!='-' && n!='+') + break; + if(cp[1]==n) + break; + while((n= *++cp)) + { + if(isdigit(n)) + width = 10*width + n-'0'; + else if(n=='L' || n=='R' || n =='Z') + justify=n; + else + *attribute++ = n; + } + } + } + else if(cmd) + parent=kiaentity(lexp,sh_argstr(cmd),-1,'p',-1,-1,lexp->unknown,'b',0,""); + *attribute = 0; + while(argp) + { + if((cp=strchr(argp->argval,'='))||(cp=strchr(argp->argval,'?'))) + n = cp-argp->argval; + else + n = strlen(argp->argval); + eline = lexp->sh->inlineno-(lexp->token==NL); + r=kiaentity(lexp,argp->argval,n,type,line,eline,parent,justify,width,atbuff); + sfprintf(lexp->kiatmp,"p;%..64d;v;%..64d;%d;%d;s;\n",lexp->current,r,line,eline); + argp = argp->argnxt.ap; + } + return(r); +} +#endif /* SHOPT_KIA */ + +static void typeset_order(const char *str,int line) +{ + register int c,n=0; + unsigned const char *cp=(unsigned char*)str; + static unsigned char *table; + if(*cp!='+' && *cp!='-') + return; + if(!table) + { + table = calloc(1,256); + for(cp=(unsigned char*)"bflmnprstuxACHS";c = *cp; cp++) + table[c] = 1; + for(cp=(unsigned char*)"aiEFLRXhTZ";c = *cp; cp++) + table[c] = 2; + for(c='0'; c <='9'; c++) + table[c] = 3; + } + for(cp=(unsigned char*)str; c= *cp++; n=table[c]) + { + if(table[c] < n) + errormsg(SH_DICT,ERROR_warn(0),e_lextypeset,line,str); + } +} + +/* + * add type definitions when compiling with -n + */ +static void check_typedef(struct comnod *tp) +{ + char *cp=0; + if(tp->comtyp&COMSCAN) + { + struct argnod *ap = tp->comarg; + while(ap = ap->argnxt.ap) + { + if(!(ap->argflag&ARG_RAW) || memcmp(ap->argval,"--",2)) + break; + if(sh_isoption(SH_NOEXEC)) + typeset_order(ap->argval,tp->comline); + if(memcmp(ap->argval,"-T",2)==0) + { + if(ap->argval[2]) + cp = ap->argval+2; + else if((ap->argnxt.ap)->argflag&ARG_RAW) + cp = (ap->argnxt.ap)->argval; + if(cp) + break; + } + } + } + else + { + struct dolnod *dp = (struct dolnod*)tp->comarg; + char **argv = dp->dolval + dp->dolbot+1; + while((cp= *argv++) && memcmp(cp,"--",2)) + { + if(sh_isoption(SH_NOEXEC)) + typeset_order(cp,tp->comline); + if(memcmp(cp,"-T",2)==0) + { + if(cp[2]) + cp = cp+2; + else + cp = *argv; + break; + } + } + } + if(cp) + { + Namval_t *mp=(Namval_t*)tp->comnamp ,*bp; + bp = sh_addbuiltin(cp, (Shbltin_f)mp->nvalue.bfp, (void*)0); + nv_onattr(bp,nv_isattr(mp,NV_PUBLIC)); + } +} + +/* + * Make a parent node for fork() or io-redirection + */ +static Shnode_t *makeparent(Lex_t *lp, int flag, Shnode_t *child) +{ + register Shnode_t *par = getnode(forknod); + par->fork.forktyp = flag; + par->fork.forktre = child; + par->fork.forkio = 0; + par->fork.forkline = sh_getlineno(lp)-1; + return(par); +} + +static int paramsub(const char *str) +{ + register int c,sub=0,lit=0; + while(c= *str++) + { + if(c=='$' && !lit) + { + if(*str=='(') + return(0); + if(sub) + continue; + if(*str=='{') + str++; + if(!isdigit(*str) && strchr("?#@*!$ ",*str)==0) + return(1); + } + else if(c=='`') + return(0); + else if(c=='[' && !lit) + sub++; + else if(c==']' && !lit) + sub--; + else if(c=='\'') + lit = !lit; + } + return(0); +} + +static Shnode_t *getanode(Lex_t *lp, struct argnod *ap) +{ + register Shnode_t *t = getnode(arithnod); + t->ar.artyp = TARITH; + t->ar.arline = sh_getlineno(lp); + t->ar.arexpr = ap; + if(ap->argflag&ARG_RAW) + t->ar.arcomp = sh_arithcomp(lp->sh,ap->argval); + else + { + if(sh_isoption(SH_NOEXEC) && (ap->argflag&ARG_MAC) && paramsub(ap->argval)) + errormsg(SH_DICT,ERROR_warn(0),e_lexwarnvar,lp->sh->inlineno); + t->ar.arcomp = 0; + } + return(t); +} + +/* + * Make a node corresponding to a command list + */ +static Shnode_t *makelist(Lex_t *lexp, int type, Shnode_t *l, Shnode_t *r) +{ + register Shnode_t *t; + if(!l || !r) + sh_syntax(lexp); + else + { + if((type&COMMSK) == TTST) + t = getnode(tstnod); + else + t = getnode(lstnod); + t->lst.lsttyp = type; + t->lst.lstlef = l; + t->lst.lstrit = r; + } + return(t); +} + +/* + * entry to shell parser + * Flag can be the union of SH_EOF|SH_NL + */ + +void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) +{ + register Shnode_t *t; + Lex_t *lexp = (Lex_t*)shp->lex_context; + Fcin_t sav_input; + struct argnod *sav_arg = lexp->arg; + int sav_prompt = shp->nextprompt; + if(shp->binscript && (sffileno(iop)==shp->infd || (flag&SH_FUNEVAL))) + return((void*)sh_trestore(shp,iop)); + fcsave(&sav_input); + shp->st.staklist = 0; + lexp->noreserv = 0; + lexp->heredoc = 0; + lexp->inlineno = shp->inlineno; + lexp->firstline = shp->st.firstline; + shp->nextprompt = 1; + loop_level = 0; + label_list = label_last = 0; + if(sh_isoption(SH_INTERACTIVE)) + sh_onstate(SH_INTERACTIVE); + if(sh_isoption(SH_VERBOSE)) + sh_onstate(SH_VERBOSE); + sh_lexopen(lexp,shp,0); + if(fcfopen(iop) < 0) + return(NIL(void*)); + if(fcfile()) + { + char *cp = fcfirst(); + if( cp[0]==CNTL('k') && cp[1]==CNTL('s') && cp[2]==CNTL('h') && cp[3]==0) + { + int version; + fcseek(4); + fcgetc(version); + fcclose(); + fcrestore(&sav_input); + lexp->arg = sav_arg; + if(version > 3) + errormsg(SH_DICT,ERROR_exit(1),e_lexversion); + if(sffileno(iop)==shp->infd || (flag&SH_FUNEVAL)) + shp->binscript = 1; + sfgetc(iop); + t = sh_trestore(shp,iop); + if(flag&SH_NL) + { + Shnode_t *tt; + while(1) + { + if(!(tt = sh_trestore(shp,iop))) + break; + t =makelist(lexp,TLST, t, tt); + } + } + return((void*)t); + } + } + flag &= ~SH_FUNEVAL; + if((flag&SH_NL) && (shp->inlineno=error_info.line+shp->st.firstline)==0) + shp->inlineno=1; +#if KSHELL + shp->nextprompt = 2; +#endif + t = sh_cmd(lexp,(flag&SH_EOF)?EOFSYM:'\n',SH_SEMI|SH_EMPTY|(flag&SH_NL)); + fcclose(); + fcrestore(&sav_input); + lexp->arg = sav_arg; + /* unstack any completed alias expansions */ + if((sfset(iop,0,0)&SF_STRING) && !sfreserve(iop,0,-1)) + { + Sfio_t *sp = sfstack(iop,NULL); + if(sp) + sfclose(sp); + } + shp->nextprompt = sav_prompt; + if(flag&SH_NL) + { + shp->st.firstline = lexp->firstline; + shp->inlineno = lexp->inlineno; + } + stkseek(shp->stk,0); + return((void*)t); +} + +/* + * This routine parses up the matching right parenthesis and returns + * the parse tree + */ +Shnode_t *sh_dolparen(Lex_t* lp) +{ + register Shnode_t *t=0; + Sfio_t *sp = fcfile(); + int line = lp->sh->inlineno; + lp->sh->inlineno = error_info.line+lp->sh->st.firstline; + sh_lexopen(lp,lp->sh,1); + lp->comsub = 1; + switch(sh_lex(lp)) + { + /* ((...)) arithmetic expression */ + case EXPRSYM: + t = getanode(lp,lp->arg); + break; + case LPAREN: + t = sh_cmd(lp,RPAREN,SH_NL|SH_EMPTY); + break; + case LBRACE: + t = sh_cmd(lp,RBRACE,SH_NL|SH_EMPTY); + break; + } + lp->comsub = 0; + if(!sp && (sp=fcfile())) + { + /* + * This code handles the case where string has been converted + * to a file by an alias setup + */ + register int c; + char *cp; + if(fcgetc(c) > 0) + fcseek(-1); + cp = fcseek(0); + fcclose(); + fcsopen(cp); + sfclose(sp); + } + lp->sh->inlineno = line; + return(t); +} + +/* + * remove temporary files and stacks + */ + +void sh_freeup(Shell_t *shp) +{ + if(shp->st.staklist) + sh_funstaks(shp->st.staklist,-1); + shp->st.staklist = 0; +} + +/* + * increase reference count for each stack in function list when flag>0 + * decrease reference count for each stack in function list when flag<=0 + * stack is freed when reference count is zero + */ + +void sh_funstaks(register struct slnod *slp,int flag) +{ + register struct slnod *slpold; + while(slpold=slp) + { + if(slp->slchild) + sh_funstaks(slp->slchild,flag); + slp = slp->slnext; + if(flag<=0) + stakdelete(slpold->slptr); + else + staklink(slpold->slptr); + } +} +/* + * cmd + * empty + * list + * list & [ cmd ] + * list [ ; cmd ] + */ + +static Shnode_t *sh_cmd(Lex_t *lexp, register int sym, int flag) +{ + register Shnode_t *left, *right; + register int type = FINT|FAMP; + if(sym==NL) + lexp->lasttok = 0; + left = list(lexp,flag); + if(lexp->token==NL) + { + if(flag&SH_NL) + lexp->token=';'; + } + else if(!left && !(flag&SH_EMPTY)) + sh_syntax(lexp); + switch(lexp->token) + { + case COOPSYM: /* set up a cooperating process */ + type |= (FPIN|FPOU|FPCL|FCOOP); + /* FALL THRU */ + case '&': + if(left) + { + /* (...)& -> {...;} & */ + if(left->tre.tretyp==TPAR) + left = left->par.partre; + left = makeparent(lexp,TFORK|type, left); + } + /* FALL THRU */ + case ';': + if(!left) + sh_syntax(lexp); + if(right=sh_cmd(lexp,sym,flag|SH_EMPTY)) + left=makelist(lexp,TLST, left, right); + break; + case EOFSYM: + if(sym==NL) + break; + default: + if(sym && sym!=lexp->token) + { + if(sym!=ELSESYM || (lexp->token!=ELIFSYM && lexp->token!=FISYM)) + sh_syntax(lexp); + } + } + return(left); +} + +/* + * list + * term + * list && term + * list || term + * unfortunately, these are equal precedence + */ +static Shnode_t *list(Lex_t *lexp, register int flag) +{ + register Shnode_t *t = term(lexp,flag); + register int token; + while(t && ((token=lexp->token)==ANDFSYM || token==ORFSYM)) + t = makelist(lexp,(token==ANDFSYM?TAND:TORF), t, term(lexp,SH_NL|SH_SEMI)); + return(t); +} + +/* + * term + * item + * item | term + */ +static Shnode_t *term(Lex_t *lexp,register int flag) +{ + register Shnode_t *t; + register int token; + if(flag&SH_NL) + token = skipnl(lexp,flag); + else + token = sh_lex(lexp); + /* check to see if pipeline is to be timed */ + if(token==TIMESYM || token==NOTSYM) + { + t = getnode(parnod); + t->par.partyp=TTIME; + if(lexp->token==NOTSYM) + t->par.partyp |= COMSCAN; + t->par.partre = term(lexp,0); + } +#if SHOPT_COSHELL + else if((t=item(lexp,SH_NL|SH_EMPTY|(flag&SH_SEMI))) && (lexp->token=='|' || lexp->token==PIPESYM2)) +#else + else if((t=item(lexp,SH_NL|SH_EMPTY|(flag&SH_SEMI))) && lexp->token=='|') +#endif /* SHOPT_COSHELL */ + { + register Shnode_t *tt; + int showme = t->tre.tretyp&FSHOWME; + t = makeparent(lexp,TFORK|FPOU,t); +#if SHOPT_COSHELL + if(lexp->token==PIPESYM2) + t->tre.tretyp |= FALTPIPE; +#endif /* SHOPT_COSHELL */ + if(tt=term(lexp,SH_NL)) + { + switch(tt->tre.tretyp&COMMSK) + { + case TFORK: + tt->tre.tretyp |= FPIN|FPCL; + break; + case TFIL: + tt->lst.lstlef->tre.tretyp |= FPIN|FPCL; + break; + default: + tt= makeparent(lexp,TSETIO|FPIN|FPCL,tt); + } + t=makelist(lexp,TFIL,t,tt); + t->tre.tretyp |= showme; + } + else if(lexp->token) + sh_syntax(lexp); + } + return(t); +} + +/* + * case statement + */ +static struct regnod* syncase(Lex_t *lexp,register int esym) +{ + register int tok = skipnl(lexp,0); + register struct regnod *r; + if(tok==esym) + return(NIL(struct regnod*)); + r = (struct regnod*)stakalloc(sizeof(struct regnod)); + r->regptr=0; + r->regflag=0; + if(tok==LPAREN) + skipnl(lexp,0); + while(1) + { + if(!lexp->arg) + sh_syntax(lexp); + lexp->arg->argnxt.ap=r->regptr; + r->regptr = lexp->arg; + if((tok=sh_lex(lexp))==RPAREN) + break; + else if(tok=='|') + sh_lex(lexp); + else + sh_syntax(lexp); + } + r->regcom=sh_cmd(lexp,0,SH_NL|SH_EMPTY|SH_SEMI); + if((tok=lexp->token)==BREAKCASESYM) + r->regnxt=syncase(lexp,esym); + else if(tok==FALLTHRUSYM) + { + r->regflag++; + r->regnxt=syncase(lexp,esym); + } + else + { + if(tok!=esym && tok!=EOFSYM) + sh_syntax(lexp); + r->regnxt=0; + } + if(lexp->token==EOFSYM) + return(NIL(struct regnod*)); + return(r); +} + +/* + * This routine creates the parse tree for the arithmetic for + * When called, shlex.arg contains the string inside ((...)) + * When the first argument is missing, a while node is returned + * Otherise a list containing an arithmetic command and a while + * is returned. + */ +static Shnode_t *arithfor(Lex_t *lexp,register Shnode_t *tf) +{ + register Shnode_t *t, *tw = tf; + register int offset; + register struct argnod *argp; + register int n; + Stk_t *stkp = lexp->sh->stk; + int argflag = lexp->arg->argflag; + /* save current input */ + Fcin_t sav_input; + fcsave(&sav_input); + fcsopen(lexp->arg->argval); + /* split ((...)) into three expressions */ + for(n=0; ; n++) + { + register int c; + argp = (struct argnod*)stkseek(stkp,ARGVAL); + argp->argnxt.ap = 0; + argp->argchn.cp = 0; + argp->argflag = argflag; + if(n==2) + break; + /* copy up to ; onto the stack */ + sh_lexskip(lexp,';',1,ST_NESTED); + offset = stktell(stkp)-1; + if((c=fcpeek(-1))!=';') + break; + /* remove trailing white space */ + while(offset>ARGVAL && ((c= *stkptr(stkp,offset-1)),isspace(c))) + offset--; + /* check for empty initialization expression */ + if(offset==ARGVAL && n==0) + continue; + stkseek(stkp,offset); + /* check for empty condition and treat as while((1)) */ + if(offset==ARGVAL) + sfputc(stkp,'1'); + argp = (struct argnod*)stkfreeze(stkp,1); + t = getanode(lexp,argp); + if(n==0) + tf = makelist(lexp,TLST,t,tw); + else + tw->wh.whtre = t; + } + while((offset=fcpeek(0)) && isspace(offset)) + fcseek(1); + stakputs(fcseek(0)); + argp = (struct argnod*)stakfreeze(1); + fcrestore(&sav_input); + if(n<2) + { + lexp->token = RPAREN|SYMREP; + sh_syntax(lexp); + } + /* check whether the increment is present */ + if(*argp->argval) + { + t = getanode(lexp,argp); + tw->wh.whinc = (struct arithnod*)t; + } + else + tw->wh.whinc = 0; + sh_lexopen(lexp, lexp->sh,1); + if((n=sh_lex(lexp))==NL) + n = skipnl(lexp,0); + else if(n==';') + n = sh_lex(lexp); + if(n!=DOSYM && n!=LBRACE) + sh_syntax(lexp); + tw->wh.dotre = sh_cmd(lexp,n==DOSYM?DONESYM:RBRACE,SH_NL|SH_SEMI); + tw->wh.whtyp = TWH; + return(tf); + +} + +static Shnode_t *funct(Lex_t *lexp) +{ + Shell_t *shp = lexp->sh; + register Shnode_t *t; + register int flag; + struct slnod *volatile slp=0; + Stak_t *savstak; + Sfoff_t first, last; + struct functnod *volatile fp; + Sfio_t *iop; +#if SHOPT_KIA + unsigned long current = lexp->current; +#endif /* SHOPT_KIA */ + int nargs=0,size=0,jmpval, saveloop=loop_level; + struct argnod *savelabel = label_last; + struct checkpt buff; + int save_optget = opt_get; + void *in_mktype = shp->mktype; + shp->mktype = 0; + opt_get = 0; + t = getnode(functnod); + t->funct.functline = shp->inlineno; + t->funct.functtyp=TFUN; + t->funct.functargs = 0; + if(!(flag = (lexp->token==FUNCTSYM))) + t->funct.functtyp |= FPOSIX; + else if(sh_lex(lexp)) + sh_syntax(lexp); + if(!(iop=fcfile())) + { + iop = sfopen(NIL(Sfio_t*),fcseek(0),"s"); + fcclose(); + fcfopen(iop); + } + t->funct.functloc = first = fctell(); + if(!shp->st.filename || sffileno(iop)<0) + { + if(fcfill() >= 0) + fcseek(-1); + if(sh_isstate(SH_HISTORY) && shp->gd->hist_ptr) + t->funct.functloc = sfseek(shp->gd->hist_ptr->histfp,(off_t)0,SEEK_CUR); + else + { + /* copy source to temporary file */ + t->funct.functloc = 0; + if(lexp->sh->heredocs) + t->funct.functloc = sfseek(lexp->sh->heredocs,(Sfoff_t)0, SEEK_END); + else + lexp->sh->heredocs = sftmp(HERE_MEM); + lexp->sh->funlog = lexp->sh->heredocs; + t->funct.functtyp |= FPIN; + } + } + t->funct.functnam= (char*)lexp->arg->argval; +#if SHOPT_KIA + if(lexp->kiafile) + lexp->current = kiaentity(lexp,t->funct.functnam,-1,'p',-1,-1,lexp->script,'p',0,""); +#endif /* SHOPT_KIA */ + if(flag) + { + lexp->token = sh_lex(lexp); +#if SHOPT_BASH + if(lexp->token == LPAREN) + { + if((lexp->token = sh_lex(lexp)) == RPAREN) + t->funct.functtyp |= FPOSIX; + else + sh_syntax(lexp); + } +#endif + } + if(t->funct.functtyp&FPOSIX) + skipnl(lexp,0); + else + { + if(lexp->token==0) + { + struct comnod *ac; + char *cp, **argv, **argv0; + int c; + t->funct.functargs = ac = (struct comnod*)simple(lexp,SH_NOIO|SH_FUNDEF,NIL(struct ionod*)); + if(ac->comset || (ac->comtyp&COMSCAN)) + errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax4,lexp->sh->inlineno); + argv0 = argv = ((struct dolnod*)ac->comarg)->dolval+ARG_SPARE; + while(cp= *argv++) + { + size += strlen(cp)+1; + if((c = mbchar(cp)) && isaletter(c)) + while(c=mbchar(cp), isaname(c)); + } + if(c) + errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax4,lexp->sh->inlineno); + nargs = argv-argv0; + size += sizeof(struct dolnod)+(nargs+ARG_SPARE)*sizeof(char*); + if(shp->shcomp && memcmp(".sh.math.",t->funct.functnam,9)==0) + { + Namval_t *np= nv_open(t->funct.functnam,shp->fun_tree,NV_ADD|NV_VARNAME); + np->nvalue.rp = new_of(struct Ufunction,shp->funload?sizeof(Dtlink_t):0); + memset((void*)np->nvalue.rp,0,sizeof(struct Ufunction)); + np->nvalue.rp->argc = ((struct dolnod*)ac->comarg)->dolnum; + } + } + while(lexp->token==NL) + lexp->token = sh_lex(lexp); + } + if((flag && lexp->token!=LBRACE) || lexp->token==EOFSYM) + sh_syntax(lexp); + sh_pushcontext(shp,&buff,1); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval == 0) + { + /* create a new stak frame to compile the command */ + savstak = stakcreate(STAK_SMALL); + savstak = stakinstall(savstak, 0); + slp = (struct slnod*)stakalloc(sizeof(struct slnod)+sizeof(struct functnod)); + slp->slchild = 0; + slp->slnext = shp->st.staklist; + shp->st.staklist = 0; + t->funct.functstak = (struct slnod*)slp; + /* + * store the pathname of function definition file on stack + * in name field of fake for node + */ + fp = (struct functnod*)(slp+1); + fp->functtyp = TFUN|FAMP; + fp->functnam = 0; + fp->functline = t->funct.functline; + if(shp->st.filename) + fp->functnam = stakcopy(shp->st.filename); + loop_level = 0; + label_last = label_list; + if(size) + { + struct dolnod *dp = (struct dolnod*)stakalloc(size); + char *cp, *sp, **argv, **old = ((struct dolnod*)t->funct.functargs->comarg)->dolval+1; + argv = ((char**)(dp->dolval))+1; + dp->dolnum = ((struct dolnod*)t->funct.functargs->comarg)->dolnum; + t->funct.functargs->comarg = (struct argnod*)dp; + for(cp=(char*)&argv[nargs]; sp= *old++; cp++) + { + *argv++ = cp; + cp = strcopy(cp,sp); + } + *argv = 0; + } + if(!flag && lexp->token==0) + { + /* copy current word token to current stak frame */ + struct argnod *ap; + flag = ARGVAL + strlen(lexp->arg->argval); + ap = (struct argnod*)stakalloc(flag); + memcpy(ap,lexp->arg,flag); + lexp->arg = ap; + } + t->funct.functtre = item(lexp,SH_NOIO); + } + else if(shp->shcomp) + exit(1); + sh_popcontext(shp,&buff); + loop_level = saveloop; + label_last = savelabel; + /* restore the old stack */ + if(slp) + { + slp->slptr = stakinstall(savstak,0); + slp->slchild = shp->st.staklist; + } +#if SHOPT_KIA + lexp->current = current; +#endif /* SHOPT_KIA */ + if(jmpval) + { + if(slp && slp->slptr) + { + shp->st.staklist = slp->slnext; + stakdelete(slp->slptr); + } + siglongjmp(*shp->jmplist,jmpval); + } + shp->st.staklist = (struct slnod*)slp; + last = fctell(); + fp->functline = (last-first); + fp->functtre = t; + shp->mktype = in_mktype; + if(lexp->sh->funlog) + { + if(fcfill()>0) + fcseek(-1); + lexp->sh->funlog = 0; + } +#if SHOPT_KIA + if(lexp->kiafile) + kiaentity(lexp,t->funct.functnam,-1,'p',t->funct.functline,shp->inlineno-1,lexp->current,'p',0,""); +#endif /* SHOPT_KIA */ + t->funct.functtyp |= opt_get; + opt_get = save_optget; + return(t); +} + +/* + * Compound assignment + */ +static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type) +{ + register int n; + register Shnode_t *t, **tp; + register struct comnod *ac; + Stk_t *stkp = lexp->sh->stk; + int array=0, index=0; + Namval_t *np; + n = strlen(ap->argval)-1; + if(ap->argval[n]!='=') + sh_syntax(lexp); + if(ap->argval[n-1]=='+') + { + ap->argval[n--]=0; + array = ARG_APPEND; + } + /* shift right */ + while(n > 0) + { + ap->argval[n] = ap->argval[n-1]; + n--; + } + *ap->argval=0; + t = getnode(fornod); + t->for_.fornam = (char*)(ap->argval+1); + t->for_.fortyp = sh_getlineno(lexp); + tp = &t->for_.fortre; + ap->argchn.ap = (struct argnod*)t; + ap->argflag &= ARG_QUOTED; + ap->argflag |= array; + lexp->assignok = SH_ASSIGN; + if(type==NV_ARRAY) + lexp->noreserv = 1; + else + lexp->aliasok = 1; + array= (type==NV_ARRAY)?SH_ARRAY:0; + if((n=skipnl(lexp,0))==RPAREN || n==LPAREN) + { + struct argnod *ar,*aq,**settail; + ac = (struct comnod*)getnode(comnod); + memset((void*)ac,0,sizeof(*ac)); + comarray: + settail= &ac->comset; + ac->comline = sh_getlineno(lexp); + while(n==LPAREN) + { + ar = (struct argnod*)stkseek(stkp,ARGVAL); + ar->argflag= ARG_ASSIGN; + sfprintf(stkp,"[%d]=",index++); + if(aq=ac->comarg) + { + ac->comarg = aq->argnxt.ap; + sfprintf(stkp,"%s",aq->argval); + ar->argflag |= aq->argflag; + } + ar = (struct argnod*)stkfreeze(stkp,1); + ar->argnxt.ap = 0; + if(!aq) + ar = assign(lexp,ar,0); + ar->argflag |= ARG_MESSAGE; + *settail = ar; + settail = &(ar->argnxt.ap); + if(aq) + continue; + while((n = skipnl(lexp,0))==0) + { + ar = (struct argnod*)stkseek(stkp,ARGVAL); + ar->argflag= ARG_ASSIGN; + sfprintf(stkp,"[%d]=",index++); + stakputs(lexp->arg->argval); + ar = (struct argnod*)stkfreeze(stkp,1); + ar->argnxt.ap = 0; + ar->argflag = lexp->arg->argflag; + *settail = ar; + settail = &(ar->argnxt.ap); + } + } + } + else if(n && n!=FUNCTSYM) + sh_syntax(lexp); + else if(type!=NV_ARRAY && n!=FUNCTSYM && !(lexp->arg->argflag&ARG_ASSIGN) && !((np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && (nv_isattr(np,BLT_DCL)|| np==SYSDOT))) + { + array=SH_ARRAY; + if(fcgetc(n)==LPAREN) + { + int c; + if(fcgetc(c)==RPAREN) + { + lexp->token = SYMRES; + array = 0; + } + else + fcseek(-2); + } + else if(n>0) + fcseek(-1); + if(array && type==NV_TYPE) + { + struct argnod *arg = lexp->arg; + n = lexp->token; + if(path_search(lexp->sh,lexp->arg->argval,NIL(Pathcomp_t**),1) && (np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && nv_isattr(np,BLT_DCL)) + { + lexp->token = n; + lexp->arg = arg; + array = 0; + } + else + sh_syntax(lexp); + } + } + lexp->noreserv = 0; + while(1) + { + if((n=lexp->token)==RPAREN) + break; + if(n==FUNCTSYM || n==SYMRES) + ac = (struct comnod*)funct(lexp); + else + ac = (struct comnod*)simple(lexp,SH_NOIO|SH_ASSIGN|array,NIL(struct ionod*)); + if((n=lexp->token)==RPAREN) + break; + if(n!=NL && n!=';') + { + if(array && n==LPAREN) + goto comarray; + sh_syntax(lexp); + } + lexp->assignok = SH_ASSIGN; + if((n=skipnl(lexp,0)) || array) + { + if(n==RPAREN) + break; + if(array || n!=FUNCTSYM) + sh_syntax(lexp); + } + if((n!=FUNCTSYM) && !(lexp->arg->argflag&ARG_ASSIGN) && !((np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && (nv_isattr(np,BLT_DCL)||np==SYSDOT))) + { + struct argnod *arg = lexp->arg; + if(n!=0) + sh_syntax(lexp); + /* check for sys5 style function */ + if(sh_lex(lexp)!=LPAREN || sh_lex(lexp)!=RPAREN) + { + lexp->arg = arg; + lexp->token = 0; + sh_syntax(lexp); + } + lexp->arg = arg; + lexp->token = SYMRES; + } + t = makelist(lexp,TLST,(Shnode_t*)ac,t); + *tp = t; + tp = &t->lst.lstrit; + } + *tp = (Shnode_t*)ac; + lexp->assignok = 0; + return(ap); +} + +/* + * item + * + * ( cmd ) [ < in ] [ > out ] + * word word* [ < in ] [ > out ] + * if ... then ... else ... fi + * for ... while ... do ... done + * case ... in ... esac + * begin ... end + */ + +static Shnode_t *item(Lex_t *lexp,int flag) +{ + register Shnode_t *t; + register struct ionod *io; + register int tok = (lexp->token&0xff); + int savwdval = lexp->lasttok; + int savline = lexp->lastline; + int showme=0, comsub; + if(!(flag&SH_NOIO) && (tok=='<' || tok=='>' || lexp->token==IOVNAME)) + io=inout(lexp,NIL(struct ionod*),1); + else + io=0; + if((tok=lexp->token) && tok!=EOFSYM && tok!=FUNCTSYM) + { + lexp->lastline = sh_getlineno(lexp); + lexp->lasttok = lexp->token; + } + switch(tok) + { + /* [[ ... ]] test expression */ + case BTESTSYM: + t = test_expr(lexp,ETESTSYM); + t->tre.tretyp &= ~TTEST; + break; + /* ((...)) arithmetic expression */ + case EXPRSYM: + t = getanode(lexp,lexp->arg); + sh_lex(lexp); + goto done; + + /* case statement */ + case CASESYM: + { + int savetok = lexp->lasttok; + int saveline = lexp->lastline; + t = getnode(swnod); + if(sh_lex(lexp)) + sh_syntax(lexp); + t->sw.swarg=lexp->arg; + t->sw.swtyp=TSW; + t->sw.swio = 0; + t->sw.swtyp |= FLINENO; + t->sw.swline = lexp->sh->inlineno; + if((tok=skipnl(lexp,0))!=INSYM && tok!=LBRACE) + sh_syntax(lexp); + if(!(t->sw.swlst=syncase(lexp,tok==INSYM?ESACSYM:RBRACE)) && lexp->token==EOFSYM) + { + lexp->lasttok = savetok; + lexp->lastline = saveline; + sh_syntax(lexp); + } + break; + } + + /* if statement */ + case IFSYM: + { + register Shnode_t *tt; + t = getnode(ifnod); + t->if_.iftyp=TIF; + t->if_.iftre=sh_cmd(lexp,THENSYM,SH_NL); + t->if_.thtre=sh_cmd(lexp,ELSESYM,SH_NL|SH_SEMI); + tok = lexp->token; + t->if_.eltre=(tok==ELSESYM?sh_cmd(lexp,FISYM,SH_NL|SH_SEMI): + (tok==ELIFSYM?(lexp->token=IFSYM, tt=item(lexp,SH_NOIO)):0)); + if(tok==ELIFSYM) + { + if(!tt || tt->tre.tretyp!=TSETIO) + goto done; + t->if_.eltre = tt->fork.forktre; + tt->fork.forktre = t; + t = tt; + goto done; + } + break; + } + + /* for and select statement */ + case FORSYM: + case SELECTSYM: + { + t = getnode(fornod); + t->for_.fortyp=(lexp->token==FORSYM?TFOR:TSELECT); + t->for_.forlst=0; + t->for_.forline = lexp->sh->inlineno; + if(sh_lex(lexp)) + { + if(lexp->token!=EXPRSYM || t->for_.fortyp!=TFOR) + sh_syntax(lexp); + /* arithmetic for */ + t = arithfor(lexp,t); + break; + } + t->for_.fornam=(char*) lexp->arg->argval; + t->for_.fortyp |= FLINENO; +#if SHOPT_KIA + if(lexp->kiafile) + writedefs(lexp,lexp->arg,lexp->sh->inlineno,'v',NIL(struct argnod*)); +#endif /* SHOPT_KIA */ + while((tok=sh_lex(lexp))==NL); + if(tok==INSYM) + { + if(sh_lex(lexp)) + { + if(lexp->token != NL && lexp->token !=';') + sh_syntax(lexp); + /* some Linux scripts assume this */ + if(sh_isoption(SH_NOEXEC)) + errormsg(SH_DICT,ERROR_warn(0),e_lexemptyfor,lexp->sh->inlineno-(lexp->token=='\n')); + t->for_.forlst = (struct comnod*)getnode(comnod); + (t->for_.forlst)->comarg = 0; + (t->for_.forlst)->comset = 0; + (t->for_.forlst)->comnamp = 0; + (t->for_.forlst)->comnamq = 0; + (t->for_.forlst)->comstate = 0; + (t->for_.forlst)->comio = 0; + (t->for_.forlst)->comtyp = 0; + } + else + t->for_.forlst=(struct comnod*)simple(lexp,SH_NOIO,NIL(struct ionod*)); + if(lexp->token != NL && lexp->token !=';') + sh_syntax(lexp); + tok = skipnl(lexp,0); + } + /* 'for i;do cmd' is valid syntax */ + else if(tok==';') + tok=sh_lex(lexp); + if(tok!=DOSYM && tok!=LBRACE) + sh_syntax(lexp); + loop_level++; + t->for_.fortre=sh_cmd(lexp,tok==DOSYM?DONESYM:RBRACE,SH_NL|SH_SEMI); + if(--loop_level==0) + label_last = label_list; + break; + } + + /* This is the code for parsing function definitions */ + case FUNCTSYM: + return(funct(lexp)); + +#if SHOPT_NAMESPACE + case NSPACESYM: + t = getnode(fornod); + t->for_.fortyp=TNSPACE; + t->for_.forlst=0; + if(sh_lex(lexp)) + sh_syntax(lexp); + t->for_.fornam=(char*) lexp->arg->argval; + while((tok=sh_lex(lexp))==NL); + if(tok!=LBRACE) + sh_syntax(lexp); + t->for_.fortre = sh_cmd(lexp,RBRACE,SH_NL); + break; +#endif /* SHOPT_NAMESPACE */ + + /* while and until */ + case WHILESYM: + case UNTILSYM: + t = getnode(whnod); + t->wh.whtyp=(lexp->token==WHILESYM ? TWH : TUN); + loop_level++; + t->wh.whtre = sh_cmd(lexp,DOSYM,SH_NL); + t->wh.dotre = sh_cmd(lexp,DONESYM,SH_NL|SH_SEMI); + if(--loop_level==0) + label_last = label_list; + t->wh.whinc = 0; + break; + + case LABLSYM: + { + register struct argnod *argp = label_list; + while(argp) + { + if(strcmp(argp->argval,lexp->arg->argval)==0) + errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax3,lexp->sh->inlineno,argp->argval); + argp = argp->argnxt.ap; + } + lexp->arg->argnxt.ap = label_list; + label_list = lexp->arg; + label_list->argchn.len = sh_getlineno(lexp); + label_list->argflag = loop_level; + skipnl(lexp,flag); + if(!(t = item(lexp,SH_NL))) + sh_syntax(lexp); + tok = (t->tre.tretyp&(COMSCAN|COMSCAN-1)); + if(sh_isoption(SH_NOEXEC) && tok!=TWH && tok!=TUN && tok!=TFOR && tok!=TSELECT) + errormsg(SH_DICT,ERROR_warn(0),e_lexlabignore,label_list->argchn.len,label_list->argval); + return(t); + } + + /* command group with {...} */ + case LBRACE: + comsub = lexp->comsub; + lexp->comsub = 0; + t = sh_cmd(lexp,RBRACE,SH_NL|SH_SEMI); + lexp->comsub = comsub; + break; + + case LPAREN: + t = getnode(parnod); + t->par.partre=sh_cmd(lexp,RPAREN,SH_NL|SH_SEMI); + t->par.partyp=TPAR; + break; + +#if SHOPT_COSHELL + case '&': + if(tok=sh_lex(lexp)) + { + if(tok!=NL) + sh_syntax(lexp); + t = getnode(comnod); + memset(t,0,sizeof(struct comnod)); + t->com.comline = sh_getlineno(lexp); + } + else + t = (Shnode_t*)simple(lexp,SH_NOIO,NIL(struct ionod*)); + t->com.comtyp |= FAMP; + if(lexp->token=='&' || lexp->token=='|') + sh_syntax(lexp); + return(t); + break; +#endif /* SHOPT_COSHELL */ + default: + if(io==0) + return(0); + + case ';': + if(io==0) + { + if(!(flag&SH_SEMI)) + return(0); + if(sh_lex(lexp)==';') + sh_syntax(lexp); + showme = FSHOWME; + } + /* simple command */ + case 0: + t = (Shnode_t*)simple(lexp,flag,io); + if(t->com.comarg && lexp->intypeset) + check_typedef(&t->com); + lexp->intypeset = 0; + lexp->inexec = 0; + t->tre.tretyp |= showme; + return(t); + } + sh_lex(lexp); + if(io=inout(lexp,io,0)) + { + if((tok=t->tre.tretyp&COMMSK) != TFORK) + tok = TSETIO; + t=makeparent(lexp,tok,t); + t->tre.treio=io; + } +done: + lexp->lasttok = savwdval; + lexp->lastline = savline; + return(t); +} + +static struct argnod *process_sub(Lex_t *lexp,int tok) +{ + struct argnod *argp; + Shnode_t *t; + int mode = (tok==OPROCSYM); + t = sh_cmd(lexp,RPAREN,SH_NL); + argp = (struct argnod*)stkalloc(lexp->sh->stk,sizeof(struct argnod)); + *argp->argval = 0; + argp->argchn.ap = (struct argnod*)makeparent(lexp,mode?TFORK|FPIN|FAMP|FPCL:TFORK|FPOU,t); + argp->argflag = (ARG_EXP|mode); + return(argp); +} + + +/* + * This is for a simple command, for list, or compound assignment + */ +static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) +{ + register struct comnod *t; + register struct argnod *argp; + register int tok; + Stk_t *stkp = lexp->sh->stk; + struct argnod **argtail; + struct argnod **settail; + int cmdarg=0; + int argno = 0; + int assignment = 0; + int key_on = (!(flag&SH_NOIO) && sh_isoption(SH_KEYWORD)); + int associative=0; + if((argp=lexp->arg) && (argp->argflag&ARG_ASSIGN) && argp->argval[0]=='[') + { + flag |= SH_ARRAY; + associative = 1; + } + t = (struct comnod*)getnode(comnod); + t->comio=io; /*initial io chain*/ + /* set command line number for error messages */ + t->comline = sh_getlineno(lexp); + argtail = &(t->comarg); + t->comset = 0; + t->comnamp = 0; + t->comnamq = 0; + t->comstate = 0; + settail = &(t->comset); + while(lexp->token==0) + { + argp = lexp->arg; + if(*argp->argval==LBRACE && (flag&SH_FUNDEF) && argp->argval[1]==0) + { + lexp->token = LBRACE; + break; + } + if(associative && argp->argval[0]!='[') + sh_syntax(lexp); + /* check for assignment argument */ + if((argp->argflag&ARG_ASSIGN) && assignment!=2) + { + *settail = argp; + settail = &(argp->argnxt.ap); + lexp->assignok = (flag&SH_ASSIGN)?SH_ASSIGN:1; + if(assignment) + { + struct argnod *ap=argp; + char *last, *cp; + if(assignment==1) + { + last = strchr(argp->argval,'='); + if(last && (last[-1]==']'|| (last[-1]=='+' && last[-2]==']')) && (cp=strchr(argp->argval,'[')) && (cp < last) && cp[-1]!='.') + last = cp; + stkseek(stkp,ARGVAL); + sfwrite(stkp,argp->argval,last-argp->argval); + ap=(struct argnod*)stkfreeze(stkp,1); + ap->argflag = ARG_RAW; + ap->argchn.ap = 0; + } + *argtail = ap; + argtail = &(ap->argnxt.ap); + if(argno>=0) + argno++; + } + else /* alias substitutions allowed */ + lexp->aliasok = 1; + } + else + { + if(!(argp->argflag&ARG_RAW)) + argno = -1; + if(argno>=0 && argno++==cmdarg && !(flag&SH_ARRAY) && *argp->argval!='/') + { + /* check for builtin command */ + Namval_t *np=nv_bfsearch(argp->argval,lexp->sh->fun_tree, (Namval_t**)&t->comnamq,(char**)0); + if(cmdarg==0) + t->comnamp = (void*)np; + if(np && is_abuiltin(np)) + { + if(nv_isattr(np,BLT_DCL)) + { + assignment = 1+(*argp->argval=='a'); + if(np==SYSTYPESET) + lexp->intypeset = 1; + key_on = 1; + } + else if(np==SYSCOMMAND) + cmdarg++; + else if(np==SYSEXEC) + lexp->inexec = 1; + else if(np->nvalue.bfp==(Nambfp_f)b_getopts) + opt_get |= FOPTGET; + } + } + *argtail = argp; + argtail = &(argp->argnxt.ap); + if(!(lexp->assignok=key_on) && !(flag&SH_NOIO) && sh_isoption(SH_NOEXEC)) + lexp->assignok = SH_COMPASSIGN; + lexp->aliasok = 0; + } + retry: + tok = sh_lex(lexp); + if(tok==LABLSYM && (flag&SH_ASSIGN)) + lexp->token = tok = 0; + if((tok==IPROCSYM || tok==OPROCSYM)) + { + argp = process_sub(lexp,tok); + argno = -1; + *argtail = argp; + argtail = &(argp->argnxt.ap); + goto retry; + } + if(tok==LPAREN) + { + if(argp->argflag&ARG_ASSIGN) + { + int intypeset = lexp->intypeset; + int type = 0; + lexp->intypeset = 0; + if(t->comnamp==SYSTYPESET) + { + struct argnod *ap; + for(ap=t->comarg->argnxt.ap;ap;ap=ap->argnxt.ap) + { + if(*ap->argval!='-') + break; + if(strchr(ap->argval,'T')) + type = NV_TYPE; + else if(strchr(ap->argval,'a')) + type = NV_ARRAY; + else + continue; + break; + } + } + argp = assign(lexp,argp,type); + lexp->intypeset = intypeset; + if(associative) + lexp->assignok |= SH_ASSIGN; + goto retry; + } + else if(argno==1 && !t->comset) + { + /* SVR2 style function */ + if(!(flag&SH_ARRAY) && sh_lex(lexp) == RPAREN) + { + lexp->arg = argp; + return(funct(lexp)); + } + lexp->token = LPAREN; + } + } + else if(flag&SH_ASSIGN) + { + if(tok==RPAREN) + break; + else if(tok==NL && (flag&SH_ARRAY)) + { + lexp->comp_assign = 2; + goto retry; + } + + } + if(!(flag&SH_NOIO)) + { + if(io) + { + while(io->ionxt) + io = io->ionxt; + io->ionxt = inout(lexp,(struct ionod*)0,0); + } + else + t->comio = io = inout(lexp,(struct ionod*)0,0); + } + } + *argtail = 0; + t->comtyp = TCOM; +#if SHOPT_KIA + if(lexp->kiafile && !(flag&SH_NOIO)) + { + register Namval_t *np=(Namval_t*)t->comnamp; + unsigned long r=0; + int line = t->comline; + argp = t->comarg; + if(np) + r = kiaentity(lexp,nv_name(np),-1,'p',-1,0,lexp->unknown,'b',0,""); + else if(argp) + r = kiaentity(lexp,sh_argstr(argp),-1,'p',-1,0,lexp->unknown,'c',0,""); + if(r>0) + sfprintf(lexp->kiatmp,"p;%..64d;p;%..64d;%d;%d;c;\n",lexp->current,r,line,line); + if(t->comset && argno==0) + writedefs(lexp,t->comset,line,'v',t->comarg); + else if(np && nv_isattr(np,BLT_DCL)) + writedefs(lexp,argp,line,0,NIL(struct argnod*)); + else if(argp && strcmp(argp->argval,"read")==0) + writedefs(lexp,argp,line,0,NIL(struct argnod*)); +#if 0 + else if(argp && strcmp(argp->argval,"unset")==0) + writedefs(lexp,argp,line,'u',NIL(struct argnod*)); +#endif + else if(argp && *argp->argval=='.' && argp->argval[1]==0 && (argp=argp->argnxt.ap)) + { + r = kiaentity(lexp,sh_argstr(argp),-1,'p',0,0,lexp->script,'d',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;p;%..64d;%d;%d;d;\n",lexp->current,r,line,line); + } + } +#endif /* SHOPT_KIA */ + if(t->comnamp && (argp=t->comarg->argnxt.ap)) + { + Namval_t *np=(Namval_t*)t->comnamp; + if((np==SYSBREAK || np==SYSCONT) && (argp->argflag&ARG_RAW) && !isdigit(*argp->argval)) + { + register char *cp = argp->argval; + /* convert break/continue labels to numbers */ + tok = 0; + for(argp=label_list;argp!=label_last;argp=argp->argnxt.ap) + { + if(strcmp(cp,argp->argval)) + continue; + tok = loop_level-argp->argflag; + if(tok>=1) + { + argp = t->comarg->argnxt.ap; + if(tok>9) + { + argp->argval[1] = '0'+tok%10; + argp->argval[2] = 0; + tok /= 10; + } + else + argp->argval[1] = 0; + *argp->argval = '0'+tok; + } + break; + } + if(sh_isoption(SH_NOEXEC) && tok==0) + errormsg(SH_DICT,ERROR_warn(0),e_lexlabunknown,lexp->sh->inlineno-(lexp->token=='\n'),cp); + } + else if(sh_isoption(SH_NOEXEC) && np==SYSSET && ((tok= *argp->argval)=='-'||tok=='+') && + (argp->argval[1]==0||strchr(argp->argval,'k'))) + errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete5,lexp->sh->inlineno-(lexp->token=='\n'),argp->argval); + } + /* expand argument list if possible */ + if(argno>0 && !(flag&SH_ARRAY)) + t->comarg = qscan(t,argno); + else if(t->comarg) + t->comtyp |= COMSCAN; + lexp->aliasok = 0; + return((Shnode_t*)t); +} + +/* + * skip past newlines but issue prompt if interactive + */ +static int skipnl(Lex_t *lexp,int flag) +{ + register int token; + while((token=sh_lex(lexp))==NL); + if(token==';' && !(flag&SH_SEMI)) + sh_syntax(lexp); + return(token); +} + +/* + * check for and process and i/o redirections + * if flag>0 then an alias can be in the next word + * if flag<0 only one redirection will be processed + */ +static struct ionod *inout(Lex_t *lexp,struct ionod *lastio,int flag) +{ + register int iof = lexp->digits, token=lexp->token; + register struct ionod *iop; + Stk_t *stkp = lexp->sh->stk; + char *iovname=0; + register int errout=0; + if(token==IOVNAME) + { + iovname=lexp->arg->argval+1; + token= sh_lex(lexp); + iof = 0; + } + switch(token&0xff) + { + case '<': + if(token==IODOCSYM) + iof |= (IODOC|IORAW); + else if(token==IOMOV0SYM) + iof |= IOMOV; + else if(token==IORDWRSYMT) + iof |= IORDW|IOREWRITE; + else if(token==IORDWRSYM) + iof |= IORDW; + else if((token&SYMSHARP) == SYMSHARP) + { + int n; + iof |= IOLSEEK; + if(fcgetc(n)=='#') + iof |= IOCOPY; + else if(n>0) + fcseek(-1); + } + break; + + case '>': + if(iof<0) + { + errout = 1; + iof = 1; + } + iof |= IOPUT; + if(token==IOAPPSYM) + iof |= IOAPP; + else if(token==IOMOV1SYM) + iof |= IOMOV; + else if(token==IOCLOBSYM) + iof |= IOCLOB; + else if((token&SYMSHARP) == SYMSHARP) + iof |= IOLSEEK; + else if((token&SYMSEMI) == SYMSEMI) + iof |= IOREWRITE; + break; + + default: + return(lastio); + } + lexp->digits=0; + iop=(struct ionod*) stkalloc(stkp,sizeof(struct ionod)); + iop->iodelim = 0; + if(token=sh_lex(lexp)) + { + if(token==RPAREN && (iof&IOLSEEK) && lexp->comsub) + { + lexp->arg = (struct argnod*)stkalloc(stkp,sizeof(struct argnod)+3); + strcpy(lexp->arg->argval,"CUR"); + lexp->arg->argflag = ARG_RAW; + iof |= IOARITH; + fcseek(-1); + } + else if(token==EXPRSYM && (iof&IOLSEEK)) + iof |= IOARITH; + else if(((token==IPROCSYM && !(iof&IOPUT)) || (token==OPROCSYM && (iof&IOPUT))) && !(iof&(IOLSEEK|IOREWRITE|IOMOV|IODOC))) + { + lexp->arg = process_sub(lexp,token); + iof |= IOPROCSUB; + } + else + sh_syntax(lexp); + } + if( (iof&IOPROCSUB) && !(iof&IOLSEEK)) + iop->ioname= (char*)lexp->arg->argchn.ap; + else + iop->ioname=lexp->arg->argval; + iop->iovname = iovname; + if(iof&IODOC) + { + if(lexp->digits==2) + { + iof |= IOSTRG; + if(!(lexp->arg->argflag&ARG_RAW)) + iof &= ~IORAW; + } + else + { + if(!lexp->sh->heredocs) + lexp->sh->heredocs = sftmp(HERE_MEM); + iop->iolst=lexp->heredoc; + lexp->heredoc=iop; + if(lexp->arg->argflag&ARG_QUOTED) + iof |= IOQUOTE; + if(lexp->digits==3) + iof |= IOLSEEK; + if(lexp->digits) + iof |= IOSTRIP; + } + } + else + { + iop->iolst = 0; + if(lexp->arg->argflag&ARG_RAW) + iof |= IORAW; + } + iop->iofile=iof; + if(flag>0) + /* allow alias substitutions and parameter assignments */ + lexp->aliasok = lexp->assignok = 1; +#if SHOPT_KIA + if(lexp->kiafile) + { + int n = lexp->sh->inlineno-(lexp->token=='\n'); + if(!(iof&IOMOV)) + { + unsigned long r=kiaentity(lexp,(iof&IORAW)?sh_fmtq(iop->ioname):iop->ioname,-1,'f',0,0,lexp->script,'f',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;%c;%d\n",lexp->current,r,n,n,(iof&IOPUT)?((iof&IOAPP)?'a':'w'):((iof&IODOC)?'h':'r'),iof&IOUFD); + } + } +#endif /* SHOPT_KIA */ + if(flag>=0) + { + struct ionod *ioq=iop; + sh_lex(lexp); + if(errout) + { + /* redirect standard output to standard error */ + ioq = (struct ionod*)stkalloc(stkp,sizeof(struct ionod)); + memset(ioq,0,sizeof(*ioq)); + ioq->ioname = "1"; + ioq->iolst = 0; + ioq->iodelim = 0; + ioq->iofile = IORAW|IOPUT|IOMOV|2; + iop->ionxt=ioq; + } + ioq->ionxt=inout(lexp,lastio,flag); + } + else + iop->ionxt=0; + return(iop); +} + +/* + * convert argument chain to argument list when no special arguments + */ + +static struct argnod *qscan(struct comnod *ac,int argn) +{ + register char **cp; + register struct argnod *ap; + register struct dolnod* dp; + register int special=0; + /* special hack for test -t compatibility */ + if((Namval_t*)ac->comnamp==SYSTEST) + special = 2; + else if(*(ac->comarg->argval)=='[' && ac->comarg->argval[1]==0) + special = 3; + if(special) + { + ap = ac->comarg->argnxt.ap; + if(argn==(special+1) && ap->argval[1]==0 && *ap->argval=='!') + ap = ap->argnxt.ap; + else if(argn!=special) + special=0; + } + if(special) + { + const char *message; + if(strcmp(ap->argval,"-t")) + { + message = "line %d: Invariant test"; + special=0; + } + else + { + message = "line %d: -t requires argument"; + argn++; + } + if(sh_isoption(SH_NOEXEC)) + errormsg(SH_DICT,ERROR_warn(0),message,ac->comline); + } + /* leave space for an extra argument at the front */ + dp = (struct dolnod*)stakalloc((unsigned)sizeof(struct dolnod) + ARG_SPARE*sizeof(char*) + argn*sizeof(char*)); + cp = dp->dolval+ARG_SPARE; + dp->dolnum = argn; + dp->dolbot = ARG_SPARE; + ap = ac->comarg; + while(ap) + { + *cp++ = ap->argval; + ap = ap->argnxt.ap; + } + if(special==3) + { + cp[0] = cp[-1]; + cp[-1] = "1"; + cp++; + } + else if(special) + *cp++ = "1"; + *cp = 0; + return((struct argnod*)dp); +} + +static Shnode_t *test_expr(Lex_t *lp,int sym) +{ + register Shnode_t *t = test_or(lp); + if(lp->token!=sym) + sh_syntax(lp); + return(t); +} + +static Shnode_t *test_or(Lex_t *lp) +{ + register Shnode_t *t = test_and(lp); + while(lp->token==ORFSYM) + t = makelist(lp,TORF|TTEST,t,test_and(lp)); + return(t); +} + +static Shnode_t *test_and(Lex_t *lp) +{ + register Shnode_t *t = test_primary(lp); + while(lp->token==ANDFSYM) + t = makelist(lp,TAND|TTEST,t,test_primary(lp)); + return(t); +} + +/* + * convert =~ into == ~(E) + */ +static void ere_match(void) +{ + Sfio_t *base, *iop = sfopen((Sfio_t*)0," ~(E)","s"); + register int c; + while( fcgetc(c),(c==' ' || c=='\t')); + if(c) + fcseek(-1); + if(!(base=fcfile())) + base = sfopen(NIL(Sfio_t*),fcseek(0),"s"); + fcclose(); + sfstack(base,iop); + fcfopen(base); +} + +static Shnode_t *test_primary(Lex_t *lexp) +{ + register struct argnod *arg; + register Shnode_t *t; + register int num,token; + token = skipnl(lexp,0); + num = lexp->digits; + switch(token) + { + case '(': + t = test_expr(lexp,')'); + t = makelist(lexp,TTST|TTEST|TPAREN ,t, (Shnode_t*)pointerof(lexp->sh->inlineno)); + break; + case '!': + if(!(t = test_primary(lexp))) + sh_syntax(lexp); + t->tre.tretyp |= TNEGATE; + return(t); + case TESTUNOP: + if(sh_lex(lexp)) + sh_syntax(lexp); +#if SHOPT_KIA + if(lexp->kiafile && !strchr("sntzoOG",num)) + { + int line = lexp->sh->inlineno- (lexp->token==NL); + unsigned long r; + r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->script,'t',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line); + } +#endif /* SHOPT_KIA */ + t = makelist(lexp,TTST|TTEST|TUNARY|(num<<TSHIFT), + (Shnode_t*)lexp->arg,(Shnode_t*)lexp->arg); + t->tst.tstline = lexp->sh->inlineno; + break; + /* binary test operators */ + case 0: + arg = lexp->arg; + if((token=sh_lex(lexp))==TESTBINOP) + { + num = lexp->digits; + if(num==TEST_REP) + { + ere_match(); + num = TEST_PEQ; + } + } + else if(token=='<') + num = TEST_SLT; + else if(token=='>') + num = TEST_SGT; + else if(token==ANDFSYM||token==ORFSYM||token==ETESTSYM||token==RPAREN) + { + t = makelist(lexp,TTST|TTEST|TUNARY|('n'<<TSHIFT), + (Shnode_t*)arg,(Shnode_t*)arg); + t->tst.tstline = lexp->sh->inlineno; + return(t); + } + else + sh_syntax(lexp); +#if SHOPT_KIA + if(lexp->kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) + { + int line = lexp->sh->inlineno- (lexp->token==NL); + unsigned long r; + r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->current,'t',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line); + } +#endif /* SHOPT_KIA */ + if(sh_lex(lexp)) + sh_syntax(lexp); + if(num&TEST_PATTERN) + { + if(lexp->arg->argflag&(ARG_EXP|ARG_MAC)) + num &= ~TEST_PATTERN; + } + t = getnode(tstnod); + t->lst.lsttyp = TTST|TTEST|TBINARY|(num<<TSHIFT); + t->lst.lstlef = (Shnode_t*)arg; + t->lst.lstrit = (Shnode_t*)lexp->arg; + t->tst.tstline = lexp->sh->inlineno; +#if SHOPT_KIA + if(lexp->kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) + { + int line = lexp->sh->inlineno-(lexp->token==NL); + unsigned long r; + r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->current,'t',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line); + } +#endif /* SHOPT_KIA */ + break; + default: + return(0); + } + skipnl(lexp,0); + return(t); +} + +#if SHOPT_KIA +/* + * return an entity checksum + * The entity is created if it doesn't exist + */ +unsigned long kiaentity(Lex_t *lexp,const char *name,int len,int type,int first,int last,unsigned long parent, int pkind, int width, const char *attr) +{ + Stk_t *stkp = lexp->sh->stk; + Namval_t *np; + long offset = stktell(stkp); + sfputc(stkp,type); + if(len>0) + sfwrite(stkp,name,len); + else + { + if(type=='p') + sfputr(stkp,path_basename(name),0); + else + sfputr(stkp,name,0); + } + np = nv_search(stakptr(offset),lexp->entity_tree,NV_ADD); + stkseek(stkp,offset); + np->nvalue.i = pkind; + nv_setsize(np,width); + if(!nv_isattr(np,NV_TAGGED) && first>=0) + { + nv_onattr(np,NV_TAGGED); + if(!pkind) + pkind = '0'; + if(len>0) + sfprintf(lexp->kiafile,"%..64d;%c;%.*s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,len,name,first,last,parent,lexp->fscript,pkind,width,attr); + else + sfprintf(lexp->kiafile,"%..64d;%c;%s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,name,first,last,parent,lexp->fscript,pkind,width,attr); + } + return(np->hash); +} + +static void kia_add(register Namval_t *np, void *data) +{ + char *name = nv_name(np); + Lex_t *lp = (Lex_t*)data; + NOT_USED(data); + kiaentity(lp,name+1,-1,*name,0,-1,(*name=='p'?lp->unknown:lp->script),np->nvalue.i,nv_size(np),""); +} + +int kiaclose(Lex_t *lexp) +{ + register off_t off1,off2; + register int n; + if(lexp->kiafile) + { + unsigned long r = kiaentity(lexp,lexp->scriptname,-1,'p',-1,lexp->sh->inlineno-1,0,'s',0,""); + kiaentity(lexp,lexp->scriptname,-1,'p',1,lexp->sh->inlineno-1,r,'s',0,""); + kiaentity(lexp,lexp->scriptname,-1,'f',1,lexp->sh->inlineno-1,r,'s',0,""); + nv_scan(lexp->entity_tree,kia_add,(void*)lexp,NV_TAGGED,0); + off1 = sfseek(lexp->kiafile,(off_t)0,SEEK_END); + sfseek(lexp->kiatmp,(off_t)0,SEEK_SET); + sfmove(lexp->kiatmp,lexp->kiafile,SF_UNBOUND,-1); + off2 = sfseek(lexp->kiafile,(off_t)0,SEEK_END); +#ifdef SF_BUFCONST + if(off2==off1) + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%lld;%d\nDIRECTORY;",(Sflong_t)lexp->kiabegin,(size_t)(off1-lexp->kiabegin)); + else + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%lld;%d\nRELATIONSHIP;%lld;%d\nDIRECTORY;",(Sflong_t)lexp->kiabegin,(size_t)(off1-lexp->kiabegin),(Sflong_t)off1,(size_t)(off2-off1)); + if(off2 >= INT_MAX) + off2 = -(n+12); + sfprintf(lexp->kiafile,"%010.10lld;%010d\n",(Sflong_t)off2+10, n+12); +#else + if(off2==off1) + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%d;%d\nDIRECTORY;",lexp->kiabegin,off1-lexp->kiabegin); + else + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%d;%d\nRELATIONSHIP;%d;%d\nDIRECTORY;",lexp->kiabegin,off1-lexp->kiabegin,off1,off2-off1); + sfprintf(lexp->kiafile,"%010d;%010d\n",off2+10, n+12); +#endif + } + return(sfclose(lexp->kiafile)); +} +#endif /* SHOPT_KIA */ |