/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2011 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* UNIX shell
*
* S. R. Bourne
* Rewritten by David Korn
* AT&T Labs
*
*/
/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#include "defs.h"
#include "path.h"
#include "builtins.h"
#include "terminal.h"
#include "edit.h"
#include "FEATURE/poll"
#if SHOPT_KIA
# include "shlex.h"
# include "io.h"
#endif /* SHOPT_KIA */
#if SHOPT_PFSH
# define PFSHOPT "P"
#else
# define PFSHOPT
#endif
#if SHOPT_BASH
# define BASHOPT "\374"
#else
# define BASHOPT
#endif
#if SHOPT_HISTEXPAND
# define HFLAG "H"
#else
# define HFLAG ""
#endif
#define SORT 1
#define PRINT 2
static char *null;
/* The following order is determined by sh_optset */
static const char optksh[] = PFSHOPT BASHOPT "DircabefhkmnpstuvxBCGEl" HFLAG;
static const int flagval[] =
{
#if SHOPT_PFSH
SH_PFSH,
#endif
#if SHOPT_BASH
SH_POSIX,
#endif
SH_DICTIONARY, SH_INTERACTIVE, SH_RESTRICTED, SH_CFLAG,
SH_ALLEXPORT, SH_NOTIFY, SH_ERREXIT, SH_NOGLOB, SH_TRACKALL,
SH_KEYWORD, SH_MONITOR, SH_NOEXEC, SH_PRIVILEGED, SH_SFLAG, SH_TFLAG,
SH_NOUNSET, SH_VERBOSE, SH_XTRACE, SH_BRACEEXPAND, SH_NOCLOBBER,
SH_GLOBSTARS, SH_RC, SH_LOGIN_SHELL,
#if SHOPT_HISTEXPAND
SH_HISTEXPAND,
#endif
0
};
#define NUM_OPTS (sizeof(flagval)/sizeof(*flagval))
typedef struct _arg_
{
Shell_t *sh;
struct dolnod *argfor; /* linked list of blocks to be cleaned up */
struct dolnod *dolh;
char flagadr[NUM_OPTS+1];
#if SHOPT_KIA
char *kiafile;
#endif /* SHOPT_KIA */
} Arg_t;
static int arg_expand(Shell_t*,struct argnod*,struct argnod**,int);
static void sh_argset(Arg_t*, char *[]);
/* ======== option handling ======== */
void *sh_argopen(Shell_t *shp)
{
void *addr = newof(0,Arg_t,1,0);
Arg_t *ap = (Arg_t*)addr;
ap->sh = shp;
return(addr);
}
static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
{
#if SHOPT_BASH
extern const char sh_bash1[], sh_bash2[];
if(strcmp(s,"bash1")==0)
{
if(sh_isoption(SH_BASH))
sfputr(sp,sh_bash1,-1);
}
else if(strcmp(s,"bash2")==0)
{
if(sh_isoption(SH_BASH))
sfputr(sp,sh_bash2,-1);
}
else if(*s==':' && sh_isoption(SH_BASH))
sfputr(sp,s,-1);
else
#endif
if(*s!=':')
sfputr(sp,sh_set,-1);
return(1);
}
/*
* This routine turns options on and off
* The options "PDicr" are illegal from set command.
* The -o option is used to set option by name
* This routine returns the number of non-option arguments
*/
int sh_argopts(int argc,register char *argv[], void *context)
{
Shell_t *shp = (Shell_t*)context;
register int n,o;
register Arg_t *ap = (Arg_t*)(shp->arg_context);
Lex_t *lp = (Lex_t*)(shp->lex_context);
Shopt_t newflags;
int setflag=0, action=0, trace=(int)sh_isoption(SH_XTRACE);
Namval_t *np = NIL(Namval_t*);
const char *cp;
int verbose,f;
Optdisc_t disc;
newflags=ap->sh->options;
memset(&disc, 0, sizeof(disc));
disc.version = OPT_VERSION;
disc.infof = infof;
opt_info.disc = &disc;
if(argc>0)
setflag = 4;
else
argc = -argc;
while((n = optget(argv,setflag?sh_optset:sh_optksh)))
{
o=0;
f=*opt_info.option=='-' && (opt_info.num || opt_info.arg);
switch(n)
{
case 'A':
np = nv_open(opt_info.arg,ap->sh->var_tree,NV_NOASSIGN|NV_ARRAY|NV_VARNAME);
if(f)
nv_unset(np);
continue;
#if SHOPT_BASH
case 'O': /* shopt options, only in bash mode */
if(!sh_isoption(SH_BASH))
errormsg(SH_DICT,ERROR_exit(1), e_option, opt_info.name);
#endif
case 'o': /* set options */
byname:
if(!opt_info.arg||!*opt_info.arg||*opt_info.arg=='-')
{
action = PRINT;
/* print style: -O => shopt options
* bash => print unset options also, no heading
*/
verbose = (f?PRINT_VERBOSE:PRINT_NO_HEADER)|
(n=='O'?PRINT_SHOPT:0)|
(sh_isoption(SH_BASH)?PRINT_ALL|PRINT_NO_HEADER:0)|
((opt_info.arg&&(!*opt_info.arg||*opt_info.arg=='-'))?(PRINT_TABLE|PRINT_NO_HEADER):0);
continue;
}
o = sh_lookopt(opt_info.arg,&f);
if(o<=0
|| (!sh_isoption(SH_BASH) && (o&SH_BASHEXTRA))
|| ((!sh_isoption(SH_BASH) || n=='o') && (o&SH_BASHOPT))
|| (setflag && (o&SH_COMMANDLINE)))
{
errormsg(SH_DICT,2, e_option, opt_info.arg);
error_info.errors++;
}
o &= 0xff;
if(sh_isoption(SH_RESTRICTED) && !f && o==SH_RESTRICTED)
errormsg(SH_DICT,ERROR_exit(1), e_restricted, opt_info.arg);
break;
#if SHOPT_BASH
case -1: /* --rcfile */
ap->sh->gd->rcfile = opt_info.arg;
continue;
case -2: /* --noediting */
if (!f)
{
off_option(&newflags,SH_VI);
off_option(&newflags,SH_EMACS);
off_option(&newflags,SH_GMACS);
}
continue;
case -3: /* --profile */
n = 'l';
goto skip;
case -4: /* --posix */
/* mask lower 8 bits to find char in optksh string */
n&=0xff;
goto skip;
case -5: /* --version */
sfputr(sfstdout, "ksh bash emulation, version ",-1);
np = nv_open("BASH_VERSION",ap->sh->var_tree,0);
sfputr(sfstdout, nv_getval(np),-1);
np = nv_open("MACHTYPE",ap->sh->var_tree,0);
sfprintf(sfstdout, " (%s)\n", nv_getval(np));
sh_exit(0);
#endif
case -6: /* --default */
{
register const Shtable_t *tp;
for(tp=shtab_options; o = tp->sh_number; tp++)
if(!(o&SH_COMMANDLINE) && is_option(&newflags,o&0xff))
off_option(&newflags,o&0xff);
}
continue;
case -7:
f = 0;
goto byname;
case 'D':
on_option(&newflags,SH_NOEXEC);
goto skip;
case 'T':
if (opt_info.num)
ap->sh->test |= opt_info.num;
else
ap->sh->test = 0;
continue;
case 's':
if(setflag)
{
action = SORT;
continue;
}
#if SHOPT_KIA
goto skip;
case 'R':
if(setflag)
n = ':';
else
{
ap->kiafile = opt_info.arg;
n = 'n';
}
/*FALLTHROUGH*/
#endif /* SHOPT_KIA */
#if SHOPT_REGRESS
goto skip;
case 'I':
continue;
#endif /* SHOPT_REGRESS */
skip:
default:
if(cp=strchr(optksh,n))
o = flagval[cp-optksh];
break;
case ':':
if(opt_info.name[0]=='-'&&opt_info.name[1]=='-')
{
opt_info.arg = argv[opt_info.index-1] + 2;
f = 1;
goto byname;
}
errormsg(SH_DICT,2, "%s", opt_info.arg);
continue;
case '?':
errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
return(-1);
}
if(f)
{
if(o==SH_VI || o==SH_EMACS || o==SH_GMACS)
{
off_option(&newflags,SH_VI);
off_option(&newflags,SH_EMACS);
off_option(&newflags,SH_GMACS);
}
on_option(&newflags,o);
off_option(&ap->sh->offoptions,o);
}
else
{
if ((o == SH_RESTRICTED) &&
sh_isoption(SH_RESTRICTED)) {
errormsg(SH_DICT, ERROR_exit(1),
e_restricted, "r");
}
if(o==SH_XTRACE)
trace = 0;
off_option(&newflags,o);
if(setflag==0)
on_option(&ap->sh->offoptions,o);
}
}
if(error_info.errors)
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
/* check for '-' or '+' argument */
if((cp=argv[opt_info.index]) && cp[1]==0 && (*cp=='+' || *cp=='-') &&
strcmp(argv[opt_info.index-1],"--"))
{
opt_info.index++;
off_option(&newflags,SH_XTRACE);
off_option(&newflags,SH_VERBOSE);
trace = 0;
}
if(trace)
sh_trace(shp,argv,1);
argc -= opt_info.index;
argv += opt_info.index;
if(action==PRINT)
sh_printopts(newflags,verbose,0);
if(setflag)
{
if(action==SORT)
{
if(argc>0)
strsort(argv,argc,strcoll);
else
strsort(ap->sh->st.dolv+1,ap->sh->st.dolc,strcoll);
}
if(np)
{
nv_setvec(np,0,argc,argv);
nv_close(np);
}
else if(argc>0 || ((cp=argv[-1]) && strcmp(cp,"--")==0))
sh_argset(ap,argv-1);
}
else if(is_option(&newflags,SH_CFLAG))
{
if(!(ap->sh->comdiv = *argv++))
{
errormsg(SH_DICT,2,e_cneedsarg);
errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*)));
}
argc--;
}
/* handling SH_INTERACTIVE and SH_PRIVILEGED has been moved to
* sh_applyopts(), so that the code can be reused from b_shopt(), too
*/
sh_applyopts(ap->sh,newflags);
#if SHOPT_KIA
if(ap->kiafile)
{
if(!argv[0])
errormsg(SH_DICT,ERROR_usage(2),"-R requires scriptname");
if(!(lp->kiafile=sfopen(NIL(Sfio_t*),ap->kiafile,"w+")))
errormsg(SH_DICT,ERROR_system(3),e_create,ap->kiafile);
if(!(lp->kiatmp=sftmp(2*SF_BUFSIZE)))
errormsg(SH_DICT,ERROR_system(3),e_tmpcreate);
sfputr(lp->kiafile,";vdb;CIAO/ksh",'\n');
lp->kiabegin = sftell(lp->kiafile);
lp->entity_tree = dtopen(&_Nvdisc,Dtbag);
lp->scriptname = strdup(sh_fmtq(argv[0]));
lp->script=kiaentity(lp,lp->scriptname,-1,'p',-1,0,0,'s',0,"");
lp->fscript=kiaentity(lp,lp->scriptname,-1,'f',-1,0,0,'s',0,"");
lp->unknown=kiaentity(lp,"<unknown>",-1,'p',-1,0,0,'0',0,"");
kiaentity(lp,"<unknown>",-1,'p',0,0,lp->unknown,'0',0,"");
lp->current = lp->script;
ap->kiafile = 0;
}
#endif /* SHOPT_KIA */
return(argc);
}
/* apply new options */
void sh_applyopts(Shell_t* shp,Shopt_t newflags)
{
/* cannot set -n for interactive shells since there is no way out */
if(sh_isoption(SH_INTERACTIVE))
off_option(&newflags,SH_NOEXEC);
if(is_option(&newflags,SH_PRIVILEGED))
on_option(&newflags,SH_NOUSRPROFILE);
if(!sh_isstate(SH_INIT) && is_option(&newflags,SH_PRIVILEGED) != sh_isoption(SH_PRIVILEGED) || sh_isstate(SH_INIT) && is_option(&((Arg_t*)shp->arg_context)->sh->offoptions,SH_PRIVILEGED) && shp->gd->userid!=shp->gd->euserid)
{
if(!is_option(&newflags,SH_PRIVILEGED))
{
setuid(shp->gd->userid);
setgid(shp->gd->groupid);
if(shp->gd->euserid==0)
{
shp->gd->euserid = shp->gd->userid;
shp->gd->egroupid = shp->gd->groupid;
}
}
else if((shp->gd->userid!=shp->gd->euserid && setuid(shp->gd->euserid)<0) ||
(shp->gd->groupid!=shp->gd->egroupid && setgid(shp->gd->egroupid)<0) ||
(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid))
off_option(&newflags,SH_PRIVILEGED);
}
#if SHOPT_BASH
on_option(&newflags,SH_CMDHIST);
on_option(&newflags,SH_CHECKHASH);
on_option(&newflags,SH_EXECFAIL);
on_option(&newflags,SH_EXPAND_ALIASES);
on_option(&newflags,SH_HISTAPPEND);
on_option(&newflags,SH_INTERACTIVE_COMM);
on_option(&newflags,SH_LITHIST);
on_option(&newflags,SH_NOEMPTYCMDCOMPL);
if(!is_option(&newflags,SH_XPG_ECHO) && sh_isoption(SH_XPG_ECHO))
astconf("UNIVERSE", 0, "ucb");
if(is_option(&newflags,SH_XPG_ECHO) && !sh_isoption(SH_XPG_ECHO))
astconf("UNIVERSE", 0, "att");
if(!is_option(&newflags,SH_PHYSICAL) && sh_isoption(SH_PHYSICAL))
astconf("PATH_RESOLVE", 0, "metaphysical");
if(is_option(&newflags,SH_PHYSICAL) && !sh_isoption(SH_PHYSICAL))
astconf("PATH_RESOLVE", 0, "physical");
if(is_option(&newflags,SH_HISTORY2) && !sh_isoption(SH_HISTORY2))
{
sh_onstate(SH_HISTORY);
sh_onoption(SH_HISTORY);
}
if(!is_option(&newflags,SH_HISTORY2) && sh_isoption(SH_HISTORY2))
{
sh_offstate(SH_HISTORY);
sh_offoption(SH_HISTORY);
}
#endif
shp->options = newflags;
}
/*
* returns the value of $-
*/
char *sh_argdolminus(void* context)
{
register Arg_t *ap = (Arg_t*)context;
register const char *cp=optksh;
register char *flagp=ap->flagadr;
while(cp< &optksh[NUM_OPTS])
{
int n = flagval[cp-optksh];
if(sh_isoption(n))
*flagp++ = *cp;
cp++;
}
*flagp = 0;
return(ap->flagadr);
}
/*
* set up positional parameters
*/
static void sh_argset(Arg_t *ap,char *argv[])
{
sh_argfree(ap->sh,ap->dolh,0);
ap->dolh = sh_argcreate(argv);
/* link into chain */
ap->dolh->dolnxt = ap->argfor;
ap->argfor = ap->dolh;
ap->sh->st.dolc = ap->dolh->dolnum-1;
ap->sh->st.dolv = ap->dolh->dolval;
}
/*
* free the argument list if the use count is 1
* If count is greater than 1 decrement count and return same blk
* Free the argument list if the use count is 1 and return next blk
* Delete the blk from the argfor chain
* If flag is set, then the block dolh is not freed
*/
struct dolnod *sh_argfree(Shell_t *shp, struct dolnod *blk,int flag)
{
register struct dolnod* argr=blk;
register struct dolnod* argblk;
register Arg_t *ap = (Arg_t*)shp->arg_context;
if(argblk=argr)
{
if((--argblk->dolrefcnt)==0)
{
argr = argblk->dolnxt;
if(flag && argblk==ap->dolh)
ap->dolh->dolrefcnt = 1;
else
{
/* delete from chain */
if(ap->argfor == argblk)
ap->argfor = argblk->dolnxt;
else
{
for(argr=ap->argfor;argr;argr=argr->dolnxt)
if(argr->dolnxt==argblk)
break;
if(!argr)
return(NIL(struct dolnod*));
argr->dolnxt = argblk->dolnxt;
argr = argblk->dolnxt;
}
free((void*)argblk);
}
}
}
return(argr);
}
/*
* grab space for arglist and copy args
* The strings are copied after the argment vector
*/
struct dolnod *sh_argcreate(register char *argv[])
{
register struct dolnod *dp;
register char **pp=argv, *sp;
register int size=0,n;
/* count args and number of bytes of arglist */
while(sp= *pp++)
size += strlen(sp);
n = (pp - argv)-1;
dp=new_of(struct dolnod,n*sizeof(char*)+size+n);
dp->dolrefcnt=1; /* use count */
dp->dolnum = n;
dp->dolnxt = 0;
pp = dp->dolval;
sp = (char*)dp + sizeof(struct dolnod) + n*sizeof(char*);
while(n--)
{
*pp++ = sp;
sp = strcopy(sp, *argv++) + 1;
}
*pp = NIL(char*);
return(dp);
}
/*
* used to set new arguments for functions
*/
struct dolnod *sh_argnew(Shell_t *shp,char *argi[], struct dolnod **savargfor)
{
register Arg_t *ap = (Arg_t*)shp->arg_context;
register struct dolnod *olddolh = ap->dolh;
*savargfor = ap->argfor;
ap->dolh = 0;
ap->argfor = 0;
sh_argset(ap,argi);
return(olddolh);
}
/*
* reset arguments as they were before function
*/
void sh_argreset(Shell_t *shp,struct dolnod *blk, struct dolnod *afor)
{
register Arg_t *ap = (Arg_t*)shp->arg_context;
while(ap->argfor=sh_argfree(shp,ap->argfor,0));
ap->argfor = afor;
if(ap->dolh = blk)
{
shp->st.dolc = ap->dolh->dolnum-1;
shp->st.dolv = ap->dolh->dolval;
}
}
/*
* increase the use count so that an sh_argset will not make it go away
*/
struct dolnod *sh_arguse(Shell_t* shp)
{
register struct dolnod *dh;
register Arg_t *ap = (Arg_t*)shp->arg_context;
if(dh=ap->dolh)
dh->dolrefcnt++;
return(dh);
}
/*
* Print option settings on standard output
* if mode is inclusive or of PRINT_*
* if <mask> is set, only options with this mask value are displayed
*/
void sh_printopts(Shopt_t oflags,register int mode, Shopt_t *mask)
{
register const Shtable_t *tp;
const char *name;
int on;
int value;
if(!(mode&PRINT_NO_HEADER))
sfputr(sfstdout,sh_translate(e_heading),'\n');
if(mode&PRINT_TABLE)
{
int w;
int c;
int r;
int i;
c = 0;
for(tp=shtab_options; value=tp->sh_number; tp++)
{
if(mask && !is_option(mask,value&0xff))
continue;
name = tp->sh_name;
if(name[0] == 'n' && name[1] == 'o' && name[2] != 't')
name += 2;
if(c<(w=strlen(name)))
c = w;
}
c += 4;
if((w = ed_window()) < (2*c))
w = 2*c;
r = w / c;
i = 0;
for(tp=shtab_options; value=tp->sh_number; tp++)
{
if(mask && !is_option(mask,value&0xff))
continue;
on = !!is_option(&oflags,value);
value &= 0xff;
name = tp->sh_name;
if(name[0] == 'n' && name[1] == 'o' && name[2] != 't')
{
name += 2;
on = !on;
}
if(++i>=r)
{
i = 0;
sfprintf(sfstdout, "%s%s\n", on ? "" : "no", name);
}
else
sfprintf(sfstdout, "%s%-*s", on ? "" : "no", on ? c : (c-2), name);
}
if(i)
sfputc(sfstdout,'\n');
return;
}
#if SHOPT_RAWONLY
on_option(&oflags,SH_VIRAW);
#endif
if(!(mode&(PRINT_ALL|PRINT_VERBOSE))) /* only print set options */
{
if(mode&PRINT_SHOPT)
sfwrite(sfstdout,"shopt -s",3);
else
sfwrite(sfstdout,"set --default",13);
}
for(tp=shtab_options; value=tp->sh_number; tp++)
{
if(mask && !is_option(mask,value&0xff))
continue;
if(sh_isoption(SH_BASH))
{
if (!(mode&PRINT_SHOPT) != !(value&SH_BASHOPT))
continue;
}
else if (value&(SH_BASHEXTRA|SH_BASHOPT))
continue;
on = !!is_option(&oflags,value);
name = tp->sh_name;
if(name[0] == 'n' && name[1] == 'o' && name[2] != 't')
{
name += 2;
on = !on;
}
if(mode&PRINT_VERBOSE)
{
sfputr(sfstdout,name,' ');
sfnputc(sfstdout,' ',24-strlen(name));
sfputr(sfstdout,on ? sh_translate(e_on) : sh_translate(e_off),'\n');
}
else if(mode&PRINT_ALL) /* print unset options also */
{
if(mode&PRINT_SHOPT)
sfprintf(sfstdout, "shopt -%c %s\n",
on?'s':'u',
name);
else
sfprintf(sfstdout, "set %co %s\n",
on?'-':'+',
name);
}
else if(!(value&SH_COMMANDLINE) && is_option(&oflags,value&0xff))
sfprintf(sfstdout," %s%s%s",(mode&PRINT_SHOPT)?"":"--",on?"":"no",name);
}
if(!(mode&(PRINT_VERBOSE|PRINT_ALL)))
sfputc(sfstdout,'\n');
}
/*
* build an argument list
*/
char **sh_argbuild(Shell_t *shp,int *nargs, const struct comnod *comptr,int flag)
{
register struct argnod *argp;
struct argnod *arghead=0;
shp->xargmin = 0;
{
register const struct comnod *ac = comptr;
register int n;
/* see if the arguments have already been expanded */
if(!ac->comarg)
{
*nargs = 0;
return(&null);
}
else if(!(ac->comtyp&COMSCAN))
{
register struct dolnod *ap = (struct dolnod*)ac->comarg;
*nargs = ap->dolnum;
return(ap->dolval+ap->dolbot);
}
shp->lastpath = 0;
*nargs = 0;
if(ac)
{
if(ac->comnamp == SYSLET)
flag |= ARG_LET;
argp = ac->comarg;
while(argp)
{
n = arg_expand(shp,argp,&arghead,flag);
if(n>1)
{
if(shp->xargmin==0)
shp->xargmin = *nargs;
shp->xargmax = *nargs+n;
}
*nargs += n;
argp = argp->argnxt.ap;
}
argp = arghead;
}
}
{
register char **comargn;
register int argn;
register char **comargm;
argn = *nargs;
/* allow room to prepend args */
argn += 1;
comargn=(char**)stkalloc(shp->stk,(unsigned)(argn+1)*sizeof(char*));
comargm = comargn += argn;
*comargn = NIL(char*);
if(!argp)
{
/* reserve an extra null pointer */
*--comargn = 0;
return(comargn);
}
while(argp)
{
struct argnod *nextarg = argp->argchn.ap;
argp->argchn.ap = 0;
*--comargn = argp->argval;
if(!(argp->argflag&ARG_RAW))
sh_trim(*comargn);
if(!(argp=nextarg) || (argp->argflag&ARG_MAKE))
{
if((argn=comargm-comargn)>1)
strsort(comargn,argn,strcoll);
comargm = comargn;
}
}
shp->last_table = 0;
return(comargn);
}
}
#if _pipe_socketpair && !_socketpair_devfd
# define sh_pipe arg_pipe
/*
* create a real pipe (not a socket) and print message on failure
*/
static int arg_pipe(register int pv[])
{
Shell_t *shp = sh_getinterp();
int fd[2];
if(pipe(fd)<0 || (pv[0]=fd[0])<0 || (pv[1]=fd[1])<0)
errormsg(SH_DICT,ERROR_system(1),e_pipe);
pv[0] = sh_iomovefd(pv[0]);
pv[1] = sh_iomovefd(pv[1]);
shp->fdstatus[pv[0]] = IONOSEEK|IOREAD;
shp->fdstatus[pv[1]] = IONOSEEK|IOWRITE;
sh_subsavefd(pv[0]);
sh_subsavefd(pv[1]);
return(0);
}
#endif
struct argnod *sh_argprocsub(Shell_t *shp,struct argnod *argp)
{
/* argument of the form <(cmd) or >(cmd) */
register struct argnod *ap;
int monitor, fd, pv[3];
int subshell = shp->subshell;
ap = (struct argnod*)stkseek(shp->stk,ARGVAL);
ap->argflag |= ARG_MAKE;
ap->argflag &= ~ARG_RAW;
sfwrite(shp->stk,e_devfdNN,8);
pv[2] = 0;
sh_pipe(pv);
fd = argp->argflag&ARG_RAW;
if(fd==0 && shp->subshell)
sh_subtmpfile(shp);
sfputr(shp->stk,fmtbase((long)pv[fd],10,0),0);
ap = (struct argnod*)stkfreeze(shp->stk,0);
shp->inpipe = shp->outpipe = 0;
if(monitor = (sh_isstate(SH_MONITOR)!=0))
sh_offstate(SH_MONITOR);
shp->subshell = 0;
if(fd)
{
shp->inpipe = pv;
sh_exec((Shnode_t*)argp->argchn.ap,(int)sh_isstate(SH_ERREXIT));
}
else
{
shp->outpipe = pv;
sh_exec((Shnode_t*)argp->argchn.ap,(int)sh_isstate(SH_ERREXIT));
}
shp->subshell = subshell;
if(monitor)
sh_onstate(SH_MONITOR);
close(pv[1-fd]);
sh_iosave(shp,-pv[fd], shp->topfd, (char*)0);
return(ap);
}
/* Argument expansion */
static int arg_expand(Shell_t *shp,register struct argnod *argp, struct argnod **argchain,int flag)
{
register int count = 0;
argp->argflag &= ~ARG_MAKE;
#if SHOPT_DEVFD
if(*argp->argval==0 && (argp->argflag&ARG_EXP))
{
struct argnod *ap;
ap = sh_argprocsub(shp,argp);
ap->argchn.ap = *argchain;
*argchain = ap;
count++;
}
else
#endif /* SHOPT_DEVFD */
if(!(argp->argflag&ARG_RAW))
{
#if SHOPT_OPTIMIZE
struct argnod *ap;
sh_stats(STAT_ARGEXPAND);
if(flag&ARG_OPTIMIZE)
argp->argchn.ap=0;
if(ap=argp->argchn.ap)
{
sh_stats(STAT_ARGHITS);
count = 1;
ap->argchn.ap = *argchain;
ap->argflag |= ARG_RAW;
ap->argflag &= ~ARG_EXP;
*argchain = ap;
}
else
#endif /* SHOPT_OPTIMIZE */
count = sh_macexpand(shp,argp,argchain,flag);
}
else
{
argp->argchn.ap = *argchain;
*argchain = argp;
argp->argflag |= ARG_MAKE;
count++;
}
return(count);
}