mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
The >;word and <>;word redirection operators cannot be used with the 'exec' builtin, but the 'redirect' builtin (which used to be an alias of 'command exec') permitted them. However, they do not have the documented effect of the added ';'. So this commit blocks those operators for 'redirect' as they are blocked for 'exec'. It also tweaks redirect's error message if a non-redirection argument is encountered. src/cmd/ksh93/sh/parse.c: simple(): - Set the lexp->inexec flag for SYSREDIR (redirect) as well as SYSEXEC (exec). This flag is checked for in sh_lex() (lex.c) to throw a syntax error if one of these two operators is used. src/cmd/ksh93/sh.1, src/cmd/ksh93/data/builtins.c: - Documentation tweaks. src/cmd/ksh93/sh/xec.c, src/cmd/ksh93/bltins/misc.c: - When 'redirect' gives an 'incorrect syntax' (e_badsyntax) error message, include the first word that was found not to be a valid redirection. This is simply the first argument, as redirections are removed from the arguments list. src/cmd/ksh93/tests/io.sh: - Update test to reflect new error message format.
2079 lines
50 KiB
C
2079 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 "path.h"
|
|
#include "test.h"
|
|
#include "history.h"
|
|
#include "version.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
|
|
/*
|
|
* 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 = NIL(Shnode_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 > SHCOMP_HDR_VERSION)
|
|
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=-1;
|
|
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 || np==SYSREDIR)
|
|
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 */
|