parse.c revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968
/***********************************************************************
* *
* 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 */