diff options
Diffstat (limited to 'usr/src/lib/libshell/common/sh/parse.c')
| -rw-r--r-- | usr/src/lib/libshell/common/sh/parse.c | 1757 |
1 files changed, 1757 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/sh/parse.c b/usr/src/lib/libshell/common/sh/parse.c new file mode 100644 index 0000000000..1cacd79e57 --- /dev/null +++ b/usr/src/lib/libshell/common/sh/parse.c @@ -0,0 +1,1757 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* 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> +#endif +#include <ctype.h> +#include <fcin.h> +#include <error.h> +#include "shlex.h" +#include "history.h" +#include "builtins.h" +#include "test.h" +#include "history.h" + +#define HERE_MEM 1024 /* size of here-docs kept in memory */ + +#define hash nvlink.hl._hash + +/* These routines are local to this module */ + +static Shnode_t *makeparent(int, Shnode_t*); +static Shnode_t *makelist(int, Shnode_t*, Shnode_t*); +static struct argnod *qscan(struct comnod*, int); +static struct ionod *inout(struct ionod*, int); +static Shnode_t *sh_cmd(int,int); +static Shnode_t *term(int); +static Shnode_t *list(int); +static struct regnod *syncase(int); +static Shnode_t *item(int); +static Shnode_t *simple(int, struct ionod*); +static int skipnl(int); +static Shnode_t *test_expr(int); +static Shnode_t *test_and(void); +static Shnode_t *test_or(void); +static Shnode_t *test_primary(void); + +#define sh_getlineno() (shlex.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 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(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=shlex.script; + if(type==0) + { + parent = shlex.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(sh_argstr(cmd),-1,'p',-1,-1,shlex.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 = sh.inlineno-(shlex.token==NL); + r=kiaentity(argp->argval,n,type,line,eline,parent,justify,width,atbuff); + sfprintf(shlex.kiatmp,"p;%..64d;v;%..64d;%d;%d;s;\n",shlex.current,r,line,eline); + argp = argp->argnxt.ap; + } + return(r); +} +#endif /* SHOPT_KIA */ +/* + * Make a parent node for fork() or io-redirection + */ +static Shnode_t *makeparent(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()-1; + return(par); +} + +static Shnode_t *getanode(struct argnod *ap) +{ + register Shnode_t *t = getnode(arithnod); + t->ar.artyp = TARITH; + t->ar.arline = sh_getlineno(); + t->ar.arexpr = ap; + if(ap->argflag&ARG_RAW) + t->ar.arcomp = sh_arithcomp(ap->argval); + else + t->ar.arcomp = 0; + return(t); +} + +/* + * Make a node corresponding to a command list + */ +static Shnode_t *makelist(int type, Shnode_t *l, Shnode_t *r) +{ + register Shnode_t *t; + if(!l || !r) + sh_syntax(); + 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; + Fcin_t sav_input; + struct argnod *sav_arg = shlex.arg; + int sav_prompt = shp->nextprompt; + if(shp->binscript && sffileno(iop)==shp->infd) + return((void*)sh_trestore(iop)); + fcsave(&sav_input); + shp->st.staklist = 0; + shlex.heredoc = 0; + shlex.inlineno = shp->inlineno; + shlex.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((Lex_t*)shp->lex_context,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); + shlex.arg = sav_arg; + if(version > 3) + errormsg(SH_DICT,ERROR_exit(1),e_lexversion); + if(sffileno(iop)==shp->infd) + shp->binscript = 1; + sfgetc(iop); + return((void*)sh_trestore(iop)); + } + } + 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((flag&SH_EOF)?EOFSYM:'\n',SH_SEMI|SH_EMPTY|(flag&SH_NL)); + fcclose(); + fcrestore(&sav_input); + shlex.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 = shlex.firstline; + shp->inlineno = shlex.inlineno; + } + stakseek(0); + return((void*)t); +} + +/* + * This routine parses up the matching right parenthesis and returns + * the parse tree + */ +Shnode_t *sh_dolparen(void) +{ + register Shnode_t *t=0; + register Lex_t *lp = (Lex_t*)sh.lex_context; + Sfio_t *sp = fcfile(); + int line = sh.inlineno; + sh.inlineno = error_info.line+sh.st.firstline; + sh_lexopen(lp,&sh,1); + shlex.comsub = 1; + switch(sh_lex()) + { + /* ((...)) arithmetic expression */ + case EXPRSYM: + t = getanode(shlex.arg); + break; + case LPAREN: + t = sh_cmd(RPAREN,SH_NL|SH_EMPTY); + break; + } + shlex.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); + } + sh.inlineno = line; + return(t); +} + +/* + * remove temporary files and stacks + */ + +void sh_freeup(void) +{ + if(sh.st.staklist) + sh_funstaks(sh.st.staklist,-1); + sh.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(register int sym, int flag) +{ + register Shnode_t *left, *right; + register int type = FINT|FAMP; + if(sym==NL) + shlex.lasttok = 0; + left = list(flag); + if(shlex.token==NL) + { + if(flag&SH_NL) + shlex.token=';'; + } + else if(!left && !(flag&SH_EMPTY)) + sh_syntax(); + switch(shlex.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(TFORK|type, left); + } + /* FALL THRU */ + case ';': + if(!left) + sh_syntax(); + if(right=sh_cmd(sym,flag|SH_EMPTY)) + left=makelist(TLST, left, right); + break; + case EOFSYM: + if(sym==NL) + break; + default: + if(sym && sym!=shlex.token) + { + if(sym!=ELSESYM || (shlex.token!=ELIFSYM && shlex.token!=FISYM)) + sh_syntax(); + } + } + return(left); +} + +/* + * list + * term + * list && term + * list || term + * unfortunately, these are equal precedence + */ +static Shnode_t *list(register int flag) +{ + register Shnode_t *t = term(flag); + register int token; + while(t && ((token=shlex.token)==ANDFSYM || token==ORFSYM)) + t = makelist((token==ANDFSYM?TAND:TORF), t, term(SH_NL|SH_SEMI)); + return(t); +} + +/* + * term + * item + * item | term + */ +static Shnode_t *term(register int flag) +{ + register Shnode_t *t; + register int token; + if(flag&SH_NL) + token = skipnl(flag); + else + token = sh_lex(); + /* check to see if pipeline is to be timed */ + if(token==TIMESYM || token==NOTSYM) + { + t = getnode(parnod); + t->par.partyp=TTIME; + if(shlex.token==NOTSYM) + t->par.partyp |= COMSCAN; + t->par.partre = term(0); + } + else if((t=item(SH_NL|SH_EMPTY|(flag&SH_SEMI))) && shlex.token=='|') + { + register Shnode_t *tt; + int showme = t->tre.tretyp&FSHOWME; + t = makeparent(TFORK|FPOU,t); + if(tt=term(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(TSETIO|FPIN|FPCL,tt); + } + t=makelist(TFIL,t,tt); + t->tre.tretyp |= showme; + } + else if(shlex.token) + sh_syntax(); + } + return(t); +} + +/* + * case statement + */ +static struct regnod* syncase(register int esym) +{ + register int tok = skipnl(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(0); + while(1) + { + if(!shlex.arg) + sh_syntax(); + shlex.arg->argnxt.ap=r->regptr; + r->regptr = shlex.arg; + if((tok=sh_lex())==RPAREN) + break; + else if(tok=='|') + sh_lex(); + else + sh_syntax(); + } + r->regcom=sh_cmd(0,SH_NL|SH_EMPTY|SH_SEMI); + if((tok=shlex.token)==BREAKCASESYM) + r->regnxt=syncase(esym); + else if(tok==FALLTHRUSYM) + { + r->regflag++; + r->regnxt=syncase(esym); + } + else + { + if(tok!=esym && tok!=EOFSYM) + sh_syntax(); + r->regnxt=0; + } + if(shlex.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(register Shnode_t *tf) +{ + register Shnode_t *t, *tw = tf; + register int offset; + register struct argnod *argp; + register int n; + int argflag = shlex.arg->argflag; + /* save current input */ + Fcin_t sav_input; + fcsave(&sav_input); + fcsopen(shlex.arg->argval); + /* split ((...)) into three expressions */ + for(n=0; ; n++) + { + register int c; + argp = (struct argnod*)stakseek(ARGVAL); + argp->argnxt.ap = 0; + argp->argchn.cp = 0; + argp->argflag = argflag; + if(n==2) + break; + /* copy up to ; onto the stack */ + sh_lexskip(';',1,ST_NESTED); + offset = staktell()-1; + if((c=fcpeek(-1))!=';') + break; + /* remove trailing white space */ + while(offset>ARGVAL && ((c= *stakptr(offset-1)),isspace(c))) + offset--; + /* check for empty initialization expression */ + if(offset==ARGVAL && n==0) + continue; + stakseek(offset); + /* check for empty condition and treat as while((1)) */ + if(offset==ARGVAL) + stakputc('1'); + argp = (struct argnod*)stakfreeze(1); + t = getanode(argp); + if(n==0) + tf = makelist(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) + { + shlex.token = RPAREN|SYMREP; + sh_syntax(); + } + /* check whether the increment is present */ + if(*argp->argval) + { + t = getanode(argp); + tw->wh.whinc = (struct arithnod*)t; + } + else + tw->wh.whinc = 0; + sh_lexopen((Lex_t*)sh.lex_context, &sh,1); + if((n=sh_lex())==NL) + n = skipnl(0); + else if(n==';') + n = sh_lex(); + if(n!=DOSYM && n!=LBRACE) + sh_syntax(); + tw->wh.dotre = sh_cmd(n==DOSYM?DONESYM:RBRACE,SH_NL); + tw->wh.whtyp = TWH; + return(tf); + +} + +static Shnode_t *funct(void) +{ + register Shnode_t *t; + register int flag; + struct slnod *volatile slp=0; + Stak_t *savstak; + Sfoff_t first, last; + struct functnod *fp; + Sfio_t *iop; +#if SHOPT_KIA + unsigned long current = shlex.current; +#endif /* SHOPT_KIA */ + int jmpval, saveloop=loop_level; + struct argnod *savelabel = label_last; + struct checkpt buff; + t = getnode(functnod); + t->funct.functline = sh.inlineno; + t->funct.functtyp=TFUN; + t->funct.functargs = 0; + if(!(flag = (shlex.token==FUNCTSYM))) + t->funct.functtyp |= FPOSIX; + else if(sh_lex()) + sh_syntax(); + if(!(iop=fcfile())) + { + iop = sfopen(NIL(Sfio_t*),fcseek(0),"s"); + fcclose(); + fcfopen(iop); + } + t->funct.functloc = first = fctell(); + if(!sh.st.filename || sffileno(iop)<0) + { + if(fcfill() >= 0) + fcseek(-1); + if(sh_isstate(SH_HISTORY)) + t->funct.functloc = sfseek(sh.hist_ptr->histfp,(off_t)0,SEEK_CUR); + else + { + /* copy source to temporary file */ + t->funct.functloc = 0; + if(shlex.sh->heredocs) + t->funct.functloc = sfseek(shlex.sh->heredocs,(Sfoff_t)0, SEEK_END); + else + shlex.sh->heredocs = sftmp(HERE_MEM); + shlex.sh->funlog = shlex.sh->heredocs; + t->funct.functtyp |= FPIN; + } + } + t->funct.functnam= (char*)shlex.arg->argval; +#if SHOPT_KIA + if(shlex.kiafile) + shlex.current = kiaentity(t->funct.functnam,-1,'p',-1,-1,shlex.script,'p',0,""); +#endif /* SHOPT_KIA */ + if(flag) + { + shlex.token = sh_lex(); +#if SHOPT_BASH + if(shlex.token == LPAREN) + { + if((shlex.token = sh_lex()) == RPAREN) + t->funct.functtyp |= FPOSIX; + else + sh_syntax(); + } +#endif + } + if(t->funct.functtyp&FPOSIX) + skipnl(0); + else + { + if(shlex.token==0) + t->funct.functargs = (struct comnod*)simple(SH_NOIO|SH_FUNDEF,NIL(struct ionod*)); + while(shlex.token==NL) + shlex.token = sh_lex(); + } + if((flag && shlex.token!=LBRACE) || shlex.token==EOFSYM) + sh_syntax(); + sh_pushcontext(&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 = sh.st.staklist; + sh.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(sh.st.filename) + fp->functnam = stakcopy(sh.st.filename); + loop_level = 0; + label_last = label_list; + if(!flag && shlex.token==0) + { + /* copy current word token to current stak frame */ + struct argnod *ap; + flag = ARGVAL + strlen(shlex.arg->argval); + ap = (struct argnod*)stakalloc(flag); + memcpy(ap,shlex.arg,flag); + shlex.arg = ap; + } + t->funct.functtre = item(SH_NOIO); + } + sh_popcontext(&buff); + loop_level = saveloop; + label_last = savelabel; + /* restore the old stack */ + if(slp) + { + slp->slptr = stakinstall(savstak,0); + slp->slchild = sh.st.staklist; + } +#if SHOPT_KIA + shlex.current = current; +#endif /* SHOPT_KIA */ + if(jmpval) + { + if(slp && slp->slptr) + { + sh.st.staklist = slp->slnext; + stakdelete(slp->slptr); + } + siglongjmp(*sh.jmplist,jmpval); + } + sh.st.staklist = (struct slnod*)slp; + last = fctell(); + fp->functline = (last-first); + fp->functtre = t; + if(shlex.sh->funlog) + { + if(fcfill()>0) + fcseek(-1); + shlex.sh->funlog = 0; + } +#if SHOPT_KIA + if(shlex.kiafile) + kiaentity(t->funct.functnam,-1,'p',t->funct.functline,sh.inlineno-1,shlex.current,'p',0,""); +#endif /* SHOPT_KIA */ + return(t); +} + +/* + * Compound assignment + */ +static struct argnod *assign(register struct argnod *ap) +{ + register int n; + register Shnode_t *t, **tp; + register struct comnod *ac; + int array=0; + Namval_t *np; + n = strlen(ap->argval)-1; + if(ap->argval[n]!='=') + sh_syntax(); + 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(); + tp = &t->for_.fortre; + ap->argchn.ap = (struct argnod*)t; + ap->argflag &= ARG_QUOTED; + ap->argflag |= array; + shlex.assignok = SH_ASSIGN; + array=0; + if((n=skipnl(0))==RPAREN || n==LPAREN) + { + int index= 0; + struct argnod **settail; + ac = (struct comnod*)getnode(comnod); + settail= &ac->comset; + memset((void*)ac,0,sizeof(*ac)); + ac->comline = sh_getlineno(); + while(n==LPAREN) + { + struct argnod *ap; + ap = (struct argnod*)stakseek(ARGVAL); + ap->argflag= ARG_ASSIGN; + sfprintf(stkstd,"[%d]=",index++); + ap = (struct argnod*)stakfreeze(1); + ap->argnxt.ap = 0; + ap = assign(ap); + ap->argflag |= ARG_MESSAGE; + *settail = ap; + settail = &(ap->argnxt.ap); + n = skipnl(0); + } + } + else if(n) + sh_syntax(); + else if(!(shlex.arg->argflag&ARG_ASSIGN) && !((np=nv_search(shlex.arg->argval,sh.fun_tree,0)) && nv_isattr(np,BLT_DCL))) + array=SH_ARRAY; + while(1) + { + if((n=shlex.token)==RPAREN) + break; + if(n==FUNCTSYM || n==SYMRES) + ac = (struct comnod*)funct(); + else + ac = (struct comnod*)simple(SH_NOIO|SH_ASSIGN|array,NIL(struct ionod*)); + if((n=shlex.token)==RPAREN) + break; + if(n!=NL && n!=';') + sh_syntax(); + shlex.assignok = SH_ASSIGN; + if((n=skipnl(0)) || array) + { + if(n==RPAREN) + break; + if(array || n!=FUNCTSYM) + sh_syntax(); + } + if((n!=FUNCTSYM) && !(shlex.arg->argflag&ARG_ASSIGN) && !((np=nv_search(shlex.arg->argval,sh.fun_tree,0)) && nv_isattr(np,BLT_DCL))) + { + struct argnod *arg = shlex.arg; + if(n!=0) + sh_syntax(); + /* check for sys5 style function */ + if(sh_lex()!=LPAREN || sh_lex()!=RPAREN) + { + shlex.arg = arg; + shlex.token = 0; + sh_syntax(); + } + shlex.arg = arg; + shlex.token = SYMRES; + } + t = makelist(TLST,(Shnode_t*)ac,t); + *tp = t; + tp = &t->lst.lstrit; + } + *tp = (Shnode_t*)ac; + shlex.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(int flag) +{ + register Shnode_t *t; + register struct ionod *io; + register int tok = (shlex.token&0xff); + int savwdval = shlex.lasttok; + int savline = shlex.lastline; + int showme=0; + if(!(flag&SH_NOIO) && (tok=='<' || tok=='>')) + io=inout(NIL(struct ionod*),1); + else + io=0; + if((tok=shlex.token) && tok!=EOFSYM && tok!=FUNCTSYM) + { + shlex.lastline = sh_getlineno(); + shlex.lasttok = shlex.token; + } + switch(tok) + { + /* [[ ... ]] test expression */ + case BTESTSYM: + t = test_expr(ETESTSYM); + t->tre.tretyp &= ~TTEST; + break; + /* ((...)) arithmetic expression */ + case EXPRSYM: + t = getanode(shlex.arg); + sh_lex(); + goto done; + + /* case statement */ + case CASESYM: + { + int savetok = shlex.lasttok; + int saveline = shlex.lastline; + t = getnode(swnod); + if(sh_lex()) + sh_syntax(); + t->sw.swarg=shlex.arg; + t->sw.swtyp=TSW; + t->sw.swio = 0; + t->sw.swtyp |= FLINENO; + t->sw.swline = sh.inlineno; + if((tok=skipnl(0))!=INSYM && tok!=LBRACE) + sh_syntax(); + if(!(t->sw.swlst=syncase(tok==INSYM?ESACSYM:RBRACE)) && shlex.token==EOFSYM) + { + shlex.lasttok = savetok; + shlex.lastline = saveline; + sh_syntax(); + } + break; + } + + /* if statement */ + case IFSYM: + { + register Shnode_t *tt; + t = getnode(ifnod); + t->if_.iftyp=TIF; + t->if_.iftre=sh_cmd(THENSYM,SH_NL); + t->if_.thtre=sh_cmd(ELSESYM,SH_NL|SH_SEMI); + tok = shlex.token; + t->if_.eltre=(tok==ELSESYM?sh_cmd(FISYM,SH_NL|SH_SEMI): + (tok==ELIFSYM?(shlex.token=IFSYM, tt=item(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=(shlex.token==FORSYM?TFOR:TSELECT); + t->for_.forlst=0; + t->for_.forline = sh.inlineno; + if(sh_lex()) + { + if(shlex.token!=EXPRSYM || t->for_.fortyp!=TFOR) + sh_syntax(); + /* arithmetic for */ + t = arithfor(t); + break; + } + t->for_.fornam=(char*) shlex.arg->argval; + t->for_.fortyp |= FLINENO; +#if SHOPT_KIA + if(shlex.kiafile) + writedefs(shlex.arg,sh.inlineno,'v',NIL(struct argnod*)); +#endif /* SHOPT_KIA */ + while((tok=sh_lex())==NL); + if(tok==INSYM) + { + if(sh_lex()) + { + if(shlex.token != NL && shlex.token !=';') + sh_syntax(); + /* some Linux scripts assume this */ + if(sh_isoption(SH_NOEXEC)) + errormsg(SH_DICT,ERROR_warn(0),e_lexemptyfor,sh.inlineno-(shlex.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(SH_NOIO,NIL(struct ionod*)); + if(shlex.token != NL && shlex.token !=';') + sh_syntax(); + tok = skipnl(0); + } + /* 'for i;do cmd' is valid syntax */ + else if(tok==';') + tok=sh_lex(); + if(tok!=DOSYM && tok!=LBRACE) + sh_syntax(); + loop_level++; + t->for_.fortre=sh_cmd(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()); + +#if SHOPT_NAMESPACE + case NSPACESYM: + t = getnode(fornod); + t->for_.fortyp=TNSPACE; + t->for_.forlst=0; + if(sh_lex()) + sh_syntax(); + t->for_.fornam=(char*) shlex.arg->argval; + while((tok=sh_lex())==NL); + if(tok!=LBRACE) + sh_syntax(); + t->for_.fortre = sh_cmd(RBRACE,SH_NL); + break; +#endif /* SHOPT_NAMESPACE */ + + /* while and until */ + case WHILESYM: + case UNTILSYM: + t = getnode(whnod); + t->wh.whtyp=(shlex.token==WHILESYM ? TWH : TUN); + loop_level++; + t->wh.whtre = sh_cmd(DOSYM,SH_NL); + t->wh.dotre = sh_cmd(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,shlex.arg->argval)==0) + errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax3,sh.inlineno,argp->argval); + argp = argp->argnxt.ap; + } + shlex.arg->argnxt.ap = label_list; + label_list = shlex.arg; + label_list->argchn.len = sh_getlineno(); + label_list->argflag = loop_level; + skipnl(flag); + if(!(t = item(SH_NL))) + sh_syntax(); + 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: + t = sh_cmd(RBRACE,SH_NL); + break; + + case LPAREN: + t = getnode(parnod); + t->par.partre=sh_cmd(RPAREN,SH_NL); + t->par.partyp=TPAR; + break; + + default: + if(io==0) + return(0); + + case ';': + if(io==0) + { + if(!(flag&SH_SEMI)) + return(0); + if(sh_lex()==';') + sh_syntax(); + showme = FSHOWME; + } + /* simple command */ + case 0: + t = (Shnode_t*)simple(flag,io); + t->tre.tretyp |= showme; + return(t); + } + sh_lex(); + if(io=inout(io,0)) + { + if((tok=t->tre.tretyp&COMMSK) != TFORK) + tok = TSETIO; + t=makeparent(tok,t); + t->tre.treio=io; + } +done: + shlex.lasttok = savwdval; + shlex.lastline = savline; + return(t); +} + +/* + * This is for a simple command, for list, or compound assignment + */ +static Shnode_t *simple(int flag, struct ionod *io) +{ + register struct comnod *t; + register struct argnod *argp; + register int tok; + struct argnod **argtail; + struct argnod **settail; + int argno = 0; + int assignment = 0; + int key_on = (!(flag&SH_NOIO) && sh_isoption(SH_KEYWORD)); + int associative=0; + if((argp=shlex.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(); + argtail = &(t->comarg); + t->comset = 0; + t->comnamp = 0; + t->comnamq = 0; + t->comstate = 0; + settail = &(t->comset); + while(shlex.token==0) + { + argp = shlex.arg; + if(*argp->argval==LBRACE && (flag&SH_FUNDEF) && argp->argval[1]==0) + { + shlex.token = LBRACE; + break; + } + if(associative && argp->argval[0]!='[') + sh_syntax(); + /* check for assignment argument */ + if((argp->argflag&ARG_ASSIGN) && assignment!=2) + { + *settail = argp; + settail = &(argp->argnxt.ap); + shlex.assignok = (flag&SH_ASSIGN)?SH_ASSIGN:1; + if(assignment) + { + struct argnod *ap=argp; + char *last, *cp; + if(assignment==1) + { + last = strchr(argp->argval,'='); + if((cp=strchr(argp->argval,'[')) && (cp < last)) + last = cp; + stakseek(ARGVAL); + stakwrite(argp->argval,last-argp->argval); + ap=(struct argnod*)stakfreeze(1); + ap->argflag = ARG_RAW; + ap->argchn.ap = 0; + } + *argtail = ap; + argtail = &(ap->argnxt.ap); + if(argno>=0) + argno++; + } + else /* alias substitutions allowed */ + shlex.aliasok = 1; + } + else + { + if(!(argp->argflag&ARG_RAW)) + argno = -1; + if(argno>=0 && argno++==0 && !(flag&SH_ARRAY) && *argp->argval!='/') + { + /* check for builtin command */ + Namval_t *np=nv_bfsearch(argp->argval,sh.fun_tree, (Namval_t**)&t->comnamq,(char**)0); + if((t->comnamp=(void*)np) && is_abuiltin(np) && + nv_isattr(np,BLT_DCL)) + { + assignment = 1+(*argp->argval=='a'); + key_on = 1; + } + } + *argtail = argp; + argtail = &(argp->argnxt.ap); + if(!(shlex.assignok=key_on) && !(flag&SH_NOIO)) + shlex.assignok = SH_COMPASSIGN; + shlex.aliasok = 0; + } + retry: + tok = sh_lex(); +#if SHOPT_DEVFD + if((tok==IPROCSYM || tok==OPROCSYM)) + { + Shnode_t *t; + int mode = (tok==OPROCSYM); + t = sh_cmd(RPAREN,SH_NL); + argp = (struct argnod*)stakalloc(sizeof(struct argnod)); + *argp->argval = 0; + argno = -1; + *argtail = argp; + argtail = &(argp->argnxt.ap); + argp->argchn.ap = (struct argnod*)makeparent(mode?TFORK|FPIN|FAMP|FPCL:TFORK|FPOU,t); + argp->argflag = (ARG_EXP|mode); + goto retry; + } +#endif /* SHOPT_DEVFD */ + if(tok==LPAREN) + { + if(argp->argflag&ARG_ASSIGN) + { + argp = assign(argp); + if(associative) + shlex.assignok |= SH_ASSIGN; + goto retry; + } + else if(argno==1 && !t->comset) + { + /* SVR2 style function */ + if(sh_lex() == RPAREN) + { + shlex.arg = argp; + return(funct()); + } + shlex.token = LPAREN; + } + } + else if(flag&SH_ASSIGN) + { + if(tok==RPAREN) + break; + else if(tok==NL && (flag&SH_ARRAY)) + goto retry; + } + if(!(flag&SH_NOIO)) + { + if(io) + { + while(io->ionxt) + io = io->ionxt; + io->ionxt = inout((struct ionod*)0,0); + } + else + t->comio = io = inout((struct ionod*)0,0); + } + } + *argtail = 0; + t->comtyp = TCOM; +#if SHOPT_KIA + if(shlex.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(nv_name(np),-1,'p',-1,0,shlex.unknown,'b',0,""); + else if(argp) + r = kiaentity(sh_argstr(argp),-1,'p',-1,0,shlex.unknown,'c',0,""); + if(r>0) + sfprintf(shlex.kiatmp,"p;%..64d;p;%..64d;%d;%d;c;\n",shlex.current,r,line,line); + if(t->comset && argno==0) + writedefs(t->comset,line,'v',t->comarg); + else if(np && nv_isattr(np,BLT_DCL)) + writedefs(argp,line,0,NIL(struct argnod*)); + else if(argp && strcmp(argp->argval,"read")==0) + writedefs(argp,line,0,NIL(struct argnod*)); +#if 0 + else if(argp && strcmp(argp->argval,"unset")==0) + writedefs(argp,line,'u',NIL(struct argnod*)); +#endif + else if(argp && *argp->argval=='.' && argp->argval[1]==0 && (argp=argp->argnxt.ap)) + { + r = kiaentity(sh_argstr(argp),-1,'p',0,0,shlex.script,'d',0,""); + sfprintf(shlex.kiatmp,"p;%..64d;p;%..64d;%d;%d;d;\n",shlex.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,sh.inlineno-(shlex.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,sh.inlineno-(shlex.token=='\n'),argp->argval); + } + /* expand argument list if possible */ + if(argno>0) + t->comarg = qscan(t,argno); + else if(t->comarg) + t->comtyp |= COMSCAN; + shlex.aliasok = 0; + return((Shnode_t*)t); +} + +/* + * skip past newlines but issue prompt if interactive + */ +static int skipnl(int flag) +{ + register int token; + while((token=sh_lex())==NL); + if(token==';' && !(flag&SH_SEMI)) + sh_syntax(); + 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(struct ionod *lastio,int flag) +{ + register int iof = shlex.digits, token=shlex.token; + register struct ionod *iop; + char *iovname=0; +#if SHOPT_BASH + register int errout=0; +#endif + if(token==IOVNAME) + { + iovname=shlex.arg->argval+1; + token= sh_lex(); + iof = 0; + } + switch(token&0xff) + { + case '<': + if(token==IODOCSYM) + iof |= (IODOC|IORAW); + else if(token==IOMOV0SYM) + iof |= IOMOV; + 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 SHOPT_BASH + if(iof<0) + { + errout = 1; + iof = 1; + } +#endif + 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; + break; + + default: + return(lastio); + } + shlex.digits=0; + iop=(struct ionod*) stakalloc(sizeof(struct ionod)); + iop->iodelim = 0; + if(token=sh_lex()) + { + if(token==RPAREN && (iof&IOLSEEK) && shlex.comsub) + { + shlex.arg = (struct argnod*)stakalloc(sizeof(struct argnod)+3); + strcpy(shlex.arg->argval,"CUR"); + shlex.arg->argflag = ARG_RAW; + iof |= IOARITH; + fcseek(-1); + } + else if(token==EXPRSYM && (iof&IOLSEEK)) + iof |= IOARITH; + else + sh_syntax(); + } + iop->ioname=shlex.arg->argval; + iop->iovname = iovname; + if(iof&IODOC) + { + if(shlex.digits==2) + { + iof |= IOSTRG; + if(!(shlex.arg->argflag&ARG_RAW)) + iof &= ~IORAW; + } + else + { + if(!shlex.sh->heredocs) + shlex.sh->heredocs = sftmp(HERE_MEM); + iop->iolst=shlex.heredoc; + shlex.heredoc=iop; + if(shlex.arg->argflag&ARG_QUOTED) + iof |= IOQUOTE; + if(shlex.digits==3) + iof |= IOLSEEK; + if(shlex.digits) + iof |= IOSTRIP; + } + } + else + { + iop->iolst = 0; + if(shlex.arg->argflag&ARG_RAW) + iof |= IORAW; + } + iop->iofile=iof; + if(flag>0) + /* allow alias substitutions and parameter assignments */ + shlex.aliasok = shlex.assignok = 1; +#if SHOPT_KIA + if(shlex.kiafile) + { + int n = sh.inlineno-(shlex.token=='\n'); + if(!(iof&IOMOV)) + { + unsigned long r=kiaentity((iof&IORAW)?sh_fmtq(iop->ioname):iop->ioname,-1,'f',0,0,shlex.script,'f',0,""); + sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;%c;%d\n",shlex.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(); +#if SHOPT_BASH + if(errout) + { + /* redirect standard output to standard error */ + ioq = (struct ionod*)stakalloc(sizeof(struct ionod)); + ioq->ioname = "1"; + ioq->iolst = 0; + ioq->iodelim = 0; + ioq->iofile = IORAW|IOPUT|IOMOV|2; + iop->ionxt=ioq; + } +#endif + ioq->ionxt=inout(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(int sym) +{ + register Shnode_t *t = test_or(); + if(shlex.token!=sym) + sh_syntax(); + return(t); +} + +static Shnode_t *test_or(void) +{ + register Shnode_t *t = test_and(); + while(shlex.token==ORFSYM) + t = makelist(TORF|TTEST,t,test_and()); + return(t); +} + +static Shnode_t *test_and(void) +{ + register Shnode_t *t = test_primary(); + while(shlex.token==ANDFSYM) + t = makelist(TAND|TTEST,t,test_primary()); + 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(void) +{ + register struct argnod *arg; + register Shnode_t *t; + register int num,token; + token = skipnl(0); + num = shlex.digits; + switch(token) + { + case '(': + t = test_expr(')'); + t = makelist(TTST|TTEST|TPAREN ,t, (Shnode_t*)pointerof(sh.inlineno)); + break; + case '!': + if(!(t = test_primary())) + sh_syntax(); + t->tre.tretyp |= TNEGATE; + return(t); + case TESTUNOP: + if(sh_lex()) + sh_syntax(); +#if SHOPT_KIA + if(shlex.kiafile && !strchr("sntzoOG",num)) + { + int line = sh.inlineno- (shlex.token==NL); + unsigned long r; + r=kiaentity(sh_argstr(shlex.arg),-1,'f',0,0,shlex.script,'t',0,""); + sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",shlex.current,r,line,line); + } +#endif /* SHOPT_KIA */ + t = makelist(TTST|TTEST|TUNARY|(num<<TSHIFT), + (Shnode_t*)shlex.arg,(Shnode_t*)shlex.arg); + t->tst.tstline = sh.inlineno; + break; + /* binary test operators */ + case 0: + arg = shlex.arg; + if((token=sh_lex())==TESTBINOP) + { + num = shlex.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(TTST|TTEST|TUNARY|('n'<<TSHIFT), + (Shnode_t*)arg,(Shnode_t*)arg); + t->tst.tstline = sh.inlineno; + return(t); + } + else + sh_syntax(); +#if SHOPT_KIA + if(shlex.kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) + { + int line = sh.inlineno- (shlex.token==NL); + unsigned long r; + r=kiaentity(sh_argstr(shlex.arg),-1,'f',0,0,shlex.current,'t',0,""); + sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",shlex.current,r,line,line); + } +#endif /* SHOPT_KIA */ + if(sh_lex()) + sh_syntax(); + if(num&TEST_PATTERN) + { + if(shlex.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*)shlex.arg; + t->tst.tstline = sh.inlineno; +#if SHOPT_KIA + if(shlex.kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) + { + int line = sh.inlineno-(shlex.token==NL); + unsigned long r; + r=kiaentity(sh_argstr(shlex.arg),-1,'f',0,0,shlex.current,'t',0,""); + sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",shlex.current,r,line,line); + } +#endif /* SHOPT_KIA */ + break; + default: + return(0); + } + skipnl(0); + return(t); +} + +#if SHOPT_KIA +/* + * return an entity checksum + * The entity is created if it doesn't exist + */ +unsigned long kiaentity(const char *name,int len,int type,int first,int last,unsigned long parent, int pkind, int width, const char *attr) +{ + Namval_t *np; + long offset = staktell(); + stakputc(type); + if(len>0) + stakwrite(name,len); + else + { + if(type=='p') + stakputs(path_basename(name)); + else + stakputs(name); + } + stakputc(0); + np = nv_search(stakptr(offset),shlex.entity_tree,NV_ADD); + stakseek(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(shlex.kiafile,"%..64d;%c;%.*s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,len,name,first,last,parent,shlex.fscript,pkind,width,attr); + else + sfprintf(shlex.kiafile,"%..64d;%c;%s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,name,first,last,parent,shlex.fscript,pkind,width,attr); + } + return(np->hash); +} + +static void kia_add(register Namval_t *np, void *data) +{ + char *name = nv_name(np); + NOT_USED(data); + kiaentity(name+1,-1,*name,0,-1,(*name=='p'?shlex.unknown:shlex.script),np->nvalue.i,nv_size(np),""); +} + +int kiaclose(void) +{ + register off_t off1,off2; + register int n; + if(shlex.kiafile) + { + unsigned long r = kiaentity(shlex.scriptname,-1,'p',-1,sh.inlineno-1,0,'s',0,""); + kiaentity(shlex.scriptname,-1,'p',1,sh.inlineno-1,r,'s',0,""); + kiaentity(shlex.scriptname,-1,'f',1,sh.inlineno-1,r,'s',0,""); + nv_scan(shlex.entity_tree,kia_add,(void*)0,NV_TAGGED,0); + off1 = sfseek(shlex.kiafile,(off_t)0,SEEK_END); + sfseek(shlex.kiatmp,(off_t)0,SEEK_SET); + sfmove(shlex.kiatmp,shlex.kiafile,SF_UNBOUND,-1); + off2 = sfseek(shlex.kiafile,(off_t)0,SEEK_END); +#ifdef SF_BUFCONST + if(off2==off1) + n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%lld;%d\nDIRECTORY;",(Sflong_t)shlex.kiabegin,(size_t)(off1-shlex.kiabegin)); + else + n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%lld;%d\nRELATIONSHIP;%lld;%d\nDIRECTORY;",(Sflong_t)shlex.kiabegin,(size_t)(off1-shlex.kiabegin),(Sflong_t)off1,(size_t)(off2-off1)); + if(off2 >= INT_MAX) + off2 = -(n+12); + sfprintf(shlex.kiafile,"%010.10lld;%010d\n",(Sflong_t)off2+10, n+12); +#else + if(off2==off1) + n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%d;%d\nDIRECTORY;",shlex.kiabegin,off1-shlex.kiabegin); + else + n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%d;%d\nRELATIONSHIP;%d;%d\nDIRECTORY;",shlex.kiabegin,off1-shlex.kiabegin,off1,off2-off1); + sfprintf(shlex.kiafile,"%010d;%010d\n",off2+10, n+12); +#endif + } + return(sfclose(shlex.kiafile)); +} +#endif /* SHOPT_KIA */ |
