mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-25 07:26:27 +00:00
The new command substitution mechanism imported in970069a6
from Red Hat patches introduced this bug: backtick-style command substitutions hang when processing about 117KiB of data or more. It is fixed by another Red Hat patch: https://src.fedoraproject.org/rpms/ksh/blob/642af4d6/f/ksh-20140415-hokaido.patch It saves the value of the shp->comsub flag so that it is set to 2 (usually meaning new-style $(comsubs)) in two specific cases even when processing backtick comsubs. This stops the sh_subtmpfile() function in subshell.c from creating a /tmp file. However, I think that approach is quite ugly, so I'm taking a slightly different one that has the same effect. src/cmd/ksh93/include/defs.h, src/cmd/ksh93/sh/subshell.c: - Redefine sh_subtmpfile() to pass the comsub flag as an argument. (Remove the shp pointer argument, which is redundant; a pointer to the shell state can easily be obtained in the function.) src/cmd/ksh93/sh/xec.c: sh_exec(): - Apply the Red Hat fix by passing flag 2 to sh_subtmpfile(). src/cmd/ksh93/tests/subshell.sh: - Move regress test fromce68e1be
from basic.sh to here; this is the place for command substitution tests as they are subshells. - Add regress test for this bug. All other changed files: - Update sh_subtmpfile() calls to pass on the shp->comsub flag.
790 lines
19 KiB
C
790 lines
19 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
|
|
*
|
|
*/
|
|
|
|
#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_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 "DircabefhkmnpstuvxBCGEl" HFLAG;
|
|
static const int flagval[] =
|
|
{
|
|
#if SHOPT_PFSH
|
|
SH_PFSH,
|
|
#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(*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;
|
|
case 'o': /* set options */
|
|
byname:
|
|
if(!opt_info.arg||!*opt_info.arg||*opt_info.arg=='-')
|
|
{
|
|
action = PRINT;
|
|
verbose = (f?PRINT_VERBOSE:PRINT_NO_HEADER)|
|
|
((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 || (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;
|
|
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"); /* set -r cannot be unset */
|
|
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--;
|
|
}
|
|
/* SH_INTERACTIVE and SH_PRIVILEGED are handled in sh_applyopts() */
|
|
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);
|
|
}
|
|
/* -o posix also switches -o braceexpand and -o letoctal */
|
|
if(!sh_isoption(SH_POSIX) && is_option(&newflags,SH_POSIX))
|
|
{
|
|
#if SHOPT_BRACEPAT
|
|
off_option(&newflags,SH_BRACEEXPAND);
|
|
#endif
|
|
on_option(&newflags,SH_LETOCTAL);
|
|
}
|
|
else if(sh_isoption(SH_POSIX) && !is_option(&newflags,SH_POSIX))
|
|
{
|
|
#if SHOPT_BRACEPAT
|
|
on_option(&newflags,SH_BRACEEXPAND);
|
|
#endif
|
|
off_option(&newflags,SH_LETOCTAL);
|
|
}
|
|
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 argument 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 */
|
|
sfwrite(sfstdout,"set --default",13);
|
|
for(tp=shtab_options; value=tp->sh_number; tp++)
|
|
{
|
|
if(mask && !is_option(mask,value&0xff))
|
|
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 */
|
|
sfprintf(sfstdout, "set %co %s\n", on?'-':'+', name);
|
|
else if(!(value&SH_COMMANDLINE) && is_option(&oflags,value&0xff))
|
|
sfprintf(sfstdout, " %s%s%s","--", 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(a) sh_rpipe(a)
|
|
#endif
|
|
|
|
struct argnod *sh_argprocsub(Shell_t *shp,struct argnod *argp)
|
|
{
|
|
/* argument of the form <(cmd) or >(cmd) */
|
|
register struct argnod *ap;
|
|
int monitor, interactive, fd, pv[3];
|
|
int subshell = shp->subshell;
|
|
ap = (struct argnod*)stkseek(shp->stk,ARGVAL);
|
|
ap->argflag |= ARG_MAKE;
|
|
ap->argflag &= ~ARG_RAW;
|
|
fd = argp->argflag&ARG_RAW;
|
|
if(fd==0 && shp->subshell)
|
|
sh_subtmpfile(shp->comsub);
|
|
#if SHOPT_DEVFD
|
|
sfwrite(shp->stk,e_devfdNN,8);
|
|
pv[2] = 0;
|
|
sh_pipe(pv);
|
|
#else
|
|
pv[0] = -1;
|
|
while((shp->fifo = pathtemp(0,0,0,"ksh.fifo",0), mkfifo(shp->fifo,0))<0)
|
|
{
|
|
if(errno==EEXIST || errno==EACCES || errno==ENOENT || errno==ENOTDIR || errno==EROFS)
|
|
continue; /* lost race (name conflict or tmp dir change); try again */
|
|
errormsg(SH_DICT,ERROR_system(128),"process substitution: FIFO creation failed");
|
|
}
|
|
chmod(shp->fifo,S_IRUSR|S_IWUSR); /* mkfifo + chmod works regardless of umask */
|
|
sfputr(shp->stk,shp->fifo,0);
|
|
#endif /* SHOPT_DEVFD */
|
|
sfputr(shp->stk,fmtbase((long)pv[fd],10,0),0);
|
|
ap = (struct argnod*)stkfreeze(shp->stk,0);
|
|
shp->inpipe = shp->outpipe = 0;
|
|
|
|
/* turn off job control */
|
|
if(interactive = (sh_isstate(SH_INTERACTIVE)!=0))
|
|
sh_offstate(SH_INTERACTIVE);
|
|
if(monitor = (sh_isstate(SH_MONITOR)!=0))
|
|
sh_offstate(SH_MONITOR);
|
|
|
|
/* do the process substitution */
|
|
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));
|
|
}
|
|
|
|
/* restore the previous state */
|
|
shp->subshell = subshell;
|
|
if(monitor)
|
|
sh_onstate(SH_MONITOR);
|
|
if(interactive)
|
|
sh_onstate(SH_INTERACTIVE);
|
|
#if SHOPT_DEVFD
|
|
close(pv[1-fd]);
|
|
sh_iosave(shp,-pv[fd], shp->topfd, (char*)0);
|
|
#else
|
|
free(shp->fifo);
|
|
shp->fifo = 0;
|
|
#endif /* SHOPT_DEVFD */
|
|
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(*argp->argval==0 && (argp->argflag&ARG_EXP))
|
|
{
|
|
struct argnod *ap;
|
|
ap = sh_argprocsub(shp,argp);
|
|
ap->argchn.ap = *argchain;
|
|
*argchain = ap;
|
|
count++;
|
|
}
|
|
else
|
|
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);
|
|
}
|
|
|