mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-24 15:04:13 +00:00
2092 lines
50 KiB
C
2092 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);
|
|
}
|
|
#if SHOPT_COSHELL
|
|
else if((t=item(lexp,SH_NL|SH_EMPTY|(flag&SH_SEMI))) && (lexp->token=='|' || lexp->token==PIPESYM2))
|
|
#else
|
|
else if((t=item(lexp,SH_NL|SH_EMPTY|(flag&SH_SEMI))) && lexp->token=='|')
|
|
#endif /* SHOPT_COSHELL */
|
|
{
|
|
register Shnode_t *tt;
|
|
int showme = t->tre.tretyp&FSHOWME;
|
|
t = makeparent(lexp,TFORK|FPOU,t);
|
|
#if SHOPT_COSHELL
|
|
if(lexp->token==PIPESYM2)
|
|
t->tre.tretyp |= FALTPIPE;
|
|
#endif /* SHOPT_COSHELL */
|
|
if(tt=term(lexp,SH_NL))
|
|
{
|
|
switch(tt->tre.tretyp&COMMSK)
|
|
{
|
|
case TFORK:
|
|
tt->tre.tretyp |= FPIN|FPCL;
|
|
break;
|
|
case TFIL:
|
|
tt->lst.lstlef->tre.tretyp |= FPIN|FPCL;
|
|
break;
|
|
default:
|
|
tt= makeparent(lexp,TSETIO|FPIN|FPCL,tt);
|
|
}
|
|
t=makelist(lexp,TFIL,t,tt);
|
|
t->tre.tretyp |= showme;
|
|
}
|
|
else if(lexp->token)
|
|
sh_syntax(lexp);
|
|
}
|
|
return(t);
|
|
}
|
|
|
|
/*
|
|
* case statement
|
|
*/
|
|
static struct regnod* syncase(Lex_t *lexp,register int esym)
|
|
{
|
|
register int tok = skipnl(lexp,0);
|
|
register struct regnod *r;
|
|
if(tok==esym)
|
|
return(NIL(struct regnod*));
|
|
r = (struct regnod*)stakalloc(sizeof(struct regnod));
|
|
r->regptr=0;
|
|
r->regflag=0;
|
|
if(tok==LPAREN)
|
|
skipnl(lexp,0);
|
|
while(1)
|
|
{
|
|
if(!lexp->arg)
|
|
sh_syntax(lexp);
|
|
lexp->arg->argnxt.ap=r->regptr;
|
|
r->regptr = lexp->arg;
|
|
if((tok=sh_lex(lexp))==RPAREN)
|
|
break;
|
|
else if(tok=='|')
|
|
sh_lex(lexp);
|
|
else
|
|
sh_syntax(lexp);
|
|
}
|
|
r->regcom=sh_cmd(lexp,0,SH_NL|SH_EMPTY|SH_SEMI);
|
|
if((tok=lexp->token)==BREAKCASESYM)
|
|
r->regnxt=syncase(lexp,esym);
|
|
else if(tok==FALLTHRUSYM)
|
|
{
|
|
r->regflag++;
|
|
r->regnxt=syncase(lexp,esym);
|
|
}
|
|
else
|
|
{
|
|
if(tok!=esym && tok!=EOFSYM)
|
|
sh_syntax(lexp);
|
|
r->regnxt=0;
|
|
}
|
|
if(lexp->token==EOFSYM)
|
|
return(NIL(struct regnod*));
|
|
return(r);
|
|
}
|
|
|
|
/*
|
|
* This routine creates the parse tree for the arithmetic for
|
|
* When called, shlex.arg contains the string inside ((...))
|
|
* When the first argument is missing, a while node is returned
|
|
* 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 *savstak;
|
|
Sfoff_t first, last;
|
|
struct functnod *volatile fp;
|
|
Sfio_t *iop;
|
|
#if SHOPT_KIA
|
|
unsigned long current = lexp->current;
|
|
#endif /* SHOPT_KIA */
|
|
int nargs=0,size=0,jmpval, saveloop=loop_level;
|
|
struct argnod *savelabel = label_last;
|
|
struct checkpt buff;
|
|
int save_optget = opt_get;
|
|
void *in_mktype = shp->mktype;
|
|
shp->mktype = 0;
|
|
opt_get = 0;
|
|
t = getnode(functnod);
|
|
t->funct.functline = shp->inlineno;
|
|
t->funct.functtyp=TFUN;
|
|
t->funct.functargs = 0;
|
|
if(!(flag = (lexp->token==FUNCTSYM)))
|
|
t->funct.functtyp |= FPOSIX;
|
|
else if(sh_lex(lexp))
|
|
sh_syntax(lexp);
|
|
if(!(iop=fcfile()))
|
|
{
|
|
iop = sfopen(NIL(Sfio_t*),fcseek(0),"s");
|
|
fcclose();
|
|
fcfopen(iop);
|
|
}
|
|
t->funct.functloc = first = fctell();
|
|
if(!shp->st.filename || sffileno(iop)<0)
|
|
{
|
|
if(fcfill() >= 0)
|
|
fcseek(-1);
|
|
if(sh_isstate(SH_HISTORY) && shp->gd->hist_ptr)
|
|
t->funct.functloc = sfseek(shp->gd->hist_ptr->histfp,(off_t)0,SEEK_CUR);
|
|
else
|
|
{
|
|
/* copy source to temporary file */
|
|
t->funct.functloc = 0;
|
|
if(lexp->sh->heredocs)
|
|
t->funct.functloc = sfseek(lexp->sh->heredocs,(Sfoff_t)0, SEEK_END);
|
|
else
|
|
lexp->sh->heredocs = sftmp(HERE_MEM);
|
|
lexp->sh->funlog = lexp->sh->heredocs;
|
|
t->funct.functtyp |= FPIN;
|
|
}
|
|
}
|
|
t->funct.functnam= (char*)lexp->arg->argval;
|
|
#if SHOPT_KIA
|
|
if(lexp->kiafile)
|
|
lexp->current = kiaentity(lexp,t->funct.functnam,-1,'p',-1,-1,lexp->script,'p',0,"");
|
|
#endif /* SHOPT_KIA */
|
|
if(flag)
|
|
{
|
|
lexp->token = sh_lex(lexp);
|
|
#if SHOPT_BASH
|
|
if(lexp->token == LPAREN)
|
|
{
|
|
if((lexp->token = sh_lex(lexp)) == RPAREN)
|
|
t->funct.functtyp |= FPOSIX;
|
|
else
|
|
sh_syntax(lexp);
|
|
}
|
|
#endif
|
|
}
|
|
if(t->funct.functtyp&FPOSIX)
|
|
skipnl(lexp,0);
|
|
else
|
|
{
|
|
if(lexp->token==0)
|
|
{
|
|
struct comnod *ac;
|
|
char *cp, **argv, **argv0;
|
|
int c;
|
|
t->funct.functargs = ac = (struct comnod*)simple(lexp,SH_NOIO|SH_FUNDEF,NIL(struct ionod*));
|
|
if(ac->comset || (ac->comtyp&COMSCAN))
|
|
errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax4,lexp->sh->inlineno);
|
|
argv0 = argv = ((struct dolnod*)ac->comarg)->dolval+ARG_SPARE;
|
|
while(cp= *argv++)
|
|
{
|
|
size += strlen(cp)+1;
|
|
if((c = mbchar(cp)) && isaletter(c))
|
|
while(c=mbchar(cp), isaname(c));
|
|
}
|
|
if(c)
|
|
errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax4,lexp->sh->inlineno);
|
|
nargs = argv-argv0;
|
|
size += sizeof(struct dolnod)+(nargs+ARG_SPARE)*sizeof(char*);
|
|
if(shp->shcomp && memcmp(".sh.math.",t->funct.functnam,9)==0)
|
|
{
|
|
Namval_t *np= nv_open(t->funct.functnam,shp->fun_tree,NV_ADD|NV_VARNAME);
|
|
np->nvalue.rp = new_of(struct Ufunction,shp->funload?sizeof(Dtlink_t):0);
|
|
memset((void*)np->nvalue.rp,0,sizeof(struct Ufunction));
|
|
np->nvalue.rp->argc = ((struct dolnod*)ac->comarg)->dolnum;
|
|
}
|
|
}
|
|
while(lexp->token==NL)
|
|
lexp->token = sh_lex(lexp);
|
|
}
|
|
if((flag && lexp->token!=LBRACE) || lexp->token==EOFSYM)
|
|
sh_syntax(lexp);
|
|
sh_pushcontext(shp,&buff,1);
|
|
jmpval = sigsetjmp(buff.buff,0);
|
|
if(jmpval == 0)
|
|
{
|
|
/* create a new stak frame to compile the command */
|
|
savstak = stakcreate(STAK_SMALL);
|
|
savstak = stakinstall(savstak, 0);
|
|
slp = (struct slnod*)stakalloc(sizeof(struct slnod)+sizeof(struct functnod));
|
|
slp->slchild = 0;
|
|
slp->slnext = shp->st.staklist;
|
|
shp->st.staklist = 0;
|
|
t->funct.functstak = (struct slnod*)slp;
|
|
/*
|
|
* store the pathname of function definition file on stack
|
|
* in name field of fake for node
|
|
*/
|
|
fp = (struct functnod*)(slp+1);
|
|
fp->functtyp = TFUN|FAMP;
|
|
fp->functnam = 0;
|
|
fp->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)))
|
|
{
|
|
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)))
|
|
{
|
|
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;
|
|
|
|
#if SHOPT_COSHELL
|
|
case '&':
|
|
if(tok=sh_lex(lexp))
|
|
{
|
|
if(tok!=NL)
|
|
sh_syntax(lexp);
|
|
t = getnode(comnod);
|
|
memset(t,0,sizeof(struct comnod));
|
|
t->com.comline = sh_getlineno(lexp);
|
|
}
|
|
else
|
|
t = (Shnode_t*)simple(lexp,SH_NOIO,NIL(struct ionod*));
|
|
t->com.comtyp |= FAMP;
|
|
if(lexp->token=='&' || lexp->token=='|')
|
|
sh_syntax(lexp);
|
|
return(t);
|
|
break;
|
|
#endif /* SHOPT_COSHELL */
|
|
default:
|
|
if(io==0)
|
|
return(0);
|
|
|
|
case ';':
|
|
if(io==0)
|
|
{
|
|
if(!(flag&SH_SEMI))
|
|
return(0);
|
|
if(sh_lex(lexp)==';')
|
|
sh_syntax(lexp);
|
|
showme = FSHOWME;
|
|
}
|
|
/* simple command */
|
|
case 0:
|
|
t = (Shnode_t*)simple(lexp,flag,io);
|
|
if(t->com.comarg && lexp->intypeset)
|
|
check_typedef(&t->com);
|
|
lexp->intypeset = 0;
|
|
lexp->inexec = 0;
|
|
t->tre.tretyp |= showme;
|
|
return(t);
|
|
}
|
|
sh_lex(lexp);
|
|
if(io=inout(lexp,io,0))
|
|
{
|
|
if((tok=t->tre.tretyp&COMMSK) != TFORK)
|
|
tok = TSETIO;
|
|
t=makeparent(lexp,tok,t);
|
|
t->tre.treio=io;
|
|
}
|
|
done:
|
|
lexp->lasttok = savwdval;
|
|
lexp->lastline = savline;
|
|
return(t);
|
|
}
|
|
|
|
static struct argnod *process_sub(Lex_t *lexp,int tok)
|
|
{
|
|
struct argnod *argp;
|
|
Shnode_t *t;
|
|
int mode = (tok==OPROCSYM);
|
|
t = sh_cmd(lexp,RPAREN,SH_NL);
|
|
argp = (struct argnod*)stkalloc(lexp->sh->stk,sizeof(struct argnod));
|
|
*argp->argval = 0;
|
|
argp->argchn.ap = (struct argnod*)makeparent(lexp,mode?TFORK|FPIN|FAMP|FPCL:TFORK|FPOU,t);
|
|
argp->argflag = (ARG_EXP|mode);
|
|
return(argp);
|
|
}
|
|
|
|
|
|
/*
|
|
* This is for a simple command, for list, or compound assignment
|
|
*/
|
|
static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io)
|
|
{
|
|
register struct comnod *t;
|
|
register struct argnod *argp;
|
|
register int tok;
|
|
Stk_t *stkp = lexp->sh->stk;
|
|
struct argnod **argtail;
|
|
struct argnod **settail;
|
|
int cmdarg=0;
|
|
int argno = 0;
|
|
int assignment = 0;
|
|
int key_on = (!(flag&SH_NOIO) && sh_isoption(SH_KEYWORD));
|
|
int associative=0;
|
|
if((argp=lexp->arg) && (argp->argflag&ARG_ASSIGN) && argp->argval[0]=='[')
|
|
{
|
|
flag |= SH_ARRAY;
|
|
associative = 1;
|
|
}
|
|
t = (struct comnod*)getnode(comnod);
|
|
t->comio=io; /*initial io chain*/
|
|
/* set command line number for error messages */
|
|
t->comline = sh_getlineno(lexp);
|
|
argtail = &(t->comarg);
|
|
t->comset = 0;
|
|
t->comnamp = 0;
|
|
t->comnamq = 0;
|
|
t->comstate = 0;
|
|
settail = &(t->comset);
|
|
while(lexp->token==0)
|
|
{
|
|
argp = lexp->arg;
|
|
if(*argp->argval==LBRACE && (flag&SH_FUNDEF) && argp->argval[1]==0)
|
|
{
|
|
lexp->token = LBRACE;
|
|
break;
|
|
}
|
|
if(associative && argp->argval[0]!='[')
|
|
sh_syntax(lexp);
|
|
/* check for assignment argument */
|
|
if((argp->argflag&ARG_ASSIGN) && assignment!=2)
|
|
{
|
|
*settail = argp;
|
|
settail = &(argp->argnxt.ap);
|
|
lexp->assignok = (flag&SH_ASSIGN)?SH_ASSIGN:1;
|
|
if(assignment)
|
|
{
|
|
struct argnod *ap=argp;
|
|
char *last, *cp;
|
|
if(assignment==1)
|
|
{
|
|
last = strchr(argp->argval,'=');
|
|
if(last && (last[-1]==']'|| (last[-1]=='+' && last[-2]==']')) && (cp=strchr(argp->argval,'[')) && (cp < last) && cp[-1]!='.')
|
|
last = cp;
|
|
stkseek(stkp,ARGVAL);
|
|
sfwrite(stkp,argp->argval,last-argp->argval);
|
|
ap=(struct argnod*)stkfreeze(stkp,1);
|
|
ap->argflag = ARG_RAW;
|
|
ap->argchn.ap = 0;
|
|
}
|
|
*argtail = ap;
|
|
argtail = &(ap->argnxt.ap);
|
|
if(argno>=0)
|
|
argno++;
|
|
}
|
|
else /* alias substitutions allowed */
|
|
lexp->aliasok = 1;
|
|
}
|
|
else
|
|
{
|
|
if(!(argp->argflag&ARG_RAW))
|
|
argno = -1;
|
|
if(argno>=0 && argno++==cmdarg && !(flag&SH_ARRAY) && *argp->argval!='/')
|
|
{
|
|
/* check for builtin command */
|
|
Namval_t *np=nv_bfsearch(argp->argval,lexp->sh->fun_tree, (Namval_t**)&t->comnamq,(char**)0);
|
|
if(cmdarg==0)
|
|
t->comnamp = (void*)np;
|
|
if(np && is_abuiltin(np))
|
|
{
|
|
if(nv_isattr(np,BLT_DCL))
|
|
{
|
|
assignment = 1+(*argp->argval=='a');
|
|
if(np==SYSTYPESET)
|
|
lexp->intypeset = 1;
|
|
key_on = 1;
|
|
}
|
|
else if(np==SYSCOMMAND)
|
|
cmdarg++;
|
|
else if(np==SYSEXEC)
|
|
lexp->inexec = 1;
|
|
else if(np->nvalue.bfp==(Nambfp_f)b_getopts)
|
|
opt_get |= FOPTGET;
|
|
}
|
|
}
|
|
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==SYSTYPESET)
|
|
{
|
|
struct argnod *ap;
|
|
for(ap=t->comarg->argnxt.ap;ap;ap=ap->argnxt.ap)
|
|
{
|
|
if(*ap->argval!='-')
|
|
break;
|
|
if(strchr(ap->argval,'T'))
|
|
type = NV_TYPE;
|
|
else if(strchr(ap->argval,'a'))
|
|
type = NV_ARRAY;
|
|
else 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*));
|
|
#if 0
|
|
else if(argp && strcmp(argp->argval,"unset")==0)
|
|
writedefs(lexp,argp,line,'u',NIL(struct argnod*));
|
|
#endif
|
|
else if(argp && *argp->argval=='.' && argp->argval[1]==0 && (argp=argp->argnxt.ap))
|
|
{
|
|
r = kiaentity(lexp,sh_argstr(argp),-1,'p',0,0,lexp->script,'d',0,"");
|
|
sfprintf(lexp->kiatmp,"p;%..64d;p;%..64d;%d;%d;d;\n",lexp->current,r,line,line);
|
|
}
|
|
}
|
|
#endif /* SHOPT_KIA */
|
|
if(t->comnamp && (argp=t->comarg->argnxt.ap))
|
|
{
|
|
Namval_t *np=(Namval_t*)t->comnamp;
|
|
if((np==SYSBREAK || np==SYSCONT) && (argp->argflag&ARG_RAW) && !isdigit(*argp->argval))
|
|
{
|
|
register char *cp = argp->argval;
|
|
/* convert break/continue labels to numbers */
|
|
tok = 0;
|
|
for(argp=label_list;argp!=label_last;argp=argp->argnxt.ap)
|
|
{
|
|
if(strcmp(cp,argp->argval))
|
|
continue;
|
|
tok = loop_level-argp->argflag;
|
|
if(tok>=1)
|
|
{
|
|
argp = t->comarg->argnxt.ap;
|
|
if(tok>9)
|
|
{
|
|
argp->argval[1] = '0'+tok%10;
|
|
argp->argval[2] = 0;
|
|
tok /= 10;
|
|
}
|
|
else
|
|
argp->argval[1] = 0;
|
|
*argp->argval = '0'+tok;
|
|
}
|
|
break;
|
|
}
|
|
if(sh_isoption(SH_NOEXEC) && tok==0)
|
|
errormsg(SH_DICT,ERROR_warn(0),e_lexlabunknown,lexp->sh->inlineno-(lexp->token=='\n'),cp);
|
|
}
|
|
else if(sh_isoption(SH_NOEXEC) && np==SYSSET && ((tok= *argp->argval)=='-'||tok=='+') &&
|
|
(argp->argval[1]==0||strchr(argp->argval,'k')))
|
|
errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete5,lexp->sh->inlineno-(lexp->token=='\n'),argp->argval);
|
|
}
|
|
/* expand argument list if possible */
|
|
if(argno>0 && !(flag&(SH_ARRAY|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;
|
|
/* special hack for test -t compatibility */
|
|
if((Namval_t*)ac->comnamp==SYSTEST)
|
|
special = 2;
|
|
else if(*(ac->comarg->argval)=='[' && ac->comarg->argval[1]==0)
|
|
special = 3;
|
|
if(special)
|
|
{
|
|
ap = ac->comarg->argnxt.ap;
|
|
if(argn==(special+1) && ap->argval[1]==0 && *ap->argval=='!')
|
|
ap = ap->argnxt.ap;
|
|
else if(argn!=special)
|
|
special=0;
|
|
}
|
|
if(special)
|
|
{
|
|
const char *message;
|
|
if(strcmp(ap->argval,"-t"))
|
|
{
|
|
message = "line %d: Invariant test";
|
|
special=0;
|
|
}
|
|
else
|
|
{
|
|
message = "line %d: -t requires argument";
|
|
argn++;
|
|
}
|
|
if(sh_isoption(SH_NOEXEC))
|
|
errormsg(SH_DICT,ERROR_warn(0),message,ac->comline);
|
|
}
|
|
/* leave space for an extra argument at the front */
|
|
dp = (struct dolnod*)stakalloc((unsigned)sizeof(struct dolnod) + ARG_SPARE*sizeof(char*) + argn*sizeof(char*));
|
|
cp = dp->dolval+ARG_SPARE;
|
|
dp->dolnum = argn;
|
|
dp->dolbot = ARG_SPARE;
|
|
ap = ac->comarg;
|
|
while(ap)
|
|
{
|
|
*cp++ = ap->argval;
|
|
ap = ap->argnxt.ap;
|
|
}
|
|
if(special==3)
|
|
{
|
|
cp[0] = cp[-1];
|
|
cp[-1] = "1";
|
|
cp++;
|
|
}
|
|
else if(special)
|
|
*cp++ = "1";
|
|
*cp = 0;
|
|
return((struct argnod*)dp);
|
|
}
|
|
|
|
static Shnode_t *test_expr(Lex_t *lp,int sym)
|
|
{
|
|
register Shnode_t *t = test_or(lp);
|
|
if(lp->token!=sym)
|
|
sh_syntax(lp);
|
|
return(t);
|
|
}
|
|
|
|
static Shnode_t *test_or(Lex_t *lp)
|
|
{
|
|
register Shnode_t *t = test_and(lp);
|
|
while(lp->token==ORFSYM)
|
|
t = makelist(lp,TORF|TTEST,t,test_and(lp));
|
|
return(t);
|
|
}
|
|
|
|
static Shnode_t *test_and(Lex_t *lp)
|
|
{
|
|
register Shnode_t *t = test_primary(lp);
|
|
while(lp->token==ANDFSYM)
|
|
t = makelist(lp,TAND|TTEST,t,test_primary(lp));
|
|
return(t);
|
|
}
|
|
|
|
/*
|
|
* convert =~ into == ~(E)
|
|
*/
|
|
static void ere_match(void)
|
|
{
|
|
Sfio_t *base, *iop = sfopen((Sfio_t*)0," ~(E)","s");
|
|
register int c;
|
|
while( fcgetc(c),(c==' ' || c=='\t'));
|
|
if(c)
|
|
fcseek(-1);
|
|
if(!(base=fcfile()))
|
|
base = sfopen(NIL(Sfio_t*),fcseek(0),"s");
|
|
fcclose();
|
|
sfstack(base,iop);
|
|
fcfopen(base);
|
|
}
|
|
|
|
static Shnode_t *test_primary(Lex_t *lexp)
|
|
{
|
|
register struct argnod *arg;
|
|
register Shnode_t *t;
|
|
register int num,token;
|
|
token = skipnl(lexp,0);
|
|
num = lexp->digits;
|
|
switch(token)
|
|
{
|
|
case '(':
|
|
t = test_expr(lexp,')');
|
|
t = makelist(lexp,TTST|TTEST|TPAREN ,t, (Shnode_t*)pointerof(lexp->sh->inlineno));
|
|
break;
|
|
case '!':
|
|
if(!(t = test_primary(lexp)))
|
|
sh_syntax(lexp);
|
|
t->tre.tretyp |= TNEGATE;
|
|
return(t);
|
|
case TESTUNOP:
|
|
if(sh_lex(lexp))
|
|
sh_syntax(lexp);
|
|
#if SHOPT_KIA
|
|
if(lexp->kiafile && !strchr("sntzoOG",num))
|
|
{
|
|
int line = lexp->sh->inlineno- (lexp->token==NL);
|
|
unsigned long r;
|
|
r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->script,'t',0,"");
|
|
sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line);
|
|
}
|
|
#endif /* SHOPT_KIA */
|
|
t = makelist(lexp,TTST|TTEST|TUNARY|(num<<TSHIFT),
|
|
(Shnode_t*)lexp->arg,(Shnode_t*)lexp->arg);
|
|
t->tst.tstline = lexp->sh->inlineno;
|
|
break;
|
|
/* binary test operators */
|
|
case 0:
|
|
arg = lexp->arg;
|
|
if((token=sh_lex(lexp))==TESTBINOP)
|
|
{
|
|
num = lexp->digits;
|
|
if(num==TEST_REP)
|
|
{
|
|
ere_match();
|
|
num = TEST_PEQ;
|
|
}
|
|
}
|
|
else if(token=='<')
|
|
num = TEST_SLT;
|
|
else if(token=='>')
|
|
num = TEST_SGT;
|
|
else if(token==ANDFSYM||token==ORFSYM||token==ETESTSYM||token==RPAREN)
|
|
{
|
|
t = makelist(lexp,TTST|TTEST|TUNARY|('n'<<TSHIFT),
|
|
(Shnode_t*)arg,(Shnode_t*)arg);
|
|
t->tst.tstline = lexp->sh->inlineno;
|
|
return(t);
|
|
}
|
|
else
|
|
sh_syntax(lexp);
|
|
#if SHOPT_KIA
|
|
if(lexp->kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT))
|
|
{
|
|
int line = lexp->sh->inlineno- (lexp->token==NL);
|
|
unsigned long r;
|
|
r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->current,'t',0,"");
|
|
sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line);
|
|
}
|
|
#endif /* SHOPT_KIA */
|
|
if(sh_lex(lexp))
|
|
sh_syntax(lexp);
|
|
if(num&TEST_PATTERN)
|
|
{
|
|
if(lexp->arg->argflag&(ARG_EXP|ARG_MAC))
|
|
num &= ~TEST_PATTERN;
|
|
}
|
|
t = getnode(tstnod);
|
|
t->lst.lsttyp = TTST|TTEST|TBINARY|(num<<TSHIFT);
|
|
t->lst.lstlef = (Shnode_t*)arg;
|
|
t->lst.lstrit = (Shnode_t*)lexp->arg;
|
|
t->tst.tstline = lexp->sh->inlineno;
|
|
#if SHOPT_KIA
|
|
if(lexp->kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT))
|
|
{
|
|
int line = lexp->sh->inlineno-(lexp->token==NL);
|
|
unsigned long r;
|
|
r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->current,'t',0,"");
|
|
sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line);
|
|
}
|
|
#endif /* SHOPT_KIA */
|
|
break;
|
|
default:
|
|
return(0);
|
|
}
|
|
skipnl(lexp,0);
|
|
return(t);
|
|
}
|
|
|
|
#if SHOPT_KIA
|
|
/*
|
|
* return an entity checksum
|
|
* The entity is created if it doesn't exist
|
|
*/
|
|
unsigned long kiaentity(Lex_t *lexp,const char *name,int len,int type,int first,int last,unsigned long parent, int pkind, int width, const char *attr)
|
|
{
|
|
Stk_t *stkp = lexp->sh->stk;
|
|
Namval_t *np;
|
|
long offset = stktell(stkp);
|
|
sfputc(stkp,type);
|
|
if(len>0)
|
|
sfwrite(stkp,name,len);
|
|
else
|
|
{
|
|
if(type=='p')
|
|
sfputr(stkp,path_basename(name),0);
|
|
else
|
|
sfputr(stkp,name,0);
|
|
}
|
|
np = nv_search(stakptr(offset),lexp->entity_tree,NV_ADD);
|
|
stkseek(stkp,offset);
|
|
np->nvalue.i = pkind;
|
|
nv_setsize(np,width);
|
|
if(!nv_isattr(np,NV_TAGGED) && first>=0)
|
|
{
|
|
nv_onattr(np,NV_TAGGED);
|
|
if(!pkind)
|
|
pkind = '0';
|
|
if(len>0)
|
|
sfprintf(lexp->kiafile,"%..64d;%c;%.*s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,len,name,first,last,parent,lexp->fscript,pkind,width,attr);
|
|
else
|
|
sfprintf(lexp->kiafile,"%..64d;%c;%s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,name,first,last,parent,lexp->fscript,pkind,width,attr);
|
|
}
|
|
return(np->hash);
|
|
}
|
|
|
|
static void kia_add(register Namval_t *np, void *data)
|
|
{
|
|
char *name = nv_name(np);
|
|
Lex_t *lp = (Lex_t*)data;
|
|
NOT_USED(data);
|
|
kiaentity(lp,name+1,-1,*name,0,-1,(*name=='p'?lp->unknown:lp->script),np->nvalue.i,nv_size(np),"");
|
|
}
|
|
|
|
int kiaclose(Lex_t *lexp)
|
|
{
|
|
register off_t off1,off2;
|
|
register int n;
|
|
if(lexp->kiafile)
|
|
{
|
|
unsigned long r = kiaentity(lexp,lexp->scriptname,-1,'p',-1,lexp->sh->inlineno-1,0,'s',0,"");
|
|
kiaentity(lexp,lexp->scriptname,-1,'p',1,lexp->sh->inlineno-1,r,'s',0,"");
|
|
kiaentity(lexp,lexp->scriptname,-1,'f',1,lexp->sh->inlineno-1,r,'s',0,"");
|
|
nv_scan(lexp->entity_tree,kia_add,(void*)lexp,NV_TAGGED,0);
|
|
off1 = sfseek(lexp->kiafile,(off_t)0,SEEK_END);
|
|
sfseek(lexp->kiatmp,(off_t)0,SEEK_SET);
|
|
sfmove(lexp->kiatmp,lexp->kiafile,SF_UNBOUND,-1);
|
|
off2 = sfseek(lexp->kiafile,(off_t)0,SEEK_END);
|
|
#ifdef SF_BUFCONST
|
|
if(off2==off1)
|
|
n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%lld;%d\nDIRECTORY;",(Sflong_t)lexp->kiabegin,(size_t)(off1-lexp->kiabegin));
|
|
else
|
|
n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%lld;%d\nRELATIONSHIP;%lld;%d\nDIRECTORY;",(Sflong_t)lexp->kiabegin,(size_t)(off1-lexp->kiabegin),(Sflong_t)off1,(size_t)(off2-off1));
|
|
if(off2 >= INT_MAX)
|
|
off2 = -(n+12);
|
|
sfprintf(lexp->kiafile,"%010.10lld;%010d\n",(Sflong_t)off2+10, n+12);
|
|
#else
|
|
if(off2==off1)
|
|
n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%d;%d\nDIRECTORY;",lexp->kiabegin,off1-lexp->kiabegin);
|
|
else
|
|
n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%d;%d\nRELATIONSHIP;%d;%d\nDIRECTORY;",lexp->kiabegin,off1-lexp->kiabegin,off1,off2-off1);
|
|
sfprintf(lexp->kiafile,"%010d;%010d\n",off2+10, n+12);
|
|
#endif
|
|
}
|
|
return(sfclose(lexp->kiafile));
|
|
}
|
|
#endif /* SHOPT_KIA */
|