summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/parse.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/parse.c
downloadksh-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.c2082
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 */