2021-10-11 19:38:01 -03:00

1128 lines
21 KiB
C
Executable File

#ident "@(#)cmd.c 1.8 95/03/21 SMI" /* From AT&T Toolchest */
/*
* UNIX shell
*
* S. R. Bourne
* Rewritten by David Korn
* AT&T Bell Laboratories
*
*/
#include "defs.h"
#include "sym.h"
#include "history.h"
#include "builtins.h"
/* These routines are referenced by this module */
static union anynode *makelist();
static struct argnod *qscan();
static struct ionod *inout();
static void chkword();
static void chkflags();
static void chksym();
static union anynode *sh_cmd();
static union anynode *term();
static union anynode *list();
#ifdef notdef /* #ifdef POSIX */
static union anynode *andlist();
#endif /* POSIX */
static struct regnod *syncase();
static union anynode *item();
static int skipnl();
static void prsym();
int getlineno();
#ifdef NEWTEST
# include "test.h"
static union anynode *test_expr();
static union anynode *test_and();
static union anynode *test_or();
static union anynode *test_primary();
#endif /* NEWTEST */
static int heredoc;
#define getnode(type) ((union anynode*)stakalloc(sizeof(struct type)))
/*
* ======== command line decoding ========
*
* This is the parser for a shell command line
*/
/*
* Make a node which will cause the shell to fork
*/
union anynode *sh_mkfork(flgs, i)
int flgs;
union anynode *i;
{
register union anynode *t;
t = getnode(forknod);
t->fork.forktyp = flgs|TFORK;
t->fork.forktre = i;
t->fork.forkio = 0;
return(t);
}
/*
* Make a node corresponding to a command list
*/
static union anynode *makelist(type,i,r)
int type;
union anynode *i, *r;
{
register union anynode *t;
if(i==0 || r==0)
sh_syntax();
else
{
t = getnode(lstnod);
t->lst.lsttyp = type;
t->lst.lstlef = i;
t->lst.lstrit = r;
}
return(t);
}
/*
* shell parser
*/
union anynode *sh_parse(sym,flg)
int sym;
int flg;
{
register union anynode *t;
st.staklist = 0;
st.iopend = st.iotemp = 0;
t = sh_cmd(sym,flg);
stakseek(0);
return(t);
}
/*
* remove temporary files and stacks
*/
void sh_freeup()
{
register struct ionod *iop = st.iotemp;
while(iop)
{
unlink(iop->ioname);
if(iop->iolink)
free(iop->iolink);
iop=iop->iolst;
}
st.iotemp = 0;
if(st.staklist)
sh_funstaks(st.staklist,-1);
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(slp,flag)
register struct slnod *slp;
register 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 union anynode *sh_cmd(sym,flg)
register int sym;
int flg;
{
register int flag = FINT|FAMP;
register union anynode *i, *e;
struct ionod *saviotemp = st.iotemp;
/* parser output goes on standard error */
sh.wdset = 0;
if(sym==NL)
sh.owdval = 0;
p_setout(ERRIO);
i = list(flg);
if(sh.wdval==NL)
{
if(flg&NLFLG)
{
sh.wdval=';';
sh_prompt(0);
}
}
else if(i==0 && (flg&MTFLG)==0)
sh_syntax();
switch(sh.wdval)
{
case COOPSYM: /* set up a cooperating process */
flag |= (FPIN|FPOU|FPCL|FCOOP);
case '&':
if(i)
{
if(saviotemp!=st.iotemp || heredoc)
flag |= FTMP;
i = sh_mkfork(flag, i);
}
else
sh_syntax();
case ';':
if(e=sh_cmd(sym,flg|MTFLG))
i=makelist(TLST, i, e);
else if(i==0)
{
sh.wdval = ';';
sh_syntax();
}
break;
case EOFSYM:
if(sym==NL)
break;
default:
if(sym)
chksym(sym);
}
return(i);
}
/*
* Section 3.9.3 in POSIX.2 states that the operators
* && and || shall have equal precedence and shall be
* evaluated from beginning to end
* VSC test suite sh_06.sh, test case tp458().
*/
#ifdef notdef /* #ifdef POSIX */
/* precedence of && higher than || */
/*
* list
* andlist
* list || andlist
*/
static union anynode *list(flg)
{
register union anynode *r = andlist(flg);
while(r && sh.wdval==ORFSYM)
r = makelist(TORF , r, andlist(NLFLG));
return(r);
}
/*
* andlist
* term
* and && term
*/
static union anynode *andlist(flg)
{
register union anynode *r = term(flg);
while(r && sh.wdval==ANDFSYM)
r = makelist(TAND, r, term(NLFLG));
return(r);
}
#else
/*
* list
* term
* list && term
* list || term
*/
static union anynode *list(flg)
{
register union anynode *r;
register int b;
r = term(flg);
while(r && ((b=(sh.wdval==ANDFSYM)) || sh.wdval==ORFSYM))
{
r = makelist((b ? TAND : TORF), r, term(NLFLG));
}
return(r);
}
#endif /* POSIX */
/*
* term
* item
* item | term
*/
static union anynode *term(flg)
register int flg;
{
register union anynode *t;
struct ionod *saviotemp = st.iotemp;
heredoc = 0;
sh.reserv++;
if(flg&NLFLG)
skipnl();
else
sh_lex();
/* check to see if pipeline is to be timed */
#ifdef POSIX
if(sh.wdval==TIMSYM || sh.wdval==NOTSYM)
#else
if(sh.wdval==TIMSYM)
#endif /* POSIX */
{
t = getnode(parnod);
t->par.partyp=TTIME;
#ifdef POSIX
if(sh.wdval==NOTSYM)
t->par.partyp |= COMSCAN;
#endif /* POSIX */
t->par.partre = term(0);
}
else if((t=item(NLFLG|MTFLG)) && sh.wdval=='|')
{
register union anynode *tt;
if(saviotemp!=st.iotemp || heredoc)
flg = (FTMP|FPOU);
else
flg = FPOU;
if(tt=term(NLFLG))
t=makelist(TFIL,sh_mkfork(flg,t),sh_mkfork(FPIN|FPCL,tt));
else
chksym(0);
}
return(t);
}
/*
* case statement
*/
static struct regnod* syncase(esym)
register int esym;
{
sh.wdset |= IN_CASE; /* set to avoid aliasing expressions */
skipnl();
if(sh.wdval==esym)
{
sh.wdset &= ~IN_CASE;
return(0);
}
else
{
register struct regnod *r=(struct regnod*) stakalloc(sizeof(struct regnod));
r->regptr=0;
r->regflag=0;
if(sh.wdval == '(')
skipnl();
while(1)
{
chkflags(A_EXP);
sh.wdarg->argnxt.ap=r->regptr;
r->regptr=sh.wdarg;
if(sh.wdval==')' || sh.wdval=='|' || ( sh_lex()!=')' && sh.wdval!='|' ))
sh_syntax();
if(sh.wdval=='|')
sh_lex();
else
break;
}
sh.wdset &= ~IN_CASE;
r->regcom=sh_cmd(0,NLFLG|MTFLG);
if(sh.wdval==ECSYM)
r->regnxt=syncase(esym);
else if(sh.wdval==ECASYM)
{
r->regflag++;
r->regnxt=syncase(esym);
}
else
{
chksym(esym);
r->regnxt=0;
}
return(r);
}
}
/*
* item
*
* ( cmd ) [ < in ] [ > out ]
* word word* [ < in ] [ > out ]
* if ... then ... else ... fi
* for ... while ... do ... done
* case ... in ... esac
* begin ... end
*/
static union anynode *item(flag)
int flag;
{
register union anynode *t;
register struct ionod *io;
int savwdval = sh.owdval;
int savline = sh.olineno;
if(flag)
io=inout((struct ionod*)0,1);
else
io=0;
if(sh.wdval && sh.wdval!=EOFSYM && sh.wdval!=PROCSYM)
{
sh.olineno = getlineno(0);
sh.owdval = sh.wdval;
}
switch(sh.wdval)
{
#ifdef NEWTEST
/* [[ ... ]] test expression */
case BTSTSYM:
sh.wdset |= (IN_TEST|TEST_OP1);
t = test_expr(ETSTSYM);
t->tre.tretyp &= ~TTEST;
sh.wdset &= ~(IN_TEST|TEST_OP1);
break;
#endif /* NEWTEST */
/* ((...)) arithmetic expression */
case EXPRSYM:
t = getnode(arithnod);
t->ar.artyp=TARITH;
t->ar.arline = getlineno(st.firstline);
chkflags(0);
t->ar.arexpr = sh.wdarg;
sh_lex();
goto done;
/* case statement */
case CASYM:
{
t = getnode(swnod);
chkword();
chkflags(0);
t->sw.swarg=sh.wdarg;
skipnl();
chksym(INSYM|BRSYM);
t->sw.swlst=syncase(sh.wdval==INSYM?ESSYM:KTSYM);
t->sw.swtyp=TSW;
break;
}
/* if statement */
case IFSYM:
{
register union anynode *tt;
register int w;
t = getnode(ifnod);
t->if_.iftyp=TIF;
t->if_.iftre=sh_cmd(THSYM,NLFLG);
t->if_.thtre=sh_cmd(ELSYM|FISYM|EFSYM,NLFLG);
w = sh.wdval;
t->if_.eltre=(w==ELSYM?sh_cmd(FISYM,NLFLG):
(w==EFSYM?(sh.wdval=IFSYM, tt=item(0)):0));
if(w==EFSYM)
{
if(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 SELSYM:
{
t = getnode(fornod);
t->for_.fortyp=(sh.wdval==FORSYM?TFOR:TSELECT);
t->for_.forlst=0;
chkword();
t->for_.fornam=(char*) sh.wdarg->argval;
while((sh.reserv++, sh_lex()==NL))
sh_prompt(0);
if(sh.wdval==INSYM)
{
chkword();
t->for_.forlst=(struct comnod*) item(0);
if(sh.wdval!=NL && sh.wdval!=';')
sh_syntax();
if(sh.wdval==NL)
sh_prompt(0);
skipnl();
}
/* 'for i;do cmd' is valid syntax */
else if(sh.wdval==';')
{
sh.reserv = 1;
sh_lex();
}
chksym(DOSYM|BRSYM);
t->for_.fortre=sh_cmd(sh.wdval==DOSYM?ODSYM:KTSYM,NLFLG);
break;
}
/* This is the code for parsing function definitions */
case PROCSYM:
funct_5_2:
{
Stak_t *savstak;
struct slnod *slp;
int savstates = st.states;
int saveline = st.firstline;
register struct fileblk *fd = NULL;
struct ionod *saviotemp = st.iotemp;
t = getnode(procnod);
t->proc.proctyp=TPROC;
t->proc.procloc = -1;
st.firstline = st.standin->flin;
t->proc.procline = st.firstline;
flag = (sh.wdval==PROCSYM);
if(flag)
chkword();
t->proc.procnam= (char*)sh.wdarg->argval;
skipnl();
if(is_option(INTFLG) && !is_option(NOLOG) &&
st.standin->ftype==F_ISFILE)
{
/* just in case history file not open yet */
if(hist_open())
{
fd = io_ftable[hist_ptr->fixfd];
st.states |= FIXFLG;
t->proc.procloc =
hist_position(hist_ptr->fixind) +
fd->ptr - fd->base;
}
}
if(flag)
chksym(BRSYM);
/* create a new stak frame to compile the command */
savstak = stakcreate(STAK_SMALL);
savstak = stakinstall(savstak, 0);
slp = (struct slnod*)stakalloc(sizeof(struct slnod));
slp->slchild = 0;
slp->slnext = st.staklist;
st.staklist = 0;
t->proc.procstak = slp;
t->proc.proctre = item(0);
/* restore the old stack */
slp->slptr = stakinstall(savstak,0);
slp->slchild = st.staklist;
st.staklist = slp;
if(st.iotemp != saviotemp)
{
st.iotemp = saviotemp;
st.states |= RM_TMP;
}
if(fd && !(savstates&FIXFLG))
{
hist_flush();
hist_cancel();
st.states &= ~FIXFLG;
}
st.firstline = saveline;
return(t);
}
/* while and until */
case WHSYM:
case UNSYM:
{
t = getnode(whnod);
t->wh.whtyp=(sh.wdval==WHSYM ? TWH : TUN);
t->wh.whtre = sh_cmd(DOSYM,NLFLG);
t->wh.dotre = sh_cmd(ODSYM,NLFLG);
break;
}
/* command group with {...} */
case BRSYM:
t = sh_cmd(KTSYM,NLFLG);
break;
case '(':
{
register union anynode * p;
p = getnode(parnod);
p->par.partre=sh_cmd(')',NLFLG);
p->par.partyp=TPAR;
t=sh_mkfork(0,p);
break;
}
default:
if(io==0)
return(0);
/* simple command */
case 0:
{
register struct argnod *argp;
struct argnod **argtail;
struct argnod **settail;
int keywd=KEYFLG;
int argno = 0;
int bltin = 0;
int key_on = (flag?is_option(KEYFLG):0);
t = getnode(comnod);
t->com.comio=io; /*initial io chain*/
/* set command line number for error messages */
t->com.comline = getlineno(st.firstline);
argtail = &(t->com.comarg);
t->com.comset = 0;
t->com.comnamp = 0;
settail = &(t->com.comset);
while(sh.wdval==0)
{
argp = sh.wdarg;
argp->argchn = 0;
/* test for keyword argument */
if(sh.wdset&keywd)
{
chkflags(0);
if(bltin&SYSDECLARE)
goto cmdarg;
*settail = argp;
settail = &(argp->argnxt.ap);
/* alias substitutions allowed */
sh.wdset |= (KEYFLG|CAN_ALIAS);
}
else
{
sh.wdset = 0; /* don't hunt for aliases*/
chkflags(A_SPLIT|A_EXP);
cmdarg:
if((argp->argflag&A_RAW) == 0)
argno = -1;
if(argno>=0 && argno++==0)
{
/* check for builtin command */
if((t->com.comnamp=nam_search(argp->argval,sh.fun_tree,0)) &&
nam_istype(t->com.comnamp,BLT_DCL))
{
bltin = SYSDECLARE;
key_on = KEYFLG;
}
}
*argtail = argp;
argtail = &(argp->argnxt.ap);
sh.wdset = keywd= key_on;
}
#ifdef DEVFD
retry:
sh_lex();
if((sh.wdval==IPROC ||sh.wdval==OPROC))
{
union anynode *t;
int flag = (sh.wdval==OPROC);
t = sh_cmd(')',NLFLG);
argp = (struct argnod*)stakalloc(sizeof(struct argnod));
*argp->argval = 0;
argno = -1;
*argtail = argp;
argtail = &(argp->argnxt.ap);
argp->argchn = (struct argnod*)sh_mkfork(flag?FPIN|FAMP|FPCL:FPOU,t);
argp->argflag = (A_EXP|flag);
goto retry;
}
#else
sh_lex();
#endif /* DEVFD */
if(argno==1 && !t->com.comset && sh.wdval== '(')
{
/* SVR2 style function */
sh_lex();
if(sh.wdval == ')')
{
sh.wdarg = argp;
goto funct_5_2;
}
sh.wdval = '(';
}
if(flag)
{
if(io)
{
while(io->ionxt)
io = io->ionxt;
io->ionxt = inout((struct ionod*)0,0);
}
else
t->com.comio = io = inout((struct ionod*)0,0);
}
}
*argtail = 0;
t->com.comtyp = TCOM;
/* expand argument list if possible */
if(argno>0)
t->com.comarg = qscan(&t->com,argno);
else if(t->com.comarg)
t->com.comtyp |= COMSCAN;
sh.wdset &= ~CAN_ALIAS;
return(t);
}
}
sh.reserv++;
sh_lex();
if(io=inout(io,0))
{
int type = t->tre.tretyp&COMMSK;
t=sh_mkfork(0,t);
t->tre.treio=io;
t->fork.forkline = getlineno(st.firstline)-1;
if(type != TFORK)
t->tre.tretyp = TSETIO;
}
done:
sh.owdval = savwdval;
sh.olineno = savline;
return(t);
}
/*
* skip past newlines but issue prompt if interactive
*/
static int skipnl()
{
while((sh.reserv++, sh_lex()==NL))
sh_prompt(0);
if(sh.wdval==';')
sh_syntax();
return(sh.wdval);
}
/*
* check for and process and i/o redirections
* if flag is set then an alias can be in the next word
*/
static struct ionod *inout(lastio,flag)
struct ionod *lastio;
{
register int iof;
register struct ionod *iop;
register int c;
iof=sh.wdnum;
switch(sh.wdval)
{
case DOCSYM: /* << */
iof |= (IODOC|IORAW);
heredoc = FTMP;
break;
case APPSYM: /* >> */
case '>':
if(sh.wdnum==0)
iof |= 1;
iof |= IOPUT;
if(sh.wdval==APPSYM)
{
iof |= IOAPP;
break;
}
case '<':
if((c=io_nextc())=='&')
iof |= IOMOV;
else if(c=='|' && sh.wdval=='>')
iof |= IOCLOB;
else if(c=='>')
iof |= IORDW;
else
io_unreadc(c);
break;
default:
return(lastio);
}
chkword();
iop=(struct ionod*) stakalloc(sizeof(struct ionod));
iop->ioname=sh.wdarg->argval;
iop->iolink = 0;
iop->iodelim = 0;
if(iof&IODOC)
{
iop->iolst=st.iopend;
st.iopend=iop;
}
else
{
iop->iolst = 0;
chkflags(A_EXP);
if(sh.wdarg->argflag&A_RAW)
iof |= IORAW;
}
iop->iofile=iof;
if(flag)
/* allow alias substitutions and parameter assignments */
sh.wdset |= (KEYFLG|CAN_ALIAS);
sh_lex();
iop->ionxt=inout(lastio,flag);
return(iop);
}
/*
* get next token and make sure that it is not a keyword or meta-character
*/
static void chkword()
{
if(sh_lex())
sh_syntax();
}
/*
* see if this token is syntactically correct
*/
static void chksym(sym)
register int sym;
{
register int x = sym&sh.wdval;
if(((x&SYMFLG) ? x : sym) != sh.wdval)
sh_syntax();
}
/*
* print the name of a syntactic token
*/
static void prsym(sym)
register int sym;
{
if(sym&SYMFLG)
{
register const struct sysnod *sp=tab_reserved;
while(sp->sysval && sp->sysval!=sym)
sp++;
p_str(sp->sysnam,0);
}
else if(sym==EOFSYM)
p_str(e_endoffile,0);
else
{
if(sym&SYMREP)
p_char(sym);
if(sym==NL)
p_str("newline or ;",0);
else
p_char(sym);
}
p_char('\'');
}
/*
* print a bad syntax message
*/
void sh_syntax()
{
register const char *cp = e_unexpected;
register int w = sh.wdval;
p_setout(ERRIO);
p_prp(e_synbad);
if((w==EOFSYM) && sh.owdval)
{
w = sh.owdval;
cp = e_unmatched;
}
else
sh.olineno = st.standin->flin;
if(!(st.states&(PROMPT|PROFILE)))
{
p_str(e_atline,0);
p_num(sh.olineno,SP);
}
p_str(e_colon,'`');
if(w)
prsym(w);
else
{
sh_trim(sh.wdarg->argval);
p_str(sh.wdarg->argval,'\'');
}
p_str(cp,NL);
exitset();
#ifdef WEXP_E
if (is_option(WEXP_E))
sh_exit(WEXP_SYNTAX);
else
#endif /* WEXP_E */
sh_exit(SYNBAD);
}
/*
* check argument for possible optimizations
* in many cases we can skip mac_expand and file name expansion
* fexp is set to the union of A_SPLIT and A_EXP when splitting and/or
* file expansion are possible.
*/
#define EXP_MACRO 2 /* macro expansion needed */
#define EXP_TRIM 4 /* quoted characters in string */
#define EXP_FILE 8 /* file expansion characters*/
#define EXP_QUOTE 16 /* string contains " character */
static void chkflags(fexp)
{
register struct argnod* argp = sh.wdarg;
register int c;
argp->argflag = 0;
{
register int flag = 0;
char nquote = 0;
char *sp=argp->argval;
while(c= *sp++)
{
if(c==ESCAPE)
{
flag |= EXP_TRIM;
sp++;
}
else if(isexp(c))
{
if(c == '$' || c == '`')
{
flag |= EXP_MACRO;
if(c=='`' || *sp==LPAREN)
sh.nested_sub++;
if(!nquote)
{
flag |= EXP_FILE;
break;
}
}
else if(!nquote)
{
/* special case of '[' */
if(*sp || c!='[')
flag |= EXP_FILE;
}
}
else if(c == '"')
{
/* toggle the quote count */
nquote = !nquote;
flag |= EXP_QUOTE;
}
}
if(!(flag&EXP_FILE))
fexp &= ~A_EXP;
/* return if no macro expansion, file expansion or trimming required */
if(flag==0)
{
argp->argflag |= A_RAW;
return;
}
/* return if macro or command substitution needed */
if(flag&EXP_MACRO)
{
argp->argflag |= (A_MAC|fexp);
if(is_option(NOEXEC))
{
int wdset = sh.wdset;
int offset = staktell();
char *savst = stakfreeze(0);
st.cmdline = getlineno(st.firstline);
mac_expand(argp->argval);
stakset(savst,offset);
sh.wdarg = argp;
sh.wdset = wdset;
}
return;
}
/* check to see if file expansion is required */
if(fexp&A_EXP)
{
argp->argflag|= A_EXP;
/* return if no quotes otherwise don't optimize */
if(flag&(EXP_QUOTE|EXP_TRIM))
{
argp->argflag= A_MAC|A_EXP;
return;
}
return;
}
argp->argflag |= A_RAW;
}
/* just get rid of quoting stuff and consider argument as expanded */
{
register char *dp,*sp;
char nquote = 0; /* set within quoted string */
dp = sp = argp->argval;
while(c= *sp++)
{
if(c != '"')
{
if(c==ESCAPE)
{
/* strip escchar's in double quotes */
c = *sp++;
if(nquote && !escchar(c) && c!='"')
*dp++ = ESCAPE;
}
*dp++ = c;
}
else /* toggle quote marker */
nquote = !nquote;
}
*dp = 0;
}
}
/*
* convert argument chain to argument list when no special arguments
*/
#ifdef VPIX
# define EXTRA 2
#else
# define EXTRA 1
#endif /* VPIX */
static struct argnod *qscan(ac,argn)
struct comnod *ac;
int argn;
{
register char **cp;
register struct argnod *ap;
register struct dolnod* dp;
/* leave space for an extra argument at the front */
dp = (struct dolnod*)stakalloc((unsigned)sizeof(struct dolnod) + EXTRA*sizeof(char*) + argn*sizeof(char*));
cp = dp->dolarg+EXTRA;
dp->doluse = argn;
ap = ac->comarg;
while(ap)
{
*cp++ = ap->argval;
ap = ap->argnxt.ap;
}
*cp = NULL;
return((struct argnod*)dp);
}
int getlineno(offset)
register int offset;
{
if(st.exec_flag)
return(st.cmdline);
return(st.standin->flin-offset-((st.peekn&STRIP)==NL));
}
#ifdef NEWTEST
static union anynode *test_expr(sym)
{
register union anynode *t = test_or();
if(sh.wdval!=sym)
sh_syntax();
return(t);
}
static union anynode *test_or()
{
register union anynode *t = test_and();
while(sh.wdval==ORFSYM)
t = makelist(TORF|TTEST,t,test_and());
return(t);
}
static union anynode *test_and()
{
register union anynode *t = test_primary();
while(sh.wdval==ANDFSYM)
t = makelist(TAND|TTEST,t,test_primary());
return(t);
}
static union anynode *test_primary()
{
register int num;
register struct argnod *arg;
register union anynode *t;
sh.wdset |= TEST_OP1;
skipnl();
num = sh.wdnum;
switch(sh.wdval)
{
case '(':
t = test_expr(')');
t = makelist(TTST|TTEST|TPAREN ,t, t);
sh_lex();
break;
case '!':
t = test_primary();
t->tre.tretyp |= TNEGATE;
break;
case TESTUNOP:
chkword();
chkflags(0);
t = makelist(TTST|TTEST|TUNARY|(num<<TSHIFT),
(union anynode*)sh.wdarg,(union anynode*)sh.wdarg);
skipnl();
break;
/* binary test operators */
case 0:
chkflags(0);
arg = sh.wdarg;
sh.wdset |= TEST_OP2;
sh_lex();
if(sh.wdval==TESTBINOP)
num = sh.wdnum;
else if(sh.wdval=='<')
num = TEST_SLT;
else if(sh.wdval=='>')
num = TEST_SGT;
else
sh_syntax();
chkword();
if(num&TEST_PATTERN)
{
chkflags(A_EXP);
if(sh.wdarg->argflag&A_EXP)
num &= ~TEST_PATTERN;
}
else
chkflags(0);
t = makelist(TTST|TTEST|TBINARY|(num<<TSHIFT),
(union anynode*)arg,(union anynode*)sh.wdarg);
skipnl();
}
return(t);
}
#endif /* NEWTEST */