1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-24 06:54:13 +00:00
cde/src/cmd/ksh93/sh/parse.c
Martijn Dekker b9d10c5a9c Fix 'command' expansion bug and POSIX compliance
The 'command' name can now result from an expansion, e.g.:
	c=command; "$c" ls
	set -- command ls; "$@"
both work now. This fixes BUG_CMDEXPAN.

If -o posix is on, 'command' now disables not only the "special"
but also the "declaration" properties of builtin commands that it
invokes. This is because POSIX specifies 'command' as a simple
regular builtin, and any command name following 'command' is just
an argument to the 'command' command, so there is nothing that
allows any further arguments (such as assignment-arguments) to be
treated specially by the parser. So, if and only if -o posix is on:
a. Arguments that start with a variable name followed by '=' are
   always treated as regular words subject to normal shell syntax.
b. Since assignment-arguments are not processed as assignments
   before the command itself, 'command' can now stop the shell from
   exiting (as required by the standard) if a command that it
   invokes (such as 'export') tries to modify a readonly variable.
   This fixes BUG_CMDSPEXIT.

Most of 'command' is integrated in the parser and parse tree
executer, so that is where it needed fixing.

src/cmd/ksh93/sh/parse.c: simple():
- If the posix option is on, do not skip past SYSCOMMAND so that
  any declaration builtin commands that are arguments to 'command'
  are not detected and thus not treated specially at parsetime.

src/cmd/ksh93/sh/xec.c: sh_exec():
- When detecting SYSCOMMAND in order to skip past it, not only
  compare the Namval_t pointer 'np' to SYSCOMMAND, but also handle
  the case where that pointer is NULL, as when the command name
  results from an expansion. In that case, search the function tree
  shp->fun_tree for the name and see if that yields the SYSCOMMAND
  pointer. fun_tree is initialised with a dtview to bltin_tree, so
  searching fun_tree instead allows for overriding 'command' with a
  shell function (which the POSIX standard requires us to allow).

src/cmd/ksh93/sh.1,
src/cmd/ksh93/data/builtins.c:
- Update documentation to match these changes.
- Various related edits and improvements.

src/cmd/ksh93/tests/builtins.sh:
- Check that 'command' works if resulting from an expansion.
- Check that 'command' can be overridden by a shell function.
2020-09-11 10:06:43 +02:00

2078 lines
50 KiB
C

/***********************************************************************
* *
* 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);
}
else if((t=item(lexp,SH_NL|SH_EMPTY|(flag&SH_SEMI))) && lexp->token=='|')
{
register Shnode_t *tt;
int showme = t->tre.tretyp&FSHOWME;
t = makeparent(lexp,TFORK|FPOU,t);
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
* Otherwise 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 *volatile savstak=0;
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->functargs = 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;
type |= NV_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;
lexp->assignok = 0;
}
else
lexp->aliasok = 2;
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 || np==SYSSOURCE)))
{
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|type|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 || np==SYSSOURCE)))
{
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==';')
while((tok=sh_lex(lexp))==NL);
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(functnod);
t->funct.functtyp=TNSPACE;
t->funct.functargs = 0;
t->funct.functloc = 0;
if(sh_lex(lexp))
sh_syntax(lexp);
t->funct.functnam=(char*) lexp->arg->argval;
while((tok=sh_lex(lexp))==NL);
if(tok!=LBRACE)
sh_syntax(lexp);
t->funct.functtre = 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;
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);
done:
/* redirection(s) following a compound command or arithmetic expression */
if(io=inout(lexp,io,0))
{
t=makeparent(lexp,TSETIO,t);
t->tre.treio=io;
}
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(np && is_abuiltin(np))
{
if(cmdarg==0)
t->comnamp = (void*)np;
if(nv_isattr(np,BLT_DCL))
{
assignment = 1;
if(np >= SYSTYPESET && np <= SYSTYPESET_END)
lexp->intypeset = 1;
key_on = 1;
}
else if(np==SYSCOMMAND && !sh_isoption(SH_POSIX))
cmdarg++;
else if(np==SYSEXEC)
lexp->inexec = 1;
else if(np->nvalue.bfp==(Nambfp_f)b_getopts)
opt_get |= FOPTGET;
}
}
if((flag&NV_COMVAR) && !assignment)
sh_syntax(lexp);
*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 == SYSCOMPOUND)
type = NV_COMVAR;
else if((Namval_t*)t->comnamp >= SYSTYPESET && (Namval_t*)t->comnamp <= SYSTYPESET_END)
{
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 if(strchr(ap->argval,'C'))
type = NV_COMVAR;
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*));
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|NV_APPEND)))
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;
/*
* The 'special' variable flags a parser hack for ancient 'test -t' compatibility.
* As this is done at parse time, it only affects literal '-t', not 'foo=-t; test "$foo"'.
* It only works for a simple '-t'; a compound expression ending in '-t' is hacked in bltins/test.c.
*/
if(!sh_isoption(SH_POSIX))
{
if((Namval_t*)ac->comnamp==SYSTEST)
special = 2; /* convert "test -t" to "test -t 1" */
else if(*(ac->comarg->argval)=='[' && ac->comarg->argval[1]==0)
special = 3; /* convert "[ -t ]" to "[ -t 1 ]" */
}
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);
}
sfputc(stkp,'\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 */