summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/args.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ksh93/sh/args.c')
-rw-r--r--src/cmd/ksh93/sh/args.c892
1 files changed, 892 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/args.c b/src/cmd/ksh93/sh/args.c
new file mode 100644
index 0000000..4d27580
--- /dev/null
+++ b/src/cmd/ksh93/sh/args.c
@@ -0,0 +1,892 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 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_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_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;
+ fd = argp->argflag&ARG_RAW;
+ if(fd==0 && shp->subshell)
+ sh_subtmpfile(shp);
+#if SHOPT_DEVFD
+ sfwrite(shp->stk,e_devfdNN,8);
+ pv[2] = 0;
+ sh_pipe(pv);
+#else
+ pv[0] = -1;
+ shp->fifo = pathtemp(0,0,0,"ksh.fifo",0);
+ mkfifo(shp->fifo,S_IRUSR|S_IWUSR);
+ 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;
+ 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);
+#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);
+}
+