summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ksh93/sh')
-rw-r--r--src/cmd/ksh93/sh/args.c892
-rw-r--r--src/cmd/ksh93/sh/arith.c549
-rw-r--r--src/cmd/ksh93/sh/array.c1843
-rw-r--r--src/cmd/ksh93/sh/bash.c423
-rw-r--r--src/cmd/ksh93/sh/defs.c48
-rw-r--r--src/cmd/ksh93/sh/deparse.c603
-rw-r--r--src/cmd/ksh93/sh/env.c255
-rw-r--r--src/cmd/ksh93/sh/expand.c468
-rw-r--r--src/cmd/ksh93/sh/fault.c672
-rw-r--r--src/cmd/ksh93/sh/fcin.c214
-rw-r--r--src/cmd/ksh93/sh/init.c2238
-rw-r--r--src/cmd/ksh93/sh/io.c2647
-rw-r--r--src/cmd/ksh93/sh/jobs.c2055
-rw-r--r--src/cmd/ksh93/sh/lex.c2516
-rw-r--r--src/cmd/ksh93/sh/macro.c2814
-rw-r--r--src/cmd/ksh93/sh/main.c787
-rw-r--r--src/cmd/ksh93/sh/name.c3684
-rw-r--r--src/cmd/ksh93/sh/nvdisc.c1468
-rw-r--r--src/cmd/ksh93/sh/nvtree.c1161
-rw-r--r--src/cmd/ksh93/sh/nvtype.c1709
-rw-r--r--src/cmd/ksh93/sh/parse.c2082
-rw-r--r--src/cmd/ksh93/sh/path.c1824
-rw-r--r--src/cmd/ksh93/sh/pmain.c46
-rw-r--r--src/cmd/ksh93/sh/shcomp.c177
-rw-r--r--src/cmd/ksh93/sh/streval.c1023
-rw-r--r--src/cmd/ksh93/sh/string.c730
-rw-r--r--src/cmd/ksh93/sh/subshell.c780
-rw-r--r--src/cmd/ksh93/sh/suid_exec.c509
-rw-r--r--src/cmd/ksh93/sh/tdump.c262
-rw-r--r--src/cmd/ksh93/sh/timers.c248
-rw-r--r--src/cmd/ksh93/sh/trestore.c353
-rw-r--r--src/cmd/ksh93/sh/waitevent.c54
-rw-r--r--src/cmd/ksh93/sh/xec.c3991
33 files changed, 39125 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);
+}
+
diff --git a/src/cmd/ksh93/sh/arith.c b/src/cmd/ksh93/sh/arith.c
new file mode 100644
index 0000000..f1814fa
--- /dev/null
+++ b/src/cmd/ksh93/sh/arith.c
@@ -0,0 +1,549 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Shell arithmetic - uses streval library
+ * David Korn
+ * AT&T Labs
+ */
+
+#include "defs.h"
+#include "lexstates.h"
+#include "name.h"
+#include "streval.h"
+#include "variables.h"
+#include "builtins.h"
+
+#ifndef LLONG_MAX
+#define LLONG_MAX LONG_MAX
+#endif
+
+typedef Sfdouble_t (*Math_f)(Sfdouble_t, ...);
+
+extern const Namdisc_t ENUM_disc;
+static Sfdouble_t NaN, Inf, Fun;
+static Namval_t Infnod =
+{
+ { 0 },
+ "Inf",
+ NV_NOFREE|NV_LDOUBLE,NV_RDONLY
+};
+
+static Namval_t NaNnod =
+{
+ { 0 },
+ "NaN",
+ NV_NOFREE|NV_LDOUBLE,NV_RDONLY
+};
+
+static Namval_t FunNode =
+{
+ { 0 },
+ "?",
+ NV_NOFREE|NV_LDOUBLE,NV_RDONLY
+};
+
+static Namval_t *scope(register Namval_t *np,register struct lval *lvalue,int assign)
+{
+ register int flag = lvalue->flag;
+ register char *sub=0, *cp=(char*)np;
+ register Namval_t *mp;
+ Shell_t *shp = lvalue->shp;
+ int flags = HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET;
+ int c=0,nosub = lvalue->nosub;
+ Dt_t *sdict = (shp->st.real_fun? shp->st.real_fun->sdict:0);
+ Dt_t *nsdict = (shp->namespace?nv_dict(shp->namespace):0);
+ Dt_t *root = shp->var_tree;
+ assign = assign?NV_ASSIGN:NV_NOASSIGN;
+ lvalue->nosub = 0;
+ if(nosub<0 && lvalue->ovalue)
+ return((Namval_t*)lvalue->ovalue);
+ lvalue->ovalue = 0;
+ if(cp>=lvalue->expr && cp < lvalue->expr+lvalue->elen)
+ {
+ int offset;
+ /* do binding to node now */
+ int c = cp[flag];
+ cp[flag] = 0;
+ if((!(np = nv_open(cp,shp->var_tree,assign|NV_VARNAME|NV_NOADD|NV_NOFAIL)) || nv_isnull(np)) && sh_macfun(shp,cp, offset = staktell()))
+ {
+ Fun = sh_arith(shp,sub=stakptr(offset));
+ FunNode.nvalue.ldp = &Fun;
+ cp[flag] = c;
+ return(&FunNode);
+ }
+ if(!np && assign)
+ np = nv_open(cp,shp->var_tree,assign|NV_VARNAME);
+ cp[flag] = c;
+ if(!np)
+ return(0);
+ root = shp->last_root;
+ if(cp[flag+1]=='[')
+ flag++;
+ else
+ flag = 0;
+ cp = (char*)np;
+ }
+ else if(assign==NV_ASSIGN && nv_isnull(np) && !nv_isattr(np, ~(NV_MINIMAL|NV_NOFREE)))
+ flags |= NV_ADD;
+ if((lvalue->emode&ARITH_COMP) && dtvnext(root) && ((sdict && (mp=nv_search(cp,sdict,flags&~NV_ADD))) || (mp=nv_search(cp,root,flags&~(NV_ADD))) || (nsdict && (mp=nv_search(cp,nsdict,flags&~(NV_ADD|HASH_NOSCOPE)))) ))
+ np = mp;
+ while(nv_isref(np))
+ {
+#if SHOPT_FIXEDARRAY
+ int n,dim;
+ dim = nv_refdimen(np);
+ n = nv_refindex(np);
+#endif /* SHOPT_FIXEDARRAY */
+ sub = nv_refsub(np);
+ np = nv_refnode(np);
+#if SHOPT_FIXEDARRAY
+ if(n)
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ ap->nelem = dim;
+ nv_putsub(np,(char*)0,n);
+ }
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if(sub)
+ nv_putsub(np,sub,assign==NV_ASSIGN?ARRAY_ADD:0);
+ }
+ if(!nosub && flag)
+ {
+ int hasdot = 0;
+ cp = (char*)&lvalue->expr[flag];
+ if(sub)
+ {
+ goto skip;
+ }
+ sub = cp;
+ while(1)
+ {
+ Namarr_t *ap;
+ Namval_t *nq;
+ cp = nv_endsubscript(np,cp,0);
+ if(c || *cp=='.')
+ {
+ c = '.';
+ while(*cp=='.')
+ {
+ hasdot=1;
+ cp++;
+ while(c=mbchar(cp),isaname(c));
+ }
+ if(c=='[')
+ continue;
+ }
+ flag = *cp;
+ *cp = 0;
+ if(c || hasdot)
+ {
+ sfprintf(shp->strbuf,"%s%s%c",nv_name(np),sub,0);
+ sub = sfstruse(shp->strbuf);
+ }
+ if(strchr(sub,'$'))
+ sub = sh_mactrim(shp,sub,0);
+ *cp = flag;
+ if(c || hasdot)
+ {
+ np = nv_open(sub,shp->var_tree,NV_VARNAME|assign);
+ return(np);
+ }
+#if SHOPT_FIXEDARRAY
+ ap = nv_arrayptr(np);
+ cp = nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE|(ap&&ap->fixed?NV_FARRAY:0));
+#else
+ cp = nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE);
+#endif /* SHOPT_FIXEDARRAY */
+ if(*cp!='[')
+ break;
+ skip:
+ if(nq = nv_opensub(np))
+ np = nq;
+ else
+ {
+ ap = nv_arrayptr(np);
+ if(ap && !ap->table)
+ ap->table = dtopen(&_Nvdisc,Dtoset);
+ if(ap && ap->table && (nq=nv_search(nv_getsub(np),ap->table,NV_ADD)))
+ nq->nvenv = (char*)np;
+ if(nq && nv_isnull(nq))
+ np = nv_arraychild(np,nq,0);
+ }
+ sub = cp;
+ }
+ }
+ else if(nosub>0)
+ nv_putsub(np,(char*)0,nosub-1);
+ return(np);
+}
+
+static Math_f sh_mathstdfun(const char *fname, size_t fsize, short * nargs)
+{
+ register const struct mathtab *tp;
+ register char c = fname[0];
+ for(tp=shtab_math; *tp->fname; tp++)
+ {
+ if(*tp->fname > c)
+ break;
+ if(tp->fname[1]==c && tp->fname[fsize+1]==0 && strncmp(&tp->fname[1],fname,fsize)==0)
+ {
+ if(nargs)
+ *nargs = *tp->fname;
+ return(tp->fnptr);
+ }
+ }
+ return(0);
+}
+
+int sh_mathstd(const char *name)
+{
+ return(sh_mathstdfun(name,strlen(name),NULL)!=0);
+}
+
+static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdouble_t n)
+{
+ Shell_t *shp = lvalue->shp;
+ register Sfdouble_t r= 0;
+ char *str = (char*)*ptr;
+ register char *cp;
+ switch(type)
+ {
+ case ASSIGN:
+ {
+ register Namval_t *np = (Namval_t*)(lvalue->value);
+ np = scope(np,lvalue,1);
+ nv_putval(np, (char*)&n, NV_LDOUBLE);
+ if(lvalue->eflag)
+ lvalue->ptr = (void*)nv_hasdisc(np,&ENUM_disc);
+ lvalue->eflag = 0;
+ r=nv_getnum(np);
+ lvalue->value = (char*)np;
+ break;
+ }
+ case LOOKUP:
+ {
+ register int c = *str;
+ register char *xp=str;
+ lvalue->value = (char*)0;
+ if(c=='.')
+ str++;
+ c = mbchar(str);
+ if(isaletter(c))
+ {
+ register Namval_t *np;
+ int dot=0;
+ while(1)
+ {
+ while(xp=str, c=mbchar(str), isaname(c));
+ str = xp;
+ while(c=='[' && dot==NV_NOADD)
+ {
+ str = nv_endsubscript((Namval_t*)0,str,0);
+ c = *str;
+ }
+ if(c!='.')
+ break;
+ dot=NV_NOADD;
+ if((c = *++str) !='[')
+ continue;
+ str = nv_endsubscript((Namval_t*)0,cp=str,NV_SUBQUOTE)-1;
+ if(sh_checkid(cp+1,(char*)0))
+ str -=2;
+ }
+ if(c=='(')
+ {
+ int off=stktell(shp->stk);
+ int fsize = str- (char*)(*ptr);
+ const struct mathtab *tp;
+ Namval_t *np;
+ c = **ptr;
+ lvalue->fun = 0;
+ sfprintf(shp->stk,".sh.math.%.*s%c",fsize,*ptr,0);
+ stkseek(shp->stk,off);
+ if(np=nv_search(stkptr(shp->stk,off),shp->fun_tree,0))
+ {
+ lvalue->nargs = -np->nvalue.rp->argc;
+ lvalue->fun = (Math_f)np;
+ break;
+ }
+ if(fsize<=(sizeof(tp->fname)-2))
+ lvalue->fun = (Math_f)sh_mathstdfun(*ptr,fsize,&lvalue->nargs);
+ if(lvalue->fun)
+ break;
+ if(lvalue->emode&ARITH_COMP)
+ lvalue->value = (char*)e_function;
+ else
+ lvalue->value = (char*)ERROR_dictionary(e_function);
+ return(r);
+ }
+ if((lvalue->emode&ARITH_COMP) && dot)
+ {
+ lvalue->value = (char*)*ptr;
+ lvalue->flag = str-lvalue->value;
+ break;
+ }
+ *str = 0;
+ if(sh_isoption(SH_NOEXEC))
+ np = L_ARGNOD;
+ else
+ {
+ int offset = staktell();
+ char *saveptr = stakfreeze(0);
+ Dt_t *root = (lvalue->emode&ARITH_COMP)?shp->var_base:shp->var_tree;
+ *str = c;
+ cp = str;
+ while(c=='[' || c=='.')
+ {
+ if(c=='[')
+ {
+ str = nv_endsubscript(np,str,0);
+ if((c= *str)!='[' && c!='.')
+ {
+ str = cp;
+ c = '[';
+ break;
+ }
+ }
+ else
+ {
+ dot = NV_NOADD|NV_NOFAIL;
+ str++;
+ while(xp=str, c=mbchar(str), isaname(c));
+ str = xp;
+ }
+ }
+ *str = 0;
+ cp = (char*)*ptr;
+ if ((cp[0] == 'i' || cp[0] == 'I') && (cp[1] == 'n' || cp[1] == 'N') && (cp[2] == 'f' || cp[2] == 'F') && cp[3] == 0)
+ {
+ Inf = strtold("Inf", NiL);
+ Infnod.nvalue.ldp = &Inf;
+ np = &Infnod;
+ }
+ else if ((cp[0] == 'n' || cp[0] == 'N') && (cp[1] == 'a' || cp[1] == 'A') && (cp[2] == 'n' || cp[2] == 'N') && cp[3] == 0)
+ {
+ NaN = strtold("NaN", NiL);
+ NaNnod.nvalue.ldp = &NaN;
+ np = &NaNnod;
+ }
+ else if(!(np = nv_open(*ptr,root,NV_NOREF|NV_NOASSIGN|NV_VARNAME|dot)))
+ {
+ lvalue->value = (char*)*ptr;
+ lvalue->flag = str-lvalue->value;
+ }
+ if(saveptr != stakptr(0))
+ stakset(saveptr,offset);
+ else
+ stakseek(offset);
+ }
+ *str = c;
+ if(!np && lvalue->value)
+ break;
+ lvalue->value = (char*)np;
+ /* bind subscript later */
+ if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
+ lvalue->isfloat=1;
+ lvalue->flag = 0;
+ if(c=='[')
+ {
+ lvalue->flag = (str-lvalue->expr);
+ do
+ {
+ while(c=='.')
+ {
+ str++;
+ while(xp=str, c=mbchar(str), isaname(c));
+ c = *(str = xp);
+ }
+ if(c=='[')
+ str = nv_endsubscript(np,str,0);
+ }
+ while((c= *str)=='[' || c=='.');
+ break;
+ }
+ }
+ else
+ {
+ char lastbase=0, *val = xp, oerrno = errno;
+ lvalue->eflag = 0;
+ errno = 0;
+ if(shp->bltindata.bnode==SYSLET && !sh_isoption(SH_LETOCTAL))
+ {
+ while(*val=='0' && isdigit(val[1]))
+ val++;
+ }
+ r = strtonll(val,&str, &lastbase,-1);
+ if(*str=='8' || *str=='9')
+ {
+ lastbase=10;
+ errno = 0;
+ r = strtonll(val,&str, &lastbase,-1);
+ }
+ if(lastbase<=1)
+ lastbase=10;
+ if(*val=='0')
+ {
+ while(*val=='0')
+ val++;
+ if(*val==0 || *val=='.' || *val=='x' || *val=='X')
+ val--;
+ }
+ if(r==LLONG_MAX && errno)
+ c='e';
+ else
+ c = *str;
+ if(c==GETDECIMAL(0) || c=='e' || c == 'E' || lastbase ==
+ 16 && (c == 'p' || c == 'P'))
+ {
+ lvalue->isfloat=1;
+ r = strtold(val,&str);
+ }
+ else if(lastbase==10 && val[1])
+ {
+ if(val[2]=='#')
+ val += 3;
+ if((str-val)>2*sizeof(Sflong_t))
+ {
+ Sfdouble_t rr;
+ rr = strtold(val,&str);
+ if(rr!=r)
+ {
+ r = rr;
+ lvalue->isfloat=1;
+ }
+ }
+ }
+ errno = oerrno;
+ }
+ break;
+ }
+ case VALUE:
+ {
+ register Namval_t *np = (Namval_t*)(lvalue->value);
+ if(sh_isoption(SH_NOEXEC))
+ return(0);
+ np = scope(np,lvalue,0);
+ if(!np)
+ {
+ if(sh_isoption(SH_NOUNSET))
+ {
+ *ptr = lvalue->value;
+ goto skip;
+ }
+ return(0);
+ }
+ lvalue->ovalue = (char*)np;
+ if(lvalue->eflag)
+ lvalue->ptr = (void*)nv_hasdisc(np,&ENUM_disc);
+ else if((Namfun_t*)lvalue->ptr && !nv_hasdisc(np,&ENUM_disc) && !nv_isattr(np,NV_INTEGER))
+ {
+ Namval_t *mp,node;
+ mp = ((Namfun_t*)lvalue->ptr)->type;
+ memset(&node,0,sizeof(node));
+ nv_clone(mp,&node,0);
+ nv_offattr(&node,NV_RDONLY|NV_NOFREE);
+ nv_putval(&node,np->nvname,0);
+ if(nv_isattr(&node,NV_NOFREE))
+ return(r=nv_getnum(&node));
+ }
+ lvalue->eflag = 0;
+ if(((lvalue->emode&2) || lvalue->level>1 || sh_isoption(SH_NOUNSET)) && nv_isnull(np) && !nv_isattr(np,NV_INTEGER))
+ {
+ *ptr = nv_name(np);
+ skip:
+ lvalue->value = (char*)ERROR_dictionary(e_notset);
+ lvalue->emode |= 010;
+ return(0);
+ }
+ r = nv_getnum(np);
+ if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY))
+ lvalue->isfloat= (r!=(Sflong_t)r);
+ else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
+ lvalue->isfloat=1;
+ if((lvalue->emode&ARITH_ASSIGNOP) && nv_isarray(np))
+ lvalue->nosub = nv_aindex(np)+1;
+ return(r);
+ }
+
+ case MESSAGE:
+ sfsync(NIL(Sfio_t*));
+#if 0
+ if(warn)
+ errormsg(SH_DICT,ERROR_warn(0),lvalue->value,*ptr);
+ else
+#endif
+ if(lvalue->emode&ARITH_COMP)
+ return(-1);
+
+ errormsg(SH_DICT,ERROR_exit((lvalue->emode&3)!=0),lvalue->value,*ptr);
+ }
+ *ptr = str;
+ return(r);
+}
+
+/*
+ * convert number defined by string to a Sfdouble_t
+ * ptr is set to the last character processed
+ * if mode>0, an error will be fatal with value <mode>
+ */
+
+Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
+{
+ Shell_t *shp = sh_getinterp();
+ register Sfdouble_t d;
+ char base=(shp->inarith?0:10), *last;
+ if(*str==0)
+ {
+ if(ptr)
+ *ptr = (char*)str;
+ return(0);
+ }
+ errno = 0;
+ d = strtonll(str,&last,&base,-1);
+ if(*last || errno)
+ {
+ if(!last || *last!='.' || last[1]!='.')
+ d = strval(shp,str,&last,arith,mode);
+ if(!ptr && *last && mode>0)
+ errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*last,str);
+ }
+ else if (!d && *str=='-')
+ d = -0.0;
+ if(ptr)
+ *ptr = last;
+ return(d);
+}
+
+Sfdouble_t sh_arith(Shell_t *shp,register const char *str)
+{
+ return(sh_strnum(str, (char**)0, 1));
+}
+
+void *sh_arithcomp(Shell_t *shp,register char *str)
+{
+ const char *ptr = str;
+ Arith_t *ep;
+ ep = arith_compile(shp,str,(char**)&ptr,arith,ARITH_COMP|1);
+ if(*ptr)
+ errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*ptr,str);
+ return((void*)ep);
+}
diff --git a/src/cmd/ksh93/sh/array.c b/src/cmd/ksh93/sh/array.c
new file mode 100644
index 0000000..658d5a2
--- /dev/null
+++ b/src/cmd/ksh93/sh/array.c
@@ -0,0 +1,1843 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Array processing routines
+ *
+ * David Korn
+ * AT&T Labs
+ * dgk@research.att.com
+ *
+ */
+
+#include "defs.h"
+#include <stak.h>
+#include "name.h"
+
+#define NUMSIZE (4+(ARRAY_MAX>999)+(ARRAY_MAX>9999)+(ARRAY_MAX>99999))
+#define is_associative(ap) array_assoc((Namarr_t*)(ap))
+#define array_setbit(cp, n, b) (cp[n] |= (b))
+#define array_clrbit(cp, n, b) (cp[n] &= ~(b))
+#define array_isbit(cp, n, b) (cp[n] & (b))
+#define NV_CHILD NV_EXPORT
+#define ARRAY_CHILD 1
+#define ARRAY_NOFREE 2
+
+struct index_array
+{
+ Namarr_t header;
+ void *xp; /* if set, subscripts will be converted */
+ int cur; /* index of current element */
+ int maxi; /* maximum index for array */
+ unsigned char *bits; /* bit array for child subscripts */
+ union Value val[1]; /* array of value holders */
+};
+
+struct assoc_array
+{
+ Namarr_t header;
+ Namval_t *pos;
+ Namval_t *nextpos;
+ Namval_t *cur;
+};
+
+#if SHOPT_FIXEDARRAY
+ struct fixed_array
+ {
+ unsigned char ndim;
+ unsigned char dim;
+ unsigned char level;
+ unsigned char ptr;
+ short size;
+ int nelem;
+ int curi;
+ int *max;
+ int *incr;
+ int *cur;
+ char *data;
+ };
+# define array_fixed_data(ap) ((ap)?((struct fixed_array*)((ap)->fixed))->data:0)
+ static void array_fixed_setdata(Namval_t*,Namarr_t*,struct fixed_array*);
+#endif /* SHOPT_FIXEDARRAY */
+
+static Namarr_t *array_scope(Namval_t *np, Namarr_t *ap, int flags)
+{
+ Namarr_t *aq;
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp;
+#endif /* SHOPT_FIXEDARRAY */
+ struct index_array *ar;
+ size_t size = ap->hdr.dsize;
+ if(size==0)
+ size = ap->hdr.disc->dsize;
+ if(!(aq=newof(NIL(Namarr_t*),Namarr_t,1,size-sizeof(Namarr_t))))
+ return(0);
+ memcpy(aq,ap,size);
+ aq->hdr.nofree &= ~1;
+ aq->hdr.nofree |= (flags&NV_RDONLY)?1:0;
+ if(is_associative(aq))
+ {
+ aq->scope = (void*)dtopen(&_Nvdisc,Dtoset);
+ dtview((Dt_t*)aq->scope,aq->table);
+ aq->table = (Dt_t*)aq->scope;
+ return(aq);
+ }
+#if SHOPT_FIXEDARRAY
+ else if(fp = (struct fixed_array*)ap->fixed)
+ {
+ aq->scope = (void*)ap;
+ fp = (struct fixed_array*)(aq+1);
+ aq->fixed = (void*)fp;
+ fp->max = (int*)(fp+1);
+ fp->incr = fp->max+fp->ndim;
+ fp->cur = fp->incr+fp->ndim;
+ return(aq);
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ aq->scope = (void*)ap;
+ ar = (struct index_array*)aq;
+ memset(ar->val, 0, ar->maxi*sizeof(char*));
+ ar->bits = (unsigned char*)&ar->val[ar->maxi];
+ return(aq);
+}
+
+static int array_unscope(Namval_t *np,Namarr_t *ap)
+{
+ Namfun_t *fp;
+ if(!ap->scope)
+ return(0);
+ if(is_associative(ap))
+ (*ap->fun)(np, NIL(char*), NV_AFREE);
+ if((fp = nv_disc(np,(Namfun_t*)ap,NV_POP)) && !(fp->nofree&1))
+ free((void*)fp);
+ nv_delete(np,(Dt_t*)0,0);
+ return(1);
+}
+
+static void array_syncsub(Namarr_t *ap, Namarr_t *aq)
+{
+ ((struct index_array*)ap)->cur = ((struct index_array*)aq)->cur;
+}
+
+static int array_covered(Namval_t *np, struct index_array *ap)
+{
+ struct index_array *aq = (struct index_array*)ap->header.scope;
+ if(!ap->header.fun && aq)
+#if SHOPT_FIXEDARRAY
+ return (ap->header.fixed || ((ap->cur<aq->maxi) && aq->val[ap->cur].cp));
+#else
+ return ((ap->cur<aq->maxi) && aq->val[ap->cur].cp);
+#endif /* SHOPT_FIXEDARRAY */
+ return(0);
+}
+
+/*
+ * replace discipline with new one
+ */
+static void array_setptr(register Namval_t *np, struct index_array *old, struct index_array *new)
+{
+ register Namfun_t **fp = &np->nvfun;
+ while(*fp && *fp!= &old->header.hdr)
+ fp = &((*fp)->next);
+ if(*fp)
+ {
+ new->header.hdr.next = (*fp)->next;
+ *fp = &new->header.hdr;
+ }
+ else sfprintf(sfstderr,"discipline not replaced\n");
+}
+
+/*
+ * Calculate the amount of space to be allocated to hold an
+ * indexed array into which <maxi> is a legal index. The number of
+ * elements that will actually fit into the array (> <maxi>
+ * but <= ARRAY_MAX) is returned.
+ *
+ */
+static int arsize(struct index_array *ap, register int maxi)
+{
+ if(ap && maxi < 2*ap->maxi)
+ maxi = 2*ap->maxi;
+ maxi = roundof(maxi,ARRAY_INCR);
+ return (maxi>ARRAY_MAX?ARRAY_MAX:maxi);
+}
+
+static struct index_array *array_grow(Namval_t*, struct index_array*,int);
+
+/* return index of highest element of an array */
+int array_maxindex(Namval_t *np)
+{
+ register struct index_array *ap = (struct index_array*)nv_arrayptr(np);
+ register int i = ap->maxi;
+ if(is_associative(ap))
+ return(-1);
+ while(i>0 && ap->val[--i].cp==0);
+ return(i+1);
+}
+
+static union Value *array_getup(Namval_t *np, Namarr_t *arp, int update)
+{
+ register struct index_array *ap = (struct index_array*)arp;
+ register union Value *up;
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp;
+#endif /* SHOPT_FIXEDARRAY */
+ int nofree;
+ if(!arp)
+ return(&np->nvalue);
+ if(is_associative(ap))
+ {
+ Namval_t *mp;
+ mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT));
+ if(mp)
+ {
+ nofree = nv_isattr(mp,NV_NOFREE);
+ up = &mp->nvalue;
+ }
+ else
+ return((union Value*)((*arp->fun)(np,NIL(char*),0)));
+ }
+#if SHOPT_FIXEDARRAY
+ else if(fp = (struct fixed_array*)arp->fixed)
+ {
+ if(!fp->data)
+ array_fixed_setdata(np,arp,fp);
+ up = &np->nvalue;
+ if(fp->ptr)
+ up->cp = *(((char**)fp->data)+fp->curi);
+ else
+ up->cp = fp->data+fp->size*fp->curi;
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ else
+ {
+ if(ap->cur >= ap->maxi)
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np));
+ up = &(ap->val[ap->cur]);
+ nofree = array_isbit(ap->bits,ap->cur,ARRAY_NOFREE);
+ }
+ if(update)
+ {
+ if(nofree)
+ nv_onattr(np,NV_NOFREE);
+ else
+ nv_offattr(np,NV_NOFREE);
+ }
+ return(up);
+}
+
+int nv_arrayisset(Namval_t *np, Namarr_t *arp)
+{
+ register struct index_array *ap = (struct index_array*)arp;
+ union Value *up;
+ if(is_associative(ap))
+ return((np = nv_opensub(np)) && !nv_isnull(np));
+ if(ap->cur >= ap->maxi)
+ return(0);
+ up = &(ap->val[ap->cur]);
+ if(up->cp==Empty)
+ {
+ Namfun_t *fp = &arp->hdr;
+ for(fp=fp->next; fp; fp = fp->next)
+ {
+ if(fp->disc && (fp->disc->getnum || fp->disc->getval))
+ return(1);
+ }
+ }
+ return(up->cp && up->cp!=Empty);
+}
+
+/*
+ * Get the Value pointer for an array.
+ * Delete space as necessary if flag is ARRAY_DELETE
+ * After the lookup is done the last @ or * subscript is incremented
+ */
+static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag)
+{
+ register struct index_array *ap = (struct index_array*)arp;
+ register union Value *up;
+ Namval_t *mp;
+ int wasundef;
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp=(struct fixed_array*)(arp->fixed);
+#endif /* SHOPT_FIXEDARRAY */
+ if(flag&ARRAY_LOOKUP)
+ ap->header.nelem &= ~ARRAY_NOSCOPE;
+ else
+ ap->header.nelem |= ARRAY_NOSCOPE;
+ if(wasundef = ap->header.nelem&ARRAY_UNDEF)
+ {
+ ap->header.nelem &= ~ARRAY_UNDEF;
+ /* delete array is the same as delete array[@] */
+ if(flag&ARRAY_DELETE)
+ {
+#if SHOPT_FIXEDARRAY
+ nv_putsub(np, NIL(char*), ARRAY_SCAN|ARRAY_NOSCOPE|(ap->header.fixed?(ARRAY_UNDEF|ARRAY_FIXED):0));
+#else
+ nv_putsub(np, NIL(char*), ARRAY_SCAN|ARRAY_NOSCOPE);
+#endif /* SHOPT_FIXEDARRAY */
+ ap->header.nelem |= ARRAY_SCAN;
+ }
+ else /* same as array[0] */
+ {
+ if(is_associative(ap))
+ (*ap->header.fun)(np,"0",flag==ARRAY_ASSIGN?NV_AADD:0);
+#if SHOPT_FIXEDARRAY
+ else if(fp)
+ {
+ int n=fp->ndim;
+ fp->curi = 0;
+ while(--n>=0)
+ fp->cur[n] = 0;
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ else
+ ap->cur = 0;
+ }
+ }
+ if(is_associative(ap))
+ {
+ mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT));
+ if(!mp)
+ up = (union Value*)&mp;
+ else if(nv_isarray(mp))
+ {
+ if(wasundef)
+ nv_putsub(mp,NIL(char*),ARRAY_UNDEF);
+ return(mp);
+ }
+ else
+ {
+ up = &mp->nvalue;
+ if(nv_isvtree(mp))
+ {
+ if(!up->cp && flag==ARRAY_ASSIGN)
+ {
+ nv_arraychild(np,mp,0);
+ ap->header.nelem++;
+ }
+ return(mp);
+ }
+ }
+ }
+#if SHOPT_FIXEDARRAY
+ else if(fp)
+ {
+ char *data = array_fixed_data((Namarr_t*)ap->header.scope);
+ if(flag==ARRAY_ASSIGN && data==fp->data)
+ {
+ if(data)
+ {
+ fp->data = (char*)malloc(fp->nelem*fp->size);
+ memcpy(fp->data,data,fp->nelem*fp->size);
+ }
+ else
+ array_fixed_setdata(np,&ap->header,fp);
+ }
+ if(fp->ptr)
+ {
+ if(!fp->data)
+ array_fixed_setdata(np,&ap->header,fp);
+ np->nvalue.cp = *(((char**)fp->data)+fp->curi);
+ }
+ else
+ np->nvalue.cp = fp->data+fp->size*fp->curi;
+ return(np);
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ else
+ {
+ if(!(ap->header.nelem&ARRAY_SCAN) && ap->cur >= ap->maxi)
+ ap = array_grow(np, ap, (int)ap->cur);
+ if(ap->cur>=ap->maxi)
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np));
+ up = &(ap->val[ap->cur]);
+ if((!up->cp||up->cp==Empty) && nv_type(np) && nv_isvtree(np))
+ {
+ char *cp;
+ if(!ap->header.table)
+ ap->header.table = dtopen(&_Nvdisc,Dtoset);
+ sfprintf(sh.strbuf,"%d",ap->cur);
+ cp = sfstruse(sh.strbuf);
+ mp = nv_search(cp, ap->header.table, NV_ADD);
+ mp->nvenv = (char*)np;
+ nv_arraychild(np,mp,0);
+ }
+ if(up->np && array_isbit(ap->bits,ap->cur,ARRAY_CHILD))
+ {
+ if(wasundef && nv_isarray(up->np))
+ nv_putsub(up->np,NIL(char*),ARRAY_UNDEF);
+ return(up->np);
+ }
+ }
+ np->nvalue.cp = up->cp;
+ if(!up->cp)
+ {
+ char *xp = nv_setdisc(np,"get",np,(Namfun_t*)np);
+ if(flag!=ARRAY_ASSIGN)
+ return(xp && xp!=(char*)np?np:0);
+ if(!array_covered(np,ap))
+ ap->header.nelem++;
+ }
+ return(np);
+}
+
+#if SHOPT_TYPEDEF
+int nv_arraysettype(Namval_t *np, Namval_t *tp, const char *sub, int flags)
+{
+ Namval_t *nq;
+ char *av[2];
+ int rdonly = nv_isattr(np,NV_RDONLY);
+ int xtrace = sh_isoption(SH_XTRACE);
+ Namarr_t *ap = nv_arrayptr(np);
+ av[1] = 0;
+ sh.last_table = 0;
+ if(!ap->table)
+ ap->table = dtopen(&_Nvdisc,Dtoset);
+ if(nq = nv_search(sub, ap->table, NV_ADD))
+ {
+ if(!nq->nvfun && nq->nvalue.cp && *nq->nvalue.cp==0)
+ _nv_unset(nq,NV_RDONLY);
+ nv_arraychild(np,nq,0);
+ if(!nv_isattr(tp,NV_BINARY))
+ {
+ sfprintf(sh.strbuf,"%s=%s",nv_name(nq),nv_getval(np));
+ av[0] = strdup(sfstruse(sh.strbuf));
+ }
+ if(!nv_clone(tp,nq,flags|NV_NOFREE))
+ return(0);
+ ap->nelem |= ARRAY_SCAN;
+ if(!rdonly)
+ nv_offattr(nq,NV_RDONLY);
+ if(!nv_isattr(tp,NV_BINARY))
+ {
+ if(xtrace)
+ sh_offoption(SH_XTRACE);
+ ap->nelem &= ~ARRAY_SCAN;
+ sh_eval(sh_sfeval(av),0);
+ ap->nelem |= ARRAY_SCAN;
+ free((void*)av[0]);
+ if(xtrace)
+ sh_onoption(SH_XTRACE);
+ }
+ return(1);
+ }
+ return(0);
+}
+#endif /* SHOPT_TYPEDEF */
+
+
+static Namfun_t *array_clone(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ Namarr_t *ap = (Namarr_t*)fp;
+ Namval_t *nq, *mq;
+ char *name, *sub=0;
+ int nelem, skipped=0;
+ Dt_t *otable=ap->table;
+ struct index_array *aq = (struct index_array*)ap, *ar;
+ Shell_t *shp = sh_getinterp();
+ if(flags&NV_MOVE)
+ {
+ if((flags&NV_COMVAR) && nv_putsub(np,NIL(char*),ARRAY_SCAN))
+ {
+ do
+ {
+ if(nq=nv_opensub(np))
+ nq->nvenv = (void*)mp;
+ }
+ while(nv_nextsub(np));
+ }
+ return(fp);
+ }
+ nelem = ap->nelem;
+ if(nelem&ARRAY_NOCLONE)
+ return(0);
+ if((flags&NV_TYPE) && !ap->scope)
+ {
+ ap = array_scope(np,ap,flags);
+ return(&ap->hdr);
+ }
+ ap = (Namarr_t*)nv_clone_disc(fp,0);
+ if(flags&NV_COMVAR)
+ {
+ ap->scope = 0;
+ ap->nelem = 0;
+ sh.prev_table = sh.last_table;
+ sh.prev_root = sh.last_root;
+ }
+ if(ap->table)
+ {
+ ap->table = dtopen(&_Nvdisc,Dtoset);
+ if(ap->scope && !(flags&NV_COMVAR))
+ {
+ ap->scope = ap->table;
+ dtview(ap->table, otable->view);
+ }
+ }
+ mp->nvfun = (Namfun_t*)ap;
+ mp->nvflag &= NV_MINIMAL;
+ mp->nvflag |= (np->nvflag&~(NV_MINIMAL|NV_NOFREE));
+ if(!(nelem&(ARRAY_SCAN|ARRAY_UNDEF)) && (sub=nv_getsub(np)))
+ sub = strdup(sub);
+ ar = (struct index_array*)ap;
+ if(!is_associative(ap))
+ ar->bits = (unsigned char*)&ar->val[ar->maxi];
+ if(!nv_putsub(np,NIL(char*),ARRAY_SCAN|((flags&NV_COMVAR)?0:ARRAY_NOSCOPE)))
+ {
+ if(ap->fun)
+ (*ap->fun)(np,(char*)np,0);
+ skipped=1;
+ goto skip;
+ }
+ do
+ {
+ name = nv_getsub(np);
+ nv_putsub(mp,name,ARRAY_ADD|ARRAY_NOSCOPE);
+ mq = 0;
+ if(nq=nv_opensub(np))
+ mq = nv_search(name,ap->table,NV_ADD);
+ if(nq && (((flags&NV_COMVAR) && nv_isvtree(nq)) || nv_isarray(nq)))
+ {
+ mq->nvalue.cp = 0;
+ if(!is_associative(ap))
+ ar->val[ar->cur].np = mq;
+ nv_clone(nq,mq,flags);
+ }
+ else if(flags&NV_ARRAY)
+ {
+ if((flags&NV_NOFREE) && !is_associative(ap))
+ array_setbit(aq->bits,aq->cur,ARRAY_NOFREE);
+ else if(nq && (flags&NV_NOFREE))
+ {
+ mq->nvalue = nq->nvalue;
+ nv_onattr(nq,NV_NOFREE);
+ }
+ }
+ else if(nv_isattr(np,NV_INTEGER))
+ {
+ Sfdouble_t d= nv_getnum(np);
+ if(!is_associative(ap))
+ ar->val[ar->cur].cp = 0;
+ nv_putval(mp,(char*)&d,NV_LDOUBLE);
+ }
+ else
+ {
+ if(!is_associative(ap))
+ ar->val[ar->cur].cp = 0;
+ nv_putval(mp,nv_getval(np),NV_RDONLY);
+ }
+ aq->header.nelem |= ARRAY_NOSCOPE;
+ }
+ while(nv_nextsub(np));
+skip:
+ if(sub)
+ {
+ if(!skipped)
+ nv_putsub(np,sub,0L);
+ free((void*)sub);
+ }
+ aq->header.nelem = ap->nelem = nelem;
+ return(&ap->hdr);
+}
+
+static char *array_getval(Namval_t *np, Namfun_t *disc)
+{
+ register Namarr_t *aq,*ap = (Namarr_t*)disc;
+ register Namval_t *mp;
+ register char *cp=0;
+ if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np)
+ {
+ if(!mp && !is_associative(ap) && (aq=(Namarr_t*)ap->scope))
+ {
+ array_syncsub(aq,ap);
+ if((mp=array_find(np,aq,ARRAY_LOOKUP))==np)
+ return(nv_getv(np,&aq->hdr));
+ }
+ if(mp)
+ {
+ cp = nv_getval(mp);
+ nv_offattr(mp,NV_EXPORT);
+ }
+ return(cp);
+ }
+#if SHOPT_FIXEDARRAY
+ if(ap->fixed && nv_isattr(np,NV_INT16P) == NV_INT16)
+ np->nvalue.s = *np->nvalue.sp;
+#endif /* SHOPT_FIXEDARRAY */
+ return(nv_getv(np,&ap->hdr));
+}
+
+static Sfdouble_t array_getnum(Namval_t *np, Namfun_t *disc)
+{
+ register Namarr_t *aq,*ap = (Namarr_t*)disc;
+ register Namval_t *mp;
+ if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np)
+ {
+ if(!mp && !is_associative(ap) && (aq=(Namarr_t*)ap->scope))
+ {
+ array_syncsub(aq,ap);
+ if((mp=array_find(np,aq,ARRAY_LOOKUP))==np)
+ return(nv_getn(np,&aq->hdr));
+ }
+ return(mp?nv_getnum(mp):0);
+ }
+ return(nv_getn(np,&ap->hdr));
+}
+
+static void array_putval(Namval_t *np, const char *string, int flags, Namfun_t *dp)
+{
+ register Namarr_t *ap = (Namarr_t*)dp;
+ register union Value *up;
+ register Namval_t *mp;
+ register struct index_array *aq = (struct index_array*)ap;
+ int scan,nofree = nv_isattr(np,NV_NOFREE);
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp;
+#endif /* SHOPT_FIXEDARRAY */
+ do
+ {
+ int xfree = is_associative(ap)?0:array_isbit(aq->bits,aq->cur,ARRAY_NOFREE);
+ mp = array_find(np,ap,string?ARRAY_ASSIGN:ARRAY_DELETE);
+ scan = ap->nelem&ARRAY_SCAN;
+ if(mp && mp!=np)
+ {
+ if(!is_associative(ap) && string && !(flags&NV_APPEND) && !nv_type(np) && nv_isvtree(mp) && !(ap->nelem&ARRAY_TREE))
+
+ {
+ if(!nv_isattr(np,NV_NOFREE))
+ _nv_unset(mp,flags&NV_RDONLY);
+ array_clrbit(aq->bits,aq->cur,ARRAY_CHILD);
+ aq->val[aq->cur].cp = 0;
+ if(!nv_isattr(mp,NV_NOFREE))
+ nv_delete(mp,ap->table,0);
+ goto skip;
+ }
+ if(!xfree)
+ nv_putval(mp, string, flags);
+ if(string)
+ {
+#if SHOPT_TYPEDEF
+ if(ap->hdr.type && ap->hdr.type!=nv_type(mp))
+ nv_arraysettype(np,ap->hdr.type,nv_getsub(np),0);
+#endif /* SHOPT_TYPEDEF */
+ continue;
+ }
+ ap->nelem |= scan;
+ }
+ if(!string)
+ {
+ if(mp)
+ {
+ if(is_associative(ap))
+ {
+ (*ap->fun)(np,NIL(char*),NV_ADELETE);
+ np->nvalue.cp = 0;
+ }
+ else
+ {
+ if(mp!=np)
+ {
+ array_clrbit(aq->bits,aq->cur,ARRAY_CHILD);
+ aq->val[aq->cur].cp = 0;
+ if(!xfree)
+ nv_delete(mp,ap->table,0);
+ }
+ if(!array_covered(np,(struct index_array*)ap))
+ {
+ if(array_elem(ap))
+ ap->nelem--;
+ }
+#if SHOPT_FIXEDARRAY
+ else if(fp=(struct fixed_array*)ap->fixed)
+ {
+ char *data = array_fixed_data((Namarr_t*)ap->scope);
+ int n = fp->size*fp->curi;
+ if(data)
+ {
+ memcpy(fp->data+n,data+n,fp->size);
+ continue;
+ }
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ }
+ }
+ if(array_elem(ap)==0 && (ap->nelem&ARRAY_SCAN))
+ {
+ if(is_associative(ap))
+ (*ap->fun)(np, NIL(char*), NV_AFREE);
+ else if(ap->table)
+ dtclose(ap->table);
+ nv_offattr(np,NV_ARRAY);
+ }
+ if(!mp || mp!=np || is_associative(ap))
+ continue;
+ }
+ skip:
+ /* prevent empty string from being deleted */
+ up = array_getup(np,ap,!nofree);
+ if(up->cp == Empty)
+ up->cp = 0;
+#if SHOPT_FIXEDARRAY
+ if(nv_isarray(np) && !ap->fixed)
+#else
+ if(nv_isarray(np))
+#endif /* SHOPT_FIXEDARRAY */
+ np->nvalue.up = up;
+ nv_putv(np,string,flags,&ap->hdr);
+#if SHOPT_FIXEDARRAY
+ if(fp = (struct fixed_array*)ap->fixed)
+ {
+ if(fp->ptr)
+ {
+ char **cp = (char**)fp->data;
+ cp[fp->curi] = (char*)(np->nvalue.cp?np->nvalue.cp:Empty);
+ }
+ }
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if(!is_associative(ap))
+ {
+ if(string)
+ array_clrbit(aq->bits,aq->cur,ARRAY_NOFREE);
+ else if(mp==np)
+ aq->val[aq->cur].cp = 0;
+ }
+#if SHOPT_TYPEDEF
+ if(string && ap->hdr.type && nv_isvtree(np))
+ nv_arraysettype(np,ap->hdr.type,nv_getsub(np),0);
+#endif /* SHOPT_TYPEDEF */
+ }
+ while(!string && nv_nextsub(np));
+ if(ap)
+ ap->nelem &= ~ARRAY_NOSCOPE;
+ if(nofree)
+ nv_onattr(np,NV_NOFREE);
+ else
+ nv_offattr(np,NV_NOFREE);
+ if(!string && !nv_isattr(np,NV_ARRAY))
+ {
+ Namfun_t *nfp;
+#if SHOPT_FIXEDARRAY
+ char *data = array_fixed_data((Namarr_t*)ap->scope);
+ fp = (struct fixed_array*)ap->fixed;
+ if(fp && (!ap->scope || data!=fp->data))
+ {
+ if(fp->ptr)
+ {
+ int n = fp->nelem;
+ char **cp = (char**)fp->data;
+ while(n-->0)
+ {
+ if(cp && *cp!=Empty)
+ free(*cp);
+ cp++;
+ }
+ }
+ free((void*)fp->data);
+ if(data)
+ fp->data = data;
+ }
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if(!is_associative(ap) && aq->xp)
+ {
+ _nv_unset(nv_namptr(aq->xp,0),NV_RDONLY);
+ free((void*)aq->xp);
+ }
+ if((nfp = nv_disc(np,(Namfun_t*)ap,NV_POP)) && !(nfp->nofree&1))
+ free((void*)nfp);
+ if(!nv_isnull(np))
+ {
+ nv_onattr(np,NV_NOFREE);
+ _nv_unset(np,flags);
+ }
+ else
+ nv_offattr(np,NV_NOFREE);
+ if(np->nvalue.cp==Empty)
+ np->nvalue.cp = 0;
+ }
+ if(!string && (flags&NV_TYPE))
+ array_unscope(np,ap);
+}
+
+static const Namdisc_t array_disc =
+{
+ sizeof(Namarr_t),
+ array_putval,
+ array_getval,
+ array_getnum,
+ 0,
+ 0,
+ array_clone
+};
+
+static void array_copytree(Namval_t *np, Namval_t *mp)
+{
+ Namfun_t *fp = nv_disc(np,NULL,NV_POP);
+ nv_offattr(np,NV_ARRAY);
+ nv_clone(np,mp,0);
+ if(np->nvalue.cp && !nv_isattr(np,NV_NOFREE))
+ free((void*)np->nvalue.cp);
+ np->nvalue.cp = 0;
+ np->nvalue.up = &mp->nvalue;
+ fp->nofree &= ~1;
+ nv_disc(np,(Namfun_t*)fp, NV_FIRST);
+ fp->nofree |= 1;
+ nv_onattr(np,NV_ARRAY);
+ mp->nvenv = (char*)np;
+}
+
+/*
+ * Increase the size of the indexed array of elements in <arp>
+ * so that <maxi> is a legal index. If <arp> is 0, an array
+ * of the required size is allocated. A pointer to the
+ * allocated Namarr_t structure is returned.
+ * <maxi> becomes the current index of the array.
+ */
+static struct index_array *array_grow(Namval_t *np, register struct index_array *arp,int maxi)
+{
+ register struct index_array *ap;
+ register int i;
+ register int newsize = arsize(arp,maxi+1);
+ if (maxi >= ARRAY_MAX)
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript, fmtbase((long)maxi,10,0));
+ i = (newsize-1)*sizeof(union Value*)+newsize;
+ ap = new_of(struct index_array,i);
+ memset((void*)ap,0,sizeof(*ap)+i);
+ ap->maxi = newsize;
+ ap->cur = maxi;
+ ap->bits = (unsigned char*)&ap->val[newsize];
+ memset(ap->bits, 0, newsize);
+ if(arp)
+ {
+ ap->header = arp->header;
+ ap->header.hdr.dsize = sizeof(*ap) + i;
+ for(i=0;i < arp->maxi;i++)
+ {
+ ap->bits[i] = arp->bits[i];
+ ap->val[i].cp = arp->val[i].cp;
+ }
+ memcpy(ap->bits, arp->bits, arp->maxi);
+ array_setptr(np,arp,ap);
+ free((void*)arp);
+ }
+ else
+ {
+ Namval_t *mp=0;
+ ap->header.hdr.dsize = sizeof(*ap) + i;
+ i = 0;
+ ap->header.fun = 0;
+ if((nv_isnull(np)||np->nvalue.cp==Empty) && nv_isattr(np,NV_NOFREE))
+ {
+ i = ARRAY_TREE;
+ nv_offattr(np,NV_NOFREE);
+ }
+ if(np->nvalue.cp==Empty)
+ np->nvalue.cp=0;
+ if(nv_hasdisc(np,&array_disc) || (nv_type(np) && nv_isvtree(np)))
+ {
+ ap->header.table = dtopen(&_Nvdisc,Dtoset);
+ mp = nv_search("0", ap->header.table,NV_ADD);
+ if(mp && nv_isnull(mp))
+ {
+ Namfun_t *fp;
+ ap->val[0].np = mp;
+ array_setbit(ap->bits,0,ARRAY_CHILD);
+ for(fp=np->nvfun; fp && !fp->disc->readf; fp=fp->next);
+ if(fp && fp->disc && fp->disc->readf)
+ (*fp->disc->readf)(mp,(Sfio_t*)0,0,fp);
+ i++;
+ }
+ }
+ else
+ if((ap->val[0].cp=np->nvalue.cp))
+ i++;
+ else if(nv_isattr(np,NV_INTEGER) && !nv_isnull(np))
+ {
+ Sfdouble_t d= nv_getnum(np);
+ i++;
+ }
+ ap->header.nelem = i;
+ ap->header.hdr.disc = &array_disc;
+ nv_disc(np,(Namfun_t*)ap, NV_FIRST);
+ nv_onattr(np,NV_ARRAY);
+ if(mp)
+ {
+ array_copytree(np,mp);
+ ap->header.hdr.nofree &= ~1;
+ }
+ }
+ for(;i < newsize;i++)
+ ap->val[i].cp = 0;
+ return(ap);
+}
+
+int nv_atypeindex(Namval_t *np, const char *tname)
+{
+ Namval_t *tp;
+ int offset = staktell();
+ int n = strlen(tname)-1;
+ sfprintf(stkstd,"%s.%.*s%c",NV_CLASS,n,tname,0);
+ tp = nv_open(stakptr(offset), sh.var_tree, NV_NOADD|NV_VARNAME);
+ stakseek(offset);
+ if(tp)
+ {
+ struct index_array *ap = (struct index_array*)nv_arrayptr(np);
+ if(!nv_hasdisc(tp,&ENUM_disc))
+ errormsg(SH_DICT,ERROR_exit(1),e_notenum,tp->nvname);
+ if(!ap)
+ ap = array_grow(np,ap,1);
+ ap->xp = calloc(NV_MINSZ,1);
+ np = nv_namptr(ap->xp,0);
+ np->nvname = tp->nvname;
+ nv_onattr(np,NV_MINIMAL);
+ nv_clone(tp,np,NV_NOFREE);
+ nv_offattr(np,NV_RDONLY);
+ return(1);
+ }
+ errormsg(SH_DICT,ERROR_exit(1),e_unknowntype, n,tname);
+ return(0);
+}
+
+Namarr_t *nv_arrayptr(register Namval_t *np)
+{
+ if(nv_isattr(np,NV_ARRAY))
+ return((Namarr_t*)nv_hasdisc(np, &array_disc));
+ return(0);
+}
+
+/*
+ * Verify that argument is an indexed array and convert to associative,
+ * freeing relevant storage
+ */
+static Namarr_t *nv_changearray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int))
+{
+ register Namarr_t *ap;
+ char numbuff[NUMSIZE+1];
+ unsigned dot, digit, n;
+ union Value *up;
+ struct index_array *save_ap;
+ register char *string_index=&numbuff[NUMSIZE];
+ numbuff[NUMSIZE]='\0';
+
+ if(!fun || !(ap = nv_arrayptr(np)) || is_associative(ap))
+ return(NIL(Namarr_t*));
+
+ nv_stack(np,&ap->hdr);
+ save_ap = (struct index_array*)nv_stack(np,0);
+ ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT));
+ ap->nelem = 0;
+ ap->fun = fun;
+ nv_onattr(np,NV_ARRAY);
+
+ for(dot = 0; dot < (unsigned)save_ap->maxi; dot++)
+ {
+ if(save_ap->val[dot].cp)
+ {
+ if ((digit = dot)== 0)
+ *--string_index = '0';
+ else while( n = digit )
+ {
+ digit /= 10;
+ *--string_index = '0' + (n-10*digit);
+ }
+ nv_putsub(np, string_index, ARRAY_ADD);
+ up = (union Value*)((*ap->fun)(np,NIL(char*),0));
+ up->cp = save_ap->val[dot].cp;
+ save_ap->val[dot].cp = 0;
+ }
+ string_index = &numbuff[NUMSIZE];
+ }
+ free((void*)save_ap);
+ return(ap);
+}
+
+/*
+ * set the associative array processing method for node <np> to <fun>
+ * The array pointer is returned if sucessful.
+ */
+Namarr_t *nv_setarray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int))
+{
+ register Namarr_t *ap;
+ char *value=0;
+ Namfun_t *fp;
+ int nelem = 0;
+ if(fun && (ap = nv_arrayptr(np)))
+ {
+ /*
+ * if it's already an indexed array, convert to
+ * associative structure
+ */
+ if(!is_associative(ap))
+ ap = nv_changearray(np, fun);
+ return(ap);
+ }
+ if(nv_isnull(np) && nv_isattr(np,NV_NOFREE))
+ {
+ nelem = ARRAY_TREE;
+ nv_offattr(np,NV_NOFREE);
+ }
+ if(!(fp=nv_isvtree(np)))
+ value = nv_getval(np);
+ if(fun && !ap && (ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT))))
+ {
+ /* check for preexisting initialization and save */
+ ap->nelem = nelem;
+ ap->fun = fun;
+ nv_onattr(np,NV_ARRAY);
+ if(fp || value)
+ {
+ nv_putsub(np, "0", ARRAY_ADD);
+ if(value)
+ nv_putval(np, value, 0);
+ else
+ {
+ Namval_t *mp = (Namval_t*)((*fun)(np,NIL(char*),NV_ACURRENT));
+ array_copytree(np,mp);
+ }
+ }
+ return(ap);
+ }
+ return(NIL(Namarr_t*));
+}
+
+/*
+ * move parent subscript into child
+ */
+Namval_t *nv_arraychild(Namval_t *np, Namval_t *nq, int c)
+{
+ Namfun_t *fp;
+ register Namarr_t *ap = nv_arrayptr(np);
+ union Value *up;
+ Namval_t *tp;
+ if(!nq)
+ return(ap?array_find(np,ap, ARRAY_LOOKUP):0);
+ if(!ap)
+ {
+ nv_putsub(np, NIL(char*), ARRAY_FILL);
+ ap = nv_arrayptr(np);
+ }
+ if(!(up = array_getup(np,ap,0)))
+ return((Namval_t*)0);
+ np->nvalue.cp = up->cp;
+ if((tp=nv_type(np)) || c)
+ {
+ ap->nelem |= ARRAY_NOCLONE;
+ nq->nvenv = (char*)np;
+ if(c=='t')
+ nv_clone(tp,nq, 0);
+ else
+ nv_clone(np, nq, NV_NODISC);
+ nv_offattr(nq,NV_ARRAY);
+ ap->nelem &= ~ARRAY_NOCLONE;
+ }
+ nq->nvenv = (char*)np;
+ if((fp=nq->nvfun) && fp->disc && fp->disc->setdisc && (fp = nv_disc(nq,fp,NV_POP)))
+ free((void*)fp);
+ if(!ap->fun)
+ {
+ struct index_array *aq = (struct index_array*)ap;
+ array_setbit(aq->bits,aq->cur,ARRAY_CHILD);
+ if(c=='.' && !nq->nvalue.cp)
+ ap->nelem++;
+ up->np = nq;
+ }
+ if(c=='.')
+ nv_setvtree(nq);
+ return(nq);
+}
+
+/*
+ * This routine sets subscript of <np> to the next element, if any.
+ * The return value is zero, if there are no more elements
+ * Otherwise, 1 is returned.
+ */
+int nv_nextsub(Namval_t *np)
+{
+ register struct index_array *ap = (struct index_array*)nv_arrayptr(np);
+ register unsigned dot;
+ struct index_array *aq=0, *ar=0;
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp;
+#endif /* SHOPT_FIXEDARRAY */
+ if(!ap || !(ap->header.nelem&ARRAY_SCAN))
+ return(0);
+ if(is_associative(ap))
+ {
+ Namval_t *nq;
+ if(nq=(*ap->header.fun)(np,NIL(char*),NV_ANEXT))
+ {
+ if(nv_isattr(nq,NV_CHILD))
+ nv_putsub(nq->nvalue.np,NIL(char*),ARRAY_UNDEF);
+ return(1);
+ }
+ ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD);
+ return(0);
+ }
+#if SHOPT_FIXEDARRAY
+ else if(fp = (struct fixed_array*)ap->header.fixed)
+ {
+ if(ap->header.nelem&ARRAY_FIXED)
+ {
+ while(++fp->curi < fp->nelem)
+ {
+ nv_putsub(np,0,fp->curi|ARRAY_FIXED|ARRAY_SCAN);
+ if(fp->ptr && *(((char**)fp->data)+fp->curi))
+ return(1);
+ }
+ ap->header.nelem &= ~ARRAY_FIXED;
+ return(0);
+ }
+ dot = fp->dim;
+ if((fp->cur[dot]+1) < fp->max[dot])
+ {
+ fp->cur[dot]++;
+ for(fp->curi=0,dot=0; dot < fp->ndim; dot++)
+ fp->curi += fp->incr[dot]*fp->cur[dot];
+ return(1);
+ }
+ if(fp->level)
+ {
+ dot= --fp->dim;
+ while((dot+1) < fp->ndim)
+ fp->cur[++dot] = 0;
+ fp->level--;
+ fp->curi = 0;
+ }
+ else
+ ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD);
+ return(0);
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ if(!(ap->header.nelem&ARRAY_NOSCOPE))
+ ar = (struct index_array*)ap->header.scope;
+ for(dot=ap->cur+1; dot < (unsigned)ap->maxi; dot++)
+ {
+ aq = ap;
+ if(!ap->val[dot].cp && !(ap->header.nelem&ARRAY_NOSCOPE))
+ {
+ if(!(aq=ar) || dot>=(unsigned)aq->maxi)
+ continue;
+ }
+ if(aq->val[dot].cp==Empty && array_elem(&aq->header) < nv_aimax(np)+1) {
+ ap->cur = dot;
+ if(nv_getval(np)==Empty)
+ continue;
+ }
+ if(aq->val[dot].cp)
+ {
+ ap->cur = dot;
+ if(array_isbit(aq->bits, dot,ARRAY_CHILD))
+ {
+ Namval_t *mp = aq->val[dot].np;
+ if((aq->header.nelem&ARRAY_NOCHILD) && nv_isvtree(mp) && !mp->nvfun->dsize)
+ continue;
+ if(nv_isarray(mp))
+ nv_putsub(mp,NIL(char*),ARRAY_SCAN);
+ }
+ return(1);
+ }
+ }
+ ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD);
+ ap->cur = 0;
+ return(0);
+}
+
+/*
+ * Set an array subscript for node <np> given the subscript <sp>
+ * An array is created if necessary.
+ * <mode> can be a number, plus or more of symbolic constants
+ * ARRAY_SCAN, ARRAY_UNDEF, ARRAY_ADD
+ * The node pointer is returned which can be NULL if <np> is
+ * not already array and the ARRAY_ADD bit of <mode> is not set.
+ * ARRAY_FILL sets the specified subscript to the empty string when
+ * ARRAY_ADD is specified and there is no value or sets all
+ * the elements up to the number specified if ARRAY_ADD is not specified
+ */
+Namval_t *nv_putsub(Namval_t *np,register char *sp,register long mode)
+{
+ register struct index_array *ap = (struct index_array*)nv_arrayptr(np);
+ register int size = (mode&ARRAY_MASK);
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp;
+ if(!ap || (!ap->header.fixed && !ap->header.fun))
+#else
+ if(!ap || !ap->header.fun)
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ if(sp)
+ {
+ Shell_t *shp = sh_getinterp();
+ if(ap && ap->xp && !strmatch(sp,"+([0-9])"))
+ {
+ Namval_t *mp = nv_namptr(ap->xp,0);
+ nv_putval(mp, sp,0);
+ size = nv_getnum(mp);
+ }
+ else
+ size = (int)sh_arith(shp,(char*)sp);
+ }
+ if(size <0 && ap)
+ size += array_maxindex(np);
+ if(size >= ARRAY_MAX || (size < 0))
+ {
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np));
+ return(NIL(Namval_t*));
+ }
+ if(!ap || size>=ap->maxi)
+ {
+ if(size==0 && !(mode&ARRAY_FILL))
+ return(NIL(Namval_t*));
+ if(sh.subshell)
+ np = sh_assignok(np,1);
+ ap = array_grow(np, ap,size);
+ }
+ ap->header.nelem &= ~ARRAY_UNDEF;
+ ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF|ARRAY_NOSCOPE));
+#if 0
+ if(array_isbit(ap->bits,oldsize,ARRAY_CHILD))
+ mp = ap->val[oldsize].np;
+ if(size != oldsize && mp->nvalue.cp)
+ {
+ Namfun_t *nfp;
+ for(nfp=np->nvfun; nfp; nfp=nfp->next)
+ {
+ if(nfp->disc && nfp->disc->readf)
+ {
+ (*nfp->disc->readf)(mp,(Sfio_t*)0,0,nfp);
+ break;
+ }
+ }
+ }
+#endif
+ ap->cur = size;
+ if((mode&ARRAY_SCAN) && (ap->cur--,!nv_nextsub(np)))
+ np = 0;
+ if(mode&(ARRAY_FILL|ARRAY_ADD))
+ {
+ if(!(mode&ARRAY_ADD))
+ {
+ int n;
+ if(mode&ARRAY_SETSUB)
+ {
+ for(n=0; n <= ap->maxi; n++)
+ ap->val[n].cp = 0;
+ ap->header.nelem = 0;
+ }
+ for(n=0; n <= size; n++)
+ {
+ if(!ap->val[n].cp)
+ {
+ ap->val[n].cp = Empty;
+ if(!array_covered(np,ap))
+ ap->header.nelem++;
+ }
+ }
+ if(n=ap->maxi-ap->maxi)
+ memset(&ap->val[size],0,n*sizeof(union Value));
+ }
+ else if(!(sp=(char*)ap->val[size].cp) || sp==Empty)
+ {
+ if(sh.subshell)
+ np = sh_assignok(np,1);
+ if(ap->header.nelem&ARRAY_TREE)
+ {
+ char *cp;
+ Namval_t *mp;
+ if(!ap->header.table)
+ ap->header.table = dtopen(&_Nvdisc,Dtoset);
+ sfprintf(sh.strbuf,"%d",ap->cur);
+ cp = sfstruse(sh.strbuf);
+ mp = nv_search(cp, ap->header.table, NV_ADD);
+ mp->nvenv = (char*)np;
+ nv_arraychild(np,mp,0);
+ nv_setvtree(mp);
+ }
+ else
+ ap->val[size].cp = Empty;
+ if(!sp && !array_covered(np,ap))
+ ap->header.nelem++;
+ }
+ }
+ else if(!(mode&ARRAY_SCAN))
+ {
+ ap->header.nelem &= ~ARRAY_SCAN;
+ if(array_isbit(ap->bits,size,ARRAY_CHILD))
+ nv_putsub(ap->val[size].np,NIL(char*),ARRAY_UNDEF);
+ if(sp && !(mode&ARRAY_ADD) && !ap->val[size].cp)
+ np = 0;
+ }
+ return((Namval_t*)np);
+ }
+#if SHOPT_FIXEDARRAY
+ if(fp=(struct fixed_array*)ap->header.fixed)
+ {
+ if(!fp->data)
+ return(np);
+ if(mode&ARRAY_UNDEF)
+ {
+ fp->dim = 0;
+ fp->curi = 0;
+ for(size=fp->ndim;--size>=0;)
+ fp->cur[size] = 0;
+ ap->header.nelem &= ~ARRAY_MASK;
+ if(mode&ARRAY_FIXED)
+ {
+ mode &= ~ARRAY_UNDEF;
+ ap->header.nelem |= (ARRAY_FIXED|fp->nelem);
+ }
+ else
+ ap->header.nelem |= fp->max[0];
+ }
+ else if(mode&ARRAY_FIXED)
+ {
+ size = (mode&ARRAY_MASK)&~(ARRAY_FIXED);
+ fp->curi = size;
+ for(fp->dim=0;size>0 && fp->dim<fp->ndim; fp->dim++)
+ {
+ fp->cur[fp->dim] = size/fp->incr[fp->dim];
+ size -= fp->incr[fp->dim]*fp->cur[fp->dim];
+ }
+ while(fp->dim < fp->ndim)
+ fp->cur[fp->dim++] = 0;
+ fp->dim = ap->header.nelem;
+ ap->header.nelem |= ARRAY_FIXED;
+ }
+ else if(fp->dim< fp->ndim)
+ {
+ fp->curi += (size-fp->cur[fp->dim])*fp->incr[fp->dim];
+ fp->cur[fp->dim] = size;
+ }
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ ap->header.nelem &= ~ARRAY_UNDEF;
+ if(!(mode&ARRAY_FILL))
+ ap->header.nelem &= ~ARRAY_SCAN;
+ ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF|ARRAY_NOSCOPE));
+#if SHOPT_FIXEDARRAY
+ if(fp)
+ return(np);
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if(sp)
+ {
+ if(mode&ARRAY_SETSUB)
+ {
+ (*ap->header.fun)(np, sp, NV_ASETSUB);
+ return(np);
+ }
+ (*ap->header.fun)(np, sp, (mode&ARRAY_ADD)?NV_AADD:0);
+ if(!(mode&(ARRAY_SCAN|ARRAY_ADD)) && !(*ap->header.fun)(np,NIL(char*),NV_ACURRENT))
+ np = 0;
+ }
+ else if(mode&ARRAY_SCAN)
+ (*ap->header.fun)(np,(char*)np,0);
+ else if(mode&ARRAY_UNDEF)
+ (*ap->header.fun)(np, "",0);
+ if((mode&ARRAY_SCAN) && !nv_nextsub(np))
+ np = 0;
+ return(np);
+}
+
+#if SHOPT_FIXEDARRAY
+int nv_arrfixed(Namval_t *np, Sfio_t *out, int flag, char *dim)
+{
+ Namarr_t *ap = nv_arrayptr(np);
+ struct fixed_array *fp = (struct fixed_array*)ap->fixed;
+ int n;
+ if(flag)
+ {
+ if(out)
+ {
+ for(n=0; n < fp->dim; n++)
+ sfprintf(out,"[%d]",fp->cur[n]);
+ }
+ if(dim)
+ *dim = fp->dim;
+ return(fp->curi);
+ }
+ if(out)
+ {
+ for(n=0; n < fp->ndim; n++)
+ sfprintf(out,"[%d]",fp->max[n]);
+ }
+ fp->dim = 0;
+ return(fp->curi);
+}
+
+static void array_fixed_setdata(Namval_t *np,Namarr_t* ap,struct fixed_array* fp)
+{
+ int n = ap->nelem;
+ ap->nelem = 1;
+ fp->size = fp->ptr?sizeof(void*):nv_datasize(np,0);
+ ap->nelem = n;
+ fp->data = (char*)calloc(fp->nelem,fp->size);
+ if(fp->ptr)
+ {
+ char **cp = (char**)fp->data;
+ for(n=fp->nelem; n-->0;)
+ *cp++ = Empty;
+ }
+}
+
+static int array_fixed_init(Namval_t *np, char *sub, char *cp)
+{
+ Shell_t *shp=sh_getinterp();
+ Namarr_t *ap;
+ struct fixed_array *fp;
+ int n=1,sz;
+ char *ep=cp;
+ while(*ep=='[')
+ {
+ ep = nv_endsubscript(np,ep,0);
+ n++;
+ }
+ if(*ep)
+ return(0);
+ sz = sizeof(struct fixed_array)+ 3*n*sizeof(int);
+ if(!(ap=newof(NIL(Namarr_t*),Namarr_t,1,sz)))
+ return(0);
+ ap->hdr.disc = &array_disc;
+ ap->hdr.dsize = sizeof(Namarr_t)+sz;
+ ap->hdr.nofree &= ~1;
+ fp = (struct fixed_array*)(ap+1);
+ ap->fixed = (void*)fp;
+ fp->ndim = n;
+ fp->max = (int*)(fp+1);
+ fp->incr = fp->max+n;
+ fp->cur = fp->incr+n;
+ fp->max[0] = (int)sh_arith(shp,(char*)sub);
+ for(n=1,ep=cp;*ep=='['; ep=cp)
+ {
+ cp = nv_endsubscript(np,ep,0);
+ cp[-1]=0;
+ fp->max[n++] = sz = (int)sh_arith(shp,(char*)ep+1);
+ if(sz<0)
+ {
+ free((void*)ap);
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np));
+ }
+ cp[-1] = ']';
+ }
+ nv_disc(np,(Namfun_t*)ap, NV_FIRST);
+ fp->ptr = !np->nvsize;
+ nv_onattr(np,NV_ARRAY|(fp->ptr?0:NV_NOFREE));
+ fp->incr[n=fp->ndim-1] = 1;
+ for(sz=1; --n>=0;)
+ sz = fp->incr[n] = sz*fp->max[n+1];
+ fp->nelem = sz*fp->max[0];
+ ap->nelem = fp->max[0];
+ return(1);
+}
+
+static char *array_fixed(Namval_t *np, char *sub, char *cp,int mode)
+{
+ Shell_t *shp=sh_getinterp();
+ Namarr_t *ap = nv_arrayptr(np);
+ struct fixed_array *fp = (struct fixed_array*)ap->fixed;
+ char *ep;
+ int size,n=0,sz;
+ if(!fp->data)
+ array_fixed_setdata(np,ap,fp);
+ ap->nelem &= ~ARRAY_UNDEF;
+ if(ap->nelem&ARRAY_FIXED)
+ {
+ ap->nelem &= ~ARRAY_FIXED;
+ n = fp->dim;
+ sz = fp->curi;
+ if(*sub==0)
+ goto skip;
+ }
+ else
+ fp->curi = 0;
+ size = (int)sh_arith(shp,(char*)sub);
+ fp->cur[n] = size;
+ if(size >= fp->max[n] || (size < 0))
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np));
+ *cp++ = ']';
+ sz = fp->curi + fp->cur[n]*fp->incr[n];
+ for(n++,ep=cp;*ep=='['; ep=cp,n++)
+ {
+ if(n >= fp->ndim)
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np));
+ cp = nv_endsubscript(np,ep,0);
+ cp[-1]=0;
+ size = (int)sh_arith(shp,(char*)ep+1);
+ if(size >= fp->max[n] || (size < 0))
+ errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np));
+ fp->cur[n] = size;
+ cp[-1] = ']';
+ sz += fp->cur[n]*fp->incr[n];
+ }
+skip:
+ fp->dim = n;
+ ap->nelem &= ~ARRAY_MASK;
+ ap->nelem |= fp->max[n];
+ while(n < fp->ndim)
+ fp->cur[n++] = 0;
+ fp->curi = sz;
+ return(cp-1);
+}
+#endif /* SHOPT_FIXEDARRAY */
+
+/*
+ * process an array subscript for node <np> given the subscript <cp>
+ * returns pointer to character after the subscript
+ */
+char *nv_endsubscript(Namval_t *np, register char *cp, int mode)
+{
+ register int count=1, quoted=0, c;
+ register char *sp = cp+1;
+ /* first find matching ']' */
+ while(count>0 && (c= *++cp))
+ {
+ if(c=='\\' && (!(mode&NV_SUBQUOTE) || (c=cp[1])=='[' || c==']' || c=='\\' || c=='*' || c=='@'))
+ {
+ quoted=1;
+ cp++;
+ }
+ else if(c=='[')
+ count++;
+ else if(c==']')
+ count--;
+ }
+ *cp = 0;
+ if(quoted)
+ {
+ /* strip escape characters */
+ count = staktell();
+ stakwrite(sp,1+cp-sp);
+ sh_trim(sp=stakptr(count));
+ }
+ if(mode && np)
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ int scan = 0;
+#if SHOPT_FIXEDARRAY
+ if((mode&NV_FARRAY) && !nv_isarray(np))
+ {
+ if(array_fixed_init(np,sp,cp+1))
+ {
+ *cp++ = c;
+ return(strchr(cp,0));
+ }
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ if(ap)
+ scan = ap->nelem&ARRAY_SCAN;
+ if((mode&NV_ASSIGN) && (cp[1]=='=' || cp[1]=='+'))
+ mode |= NV_ADD;
+ else if(ap && cp[1]=='.' && (mode&NV_FARRAY))
+ mode |= NV_ADD;
+#if SHOPT_FIXEDARRAY
+ if(ap && ap->fixed)
+ cp = array_fixed(np,sp,cp,mode);
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ nv_putsub(np, sp, ((mode&NV_ADD)?ARRAY_ADD:0)|(cp[1]&&(mode&NV_ADD)?ARRAY_FILL:mode&ARRAY_FILL));
+ if(scan)
+ ap->nelem |= scan;
+ }
+ if(quoted)
+ stakseek(count);
+ *cp++ = c;
+ return(cp);
+}
+
+
+Namval_t *nv_opensub(Namval_t* np)
+{
+ register struct index_array *ap = (struct index_array*)nv_arrayptr(np);
+#if SHOPT_FIXEDARRAY
+ struct fixed_array *fp;
+#endif /* SHOPT_FIXEDARRAY */
+ if(ap)
+ {
+ if(is_associative(ap))
+ return((Namval_t*)((*ap->header.fun)(np,NIL(char*),NV_ACURRENT)));
+#if SHOPT_FIXEDARRAY
+ else if(!(fp=(struct fixed_array*)ap->header.fixed) && array_isbit(ap->bits,ap->cur,ARRAY_CHILD))
+#else
+ else if(array_isbit(ap->bits,ap->cur,ARRAY_CHILD))
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ return(ap->val[ap->cur].np);
+ }
+#if SHOPT_FIXEDARRAY
+ else if(fp)
+ {
+ int n = fp->dim;
+ if((fp->dim+1) < fp->ndim)
+ {
+ fp->dim++;
+ if(ap->header.nelem&ARRAY_SCAN)
+ {
+ while(++n < fp->ndim)
+ fp->cur[n] = 0;
+ fp->level++;
+ }
+ return(np);
+ }
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ }
+ return(NIL(Namval_t*));
+}
+
+char *nv_getsub(Namval_t* np)
+{
+ static char numbuff[NUMSIZE];
+ register struct index_array *ap;
+ register unsigned dot, n;
+ register char *cp = &numbuff[NUMSIZE];
+ if(!np || !(ap = (struct index_array*)nv_arrayptr(np)))
+ return(NIL(char*));
+ if(is_associative(ap))
+ return((char*)((*ap->header.fun)(np,NIL(char*),NV_ANAME)));
+ if(ap->xp)
+ {
+ np = nv_namptr(ap->xp,0);
+ np->nvalue.s = ap->cur;
+ return(nv_getval(np));
+ }
+ if((dot = ap->cur)==0)
+ *--cp = '0';
+ else while(n=dot)
+ {
+ dot /= 10;
+ *--cp = '0' + (n-10*dot);
+ }
+ return(cp);
+}
+
+/*
+ * If <np> is an indexed array node, the current subscript index
+ * returned, otherwise returns -1
+ */
+int nv_aindex(register Namval_t* np)
+{
+ Namarr_t *ap = nv_arrayptr(np);
+ if(!ap)
+ return(0);
+ else if(is_associative(ap))
+ return(-1);
+#if SHOPT_FIXEDARRAY
+ else if(ap->fixed)
+ return(-1);
+#endif /* SHOPT_FIXEDARRAY */
+ return(((struct index_array*)(ap))->cur&ARRAY_MASK);
+}
+
+int nv_arraynsub(register Namarr_t* ap)
+{
+ return(array_elem(ap));
+}
+
+int nv_aimax(register Namval_t* np)
+{
+ struct index_array *ap = (struct index_array*)nv_arrayptr(np);
+ int sub = -1;
+#if SHOPT_FIXEDARRAY
+ if(!ap || is_associative(&ap->header) || ap->header.fixed)
+#else
+ if(!ap || is_associative(&ap->header))
+#endif /* SHOPT_FIXEDARRAY */
+ return(-1);
+ sub = ap->maxi;
+ while(--sub>0 && ap->val[sub].cp==0);
+ return(sub);
+}
+
+/*
+ * This is the default implementation for associative arrays
+ */
+void *nv_associative(register Namval_t *np,const char *sp,int mode)
+{
+ register struct assoc_array *ap = (struct assoc_array*)nv_arrayptr(np);
+ register int type;
+ switch(mode)
+ {
+ case NV_AINIT:
+ if(ap = (struct assoc_array*)calloc(1,sizeof(struct assoc_array)))
+ {
+ ap->header.table = dtopen(&_Nvdisc,Dtoset);
+ ap->cur = 0;
+ ap->pos = 0;
+ ap->header.hdr.disc = &array_disc;
+ nv_disc(np,(Namfun_t*)ap, NV_FIRST);
+ ap->header.hdr.dsize = sizeof(struct assoc_array);
+ ap->header.hdr.nofree &= ~1;
+ }
+ return((void*)ap);
+ case NV_ADELETE:
+ if(ap->cur)
+ {
+ if(!ap->header.scope || (Dt_t*)ap->header.scope==ap->header.table || !nv_search(ap->cur->nvname,(Dt_t*)ap->header.scope,0))
+ ap->header.nelem--;
+ _nv_unset(ap->cur,NV_RDONLY);
+ nv_delete(ap->cur,ap->header.table,0);
+ ap->cur = 0;
+ }
+ return((void*)ap);
+ case NV_AFREE:
+ ap->pos = 0;
+ if(ap->header.scope)
+ {
+ ap->header.table = dtview(ap->header.table,(Dt_t*)0);
+ dtclose(ap->header.scope);
+ ap->header.scope = 0;
+ }
+ else
+ dtclose(ap->header.table);
+ return((void*)ap);
+ case NV_ANEXT:
+ if(!ap->pos)
+ {
+ if((ap->header.nelem&ARRAY_NOSCOPE) && ap->header.scope && dtvnext(ap->header.table))
+ {
+ ap->header.scope = dtvnext(ap->header.table);
+ ap->header.table->view = 0;
+ }
+ if(!(ap->pos=ap->cur))
+ ap->pos = (Namval_t*)dtfirst(ap->header.table);
+ }
+ else
+ ap->pos = ap->nextpos;
+ for(;ap->cur=ap->pos; ap->pos=ap->nextpos)
+ {
+ ap->nextpos = (Namval_t*)dtnext(ap->header.table,ap->pos);
+ if(!nv_isnull(ap->cur))
+ {
+ if((ap->header.nelem&ARRAY_NOCHILD) && nv_isattr(ap->cur,NV_CHILD))
+ continue;
+ return((void*)ap);
+ }
+ }
+ if((ap->header.nelem&ARRAY_NOSCOPE) && ap->header.scope && !dtvnext(ap->header.table))
+ {
+ ap->header.table->view = (Dt_t*)ap->header.scope;
+ ap->header.scope = ap->header.table;
+ }
+ return(NIL(void*));
+ case NV_ASETSUB:
+ ap->cur = (Namval_t*)sp;
+ return((void*)ap->cur);
+ case NV_ACURRENT:
+ if(ap->cur)
+ ap->cur->nvenv = (char*)np;
+ return((void*)ap->cur);
+ case NV_ANAME:
+ if(ap->cur)
+ {
+ Shell_t *shp = sh_getinterp();
+ if(!shp->instance && nv_isnull(ap->cur))
+ return(NIL(void*));
+ return((void*)ap->cur->nvname);
+ }
+ return(NIL(void*));
+ default:
+ if(sp)
+ {
+ Namval_t *mp=0;
+ ap->cur = 0;
+ if(sp==(char*)np)
+ return(0);
+ type = nv_isattr(np,NV_PUBLIC&~(NV_ARRAY|NV_CHILD|NV_MINIMAL));
+ if(mode)
+ mode = NV_ADD|HASH_NOSCOPE;
+ else if(ap->header.nelem&ARRAY_NOSCOPE)
+ mode = HASH_NOSCOPE;
+ if(*sp==0 && sh_isoption(SH_XTRACE) && (mode&NV_ADD))
+ errormsg(SH_DICT,ERROR_warn(0),"adding empty subscript");
+ if(sh.subshell && (mp=nv_search(sp,ap->header.table,0)) && nv_isnull(mp))
+ ap->cur = mp;
+ if((mp || (mp=nv_search(sp,ap->header.table,mode))) && nv_isnull(mp) && (mode&NV_ADD))
+ {
+ nv_onattr(mp,type);
+ mp->nvenv = (char*)np;
+ if((mode&NV_ADD) && nv_type(np))
+ nv_arraychild(np,mp,0);
+ if(sh.subshell)
+ np = sh_assignok(np,1);
+ if(!ap->header.scope || !nv_search(sp,dtvnext(ap->header.table),0))
+ ap->header.nelem++;
+ if(nv_isnull(mp))
+ {
+ if(ap->header.nelem&ARRAY_TREE)
+ nv_setvtree(mp);
+ mp->nvalue.cp = Empty;
+ }
+ }
+ else if(ap->header.nelem&ARRAY_SCAN)
+ {
+ Namval_t fake;
+ fake.nvname = (char*)sp;
+ ap->pos = mp = (Namval_t*)dtprev(ap->header.table,&fake);
+ ap->nextpos = (Namval_t*)dtnext(ap->header.table,mp);
+ }
+ else if(!mp && *sp && mode==0)
+ mp = nv_search(sp,ap->header.table,NV_ADD|HASH_NOSCOPE);
+ np = mp;
+ if(ap->pos && ap->pos==np)
+ ap->header.nelem |= ARRAY_SCAN;
+ else if(!(ap->header.nelem&ARRAY_SCAN))
+ ap->pos = 0;
+ ap->cur = np;
+ }
+ if(ap->cur)
+ return((void*)(&ap->cur->nvalue));
+ else
+ return((void*)(&ap->cur));
+ }
+}
+
+/*
+ * Assign values to an array
+ */
+void nv_setvec(register Namval_t *np,int append,register int argc,register char *argv[])
+{
+ int arg0=0;
+ struct index_array *ap=0,*aq;
+ if(nv_isarray(np))
+ {
+ ap = (struct index_array*)nv_arrayptr(np);
+ if(ap && is_associative(ap))
+ errormsg(SH_DICT,ERROR_exit(1),"cannot append index array to associative array %s",nv_name(np));
+ }
+ if(append)
+ {
+ if(ap)
+ {
+ if(!(aq = (struct index_array*)ap->header.scope))
+ aq = ap;
+ arg0 = ap->maxi;
+ while(--arg0>0 && ap->val[arg0].cp==0 && aq->val[arg0].cp==0);
+ arg0++;
+ }
+ else if(!nv_isnull(np))
+ arg0=1;
+ }
+ while(--argc >= 0)
+ {
+ nv_putsub(np,NIL(char*),(long)argc+arg0|ARRAY_FILL|ARRAY_ADD);
+ nv_putval(np,argv[argc],0);
+ }
+}
+
diff --git a/src/cmd/ksh93/sh/bash.c b/src/cmd/ksh93/sh/bash.c
new file mode 100644
index 0000000..fd2f3a1
--- /dev/null
+++ b/src/cmd/ksh93/sh/bash.c
@@ -0,0 +1,423 @@
+/***********************************************************************
+* *
+* 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> *
+* *
+***********************************************************************/
+/*
+ * bash specific extensions
+ * originally provided by Karsten Fleischer
+ */
+
+#include "defs.h"
+#include "path.h"
+#include "io.h"
+#include "builtins.h"
+#include "name.h"
+
+#ifndef BASH_MAJOR
+# define BASH_MAJOR "1"
+# define BASH_MINOR "0"
+# define BASH_PATCH "0"
+# define BASH_BUILD "0"
+# define BASH_RELEASE "experimental"
+#endif
+#define BASH_VERSION BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE
+
+
+extern const char bash_pre_rc[];
+
+static char *login_files[4];
+
+const char sh_bash1[] =
+ "[B?Enable brace group expansion. This option is only availabe in bash "
+ "compatibility mode. In ksh mode, brace group expansion is always on.]"
+ "[P?Do not follow symbolic links, use physical directory structure "
+ "instead. Only available in bash compatibility mode.]";
+const char sh_bash2[] =
+"[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by "
+ "the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets "
+ "the value of that option; \b+O\b unsets it. If \ashopt_option\a is "
+ "not supplied, the names and values of the shell options accepted by "
+ "\bshopt\b are printed on the standard output. If the invocation "
+ "option is \b+O\b, the output is displayed in a format that may be "
+ "reused as input. Only available if invoked as \bbash\b.]"
+"[01:init-file|rcfile]:[file?Execute commands from \afile\a instead of the "
+ "standard personal initialization file ~/.bashrc if the shell is "
+ "interactive. Only available if invoked as \bbash\b.]"
+"[02:editing?For option compatibility with \bbash\b only. Ignored.]"
+"[03:profile?Read either the system-wide startup file or any of the "
+ "personal initialization files. On by default for interactive "
+ "shells. Only available if invoked as \bbash\b.]"
+"[04:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in "
+ "POSIX mode is not the same as \bksh\b.]"
+"[05:version?Print version number and exit.]";
+
+const char sh_optshopt[] =
+"+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]"
+"[-author?Karsten Fleischer <K.Fleischer@omnium.de>]"
+USAGE_LICENSE
+"[+NAME?shopt - set/unset variables controlling optional shell behavior]"
+"[+DESCRIPTION?\bshopt\b sets or unsets variables controlling optional shell "
+ "behavior. With no options, or with the \b-p\b option, a list of all "
+ "settable options is displayed, with an indication of whether or not "
+ "each is set.]"
+"[p?Causes output to be displayed in a form that may be reused as input.]"
+"[s?Set each \aoptname\a.]"
+"[u?Unset each \aoptname\a.]"
+"[q?Suppress output (quiet mode). The return status indicates whether the "
+ "\aoptname\a is set or unset. If multiple \aoptname\a arguments are "
+ "given with \b-q\b, the return status is zero if all \aoptname\as are "
+ "enabled; non-zero otherwise.]"
+"[o?Restricts the values of \aoptname\a to be those defined for the \b-o\b "
+ "option to the set builtin.]"
+"[+?If either \b-s\b or \b-u\b is used with no \aoptname\a arguments, the "
+ "display is limited to those options which are set or unset.]"
+"[+?\bshopt\b supports all bash options. Some settings do not have any effect "
+ "or are are always on and cannot be changed.]"
+"[+?The value of \aoptname\a must be one of the following:]{"
+ "[+cdable_vars?If set, arguments to the \bcd\b command are "
+ "assumed to be names of variables whose values are to "
+ "be used if the usual \bcd\b proceeding fails.]"
+ "[+cdspell?Currently ignored.]"
+ "[+checkhash?Always on.]"
+ "[+checkwinsize?Currently ignored.]"
+ "[+cmdhist?Always on.]"
+ "[+dotglob?If set, include filenames beginning with a \b.\b "
+ "in the results of pathname expansion.]"
+ "[+execfail?Always on.]"
+ "[+expand_aliases?Always on.]"
+ "[+extglob?Enable extended pattern matching features.]"
+ "[+histappend?Always on.]"
+ "[+histreedit?If set and an edit mode is selected, the user "
+ "is given the opportunity to re-edit a failed history "
+ "substitution.]"
+ "[+histverify?If set and an edit mode is selected, the result "
+ "of a history substitution will not be executed "
+ "immediately but be placed in the edit buffer for "
+ "further modifications.]"
+ "[+hostcomplete?Currently ignored.]"
+ "[+huponexit?Currently ignored.]"
+ "[+interactive_comments?Always on.]"
+ "[+lithist?Always on.]"
+ "[+login_shell?This option is set if the shell is started as "
+ "a login shell. The value cannot be changed.]"
+ "[+mailwarn?Currently ignored.]"
+ "[+no_empty_cmd_completion?Always on.]"
+ "[+nocaseglob?Match filenames in a case-insensitive fashion "
+ "when performing filename expansion.]"
+ "[+nullglob?Allows filename patterns which match no files to "
+ "expand to a null string, rather than themselves.]"
+ "[+progcomp?Currently ignored.]"
+ "[+promptvars?Currently ignored.]"
+ "[+restricted_shell?This option is set if the shell is started "
+ "as a restricted shell. The value cannot be changed. "
+ "It is not reset during execution of startup files, "
+ "allowing the startup files to determine whether the "
+ "shell is restricted.]"
+ "[+shift_verbose?Currently ignored.]"
+ "[+sourcepath?If set, the \b.\b builtin uses the value of PATH "
+ "to find the directory containing the file supplied "
+ "as an argument.]"
+ "[+xpg_echo?If set, the \becho\b and \bprint\b builtins "
+ "expand backslash-escape sequences.]"
+"}"
+"\n"
+"\n[optname ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+?The return status when listing options is zero if all \aoptnames\a "
+ "are enabled, non-zero otherwise. When setting or unsetting options, "
+ "the return status is zero unless an \aoptname\a is not a valid shell "
+ "option.]"
+"}"
+
+"[+SEE ALSO?\bset\b(1)]"
+;
+
+/* GLOBIGNORE discipline. Turn on SH_DOTGLOB on set, turn off on unset. */
+
+static void put_globignore(register Namval_t* np, const char *val, int flags, Namfun_t *fp)
+{
+ if(val)
+ sh_onoption(SH_DOTGLOB);
+ else
+ sh_offoption(SH_DOTGLOB);
+
+ nv_putv(np,val,flags,fp);
+}
+
+const Namdisc_t SH_GLOBIGNORE_disc = { sizeof(Namfun_t), put_globignore };
+
+/* FUNCNAME discipline */
+
+struct funcname
+{
+ Namfun_t hdr;
+};
+
+static void put_funcname(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ /* bash silently returns with an error when FUNCNAME is set,
+ unsetting FUNCNAME is allowed */
+ if(val && !(flags&NV_RDONLY))
+ error_info.exit(1);
+
+ nv_putv(np,val,flags,fp);
+}
+
+const Namdisc_t SH_FUNCNAME_disc = { sizeof(struct funcname), put_funcname };
+
+#define SET_SET 1
+#define SET_UNSET 2
+#define SET_NOARGS 4
+
+/* shopt builtin */
+
+int b_shopt(int argc,register char *argv[],void *extra)
+{
+ Shell_t *shp = (Shell_t*)extra;
+ int n, f, ret=0;
+ Shopt_t newflags=shp->options, opt;
+ int verbose=PRINT_SHOPT|PRINT_ALL|PRINT_NO_HEADER|PRINT_VERBOSE;
+ int setflag=0, quietflag=0, oflag=0;
+ memset(&opt,0,sizeof(opt));
+#if SHOPT_RAWONLY
+ on_option(&newflags,SH_VIRAW);
+#endif
+ while((n = optget(argv,sh_optshopt)))
+ {
+ switch(n)
+ {
+ case 'p':
+ verbose&=~PRINT_VERBOSE;
+ break;
+ case 's':
+ case 'u':
+ setflag|=n=='s'?SET_SET:SET_UNSET;
+ if(setflag==(SET_SET|SET_UNSET))
+ {
+ errormsg(SH_DICT,ERROR_ERROR,"cannot set and unset options simultaneously");
+ error_info.errors++;
+ }
+ break;
+ case 'q':
+ quietflag=1;
+ break;
+ case 'o':
+ oflag=1;
+ verbose&=~PRINT_SHOPT;
+ break;
+ case ':':
+ errormsg(SH_DICT,2, "%s", opt_info.arg);
+ continue;
+ case '?':
+ errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
+ return(-1);
+ }
+ }
+ if(error_info.errors)
+ errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
+ argc -= opt_info.index;
+ if(argc==0)
+ {
+ /* no args, -s => mask=current options, -u mask=~(current options)
+ else mask=all bits */
+ if(setflag&SET_SET)
+ opt=newflags;
+ else if(setflag&SET_UNSET)
+ for(n=0;n<4;n++)
+ opt.v[n]=~newflags.v[n];
+ else
+ memset(&opt,0xff,sizeof(opt));
+ setflag=SET_NOARGS;
+ }
+ while(argc>0)
+ {
+ f=1;
+ n=sh_lookopt(argv[opt_info.index],&f);
+ if(n<=0||(setflag
+ && (is_option(&opt,SH_INTERACTIVE)
+ || is_option(&opt,SH_RESTRICTED)
+ || is_option(&opt,SH_RESTRICTED2)
+ || is_option(&opt,SH_BASH)
+ || is_option(&opt,SH_LOGIN_SHELL)))
+ ||(oflag&&(n&SH_BASHOPT)))
+ {
+ errormsg(SH_DICT,ERROR_ERROR, e_option, argv[opt_info.index]);
+ error_info.errors++;
+ ret=1;
+ }
+ else if(f)
+ on_option(&opt,n&0xff);
+ else
+ off_option(&opt,n&0xff);
+ opt_info.index++;
+ argc--;
+ }
+ if(setflag&(SET_SET|SET_UNSET))
+ {
+ if(setflag&SET_SET)
+ {
+ if(sh_isoption(SH_INTERACTIVE))
+ off_option(&opt,SH_NOEXEC);
+ if(is_option(&opt,SH_VI)||is_option(&opt,SH_EMACS)||is_option(&opt,SH_GMACS))
+ {
+ off_option(&newflags,SH_VI);
+ off_option(&newflags,SH_EMACS);
+ off_option(&newflags,SH_GMACS);
+ }
+ for(n=0;n<4;n++)
+ newflags.v[n] |= opt.v[n];
+ }
+ else if(setflag&SET_UNSET)
+ for(n=0;n<4;n++)
+ newflags.v[n] &= ~opt.v[n];
+ sh_applyopts(shp,newflags);
+ shp->options = newflags;
+ if(is_option(&newflags,SH_XTRACE))
+ sh_trace(shp,argv,1);
+ }
+ else if(!(setflag&SET_NOARGS)) /* no -s,-u but args, ret=0 if opt&mask==mask */
+ {
+ for(n=0;n<4;n++)
+ ret+=((newflags.v[n]&opt.v[n])!=opt.v[n]);
+ }
+ if(!quietflag&&!(setflag&(SET_SET|SET_UNSET)))
+ sh_printopts(newflags,verbose,&opt);
+ return(ret);
+}
+
+/* mode = 0: init, called two times
+ before parsing shell args with SH_PREINIT state turned on
+ second time after sh_init() is through and with SH_PREINIT state turned off
+ mode > 1: re-init
+ mode < 0: shutdown
+*/
+
+void bash_init(Shell_t *shp,int mode)
+{
+ Sfio_t *iop;
+ Namval_t *np;
+ int n=0,xtrace,verbose;
+ if(mode>0)
+ goto reinit;
+ if(mode < 0)
+ {
+ /* termination code */
+ if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX))
+ sh_source(shp, NiL, sh_mactry(shp,(char*)e_bash_logout));
+ return;
+ }
+
+ if(sh_isstate(SH_PREINIT))
+ { /* pre-init stage */
+ if(sh_isoption(SH_RESTRICTED))
+ sh_onoption(SH_RESTRICTED2);
+ sh_onoption(SH_HISTORY2);
+ sh_onoption(SH_INTERACTIVE_COMM);
+ sh_onoption(SH_SOURCEPATH);
+ sh_onoption(SH_HISTAPPEND);
+ sh_onoption(SH_CMDHIST);
+ sh_onoption(SH_LITHIST);
+ sh_onoption(SH_NOEMPTYCMDCOMPL);
+ if(shp->login_sh==2)
+ sh_onoption(SH_LOGIN_SHELL);
+ if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0)
+ sh_onoption(SH_POSIX);
+ if(strcmp(astconf("UNIVERSE",0,0),"att")==0)
+ sh_onoption(SH_XPG_ECHO);
+ else
+ sh_offoption(SH_XPG_ECHO);
+ if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0)
+ sh_onoption(SH_PHYSICAL);
+ else
+ sh_offoption(SH_PHYSICAL);
+
+ /* add builtins */
+ sh_addbuiltin("shopt", b_shopt, &sh);
+
+ /* set up some variables needed for --version
+ * needs to go here because --version option is parsed before the init script.
+ */
+ if(np=nv_open("HOSTTYPE",shp->var_tree,0))
+ nv_putval(np, BASH_HOSTTYPE, NV_NOFREE);
+ if(np=nv_open("MACHTYPE",shp->var_tree,0))
+ nv_putval(np, BASH_MACHTYPE, NV_NOFREE);
+ if(np=nv_open("BASH_VERSION",shp->var_tree,0))
+ nv_putval(np, BASH_VERSION, NV_NOFREE);
+ if(np=nv_open("BASH_VERSINFO",shp->var_tree,0))
+ {
+ char *argv[7];
+ argv[0] = BASH_MAJOR;
+ argv[1] = BASH_MINOR;
+ argv[2] = BASH_PATCH;
+ argv[3] = BASH_BUILD;
+ argv[4] = BASH_RELEASE;
+ argv[5] = BASH_MACHTYPE;
+ argv[6] = 0;
+ nv_setvec(np, 0, 6, argv);
+ nv_onattr(np,NV_RDONLY);
+ }
+ return;
+ }
+
+ /* rest of init stage */
+
+ /* restrict BASH_ENV */
+ if(np=nv_open("BASH_ENV",shp->var_tree,0))
+ {
+ const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT);
+ Namfun_t *fp = calloc(dp->dsize,1);
+ fp->disc = dp;
+ nv_disc(np, fp, 0);
+ }
+
+ /* open GLOBIGNORE node */
+ if(np=nv_open("GLOBIGNORE",shp->var_tree,0))
+ {
+ const Namdisc_t *dp = &SH_GLOBIGNORE_disc;
+ Namfun_t *fp = calloc(dp->dsize,1);
+ fp->disc = dp;
+ nv_disc(np, fp, 0);
+ }
+
+ /* set startup files */
+ n=0;
+ if(sh_isoption(SH_LOGIN_SHELL))
+ {
+ if(!sh_isoption(SH_POSIX))
+ {
+ login_files[n++] = (char*)e_bash_profile;
+ login_files[n++] = (char*)e_bash_login;
+ }
+ login_files[n++] = (char*)e_profile;
+ }
+ shp->login_files = login_files;
+reinit:
+ xtrace = sh_isoption(SH_XTRACE);
+ sh_offoption(SH_XTRACE);
+ verbose = sh_isoption(SH_VERBOSE);
+ sh_offoption(SH_VERBOSE);
+ if(np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD))
+ nv_offattr(np,NV_RDONLY);
+ iop = sfopen(NULL, bash_pre_rc, "s");
+ sh_eval(iop,0);
+ if(xtrace)
+ sh_offoption(SH_XTRACE);
+ if(verbose)
+ sh_offoption(SH_VERBOSE);
+}
diff --git a/src/cmd/ksh93/sh/defs.c b/src/cmd/ksh93/sh/defs.c
new file mode 100644
index 0000000..308ce22
--- /dev/null
+++ b/src/cmd/ksh93/sh/defs.c
@@ -0,0 +1,48 @@
+/***********************************************************************
+* *
+* 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> *
+* *
+***********************************************************************/
+/*
+ * Ksh - AT&T Labs
+ * Written by David Korn
+ * This file defines all the read/write shell global variables
+ */
+
+#include "defs.h"
+#include "jobs.h"
+#include "shlex.h"
+#include "edit.h"
+#include "timeout.h"
+
+Shell_t sh = {0};
+struct shared *shgd;
+#ifdef __IMPORT__
+ Shell_t *_imp__sh = &sh;
+#endif
+
+Dtdisc_t _Nvdisc =
+{
+ offsetof(Namval_t,nvname), -1 , 0, 0, 0, nv_compare
+};
+
+/* reserve room for writable state table */
+char *sh_lexstates[ST_NONE] = {0};
+
+struct jobs job = {0};
+int32_t sh_mailchk = 600;
+
diff --git a/src/cmd/ksh93/sh/deparse.c b/src/cmd/ksh93/sh/deparse.c
new file mode 100644
index 0000000..d7736a4
--- /dev/null
+++ b/src/cmd/ksh93/sh/deparse.c
@@ -0,0 +1,603 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * David Korn
+ * AT&T Labs
+ *
+ * shell deparser
+ *
+ */
+
+#include "defs.h"
+#include "shnodes.h"
+#include "test.h"
+
+
+#define HUGE_INT (((unsigned)-1)>>1)
+#define BEGIN 0
+#define MIDDLE 1
+#define END 2
+#define PRE 1
+#define POST 2
+
+
+/* flags that can be specified with p_tree() */
+#define NO_NEWLINE 1
+#define NEED_BRACE 2
+#define NO_BRACKET 4
+
+static void p_comlist(const struct dolnod*,int);
+static void p_arg(const struct argnod*, int endchar, int opts);
+static void p_comarg(const struct comnod*);
+static void p_keyword(const char*,int);
+static void p_redirect(const struct ionod*);
+static void p_switch(const struct regnod*);
+static void here_body(const struct ionod*);
+static void p_tree(const Shnode_t*,int);
+
+static int level;
+static int begin_line;
+static int end_line;
+static char io_op[7];
+static char un_op[3] = "-?";
+static const struct ionod *here_doc;
+static Sfio_t *outfile;
+static const char *forinit = "";
+
+extern void sh_deparse(Sfio_t*, const Shnode_t*,int);
+
+void sh_deparse(Sfio_t *out, const Shnode_t *t,int tflags)
+{
+ outfile = out;
+ p_tree(t,tflags);
+}
+/*
+ * print script corresponding to shell tree <t>
+ */
+static void p_tree(register const Shnode_t *t,register int tflags)
+{
+ register char *cp;
+ int save = end_line;
+ int needbrace = (tflags&NEED_BRACE);
+ tflags &= ~NEED_BRACE;
+ if(tflags&NO_NEWLINE)
+ end_line = ' ';
+ else
+ end_line = '\n';
+ switch(t->tre.tretyp&COMMSK)
+ {
+ case TTIME:
+ if(t->tre.tretyp&COMSCAN)
+ p_keyword("!",BEGIN);
+ else
+ p_keyword("time",BEGIN);
+ if(t->par.partre)
+ p_tree(t->par.partre,tflags);
+ level--;
+ break;
+
+ case TCOM:
+ if(begin_line && level>0)
+ sfnputc(outfile,'\t',level);
+ begin_line = 0;
+ p_comarg((struct comnod*)t);
+ break;
+
+ case TSETIO:
+ if(t->tre.tretyp&FPCL)
+ tflags |= NEED_BRACE;
+ else
+ tflags = NO_NEWLINE|NEED_BRACE;
+ p_tree(t->fork.forktre,tflags);
+ p_redirect(t->fork.forkio);
+ break;
+
+ case TFORK:
+ if(needbrace)
+ tflags |= NEED_BRACE;
+ if(t->tre.tretyp&(FAMP|FCOOP))
+ {
+ tflags = NEED_BRACE|NO_NEWLINE;
+ end_line = ' ';
+ }
+ else if(t->fork.forkio)
+ tflags = NO_NEWLINE;
+ p_tree(t->fork.forktre,tflags);
+ if(t->fork.forkio)
+ p_redirect(t->fork.forkio);
+ if(t->tre.tretyp&FCOOP)
+ {
+ sfputr(outfile,"|&",'\n');
+ begin_line = 1;
+ }
+ else if(t->tre.tretyp&FAMP)
+ {
+ sfputr(outfile,"&",'\n');
+ begin_line = 1;
+ }
+ break;
+
+ case TIF:
+ p_keyword("if",BEGIN);
+ p_tree(t->if_.iftre,0);
+ p_keyword("then",MIDDLE);
+ p_tree(t->if_.thtre,0);
+ if(t->if_.eltre)
+ {
+ p_keyword("else",MIDDLE);
+ p_tree(t->if_.eltre,0);
+ }
+ p_keyword("fi",END);
+ break;
+
+ case TWH:
+ if(t->wh.whinc)
+ cp = "for";
+ else if(t->tre.tretyp&COMSCAN)
+ cp = "until";
+ else
+ cp = "while";
+ p_keyword(cp,BEGIN);
+ if(t->wh.whinc)
+ {
+ struct argnod *arg = (t->wh.whtre)->ar.arexpr;
+ sfprintf(outfile,"(( %s; ",forinit);
+ forinit = "";
+ sfputr(outfile,arg->argval,';');
+ arg = (t->wh.whinc)->arexpr;
+ sfprintf(outfile," %s))\n",arg->argval);
+ }
+ else
+ p_tree(t->wh.whtre,0);
+ t = t->wh.dotre;
+ goto dolist;
+
+ case TLST:
+ {
+ Shnode_t *tr = t->lst.lstrit;
+ if(tr->tre.tretyp==TWH && tr->wh.whinc && t->lst.lstlef->tre.tretyp==TARITH)
+ {
+ /* arithmetic for statement */
+ struct argnod *init = (t->lst.lstlef)->ar.arexpr;
+ forinit= init->argval;
+ p_tree(t->lst.lstrit,tflags);
+ break;
+ }
+ if(needbrace)
+ p_keyword("{",BEGIN);
+ p_tree(t->lst.lstlef,0);
+ if(needbrace)
+ tflags = 0;
+ p_tree(t->lst.lstrit,tflags);
+ if(needbrace)
+ p_keyword("}",END);
+ break;
+ }
+
+ case TAND:
+ cp = "&&";
+ goto andor;
+ case TORF:
+ cp = "||";
+ goto andor;
+ case TFIL:
+ cp = "|";
+ andor:
+ {
+ int bracket = 0;
+ if(t->tre.tretyp&TTEST)
+ {
+ tflags |= NO_NEWLINE;
+ if(!(tflags&NO_BRACKET))
+ {
+ p_keyword("[[",BEGIN);
+ tflags |= NO_BRACKET;
+ bracket=1;
+ }
+ }
+ p_tree(t->lst.lstlef,NEED_BRACE|NO_NEWLINE|(tflags&NO_BRACKET));
+ if(tflags&FALTPIPE)
+ {
+ Shnode_t *tt = t->lst.lstrit;
+ if(tt->tre.tretyp!=TFIL || !(tt->lst.lstlef->tre.tretyp&FALTPIPE))
+ {
+ sfputc(outfile,'\n');
+ return;
+ }
+ }
+ sfputr(outfile,cp,here_doc?'\n':' ');
+ if(here_doc)
+ {
+ here_body(here_doc);
+ here_doc = 0;
+ }
+ level++;
+ p_tree(t->lst.lstrit,tflags|NEED_BRACE);
+ if(bracket)
+ p_keyword("]]",END);
+ level--;
+ break;
+ }
+
+ case TPAR:
+ p_keyword("(",BEGIN);
+ p_tree(t->par.partre,0);
+ p_keyword(")",END);
+ break;
+
+ case TARITH:
+ {
+ register struct argnod *ap = t->ar.arexpr;
+ if(begin_line && level)
+ sfnputc(outfile,'\t',level);
+ sfprintf(outfile,"(( %s ))%c",ap->argval,end_line);
+ if(!(tflags&NO_NEWLINE))
+ begin_line=1;
+ break;
+ }
+
+ case TFOR:
+ cp = ((t->tre.tretyp&COMSCAN)?"select":"for");
+ p_keyword(cp,BEGIN);
+ sfputr(outfile,t->for_.fornam,' ');
+ if(t->for_.forlst)
+ {
+ sfputr(outfile,"in",' ');
+ tflags = end_line;
+ end_line = '\n';
+ p_comarg(t->for_.forlst);
+ end_line = tflags;
+ }
+ else
+ sfputc(outfile,'\n');
+ begin_line = 1;
+ t = t->for_.fortre;
+ dolist:
+ p_keyword("do",MIDDLE);
+ p_tree(t,0);
+ p_keyword("done",END);
+ break;
+
+ case TSW:
+ p_keyword("case",BEGIN);
+ p_arg(t->sw.swarg,' ',0);
+ if(t->sw.swlst)
+ {
+ begin_line = 1;
+ sfputr(outfile,"in",'\n');
+ tflags = end_line;
+ end_line = '\n';
+ p_switch(t->sw.swlst);
+ end_line = tflags;
+ }
+ p_keyword("esac",END);
+ break;
+
+ case TFUN:
+ if(t->tre.tretyp&FPOSIX)
+ {
+ sfprintf(outfile,"%s",t->funct.functnam);
+ p_keyword("()\n",BEGIN);
+ }
+ else
+ {
+ p_keyword("function",BEGIN);
+ tflags = (t->funct.functargs?' ':'\n');
+ sfputr(outfile,t->funct.functnam,tflags);
+ if(t->funct.functargs)
+ {
+ tflags = end_line;
+ end_line = '\n';
+ p_comarg(t->funct.functargs);
+ end_line = tflags;
+ }
+ }
+ begin_line = 1;
+ p_keyword("{\n",MIDDLE);
+ begin_line = 1;
+ p_tree(t->funct.functtre,0);
+ p_keyword("}",END);
+ break;
+ /* new test compound command */
+ case TTST:
+ if(!(tflags&NO_BRACKET))
+ p_keyword("[[",BEGIN);
+ if((t->tre.tretyp&TPAREN)==TPAREN)
+ {
+ p_keyword("(",BEGIN);
+ p_tree(t->lst.lstlef,NO_BRACKET|NO_NEWLINE);
+ p_keyword(")",END);
+ }
+ else
+ {
+ int flags = (t->tre.tretyp)>>TSHIFT;
+ if(t->tre.tretyp&TNEGATE)
+ sfputr(outfile,"!",' ');
+ if(t->tre.tretyp&TUNARY)
+ {
+ un_op[1] = flags;
+ sfputr(outfile,un_op,' ');
+ }
+ else
+ cp = ((char*)(shtab_testops+(flags&037)-1)->sh_name);
+ p_arg(&(t->lst.lstlef->arg),' ',0);
+ if(t->tre.tretyp&TBINARY)
+ {
+ sfputr(outfile,cp,' ');
+ p_arg(&(t->lst.lstrit->arg),' ',0);
+ }
+ }
+ if(!(tflags&NO_BRACKET))
+ p_keyword("]]",END);
+ }
+ while(begin_line && here_doc)
+ {
+ here_body(here_doc);
+ here_doc = 0;
+ }
+ end_line = save;
+ return;
+}
+
+/*
+ * print a keyword
+ * increment indent level for flag==BEGIN
+ * decrement indent level for flag==END
+ */
+static void p_keyword(const char *word,int flag)
+{
+ register int sep;
+ if(flag==END)
+ sep = end_line;
+ else if(*word=='[' || *word=='(')
+ sep = ' ';
+ else
+ sep = '\t';
+ if(flag!=BEGIN)
+ level--;
+ if(begin_line && level)
+ sfnputc(outfile,'\t',level);
+ sfputr(outfile,word,sep);
+ if(sep=='\n')
+ begin_line=1;
+ else
+ begin_line=0;
+ if(flag!=END)
+ level++;
+}
+
+static void p_arg(register const struct argnod *arg,register int endchar,int opts)
+{
+ register const char *cp;
+ register int flag;
+ do
+ {
+ if(!arg->argnxt.ap)
+ flag = endchar;
+ else if(opts&PRE)
+ {
+ /* case alternation lists in reverse order */
+ p_arg(arg->argnxt.ap,'|',opts);
+ flag = endchar;
+ }
+ else if(opts)
+ flag = ' ';
+ cp = arg->argval;
+ if(*cp==0 && (arg->argflag&ARG_EXP) && arg->argchn.ap)
+ {
+ int c = (arg->argflag&ARG_RAW)?'>':'<';
+ sfputc(outfile,c);
+ sfputc(outfile,'(');
+ p_tree((Shnode_t*)arg->argchn.ap,0);
+ sfputc(outfile,')');
+ }
+ else if(*cp==0 && opts==POST && arg->argchn.ap)
+ {
+ /* compound assignment */
+ struct fornod *fp=(struct fornod*)arg->argchn.ap;
+ sfprintf(outfile,"%s=(\n",fp->fornam);
+ sfnputc(outfile,'\t',++level);
+ p_tree(fp->fortre,0);
+ if(--level)
+ sfnputc(outfile,'\t',level);
+ sfputc(outfile,')');
+ }
+ else if((arg->argflag&ARG_RAW) && (cp[1] || (*cp!='[' && *cp!=']')))
+ cp = sh_fmtq(cp);
+ sfputr(outfile,cp,flag);
+ if(flag=='\n')
+ begin_line = 1;
+ arg = arg->argnxt.ap;
+ }
+ while((opts&POST) && arg);
+ return;
+}
+
+static void p_redirect(register const struct ionod *iop)
+{
+ register char *cp;
+ register int iof,iof2;
+ for(;iop;iop=iop->ionxt)
+ {
+ iof=iop->iofile;
+ cp = io_op;
+ if(iop->iovname)
+ {
+ sfwrite(outfile,"(;",2);
+ sfputr(outfile,iop->iovname,')');
+ cp++;
+ }
+ else
+ *cp = '0'+(iof&IOUFD);
+ if(iof&IOPUT)
+ {
+ if(*cp == '1' && !iop->iovname)
+ cp++;
+ io_op[1] = '>';
+ }
+ else
+ {
+ if(*cp == '0' && !iop->iovname)
+ cp++;
+ io_op[1] = '<';
+ }
+ io_op[2] = 0;
+ io_op[3] = 0;
+ if(iof&IOLSEEK)
+ {
+ io_op[1] = '#';
+ if(iof&IOARITH)
+ strcpy(&io_op[3]," ((");
+ }
+ else if(iof&IOMOV)
+ io_op[2] = '&';
+ else if(iof&(IORDW|IOAPP))
+ io_op[2] = '>';
+ else if(iof&IOCLOB)
+ io_op[2] = '|';
+ if(iop->iodelim)
+ {
+ /* here document */
+#ifdef xxx
+ iop->iolink = (char*)here_doc;
+#endif
+ here_doc = iop;
+ io_op[2] = '<';
+#ifdef future
+ if(iof&IOSTRIP)
+ io_op[3] = '-';
+#endif
+ }
+ sfputr(outfile,cp,' ');
+ if(iop->ionxt)
+ iof = ' ';
+ else
+ {
+ if((iof=end_line)=='\n')
+ begin_line = 1;
+ }
+ if((iof&IOLSEEK) && (iof&IOARITH))
+ iof2 = iof, iof = ' ';
+ if(iop->iodelim)
+ {
+ if(!(iop->iofile&IODOC))
+ sfwrite(outfile,"''",2);
+ sfputr(outfile,sh_fmtq(iop->iodelim),iof);
+ }
+ else if(iop->iofile&IORAW)
+ sfputr(outfile,sh_fmtq(iop->ioname),iof);
+ else
+ sfputr(outfile,iop->ioname,iof);
+ if((iof&IOLSEEK) && (iof&IOARITH))
+ sfputr(outfile, "))", iof2);
+ }
+ return;
+}
+
+static void p_comarg(register const struct comnod *com)
+{
+ register int flag = end_line;
+ if(com->comtyp&FAMP)
+ sfwrite(outfile,"& ",2);
+ if(com->comarg || com->comio)
+ flag = ' ';
+ if(com->comset)
+ p_arg(com->comset,flag,POST);
+ if(com->comarg)
+ {
+ if(!com->comio)
+ flag = end_line;
+ if(com->comtyp&COMSCAN)
+ p_arg(com->comarg,flag,POST);
+ else
+ p_comlist((struct dolnod*)com->comarg,flag);
+ }
+ if(com->comio)
+ p_redirect(com->comio);
+ return;
+}
+
+static void p_comlist(const struct dolnod *dol,int endchar)
+{
+ register char *cp, *const*argv;
+ register int flag = ' ', special;
+ argv = dol->dolval+ARG_SPARE;
+ cp = *argv;
+ special = (*cp=='[' && cp[1]==0);
+ do
+ {
+ if(cp)
+ argv++;
+ else
+ cp = "";
+ if(*argv==0)
+ {
+ if((flag=endchar)=='\n')
+ begin_line = 1;
+ special = (*cp==']' && cp[1]==0);
+ }
+ sfputr(outfile,special?cp:sh_fmtq(cp),flag);
+ special = 0;
+ }
+ while(cp = *argv);
+ return;
+}
+
+static void p_switch(register const struct regnod *reg)
+{
+ if(level>1)
+ sfnputc(outfile,'\t',level-1);
+ p_arg(reg->regptr,')',PRE);
+ begin_line = 0;
+ sfputc(outfile,'\t');
+ if(reg->regcom)
+ p_tree(reg->regcom,0);
+ level++;
+ if(reg->regflag)
+ p_keyword(";&",END);
+ else
+ p_keyword(";;",END);
+ if(reg->regnxt)
+ p_switch(reg->regnxt);
+ return;
+}
+
+/*
+ * output here documents
+ */
+static void here_body(register const struct ionod *iop)
+{
+ Sfio_t *infile;
+#ifdef xxx
+ if(iop->iolink)
+ here_body((struct inode*)iop->iolink);
+ iop->iolink = 0;
+#endif
+ if(iop->iofile&IOSTRG)
+ infile = sfnew((Sfio_t*)0,iop->ioname,iop->iosize,-1,SF_STRING|SF_READ);
+ else
+ sfseek(infile=sh.heredocs,iop->iooffset,SEEK_SET);
+ sfmove(infile,outfile,iop->iosize,-1);
+ if(iop->iofile&IOSTRG)
+ sfclose(infile);
+ sfputr(outfile,iop->iodelim,'\n');
+}
+
diff --git a/src/cmd/ksh93/sh/env.c b/src/cmd/ksh93/sh/env.c
new file mode 100644
index 0000000..717c8b8
--- /dev/null
+++ b/src/cmd/ksh93/sh/env.c
@@ -0,0 +1,255 @@
+/***********************************************************************
+* *
+* 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
+
+#include <ast.h>
+#include <cdt.h>
+
+#define env_change() (++ast.env_serial)
+
+typedef struct _venv_ Evar_t;
+struct _venv_
+{
+ union
+ {
+ Evar_t *next;
+ char *ptr;
+ } un;
+ Dtlink_t link;
+ int index;
+};
+
+typedef struct _env_
+{
+ Dt_t *dt;
+ Evar_t *freelist;
+ char **env;
+ int count;
+ int extra;
+ int max;
+ int flags;
+} Env_t;
+
+#define _BLD_env 1
+#include <env.h>
+
+#define ENV_VALID 2 /* set if env is valid */
+#define ENV_PMALLOC 1 /* set if Evar_t->un.ptr *s malloced */
+#define ENV_VMALLOC 2 /* set of Evar_t was malloced */
+#define ENV_BITS 3
+
+/*
+ * Compares the name portion of name=... only.
+ */
+static int compare(Dt_t *dt, Void_t* key1, Void_t* key2, Dtdisc_t* disc)
+{
+ register int c,d;
+ const unsigned char *s1=(unsigned const char*)key1;
+ const unsigned char *s2=(unsigned const char*)key2;
+ while((c= *s1++) && c!='=' && c==*s2)
+ s2++;
+ if(c=='=')
+ c = 0;
+ if((d=*s2)=='=')
+ d = 0;
+ return(c-d);
+}
+
+static Dtdisc_t env_disc =
+{
+ 0, -1,
+ sizeof(char*),
+ 0,
+ 0,
+ compare
+};
+
+/*
+ * return a pointer to the environment in sorted order
+ * NULL is returned if there if there is nospace
+ */
+char **env_get(Env_t* ep)
+{
+ register Evar_t *vp;
+ register int n=ep->extra;
+ if(ep->flags&ENV_VALID)
+ return(ep->env+n);
+ if(ep->count > ep->max)
+ {
+ if(ep->flags&ENV_MALLOCED)
+ free((void*)ep->env);
+ if(!(ep->env = (char**)malloc(sizeof(char*)*(ep->count+1))))
+ return(0);
+ ep->flags |= ENV_MALLOCED;
+ ep->max = ep->count;
+ }
+ for(vp=(Evar_t*)dtfirst(ep->dt);vp; vp=(Evar_t*)dtnext(ep->dt,vp))
+ {
+ vp->index = (n<<ENV_BITS) | (vp->index&((1<<ENV_BITS)-1));
+ ep->env[n++] = vp->un.ptr;
+ }
+ ep->env[n] = 0;
+ ep->flags |= ENV_VALID;
+ environ = ep->env+ep->extra;
+ return(ep->env+ep->extra);
+}
+
+/*
+ * add name=value pair given by <str> to <ep>
+ * if malloced is set, the variable will be freed when reassigned
+ * The environment list may become invalidated
+ * Returns 1 for success, 0 for failure
+ */
+int env_add(Env_t *ep, const char *str, int flags)
+{
+ Evar_t *vp = (Evar_t*)dtmatch(ep->dt,(void*)str);
+ if(vp && strcmp(str,vp->un.ptr)==0)
+ return(1);
+ if(flags&ENV_STRDUP)
+ str = strdup(str);
+ if(vp)
+ {
+ if(vp->index&ENV_PMALLOC)
+ free((void*)vp->un.ptr);
+ vp->un.ptr = (char*)str;
+ if(ep->env && (ep->flags&ENV_VALID))
+ ep->env[vp->index>>ENV_BITS] = vp->un.ptr;
+ }
+ else
+ {
+ ep->flags &= ~ENV_VALID;
+ if(vp = ep->freelist)
+ ep->freelist = vp->un.next;
+ else if(vp = newof((Evar_t*)0,Evar_t,2,0))
+ {
+ vp->index = ENV_VMALLOC;
+ ep->freelist = (vp+1);
+ ep->freelist->un.next = 0;
+ }
+ else
+ return(0);
+ vp->un.ptr = (void*)str;
+ if(!(vp=dtinsert(ep->dt,vp)))
+ return(0);
+ ep->count++;
+ }
+ if(flags)
+ vp->index |= ENV_PMALLOC;
+ else
+ vp->index &= ~ENV_PMALLOC;
+ env_change();
+ return(1);
+}
+
+/*
+ * delete name from <ep>
+ * The environment list may become invalidated
+ * Returns 1 for success, 0 for if name is not present
+ */
+int env_delete(Env_t *ep, const char *str)
+{
+ Evar_t *vp = (Evar_t*)dtmatch(ep->dt,(void*)str);
+ if(!vp)
+ return(0);
+ ep->flags &= ~ENV_VALID;
+ if(vp->index&ENV_PMALLOC)
+ free((void*)vp->un.ptr);
+ dtdelete(ep->dt,vp);
+ vp->un.next = ep->freelist;
+ ep->freelist = vp;
+ env_change();
+ return(1);
+}
+
+/*
+ * open up a structure to support environment variables
+ * initialize with environment give by <envp>
+ * If <extra> > 0, <extra> slots will be left at beginning of
+ * environment list when env_get() is involed.
+ * If <extra>==ENV_USABLE, then the original environ can be
+ * used and returned. Otherwise, a new one will be returned
+ */
+Env_t *env_open(char **envp, int extra)
+{
+ char **env;
+ Env_t *ep;
+ Evar_t *vp;
+ int n=2;
+ if(!(ep = newof((Env_t*)0,Env_t,1,0)))
+ return(0);
+ if(!(ep->dt = dtopen(&env_disc,Dtoset)))
+ return(0);
+ if(env=envp)
+ {
+ while(*env++);
+ n = (env+2)-envp;
+ }
+ if(extra==ENV_STABLE)
+ {
+ ep->env = envp;
+ ep->max = n-1;
+ }
+ else
+ ep->count = ep->extra = extra;
+ ep->freelist = vp = newof((Evar_t*)0,Evar_t,n,0);
+ vp->index = ENV_VMALLOC;
+ while(--n>0)
+ {
+ vp->un.next = (vp+1);
+ vp++;
+ }
+ vp->un.next = 0;
+ if(env)
+ {
+ for(env=envp; *env; env++)
+ env_add(ep,*env,0);
+ }
+ return(ep);
+}
+
+/*
+ * close <ep> and free up all space used by it
+ */
+void env_close(Env_t *ep)
+{
+ Evar_t *vp, *vpnext,*top;
+ if(ep->env && (ep->flags&ENV_MALLOCED))
+ free((void*)ep->env);
+ for(vp=(Evar_t*)dtfirst(ep->dt);vp; vp=vpnext)
+ {
+ vpnext = (Evar_t*)dtnext(ep->dt,vp);
+ env_delete(ep,vp->un.ptr);
+ }
+ for(top=0,vp = ep->freelist; vp; vp = vpnext)
+ {
+ vpnext = vp->un.next;
+ if(vp->index&ENV_VMALLOC)
+ {
+ vp->un.next = top;
+ top = vp;
+ }
+ }
+ for(vp=top; vp; vp = vpnext)
+ {
+ vpnext = vp->un.next;
+ free((void*)vp);
+ }
+ dtclose(ep->dt);
+}
diff --git a/src/cmd/ksh93/sh/expand.c b/src/cmd/ksh93/sh/expand.c
new file mode 100644
index 0000000..40c3dd1
--- /dev/null
+++ b/src/cmd/ksh93/sh/expand.c
@@ -0,0 +1,468 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * File name expansion
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#if KSHELL
+# include "defs.h"
+# include "variables.h"
+# include "test.h"
+#else
+# include <ast.h>
+# include <ctype.h>
+# include <setjmp.h>
+#endif /* KSHELL */
+#include <glob.h>
+#include <ls.h>
+#include <stak.h>
+#include <ast_dir.h>
+#include "io.h"
+#include "path.h"
+
+#if !SHOPT_BRACEPAT
+# define SHOPT_BRACEPAT 0
+#endif
+
+#if KSHELL
+# define argbegin argnxt.cp
+ static const char *sufstr;
+ static int suflen;
+ static int scantree(Dt_t*,const char*, struct argnod**);
+#else
+# define sh_sigcheck(sig) (0)
+# define sh_access access
+# define suflen 0
+#endif /* KSHELL */
+
+
+/*
+ * This routine builds a list of files that match a given pathname
+ * Uses external routine strgrpmatch() to match each component
+ * A leading . must match explicitly
+ *
+ */
+
+#ifndef GLOB_AUGMENTED
+# define GLOB_AUGMENTED 0
+#endif
+
+#define GLOB_RESCAN 1
+#define globptr() ((struct glob*)membase)
+
+static struct glob *membase;
+
+#if GLOB_VERSION >= 20010916L
+static char *nextdir(glob_t *gp, char *dir)
+{
+ Shell_t *shp = sh_getinterp();
+ Pathcomp_t *pp = (Pathcomp_t*)gp->gl_handle;
+ if(!dir)
+ pp = path_get(shp,"");
+ else
+ pp = pp->next;
+ gp->gl_handle = (void*)pp;
+ if(pp)
+ return(pp->name);
+ return(0);
+}
+#endif
+
+int path_expand(Shell_t *shp,const char *pattern, struct argnod **arghead)
+{
+ glob_t gdata;
+ register struct argnod *ap;
+ register glob_t *gp= &gdata;
+ register int flags,extra=0;
+#if SHOPT_BASH
+ register int off;
+ register char *sp, *cp, *cp2;
+#endif
+ sh_stats(STAT_GLOBS);
+ memset(gp,0,sizeof(gdata));
+ flags = GLOB_GROUP|GLOB_AUGMENTED|GLOB_NOCHECK|GLOB_NOSORT|GLOB_STACK|GLOB_LIST|GLOB_DISC;
+ if(sh_isoption(SH_MARKDIRS))
+ flags |= GLOB_MARK;
+ if(sh_isoption(SH_GLOBSTARS))
+ flags |= GLOB_STARSTAR;
+#if SHOPT_BASH
+#if 0
+ if(sh_isoption(SH_BASH) && !sh_isoption(SH_EXTGLOB))
+ flags &= ~GLOB_AUGMENTED;
+#endif
+ if(sh_isoption(SH_NULLGLOB))
+ flags &= ~GLOB_NOCHECK;
+ if(sh_isoption(SH_NOCASEGLOB))
+ flags |= GLOB_ICASE;
+#endif
+ if(sh_isstate(SH_COMPLETE))
+ {
+#if KSHELL
+ extra += scantree(shp->alias_tree,pattern,arghead);
+ extra += scantree(shp->fun_tree,pattern,arghead);
+# if GLOB_VERSION >= 20010916L
+ gp->gl_nextdir = nextdir;
+# endif
+#endif /* KSHELL */
+ flags |= GLOB_COMPLETE;
+ flags &= ~GLOB_NOCHECK;
+ }
+#if SHOPT_BASH
+ if(off = staktell())
+ sp = stakfreeze(0);
+ if(sh_isoption(SH_BASH))
+ {
+ /*
+ * For bash, FIGNORE is a colon separated list of suffixes to
+ * ignore when doing filename/command completion.
+ * GLOBIGNORE is similar to ksh FIGNORE, but colon separated
+ * instead of being an augmented shell pattern.
+ * Generate shell patterns out of those here.
+ */
+ if(sh_isstate(SH_FCOMPLETE))
+ cp=nv_getval(sh_scoped(shp,FIGNORENOD));
+ else
+ {
+ static Namval_t *GLOBIGNORENOD;
+ if(!GLOBIGNORENOD)
+ GLOBIGNORENOD = nv_open("GLOBIGNORE",shp->var_tree,0);
+ cp=nv_getval(sh_scoped(shp,GLOBIGNORENOD));
+ }
+ if(cp)
+ {
+ flags |= GLOB_AUGMENTED;
+ stakputs("@(");
+ if(!sh_isstate(SH_FCOMPLETE))
+ {
+ stakputs(cp);
+ for(cp=stakptr(off); *cp; cp++)
+ if(*cp == ':')
+ *cp='|';
+ }
+ else
+ {
+ cp2 = strtok(cp, ":");
+ if(!cp2)
+ cp2=cp;
+ do
+ {
+ stakputc('*');
+ stakputs(cp2);
+ if(cp2 = strtok(NULL, ":"))
+ {
+ *(cp2-1)=':';
+ stakputc('|');
+ }
+ } while(cp2);
+ }
+ stakputc(')');
+ gp->gl_fignore = stakfreeze(1);
+ }
+ else if(!sh_isstate(SH_FCOMPLETE) && sh_isoption(SH_DOTGLOB))
+ gp->gl_fignore = "";
+ }
+ else
+#endif
+ gp->gl_fignore = nv_getval(sh_scoped(shp,FIGNORENOD));
+ if(suflen)
+ gp->gl_suffix = sufstr;
+ gp->gl_intr = &shp->trapnote;
+ suflen = 0;
+ if(memcmp(pattern,"~(N",3)==0)
+ flags &= ~GLOB_NOCHECK;
+ glob(pattern, flags, 0, gp);
+#if SHOPT_BASH
+ if(off)
+ stakset(sp,off);
+ else
+ stakseek(0);
+#endif
+ sh_sigcheck(shp);
+ for(ap= (struct argnod*)gp->gl_list; ap; ap = ap->argnxt.ap)
+ {
+ ap->argchn.ap = ap->argnxt.ap;
+ if(!ap->argnxt.ap)
+ ap->argchn.ap = *arghead;
+ }
+ if(gp->gl_list)
+ *arghead = (struct argnod*)gp->gl_list;
+ return(gp->gl_pathc+extra);
+}
+
+#if KSHELL
+
+/*
+ * scan tree and add each name that matches the given pattern
+ */
+static int scantree(Dt_t *tree, const char *pattern, struct argnod **arghead)
+{
+ register Namval_t *np;
+ register struct argnod *ap;
+ register int nmatch=0;
+ register char *cp;
+ np = (Namval_t*)dtfirst(tree);
+ for(;np && !nv_isnull(np);(np = (Namval_t*)dtnext(tree,np)))
+ {
+ if(strmatch(cp=nv_name(np),pattern))
+ {
+ ap = (struct argnod*)stakseek(ARGVAL);
+ stakputs(cp);
+ ap = (struct argnod*)stakfreeze(1);
+ ap->argbegin = NIL(char*);
+ ap->argchn.ap = *arghead;
+ ap->argflag = ARG_RAW|ARG_MAKE;
+ *arghead = ap;
+ nmatch++;
+ }
+ }
+ return(nmatch);
+}
+
+/*
+ * file name completion
+ * generate the list of files found by adding an suffix to end of name
+ * The number of matches is returned
+ */
+
+int path_complete(Shell_t *shp,const char *name,register const char *suffix, struct argnod **arghead)
+{
+ sufstr = suffix;
+ suflen = strlen(suffix);
+ return(path_expand(shp,name,arghead));
+}
+
+#endif
+
+#if SHOPT_BRACEPAT
+
+static int checkfmt(Sfio_t* sp, void* vp, Sffmt_t* fp)
+{
+ return -1;
+}
+
+int path_generate(Shell_t *shp,struct argnod *todo, struct argnod **arghead)
+/*@
+ assume todo!=0;
+ return count satisfying count>=1;
+@*/
+{
+ register char *cp;
+ register int brace;
+ register struct argnod *ap;
+ struct argnod *top = 0;
+ struct argnod *apin;
+ char *pat, *rescan;
+ char *format;
+ char comma, range=0;
+ int first, last, incr, count = 0;
+ char tmp[32], end[1];
+ todo->argchn.ap = 0;
+again:
+ apin = ap = todo;
+ todo = ap->argchn.ap;
+ cp = ap->argval;
+ range = comma = brace = 0;
+ /* first search for {...,...} */
+ while(1) switch(*cp++)
+ {
+ case '{':
+ if(brace++==0)
+ pat = cp;
+ break;
+ case '}':
+ if(--brace>0)
+ break;
+ if(brace==0 && comma && *cp!='(')
+ goto endloop1;
+ comma = brace = 0;
+ break;
+ case '.':
+ if(brace==1 && *cp=='.')
+ {
+ char *endc;
+ incr = 1;
+ if(isdigit(*pat) || *pat=='+' || *pat=='-')
+ {
+ first = strtol(pat,&endc,0);
+ if(endc==(cp-1))
+ {
+ last = strtol(cp+1,&endc,0);
+ if(*endc=='.' && endc[1]=='.')
+ incr = strtol(endc+2,&endc,0);
+ else if(last<first)
+ incr = -1;
+ if(incr)
+ {
+ if(*endc=='%')
+ {
+ Sffmt_t fmt;
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.version = SFIO_VERSION;
+ fmt.form = endc;
+ fmt.extf = checkfmt;
+ sfprintf(sfstdout, "%!", &fmt);
+ if(!(fmt.flags&(SFFMT_LLONG|SFFMT_LDOUBLE)))
+ switch (fmt.fmt)
+ {
+ case 'c':
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ format = endc;
+ endc = fmt.form;
+ break;
+ }
+ }
+ else
+ format = "%d";
+ if(*endc=='}')
+ {
+ cp = endc+1;
+ range = 2;
+ goto endloop1;
+ }
+ }
+ }
+ }
+ else if((cp[2]=='}' || cp[2]=='.' && cp[3]=='.') && ((*pat>='a' && *pat<='z' && cp[1]>='a' && cp[1]<='z') || (*pat>='A' && *pat<='Z' && cp[1]>='A' && cp[1]<='Z')))
+ {
+ first = *pat;
+ last = cp[1];
+ cp += 2;
+ if(*cp=='.')
+ {
+ incr = strtol(cp+2,&endc,0);
+ cp = endc;
+ }
+ else if(first>last)
+ incr = -1;
+ if(incr && *cp=='}')
+ {
+ cp++;
+ range = 1;
+ goto endloop1;
+ }
+ }
+ cp++;
+ }
+ break;
+ case ',':
+ if(brace==1)
+ comma = 1;
+ break;
+ case '\\':
+ cp++;
+ break;
+ case 0:
+ /* insert on stack */
+ ap->argchn.ap = top;
+ top = ap;
+ if(todo)
+ goto again;
+ for(; ap; ap=apin)
+ {
+ apin = ap->argchn.ap;
+ if(!sh_isoption(SH_NOGLOB))
+ brace=path_expand(shp,ap->argval,arghead);
+ else
+ {
+ ap->argchn.ap = *arghead;
+ *arghead = ap;
+ brace=1;
+ }
+ if(brace)
+ {
+ count += brace;
+ (*arghead)->argflag |= ARG_MAKE;
+ }
+ }
+ return(count);
+ }
+endloop1:
+ rescan = cp;
+ cp = pat-1;
+ *cp = 0;
+ while(1)
+ {
+ brace = 0;
+ if(range)
+ {
+ if(range==1)
+ {
+ pat[0] = first;
+ cp = &pat[1];
+ }
+ else
+ {
+ *(rescan - 1) = 0;
+ sfsprintf(pat=tmp,sizeof(tmp),format,first);
+ *(rescan - 1) = '}';
+ *(cp = end) = 0;
+ }
+ if(incr*(first+incr) > last*incr)
+ *cp = '}';
+ else
+ first += incr;
+ }
+ /* generate each pattern and put on the todo list */
+ else while(1) switch(*++cp)
+ {
+ case '\\':
+ cp++;
+ break;
+ case '{':
+ brace++;
+ break;
+ case ',':
+ if(brace==0)
+ goto endloop2;
+ break;
+ case '}':
+ if(--brace<0)
+ goto endloop2;
+ }
+ endloop2:
+ brace = *cp;
+ *cp = 0;
+ sh_sigcheck(shp);
+ ap = (struct argnod*)stakseek(ARGVAL);
+ ap->argflag = ARG_RAW;
+ ap->argchn.ap = todo;
+ stakputs(apin->argval);
+ stakputs(pat);
+ stakputs(rescan);
+ todo = ap = (struct argnod*)stakfreeze(1);
+ if(brace == '}')
+ break;
+ if(!range)
+ pat = cp+1;
+ }
+ goto again;
+}
+
+#endif /* SHOPT_BRACEPAT */
diff --git a/src/cmd/ksh93/sh/fault.c b/src/cmd/ksh93/sh/fault.c
new file mode 100644
index 0000000..2d22e42
--- /dev/null
+++ b/src/cmd/ksh93/sh/fault.c
@@ -0,0 +1,672 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Fault handling routines
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <fcin.h>
+#include "io.h"
+#include "history.h"
+#include "shlex.h"
+#include "variables.h"
+#include "jobs.h"
+#include "path.h"
+#include "builtins.h"
+#include "ulimit.h"
+
+#define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV)
+
+static char indone;
+static int cursig = -1;
+
+#if !_std_malloc
+# include <vmalloc.h>
+#endif
+#if defined(VMFL) && (VMALLOC_VERSION>=20031205L)
+ /*
+ * This exception handler is called after vmalloc() unlocks the region
+ */
+ static int malloc_done(Vmalloc_t* vm, int type, Void_t* val, Vmdisc_t* dp)
+ {
+ dp->exceptf = 0;
+ sh_exit(SH_EXITSIG);
+ return(0);
+ }
+#endif
+
+/*
+ * Most signals caught or ignored by the shell come here
+*/
+void sh_fault(register int sig)
+{
+ register Shell_t *shp = sh_getinterp();
+ register int flag=0;
+ register char *trap;
+ register struct checkpt *pp = (struct checkpt*)shp->jmplist;
+ int action=0;
+ /* reset handler */
+ if(!(sig&SH_TRAP))
+ signal(sig, sh_fault);
+ sig &= ~SH_TRAP;
+#ifdef SIGWINCH
+ if(sig==SIGWINCH)
+ {
+ int rows=0, cols=0;
+ int32_t v;
+ astwinsize(2,&rows,&cols);
+ if(v = cols)
+ nv_putval(COLUMNS, (char*)&v, NV_INT32|NV_RDONLY);
+ if(v = rows)
+ nv_putval(LINES, (char*)&v, NV_INT32|NV_RDONLY);
+ shp->winch++;
+ }
+#endif /* SIGWINCH */
+ trap = shp->st.trapcom[sig];
+ if(shp->savesig)
+ {
+ /* critical region, save and process later */
+ if(!(shp->sigflag[sig]&SH_SIGIGNORE))
+ shp->savesig = sig;
+ return;
+ }
+ if(sig==SIGALRM && shp->bltinfun==b_sleep)
+ {
+ if(trap && *trap)
+ {
+ shp->trapnote |= SH_SIGTRAP;
+ shp->sigflag[sig] |= SH_SIGTRAP;
+ }
+ return;
+ }
+ if(shp->subshell && trap && sig!=SIGINT && sig!=SIGQUIT && sig!=SIGWINCH && sig!=SIGCONT)
+ {
+ shp->exitval = SH_EXITSIG|sig;
+ sh_subfork();
+ shp->exitval = 0;
+ return;
+ }
+ /* handle ignored signals */
+ if(trap && *trap==0)
+ return;
+ flag = shp->sigflag[sig]&~SH_SIGOFF;
+ if(!trap)
+ {
+ if(sig==SIGINT && (shp->trapnote&SH_SIGIGNORE))
+ return;
+ if(flag&SH_SIGIGNORE)
+ {
+ if(shp->subshell)
+ shp->ignsig = sig;
+ sigrelease(sig);
+ return;
+ }
+ if(flag&SH_SIGDONE)
+ {
+ void *ptr=0;
+ if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! shp->subshell)
+ {
+ /* check for TERM signal between fork/exec */
+ if(sig==SIGTERM && job.in_critical)
+ shp->trapnote |= SH_SIGTERM;
+ return;
+ }
+ shp->lastsig = sig;
+ sigrelease(sig);
+ if(pp->mode != SH_JMPSUB)
+ {
+ if(pp->mode < SH_JMPSUB)
+ pp->mode = shp->subshell?SH_JMPSUB:SH_JMPFUN;
+ else
+ pp->mode = SH_JMPEXIT;
+ }
+ if(shp->subshell)
+ sh_exit(SH_EXITSIG);
+ if(sig==SIGABRT || (abortsig(sig) && (ptr = malloc(1))))
+ {
+ if(ptr)
+ free(ptr);
+ sh_done(shp,sig);
+ }
+ /* mark signal and continue */
+ shp->trapnote |= SH_SIGSET;
+ if(sig <= shp->gd->sigmax)
+ shp->sigflag[sig] |= SH_SIGSET;
+#if defined(VMFL) && (VMALLOC_VERSION>=20031205L)
+ if(abortsig(sig))
+ {
+ /* abort inside malloc, process when malloc returns */
+ /* VMFL defined when using vmalloc() */
+ Vmdisc_t* dp = vmdisc(Vmregion,0);
+ if(dp)
+ dp->exceptf = malloc_done;
+ }
+#endif
+ return;
+ }
+ }
+ errno = 0;
+ if(pp->mode==SH_JMPCMD || (pp->mode==1 && shp->bltinfun) && !(flag&SH_SIGIGNORE))
+ shp->lastsig = sig;
+ if(trap)
+ {
+ /*
+ * propogate signal to foreground group
+ */
+ if(sig==SIGHUP && job.curpgid)
+ killpg(job.curpgid,SIGHUP);
+ flag = SH_SIGTRAP;
+ }
+ else
+ {
+ shp->lastsig = sig;
+ flag = SH_SIGSET;
+#ifdef SIGTSTP
+ if(sig==SIGTSTP)
+ {
+ shp->trapnote |= SH_SIGTSTP;
+ if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK))
+ {
+ sigrelease(sig);
+ sh_exit(SH_EXITSIG);
+ flag = 0;
+ }
+ }
+#endif /* SIGTSTP */
+ }
+#ifdef ERROR_NOTIFY
+ if((error_info.flags&ERROR_NOTIFY) && shp->bltinfun)
+ action = (*shp->bltinfun)(-sig,(char**)0,(void*)0);
+ if(action>0)
+ return;
+#endif
+ if(shp->bltinfun && shp->bltindata.notify)
+ {
+ shp->bltindata.sigset = 1;
+ return;
+ }
+ shp->trapnote |= flag;
+ if(sig <= shp->gd->sigmax)
+ shp->sigflag[sig] |= flag;
+ if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK))
+ {
+ if(action<0)
+ return;
+ sigrelease(sig);
+ sh_exit(SH_EXITSIG);
+ }
+}
+
+/*
+ * initialize signal handling
+ */
+void sh_siginit(void *ptr)
+{
+ Shell_t *shp = (Shell_t*)ptr;
+ register int sig, n;
+ register const struct shtable2 *tp = shtab_signals;
+ sig_begin();
+ /* find the largest signal number in the table */
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if ((n = SIGRTMIN) > 0 && (sig = SIGRTMAX) > n && sig < SH_TRAP)
+ {
+ shp->gd->sigruntime[SH_SIGRTMIN] = n;
+ shp->gd->sigruntime[SH_SIGRTMAX] = sig;
+ }
+#endif /* SIGRTMIN && SIGRTMAX */
+ n = SIGTERM;
+ while(*tp->sh_name)
+ {
+ sig = (tp->sh_number&((1<<SH_SIGBITS)-1));
+ if (!(sig-- & SH_TRAP))
+ {
+ if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME)
+ sig = shp->gd->sigruntime[sig];
+ if(sig>n && sig<SH_TRAP)
+ n = sig;
+ }
+ tp++;
+ }
+ shp->gd->sigmax = n++;
+ shp->st.trapcom = (char**)calloc(n,sizeof(char*));
+ shp->sigflag = (unsigned char*)calloc(n,1);
+ shp->gd->sigmsg = (char**)calloc(n,sizeof(char*));
+ for(tp=shtab_signals; sig=tp->sh_number; tp++)
+ {
+ n = (sig>>SH_SIGBITS);
+ if((sig &= ((1<<SH_SIGBITS)-1)) > (shp->gd->sigmax+1))
+ continue;
+ sig--;
+ if(n&SH_SIGRUNTIME)
+ sig = shp->gd->sigruntime[sig];
+ if(sig>=0)
+ {
+ shp->sigflag[sig] = n;
+ if(*tp->sh_name)
+ shp->gd->sigmsg[sig] = (char*)tp->sh_value;
+ }
+ }
+}
+
+/*
+ * Turn on trap handler for signal <sig>
+ */
+void sh_sigtrap(register int sig)
+{
+ register int flag;
+ void (*fun)(int);
+ sh.st.otrapcom = 0;
+ if(sig==0)
+ sh_sigdone();
+ else if(!((flag=sh.sigflag[sig])&(SH_SIGFAULT|SH_SIGOFF)))
+ {
+ /* don't set signal if already set or off by parent */
+ if((fun=signal(sig,sh_fault))==SIG_IGN)
+ {
+ signal(sig,SIG_IGN);
+ flag |= SH_SIGOFF;
+ }
+ else
+ {
+ flag |= SH_SIGFAULT;
+ if(sig==SIGALRM && fun!=SIG_DFL && fun!=sh_fault)
+ signal(sig,fun);
+ }
+ flag &= ~(SH_SIGSET|SH_SIGTRAP);
+ sh.sigflag[sig] = flag;
+ }
+}
+
+/*
+ * set signal handler so sh_done is called for all caught signals
+ */
+void sh_sigdone(void)
+{
+ register int flag, sig = shgd->sigmax;
+ sh.sigflag[0] |= SH_SIGFAULT;
+ for(sig=shgd->sigmax; sig>0; sig--)
+ {
+ flag = sh.sigflag[sig];
+ if((flag&(SH_SIGDONE|SH_SIGIGNORE|SH_SIGINTERACTIVE)) && !(flag&(SH_SIGFAULT|SH_SIGOFF)))
+ sh_sigtrap(sig);
+ }
+}
+
+/*
+ * Restore to default signals
+ * Free the trap strings if mode is non-zero
+ * If mode>1 then ignored traps cause signal to be ignored
+ */
+void sh_sigreset(register int mode)
+{
+ register char *trap;
+ register int flag, sig=sh.st.trapmax;
+ while(sig-- > 0)
+ {
+ if(trap=sh.st.trapcom[sig])
+ {
+ flag = sh.sigflag[sig]&~(SH_SIGTRAP|SH_SIGSET);
+ if(*trap)
+ {
+ if(mode)
+ free(trap);
+ sh.st.trapcom[sig] = 0;
+ }
+ else if(sig && mode>1)
+ {
+ if(sig!=SIGCHLD)
+ signal(sig,SIG_IGN);
+ flag &= ~SH_SIGFAULT;
+ flag |= SH_SIGOFF;
+ }
+ sh.sigflag[sig] = flag;
+ }
+ }
+ for(sig=SH_DEBUGTRAP-1;sig>=0;sig--)
+ {
+ if(trap=sh.st.trap[sig])
+ {
+ if(mode)
+ free(trap);
+ sh.st.trap[sig] = 0;
+ }
+
+ }
+ sh.st.trapcom[0] = 0;
+ if(mode)
+ sh.st.trapmax = 0;
+ sh.trapnote=0;
+}
+
+/*
+ * free up trap if set and restore signal handler if modified
+ */
+void sh_sigclear(register int sig)
+{
+ register int flag = sh.sigflag[sig];
+ register char *trap;
+ sh.st.otrapcom=0;
+ if(!(flag&SH_SIGFAULT))
+ return;
+ flag &= ~(SH_SIGTRAP|SH_SIGSET);
+ if(trap=sh.st.trapcom[sig])
+ {
+ if(!sh.subshell)
+ free(trap);
+ sh.st.trapcom[sig]=0;
+ }
+ sh.sigflag[sig] = flag;
+}
+
+/*
+ * check for traps
+ */
+
+void sh_chktrap(Shell_t* shp)
+{
+ register int sig=shp->st.trapmax;
+ register char *trap;
+ if(!(shp->trapnote&~SH_SIGIGNORE))
+ sig=0;
+ shp->trapnote &= ~SH_SIGTRAP;
+ /* execute errexit trap first */
+ if(sh_isstate(SH_ERREXIT) && shp->exitval)
+ {
+ int sav_trapnote = shp->trapnote;
+ shp->trapnote &= ~SH_SIGSET;
+ if(shp->st.trap[SH_ERRTRAP])
+ {
+ trap = shp->st.trap[SH_ERRTRAP];
+ shp->st.trap[SH_ERRTRAP] = 0;
+ sh_trap(trap,0);
+ shp->st.trap[SH_ERRTRAP] = trap;
+ }
+ shp->trapnote = sav_trapnote;
+ if(sh_isoption(SH_ERREXIT))
+ {
+ struct checkpt *pp = (struct checkpt*)shp->jmplist;
+ pp->mode = SH_JMPEXIT;
+ sh_exit(shp->exitval);
+ }
+ }
+ if(shp->sigflag[SIGALRM]&SH_SIGALRM)
+ sh_timetraps(shp);
+#ifdef SHOPT_BGX
+ if((shp->sigflag[SIGCHLD]&SH_SIGTRAP) && shp->st.trapcom[SIGCHLD])
+ job_chldtrap(shp,shp->st.trapcom[SIGCHLD],1);
+#endif /* SHOPT_BGX */
+ while(--sig>=0)
+ {
+ if(sig==cursig)
+ continue;
+#ifdef SHOPT_BGX
+ if(sig==SIGCHLD)
+ continue;
+#endif /* SHOPT_BGX */
+ if(shp->sigflag[sig]&SH_SIGTRAP)
+ {
+ shp->sigflag[sig] &= ~SH_SIGTRAP;
+ if(trap=shp->st.trapcom[sig])
+ {
+ cursig = sig;
+ sh_trap(trap,0);
+ cursig = -1;
+ }
+ }
+ }
+}
+
+
+/*
+ * parse and execute the given trap string, stream or tree depending on mode
+ * mode==0 for string, mode==1 for stream, mode==2 for parse tree
+ */
+int sh_trap(const char *trap, int mode)
+{
+ Shell_t *shp = sh_getinterp();
+ int jmpval, savxit = shp->exitval;
+ int was_history = sh_isstate(SH_HISTORY);
+ int was_verbose = sh_isstate(SH_VERBOSE);
+ int staktop = staktell();
+ char *savptr = stakfreeze(0);
+ char ifstable[256];
+ struct checkpt buff;
+ Fcin_t savefc;
+ fcsave(&savefc);
+ memcpy(ifstable,shp->ifstable,sizeof(ifstable));
+ sh_offstate(SH_HISTORY);
+ sh_offstate(SH_VERBOSE);
+ shp->intrap++;
+ sh_pushcontext(shp,&buff,SH_JMPTRAP);
+ jmpval = sigsetjmp(buff.buff,0);
+ if(jmpval == 0)
+ {
+ if(mode==2)
+ sh_exec((Shnode_t*)trap,sh_isstate(SH_ERREXIT));
+ else
+ {
+ Sfio_t *sp;
+ if(mode)
+ sp = (Sfio_t*)trap;
+ else
+ sp = sfopen(NIL(Sfio_t*),trap,"s");
+ sh_eval(sp,0);
+ }
+ }
+ else if(indone)
+ {
+ if(jmpval==SH_JMPSCRIPT)
+ indone=0;
+ else
+ {
+ if(jmpval==SH_JMPEXIT)
+ savxit = shp->exitval;
+ jmpval=SH_JMPTRAP;
+ }
+ }
+ sh_popcontext(shp,&buff);
+ shp->intrap--;
+ sfsync(shp->outpool);
+ if(!shp->indebug && jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN)
+ shp->exitval=savxit;
+ stakset(savptr,staktop);
+ fcrestore(&savefc);
+ memcpy(shp->ifstable,ifstable,sizeof(ifstable));
+ if(was_history)
+ sh_onstate(SH_HISTORY);
+ if(was_verbose)
+ sh_onstate(SH_VERBOSE);
+ exitset();
+ if(jmpval>SH_JMPTRAP && (((struct checkpt*)shp->jmpbuffer)->prev || ((struct checkpt*)shp->jmpbuffer)->mode==SH_JMPSCRIPT))
+ siglongjmp(*shp->jmplist,jmpval);
+ return(shp->exitval);
+}
+
+/*
+ * exit the current scope and jump to an earlier one based on pp->mode
+ */
+void sh_exit(register int xno)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct checkpt *pp = (struct checkpt*)shp->jmplist;
+ register int sig=0;
+ register Sfio_t* pool;
+ shp->exitval=xno;
+ if(xno==SH_EXITSIG)
+ shp->exitval |= (sig=shp->lastsig);
+ if(pp && pp->mode>1)
+ cursig = -1;
+#ifdef SIGTSTP
+ if(shp->trapnote&SH_SIGTSTP)
+ {
+ /* ^Z detected by the shell */
+ shp->trapnote = 0;
+ shp->sigflag[SIGTSTP] = 0;
+ if(!shp->subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK))
+ return;
+ if(sh_isstate(SH_TIMING))
+ return;
+ /* Handles ^Z for shell builtins, subshells, and functs */
+ shp->lastsig = 0;
+ sh_onstate(SH_MONITOR);
+ sh_offstate(SH_STOPOK);
+ shp->trapnote = 0;
+ if(!shp->subshell && (sig=sh_fork(shp,0,NIL(int*))))
+ {
+ job.curpgid = 0;
+ job.parent = (pid_t)-1;
+ job_wait(sig);
+ job.parent = 0;
+ shp->sigflag[SIGTSTP] = 0;
+ /* wait for child to stop */
+ shp->exitval = (SH_EXITSIG|SIGTSTP);
+ /* return to prompt mode */
+ pp->mode = SH_JMPERREXIT;
+ }
+ else
+ {
+ if(shp->subshell)
+ sh_subfork();
+ /* child process, put to sleep */
+ sh_offstate(SH_STOPOK);
+ sh_offstate(SH_MONITOR);
+ shp->sigflag[SIGTSTP] = 0;
+ /* stop child job */
+ killpg(job.curpgid,SIGTSTP);
+ /* child resumes */
+ job_clear();
+ shp->forked = 1;
+ shp->exitval = (xno&SH_EXITMASK);
+ return;
+ }
+ }
+#endif /* SIGTSTP */
+ /* unlock output pool */
+ sh_offstate(SH_NOTRACK);
+ if(!(pool=sfpool(NIL(Sfio_t*),shp->outpool,SF_WRITE)))
+ pool = shp->outpool; /* can't happen? */
+ sfclrlock(pool);
+#ifdef SIGPIPE
+ if(shp->lastsig==SIGPIPE)
+ sfpurge(pool);
+#endif /* SIGPIPE */
+ sfclrlock(sfstdin);
+ if(!pp)
+ sh_done(shp,sig);
+ shp->prefix = 0;
+#if SHOPT_TYPEDEF
+ shp->mktype = 0;
+#endif /* SHOPT_TYPEDEF*/
+ if(job.in_critical)
+ job_unlock();
+ if(pp->mode == SH_JMPSCRIPT && !pp->prev)
+ sh_done(shp,sig);
+ if(pp->mode)
+ siglongjmp(pp->buff,pp->mode);
+}
+
+static void array_notify(Namval_t *np, void *data)
+{
+ Namarr_t *ap = nv_arrayptr(np);
+ NOT_USED(data);
+ if(ap && ap->fun)
+ (*ap->fun)(np, 0, NV_AFREE);
+}
+
+/*
+ * This is the exit routine for the shell
+ */
+
+void sh_done(void *ptr, register int sig)
+{
+ Shell_t *shp = (Shell_t*)ptr;
+ register char *t;
+ register int savxit = shp->exitval;
+ shp->trapnote = 0;
+ indone=1;
+ if(sig)
+ savxit = SH_EXITSIG|sig;
+ if(shp->userinit)
+ (*shp->userinit)(shp, -1);
+ if(t=shp->st.trapcom[0])
+ {
+ shp->st.trapcom[0]=0; /*should free but not long */
+ shp->oldexit = savxit;
+ sh_trap(t,0);
+ savxit = shp->exitval;
+ }
+ else
+ {
+ /* avoid recursive call for set -e */
+ sh_offstate(SH_ERREXIT);
+ sh_chktrap(shp);
+ }
+ nv_scan(shp->var_tree,array_notify,(void*)0,NV_ARRAY,NV_ARRAY);
+ sh_freeup(shp);
+#if SHOPT_ACCT
+ sh_accend();
+#endif /* SHOPT_ACCT */
+#if SHOPT_VSH || SHOPT_ESH
+ if(mbwide()||sh_isoption(SH_EMACS)||sh_isoption(SH_VI)||sh_isoption(SH_GMACS))
+ tty_cooked(-1);
+#endif
+#ifdef JOBS
+ if((sh_isoption(SH_INTERACTIVE) && shp->login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP)))
+ job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**));
+#endif /* JOBS */
+ job_close(shp);
+ if(nv_search("VMTRACE", shp->var_tree,0))
+ strmatch((char*)0,(char*)0);
+ sfsync((Sfio_t*)sfstdin);
+ sfsync((Sfio_t*)shp->outpool);
+ sfsync((Sfio_t*)sfstdout);
+ if(savxit&SH_EXITSIG)
+ sig = savxit&SH_EXITMASK;
+ if(sig)
+ {
+ /* generate fault termination code */
+ if(RLIMIT_CORE!=RLIMIT_UNKNOWN)
+ {
+#ifdef _lib_getrlimit
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE,&rlp);
+ rlp.rlim_cur = 0;
+ setrlimit(RLIMIT_CORE,&rlp);
+#else
+ vlimit(RLIMIT_CORE,0);
+#endif
+ }
+ signal(sig,SIG_DFL);
+ sigrelease(sig);
+ kill(getpid(),sig);
+ pause();
+ }
+#if SHOPT_KIA
+ if(sh_isoption(SH_NOEXEC))
+ kiaclose((Lex_t*)shp->lex_context);
+#endif /* SHOPT_KIA */
+ exit(savxit&SH_EXITMASK);
+}
+
diff --git a/src/cmd/ksh93/sh/fcin.c b/src/cmd/ksh93/sh/fcin.c
new file mode 100644
index 0000000..ea6ea6b
--- /dev/null
+++ b/src/cmd/ksh93/sh/fcin.c
@@ -0,0 +1,214 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Routines to implement fast character input
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include <ast.h>
+#include <sfio.h>
+#include <error.h>
+#include <fcin.h>
+
+Fcin_t _Fcin = {0};
+
+/*
+ * open stream <f> for fast character input
+ */
+int fcfopen(register Sfio_t* f)
+{
+ register int n;
+ char *buff;
+ Fcin_t save;
+ errno = 0;
+ _Fcin.fcbuff = _Fcin.fcptr;
+ _Fcin._fcfile = f;
+ fcsave(&save);
+ if(!(buff=(char*)sfreserve(f,SF_UNBOUND,SF_LOCKR)))
+ {
+ fcrestore(&save);
+ _Fcin.fcchar = 0;
+ _Fcin.fcptr = _Fcin.fcbuff = &_Fcin.fcchar;
+ _Fcin.fclast = 0;
+ _Fcin._fcfile = (Sfio_t*)0;
+ return(EOF);
+ }
+ n = sfvalue(f);
+ fcrestore(&save);
+ sfread(f,buff,0);
+ _Fcin.fcoff = sftell(f);;
+ buff = (char*)sfreserve(f,SF_UNBOUND,SF_LOCKR);
+ _Fcin.fclast = (_Fcin.fcptr=_Fcin.fcbuff=(unsigned char*)buff)+n;
+ if(sffileno(f) >= 0)
+ *_Fcin.fclast = 0;
+ return(n);
+}
+
+
+/*
+ * With _Fcin.fcptr>_Fcin.fcbuff, the stream pointer is advanced and
+ * If _Fcin.fclast!=0, performs an sfreserve() for the next buffer.
+ * If a notify function has been set, it is called
+ * If last is non-zero, and the stream is a file, 0 is returned when
+ * the previous character is a 0 byte.
+ */
+int fcfill(void)
+{
+ register int n;
+ register Sfio_t *f;
+ register unsigned char *last=_Fcin.fclast, *ptr=_Fcin.fcptr;
+ if(!(f=fcfile()))
+ {
+ /* see whether pointer has passed null byte */
+ if(ptr>_Fcin.fcbuff && *--ptr==0)
+ _Fcin.fcptr=ptr;
+ else
+ _Fcin.fcoff = 0;
+ return(0);
+ }
+ if(last)
+ {
+ if( ptr<last && ptr>_Fcin.fcbuff && *(ptr-1)==0)
+ return(0);
+ if(_Fcin.fcchar)
+ *last = _Fcin.fcchar;
+ if(ptr > last)
+ _Fcin.fcptr = ptr = last;
+ }
+ if((n = ptr-_Fcin.fcbuff) && _Fcin.fcfun)
+ (*_Fcin.fcfun)(f,(const char*)_Fcin.fcbuff,n,_Fcin.context);
+ sfread(f, (char*)_Fcin.fcbuff, n);
+ _Fcin.fcoff +=n;
+ _Fcin._fcfile = 0;
+ if(!last)
+ return(0);
+ else if(fcfopen(f) < 0)
+ return(EOF);
+ return(*_Fcin.fcptr++);
+}
+
+/*
+ * Synchronize and close the current stream
+ */
+int fcclose(void)
+{
+ register unsigned char *ptr;
+ if(_Fcin.fclast==0)
+ return(0);
+ if((ptr=_Fcin.fcptr)>_Fcin.fcbuff && *(ptr-1)==0)
+ _Fcin.fcptr--;
+ if(_Fcin.fcchar)
+ *_Fcin.fclast = _Fcin.fcchar;
+ _Fcin.fclast = 0;
+ _Fcin.fcleft = 0;
+ return(fcfill());
+}
+
+/*
+ * Set the notify function that is called for each fcfill()
+ */
+void fcnotify(void (*fun)(Sfio_t*,const char*,int,void*),void* context)
+{
+ _Fcin.fcfun = fun;
+ _Fcin.context = context;
+}
+
+#ifdef __EXPORT__
+# define extern __EXPORT__
+#endif
+
+#undef fcsave
+extern void fcsave(Fcin_t *fp)
+{
+ *fp = _Fcin;
+}
+
+#undef fcrestore
+extern void fcrestore(Fcin_t *fp)
+{
+ _Fcin = *fp;
+}
+
+/* for testing purposes with small buffers */
+#if defined(IOBSIZE) && (IOBSIZE < 2*MB_LEN_MAX)
+# undef MB_LEN_MAX
+# define MB_LEN_MAX (IOBSIZE/2)
+#endif
+
+struct Extra
+{
+ unsigned char buff[2*MB_LEN_MAX];
+ unsigned char *next;
+};
+
+int _fcmbget(short *len)
+{
+ static struct Extra extra;
+ register int i, c, n;
+ if(_Fcin.fcleft)
+ {
+ if((c = mbsize(extra.next)) < 0)
+ c = 1;
+ if((_Fcin.fcleft -= c) <=0)
+ {
+ _Fcin.fcptr = (unsigned char*)fcfirst() - _Fcin.fcleft;
+ _Fcin.fcleft = 0;
+ }
+ *len = c;
+ if(c==1)
+ c = *extra.next++;
+ else if(c==0)
+ _Fcin.fcleft = 0;
+ else
+ c = mbchar(extra.next);
+ return(c);
+ }
+ switch(*len = mbsize(_Fcin.fcptr))
+ {
+ case -1:
+ if(_Fcin._fcfile && (n=(_Fcin.fclast-_Fcin.fcptr)) < MB_LEN_MAX)
+ {
+ memcpy(extra.buff, _Fcin.fcptr, n);
+ _Fcin.fcptr = _Fcin.fclast;
+ for(i=n; i < MB_LEN_MAX+n; i++)
+ {
+ if((extra.buff[i] = fcgetc(c))==0)
+ break;
+ }
+ _Fcin.fcleft = n;
+ extra.next = extra.buff;
+ return(fcmbget(len));
+ }
+ *len = 1;
+ /* fall through */
+ case 0:
+ case 1:
+ c=fcget();
+ break;
+ default:
+ c = mbchar(_Fcin.fcptr);
+ }
+ return(c);
+}
+
diff --git a/src/cmd/ksh93/sh/init.c b/src/cmd/ksh93/sh/init.c
new file mode 100644
index 0000000..16e30df
--- /dev/null
+++ b/src/cmd/ksh93/sh/init.c
@@ -0,0 +1,2238 @@
+/***********************************************************************
+* *
+* 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
+/*
+ *
+ * Shell initialization
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <stak.h>
+#include <ccode.h>
+#include <pwd.h>
+#include <tmx.h>
+#include "variables.h"
+#include "path.h"
+#include "fault.h"
+#include "name.h"
+#include "edit.h"
+#include "jobs.h"
+#include "io.h"
+#include "shlex.h"
+#include "builtins.h"
+#include "FEATURE/time"
+#include "FEATURE/dynamic"
+#include "FEATURE/externs"
+#include "lexstates.h"
+#include "version.h"
+
+#if _hdr_wctype
+#include <ast_wchar.h>
+#include <wctype.h>
+#endif
+#if !_typ_wctrans_t
+#undef wctrans_t
+#define wctrans_t sh_wctrans_t
+typedef long wctrans_t;
+#endif
+#if !_lib_wctrans
+#undef wctrans
+#define wctrans sh_wctrans
+static wctrans_t wctrans(const char *name)
+{
+ if(strcmp(name,e_tolower)==0)
+ return(1);
+ else if(strcmp(name,e_toupper)==0)
+ return(2);
+ return(0);
+}
+#endif
+#if !_lib_towctrans
+#undef towctrans
+#define towctrans sh_towctrans
+static int towctrans(int c, wctrans_t t)
+{
+ if(t==1 && isupper(c))
+ c = tolower(c);
+ else if(t==2 && islower(c))
+ c = toupper(c);
+ return(c);
+}
+#endif
+
+char e_version[] = "\n@(#)$Id: Version "
+#if SHOPT_AUDIT
+#define ATTRS 1
+ "A"
+#endif
+#if SHOPT_BASH
+#define ATTRS 1
+ "B"
+#endif
+#if SHOPT_COSHELL
+#define ATTRS 1
+ "J"
+#else
+#if SHOPT_BGX
+#define ATTRS 1
+ "j"
+#endif
+#endif
+#if SHOPT_ACCT
+#define ATTRS 1
+ "L"
+#endif
+#if SHOPT_MULTIBYTE
+#define ATTRS 1
+ "M"
+#endif
+#if SHOPT_PFSH && _hdr_exec_attr
+#define ATTRS 1
+ "P"
+#endif
+#if SHOPT_REGRESS
+#define ATTRS 1
+ "R"
+#endif
+#if ATTRS
+ " "
+#endif
+ SH_RELEASE " $\0\n";
+
+#if SHOPT_BASH
+ extern void bash_init(Shell_t*,int);
+#endif
+
+#define RANDMASK 0x7fff
+
+#ifndef ARG_MAX
+# define ARG_MAX (1*1024*1024)
+#endif
+#ifndef CHILD_MAX
+# define CHILD_MAX (1*1024)
+#endif
+#ifndef CLK_TCK
+# define CLK_TCK 60
+#endif /* CLK_TCK */
+
+#ifndef environ
+ extern char **environ;
+#endif
+
+#undef getconf
+#define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0)
+
+struct seconds
+{
+ Namfun_t hdr;
+ Shell_t *sh;
+};
+
+struct rand
+{
+ Namfun_t hdr;
+ Shell_t *sh;
+ int32_t rand_last;
+};
+
+struct ifs
+{
+ Namfun_t hdr;
+ Namval_t *ifsnp;
+};
+
+struct match
+{
+ Namfun_t hdr;
+ const char *v;
+ char *val;
+ char *rval[2];
+ int *match;
+ char node[NV_MINSZ+sizeof(char*)];
+ int first;
+ int vsize;
+ int nmatch;
+ int index;
+ int lastsub[2];
+};
+
+typedef struct _init_
+{
+ Shell_t *sh;
+#if SHOPT_FS_3D
+ Namfun_t VPATH_init;
+#endif /* SHOPT_FS_3D */
+ struct ifs IFS_init;
+ Namfun_t PATH_init;
+ Namfun_t FPATH_init;
+ Namfun_t CDPATH_init;
+ Namfun_t SHELL_init;
+ Namfun_t ENV_init;
+ Namfun_t VISUAL_init;
+ Namfun_t EDITOR_init;
+ Namfun_t HISTFILE_init;
+ Namfun_t HISTSIZE_init;
+ Namfun_t OPTINDEX_init;
+ struct seconds SECONDS_init;
+ struct rand RAND_init;
+ Namfun_t LINENO_init;
+ Namfun_t L_ARG_init;
+ Namfun_t SH_VERSION_init;
+ struct match SH_MATCH_init;
+ Namfun_t SH_MATH_init;
+#if SHOPT_COSHELL
+ Namfun_t SH_JOBPOOL_init;
+#endif /* SHOPT_COSHELL */
+#ifdef _hdr_locale
+ Namfun_t LC_TYPE_init;
+ Namfun_t LC_NUM_init;
+ Namfun_t LC_COLL_init;
+ Namfun_t LC_MSG_init;
+ Namfun_t LC_ALL_init;
+ Namfun_t LANG_init;
+#endif /* _hdr_locale */
+} Init_t;
+
+static Init_t *ip;
+static int lctype;
+static int nbltins;
+static void env_init(Shell_t*);
+static Init_t *nv_init(Shell_t*);
+static Dt_t *inittree(Shell_t*,const struct shtable2*);
+static int shlvl;
+
+#ifdef _WINIX
+# define EXE "?(.exe)"
+#else
+# define EXE
+#endif
+
+static int rand_shift;
+
+
+/*
+ * Invalidate all path name bindings
+ */
+static void rehash(register Namval_t *np,void *data)
+{
+ NOT_USED(data);
+ nv_onattr(np,NV_NOALIAS);
+}
+
+/*
+ * out of memory routine for stak routines
+ */
+static char *nospace(int unused)
+{
+ NOT_USED(unused);
+ errormsg(SH_DICT,ERROR_exit(3),e_nospace);
+ return(NIL(char*));
+}
+
+/* Trap for VISUAL and EDITOR variables */
+static void put_ed(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ register const char *cp, *name=nv_name(np);
+ register int newopt=0;
+ Shell_t *shp = nv_shell(np);
+ if(*name=='E' && nv_getval(sh_scoped(shp,VISINOD)))
+ goto done;
+ if(!(cp=val) && (*name=='E' || !(cp=nv_getval(sh_scoped(shp,EDITNOD)))))
+ goto done;
+ /* turn on vi or emacs option if editor name is either*/
+ cp = path_basename(cp);
+ if(strmatch(cp,"*[Vv][Ii]*"))
+ newopt=SH_VI;
+ else if(strmatch(cp,"*gmacs*"))
+ newopt=SH_GMACS;
+ else if(strmatch(cp,"*macs*"))
+ newopt=SH_EMACS;
+ if(newopt)
+ {
+ sh_offoption(SH_VI);
+ sh_offoption(SH_EMACS);
+ sh_offoption(SH_GMACS);
+ sh_onoption(newopt);
+ }
+done:
+ nv_putv(np, val, flags, fp);
+}
+
+/* Trap for HISTFILE and HISTSIZE variables */
+static void put_history(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ void *histopen = shp->gd->hist_ptr;
+ char *cp;
+ if(val && histopen)
+ {
+ if(np==HISTFILE && (cp=nv_getval(np)) && strcmp(val,cp)==0)
+ return;
+ if(np==HISTSIZE && sh_arith(shp,val)==nv_getnum(HISTSIZE))
+ return;
+ hist_close(shp->gd->hist_ptr);
+ }
+ nv_putv(np, val, flags, fp);
+ if(histopen)
+ {
+ if(val)
+ sh_histinit(shp);
+ else
+ hist_close(histopen);
+ }
+}
+
+/* Trap for OPTINDEX */
+static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ shp->st.opterror = shp->st.optchar = 0;
+ nv_putv(np, val, flags, fp);
+ if(!val)
+ nv_disc(np,fp,NV_POP);
+}
+
+static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp)
+{
+ return((Sfdouble_t)*np->nvalue.lp);
+}
+
+static Namfun_t *clone_optindex(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ Namfun_t *dp = (Namfun_t*)malloc(sizeof(Namfun_t));
+ memcpy((void*)dp,(void*)fp,sizeof(Namfun_t));
+ mp->nvalue.lp = np->nvalue.lp;
+ dp->nofree = 0;
+ return(dp);
+}
+
+
+/* Trap for restricted variables FPATH, PATH, SHELL, ENV */
+static void put_restricted(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ int path_scoped = 0, fpath_scoped=0;
+ Pathcomp_t *pp;
+ char *name = nv_name(np);
+ if(!(flags&NV_RDONLY) && sh_isoption(SH_RESTRICTED))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np));
+ if(np==PATHNOD || (path_scoped=(strcmp(name,PATHNOD->nvname)==0)))
+ {
+ nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED);
+ if(path_scoped && !val)
+ val = PATHNOD->nvalue.cp;
+ }
+ if(val && !(flags&NV_RDONLY) && np->nvalue.cp && strcmp(val,np->nvalue.cp)==0)
+ return;
+ if(np==FPATHNOD || (fpath_scoped=(strcmp(name,FPATHNOD->nvname)==0)))
+ shp->pathlist = (void*)path_unsetfpath(shp);
+ nv_putv(np, val, flags, fp);
+ shp->universe = 0;
+ if(shp->pathlist)
+ {
+ val = np->nvalue.cp;
+ if(np==PATHNOD || path_scoped)
+ pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH);
+ else if(val && (np==FPATHNOD || fpath_scoped))
+ pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
+ else
+ return;
+ if(shp->pathlist = (void*)pp)
+ pp->shp = shp;
+ if(!val && (flags&NV_NOSCOPE))
+ {
+ Namval_t *mp = dtsearch(shp->var_tree,np);
+ if(mp && (val=nv_getval(mp)))
+ nv_putval(mp,val,NV_RDONLY);
+ }
+#if 0
+sfprintf(sfstderr,"%d: name=%s val=%s\n",getpid(),name,val);
+path_dump((Pathcomp_t*)shp->pathlist);
+#endif
+ }
+}
+
+static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ Pathcomp_t *pp;
+ Shell_t *shp = nv_shell(np);
+ nv_putv(np, val, flags, fp);
+ if(!shp->cdpathlist)
+ return;
+ val = np->nvalue.cp;
+ pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->cdpathlist,val,PATH_CDPATH);
+ if(shp->cdpathlist = (void*)pp)
+ pp->shp = shp;
+}
+
+#ifdef _hdr_locale
+ /*
+ * This function needs to be modified to handle international
+ * error message translations
+ */
+#if ERROR_VERSION >= 20000101L
+ static char* msg_translate(const char* catalog, const char* message)
+ {
+ NOT_USED(catalog);
+ return((char*)message);
+ }
+#else
+ static char* msg_translate(const char* message, int type)
+ {
+ NOT_USED(type);
+ return((char*)message);
+ }
+#endif
+
+ /* Trap for LC_ALL, LC_CTYPE, LC_MESSAGES, LC_COLLATE and LANG */
+ static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+ {
+ Shell_t *shp = nv_shell(np);
+ int type;
+ char *name = nv_name(np);
+ if(name==(LCALLNOD)->nvname)
+ type = LC_ALL;
+ else if(name==(LCTYPENOD)->nvname)
+ type = LC_CTYPE;
+ else if(name==(LCMSGNOD)->nvname)
+ type = LC_MESSAGES;
+ else if(name==(LCCOLLNOD)->nvname)
+ type = LC_COLLATE;
+ else if(name==(LCNUMNOD)->nvname)
+ type = LC_NUMERIC;
+#ifdef LC_LANG
+ else if(name==(LANGNOD)->nvname)
+ type = LC_LANG;
+#else
+#define LC_LANG LC_ALL
+ else if(name==(LANGNOD)->nvname && (!(name=nv_getval(LCALLNOD)) || !*name))
+ type = LC_LANG;
+#endif
+ else
+ type= -1;
+ if(!sh_isstate(SH_INIT) && (type>=0 || type==LC_ALL || type==LC_LANG))
+ {
+ char* r;
+#ifdef AST_LC_setenv
+ ast.locale.set |= AST_LC_setenv;
+#endif
+ r = setlocale(type,val?val:"");
+#ifdef AST_LC_setenv
+ ast.locale.set ^= AST_LC_setenv;
+#endif
+ if(!r && val)
+ {
+ if(!sh_isstate(SH_INIT) || shp->login_sh==0)
+ errormsg(SH_DICT,0,e_badlocale,val);
+ return;
+ }
+ }
+ nv_putv(np, val, flags, fp);
+ if(CC_NATIVE!=CC_ASCII && (type==LC_ALL || type==LC_LANG || type==LC_CTYPE))
+ {
+ if(sh_lexstates[ST_BEGIN]!=sh_lexrstates[ST_BEGIN])
+ free((void*)sh_lexstates[ST_BEGIN]);
+ lctype++;
+ if(ast.locale.set&(1<<AST_LC_CTYPE))
+ {
+ register int c;
+ char *state[4];
+ sh_lexstates[ST_BEGIN] = state[0] = (char*)malloc(4*(1<<CHAR_BIT));
+ memcpy(state[0],sh_lexrstates[ST_BEGIN],(1<<CHAR_BIT));
+ sh_lexstates[ST_NAME] = state[1] = state[0] + (1<<CHAR_BIT);
+ memcpy(state[1],sh_lexrstates[ST_NAME],(1<<CHAR_BIT));
+ sh_lexstates[ST_DOL] = state[2] = state[1] + (1<<CHAR_BIT);
+ memcpy(state[2],sh_lexrstates[ST_DOL],(1<<CHAR_BIT));
+ sh_lexstates[ST_BRACE] = state[3] = state[2] + (1<<CHAR_BIT);
+ memcpy(state[3],sh_lexrstates[ST_BRACE],(1<<CHAR_BIT));
+ for(c=0; c<(1<<CHAR_BIT); c++)
+ {
+ if(state[0][c]!=S_REG)
+ continue;
+ if(state[2][c]!=S_ERR)
+ continue;
+ if(isblank(c))
+ {
+ state[0][c]=0;
+ state[1][c]=S_BREAK;
+ state[2][c]=S_BREAK;
+ continue;
+ }
+ if(!isalpha(c))
+ continue;
+ state[0][c]=S_NAME;
+ if(state[1][c]==S_REG)
+ state[1][c]=0;
+ state[2][c]=S_ALP;
+ if(state[3][c]==S_ERR)
+ state[3][c]=0;
+ }
+ }
+ else
+ {
+ sh_lexstates[ST_BEGIN]=(char*)sh_lexrstates[ST_BEGIN];
+ sh_lexstates[ST_NAME]=(char*)sh_lexrstates[ST_NAME];
+ sh_lexstates[ST_DOL]=(char*)sh_lexrstates[ST_DOL];
+ sh_lexstates[ST_BRACE]=(char*)sh_lexrstates[ST_BRACE];
+ }
+ }
+#if ERROR_VERSION < 20000101L
+ if(type==LC_ALL || type==LC_MESSAGES)
+ error_info.translate = msg_translate;
+#endif
+ }
+#endif /* _hdr_locale */
+
+/* Trap for IFS assignment and invalidates state table */
+static void put_ifs(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ register struct ifs *ip = (struct ifs*)fp;
+ ip->ifsnp = 0;
+ if(!val)
+ {
+ fp = nv_stack(np, NIL(Namfun_t*));
+ if(fp && !fp->nofree)
+ {
+ free((void*)fp);
+ fp = 0;
+ }
+ }
+ if(val != np->nvalue.cp)
+ nv_putv(np, val, flags, fp);
+ if(!val)
+ {
+ if(fp)
+ fp->next = np->nvfun;
+ np->nvfun = fp;
+ }
+}
+
+/*
+ * This is the lookup function for IFS
+ * It keeps the sh.ifstable up to date
+ */
+static char* get_ifs(register Namval_t* np, Namfun_t *fp)
+{
+ register struct ifs *ip = (struct ifs*)fp;
+ register char *cp, *value;
+ register int c,n;
+ register Shell_t *shp = nv_shell(np);
+ value = nv_getv(np,fp);
+ if(np!=ip->ifsnp)
+ {
+ ip->ifsnp = np;
+ memset(shp->ifstable,0,(1<<CHAR_BIT));
+ if(cp=value)
+ {
+#if SHOPT_MULTIBYTE
+ while(n=mbsize(cp),c= *(unsigned char*)cp)
+#else
+ while(c= *(unsigned char*)cp++)
+#endif /* SHOPT_MULTIBYTE */
+ {
+#if SHOPT_MULTIBYTE
+ cp++;
+ if(n>1)
+ {
+ cp += (n-1);
+ shp->ifstable[c] = S_MBYTE;
+ continue;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ n = S_DELIM;
+ if(c== *cp)
+ cp++;
+ else if(c=='\n')
+ n = S_NL;
+ else if(isspace(c))
+ n = S_SPACE;
+ shp->ifstable[c] = n;
+ }
+ }
+ else
+ {
+ shp->ifstable[' '] = shp->ifstable['\t'] = S_SPACE;
+ shp->ifstable['\n'] = S_NL;
+ }
+ }
+ return(value);
+}
+
+/*
+ * these functions are used to get and set the SECONDS variable
+ */
+#ifdef timeofday
+# define dtime(tp) ((double)((tp)->tv_sec)+1e-6*((double)((tp)->tv_usec)))
+# define tms timeval
+#else
+# define dtime(tp) (((double)times(tp))/shgd->lim.clk_tck)
+# define timeofday(a)
+#endif
+
+static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ double d;
+ struct tms tp;
+ if(!val)
+ {
+ nv_putv(np, val, flags, fp);
+ fp = nv_stack(np, NIL(Namfun_t*));
+ if(fp && !fp->nofree)
+ free((void*)fp);
+ return;
+ }
+ if(!np->nvalue.dp)
+ {
+ nv_setsize(np,3);
+ nv_onattr(np,NV_DOUBLE);
+ np->nvalue.dp = new_of(double,0);
+ }
+ nv_putv(np, val, flags, fp);
+ d = *np->nvalue.dp;
+ timeofday(&tp);
+ *np->nvalue.dp = dtime(&tp)-d;
+}
+
+static char* get_seconds(register Namval_t* np, Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ register int places = nv_size(np);
+ struct tms tp;
+ double d, offset = (np->nvalue.dp?*np->nvalue.dp:0);
+ NOT_USED(fp);
+ timeofday(&tp);
+ d = dtime(&tp)- offset;
+ sfprintf(shp->strbuf,"%.*f",places,d);
+ return(sfstruse(shp->strbuf));
+}
+
+static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp)
+{
+ struct tms tp;
+ double offset = (np->nvalue.dp?*np->nvalue.dp:0);
+ NOT_USED(fp);
+ timeofday(&tp);
+ return(dtime(&tp)- offset);
+}
+
+/*
+ * These three functions are used to get and set the RANDOM variable
+ */
+static void put_rand(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ struct rand *rp = (struct rand*)fp;
+ register long n;
+ if(!val)
+ {
+ fp = nv_stack(np, NIL(Namfun_t*));
+ if(fp && !fp->nofree)
+ free((void*)fp);
+ _nv_unset(np,0);
+ return;
+ }
+ if(flags&NV_INTEGER)
+ n = *(double*)val;
+ else
+ n = sh_arith(rp->sh,val);
+ srand((int)(n&RANDMASK));
+ rp->rand_last = -1;
+ if(!np->nvalue.lp)
+ np->nvalue.lp = &rp->rand_last;
+}
+
+/*
+ * get random number in range of 0 - 2**15
+ * never pick same number twice in a row
+ */
+static Sfdouble_t nget_rand(register Namval_t* np, Namfun_t *fp)
+{
+ register long cur, last= *np->nvalue.lp;
+ NOT_USED(fp);
+ do
+ cur = (rand()>>rand_shift)&RANDMASK;
+ while(cur==last);
+ *np->nvalue.lp = cur;
+ return((Sfdouble_t)cur);
+}
+
+static char* get_rand(register Namval_t* np, Namfun_t *fp)
+{
+ register long n = nget_rand(np,fp);
+ return(fmtbase(n, 10, 0));
+}
+
+/*
+ * These three routines are for LINENO
+ */
+static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp)
+{
+ double d=1;
+ if(error_info.line >0)
+ d = error_info.line;
+ else if(error_info.context && error_info.context->line>0)
+ d = error_info.context->line;
+ NOT_USED(np);
+ NOT_USED(fp);
+ return(d);
+}
+
+static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ register long n;
+ Shell_t *shp = nv_shell(np);
+ if(!val)
+ {
+ fp = nv_stack(np, NIL(Namfun_t*));
+ if(fp && !fp->nofree)
+ free((void*)fp);
+ _nv_unset(np,0);
+ return;
+ }
+ if(flags&NV_INTEGER)
+ n = *(double*)val;
+ else
+ n = sh_arith(shp,val);
+ shp->st.firstline += nget_lineno(np,fp)+1-n;
+}
+
+static char* get_lineno(register Namval_t* np, Namfun_t *fp)
+{
+ register long n = nget_lineno(np,fp);
+ return(fmtbase(n, 10, 0));
+}
+
+static char* get_lastarg(Namval_t* np, Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ char *cp;
+ int pid;
+ if(sh_isstate(SH_INIT) && (cp=shp->lastarg) && *cp=='*' && (pid=strtol(cp+1,&cp,10)) && *cp=='*')
+ nv_putval(np,(pid==shp->gd->ppid?cp+1:0),0);
+ return(shp->lastarg);
+}
+
+static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ if(flags&NV_INTEGER)
+ {
+ sfprintf(shp->strbuf,"%.*g",12,*((double*)val));
+ val = sfstruse(shp->strbuf);
+ }
+ if(val)
+ val = strdup(val);
+ if(shp->lastarg && !nv_isattr(np,NV_NOFREE))
+ free((void*)shp->lastarg);
+ else
+ nv_offattr(np,NV_NOFREE);
+ shp->lastarg = (char*)val;
+ nv_offattr(np,NV_EXPORT);
+ np->nvenv = 0;
+}
+
+static int hasgetdisc(register Namfun_t *fp)
+{
+ while(fp && !fp->disc->getnum && !fp->disc->getval)
+ fp = fp->next;
+ return(fp!=0);
+}
+
+/*
+ * store the most recent value for use in .sh.match
+ * treat .sh.match as a two dimensional array
+ */
+void sh_setmatch(Shell_t *shp,const char *v, int vsize, int nmatch, int match[],int index)
+{
+ struct match *mp = &ip->SH_MATCH_init;
+ Namval_t *np = nv_namptr(mp->node,0);
+ register int i,n,x, savesub=shp->subshell;
+ Namarr_t *ap = nv_arrayptr(SH_MATCHNOD);
+ shp->subshell = 0;
+#ifndef SHOPT_2DMATCH
+ index = 0;
+#else
+ if(index==0)
+#endif /* SHOPT_2DMATCH */
+ {
+ if(ap->hdr.next != &mp->hdr)
+ {
+ free((void*)ap);
+ ap = nv_arrayptr(np);
+ SH_MATCHNOD->nvfun = &ap->hdr;
+ }
+ if(ap)
+ {
+ ap->nelem &= ~ARRAY_SCAN;
+ i = array_elem(ap);
+ ap->nelem++;
+ while(--i>= 0)
+ {
+ nv_putsub(SH_MATCHNOD, (char*)0,i);
+ _nv_unset(SH_MATCHNOD,NV_RDONLY);
+ }
+ ap->nelem--;
+ }
+ if(!nv_hasdisc(SH_MATCHNOD,mp->hdr.disc))
+ nv_disc(SH_MATCHNOD,&mp->hdr,NV_LAST);
+ if(nmatch)
+ nv_putsub(SH_MATCHNOD, NIL(char*), (nmatch-1)|ARRAY_FILL|ARRAY_SETSUB);
+ ap = nv_arrayptr(SH_MATCHNOD);
+ ap->nelem = mp->nmatch = nmatch;
+ mp->v = v;
+ mp->first = match[0];
+ }
+#ifdef SHOPT_2DMATCH
+ else
+ {
+ if(index==1)
+ {
+ np->nvalue.cp = Empty;
+ np->nvfun = SH_MATCHNOD->nvfun;
+ nv_onattr(np,NV_NOFREE|NV_ARRAY);
+ SH_MATCHNOD->nvfun = 0;
+ for(i=0; i < mp->nmatch; i++)
+ {
+ nv_putsub(SH_MATCHNOD, (char*)0, i);
+ nv_arraychild(SH_MATCHNOD, np,0);
+ }
+ if(ap = nv_arrayptr(SH_MATCHNOD))
+ ap->nelem = mp->nmatch;
+ }
+ ap = nv_arrayptr(np);
+ nv_putsub(np, NIL(char*), index|ARRAY_FILL|ARRAY_SETSUB);
+ }
+#endif /* SHOPT_2DMATCH */
+ shp->subshell = savesub;
+ index *= 2*mp->nmatch;
+ if(mp->nmatch)
+ {
+ for(n=mp->first+(mp->v-v),vsize=0,i=0; i < 2*nmatch; i++)
+ {
+ if(match[i]>=0 && (match[i] - n) > vsize)
+ vsize = match[i] -n;
+ }
+ i = (index+2*mp->nmatch)*sizeof(match[0]);
+ if((i+vsize) >= mp->vsize)
+ {
+ if(mp->vsize)
+ mp->match = (int*)realloc(mp->match,i+vsize+1);
+ else
+ mp->match = (int*)malloc(i+vsize+1);
+ mp->vsize = i+vsize+1;
+ }
+ mp->val = ((char*)mp->match)+i;
+ memcpy(mp->match+index,match,nmatch*2*sizeof(match[0]));
+ for(x=0,i=0; i < 2*nmatch; i++)
+ {
+ if(match[i]>=0)
+ mp->match[index+i] -= n;
+ else
+ x=1;
+
+ }
+ ap->nelem -= x;
+ while(i < 2*mp->nmatch)
+ mp->match[index+i++] = -1;
+ memcpy(mp->val,v+n,vsize);
+ mp->val[vsize] = 0;
+ mp->lastsub[0] = mp->lastsub[1] = -1;
+ }
+}
+
+#define array_scan(np) ((nv_arrayptr(np)->nelem&ARRAY_SCAN))
+
+static char* get_match(register Namval_t* np, Namfun_t *fp)
+{
+ struct match *mp = (struct match*)fp;
+ int sub,sub2=0,n,i =!mp->index;
+ char *val;
+ sub = nv_aindex(SH_MATCHNOD);
+ if(np!=SH_MATCHNOD)
+ sub2 = nv_aindex(np);
+ if(sub>=mp->nmatch)
+ return(0);
+ if(sub2>0)
+ sub += sub2*mp->nmatch;
+ if(sub==mp->lastsub[!i])
+ return(mp->rval[!i]);
+ else if(sub==mp->lastsub[i])
+ return(mp->rval[i]);
+ n = mp->match[2*sub+1]-mp->match[2*sub];
+ if(n<=0)
+ return(mp->match[2*sub]<0?Empty:"");
+ val = mp->val+mp->match[2*sub];
+ if(mp->val[mp->match[2*sub+1]]==0)
+ return(val);
+ mp->index = i;
+ if(mp->rval[i])
+ {
+ free((void*)mp->rval[i]);
+ mp->rval[i] = 0;
+ }
+ mp->rval[i] = (char*)malloc(n+1);
+ mp->lastsub[i] = sub;
+ memcpy(mp->rval[i],val,n);
+ mp->rval[i][n] = 0;
+ return(mp->rval[i]);
+}
+
+static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match };
+
+static char* get_version(register Namval_t* np, Namfun_t *fp)
+{
+ return(nv_getv(np,fp));
+}
+
+static Sfdouble_t nget_version(register Namval_t* np, Namfun_t *fp)
+{
+ register const char *cp = e_version + strlen(e_version)-10;
+ register int c;
+ Sflong_t t = 0;
+ NOT_USED(fp);
+
+ while (c = *cp++)
+ if (c >= '0' && c <= '9')
+ {
+ t *= 10;
+ t += c - '0';
+ }
+ return((Sfdouble_t)t);
+}
+
+static const Namdisc_t SH_VERSION_disc = { 0, 0, get_version, nget_version };
+
+#if SHOPT_FS_3D
+ /*
+ * set or unset the mappings given a colon separated list of directories
+ */
+ static void vpath_set(char *str, int mode)
+ {
+ register char *lastp, *oldp=str, *newp=strchr(oldp,':');
+ if(!shgd->lim.fs3d)
+ return;
+ while(newp)
+ {
+ *newp++ = 0;
+ if(lastp=strchr(newp,':'))
+ *lastp = 0;
+ mount((mode?newp:""),oldp,FS3D_VIEW,0);
+ newp[-1] = ':';
+ oldp = newp;
+ newp=lastp;
+ }
+ }
+
+ /* catch vpath assignments */
+ static void put_vpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+ {
+ register char *cp;
+ if(cp = nv_getval(np))
+ vpath_set(cp,0);
+ if(val)
+ vpath_set((char*)val,1);
+ nv_putv(np,val,flags,fp);
+ }
+ static const Namdisc_t VPATH_disc = { 0, put_vpath };
+ static Namfun_t VPATH_init = { &VPATH_disc, 1 };
+#endif /* SHOPT_FS_3D */
+
+
+static const Namdisc_t IFS_disc = { sizeof(struct ifs), put_ifs, get_ifs };
+const Namdisc_t RESTRICTED_disc = { sizeof(Namfun_t), put_restricted };
+static const Namdisc_t CDPATH_disc = { sizeof(Namfun_t), put_cdpath };
+static const Namdisc_t EDITOR_disc = { sizeof(Namfun_t), put_ed };
+static const Namdisc_t HISTFILE_disc = { sizeof(Namfun_t), put_history };
+static const Namdisc_t OPTINDEX_disc = { sizeof(Namfun_t), put_optindex, 0, nget_optindex, 0, 0, clone_optindex };
+static const Namdisc_t SECONDS_disc = { sizeof(struct seconds), put_seconds, get_seconds, nget_seconds };
+static const Namdisc_t RAND_disc = { sizeof(struct rand), put_rand, get_rand, nget_rand };
+static const Namdisc_t LINENO_disc = { sizeof(Namfun_t), put_lineno, get_lineno, nget_lineno };
+static const Namdisc_t L_ARG_disc = { sizeof(Namfun_t), put_lastarg, get_lastarg };
+
+
+#define MAX_MATH_ARGS 3
+
+static char *name_math(Namval_t *np, Namfun_t *fp)
+{
+ Shell_t *shp = sh_getinterp();
+ sfprintf(shp->strbuf,".sh.math.%s",np->nvname);
+ return(sfstruse(shp->strbuf));
+}
+
+static const Namdisc_t math_child_disc =
+{
+ 0,0,0,0,0,0,0,
+ name_math
+};
+
+static Namfun_t math_child_fun =
+{
+ &math_child_disc, 1, 0, sizeof(Namfun_t)
+};
+
+static void math_init(Shell_t *shp)
+{
+ Namval_t *np;
+ char *name;
+ int i;
+ shp->mathnodes = (char*)calloc(1,MAX_MATH_ARGS*(NV_MINSZ+5));
+ name = shp->mathnodes+MAX_MATH_ARGS*NV_MINSZ;
+ for(i=0; i < MAX_MATH_ARGS; i++)
+ {
+ np = nv_namptr(shp->mathnodes,i);
+ np->nvfun = &math_child_fun;
+ memcpy(name,"arg",3);
+ name[3] = '1'+i;
+ np->nvname = name;
+ name+=5;
+ nv_onattr(np,NV_MINIMAL|NV_NOFREE|NV_LDOUBLE|NV_RDONLY);
+ }
+}
+
+static Namval_t *create_math(Namval_t *np,const char *name,int flag,Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ if(!name)
+ return(SH_MATHNOD);
+ if(name[0]!='a' || name[1]!='r' || name[2]!='g' || name[4] || !isdigit(name[3]) || (name[3]=='0' || (name[3]-'0')>MAX_MATH_ARGS))
+ return(0);
+ fp->last = (char*)&name[4];
+ return(nv_namptr(shp->mathnodes,name[3]-'1'));
+}
+
+static char* get_math(register Namval_t* np, Namfun_t *fp)
+{
+ Shell_t *shp = nv_shell(np);
+ Namval_t *mp,fake;
+ char *val;
+ int first=0;
+ fake.nvname = ".sh.math.";
+ mp = (Namval_t*)dtprev(shp->fun_tree,&fake);
+ while(mp=(Namval_t*)dtnext(shp->fun_tree,mp))
+ {
+ if(memcmp(mp->nvname,".sh.math.",9))
+ break;
+ if(first++)
+ sfputc(shp->strbuf,' ');
+ sfputr(shp->strbuf,mp->nvname+9,-1);
+ }
+ val = sfstruse(shp->strbuf);
+ return(val);
+
+}
+
+static char *setdisc_any(Namval_t *np, const char *event, Namval_t *action, Namfun_t *fp)
+{
+ Shell_t *shp=nv_shell(np);
+ Namval_t *mp,fake;
+ char *name;
+ int getname=0, off=staktell();
+ fake.nvname = nv_name(np);
+ if(!event)
+ {
+ if(!action)
+ {
+ mp = (Namval_t*)dtprev(shp->fun_tree,&fake);
+ return((char*)dtnext(shp->fun_tree,mp));
+ }
+ getname = 1;
+ }
+ stakputs(fake.nvname);
+ stakputc('.');
+ stakputs(event);
+ stakputc(0);
+ name = stakptr(off);
+ mp = nv_search(name, shp->fun_tree, action?NV_ADD:0);
+ stakseek(off);
+ if(getname)
+ return(mp?(char*)dtnext(shp->fun_tree,mp):0);
+ if(action==np)
+ action = mp;
+ return(action?(char*)action:"");
+}
+
+static const Namdisc_t SH_MATH_disc = { 0, 0, get_math, 0, setdisc_any, create_math, };
+
+#if SHOPT_COSHELL
+static const Namdisc_t SH_JOBPOOL_disc = { 0, 0, 0, 0, setdisc_any, 0, };
+#endif /* SHOPT_COSHELL */
+
+#if SHOPT_NAMESPACE
+ static char* get_nspace(Namval_t* np, Namfun_t *fp)
+ {
+ if(sh.namespace)
+ return(nv_name(sh.namespace));
+ return((char*)np->nvalue.cp);
+ }
+ static const Namdisc_t NSPACE_disc = { 0, 0, get_nspace };
+ static Namfun_t NSPACE_init = { &NSPACE_disc, 1};
+#endif /* SHOPT_NAMESPACE */
+
+#ifdef _hdr_locale
+ static const Namdisc_t LC_disc = { sizeof(Namfun_t), put_lang };
+#endif /* _hdr_locale */
+
+/*
+ * This function will get called whenever a configuration parameter changes
+ */
+static int newconf(const char *name, const char *path, const char *value)
+{
+ Shell_t *shp = sh_getinterp();
+ register char *arg;
+ if(!name)
+ setenviron(value);
+ else if(strcmp(name,"UNIVERSE")==0 && strcmp(astconf(name,0,0),value))
+ {
+ shp->universe = 0;
+ /* set directory in new universe */
+ if(*(arg = path_pwd(shp,0))=='/')
+ chdir(arg);
+ /* clear out old tracked alias */
+ stakseek(0);
+ stakputs(nv_getval(PATHNOD));
+ stakputc(0);
+ nv_putval(PATHNOD,stakseek(0),NV_RDONLY);
+ }
+ return(1);
+}
+
+#if (CC_NATIVE != CC_ASCII)
+ static void a2e(char *d, const char *s)
+ {
+ register const unsigned char *t;
+ register int i;
+ t = CCMAP(CC_ASCII, CC_NATIVE);
+ for(i=0; i<(1<<CHAR_BIT); i++)
+ d[t[i]] = s[i];
+ }
+
+ static void init_ebcdic(void)
+ {
+ int i;
+ char *cp = (char*)malloc(ST_NONE*(1<<CHAR_BIT));
+ for(i=0; i < ST_NONE; i++)
+ {
+ a2e(cp,sh_lexrstates[i]);
+ sh_lexstates[i] = cp;
+ cp += (1<<CHAR_BIT);
+ }
+ }
+#endif
+
+/*
+ * return SH_TYPE_* bitmask for path
+ * 0 for "not a shell"
+ */
+int sh_type(register const char *path)
+{
+ register const char* s;
+ register int t = 0;
+
+ if (s = (const char*)strrchr(path, '/'))
+ {
+ if (*path == '-')
+ t |= SH_TYPE_LOGIN;
+ s++;
+ }
+ else
+ s = path;
+ if (*s == '-')
+ {
+ s++;
+ t |= SH_TYPE_LOGIN;
+ }
+ for (;;)
+ {
+ if (!(t & (SH_TYPE_KSH|SH_TYPE_BASH)))
+ {
+ if (*s == 'k')
+ {
+ s++;
+ t |= SH_TYPE_KSH;
+ continue;
+ }
+#if SHOPT_BASH
+ if (*s == 'b' && *(s+1) == 'a')
+ {
+ s += 2;
+ t |= SH_TYPE_BASH;
+ continue;
+ }
+#endif
+ }
+ if (!(t & (SH_TYPE_PROFILE|SH_TYPE_RESTRICTED)))
+ {
+#if SHOPT_PFSH
+ if (*s == 'p' && *(s+1) == 'f')
+ {
+ s += 2;
+ t |= SH_TYPE_PROFILE;
+ continue;
+ }
+#endif
+ if (*s == 'r')
+ {
+ s++;
+ t |= SH_TYPE_RESTRICTED;
+ continue;
+ }
+ }
+ break;
+ }
+ if (*s++ == 's' && (*s == 'h' || *s == 'u'))
+ {
+ s++;
+ t |= SH_TYPE_SH;
+ if ((t & SH_TYPE_KSH) && *s == '9' && *(s+1) == '3')
+ s += 2;
+#if _WINIX
+ if (*s == '.' && *(s+1) == 'e' && *(s+2) == 'x' && *(s+3) == 'e')
+ s += 4;
+#endif
+ if (!isalnum(*s))
+ return t;
+ }
+ return t & ~(SH_TYPE_BASH|SH_TYPE_KSH|SH_TYPE_PROFILE|SH_TYPE_RESTRICTED);
+}
+
+
+static char *get_mode(Namval_t* np, Namfun_t* nfp)
+{
+ mode_t mode = nv_getn(np,nfp);
+ return(fmtperm(mode));
+}
+
+static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
+{
+ if(val)
+ {
+ mode_t mode;
+ char *last=0;
+ if(flag&NV_INTEGER)
+ {
+ if(flag&NV_LONG)
+ mode = *(Sfdouble_t*)val;
+ else
+ mode = *(double*)val;
+ }
+ else
+ mode = strperm(val, &last,0);
+ if(*last)
+ errormsg(SH_DICT,ERROR_exit(1),"%s: invalid mode string",val);
+ nv_putv(np,(char*)&mode,NV_INTEGER,nfp);
+ }
+ else
+ nv_putv(np,val,flag,nfp);
+}
+
+static const Namdisc_t modedisc =
+{
+ 0,
+ put_mode,
+ get_mode,
+};
+
+
+/*
+ * initialize the shell
+ */
+Shell_t *sh_init(register int argc,register char *argv[], Shinit_f userinit)
+{
+ static int beenhere;
+ Shell_t *shp;
+ register int n;
+ int type;
+ static char *login_files[3];
+ memfatal();
+ n = strlen(e_version);
+ if(e_version[n-1]=='$' && e_version[n-2]==' ')
+ e_version[n-2]=0;
+#if (CC_NATIVE == CC_ASCII)
+ memcpy(sh_lexstates,sh_lexrstates,ST_NONE*sizeof(char*));
+#else
+ init_ebcdic();
+#endif
+ if(!beenhere)
+ {
+ beenhere = 1;
+ shp = &sh;
+ shgd = newof(0,struct shared,1,0);
+ shgd->pid = getpid();
+ shgd->ppid = getppid();
+ shgd->userid=getuid();
+ shgd->euserid=geteuid();
+ shgd->groupid=getgid();
+ shgd->egroupid=getegid();
+ shgd->lim.clk_tck = getconf("CLK_TCK");
+ shgd->lim.arg_max = getconf("ARG_MAX");
+ shgd->lim.child_max = getconf("CHILD_MAX");
+ shgd->lim.ngroups_max = getconf("NGROUPS_MAX");
+ shgd->lim.posix_version = getconf("VERSION");
+ shgd->lim.posix_jobcontrol = getconf("JOB_CONTROL");
+ if(shgd->lim.arg_max <=0)
+ shgd->lim.arg_max = ARG_MAX;
+ if(shgd->lim.child_max <=0)
+ shgd->lim.child_max = CHILD_MAX;
+ if(shgd->lim.clk_tck <=0)
+ shgd->lim.clk_tck = CLK_TCK;
+#if SHOPT_FS_3D
+ if(fs3d(FS3D_TEST))
+ shgd->lim.fs3d = 1;
+#endif /* SHOPT_FS_3D */
+ shgd->ed_context = (void*)ed_open(shp);
+ error_info.exit = sh_exit;
+ error_info.id = path_basename(argv[0]);
+ }
+ else
+ shp = newof(0,Shell_t,1,0);
+ umask(shp->mask=umask(0));
+ shp->gd = shgd;
+ shp->mac_context = sh_macopen(shp);
+ shp->arg_context = sh_argopen(shp);
+ shp->lex_context = (void*)sh_lexopen(0,shp,1);
+ shp->strbuf = sfstropen();
+ shp->stk = stkstd;
+ sfsetbuf(shp->strbuf,(char*)0,64);
+ sh_onstate(SH_INIT);
+#if ERROR_VERSION >= 20000102L
+ error_info.catalog = e_dict;
+#endif
+#if SHOPT_REGRESS
+ {
+ Opt_t* nopt;
+ Opt_t* oopt;
+ char* a;
+ char** av = argv;
+ char* regress[3];
+
+ sh_regress_init(shp);
+ regress[0] = "__regress__";
+ regress[2] = 0;
+ /* NOTE: only shp is used by __regress__ at this point */
+ shp->bltindata.shp = shp;
+ while ((a = *++av) && a[0] == '-' && (a[1] == 'I' || a[1] == '-' && a[2] == 'r'))
+ {
+ if (a[1] == 'I')
+ {
+ if (a[2])
+ regress[1] = a + 2;
+ else if (!(regress[1] = *++av))
+ break;
+ }
+ else if (strncmp(a+2, "regress", 7))
+ break;
+ else if (a[9] == '=')
+ regress[1] = a + 10;
+ else if (!(regress[1] = *++av))
+ break;
+ nopt = optctx(0, 0);
+ oopt = optctx(nopt, 0);
+ b___regress__(2, regress, &shp->bltindata);
+ optctx(oopt, nopt);
+ }
+ }
+#endif
+ shp->cpipe[0] = -1;
+ shp->coutpipe = -1;
+ for(n=0;n < 10; n++)
+ {
+ /* don't use lower bits when rand() generates large numbers */
+ if(rand() > RANDMASK)
+ {
+ rand_shift = 3;
+ break;
+ }
+ }
+ sh_ioinit(shp);
+ /* initialize signal handling */
+ sh_siginit(shp);
+ stakinstall(NIL(Stak_t*),nospace);
+ /* set up memory for name-value pairs */
+ shp->init_context = nv_init(shp);
+ /* read the environment */
+ if(argc>0)
+ {
+ type = sh_type(*argv);
+ if(type&SH_TYPE_LOGIN)
+ shp->login_sh = 2;
+ }
+ env_init(shp);
+ if(!ENVNOD->nvalue.cp)
+ {
+ sfprintf(shp->strbuf,"%s/.kshrc",nv_getval(HOME));
+ nv_putval(ENVNOD,sfstruse(shp->strbuf),NV_RDONLY);
+ }
+ *SHLVL->nvalue.ip +=1;
+ nv_offattr(SHLVL,NV_IMPORT);
+#if SHOPT_SPAWN
+ {
+ /*
+ * try to find the pathname for this interpreter
+ * try using environment variable _ or argv[0]
+ */
+ char *cp=nv_getval(L_ARGNOD);
+ char buff[PATH_MAX+1];
+ shp->gd->shpath = 0;
+#if _AST_VERSION >= 20090202L
+ if((n = pathprog(NiL, buff, sizeof(buff))) > 0 && n <= sizeof(buff))
+ shp->gd->shpath = strdup(buff);
+#else
+ sfprintf(shp->strbuf,"/proc/%d/exe",getpid());
+ if((n=readlink(sfstruse(shp->strbuf),buff,sizeof(buff)-1))>0)
+ {
+ buff[n] = 0;
+ shp->gd->shpath = strdup(buff);
+ }
+#endif
+ else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/')))
+ {
+ if(*cp=='/')
+ shp->gd->shpath = strdup(cp);
+ else if(cp = nv_getval(PWDNOD))
+ {
+ int offset = staktell();
+ stakputs(cp);
+ stakputc('/');
+ stakputs(argv[0]);
+ pathcanon(stakptr(offset),PATH_DOTDOT);
+ shp->gd->shpath = strdup(stakptr(offset));
+ stakseek(offset);
+ }
+ }
+ }
+#endif
+ nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY);
+#if SHOPT_FS_3D
+ nv_stack(VPATHNOD, &VPATH_init);
+#endif /* SHOPT_FS_3D */
+ astconfdisc(newconf);
+#if SHOPT_TIMEOUT
+ shp->st.tmout = SHOPT_TIMEOUT;
+#endif /* SHOPT_TIMEOUT */
+ /* initialize jobs table */
+ job_clear();
+ if(argc>0)
+ {
+ /* check for restricted shell */
+ if(type&SH_TYPE_RESTRICTED)
+ sh_onoption(SH_RESTRICTED);
+#if SHOPT_PFSH
+ /* check for profile shell */
+ else if(type&SH_TYPE_PROFILE)
+ sh_onoption(SH_PFSH);
+#endif
+#if SHOPT_BASH
+ /* check for invocation as bash */
+ if(type&SH_TYPE_BASH)
+ {
+ shp>userinit = userinit = bash_init;
+ sh_onoption(SH_BASH);
+ sh_onstate(SH_PREINIT);
+ (*userinit)(shp, 0);
+ sh_offstate(SH_PREINIT);
+ }
+#endif
+ /* look for options */
+ /* shp->st.dolc is $# */
+ if((shp->st.dolc = sh_argopts(-argc,argv,shp)) < 0)
+ {
+ shp->exitval = 2;
+ sh_done(shp,0);
+ }
+ opt_info.disc = 0;
+ shp->st.dolv=argv+(argc-1)-shp->st.dolc;
+ shp->st.dolv[0] = argv[0];
+ if(shp->st.dolc < 1)
+ sh_onoption(SH_SFLAG);
+ if(!sh_isoption(SH_SFLAG))
+ {
+ shp->st.dolc--;
+ shp->st.dolv++;
+#if _WINIX
+ {
+ char* name;
+ name = shp->st.dolv[0];
+ if(name[1]==':' && (name[2]=='/' || name[2]=='\\'))
+ {
+#if _lib_pathposix
+ char* p;
+
+ if((n = pathposix(name, NIL(char*), 0)) > 0 && (p = (char*)malloc(++n)))
+ {
+ pathposix(name, p, n);
+ name = p;
+ }
+ else
+#endif
+ {
+ name[1] = name[0];
+ name[0] = name[2] = '/';
+ }
+ }
+ }
+#endif /* _WINIX */
+ }
+ if(beenhere==1)
+ {
+ struct lconv* lc;
+ shp->decomma = (lc=localeconv()) && lc->decimal_point && *lc->decimal_point==',';
+ beenhere = 2;
+ }
+ }
+#if SHOPT_PFSH
+ if (sh_isoption(SH_PFSH))
+ {
+ struct passwd *pw = getpwuid(shp->gd->userid);
+ if(pw)
+ shp->gd->user = strdup(pw->pw_name);
+
+ }
+#endif
+ /* set[ug]id scripts require the -p flag */
+ if(shp->gd->userid!=shp->gd->euserid || shp->gd->groupid!=shp->gd->egroupid)
+ {
+#ifdef SHOPT_P_SUID
+ /* require sh -p to run setuid and/or setgid */
+ if(!sh_isoption(SH_PRIVILEGED) && shp->gd->userid >= SHOPT_P_SUID)
+ {
+ setuid(shp->gd->euserid=shp->gd->userid);
+ setgid(shp->gd->egroupid=shp->gd->groupid);
+ }
+ else
+#endif /* SHOPT_P_SUID */
+ sh_onoption(SH_PRIVILEGED);
+#ifdef SHELLMAGIC
+ /* careful of #! setuid scripts with name beginning with - */
+ if(shp->login_sh && argv[1] && strcmp(argv[0],argv[1])==0)
+ errormsg(SH_DICT,ERROR_exit(1),e_prohibited);
+#endif /*SHELLMAGIC*/
+ }
+ else
+ sh_offoption(SH_PRIVILEGED);
+ /* shname for $0 in profiles and . scripts */
+ if(sh_isdevfd(argv[1]))
+ shp->shname = strdup(argv[0]);
+ else
+ shp->shname = strdup(shp->st.dolv[0]);
+ /*
+ * return here for shell script execution
+ * but not for parenthesis subshells
+ */
+ error_info.id = strdup(shp->st.dolv[0]); /* error_info.id is $0 */
+ shp->jmpbuffer = (void*)&shp->checkbase;
+ sh_pushcontext(shp,&shp->checkbase,SH_JMPSCRIPT);
+ shp->st.self = &shp->global;
+ shp->topscope = (Shscope_t*)shp->st.self;
+ sh_offstate(SH_INIT);
+ login_files[0] = (char*)e_profile;
+ login_files[1] = ".profile";
+ shp->gd->login_files = login_files;
+ shp->bltindata.version = SH_VERSION;
+ shp->bltindata.shp = shp;
+ shp->bltindata.shrun = sh_run;
+ shp->bltindata.shtrap = sh_trap;
+ shp->bltindata.shexit = sh_exit;
+ shp->bltindata.shbltin = sh_addbuiltin;
+#if _AST_VERSION >= 20080617L
+ shp->bltindata.shgetenv = sh_getenv;
+ shp->bltindata.shsetenv = sh_setenviron;
+ astintercept(&shp->bltindata,1);
+#endif
+#if 0
+#define NV_MKINTTYPE(x,y,z) nv_mkinttype(#x,sizeof(x),(x)-1<0,(y),(Namdisc_t*)z);
+ NV_MKINTTYPE(pid_t,"process id",0);
+ NV_MKINTTYPE(gid_t,"group id",0);
+ NV_MKINTTYPE(uid_t,"user id",0);
+ NV_MKINTTYPE(size_t,(const char*)0,0);
+ NV_MKINTTYPE(ssize_t,(const char*)0,0);
+ NV_MKINTTYPE(off_t,"offset in bytes",0);
+ NV_MKINTTYPE(ino_t,"\ai-\anode number",0);
+ NV_MKINTTYPE(mode_t,(const char*)0,&modedisc);
+ NV_MKINTTYPE(dev_t,"device id",0);
+ NV_MKINTTYPE(nlink_t,"hard link count",0);
+ NV_MKINTTYPE(blkcnt_t,"block count",0);
+ NV_MKINTTYPE(time_t,"seconds since the epoch",0);
+ nv_mkstat();
+#endif
+ if(shp->userinit=userinit)
+ (*userinit)(shp, 0);
+ return(shp);
+}
+
+Shell_t *sh_getinterp(void)
+{
+ return(&sh);
+}
+
+/*
+ * reinitialize before executing a script
+ */
+int sh_reinit(char *argv[])
+{
+ Shell_t *shp = sh_getinterp();
+ Shopt_t opt;
+ Namval_t *np,*npnext;
+ Dt_t *dp;
+ struct adata
+ {
+ Shell_t *sh;
+ void *extra[2];
+ } data;
+ for(np=dtfirst(shp->fun_tree);np;np=npnext)
+ {
+ if((dp=shp->fun_tree)->walk)
+ dp = dp->walk;
+ npnext = (Namval_t*)dtnext(shp->fun_tree,np);
+ if(np>= shgd->bltin_cmds && np < &shgd->bltin_cmds[nbltins])
+ continue;
+ if(is_abuiltin(np) && nv_isattr(np,NV_EXPORT))
+ continue;
+ if(*np->nvname=='/')
+ continue;
+ nv_delete(np,dp,NV_NOFREE);
+ }
+ dtclose(shp->alias_tree);
+ shp->alias_tree = inittree(shp,shtab_aliases);
+ shp->last_root = shp->var_tree;
+ shp->inuse_bits = 0;
+ if(shp->userinit)
+ (*shp->userinit)(shp, 1);
+ if(shp->heredocs)
+ {
+ sfclose(shp->heredocs);
+ shp->heredocs = 0;
+ }
+ /* remove locals */
+ sh_onstate(SH_INIT);
+ memset(&data,0,sizeof(data));
+ data.sh = shp;
+ nv_scan(shp->var_tree,sh_envnolocal,(void*)&data,NV_EXPORT,0);
+ nv_scan(shp->var_tree,sh_envnolocal,(void*)&data,NV_ARRAY,NV_ARRAY);
+ sh_offstate(SH_INIT);
+ memset(shp->st.trapcom,0,(shp->st.trapmax+1)*sizeof(char*));
+ memset((void*)&opt,0,sizeof(opt));
+#if SHOPT_NAMESPACE
+ if(shp->namespace)
+ {
+ dp=nv_dict(shp->namespace);
+ if(dp==shp->var_tree)
+ shp->var_tree = dtview(dp,0);
+ _nv_unset(shp->namespace,NV_RDONLY);
+ shp->namespace = 0;
+ }
+#endif /* SHOPT_NAMESPACE */
+ if(sh_isoption(SH_TRACKALL))
+ on_option(&opt,SH_TRACKALL);
+ if(sh_isoption(SH_EMACS))
+ on_option(&opt,SH_EMACS);
+ if(sh_isoption(SH_GMACS))
+ on_option(&opt,SH_GMACS);
+ if(sh_isoption(SH_VI))
+ on_option(&opt,SH_VI);
+ if(sh_isoption(SH_VIRAW))
+ on_option(&opt,SH_VIRAW);
+ shp->options = opt;
+ /* set up new args */
+ if(argv)
+ shp->arglist = sh_argcreate(argv);
+ if(shp->arglist)
+ sh_argreset(shp,shp->arglist,NIL(struct dolnod*));
+ shp->envlist=0;
+ shp->curenv = 0;
+ shp->shname = error_info.id = strdup(shp->st.dolv[0]);
+ sh_offstate(SH_FORKED);
+ shp->fn_depth = shp->dot_depth = 0;
+ sh_sigreset(0);
+ if(!(SHLVL->nvalue.ip))
+ {
+ shlvl = 0;
+ SHLVL->nvalue.ip = &shlvl;
+ nv_onattr(SHLVL,NV_INTEGER|NV_EXPORT|NV_NOFREE);
+ }
+ *SHLVL->nvalue.ip +=1;
+ nv_offattr(SHLVL,NV_IMPORT);
+ shp->st.filename = strdup(shp->lastarg);
+ nv_delete((Namval_t*)0, (Dt_t*)0, 0);
+ job.exitval = 0;
+ shp->inpipe = shp->outpipe = 0;
+ job_clear();
+ job.in_critical = 0;
+ return(1);
+}
+
+/*
+ * set when creating a local variable of this name
+ */
+Namfun_t *nv_cover(register Namval_t *np)
+{
+ if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS || np==ENVNOD || np==LINENO)
+ return(np->nvfun);
+#ifdef _hdr_locale
+ if(np==LCALLNOD || np==LCTYPENOD || np==LCMSGNOD || np==LCCOLLNOD || np==LCNUMNOD || np==LANGNOD)
+ return(np->nvfun);
+#endif
+ return(0);
+}
+
+static const char *shdiscnames[] = { "tilde", 0};
+
+#ifdef SHOPT_STATS
+struct Stats
+{
+ Namfun_t hdr;
+ Shell_t *sh;
+ char *nodes;
+ int numnodes;
+ int current;
+};
+
+static Namval_t *next_stat(register Namval_t* np, Dt_t *root,Namfun_t *fp)
+{
+ struct Stats *sp = (struct Stats*)fp;
+ if(!root)
+ sp->current = 0;
+ else if(++sp->current>=sp->numnodes)
+ return(0);
+ return(nv_namptr(sp->nodes,sp->current));
+}
+
+static Namval_t *create_stat(Namval_t *np,const char *name,int flag,Namfun_t *fp)
+{
+ struct Stats *sp = (struct Stats*)fp;
+ register const char *cp=name;
+ register int i=0,n;
+ Namval_t *nq=0;
+ Shell_t *shp = sp->sh;
+ if(!name)
+ return(SH_STATS);
+ while((i=*cp++) && i != '=' && i != '+' && i!='[');
+ n = (cp-1) -name;
+ for(i=0; i < sp->numnodes; i++)
+ {
+ nq = nv_namptr(sp->nodes,i);
+ if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0)
+ goto found;
+ }
+ nq = 0;
+found:
+ if(nq)
+ {
+ fp->last = (char*)&name[n];
+ shp->last_table = SH_STATS;
+ }
+ else
+ errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np));
+ return(nq);
+}
+
+static const Namdisc_t stat_disc =
+{
+ 0, 0, 0, 0, 0,
+ create_stat,
+ 0, 0,
+ next_stat
+};
+
+static char *name_stat(Namval_t *np, Namfun_t *fp)
+{
+ Shell_t *shp = sh_getinterp();
+ sfprintf(shp->strbuf,".sh.stats.%s",np->nvname);
+ return(sfstruse(shp->strbuf));
+}
+
+static const Namdisc_t stat_child_disc =
+{
+ 0,0,0,0,0,0,0,
+ name_stat
+};
+
+static Namfun_t stat_child_fun =
+{
+ &stat_child_disc, 1, 0, sizeof(Namfun_t)
+};
+
+static void stat_init(Shell_t *shp)
+{
+ int i,nstat = STAT_SUBSHELL+1;
+ struct Stats *sp = newof(0,struct Stats,1,nstat*NV_MINSZ);
+ Namval_t *np;
+ sp->numnodes = nstat;
+ sp->nodes = (char*)(sp+1);
+ shgd->stats = (int*)calloc(sizeof(int),nstat);
+ sp->sh = shp;
+ for(i=0; i < nstat; i++)
+ {
+ np = nv_namptr(sp->nodes,i);
+ np->nvfun = &stat_child_fun;
+ np->nvname = (char*)shtab_stats[i].sh_name;
+ nv_onattr(np,NV_RDONLY|NV_MINIMAL|NV_NOFREE|NV_INTEGER);
+ nv_setsize(np,10);
+ np->nvalue.ip = &shgd->stats[i];
+ }
+ sp->hdr.dsize = sizeof(struct Stats) + nstat*(sizeof(int)+NV_MINSZ);
+ sp->hdr.disc = &stat_disc;
+ nv_stack(SH_STATS,&sp->hdr);
+ sp->hdr.nofree = 1;
+ nv_setvtree(SH_STATS);
+}
+#else
+# define stat_init(x)
+#endif /* SHOPT_STATS */
+
+/*
+ * Initialize the shell name and alias table
+ */
+static Init_t *nv_init(Shell_t *shp)
+{
+ double d=0;
+ ip = newof(0,Init_t,1,0);
+ if(!ip)
+ return(0);
+ shp->nvfun.last = (char*)shp;
+ shp->nvfun.nofree = 1;
+ ip->sh = shp;
+ shp->var_base = shp->var_tree = inittree(shp,shtab_variables);
+ SHLVL->nvalue.ip = &shlvl;
+ ip->IFS_init.hdr.disc = &IFS_disc;
+ ip->PATH_init.disc = &RESTRICTED_disc;
+ ip->PATH_init.nofree = 1;
+ ip->FPATH_init.disc = &RESTRICTED_disc;
+ ip->FPATH_init.nofree = 1;
+ ip->CDPATH_init.disc = &CDPATH_disc;
+ ip->CDPATH_init.nofree = 1;
+ ip->SHELL_init.disc = &RESTRICTED_disc;
+ ip->SHELL_init.nofree = 1;
+ ip->ENV_init.disc = &RESTRICTED_disc;
+ ip->ENV_init.nofree = 1;
+ ip->VISUAL_init.disc = &EDITOR_disc;
+ ip->VISUAL_init.nofree = 1;
+ ip->EDITOR_init.disc = &EDITOR_disc;
+ ip->EDITOR_init.nofree = 1;
+ ip->HISTFILE_init.disc = &HISTFILE_disc;
+ ip->HISTFILE_init.nofree = 1;
+ ip->HISTSIZE_init.disc = &HISTFILE_disc;
+ ip->HISTSIZE_init.nofree = 1;
+ ip->OPTINDEX_init.disc = &OPTINDEX_disc;
+ ip->OPTINDEX_init.nofree = 1;
+ ip->SECONDS_init.hdr.disc = &SECONDS_disc;
+ ip->SECONDS_init.hdr.nofree = 1;
+ ip->RAND_init.hdr.disc = &RAND_disc;
+ ip->RAND_init.hdr.nofree = 1;
+ ip->RAND_init.sh = shp;
+ ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc;
+ ip->SH_MATCH_init.hdr.nofree = 1;
+ ip->SH_MATH_init.disc = &SH_MATH_disc;
+ ip->SH_MATH_init.nofree = 1;
+#if SHOPT_COSHELL
+ ip->SH_JOBPOOL_init.disc = &SH_JOBPOOL_disc;
+ ip->SH_JOBPOOL_init.nofree = 1;
+ nv_stack(SH_JOBPOOL, &ip->SH_JOBPOOL_init);
+#endif /* SHOPT_COSHELL */
+ ip->SH_VERSION_init.disc = &SH_VERSION_disc;
+ ip->SH_VERSION_init.nofree = 1;
+ ip->LINENO_init.disc = &LINENO_disc;
+ ip->LINENO_init.nofree = 1;
+ ip->L_ARG_init.disc = &L_ARG_disc;
+ ip->L_ARG_init.nofree = 1;
+#ifdef _hdr_locale
+ ip->LC_TYPE_init.disc = &LC_disc;
+ ip->LC_TYPE_init.nofree = 1;
+ ip->LC_NUM_init.disc = &LC_disc;
+ ip->LC_NUM_init.nofree = 1;
+ ip->LC_COLL_init.disc = &LC_disc;
+ ip->LC_COLL_init.nofree = 1;
+ ip->LC_MSG_init.disc = &LC_disc;
+ ip->LC_MSG_init.nofree = 1;
+ ip->LC_ALL_init.disc = &LC_disc;
+ ip->LC_ALL_init.nofree = 1;
+ ip->LANG_init.disc = &LC_disc;
+ ip->LANG_init.nofree = 1;
+#endif /* _hdr_locale */
+ nv_stack(IFSNOD, &ip->IFS_init.hdr);
+ ip->IFS_init.hdr.nofree = 1;
+ nv_stack(PATHNOD, &ip->PATH_init);
+ nv_stack(FPATHNOD, &ip->FPATH_init);
+ nv_stack(CDPNOD, &ip->CDPATH_init);
+ nv_stack(SHELLNOD, &ip->SHELL_init);
+ nv_stack(ENVNOD, &ip->ENV_init);
+ nv_stack(VISINOD, &ip->VISUAL_init);
+ nv_stack(EDITNOD, &ip->EDITOR_init);
+ nv_stack(HISTFILE, &ip->HISTFILE_init);
+ nv_stack(HISTSIZE, &ip->HISTSIZE_init);
+ nv_stack(OPTINDNOD, &ip->OPTINDEX_init);
+ nv_stack(SECONDS, &ip->SECONDS_init.hdr);
+ nv_stack(L_ARGNOD, &ip->L_ARG_init);
+ nv_putval(SECONDS, (char*)&d, NV_DOUBLE);
+ nv_stack(RANDNOD, &ip->RAND_init.hdr);
+ d = (shp->gd->pid&RANDMASK);
+ nv_putval(RANDNOD, (char*)&d, NV_DOUBLE);
+ nv_stack(LINENO, &ip->LINENO_init);
+ SH_MATCHNOD->nvfun = &ip->SH_MATCH_init.hdr;
+ nv_putsub(SH_MATCHNOD,(char*)0,10);
+ nv_stack(SH_MATHNOD, &ip->SH_MATH_init);
+ nv_stack(SH_VERSIONNOD, &ip->SH_VERSION_init);
+#ifdef _hdr_locale
+ nv_stack(LCTYPENOD, &ip->LC_TYPE_init);
+ nv_stack(LCALLNOD, &ip->LC_ALL_init);
+ nv_stack(LCMSGNOD, &ip->LC_MSG_init);
+ nv_stack(LCCOLLNOD, &ip->LC_COLL_init);
+ nv_stack(LCNUMNOD, &ip->LC_NUM_init);
+ nv_stack(LANGNOD, &ip->LANG_init);
+#endif /* _hdr_locale */
+ (PPIDNOD)->nvalue.lp = (&shp->gd->ppid);
+ (TMOUTNOD)->nvalue.lp = (&shp->st.tmout);
+ (MCHKNOD)->nvalue.lp = (&sh_mailchk);
+ (OPTINDNOD)->nvalue.lp = (&shp->st.optindex);
+ /* set up the seconds clock */
+ shp->alias_tree = inittree(shp,shtab_aliases);
+ shp->track_tree = dtopen(&_Nvdisc,Dtset);
+ shp->bltin_tree = inittree(shp,(const struct shtable2*)shtab_builtins);
+ shp->fun_tree = dtopen(&_Nvdisc,Dtoset);
+ dtview(shp->fun_tree,shp->bltin_tree);
+ nv_mount(DOTSHNOD, "type", shp->typedict=dtopen(&_Nvdisc,Dtoset));
+ nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0);
+ DOTSHNOD->nvalue.cp = Empty;
+ SH_LINENO->nvalue.ip = &shp->st.lineno;
+ VERSIONNOD->nvalue.nrp = newof(0,struct Namref,1,0);
+ VERSIONNOD->nvalue.nrp->np = SH_VERSIONNOD;
+ VERSIONNOD->nvalue.nrp->root = nv_dict(DOTSHNOD);
+ VERSIONNOD->nvalue.nrp->table = DOTSHNOD;
+ nv_onattr(VERSIONNOD,NV_REF);
+ math_init(shp);
+ if(!shgd->stats)
+ stat_init(shp);
+ return(ip);
+}
+
+/*
+ * initialize name-value pairs
+ */
+
+static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals)
+{
+ register Namval_t *np;
+ register const struct shtable2 *tp;
+ register unsigned n = 0;
+ register Dt_t *treep;
+ Dt_t *base_treep, *dict;
+ for(tp=name_vals;*tp->sh_name;tp++)
+ n++;
+ np = (Namval_t*)calloc(n,sizeof(Namval_t));
+ if(!shgd->bltin_nodes)
+ {
+ shgd->bltin_nodes = np;
+ shgd->bltin_nnodes = n;
+ }
+ else if(name_vals==(const struct shtable2*)shtab_builtins)
+ {
+ shgd->bltin_cmds = np;
+ nbltins = n;
+ }
+ base_treep = treep = dtopen(&_Nvdisc,Dtoset);
+ treep->user = (void*)shp;
+ for(tp=name_vals;*tp->sh_name;tp++,np++)
+ {
+ if((np->nvname = strrchr(tp->sh_name,'.')) && np->nvname!=((char*)tp->sh_name))
+ np->nvname++;
+ else
+ {
+ np->nvname = (char*)tp->sh_name;
+ treep = base_treep;
+ }
+ np->nvenv = 0;
+ if(name_vals==(const struct shtable2*)shtab_builtins)
+ np->nvalue.bfp = (Nambfp_f)((struct shtable3*)tp)->sh_value;
+ else
+ {
+ if(name_vals == shtab_variables)
+ np->nvfun = &shp->nvfun;
+ np->nvalue.cp = (char*)tp->sh_value;
+ }
+ nv_setattr(np,tp->sh_number);
+ if(nv_isattr(np,NV_TABLE))
+ nv_mount(np,(const char*)0,dict=dtopen(&_Nvdisc,Dtoset));
+ if(nv_isattr(np,NV_INTEGER))
+ nv_setsize(np,10);
+ else
+ nv_setsize(np,0);
+ dtinsert(treep,np);
+ if(nv_istable(np))
+ treep = dict;
+ }
+ return(treep);
+}
+
+/*
+ * read in the process environment and set up name-value pairs
+ * skip over items that are not name-value pairs
+ */
+
+static void env_init(Shell_t *shp)
+{
+ register char *cp;
+ register Namval_t *np,*mp;
+ register char **ep=environ;
+ char *dp,*next=0;
+ int nenv=0,k=0,size=0;
+ Namval_t *np0;
+#ifdef _ENV_H
+ shp->env = env_open(environ,3);
+ env_delete(shp->env,"_");
+#endif
+ if(!ep)
+ goto skip;
+ while(*ep++)
+ nenv++;
+ np = newof(0,Namval_t,nenv,0);
+ for(np0=np,ep=environ;cp= *ep; ep++)
+ {
+ dp = strchr(cp,'=');
+ if(!dp)
+ continue;
+ *dp++ = 0;
+ if(mp = dtmatch(shp->var_base,cp))
+ {
+ mp->nvenv = (char*)cp;
+ dp[-1] = '=';
+ }
+ else if(*cp=='A' && cp[1]=='_' && cp[2]=='_' && cp[3]=='z' && cp[4]==0)
+ {
+ dp[-1] = '=';
+ next = cp+4;
+ continue;
+ }
+ else
+ {
+ k++;
+ mp = np++;
+ mp->nvname = cp;
+ size += strlen(cp);
+ }
+ nv_onattr(mp,NV_IMPORT);
+ if(mp->nvfun || nv_isattr(mp,NV_INTEGER))
+ nv_putval(mp,dp,0);
+ else
+ {
+ mp->nvalue.cp = dp;
+ nv_onattr(mp,NV_NOFREE);
+ }
+ nv_onattr(mp,NV_EXPORT|NV_IMPORT);
+ }
+ np = (Namval_t*)realloc((void*)np0,k*sizeof(Namval_t));
+ dp = (char*)malloc(size+k);
+ while(k-->0)
+ {
+ size = strlen(np->nvname);
+ memcpy(dp,np->nvname,size+1);
+ np->nvname[size] = '=';
+ np->nvenv = np->nvname;
+ np->nvname = dp;
+ dp += size+1;
+ dtinsert(shp->var_base,np++);
+ }
+ while(cp=next)
+ {
+ if(next = strchr(++cp,'='))
+ *next = 0;
+ np = nv_search(cp+2,shp->var_tree,NV_ADD);
+ if(np!=SHLVL && nv_isattr(np,NV_IMPORT|NV_EXPORT))
+ {
+ int flag = *(unsigned char*)cp-' ';
+ int size = *(unsigned char*)(cp+1)-' ';
+ if((flag&NV_INTEGER) && size==0)
+ {
+ /* check for floating*/
+ char *val = nv_getval(np);
+ strtol(val,&dp,10);
+ if(*dp=='.' || *dp=='e' || *dp=='E')
+ {
+ char *lp;
+ flag |= NV_DOUBLE;
+ if(*dp=='.')
+ {
+ strtol(dp+1,&lp,10);
+ if(*lp)
+ dp = lp;
+ }
+ if(*dp && *dp!='.')
+ {
+ flag |= NV_EXPNOTE;
+ size = dp-val;
+ }
+ else
+ size = strlen(dp);
+ size--;
+ }
+ }
+ nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size);
+ if((flag&(NV_INTEGER|NV_UTOL|NV_LTOU))==(NV_UTOL|NV_LTOU))
+ nv_mapchar(np,(flag&NV_UTOL)?e_tolower:e_toupper);
+ }
+ else
+ cp += 2;
+ }
+skip:
+#ifdef _ENV_H
+ env_delete(shp->env,e_envmarker);
+#endif
+ if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED))
+ {
+ nv_offattr(PWDNOD,NV_TAGGED);
+ path_pwd(shp,0);
+ }
+ if((cp = nv_getval(SHELLNOD)) && (sh_type(cp)&SH_TYPE_RESTRICTED))
+ sh_onoption(SH_RESTRICTED); /* restricted shell */
+ return;
+}
+
+/*
+ * terminate shell and free up the space
+ */
+int sh_term(void)
+{
+ sfdisc(sfstdin,SF_POPDISC);
+ free((char*)sh.outbuff);
+ stakset(NIL(char*),0);
+ return(0);
+}
+
+/* function versions of these */
+
+#define DISABLE /* proto workaround */
+
+unsigned long sh_isoption DISABLE (int opt)
+{
+ return(sh_isoption(opt));
+}
+
+unsigned long sh_onoption DISABLE (int opt)
+{
+ return(sh_onoption(opt));
+}
+
+unsigned long sh_offoption DISABLE (int opt)
+{
+ return(sh_offoption(opt));
+}
+
+void sh_sigcheck DISABLE (void)
+{
+ Shell_t *shp = sh_getinterp();
+ sh_sigcheck(shp);
+}
+
+Dt_t* sh_bltin_tree DISABLE (void)
+{
+ return(sh.bltin_tree);
+}
+
+/*
+ * This code is for character mapped variables with wctrans()
+ */
+struct Mapchar
+{
+ Namfun_t hdr;
+ const char *name;
+ wctrans_t trans;
+ int lctype;
+};
+
+static void put_trans(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ struct Mapchar *mp = (struct Mapchar*)fp;
+ int c,offset = staktell(),off=offset;
+ if(val)
+ {
+ if(mp->lctype!=lctype)
+ {
+ mp->lctype = lctype;
+ mp->trans = wctrans(mp->name);
+ }
+ if(!mp->trans || (flags&NV_INTEGER))
+ goto skip;
+ while(c = mbchar(val))
+ {
+ c = towctrans(c,mp->trans);
+ stakseek(off+c);
+ stakseek(off);
+ c = mbconv(stakptr(off),c);
+ off += c;
+ stakseek(off);
+ }
+ stakputc(0);
+ val = stakptr(offset);
+ }
+ else
+ {
+ nv_putv(np,val,flags,fp);
+ nv_disc(np,fp,NV_POP);
+ if(!(fp->nofree&1))
+ free((void*)fp);
+ stakseek(offset);
+ return;
+ }
+skip:
+ nv_putv(np,val,flags,fp);
+ stakseek(offset);
+}
+
+static const Namdisc_t TRANS_disc = { sizeof(struct Mapchar), put_trans };
+
+Namfun_t *nv_mapchar(Namval_t *np,const char *name)
+{
+ wctrans_t trans = name?wctrans(name):0;
+ struct Mapchar *mp=0;
+ int n=0,low;
+ if(np)
+ mp = (struct Mapchar*)nv_hasdisc(np,&TRANS_disc);
+ if(!name)
+ return(mp?(Namfun_t*)mp->name:0);
+ if(!trans)
+ return(0);
+ if(!np)
+ return(((Namfun_t*)0)+1);
+ if((low=strcmp(name,e_tolower)) && strcmp(name,e_toupper))
+ n += strlen(name)+1;
+ if(mp)
+ {
+ if(strcmp(name,mp->name)==0)
+ return(&mp->hdr);
+ nv_disc(np,&mp->hdr,NV_POP);
+ if(!(mp->hdr.nofree&1))
+ free((void*)mp);
+ }
+ mp = newof(0,struct Mapchar,1,n);
+ mp->trans = trans;
+ mp->lctype = lctype;
+ if(low==0)
+ mp->name = e_tolower;
+ else if(n==0)
+ mp->name = e_toupper;
+ else
+ {
+ mp->name = (char*)(mp+1);
+ strcpy((char*)mp->name,name);
+ }
+ mp->hdr.disc = &TRANS_disc;
+ return(&mp->hdr);
+}
diff --git a/src/cmd/ksh93/sh/io.c b/src/cmd/ksh93/sh/io.c
new file mode 100644
index 0000000..9649f1f
--- /dev/null
+++ b/src/cmd/ksh93/sh/io.c
@@ -0,0 +1,2647 @@
+/***********************************************************************
+* *
+* 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
+
+/*
+ * Input/output file processing
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <fcin.h>
+#include <ls.h>
+#include <stdarg.h>
+#include <regex.h>
+#include "variables.h"
+#include "path.h"
+#include "io.h"
+#include "jobs.h"
+#include "shnodes.h"
+#include "history.h"
+#include "edit.h"
+#include "timeout.h"
+#include "FEATURE/externs"
+#include "FEATURE/dynamic"
+#include "FEATURE/poll"
+
+#ifdef FNDELAY
+# ifdef EAGAIN
+# if EAGAIN!=EWOULDBLOCK
+# undef EAGAIN
+# define EAGAIN EWOULDBLOCK
+# endif
+# else
+# define EAGAIN EWOULDBLOCK
+# endif /* EAGAIN */
+# ifndef O_NONBLOCK
+# define O_NONBLOCK FNDELAY
+# endif /* !O_NONBLOCK */
+#endif /* FNDELAY */
+
+#ifndef O_SERVICE
+# define O_SERVICE O_NOCTTY
+#endif
+
+#define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
+
+static void *timeout;
+static int (*fdnotify)(int,int);
+
+#if defined(_lib_socket) && defined(_sys_socket) && defined(_hdr_netinet_in)
+# include <sys/socket.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# if !defined(htons) && !_lib_htons
+# define htons(x) (x)
+# endif
+# if !defined(htonl) && !_lib_htonl
+# define htonl(x) (x)
+# endif
+# if _pipe_socketpair && !_stream_peek
+# ifndef SHUT_RD
+# define SHUT_RD 0
+# endif
+# ifndef SHUT_WR
+# define SHUT_WR 1
+# endif
+# if _socketpair_shutdown_mode
+# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[1],SHUT_RD)<0||fchmod((v)[1],S_IWUSR)<0||shutdown((v)[0],SHUT_WR)<0||fchmod((v)[0],S_IRUSR)<0)?(-1):0)
+# else
+# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[1],SHUT_RD)<0||shutdown((v)[0],SHUT_WR)<0)?(-1):0)
+# endif
+# endif
+
+#if !_lib_getaddrinfo
+
+#undef EAI_SYSTEM
+
+#define EAI_SYSTEM 1
+
+#undef addrinfo
+#undef getaddrinfo
+#undef freeaddrinfo
+
+#define addrinfo local_addrinfo
+#define getaddrinfo local_getaddrinfo
+#define freeaddrinfo local_freeaddrinfo
+
+struct addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ socklen_t ai_addrlen;
+ struct sockaddr* ai_addr;
+ struct addrinfo* ai_next;
+};
+
+static int
+getaddrinfo(const char* node, const char* service, const struct addrinfo* hint, struct addrinfo **addr)
+{
+ unsigned long ip_addr = 0;
+ unsigned short ip_port = 0;
+ struct addrinfo* ap;
+ struct hostent* hp;
+ struct sockaddr_in* ip;
+ char* prot;
+ long n;
+
+ if (!(hp = gethostbyname(node)) || hp->h_addrtype!=AF_INET || hp->h_length>sizeof(struct in_addr))
+ {
+ errno = EADDRNOTAVAIL;
+ return EAI_SYSTEM;
+ }
+ ip_addr = (unsigned long)((struct in_addr*)hp->h_addr)->s_addr;
+ if ((n = strtol(service, &prot, 10)) > 0 && n <= USHRT_MAX && !*prot)
+ ip_port = htons((unsigned short)n);
+ else
+ {
+ struct servent* sp;
+ const char* protocol = 0;
+
+ if (hint)
+ switch (hint->ai_socktype)
+ {
+ case SOCK_STREAM:
+ switch (hint->ai_protocol)
+ {
+ case 0:
+ protocol = "tcp";
+ break;
+#ifdef IPPROTO_SCTP
+ case IPPROTO_SCTP:
+ protocol = "sctp";
+ break;
+#endif
+ }
+ break;
+ case SOCK_DGRAM:
+ protocol = "udp";
+ break;
+ }
+ if (!protocol)
+ {
+ errno = EPROTONOSUPPORT;
+ return 1;
+ }
+ if (sp = getservbyname(service, protocol))
+ ip_port = sp->s_port;
+ }
+ if (!ip_port)
+ {
+ errno = EADDRNOTAVAIL;
+ return EAI_SYSTEM;
+ }
+ if (!(ap = newof(0, struct addrinfo, 1, sizeof(struct sockaddr_in))))
+ return EAI_SYSTEM;
+ if (hint)
+ *ap = *hint;
+ ap->ai_family = hp->h_addrtype;
+ ap->ai_addrlen = sizeof(struct sockaddr_in);
+ ap->ai_addr = (struct sockaddr *)(ap+1);
+ ip = (struct sockaddr_in *)ap->ai_addr;
+ ip->sin_family = AF_INET;
+ ip->sin_port = ip_port;
+ ip->sin_addr.s_addr = ip_addr;
+ *addr = ap;
+ return 0;
+}
+
+static void
+freeaddrinfo(struct addrinfo* ap)
+{
+ if (ap)
+ free(ap);
+}
+
+#endif
+
+/*
+ * return <protocol>/<host>/<service> fd
+ * If called with flags==O_NONBLOCK return 1 if protocol is supported
+ */
+
+typedef int (*Inetintr_f)(struct addrinfo*, void*);
+
+static int
+inetopen(const char* path, int flags, Inetintr_f onintr, void* handle)
+{
+ register char* s;
+ register char* t;
+ int fd;
+ int oerrno;
+ struct addrinfo hint;
+ struct addrinfo* addr;
+ struct addrinfo* p;
+ int server = !!(flags&O_SERVICE);
+
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_family = PF_UNSPEC;
+ switch (path[0])
+ {
+#ifdef IPPROTO_SCTP
+ case 's':
+ if (path[1]!='c' || path[2]!='t' || path[3]!='p' || path[4]!='/')
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_SCTP;
+ path += 5;
+ break;
+#endif
+ case 't':
+ if (path[1]!='c' || path[2]!='p' || path[3]!='/')
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ hint.ai_socktype = SOCK_STREAM;
+ path += 4;
+ break;
+ case 'u':
+ if (path[1]!='d' || path[2]!='p' || path[3]!='/')
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ hint.ai_socktype = SOCK_DGRAM;
+ path += 4;
+ break;
+ default:
+ errno = ENOTDIR;
+ return -1;
+ }
+ if(flags==O_NONBLOCK)
+ return 1;
+ if (!(s = strdup(path)))
+ return -1;
+ if (t = strchr(s, '/'))
+ {
+ *t++ = 0;
+ if (streq(s, "local"))
+ s = strdup("localhost");
+ fd = getaddrinfo(s, t, &hint, &addr);
+ }
+ else
+ fd = -1;
+ free(s);
+ if (fd)
+ {
+ if (fd != EAI_SYSTEM)
+ errno = ENOTDIR;
+ return -1;
+ }
+ oerrno = errno;
+ errno = 0;
+ fd = -1;
+ for (p = addr; p; p = p->ai_next)
+ {
+ /*
+ * some api's don't take the hint
+ */
+
+ if (!p->ai_protocol)
+ p->ai_protocol = hint.ai_protocol;
+ if (!p->ai_socktype)
+ p->ai_socktype = hint.ai_socktype;
+ while ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) >= 0)
+ {
+ if (server && !bind(fd, p->ai_addr, p->ai_addrlen) && !listen(fd, 5) || !server && !connect(fd, p->ai_addr, p->ai_addrlen))
+ goto done;
+ close(fd);
+ fd = -1;
+ if (errno != EINTR || !onintr)
+ break;
+ if ((*onintr)(addr, handle))
+ goto done;
+ }
+ }
+ done:
+ freeaddrinfo(addr);
+ if (fd >= 0)
+ errno = oerrno;
+ return fd;
+}
+
+#else
+
+#undef O_SERVICE
+#undef SHOPT_COSHELL
+
+#endif
+
+struct fdsave
+{
+ int orig_fd; /* original file descriptor */
+ int save_fd; /* saved file descriptor */
+ int subshell; /* saved for subshell */
+ char *tname; /* name used with >; */
+};
+
+struct Iodisc
+{
+ Sfdisc_t disc;
+ Shell_t *sh;
+};
+
+static int subexcept(Sfio_t*, int, void*, Sfdisc_t*);
+static int eval_exceptf(Sfio_t*, int, void*, Sfdisc_t*);
+static int slowexcept(Sfio_t*, int, void*, Sfdisc_t*);
+static int pipeexcept(Sfio_t*, int, void*, Sfdisc_t*);
+static ssize_t piperead(Sfio_t*, void*, size_t, Sfdisc_t*);
+static ssize_t slowread(Sfio_t*, void*, size_t, Sfdisc_t*);
+static ssize_t subread(Sfio_t*, void*, size_t, Sfdisc_t*);
+static ssize_t tee_write(Sfio_t*,const void*,size_t,Sfdisc_t*);
+static int io_prompt(Shell_t*,Sfio_t*,int);
+static int io_heredoc(Shell_t*,register struct ionod*, const char*, int);
+static void sftrack(Sfio_t*,int,void*);
+static const Sfdisc_t eval_disc = { NULL, NULL, NULL, eval_exceptf, NULL};
+static Sfdisc_t tee_disc = {NULL,tee_write,NULL,NULL,NULL};
+static Sfio_t *subopen(Shell_t *,Sfio_t*, off_t, long);
+static const Sfdisc_t sub_disc = { subread, 0, 0, subexcept, 0 };
+
+struct subfile
+{
+ Sfdisc_t disc;
+ Sfio_t *oldsp;
+ off_t offset;
+ long size;
+ long left;
+};
+
+struct Eof
+{
+ Namfun_t hdr;
+ int fd;
+};
+
+static Sfdouble_t nget_cur_eof(register Namval_t* np, Namfun_t *fp)
+{
+ struct Eof *ep = (struct Eof*)fp;
+ Sfoff_t end, cur =lseek(ep->fd, (Sfoff_t)0, SEEK_CUR);
+ if(*np->nvname=='C')
+ return((Sfdouble_t)cur);
+ if(cur<0)
+ return((Sfdouble_t)-1);
+ end =lseek(ep->fd, (Sfoff_t)0, SEEK_END);
+ lseek(ep->fd, (Sfoff_t)0, SEEK_CUR);
+ return((Sfdouble_t)end);
+}
+
+static const Namdisc_t EOF_disc = { sizeof(struct Eof), 0, 0, nget_cur_eof};
+
+#define MATCH_BUFF (64*1024)
+struct Match
+{
+ Sfoff_t offset;
+ char *base;
+};
+
+static int matchf(void *handle, char *ptr, size_t size)
+{
+ struct Match *mp = (struct Match*)handle;
+ mp->offset += (ptr-mp->base);
+ return(1);
+}
+
+
+static struct fdsave *filemap;
+static short filemapsize;
+
+#define PSEUDOFD (SHRT_MAX)
+
+/* ======== input output and file copying ======== */
+
+int sh_iovalidfd(Shell_t *shp, int fd)
+{
+ Sfio_t **sftable = shp->sftable;
+ int max,n, **fdptrs = shp->fdptrs;
+ unsigned char *fdstatus = shp->fdstatus;
+ if(fd<0)
+ return(0);
+ if(fd < shp->gd->lim.open_max)
+ return(1);
+ max = strtol(astconf("OPEN_MAX",NiL,NiL),NiL,0);
+ if(fd >= max)
+ {
+ errno = EBADF;
+ return(0);
+ }
+ n = (fd+16)&~0xf;
+ if(n > max)
+ n = max;
+ max = shp->gd->lim.open_max;
+ shp->sftable = (Sfio_t**)calloc((n+1)*(sizeof(int*)+sizeof(Sfio_t*)+1),1);
+ if(max)
+ memcpy(shp->sftable,sftable,max*sizeof(Sfio_t*));
+ shp->fdptrs = (int**)(&shp->sftable[n]);
+ if(max)
+ memcpy(shp->fdptrs,fdptrs,max*sizeof(int*));
+ shp->fdstatus = (unsigned char*)(&shp->fdptrs[n]);
+ if(max)
+ memcpy(shp->fdstatus,fdstatus,max);
+ if(sftable)
+ free((void*)sftable);
+ shp->gd->lim.open_max = n;
+ return(1);
+}
+
+int sh_inuse(Shell_t *shp, int fd)
+{
+ return(fd < shp->gd->lim.open_max && shp->fdptrs[fd]);
+}
+
+void sh_ioinit(Shell_t *shp)
+{
+ filemapsize = 8;
+ filemap = (struct fdsave*)malloc(filemapsize*sizeof(struct fdsave));
+ sh_iovalidfd(shp,16);
+ shp->sftable[0] = sfstdin;
+ shp->sftable[1] = sfstdout;
+ shp->sftable[2] = sfstderr;
+ sfnotify(sftrack);
+ sh_iostream(shp,0);
+ sh_iostream(shp,1);
+ /* all write steams are in the same pool and share outbuff */
+ shp->outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw"); /* pool identifier */
+ shp->outbuff = (char*)malloc(IOBSIZE+4);
+ shp->errbuff = (char*)malloc(IOBSIZE/4);
+ sfsetbuf(sfstderr,shp->errbuff,IOBSIZE/4);
+ sfsetbuf(sfstdout,shp->outbuff,IOBSIZE);
+ sfpool(sfstdout,shp->outpool,SF_WRITE);
+ sfpool(sfstderr,shp->outpool,SF_WRITE);
+ sfset(sfstdout,SF_LINE,0);
+ sfset(sfstderr,SF_LINE,0);
+ sfset(sfstdin,SF_SHARE|SF_PUBLIC,1);
+}
+
+/*
+ * Handle output stream exceptions
+ */
+static int outexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle)
+{
+ Shell_t *shp = ((struct Iodisc*)handle)->sh;
+ static int active = 0;
+ if(type==SF_DPOP || type==SF_FINAL)
+ free((void*)handle);
+ else if(type==SF_WRITE && (*(ssize_t*)data)<0 && sffileno(iop)!=2)
+ switch (errno)
+ {
+ case EINTR:
+ case EPIPE:
+#ifdef ECONNRESET
+ case ECONNRESET:
+#endif
+#ifdef ESHUTDOWN
+ case ESHUTDOWN:
+#endif
+ break;
+ default:
+ if(!active)
+ {
+ int mode = ((struct checkpt*)shp->jmplist)->mode;
+ int save = errno;
+ active = 1;
+ ((struct checkpt*)shp->jmplist)->mode = 0;
+ sfpurge(iop);
+ sfpool(iop,NIL(Sfio_t*),SF_WRITE);
+ errno = save;
+ errormsg(SH_DICT,ERROR_system(1),e_badwrite,sffileno(iop));
+ active = 0;
+ ((struct checkpt*)shp->jmplist)->mode = mode;
+ sh_exit(1);
+ }
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * create or initialize a stream corresponding to descriptor <fd>
+ * a buffer with room for a sentinal is allocated for a read stream.
+ * A discipline is inserted when read stream is a tty or a pipe
+ * For output streams, the buffer is set to sh.output and put into
+ * the sh.outpool synchronization pool
+ */
+Sfio_t *sh_iostream(Shell_t *shp, register int fd)
+{
+ register Sfio_t *iop;
+ register int status = sh_iocheckfd(shp,fd);
+ register int flags = SF_WRITE;
+ char *bp;
+ struct Iodisc *dp;
+ if(status==IOCLOSE)
+ {
+ switch(fd)
+ {
+ case 0:
+ return(sfstdin);
+ case 1:
+ return(sfstdout);
+ case 2:
+ return(sfstderr);
+ }
+ return(NIL(Sfio_t*));
+ }
+ if(status&IOREAD)
+ {
+ if(!(bp = (char *)malloc(IOBSIZE+1)))
+ return(NIL(Sfio_t*));
+ flags |= SF_READ;
+ if(!(status&IOWRITE))
+ flags &= ~SF_WRITE;
+ }
+ else
+ bp = shp->outbuff;
+ if(status&IODUP)
+ flags |= SF_SHARE|SF_PUBLIC;
+ if((iop = shp->sftable[fd]) && sffileno(iop)>=0)
+ {
+ if(status&IOTTY)
+ sfset(iop,SF_LINE|SF_WCWIDTH,1);
+ sfsetbuf(iop, bp, IOBSIZE);
+ }
+ else if(!(iop=sfnew((fd<=2?iop:0),bp,IOBSIZE,fd,flags)))
+ return(NIL(Sfio_t*));
+ dp = newof(0,struct Iodisc,1,0);
+ dp->sh = shp;
+ if(status&IOREAD)
+ {
+ sfset(iop,SF_MALLOC,1);
+ if(!(status&IOWRITE))
+ sfset(iop,SF_IOCHECK,1);
+ dp->disc.exceptf = slowexcept;
+ if(status&IOTTY)
+ dp->disc.readf = slowread;
+ else if(status&IONOSEEK)
+ {
+ dp->disc.readf = piperead;
+ sfset(iop, SF_IOINTR,1);
+ }
+ else
+ dp->disc.readf = 0;
+ dp->disc.seekf = 0;
+ dp->disc.writef = 0;
+ }
+ else
+ {
+ if((status&(IONOSEEK|IOTTY)) == IONOSEEK)
+ dp->disc.exceptf = pipeexcept;
+ else
+ dp->disc.exceptf = outexcept;
+ sfpool(iop,shp->outpool,SF_WRITE);
+ }
+ sfdisc(iop,&dp->disc);
+ shp->sftable[fd] = iop;
+ return(iop);
+}
+
+/*
+ * preserve the file descriptor or stream by moving it
+ */
+static void io_preserve(Shell_t* shp, register Sfio_t *sp, register int f2)
+{
+ register int fd;
+ if(sp)
+ fd = sfsetfd(sp,10);
+ else
+ fd = sh_fcntl(f2,F_DUPFD,10);
+ if(f2==shp->infd)
+ shp->infd = fd;
+ if(fd<0)
+ {
+ shp->toomany = 1;
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
+ errormsg(SH_DICT,ERROR_system(1),e_toomany);
+ }
+ if(f2 >= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,f2);
+ if(shp->fdptrs[fd]=shp->fdptrs[f2])
+ {
+ if(f2==job.fd)
+ job.fd=fd;
+ *shp->fdptrs[fd] = fd;
+ shp->fdptrs[f2] = 0;
+ }
+ shp->sftable[fd] = sp;
+ shp->fdstatus[fd] = shp->fdstatus[f2];
+ if(fcntl(f2,F_GETFD,0)&1)
+ {
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] |= IOCLEX;
+ }
+ shp->sftable[f2] = 0;
+}
+
+/*
+ * Given a file descriptor <f1>, move it to a file descriptor number <f2>
+ * If <f2> is needed move it, otherwise it is closed first.
+ * The original stream <f1> is closed.
+ * The new file descriptor <f2> is returned;
+ */
+int sh_iorenumber(Shell_t *shp, register int f1,register int f2)
+{
+ register Sfio_t *sp = shp->sftable[f2];
+ if(f1!=f2)
+ {
+ /* see whether file descriptor is in use */
+ if(sh_inuse(shp,f2) || (f2>2 && sp))
+ {
+ if(!(shp->inuse_bits&(1<<f2)))
+ io_preserve(shp,sp,f2);
+ sp = 0;
+ }
+ else if(f2==0)
+ shp->st.ioset = 1;
+ sh_close(f2);
+ if(f2<=2 && sp)
+ {
+ register Sfio_t *spnew = sh_iostream(shp,f1);
+ shp->fdstatus[f2] = (shp->fdstatus[f1]&~IOCLEX);
+ sfsetfd(spnew,f2);
+ sfswap(spnew,sp);
+ sfset(sp,SF_SHARE|SF_PUBLIC,1);
+ }
+ else
+ {
+ shp->fdstatus[f2] = (shp->fdstatus[f1]&~IOCLEX);
+ if((f2 = sh_fcntl(f1,F_DUPFD, f2)) < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_file+4);
+ else if(f2 <= 2)
+ sh_iostream(shp,f2);
+ }
+ if(sp)
+ shp->sftable[f1] = 0;
+ if(shp->fdstatus[f1]!=IOCLOSE)
+ sh_close(f1);
+ }
+ else if(sp)
+ {
+ sfsetfd(sp,f2);
+ if(f2<=2)
+ sfset(sp,SF_SHARE|SF_PUBLIC,1);
+ }
+ if(f2>=shp->gd->lim.open_max)
+ sh_iovalidfd(shp,f2);
+ return(f2);
+}
+
+/*
+ * close a file descriptor and update stream table and attributes
+ */
+int sh_close(register int fd)
+{
+ Shell_t *shp = sh_getinterp();
+ register Sfio_t *sp;
+ register int r = 0;
+ if(fd<0)
+ return(-1);
+ if(fd >= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,fd);
+ if(!(sp=shp->sftable[fd]) || sfclose(sp) < 0)
+ {
+ if(fdnotify)
+ (*fdnotify)(fd,SH_FDCLOSE);
+ r=close(fd);
+ }
+ if(fd>2)
+ shp->sftable[fd] = 0;
+ shp->fdstatus[fd] = IOCLOSE;
+ if(shp->fdptrs[fd])
+ *shp->fdptrs[fd] = -1;
+ shp->fdptrs[fd] = 0;
+ if(fd < 10)
+ shp->inuse_bits &= ~(1<<fd);
+ return(r);
+}
+
+#ifdef O_SERVICE
+
+static int
+onintr(struct addrinfo* addr, void* handle)
+{
+ Shell_t* sh = (Shell_t*)handle;
+
+ if (sh->trapnote&SH_SIGSET)
+ {
+ freeaddrinfo(addr);
+ sh_exit(SH_EXITSIG);
+ return -1;
+ }
+ if (sh->trapnote)
+ sh_chktrap(sh);
+ return 0;
+}
+
+#endif
+
+/*
+ * Mimic open(2) with checks for pseudo /dev/ files.
+ */
+int sh_open(register const char *path, int flags, ...)
+{
+ Shell_t *shp = sh_getinterp();
+ register int fd = -1;
+ mode_t mode;
+ char *e;
+ va_list ap;
+ va_start(ap, flags);
+ mode = (flags & O_CREAT) ? va_arg(ap, int) : 0;
+ va_end(ap);
+ errno = 0;
+ if(path==0)
+ {
+ errno = EFAULT;
+ return(-1);
+ }
+ if(*path==0)
+ {
+ errno = ENOENT;
+ return(-1);
+ }
+ if (path[0]=='/' && path[1]=='d' && path[2]=='e' && path[3]=='v' && path[4]=='/')
+ {
+ switch (path[5])
+ {
+ case 'f':
+ if (path[6]=='d' && path[7]=='/')
+ {
+ if(flags==O_NONBLOCK)
+ return(1);
+ fd = (int)strtol(path+8, &e, 10);
+ if (*e)
+ fd = -1;
+ }
+ break;
+ case 's':
+ if (path[6]=='t' && path[7]=='d')
+ switch (path[8])
+ {
+ case 'e':
+ if (path[9]=='r' && path[10]=='r' && !path[11])
+ fd = 2;
+ break;
+ case 'i':
+ if (path[9]=='n' && !path[10])
+ fd = 0;
+ break;
+ case 'o':
+ if (path[9]=='u' && path[10]=='t' && !path[11])
+ fd = 1;
+ break;
+ }
+ }
+#ifdef O_SERVICE
+ if (fd < 0)
+ {
+ if ((fd = inetopen(path+5, flags, onintr, shp)) < 0 && errno != ENOTDIR)
+ return -1;
+ if(flags==O_NONBLOCK)
+ return(fd>=0);
+ if (fd >= 0)
+ goto ok;
+ }
+ if(flags==O_NONBLOCK)
+ return(0);
+#endif
+ }
+ if (fd >= 0)
+ {
+ int nfd= -1;
+ if (flags & O_CREAT)
+ {
+ struct stat st;
+ if (stat(path,&st) >=0)
+ nfd = open(path,flags,st.st_mode);
+ }
+ else
+ nfd = open(path,flags);
+ if(nfd>=0)
+ {
+ fd = nfd;
+ goto ok;
+ }
+ if((mode=sh_iocheckfd(shp,fd))==IOCLOSE)
+ return(-1);
+ flags &= O_ACCMODE;
+ if(!(mode&IOWRITE) && ((flags==O_WRONLY) || (flags==O_RDWR)))
+ return(-1);
+ if(!(mode&IOREAD) && ((flags==O_RDONLY) || (flags==O_RDWR)))
+ return(-1);
+ if((fd=dup(fd))<0)
+ return(-1);
+ }
+ else
+ {
+#if SHOPT_REGRESS
+ char buf[PATH_MAX];
+ if(strncmp(path,"/etc/",5)==0)
+ {
+ sfsprintf(buf, sizeof(buf), "%s%s", sh_regress_etc(path, __LINE__, __FILE__), path+4);
+ path = buf;
+ }
+#endif
+ while((fd = open(path, flags, mode)) < 0)
+ if(errno!=EINTR || shp->trapnote)
+ return(-1);
+ }
+ ok:
+ flags &= O_ACCMODE;
+ if(flags==O_WRONLY)
+ mode = IOWRITE;
+ else if(flags==O_RDWR)
+ mode = (IOREAD|IOWRITE);
+ else
+ mode = IOREAD;
+ if(fd >= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,fd);
+ shp->fdstatus[fd] = mode;
+ return(fd);
+}
+
+/*
+ * Open a file for reading
+ * On failure, print message.
+ */
+int sh_chkopen(register const char *name)
+{
+ register int fd = sh_open(name,O_RDONLY,0);
+ if(fd < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_open,name);
+ return(fd);
+}
+
+/*
+ * move open file descriptor to a number > 2
+ */
+int sh_iomovefd(register int fdold)
+{
+ Shell_t *shp = sh_getinterp();
+ register int fdnew;
+ if(fdold >= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,fdold);
+ if(fdold<0 || fdold>2)
+ return(fdold);
+ fdnew = sh_iomovefd(dup(fdold));
+ shp->fdstatus[fdnew] = (shp->fdstatus[fdold]&~IOCLEX);
+ close(fdold);
+ shp->fdstatus[fdold] = IOCLOSE;
+ return(fdnew);
+}
+
+/*
+ * create a pipe and print message on failure
+ */
+int sh_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);
+}
+
+#if SHOPT_COSHELL
+ int sh_coaccept(Shell_t *shp,int *pv,int out)
+ {
+ int fd = accept(pv[0],(struct sockaddr*)0,(socklen_t*)0);
+ sh_close(pv[0]);
+ pv[0] = -1;
+ if(fd<0)
+ errormsg(SH_DICT,ERROR_system(1),e_pipe);
+ if((pv[out]=sh_fcntl(fd,F_DUPFD,10)) >=10)
+ sh_close(fd);
+ else
+ pv[out] = sh_iomovefd(fd);
+ if(fcntl(pv[out],F_SETFD,FD_CLOEXEC) >=0)
+ shp->fdstatus[pv[out]] |= IOCLEX;
+ shp->fdstatus[pv[out]] = (out?IOWRITE:IOREAD);
+ shp->fdstatus[pv[out]] |= IONOSEEK;
+ sh_subsavefd(pv[out]);
+#if defined(SHUT_RD) && defined(SHUT_WR)
+ shutdown(pv[out],out?SHUT_RD:SHUT_WR);
+#endif
+ return(0);
+ }
+
+ int sh_copipe(Shell_t *shp, int *pv, int out)
+ {
+ int r,port=20000;
+ struct sockaddr_in sin;
+ socklen_t slen;
+ if ((pv[out] = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_pipe);
+ do
+ {
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(++port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ slen = sizeof (sin);
+ }
+ while ((r=bind (pv[out], (struct sockaddr *) &sin, slen)) == -1 && errno==EADDRINUSE);
+ if(r<0 || listen(pv[out],5) <0)
+ {
+ close(pv[out]);
+ errormsg(SH_DICT,ERROR_system(1),e_pipe);
+ }
+ fcntl(pv[out],F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[pv[out]] |= IOCLEX;
+ pv[1-out] = -1;
+ pv[2] = port;
+ return(0);
+ }
+#endif /* SHOPT_COSHELL */
+
+static int pat_seek(void *handle, const char *str, size_t sz)
+{
+ char **bp = (char**)handle;
+ *bp = (char*)str;
+ return(-1);
+}
+
+static int pat_line(const regex_t* rp, const char *buff, register size_t n)
+{
+ register const char *cp=buff, *sp;
+ while(n>0)
+ {
+ for(sp=cp; n-->0 && *cp++ != '\n';);
+ if(regnexec(rp,sp,cp-sp, 0, (regmatch_t*)0, 0)==0)
+ return(sp-buff);
+ }
+ return(cp-buff);
+}
+
+static int io_patseek(Shell_t *shp, regex_t *rp, Sfio_t* sp, int flags)
+{
+ char *cp, *match;
+ int r, fd=sffileno(sp), close_exec = shp->fdstatus[fd]&IOCLEX;
+ int was_share,s=(PIPE_BUF>SF_BUFSIZE?SF_BUFSIZE:PIPE_BUF);
+ size_t n,m;
+ shp->fdstatus[sffileno(sp)] |= IOCLEX;
+ if(fd==0)
+ was_share = sfset(sp,SF_SHARE,1);
+ while((cp=sfreserve(sp, -s, SF_LOCKR)) || (cp=sfreserve(sp,SF_UNBOUND, SF_LOCKR)))
+ {
+ m = n = sfvalue(sp);
+ while(n>0 && cp[n-1]!='\n')
+ n--;
+ if(n)
+ m = n;
+ r = regrexec(rp,cp,m,0,(regmatch_t*)0, 0, '\n', (void*)&match, pat_seek);
+ if(r<0)
+ m = match-cp;
+ else if(r==2)
+ {
+ if((m = pat_line(rp,cp,m)) < n)
+ r = -1;
+ }
+ if(m && (flags&IOCOPY))
+ sfwrite(sfstdout,cp,m);
+ sfread(sp,cp,m);
+ if(r<0)
+ break;
+ }
+ if(!close_exec)
+ shp->fdstatus[sffileno(sp)] &= ~IOCLEX;
+ if(fd==0 && !(was_share&SF_SHARE))
+ sfset(sp, SF_SHARE,0);
+ return(0);
+}
+
+static Sfoff_t file_offset(Shell_t *shp, int fn, char *fname)
+{
+ Sfio_t *sp = shp->sftable[fn];
+ char *cp;
+ Sfoff_t off;
+ struct Eof endf;
+ Namval_t *mp = nv_open("EOF",shp->var_tree,0);
+ Namval_t *pp = nv_open("CUR",shp->var_tree,0);
+ memset(&endf,0,sizeof(struct Eof));
+ endf.fd = fn;
+ endf.hdr.disc = &EOF_disc;
+ endf.hdr.nofree = 1;
+ if(mp)
+ nv_stack(mp, &endf.hdr);
+ if(pp)
+ nv_stack(pp, &endf.hdr);
+ if(sp)
+ sfsync(sp);
+ off = sh_strnum(fname, &cp, 0);
+ if(mp)
+ nv_stack(mp, NiL);
+ if(pp)
+ nv_stack(pp, NiL);
+ return(*cp?(Sfoff_t)-1:off);
+}
+
+/*
+ * close a pipe
+ */
+void sh_pclose(register int pv[])
+{
+ if(pv[0]>=2)
+ sh_close(pv[0]);
+ if(pv[1]>=2)
+ sh_close(pv[1]);
+ pv[0] = pv[1] = -1;
+}
+
+static char *io_usename(char *name, int *perm, int fno, int mode)
+{
+ struct stat statb;
+ char *tname, *sp, *ep;
+ int fd;
+ if(mode==0)
+ {
+ if((fd = sh_open(name,O_RDONLY,0)) > 0)
+ {
+ if(fstat(fd,&statb) < 0)
+ return(0);
+ if(!S_ISREG(statb.st_mode))
+ return(0);
+ *perm = statb.st_mode&(RW_ALL|(S_IXUSR|S_IXGRP|S_IXOTH));
+ }
+ else if(fd < 0 && errno!=ENOENT)
+ return(0);
+ }
+ stakseek(1);
+ stakputs(name);
+ stakputc(0);
+ pathcanon(stakptr(1),PATH_PHYSICAL);
+ sp = ep = stakptr(1);
+ if(ep = strrchr(sp,'/'))
+ {
+ memmove(stakptr(0),sp,++ep-sp);
+ stakseek(ep-sp);
+ }
+ else
+ {
+ ep = sp;
+ stakseek(0);
+ }
+ stakputc('.');
+ sfprintf(stkstd,"%<#d_%d{;.tmp",getpid(),fno);
+ tname = stakfreeze(1);
+ switch(mode)
+ {
+ unlink(tname);
+ break;
+ case 1:
+ rename(tname,name);
+ break;
+ default:
+ unlink(tname);
+ break;
+ }
+ return(tname);
+}
+
+/*
+ * I/O redirection
+ * flag = 0 if files are to be restored
+ * flag = 2 if files are to be closed on exec
+ * flag = 3 when called from $( < ...), just open file and return
+ * flag = SH_SHOWME for trace only
+ */
+int sh_redirect(Shell_t *shp,struct ionod *iop, int flag)
+{
+ Sfoff_t off;
+ register char *fname;
+ register int fd, iof;
+ const char *message = e_open;
+ int o_mode; /* mode flag for open */
+ static char io_op[7]; /* used for -x trace info */
+ int trunc=0, clexec=0, fn, traceon;
+ int r, indx = shp->topfd, perm= -1;
+ char *tname=0, *after="", *trace = shp->st.trap[SH_DEBUGTRAP];
+ Namval_t *np=0;
+ int isstring = shp->subshell?(sfset(sfstdout,0,0)&SF_STRING):0;
+
+ if(flag==2)
+ clexec = 1;
+ if(iop)
+ traceon = sh_trace(shp,NIL(char**),0);
+ for(;iop;iop=iop->ionxt)
+ {
+ iof=iop->iofile;
+ fn = (iof&IOUFD);
+ if(fn==1 && shp->subshell && !shp->subshare && (flag==2 || isstring))
+ sh_subfork();
+ if(shp->redir0 && fn==0 && !(iof&IOMOV))
+ shp->redir0 = 2;
+ io_op[0] = '0'+(iof&IOUFD);
+ if(iof&IOPUT)
+ {
+ io_op[1] = '>';
+ o_mode = O_WRONLY|O_CREAT;
+ }
+ else
+ {
+ io_op[1] = '<';
+ o_mode = O_RDONLY|O_NONBLOCK;
+ }
+ io_op[2] = 0;
+ io_op[3] = 0;
+ io_op[4] = 0;
+ fname = iop->ioname;
+ if(!(iof&IORAW))
+ {
+ if(iof&IOLSEEK)
+ {
+ struct argnod *ap = (struct argnod*)stakalloc(ARGVAL+strlen(iop->ioname));
+ memset(ap, 0, ARGVAL);
+ ap->argflag = ARG_MAC;
+ strcpy(ap->argval,iop->ioname);
+ fname=sh_macpat(shp,ap,(iof&IOARITH)?ARG_ARITH:ARG_EXP);
+ }
+ else if(iof&IOPROCSUB)
+ {
+ struct argnod *ap = (struct argnod*)stakalloc(ARGVAL+strlen(iop->ioname));
+ memset(ap, 0, ARGVAL);
+ if(iof&IOPUT)
+ ap->argflag = ARG_RAW;
+ else if(shp->subshell)
+ sh_subtmpfile(shp);
+ ap->argchn.ap = (struct argnod*)fname;
+ ap = sh_argprocsub(shp,ap);
+ fname = ap->argval;
+ }
+ else
+ fname=sh_mactrim(shp,fname,(!sh_isoption(SH_NOGLOB)&&sh_isoption(SH_INTERACTIVE))?2:0);
+ }
+ errno=0;
+ np = 0;
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ if(!(iof&(IODOC|IOLSEEK|IOMOV)))
+ sh_coaddfile(shp,fname);
+ continue;
+ }
+#endif /* SHOPT_COSHELL */
+ if(iop->iovname)
+ {
+ np = nv_open(iop->iovname,shp->var_tree,NV_NOASSIGN|NV_VARNAME);
+ if(nv_isattr(np,NV_RDONLY))
+ errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
+ io_op[0] = '}';
+ if((iof&IOLSEEK) || ((iof&IOMOV) && *fname=='-'))
+ fn = nv_getnum(np);
+ }
+ if(fn>=shp->gd->lim.open_max && !sh_iovalidfd(shp,fn))
+ errormsg(SH_DICT,ERROR_system(1),e_file+4);
+ if(iof&IOLSEEK)
+ {
+ io_op[2] = '#';
+ if(iof&IOARITH)
+ {
+ strcpy(&io_op[3]," ((");
+ after = "))";
+ }
+ else if(iof&IOCOPY)
+ io_op[3] = '#';
+ goto traceit;
+ }
+ if(*fname || (iof&(IODOC|IOSTRG))==(IODOC|IOSTRG))
+ {
+ if(iof&IODOC)
+ {
+ if(traceon)
+ sfputr(sfstderr,io_op,'<');
+ fd = io_heredoc(shp,iop,fname,traceon);
+ if(traceon && (flag==SH_SHOWME))
+ sh_close(fd);
+ fname = 0;
+ }
+ else if(iof&IOMOV)
+ {
+ int dupfd,toclose= -1;
+ io_op[2] = '&';
+ if((fd=fname[0])>='0' && fd<='9')
+ {
+ char *number = fname;
+ dupfd = strtol(fname,&number,10);
+ if(*number=='-')
+ {
+ toclose = dupfd;
+ number++;
+ }
+ if(*number || dupfd > IOUFD)
+ {
+ message = e_file;
+ goto fail;
+ }
+ if(shp->subshell && dupfd==1)
+ {
+ if(sfset(sfstdout,0,0)&SF_STRING)
+ sh_subtmpfile(shp);
+ if(shp->comsub==1)
+ shp->subdup |= 1<<fn;
+ dupfd = sffileno(sfstdout);
+ }
+ else if(shp->sftable[dupfd])
+ sfsync(shp->sftable[dupfd]);
+ if(dupfd!=1 && fn < 10)
+ shp->subdup &= ~(1<<fn);
+ }
+ else if(fd=='-' && fname[1]==0)
+ {
+ fd= -1;
+ goto traceit;
+ }
+ else if(fd=='p' && fname[1]==0)
+ {
+ if(iof&IOPUT)
+ dupfd = shp->coutpipe;
+ else
+ dupfd = shp->cpipe[0];
+ if(flag)
+ toclose = dupfd;
+ }
+ else
+ {
+ message = e_file;
+ goto fail;
+ }
+ if(flag==SH_SHOWME)
+ goto traceit;
+ if((fd=sh_fcntl(dupfd,F_DUPFD,3))<0)
+ goto fail;
+ if(fd>= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,fd);
+ sh_iocheckfd(shp,dupfd);
+ shp->fdstatus[fd] = (shp->fdstatus[dupfd]&~IOCLEX);
+ if(toclose<0 && shp->fdstatus[fd]&IOREAD)
+ shp->fdstatus[fd] |= IODUP;
+ else if(dupfd==shp->cpipe[0])
+ sh_pclose(shp->cpipe);
+ else if(toclose>=0)
+ {
+ if(flag==0)
+ sh_iosave(shp,toclose,indx,(char*)0); /* save file descriptor */
+ sh_close(toclose);
+ }
+ }
+ else if(iof&IORDW)
+ {
+ if(sh_isoption(SH_RESTRICTED))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname);
+ io_op[2] = '>';
+ o_mode = O_RDWR|O_CREAT;
+ if(iof&IOREWRITE)
+ trunc = io_op[2] = ';';
+ goto openit;
+ }
+ else if(!(iof&IOPUT))
+ {
+ if(flag==SH_SHOWME)
+ goto traceit;
+ fd=sh_chkopen(fname);
+ }
+ else if(sh_isoption(SH_RESTRICTED))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname);
+ else
+ {
+ if(iof&IOAPP)
+ {
+ io_op[2] = '>';
+ o_mode |= O_APPEND;
+ }
+ else if((iof&IOREWRITE) && (flag==0 || flag==1 || sh_subsavefd(fn)))
+ {
+ io_op[2] = ';';
+ o_mode |= O_TRUNC;
+ if(tname = io_usename(fname,&perm,fn,0))
+ o_mode |= O_EXCL;
+ }
+ else
+ {
+ o_mode |= O_TRUNC;
+ if(iof&IOCLOB)
+ io_op[2] = '|';
+ else if(sh_isoption(SH_NOCLOBBER))
+ {
+ struct stat sb;
+ if(stat(fname,&sb)>=0)
+ {
+#if SHOPT_FS_3D
+ if(S_ISREG(sb.st_mode)&&
+ (!shp->gd->lim.fs3d || iview(&sb)==0))
+#else
+ if(S_ISREG(sb.st_mode))
+#endif /* SHOPT_FS_3D */
+ {
+ errno = EEXIST;
+ errormsg(SH_DICT,ERROR_system(1),e_exists,fname);
+ }
+ }
+ else
+ o_mode |= O_EXCL;
+ }
+ }
+ openit:
+ if(flag!=SH_SHOWME)
+ {
+ if((fd=sh_open(tname?tname:fname,o_mode,RW_ALL)) <0)
+ errormsg(SH_DICT,ERROR_system(1),((o_mode&O_CREAT)?e_create:e_open),fname);
+ if(perm>0)
+#if _lib_fchmod
+ fchmod(fd,perm);
+#else
+ chmod(tname,perm);
+#endif
+ }
+ }
+ traceit:
+ if(traceon && fname)
+ {
+ if(np)
+ sfprintf(sfstderr,"{%s",nv_name(np));
+ sfprintf(sfstderr,"%s %s%s%c",io_op,fname,after,iop->ionxt?' ':'\n');
+ }
+ if(flag==SH_SHOWME)
+ return(indx);
+ if(trace && fname)
+ {
+ char *argv[7], **av=argv;
+ av[3] = io_op;
+ av[4] = fname;
+ av[5] = 0;
+ av[6] = 0;
+ if(iof&IOARITH)
+ av[5] = after;
+ if(np)
+ {
+ av[0] = "{";
+ av[1] = nv_name(np);
+ av[2] = "}";
+ }
+ else
+ av +=3;
+ sh_debug(shp,trace,(char*)0,(char*)0,av,ARG_NOGLOB);
+ }
+ if(iof&IOLSEEK)
+ {
+ Sfio_t *sp = shp->sftable[fn];
+ r = shp->fdstatus[fn];
+ if(!(r&(IOSEEK|IONOSEEK)))
+ r = sh_iocheckfd(shp,fn);
+ sfsprintf(io_op,sizeof(io_op),"%d\0",fn);
+ if(r==IOCLOSE)
+ {
+ fname = io_op;
+ message = e_file;
+ goto fail;
+ }
+ if(iof&IOARITH)
+ {
+ if(r&IONOSEEK)
+ {
+ fname = io_op;
+ message = e_notseek;
+ goto fail;
+ }
+ message = e_badseek;
+ if((off = file_offset(shp,fn,fname))<0)
+ goto fail;
+ if(sp)
+ {
+ off=sfseek(sp, off, SEEK_SET);
+ sfsync(sp);
+ }
+ else
+ off=lseek(fn, off, SEEK_SET);
+ if(off<0)
+ r = -1;
+ }
+ else
+ {
+ regex_t *rp;
+ extern const char e_notimp[];
+ if(!(r&IOREAD))
+ {
+ message = e_noread;
+ goto fail;
+ }
+ if(!(rp = regcache(fname, REG_SHELL|REG_NOSUB|REG_NEWLINE|REG_AUGMENTED|REG_FIRST|REG_LEFT|REG_RIGHT, &r)))
+ {
+ message = e_badpattern;
+ goto fail;
+ }
+ if(!sp)
+ sp = sh_iostream(shp,fn);
+ r=io_patseek(shp,rp,sp,iof);
+ if(sp && flag==3)
+ {
+ /* close stream but not fn */
+ sfsetfd(sp,-1);
+ sfclose(sp);
+ }
+ }
+ if(r<0)
+ goto fail;
+ if(flag==3)
+ return(fn);
+ continue;
+ }
+ if(!np)
+ {
+ if(flag==0 || tname || (flag==1 && fn==1 && (shp->fdstatus[fn]&IONOSEEK) && shp->outpipepid && shp->outpipepid==getpid()))
+ {
+ if(fd==fn)
+ {
+ if((r=sh_fcntl(fd,F_DUPFD,10)) > 0)
+ {
+ fd = r;
+ sh_close(fn);
+ }
+ }
+ sh_iosave(shp,fn,indx,tname?fname:(trunc?Empty:0));
+ }
+ else if(sh_subsavefd(fn))
+ sh_iosave(shp,fn,indx|IOSUBSHELL,tname?fname:0);
+ }
+ if(fd<0)
+ {
+ if(sh_inuse(shp,fn) || (fn && fn==shp->infd))
+ {
+ if(fn>9 || !(shp->inuse_bits&(1<<fn)))
+ io_preserve(shp,shp->sftable[fn],fn);
+ }
+ sh_close(fn);
+ }
+ if(flag==3)
+ return(fd);
+ if(fd>=0)
+ {
+ if(np)
+ {
+ int32_t v;
+ fn = fd;
+ if(fd<10)
+ {
+ if((fn=fcntl(fd,F_DUPFD,10)) < 0)
+ goto fail;
+ if(fn>=shp->gd->lim.open_max && !sh_iovalidfd(shp,fn))
+ goto fail;
+ shp->fdstatus[fn] = shp->fdstatus[fd];
+ sh_close(fd);
+ fd = fn;
+ }
+ _nv_unset(np,0);
+ nv_onattr(np,NV_INT32);
+ v = fn;
+ nv_putval(np,(char*)&v, NV_INT32);
+ sh_iocheckfd(shp,fd);
+ }
+ else
+ {
+ fd = sh_iorenumber(shp,sh_iomovefd(fd),fn);
+ if(fn>2 && fn<10)
+ shp->inuse_bits |= (1<<fn);
+ }
+ }
+ if(fd >2 && clexec)
+ {
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] |= IOCLEX;
+ }
+ }
+ else
+ goto fail;
+ }
+ return(indx);
+fail:
+ errormsg(SH_DICT,ERROR_system(1),message,fname);
+ /* NOTREACHED */
+ return(0);
+}
+/*
+ * Create a tmp file for the here-document
+ */
+static int io_heredoc(Shell_t *shp,register struct ionod *iop, const char *name, int traceon)
+{
+ register Sfio_t *infile = 0, *outfile, *tmp;
+ register int fd;
+ Sfoff_t off;
+ if(!(iop->iofile&IOSTRG) && (!shp->heredocs || iop->iosize==0))
+ return(sh_open(e_devnull,O_RDONLY));
+ /* create an unnamed temporary file */
+ if(!(outfile=sftmp(0)))
+ errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
+ if(iop->iofile&IOSTRG)
+ {
+ if(traceon)
+ sfprintf(sfstderr,"< %s\n",name);
+ sfputr(outfile,name,'\n');
+ }
+ else
+ {
+ /*
+ * the locking is only needed in case & blocks process
+ * here-docs so this can be eliminted in some cases
+ */
+ struct flock lock;
+ int fno = sffileno(shp->heredocs);
+ if(fno>=0)
+ {
+ memset((void*)&lock,0,sizeof(lock));
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ fcntl(fno,F_SETLKW,&lock);
+ lock.l_type = F_UNLCK;
+ }
+ off = sftell(shp->heredocs);
+ infile = subopen(shp,shp->heredocs,iop->iooffset,iop->iosize);
+ if(traceon)
+ {
+ char *cp = sh_fmtq(iop->iodelim);
+ fd = (*cp=='$' || *cp=='\'')?' ':'\\';
+ sfprintf(sfstderr," %c%s\n",fd,cp);
+ sfdisc(outfile,&tee_disc);
+ }
+ tmp = outfile;
+ if(fno>=0 && !(iop->iofile&IOQUOTE))
+ tmp = sftmp(iop->iosize<IOBSIZE?iop->iosize:0);
+ if(fno>=0 || (iop->iofile&IOQUOTE))
+ {
+ /* This is a quoted here-document, not expansion */
+ sfmove(infile,tmp,SF_UNBOUND,-1);
+ sfclose(infile);
+ if(sffileno(tmp)>0)
+ {
+ sfsetbuf(tmp,malloc(IOBSIZE+1),IOBSIZE);
+ sfset(tmp,SF_MALLOC,1);
+ }
+ sfseek(shp->heredocs,off,SEEK_SET);
+ if(fno>=0)
+ fcntl(fno,F_SETLK,&lock);
+ sfseek(tmp,(off_t)0,SEEK_SET);
+ infile = tmp;
+ }
+ if(!(iop->iofile&IOQUOTE))
+ {
+ char *lastpath = shp->lastpath;
+ sh_machere(shp,infile,outfile,iop->ioname);
+ shp->lastpath = lastpath;
+ if(infile)
+ sfclose(infile);
+ }
+ }
+ /* close stream outfile, but save file descriptor */
+ fd = sffileno(outfile);
+ sfsetfd(outfile,-1);
+ sfclose(outfile);
+ if(traceon && !(iop->iofile&IOSTRG))
+ sfputr(sfstderr,iop->ioname,'\n');
+ lseek(fd,(off_t)0,SEEK_SET);
+ shp->fdstatus[fd] = IOREAD;
+ return(fd);
+}
+
+/*
+ * This write discipline also writes the output on standard error
+ * This is used when tracing here-documents
+ */
+static ssize_t tee_write(Sfio_t *iop,const void *buff,size_t n,Sfdisc_t *unused)
+{
+ NOT_USED(unused);
+ sfwrite(sfstderr,buff,n);
+ return(write(sffileno(iop),buff,n));
+}
+
+/*
+ * copy file <origfd> into a save place
+ * The saved file is set close-on-exec
+ * if <origfd> < 0, then -origfd is saved, but not duped so that it
+ * will be closed with sh_iorestore.
+ */
+void sh_iosave(Shell_t *shp, register int origfd, int oldtop, char *name)
+{
+ register int savefd;
+ int flag = (oldtop&IOSUBSHELL);
+ oldtop &= ~IOSUBSHELL;
+ /* see if already saved, only save once */
+ for(savefd=shp->topfd; --savefd>=oldtop; )
+ {
+ if(filemap[savefd].orig_fd == origfd)
+ return;
+ }
+ /* make sure table is large enough */
+ if(shp->topfd >= filemapsize)
+ {
+ char *cp, *oldptr = (char*)filemap;
+ char *oldend = (char*)&filemap[filemapsize];
+ long moved;
+ filemapsize += 8;
+ if(!(filemap = (struct fdsave*)realloc(filemap,filemapsize*sizeof(struct fdsave))))
+ errormsg(SH_DICT,ERROR_exit(4),e_nospace);
+ if(moved = (char*)filemap - oldptr)
+ {
+ for(savefd=shp->gd->lim.open_max; --savefd>=0; )
+ {
+ cp = (char*)shp->fdptrs[savefd];
+ if(cp >= oldptr && cp < oldend)
+ shp->fdptrs[savefd] = (int*)(cp+moved);
+ }
+ }
+ }
+#if SHOPT_DEVFD
+ if(origfd <0)
+ {
+ savefd = origfd;
+ origfd = -origfd;
+ }
+ else
+#endif /* SHOPT_DEVFD */
+ {
+ if((savefd = sh_fcntl(origfd, F_DUPFD, 10)) < 0 && errno!=EBADF)
+ {
+ shp->toomany=1;
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
+ errormsg(SH_DICT,ERROR_system(1),e_toomany);
+ }
+ }
+ filemap[shp->topfd].tname = name;
+ filemap[shp->topfd].subshell = flag;
+ filemap[shp->topfd].orig_fd = origfd;
+ filemap[shp->topfd++].save_fd = savefd;
+ if(savefd >=0)
+ {
+ register Sfio_t* sp = shp->sftable[origfd];
+ /* make saved file close-on-exec */
+ sh_fcntl(savefd,F_SETFD,FD_CLOEXEC);
+ if(origfd==job.fd)
+ job.fd = savefd;
+ shp->fdstatus[savefd] = shp->fdstatus[origfd];
+ shp->fdptrs[savefd] = &filemap[shp->topfd-1].save_fd;
+ if(!(shp->sftable[savefd]=sp))
+ return;
+ sfsync(sp);
+ if(origfd <=2)
+ {
+ /* copy standard stream to new stream */
+ sp = sfswap(sp,NIL(Sfio_t*));
+ shp->sftable[savefd] = sp;
+ }
+ else
+ shp->sftable[origfd] = 0;
+ }
+}
+
+/*
+ * close all saved file descriptors
+ */
+void sh_iounsave(Shell_t* shp)
+{
+ register int fd, savefd, newfd;
+ for(newfd=fd=0; fd < shp->topfd; fd++)
+ {
+ if((savefd = filemap[fd].save_fd)< 0)
+ filemap[newfd++] = filemap[fd];
+ else
+ {
+ shp->sftable[savefd] = 0;
+ sh_close(savefd);
+ }
+ }
+ shp->topfd = newfd;
+}
+
+/*
+ * restore saved file descriptors from <last> on
+ */
+void sh_iorestore(Shell_t *shp, int last, int jmpval)
+{
+ register int origfd, savefd, fd;
+ int flag = (last&IOSUBSHELL);
+ last &= ~IOSUBSHELL;
+ for (fd = shp->topfd - 1; fd >= last; fd--)
+ {
+ if(!flag && filemap[fd].subshell)
+ continue;
+ if(jmpval==SH_JMPSCRIPT)
+ {
+ if ((savefd = filemap[fd].save_fd) >= 0)
+ {
+ shp->sftable[savefd] = 0;
+ sh_close(savefd);
+ }
+ continue;
+ }
+ origfd = filemap[fd].orig_fd;
+ if(origfd<0)
+ {
+ /* this should never happen */
+ savefd = filemap[fd].save_fd;
+ shp->sftable[savefd] = 0;
+ sh_close(savefd);
+ return;
+ }
+ if(filemap[fd].tname == Empty && shp->exitval==0)
+ ftruncate(origfd,lseek(origfd,0,SEEK_CUR));
+ else if(filemap[fd].tname)
+ io_usename(filemap[fd].tname,(int*)0,origfd,shp->exitval?2:1);
+ sh_close(origfd);
+ if ((savefd = filemap[fd].save_fd) >= 0)
+ {
+ sh_fcntl(savefd, F_DUPFD, origfd);
+ if(savefd==job.fd)
+ job.fd=origfd;
+ shp->fdstatus[origfd] = shp->fdstatus[savefd];
+ /* turn off close-on-exec if flag if necessary */
+ if(shp->fdstatus[origfd]&IOCLEX)
+ fcntl(origfd,F_SETFD,FD_CLOEXEC);
+ if(origfd<=2)
+ {
+ sfswap(shp->sftable[savefd],shp->sftable[origfd]);
+ if(origfd==0)
+ shp->st.ioset = 0;
+ }
+ else
+ shp->sftable[origfd] = shp->sftable[savefd];
+ shp->sftable[savefd] = 0;
+ sh_close(savefd);
+ }
+ else
+ shp->fdstatus[origfd] = IOCLOSE;
+ }
+ if(!flag)
+ {
+ /* keep file descriptors for subshell restore */
+ for (fd = last ; fd < shp->topfd; fd++)
+ {
+ if(filemap[fd].subshell)
+ filemap[last++] = filemap[fd];
+ }
+ }
+ if(last < shp->topfd)
+ shp->topfd = last;
+}
+
+/*
+ * returns access information on open file <fd>
+ * returns -1 for failure, 0 for success
+ * <mode> is the same as for access()
+ */
+int sh_ioaccess(int fd,register int mode)
+{
+ Shell_t *shp = sh_getinterp();
+ register int flags;
+ if(mode==X_OK)
+ return(-1);
+ if((flags=sh_iocheckfd(shp,fd))!=IOCLOSE)
+ {
+ if(mode==F_OK)
+ return(0);
+ if(mode==R_OK && (flags&IOREAD))
+ return(0);
+ if(mode==W_OK && (flags&IOWRITE))
+ return(0);
+ }
+ return(-1);
+}
+
+/*
+ * Handle interrupts for slow streams
+ */
+static int slowexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle)
+{
+ Shell_t *shp = ((struct Iodisc*)handle)->sh;
+ register int n,fno;
+ NOT_USED(handle);
+ if(type==SF_DPOP || type==SF_FINAL)
+ free((void*)handle);
+ if(type==SF_WRITE && errno==EPIPE)
+ {
+ sfpurge(iop);
+ return(-1);
+ }
+ if(type!=SF_READ)
+ return(0);
+ if((shp->trapnote&(SH_SIGSET|SH_SIGTRAP)) && errno!=EIO && errno!=ENXIO)
+ errno = EINTR;
+ fno = sffileno(iop);
+ if((n=sfvalue(iop))<=0)
+ {
+#ifndef FNDELAY
+# ifdef O_NDELAY
+ if(errno==0 && (n=fcntl(fno,F_GETFL,0))&O_NDELAY)
+ {
+ n &= ~O_NDELAY;
+ fcntl(fno, F_SETFL, n);
+ return(1);
+ }
+# endif /* O_NDELAY */
+#endif /* !FNDELAY */
+#ifdef O_NONBLOCK
+ if(errno==EAGAIN)
+ {
+ n = fcntl(fno,F_GETFL,0);
+ n &= ~O_NONBLOCK;
+ fcntl(fno, F_SETFL, n);
+ return(1);
+ }
+#endif /* O_NONBLOCK */
+ if(errno!=EINTR)
+ return(0);
+ else if(shp->bltinfun && (shp->trapnote&SH_SIGTRAP) && shp->lastsig)
+ return(-1);
+ n=1;
+ sh_onstate(SH_TTYWAIT);
+ }
+ else
+ n = 0;
+ if(shp->bltinfun && shp->bltindata.sigset)
+ return(-1);
+ errno = 0;
+ if(shp->trapnote&SH_SIGSET)
+ {
+ if(isatty(fno))
+ sfputc(sfstderr,'\n');
+ sh_exit(SH_EXITSIG);
+ }
+ if(shp->trapnote&SH_SIGTRAP)
+ sh_chktrap(shp);
+ return(n);
+}
+
+/*
+ * called when slowread times out
+ */
+static void time_grace(void *handle)
+{
+ Shell_t *shp = (Shell_t*)handle;
+ timeout = 0;
+ if(sh_isstate(SH_GRACE))
+ {
+ sh_offstate(SH_GRACE);
+ if(!sh_isstate(SH_INTERACTIVE))
+ return;
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
+ errormsg(SH_DICT,2,e_timeout);
+ shp->trapnote |= SH_SIGSET;
+ return;
+ }
+ errormsg(SH_DICT,0,e_timewarn);
+ sh_onstate(SH_GRACE);
+ sigrelease(SIGALRM);
+ shp->trapnote |= SH_SIGTRAP;
+}
+
+static ssize_t piperead(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *handle)
+{
+ Shell_t *shp = ((struct Iodisc*)handle)->sh;
+ int fd = sffileno(iop);
+ if(job.waitsafe && job.savesig)
+ {
+ job_lock();
+ job_unlock();
+ }
+ if(shp->trapnote)
+ {
+ errno = EINTR;
+ return(-1);
+ }
+ if(sh_isstate(SH_INTERACTIVE) && sffileno(iop)==0 && io_prompt(shp,iop,shp->nextprompt)<0 && errno==EIO)
+ return(0);
+ sh_onstate(SH_TTYWAIT);
+ if(!(shp->fdstatus[fd]&IOCLEX) && (sfset(iop,0,0)&SF_SHARE))
+ size = ed_read(shgd->ed_context, fd, (char*)buff, size,0);
+ else
+ size = sfrd(iop,buff,size,handle);
+ sh_offstate(SH_TTYWAIT);
+ return(size);
+}
+/*
+ * This is the read discipline that is applied to slow devices
+ * This routine takes care of prompting for input
+ */
+static ssize_t slowread(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *handle)
+{
+ Shell_t *shp = ((struct Iodisc*)handle)->sh;
+ int (*readf)(void*, int, char*, int, int);
+ int reedit=0, rsize;
+#if SHOPT_HISTEXPAND
+ char *xp=0;
+#endif
+# if SHOPT_ESH
+ if(sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS))
+ readf = ed_emacsread;
+ else
+# endif /* SHOPT_ESH */
+# if SHOPT_VSH
+# if SHOPT_RAWONLY
+ if(sh_isoption(SH_VI) || ((SHOPT_RAWONLY-0) && mbwide()))
+# else
+ if(sh_isoption(SH_VI))
+# endif
+ readf = ed_viread;
+ else
+# endif /* SHOPT_VSH */
+ readf = ed_read;
+ if(shp->trapnote)
+ {
+ errno = EINTR;
+ return(-1);
+ }
+ while(1)
+ {
+ if(io_prompt(shp,iop,shp->nextprompt)<0 && errno==EIO)
+ return(0);
+ if(shp->timeout)
+ timeout = (void*)sh_timeradd(sh_isstate(SH_GRACE)?1000L*TGRACE:1000L*shp->timeout,0,time_grace,shp);
+ rsize = (*readf)(shgd->ed_context, sffileno(iop), (char*)buff, size, reedit);
+ if(timeout)
+ timerdel(timeout);
+ timeout=0;
+#if SHOPT_HISTEXPAND
+ if(rsize && *(char*)buff != '\n' && shp->nextprompt==1 && sh_isoption(SH_HISTEXPAND))
+ {
+ int r;
+ ((char*)buff)[rsize] = '\0';
+ if(xp)
+ {
+ free(xp);
+ xp = 0;
+ }
+ r = hist_expand(buff, &xp);
+ if((r & (HIST_EVENT|HIST_PRINT)) && !(r & HIST_ERROR) && xp)
+ {
+ strlcpy(buff, xp, size);
+ rsize = strlen(buff);
+ if(!sh_isoption(SH_HISTVERIFY) || readf==ed_read)
+ {
+ sfputr(sfstderr, xp, -1);
+ break;
+ }
+ reedit = rsize - 1;
+ continue;
+ }
+ if((r & HIST_ERROR) && sh_isoption(SH_HISTREEDIT))
+ {
+ reedit = rsize - 1;
+ continue;
+ }
+ if(r & (HIST_ERROR|HIST_PRINT))
+ {
+ *(char*)buff = '\n';
+ rsize = 1;
+ }
+ }
+#endif
+ break;
+ }
+ return(rsize);
+}
+
+/*
+ * check and return the attributes for a file descriptor
+ */
+
+int sh_iocheckfd(Shell_t *shp, register int fd)
+{
+ register int flags, n;
+ if((n=shp->fdstatus[fd])&IOCLOSE)
+ return(n);
+ if(!(n&(IOREAD|IOWRITE)))
+ {
+#ifdef F_GETFL
+ if((flags=fcntl(fd,F_GETFL,0)) < 0)
+ return(shp->fdstatus[fd]=IOCLOSE);
+ if((flags&O_ACCMODE)!=O_WRONLY)
+ n |= IOREAD;
+ if((flags&O_ACCMODE)!=O_RDONLY)
+ n |= IOWRITE;
+#else
+ struct stat statb;
+ if((flags = fstat(fd,&statb))< 0)
+ return(shp->fdstatus[fd]=IOCLOSE);
+ n |= (IOREAD|IOWRITE);
+ if(read(fd,"",0) < 0)
+ n &= ~IOREAD;
+#endif /* F_GETFL */
+ }
+ if(!(n&(IOSEEK|IONOSEEK)))
+ {
+ struct stat statb;
+ /* /dev/null check is a workaround for select bug */
+ static ino_t null_ino;
+ static dev_t null_dev;
+ if(null_ino==0 && stat(e_devnull,&statb) >=0)
+ {
+ null_ino = statb.st_ino;
+ null_dev = statb.st_dev;
+ }
+ if(tty_check(fd))
+ n |= IOTTY;
+ if(lseek(fd,NIL(off_t),SEEK_CUR)<0)
+ {
+ n |= IONOSEEK;
+#ifdef S_ISSOCK
+ if((fstat(fd,&statb)>=0) && S_ISSOCK(statb.st_mode))
+ {
+ n |= IOREAD|IOWRITE;
+# if _socketpair_shutdown_mode
+ if(!(statb.st_mode&S_IRUSR))
+ n &= ~IOREAD;
+ else if(!(statb.st_mode&S_IWUSR))
+ n &= ~IOWRITE;
+# endif
+ }
+#endif /* S_ISSOCK */
+ }
+ else if((fstat(fd,&statb)>=0) && (
+ S_ISFIFO(statb.st_mode) ||
+#ifdef S_ISSOCK
+ S_ISSOCK(statb.st_mode) ||
+#endif /* S_ISSOCK */
+ /* The following is for sockets on the sgi */
+ (statb.st_ino==0 && (statb.st_mode & ~(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IXUSR|S_IXGRP|S_IXOTH|S_ISUID|S_ISGID))==0) ||
+ (S_ISCHR(statb.st_mode) && (statb.st_ino!=null_ino || statb.st_dev!=null_dev))
+ ))
+ n |= IONOSEEK;
+ else
+ n |= IOSEEK;
+ }
+ if(fd==0)
+ n &= ~IOWRITE;
+ else if(fd==1)
+ n &= ~IOREAD;
+ shp->fdstatus[fd] = n;
+ return(n);
+}
+
+/*
+ * Display prompt PS<flag> on standard error
+ */
+
+static int io_prompt(Shell_t *shp,Sfio_t *iop,register int flag)
+{
+ register char *cp;
+ char buff[1];
+ char *endprompt;
+ static short cmdno;
+ int sfflags;
+ if(flag<3 && !sh_isstate(SH_INTERACTIVE))
+ flag = 0;
+ if(flag==2 && sfpkrd(sffileno(iop),buff,1,'\n',0,1) >= 0)
+ flag = 0;
+ if(flag==0)
+ return(sfsync(sfstderr));
+ sfflags = sfset(sfstderr,SF_SHARE|SF_PUBLIC|SF_READ,0);
+ if(!(shp->prompt=(char*)sfreserve(sfstderr,0,0)))
+ shp->prompt = "";
+ switch(flag)
+ {
+ case 1:
+ {
+ register int c;
+#if defined(TIOCLBIC) && defined(LFLUSHO)
+ if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
+ {
+ /*
+ * re-enable output in case the user has
+ * disabled it. Not needed with edit mode
+ */
+ int mode = LFLUSHO;
+ ioctl(sffileno(sfstderr),TIOCLBIC,&mode);
+ }
+#endif /* TIOCLBIC */
+ cp = sh_mactry(shp,nv_getval(sh_scoped(shp,PS1NOD)));
+ for(;c= *cp;cp++)
+ {
+ if(c==HIST_CHAR)
+ {
+ /* look at next character */
+ c = *++cp;
+ /* print out line number if not !! */
+ if(c!= HIST_CHAR)
+ {
+ sfprintf(sfstderr,"%d", shp->gd->hist_ptr?(int)shp->gd->hist_ptr->histind:++cmdno);
+ }
+ if(c==0)
+ goto done;
+ }
+ sfputc(sfstderr,c);
+ }
+ goto done;
+ }
+ case 2:
+ cp = nv_getval(sh_scoped(shp,PS2NOD));
+ break;
+ case 3:
+ cp = nv_getval(sh_scoped(shp,PS3NOD));
+ break;
+ default:
+ goto done;
+ }
+ if(cp)
+ sfputr(sfstderr,cp,-1);
+done:
+ if(*shp->prompt && (endprompt=(char*)sfreserve(sfstderr,0,0)))
+ *endprompt = 0;
+ sfset(sfstderr,sfflags&SF_READ|SF_SHARE|SF_PUBLIC,1);
+ return(sfsync(sfstderr));
+}
+
+/*
+ * This discipline is inserted on write pipes to prevent SIGPIPE
+ * from causing an infinite loop
+ */
+static int pipeexcept(Sfio_t* iop, int mode, void *data, Sfdisc_t* handle)
+{
+ if(mode==SF_DPOP || mode==SF_FINAL)
+ free((void*)handle);
+ else if(mode==SF_WRITE && errno==EPIPE)
+ {
+ sfpurge(iop);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * keep track of each stream that is opened and closed
+ */
+static void sftrack(Sfio_t* sp, int flag, void* data)
+{
+ Shell_t *shp = sh_getinterp();
+ register int fd = sffileno(sp);
+ register struct checkpt *pp;
+ register int mode;
+ int newfd = integralof(data);
+ if(flag==SF_SETFD || flag==SF_CLOSING)
+ {
+ if(newfd<0)
+ flag = SF_CLOSING;
+ if(fdnotify)
+ (*fdnotify)(sffileno(sp),flag==SF_CLOSING?-1:newfd);
+ }
+#ifdef DEBUG
+ if(flag==SF_READ || flag==SF_WRITE)
+ {
+ char *z = fmtbase((long)getpid(),0,0);
+ write(ERRIO,z,strlen(z));
+ write(ERRIO,": ",2);
+ write(ERRIO,"attempt to ",11);
+ if(flag==SF_READ)
+ write(ERRIO,"read from",9);
+ else
+ write(ERRIO,"write to",8);
+ write(ERRIO," locked stream\n",15);
+ return;
+ }
+#endif
+ if(fd<0 || fd==PSEUDOFD || (fd>=shp->gd->lim.open_max && !sh_iovalidfd(shp,fd)))
+ return;
+ if(sh_isstate(SH_NOTRACK))
+ return;
+ mode = sfset(sp,0,0);
+ if(sp==shp->heredocs && fd < 10 && flag==SF_NEW)
+ {
+ fd = sfsetfd(sp,10);
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ }
+ if(fd < 3)
+ return;
+ if(flag==SF_NEW)
+ {
+ if(!shp->sftable[fd] && shp->fdstatus[fd]==IOCLOSE)
+ {
+ shp->sftable[fd] = sp;
+ flag = (mode&SF_WRITE)?IOWRITE:0;
+ if(mode&SF_READ)
+ flag |= IOREAD;
+ shp->fdstatus[fd] = flag;
+ sh_iostream(shp,fd);
+ }
+ if((pp=(struct checkpt*)shp->jmplist) && pp->mode==SH_JMPCMD)
+ {
+ struct openlist *item;
+ /*
+ * record open file descriptors so they can
+ * be closed in case a longjmp prevents
+ * built-ins from cleanup
+ */
+ item = new_of(struct openlist, 0);
+ item->strm = sp;
+ item->next = pp->olist;
+ pp->olist = item;
+ }
+ if(fdnotify)
+ (*fdnotify)(-1,sffileno(sp));
+ }
+ else if(flag==SF_CLOSING || (flag==SF_SETFD && newfd<=2))
+ {
+ shp->sftable[fd] = 0;
+ shp->fdstatus[fd]=IOCLOSE;
+ if(pp=(struct checkpt*)shp->jmplist)
+ {
+ struct openlist *item;
+ for(item=pp->olist; item; item=item->next)
+ {
+ if(item->strm == sp)
+ {
+ item->strm = 0;
+ break;
+ }
+ }
+ }
+ }
+}
+
+struct eval
+{
+ Sfdisc_t disc;
+ char **argv;
+ short slen;
+ char addspace;
+};
+
+/*
+ * Create a stream consisting of a space separated argv[] list
+ */
+
+Sfio_t *sh_sfeval(register char *argv[])
+{
+ register Sfio_t *iop;
+ register char *cp;
+ if(argv[1])
+ cp = "";
+ else
+ cp = argv[0];
+ iop = sfopen(NIL(Sfio_t*),(char*)cp,"s");
+ if(argv[1])
+ {
+ register struct eval *ep;
+ if(!(ep = new_of(struct eval,0)))
+ return(NIL(Sfio_t*));
+ ep->disc = eval_disc;
+ ep->argv = argv;
+ ep->slen = -1;
+ ep->addspace = 0;
+ sfdisc(iop,&ep->disc);
+ }
+ return(iop);
+}
+
+/*
+ * This code gets called whenever an end of string is found with eval
+ */
+
+static int eval_exceptf(Sfio_t *iop,int type, void *data, Sfdisc_t *handle)
+{
+ register struct eval *ep = (struct eval*)handle;
+ register char *cp;
+ register int len;
+
+ /* no more to do */
+ if(type!=SF_READ || !(cp = ep->argv[0]))
+ {
+ if(type==SF_CLOSING)
+ sfdisc(iop,SF_POPDISC);
+ else if(ep && (type==SF_DPOP || type==SF_FINAL))
+ free((void*)ep);
+ return(0);
+ }
+
+ if(!ep->addspace)
+ {
+ /* get the length of this string */
+ ep->slen = len = strlen(cp);
+ /* move to next string */
+ ep->argv++;
+ }
+ else /* insert space between arguments */
+ {
+ len = 1;
+ cp = " ";
+ }
+ /* insert the new string */
+ sfsetbuf(iop,cp,len);
+ ep->addspace = !ep->addspace;
+ return(1);
+}
+
+/*
+ * This routine returns a stream pointer to a segment of length <size> from
+ * the stream <sp> starting at offset <offset>
+ * The stream can be read with the normal stream operations
+ */
+
+static Sfio_t *subopen(Shell_t *shp,Sfio_t* sp, off_t offset, long size)
+{
+ register struct subfile *disp;
+ if(sfseek(sp,offset,SEEK_SET) <0)
+ return(NIL(Sfio_t*));
+ if(!(disp = (struct subfile*)malloc(sizeof(struct subfile)+IOBSIZE+1)))
+ return(NIL(Sfio_t*));
+ disp->disc = sub_disc;
+ disp->oldsp = sp;
+ disp->offset = offset;
+ disp->size = disp->left = size;
+ sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,PSEUDOFD,SF_READ);
+ sfdisc(sp,&disp->disc);
+ return(sp);
+}
+
+/*
+ * read function for subfile discipline
+ */
+static ssize_t subread(Sfio_t* sp,void* buff,register size_t size,Sfdisc_t* handle)
+{
+ register struct subfile *disp = (struct subfile*)handle;
+ ssize_t n;
+ NOT_USED(sp);
+ sfseek(disp->oldsp,disp->offset,SEEK_SET);
+ if(disp->left == 0)
+ return(0);
+ if(size > disp->left)
+ size = disp->left;
+ disp->left -= size;
+ n = sfread(disp->oldsp,buff,size);
+ if(size>0)
+ disp->offset += size;
+ return(n);
+}
+
+/*
+ * exception handler for subfile discipline
+ */
+static int subexcept(Sfio_t* sp,register int mode, void *data, Sfdisc_t* handle)
+{
+ register struct subfile *disp = (struct subfile*)handle;
+ if(mode==SF_CLOSING)
+ {
+ sfdisc(sp,SF_POPDISC);
+ return(0);
+ }
+ else if(disp && (mode==SF_DPOP || mode==SF_FINAL))
+ {
+ free((void*)disp);
+ return(0);
+ }
+#ifdef SF_ATEXIT
+ else if (mode==SF_ATEXIT)
+ {
+ sfdisc(sp, SF_POPDISC);
+ return(0);
+ }
+#endif
+ else if(mode==SF_READ)
+ return(0);
+ return(-1);
+}
+
+#define NROW 15 /* number of rows before going to multi-columns */
+#define LBLSIZ 3 /* size of label field and interfield spacing */
+/*
+ * print a list of arguments in columns
+ */
+void sh_menu(Sfio_t *outfile,int argn,char *argv[])
+{
+ Shell_t *shp = sh_getinterp();
+ register int i,j;
+ register char **arg;
+ int nrow, ncol=1, ndigits=1;
+ int fldsize, wsize = ed_window();
+ char *cp = nv_getval(sh_scoped(shp,LINES));
+ nrow = (cp?1+2*((int)strtol(cp, (char**)0, 10)/3):NROW);
+ for(i=argn;i >= 10;i /= 10)
+ ndigits++;
+ if(argn < nrow)
+ {
+ nrow = argn;
+ goto skip;
+ }
+ i = 0;
+ for(arg=argv; *arg;arg++)
+ {
+ if((j=strlen(*arg)) > i)
+ i = j;
+ }
+ i += (ndigits+LBLSIZ);
+ if(i < wsize)
+ ncol = wsize/i;
+ if(argn > nrow*ncol)
+ {
+ nrow = 1 + (argn-1)/ncol;
+ }
+ else
+ {
+ ncol = 1 + (argn-1)/nrow;
+ nrow = 1 + (argn-1)/ncol;
+ }
+skip:
+ fldsize = (wsize/ncol)-(ndigits+LBLSIZ);
+ for(i=0;i<nrow;i++)
+ {
+ if(shp->trapnote&SH_SIGSET)
+ return;
+ j = i;
+ while(1)
+ {
+ arg = argv+j;
+ sfprintf(outfile,"%*d) %s",ndigits,j+1,*arg);
+ j += nrow;
+ if(j >= argn)
+ break;
+ sfnputc(outfile,' ',fldsize-strlen(*arg));
+ }
+ sfputc(outfile,'\n');
+ }
+}
+
+#undef read
+/*
+ * shell version of read() for user added builtins
+ */
+ssize_t sh_read(register int fd, void* buff, size_t n)
+{
+ Shell_t *shp = sh_getinterp();
+ register Sfio_t *sp;
+ if(sp=shp->sftable[fd])
+ return(sfread(sp,buff,n));
+ else
+ return(read(fd,buff,n));
+}
+
+#undef write
+/*
+ * shell version of write() for user added builtins
+ */
+ssize_t sh_write(register int fd, const void* buff, size_t n)
+{
+ Shell_t *shp = sh_getinterp();
+ register Sfio_t *sp;
+ if(sp=shp->sftable[fd])
+ return(sfwrite(sp,buff,n));
+ else
+ return(write(fd,buff,n));
+}
+
+#undef lseek
+/*
+ * shell version of lseek() for user added builtins
+ */
+off_t sh_seek(register int fd, off_t offset, int whence)
+{
+ Shell_t *shp = sh_getinterp();
+ register Sfio_t *sp;
+ if((sp=shp->sftable[fd]) && (sfset(sp,0,0)&(SF_READ|SF_WRITE)))
+ return(sfseek(sp,offset,whence));
+ else
+ return(lseek(fd,offset,whence));
+}
+
+#undef dup
+int sh_dup(register int old)
+{
+ Shell_t *shp = sh_getinterp();
+ register int fd = dup(old);
+ if(fd>=0)
+ {
+ if(shp->fdstatus[old] == IOCLOSE)
+ shp->fdstatus[old] = 0;
+ shp->fdstatus[fd] = (shp->fdstatus[old]&~IOCLEX);
+ if(fdnotify)
+ (*fdnotify)(old,fd);
+ }
+ return(fd);
+}
+
+#undef fcntl
+int sh_fcntl(register int fd, int op, ...)
+{
+ Shell_t *shp = sh_getinterp();
+ int newfd, arg;
+ va_list ap;
+ va_start(ap, op);
+ arg = va_arg(ap, int) ;
+ va_end(ap);
+ newfd = fcntl(fd,op,arg);
+ if(newfd>=0) switch(op)
+ {
+ case F_DUPFD:
+ if(shp->fdstatus[fd] == IOCLOSE)
+ shp->fdstatus[fd] = 0;
+ if(newfd>=shp->gd->lim.open_max)
+ sh_iovalidfd(shp,newfd);
+ shp->fdstatus[newfd] = (shp->fdstatus[fd]&~IOCLEX);
+ if(fdnotify)
+ (*fdnotify)(fd,newfd);
+ break;
+ case F_SETFD:
+ if(shp->fdstatus[fd] == IOCLOSE)
+ shp->fdstatus[fd] = 0;
+ if(arg&FD_CLOEXEC)
+ shp->fdstatus[fd] |= IOCLEX;
+ else
+ shp->fdstatus[fd] &= ~IOCLEX;
+ }
+ return(newfd);
+}
+
+#undef umask
+mode_t sh_umask(mode_t m)
+{
+ Shell_t *shp = sh_getinterp();
+ shp->mask = m;
+ return(umask(m));
+}
+
+/*
+ * give file descriptor <fd> and <mode>, return an iostream pointer
+ * <mode> must be SF_READ or SF_WRITE
+ * <fd> must be a non-negative number ofr SH_IOCOPROCESS or SH_IOHISTFILE.
+ * returns NULL on failure and may set errno.
+ */
+
+Sfio_t *sh_iogetiop(int fd, int mode)
+{
+ Shell_t *shp = sh_getinterp();
+ int n;
+ Sfio_t *iop=0;
+ if(mode!=SF_READ && mode!=SF_WRITE)
+ {
+ errno = EINVAL;
+ return(iop);
+ }
+ switch(fd)
+ {
+ case SH_IOHISTFILE:
+ if(!sh_histinit((void*)shp))
+ return(iop);
+ fd = sffileno(shp->gd->hist_ptr->histfp);
+ break;
+ case SH_IOCOPROCESS:
+ if(mode==SF_WRITE)
+ fd = shp->coutpipe;
+ else
+ fd = shp->cpipe[0];
+ break;
+ default:
+ if(fd<0 || !sh_iovalidfd(shp,fd))
+ fd = -1;
+ }
+ if(fd<0)
+ {
+ errno = EBADF;
+ return(iop);
+ }
+ if(!(n=shp->fdstatus[fd]))
+ n = sh_iocheckfd(shp,fd);
+ if(mode==SF_WRITE && !(n&IOWRITE))
+ return(iop);
+ if(mode==SF_READ && !(n&IOREAD))
+ return(iop);
+ if(!(iop = shp->sftable[fd]))
+ iop=sh_iostream(shp,fd);
+ return(iop);
+}
+
+typedef int (*Notify_f)(int,int);
+
+Notify_f sh_fdnotify(Notify_f notify)
+{
+ Notify_f old;
+ old = fdnotify;
+ fdnotify = notify;
+ return(old);
+}
+
+Sfio_t *sh_fd2sfio(int fd)
+{
+ Shell_t *shp = sh_getinterp();
+ register int status;
+ Sfio_t *sp = shp->sftable[fd];
+ if(!sp && (status = sh_iocheckfd(shp,fd))!=IOCLOSE)
+ {
+ register int flags=0;
+ if(status&IOREAD)
+ flags |= SF_READ;
+ if(status&IOWRITE)
+ flags |= SF_WRITE;
+ sp = sfnew(NULL, NULL, -1, fd,flags);
+ shp->sftable[fd] = sp;
+ }
+ return(sp);
+}
+
+Sfio_t *sh_pathopen(const char *cp)
+{
+ Shell_t *shp = sh_getinterp();
+ int n;
+#ifdef PATH_BFPATH
+ if((n=path_open(shp,cp,path_get(shp,cp))) < 0)
+ n = path_open(shp,cp,(Pathcomp_t*)0);
+#else
+ if((n=path_open(shp,cp,path_get(cp))) < 0)
+ n = path_open(shp,cp,"");
+#endif
+ if(n < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_open,cp);
+ return(sh_iostream(shp,n));
+}
+
+int sh_isdevfd(register const char *fd)
+{
+ if(!fd || memcmp(fd,"/dev/fd/",8) || fd[8]==0)
+ return(0);
+ for ( fd=&fd[8] ; *fd != '\0' ; fd++ )
+ {
+ if (*fd < '0' || *fd > '9')
+ return(0);
+ }
+ return(1);
+}
diff --git a/src/cmd/ksh93/sh/jobs.c b/src/cmd/ksh93/sh/jobs.c
new file mode 100644
index 0000000..5dca257
--- /dev/null
+++ b/src/cmd/ksh93/sh/jobs.c
@@ -0,0 +1,2055 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Job control for UNIX Shell
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ * Written October, 1982
+ * Rewritten April, 1988
+ * Revised January, 1992
+ */
+
+#include "defs.h"
+#include <wait.h>
+#include "io.h"
+#include "jobs.h"
+#include "history.h"
+
+#if !defined(WCONTINUED) || !defined(WIFCONTINUED)
+# undef WCONTINUED
+# define WCONTINUED 0
+# undef WIFCONTINUED
+# define WIFCONTINUED(wstat) (0)
+#endif
+
+#define NJOB_SAVELIST 4
+
+/*
+ * temporary hack to get W* macros to work
+ */
+#undef wait
+#define wait ______wait
+/*
+ * This struct saves a link list of processes that have non-zero exit
+ * status, have had $! saved, but haven't been waited for
+ */
+struct jobsave
+{
+ struct jobsave *next;
+ pid_t pid;
+ unsigned short exitval;
+};
+
+static struct jobsave *job_savelist;
+static int njob_savelist;
+static struct process *pwfg;
+static int jobfork;
+
+pid_t pid_fromstring(char *str)
+{
+ pid_t pid;
+ char *last;
+ errno = 0;
+ if(sizeof(pid)==sizeof(Sflong_t))
+ pid = (pid_t)strtoll(str, &last, 10);
+ else
+ pid = (pid_t)strtol(str, &last, 10);
+ if(errno==ERANGE || *last)
+ errormsg(SH_DICT,ERROR_exit(1),"%s: invalid process id",str);
+ return(pid);
+}
+
+static void init_savelist(void)
+{
+ register struct jobsave *jp;
+ while(njob_savelist < NJOB_SAVELIST)
+ {
+ jp = newof(0,struct jobsave,1,0);
+ jp->next = job_savelist;
+ job_savelist = jp;
+ njob_savelist++;
+ }
+}
+
+struct back_save
+{
+ int count;
+ struct jobsave *list;
+ struct back_save *prev;
+};
+
+#define BYTE(n) (((n)+CHAR_BIT-1)/CHAR_BIT)
+#define MAXMSG 25
+#define SH_STOPSIG (SH_EXITSIG<<1)
+
+#ifdef VSUSP
+# ifndef CNSUSP
+# ifdef _POSIX_VDISABLE
+# define CNSUSP _POSIX_VDISABLE
+# else
+# define CNSUSP 0
+# endif /* _POSIX_VDISABLE */
+# endif /* CNSUSP */
+# ifndef CSWTCH
+# ifdef CSUSP
+# define CSWTCH CSUSP
+# else
+# define CSWTCH ('z'&037)
+# endif /* CSUSP */
+# endif /* CSWTCH */
+#endif /* VSUSP */
+
+/* Process states */
+#define P_EXITSAVE 01
+#define P_STOPPED 02
+#define P_NOTIFY 04
+#define P_SIGNALLED 010
+#define P_STTY 020
+#define P_DONE 040
+#define P_COREDUMP 0100
+#define P_DISOWN 0200
+#define P_FG 0400
+#ifdef SHOPT_BGX
+#define P_BG 01000
+#endif /* SHOPT_BGX */
+
+static int job_chksave(pid_t);
+static struct process *job_bypid(pid_t);
+static struct process *job_byjid(int);
+static char *job_sigmsg(int);
+static int job_alloc(void);
+static void job_free(int);
+static struct process *job_unpost(struct process*,int);
+static void job_unlink(struct process*);
+static void job_prmsg(struct process*);
+static struct process *freelist;
+static char beenhere;
+static char possible;
+static struct process dummy;
+static char by_number;
+static Sfio_t *outfile;
+static pid_t lastpid;
+static struct back_save bck;
+
+#ifdef JOBS
+ static void job_set(struct process*);
+ static void job_reset(struct process*);
+ static void job_waitsafe(int);
+ static struct process *job_byname(char*);
+ static struct process *job_bystring(char*);
+ static struct termios my_stty; /* terminal state for shell */
+ static char *job_string;
+#else
+ extern const char e_coredump[];
+#endif /* JOBS */
+
+#ifdef SIGTSTP
+ static void job_unstop(struct process*);
+ static void job_fgrp(struct process*, int);
+# ifndef _lib_tcgetpgrp
+# ifdef TIOCGPGRP
+ static int _i_;
+# define tcgetpgrp(a) (ioctl(a, TIOCGPGRP, &_i_)>=0?_i_:-1)
+# endif /* TIOCGPGRP */
+ int tcsetpgrp(int fd,pid_t pgrp)
+ {
+ int pgid = pgrp;
+# ifdef TIOCGPGRP
+ return(ioctl(fd, TIOCSPGRP, &pgid));
+# else
+ return(-1);
+# endif /* TIOCGPGRP */
+ }
+# endif /* _lib_tcgetpgrp */
+#else
+# define job_unstop(pw)
+# undef CNSUSP
+#endif /* SIGTSTP */
+
+#ifndef OTTYDISC
+# undef NTTYDISC
+#endif /* OTTYDISC */
+
+#ifdef JOBS
+
+typedef int (*Waitevent_f)(int,long,int);
+
+#ifdef SHOPT_BGX
+void job_chldtrap(Shell_t *shp, const char *trap, int unpost)
+{
+ register struct process *pw,*pwnext;
+ pid_t bckpid;
+ int oldexit,trapnote;
+ job_lock();
+ shp->sigflag[SIGCHLD] &= ~SH_SIGTRAP;
+ trapnote = shp->trapnote;
+ shp->trapnote = 0;
+ for(pw=job.pwlist;pw;pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ if((pw->p_flag&(P_BG|P_DONE)) != (P_BG|P_DONE))
+ continue;
+ pw->p_flag &= ~P_BG;
+ bckpid = shp->bckpid;
+ oldexit = shp->savexit;
+ shp->bckpid = pw->p_pid;
+ shp->savexit = pw->p_exit;
+ if(pw->p_flag&P_SIGNALLED)
+ shp->savexit |= SH_EXITSIG;
+ sh_trap(trap,0);
+ if(pw->p_pid==bckpid && unpost)
+ job_unpost(pw,0);
+ shp->savexit = oldexit;
+ shp->bckpid = bckpid;
+ }
+ shp->trapnote = trapnote;
+ job_unlock();
+}
+#endif /* SHOPT_BGX */
+
+/*
+ * return next on link list of jobsave free list
+ */
+static struct jobsave *jobsave_create(pid_t pid)
+{
+ register struct jobsave *jp = job_savelist;
+ job_chksave(pid);
+ if(++bck.count > shgd->lim.child_max)
+ job_chksave(0);
+ if(jp)
+ {
+ njob_savelist--;
+ job_savelist = jp->next;
+ }
+ else
+ jp = newof(0,struct jobsave,1,0);
+ if(jp)
+ {
+ jp->pid = pid;
+ jp->next = bck.list;
+ bck.list = jp;
+ jp->exitval = 0;
+ }
+ return(jp);
+}
+
+#if SHOPT_COSHELL
+ pid_t sh_copid(struct cosh *csp)
+ {
+ return(COPID_BIT|(csp->id<<16)|csp->cojob->id);
+ }
+
+
+ char *sh_pid2str(Shell_t *shp,pid_t pid)
+ {
+ struct cosh *csp=0;
+ if(pid&COPID_BIT)
+ {
+ int id = (pid>>16) &0x3f;
+ for(csp=job.colist; csp; csp = csp->next)
+ {
+ if(csp->id == id)
+ break;
+ }
+ }
+ if(csp)
+ sfprintf(shp->strbuf,"%s.%d%c",csp->name,pid&0xff,0);
+ else
+ sfprintf(shp->strbuf,"%d%c",pid,0);
+ return(sfstruse(shp->strbuf));
+ }
+
+ int job_cowalk(int (*fun)(struct process*,int),int arg,char *name)
+ {
+ Shell_t *shp = sh_getinterp();
+ struct cosh *csp;
+ struct process *pw,*pwnext;
+ pid_t val;
+ int n,r=0;
+ char *cp = strchr(name,'.');
+ if(!cp)
+ n = strlen(name);
+ else
+ n = cp-name;
+ for(csp=(struct cosh*)job.colist;csp;csp=csp->next)
+ {
+ if(memcmp(name,csp->name,n)==0 && csp->name[n]==0)
+ break;
+ }
+ if(!csp)
+ errormsg(SH_DICT,ERROR_exit(1),e_jobusage,name);
+ if(cp)
+ {
+ n = pid_fromstring(cp+1);
+ val = (csp->id<<16)|n|COPID_BIT;
+ }
+ job_reap(SIGCHLD);
+ for(n=0,pw=job.pwlist; pw; pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ if((cp && val==pw->p_pid) || (pw->p_cojob && pw->p_cojob->local==(void*)csp))
+ {
+ if(fun)
+ {
+ if(pw->p_flag&P_DONE)
+ continue;
+ r |= (*fun)(pw,arg);
+ }
+ else
+ job_wait(-pw->p_pid);
+ n++;
+ }
+ }
+ if(!n)
+ shp->exitval = fun?1:ERROR_NOENT;
+ else if(fun)
+ shp->exitval = r;
+ return(r);
+ }
+
+#endif /* SHOPT_COSHELL */
+
+/*
+ * Reap one job
+ * When called with sig==0, it does a blocking wait
+ */
+int job_reap(register int sig)
+{
+ Shell_t *shp = sh_getinterp();
+ register pid_t pid;
+ register struct process *pw;
+ struct process *px;
+ register int flags;
+ struct jobsave *jp;
+ int nochild=0, oerrno, wstat;
+ Waitevent_f waitevent = shp->gd->waitevent;
+ static int wcontinued = WCONTINUED;
+#if SHOPT_COSHELL
+ Cojob_t *cjp;
+ int cojobs;
+ long cotimeout = sig?0:-1;
+ for(pw=job.pwlist;pw;pw=pw->p_nxtjob)
+ {
+ if(pw->p_cojob && !(pw->p_flag&P_DONE))
+ break;
+ }
+ cojobs = (pw!=0);
+ pid = 0;
+#endif /* SHOPT_COSHELL */
+ if (vmbusy())
+ {
+ errormsg(SH_DICT,ERROR_warn(0),"vmbusy() inside job_reap() -- should not happen");
+ if (getenv("_AST_KSH_VMBUSY_ABORT"))
+ abort();
+ }
+#ifdef DEBUG
+ if(sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d signal=%d\n",__LINE__,getpid(),job.in_critical,sig) <=0)
+ write(2,"waitsafe\n",9);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ job.savesig = 0;
+ if(sig)
+ flags = WNOHANG|WUNTRACED|wcontinued;
+ else
+ flags = WUNTRACED|wcontinued;
+ shp->gd->waitevent = 0;
+ oerrno = errno;
+ while(1)
+ {
+ if(!(flags&WNOHANG) && !sh.intrap && job.pwlist)
+ {
+ sh_onstate(SH_TTYWAIT);
+ if(waitevent && (*waitevent)(-1,-1L,0))
+ flags |= WNOHANG;
+ }
+#if SHOPT_COSHELL
+ if(cojobs)
+ {
+ if(cjp = cowait(0,0,cotimeout))
+ {
+ struct cosh *csp;
+ csp = (struct cosh*)(cjp->coshell->data);
+ csp->cojob = cjp;
+ pid = sh_copid(csp);
+ if(cjp->status < 256)
+ wstat = cjp->status <<8;
+ else
+ wstat = cjp->status-256;
+ cotimeout = 0;
+ goto cojob;
+ }
+ else if(copending(0)==0)
+ cojobs = 0;
+ cotimeout = 0;
+ }
+#endif /* SHOPT_COSHELL */
+ pid = waitpid((pid_t)-1,&wstat,flags);
+ sh_offstate(SH_TTYWAIT);
+#if SHOPT_COSHELL
+ cojob:
+#endif /* SHOPT_COSHELL */
+
+ /*
+ * some systems (linux 2.6) may return EINVAL
+ * when there are no continued children
+ */
+
+ if (pid<0 && errno==EINVAL && (flags&WCONTINUED))
+ pid = waitpid((pid_t)-1,&wstat,flags&=~WCONTINUED);
+ sh_sigcheck(shp);
+ if(pid<0 && errno==EINTR && (sig||job.savesig))
+ {
+ errno = 0;
+ continue;
+ }
+ if(pid<=0)
+ break;
+ if(wstat==0)
+ job_chksave(pid);
+ flags |= WNOHANG;
+ job.waitsafe++;
+ jp = 0;
+ lastpid = pid;
+ if(!(pw=job_bypid(pid)))
+ {
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d unknown job pid=%d pw=%x\n",__LINE__,getpid(),job.in_critical,pid,pw);
+#endif /* DEBUG */
+ if (WIFCONTINUED(wstat) && wcontinued)
+ continue;
+ pw = &dummy;
+ pw->p_exit = 0;
+ pw->p_pgrp = 0;
+ pw->p_exitmin = 0;
+ if(job.toclear)
+ job_clear();
+ jp = jobsave_create(pid);
+ pw->p_flag = 0;
+ lastpid = pw->p_pid = pid;
+ px = 0;
+ if(jp && WIFSTOPPED(wstat))
+ {
+ jp->exitval = SH_STOPSIG;
+ continue;
+ }
+ }
+#ifdef SIGTSTP
+ else
+ px=job_byjid(pw->p_job);
+ if (WIFCONTINUED(wstat) && wcontinued)
+ pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED);
+ else if(WIFSTOPPED(wstat))
+ {
+ pw->p_flag |= (P_NOTIFY|P_SIGNALLED|P_STOPPED);
+ pw->p_exit = WSTOPSIG(wstat);
+ if(pw->p_pgrp && pw->p_pgrp==job.curpgid && sh_isstate(SH_STOPOK))
+ sh_fault(pw->p_exit);
+ if(px)
+ {
+ /* move to top of job list */
+ job_unlink(px);
+ px->p_nxtjob = job.pwlist;
+ job.pwlist = px;
+ }
+ continue;
+ }
+ else
+#endif /* SIGTSTP */
+ {
+ /* check for coprocess completion */
+ if(pid==shp->cpid)
+ {
+ sh_close(sh.coutpipe);
+ sh_close(sh.cpipe[1]);
+ sh.cpipe[1] = -1;
+ sh.coutpipe = -1;
+ }
+ else if(shp->subshell)
+ sh_subjobcheck(pid);
+
+ pw->p_flag &= ~(P_STOPPED|P_SIGNALLED);
+ if (WIFSIGNALED(wstat))
+ {
+ pw->p_flag |= (P_DONE|P_NOTIFY|P_SIGNALLED);
+ if (WTERMCORE(wstat))
+ pw->p_flag |= P_COREDUMP;
+ pw->p_exit = WTERMSIG(wstat);
+ /* if process in current jobs terminates from
+ * an interrupt, propogate to parent shell
+ */
+ if(pw->p_pgrp && pw->p_pgrp==job.curpgid && pw->p_exit==SIGINT && sh_isstate(SH_STOPOK))
+ {
+ pw->p_flag &= ~P_NOTIFY;
+ sh_offstate(SH_STOPOK);
+ sh_fault(SIGINT);
+ sh_onstate(SH_STOPOK);
+ }
+ }
+ else
+ {
+ pw->p_flag |= (P_DONE|P_NOTIFY);
+ pw->p_exit = pw->p_exitmin;
+ if(WEXITSTATUS(wstat) > pw->p_exitmin)
+ pw->p_exit = WEXITSTATUS(wstat);
+ }
+#ifdef SHOPT_BGX
+ if((pw->p_flag&P_DONE) && (pw->p_flag&P_BG))
+ {
+ job.numbjob--;
+ if(shp->st.trapcom[SIGCHLD])
+ {
+ shp->sigflag[SIGCHLD] |= SH_SIGTRAP;
+ if(sig==0)
+ job_chldtrap(shp,shp->st.trapcom[SIGCHLD],0);
+ else
+ shp->trapnote |= SH_SIGTRAP;
+ }
+ else
+ pw->p_flag &= ~P_BG;
+ }
+#endif /* SHOPT_BGX */
+ if(pw->p_pgrp==0)
+ pw->p_flag &= ~P_NOTIFY;
+ }
+ if(jp && pw== &dummy)
+ {
+ jp->exitval = pw->p_exit;
+ if(pw->p_flag&P_SIGNALLED)
+ jp->exitval |= SH_EXITSIG;
+ }
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d job %d with pid %d flags=%o complete with status=%x exit=%d\n",__LINE__,getpid(),job.in_critical,pw->p_job,pid,pw->p_flag,wstat,pw->p_exit);
+ sfsync(sfstderr);
+#endif /* DEBUG*/
+ /* only top-level process in job should have notify set */
+ if(px && pw != px)
+ pw->p_flag &= ~P_NOTIFY;
+ if(pid==pw->p_fgrp && pid==tcgetpgrp(JOBTTY))
+ {
+ px = job_byjid((int)pw->p_job);
+ for(; px && (px->p_flag&P_DONE); px=px->p_nxtproc);
+ if(!px)
+ tcsetpgrp(JOBTTY,job.mypid);
+ }
+#ifndef SHOPT_BGX
+ if(!shp->intrap && shp->st.trapcom[SIGCHLD] && pid>0 && (pwfg!=job_bypid(pid)))
+ {
+ shp->sigflag[SIGCHLD] |= SH_SIGTRAP;
+ shp->trapnote |= SH_SIGTRAP;
+ }
+#endif
+ }
+ if(errno==ECHILD)
+ {
+ errno = oerrno;
+#ifdef SHOPT_BGX
+ job.numbjob = 0;
+#endif /* SHOPT_BGX */
+ nochild = 1;
+ }
+ shp->gd->waitevent = waitevent;
+ if(sh_isoption(SH_NOTIFY) && sh_isstate(SH_TTYWAIT))
+ {
+ outfile = sfstderr;
+ job_list(pw,JOB_NFLAG|JOB_NLFLAG);
+ job_unpost(pw,1);
+ sfsync(sfstderr);
+ }
+ if(sig)
+ signal(sig, job_waitsafe);
+ return(nochild);
+}
+
+/*
+ * This is the SIGCLD interrupt routine
+ */
+static void job_waitsafe(int sig)
+{
+ if(job.in_critical || vmbusy())
+ {
+ job.savesig = sig;
+ job.waitsafe++;
+ }
+ else
+ job_reap(sig);
+}
+
+/*
+ * initialize job control if possible
+ * if lflag is set the switching driver message will not print
+ */
+void job_init(Shell_t *shp, int lflag)
+{
+ register int ntry=0;
+ job.fd = JOBTTY;
+ signal(SIGCHLD,job_waitsafe);
+# if defined(SIGCLD) && (SIGCLD!=SIGCHLD)
+ signal(SIGCLD,job_waitsafe);
+# endif
+ if(njob_savelist < NJOB_SAVELIST)
+ init_savelist();
+ if(!sh_isoption(SH_INTERACTIVE))
+ return;
+ /* use new line discipline when available */
+#ifdef NTTYDISC
+# ifdef FIOLOOKLD
+ if((job.linedisc = ioctl(JOBTTY, FIOLOOKLD, 0)) <0)
+# else
+ if(ioctl(JOBTTY,TIOCGETD,&job.linedisc) !=0)
+# endif /* FIOLOOKLD */
+ return;
+ if(job.linedisc!=NTTYDISC && job.linedisc!=OTTYDISC)
+ {
+ /* no job control when running with MPX */
+# if SHOPT_VSH
+ sh_onoption(SH_VIRAW);
+# endif /* SHOPT_VSH */
+ return;
+ }
+ if(job.linedisc==NTTYDISC)
+ job.linedisc = -1;
+#endif /* NTTYDISC */
+
+ job.mypgid = getpgrp();
+ /* some systems have job control, but not initialized */
+ if(job.mypgid<=0)
+ {
+ /* Get a controlling terminal and set process group */
+ /* This should have already been done by rlogin */
+ register int fd;
+ register char *ttynam;
+#ifndef SIGTSTP
+ setpgid(0,shp->gd->pid);
+#endif /*SIGTSTP */
+ if(job.mypgid<0 || !(ttynam=ttyname(JOBTTY)))
+ return;
+ close(JOBTTY);
+ if((fd = open(ttynam,O_RDWR)) <0)
+ return;
+ if(fd!=JOBTTY)
+ sh_iorenumber(shp,fd,JOBTTY);
+ job.mypgid = shp->gd->pid;
+#ifdef SIGTSTP
+ tcsetpgrp(JOBTTY,shp->gd->pid);
+ setpgid(0,shp->gd->pid);
+#endif /* SIGTSTP */
+ }
+#ifdef SIGTSTP
+ if(possible = (setpgid(0,job.mypgid)>=0) || errno==EPERM)
+ {
+ /* wait until we are in the foreground */
+ while((job.mytgid=tcgetpgrp(JOBTTY)) != job.mypgid)
+ {
+ if(job.mytgid == -1)
+ return;
+ /* Stop this shell until continued */
+ signal(SIGTTIN,SIG_DFL);
+ kill(shp->gd->pid,SIGTTIN);
+ /* resumes here after continue tries again */
+ if(ntry++ > IOMAXTRY)
+ {
+ errormsg(SH_DICT,0,e_no_start);
+ return;
+ }
+ }
+ }
+#endif /* SIGTTIN */
+
+#ifdef NTTYDISC
+ /* set the line discipline */
+ if(job.linedisc>=0)
+ {
+ int linedisc = NTTYDISC;
+# ifdef FIOPUSHLD
+ tty_get(JOBTTY,&my_stty);
+ if (ioctl(JOBTTY, FIOPOPLD, 0) < 0)
+ return;
+ if (ioctl(JOBTTY, FIOPUSHLD, &linedisc) < 0)
+ {
+ ioctl(JOBTTY, FIOPUSHLD, &job.linedisc);
+ return;
+ }
+ tty_set(JOBTTY,TCSANOW,&my_stty);
+# else
+ if(ioctl(JOBTTY,TIOCSETD,&linedisc) !=0)
+ return;
+# endif /* FIOPUSHLD */
+ if(lflag==0)
+ errormsg(SH_DICT,0,e_newtty);
+ else
+ job.linedisc = -1;
+ }
+#endif /* NTTYDISC */
+ if(!possible)
+ return;
+
+#ifdef SIGTSTP
+ /* make sure that we are a process group leader */
+ setpgid(0,shp->gd->pid);
+# if defined(SA_NOCLDSTOP) || defined(SA_NOCLDWAIT)
+# if !defined(SA_NOCLDSTOP)
+# define SA_NOCLDSTOP 0
+# endif
+# if !defined(SA_NOCLDWAIT)
+# define SA_NOCLDWAIT 0
+# endif
+ sigflag(SIGCHLD, SA_NOCLDSTOP|SA_NOCLDWAIT, 0);
+# endif /* SA_NOCLDSTOP || SA_NOCLDWAIT */
+ signal(SIGTTIN,SIG_IGN);
+ signal(SIGTTOU,SIG_IGN);
+ /* The shell now handles ^Z */
+ signal(SIGTSTP,sh_fault);
+ tcsetpgrp(JOBTTY,shp->gd->pid);
+# ifdef CNSUSP
+ /* set the switch character */
+ tty_get(JOBTTY,&my_stty);
+ job.suspend = (unsigned)my_stty.c_cc[VSUSP];
+ if(job.suspend == (unsigned char)CNSUSP)
+ {
+ my_stty.c_cc[VSUSP] = CSWTCH;
+ tty_set(JOBTTY,TCSAFLUSH,&my_stty);
+ }
+# endif /* CNSUSP */
+ sh_onoption(SH_MONITOR);
+ job.jobcontrol++;
+ job.mypid = shp->gd->pid;
+#endif /* SIGTSTP */
+ return;
+}
+
+
+/*
+ * see if there are any stopped jobs
+ * restore tty driver and pgrp
+ */
+int job_close(Shell_t* shp)
+{
+ register struct process *pw;
+ register int count = 0, running = 0;
+ if(possible && !job.jobcontrol)
+ return(0);
+ else if(!possible && (!sh_isstate(SH_MONITOR) || sh_isstate(SH_FORKED)))
+ return(0);
+ else if(getpid() != job.mypid)
+ return(0);
+ job_lock();
+ if(!tty_check(0))
+ beenhere++;
+ for(pw=job.pwlist;pw;pw=pw->p_nxtjob)
+ {
+ if(!(pw->p_flag&P_STOPPED))
+ {
+ if(!(pw->p_flag&P_DONE))
+ running++;
+ continue;
+ }
+ if(beenhere)
+ killpg(pw->p_pgrp,SIGTERM);
+ count++;
+ }
+ if(beenhere++ == 0 && job.pwlist)
+ {
+ if(count)
+ {
+ errormsg(SH_DICT,0,e_terminate);
+ return(-1);
+ }
+ else if(running && shp->login_sh)
+ {
+ errormsg(SH_DICT,0,e_jobsrunning);
+ return(-1);
+ }
+ }
+ job_unlock();
+# ifdef SIGTSTP
+ if(possible && setpgid(0,job.mypgid)>=0)
+ tcsetpgrp(job.fd,job.mypgid);
+# endif /* SIGTSTP */
+# ifdef NTTYDISC
+ if(job.linedisc>=0)
+ {
+ /* restore old line discipline */
+# ifdef FIOPUSHLD
+ tty_get(job.fd,&my_stty);
+ if (ioctl(job.fd, FIOPOPLD, 0) < 0)
+ return(0);
+ if (ioctl(job.fd, FIOPUSHLD, &job.linedisc) < 0)
+ {
+ job.linedisc = NTTYDISC;
+ ioctl(job.fd, FIOPUSHLD, &job.linedisc);
+ return(0);
+ }
+ tty_set(job.fd,TCSAFLUSH,&my_stty);
+# else
+ if(ioctl(job.fd,TIOCSETD,&job.linedisc) !=0)
+ return(0);
+# endif /* FIOPUSHLD */
+ errormsg(SH_DICT,0,e_oldtty);
+ }
+# endif /* NTTYDISC */
+# ifdef CNSUSP
+ if(possible && job.suspend==CNSUSP)
+ {
+ tty_get(job.fd,&my_stty);
+ my_stty.c_cc[VSUSP] = CNSUSP;
+ tty_set(job.fd,TCSAFLUSH,&my_stty);
+ }
+# endif /* CNSUSP */
+ job.jobcontrol = 0;
+ return(0);
+}
+
+static void job_set(register struct process *pw)
+{
+ Shell_t *shp = pw->p_shp;
+ /* save current terminal state */
+ tty_get(job.fd,&my_stty);
+ if(pw->p_flag&P_STTY)
+ {
+ /* restore terminal state for job */
+ tty_set(job.fd,TCSAFLUSH,&pw->p_stty);
+ }
+#ifdef SIGTSTP
+ if((pw->p_flag&P_STOPPED) || tcgetpgrp(job.fd) == shp->gd->pid)
+ tcsetpgrp(job.fd,pw->p_fgrp);
+ /* if job is stopped, resume it in the background */
+ job_unstop(pw);
+#endif /* SIGTSTP */
+}
+
+static void job_reset(register struct process *pw)
+{
+ /* save the terminal state for current job */
+#ifdef SIGTSTP
+ job_fgrp(pw,tcgetpgrp(job.fd));
+ if(tcsetpgrp(job.fd,job.mypid) !=0)
+ return;
+#endif /* SIGTSTP */
+ /* force the following tty_get() to do a tcgetattr() unless fg */
+ if(!(pw->p_flag&P_FG))
+ tty_set(-1, 0, NIL(struct termios*));
+ if(pw && (pw->p_flag&P_SIGNALLED) && pw->p_exit!=SIGHUP)
+ {
+ if(tty_get(job.fd,&pw->p_stty) == 0)
+ pw->p_flag |= P_STTY;
+ /* restore terminal state for job */
+ tty_set(job.fd,TCSAFLUSH,&my_stty);
+ }
+ beenhere = 0;
+}
+#endif /* JOBS */
+
+/*
+ * wait built-in command
+ */
+
+void job_bwait(char **jobs)
+{
+ register char *jp;
+ register struct process *pw;
+ register pid_t pid;
+ if(*jobs==0)
+ job_wait((pid_t)-1);
+ else while(jp = *jobs++)
+ {
+#ifdef JOBS
+ if(*jp == '%')
+ {
+ job_lock();
+ pw = job_bystring(jp);
+ job_unlock();
+ if(pw)
+ pid = pw->p_pid;
+ else
+ return;
+ }
+# if SHOPT_COSHELL
+ else if(isalpha(*jp))
+ {
+ job_cowalk(NULL,0,jp);
+ return;
+ }
+# endif /* SHOPT_COSHELL */
+ else
+#endif /* JOBS */
+ pid = pid_fromstring(jp);
+ job_wait(-pid);
+ }
+}
+
+#ifdef JOBS
+/*
+ * execute function <fun> for each job
+ */
+
+int job_walk(Sfio_t *file,int (*fun)(struct process*,int),int arg,char *joblist[])
+{
+ register struct process *pw;
+ register int r = 0;
+ register char *jobid, **jobs=joblist;
+ register struct process *px;
+ job_string = 0;
+ outfile = file;
+ by_number = 0;
+ job_lock();
+ pw = job.pwlist;
+#if SHOPT_COSHELL
+ job_waitsafe(SIGCHLD);
+#endif /* SHOPT_COSHELL */
+ if(jobs==0)
+ {
+ /* do all jobs */
+ for(;pw;pw=px)
+ {
+ px = pw->p_nxtjob;
+ if(pw->p_env != sh.jobenv)
+ continue;
+ if((*fun)(pw,arg))
+ r = 2;
+ }
+ }
+ else if(*jobs==0) /* current job */
+ {
+ /* skip over non-stop jobs */
+ while(pw && (pw->p_env!=sh.jobenv || pw->p_pgrp==0))
+ pw = pw->p_nxtjob;
+ if((*fun)(pw,arg))
+ r = 2;
+ }
+ else while(jobid = *jobs++)
+ {
+ job_string = jobid;
+ if(*jobid==0)
+ errormsg(SH_DICT,ERROR_exit(1),e_jobusage,job_string);
+#if SHOPT_COSHELL
+ if(isalpha(*jobid))
+ {
+ r = job_cowalk(fun,arg,jobid);
+ by_number = 0;
+ job_unlock();
+ return(r);
+ }
+#endif /* SHOPT_COSHELL */
+ if(*jobid == '%')
+ pw = job_bystring(jobid);
+ else
+ {
+ int pid = pid_fromstring(jobid);
+ if(!(pw = job_bypid(pid)))
+ {
+ pw = &dummy;
+ pw->p_shp = sh_getinterp();
+ pw->p_pid = pid;
+ pw->p_pgrp = pid;
+ }
+ by_number = 1;
+ }
+ if((*fun)(pw,arg))
+ r = 2;
+ by_number = 0;
+ }
+ job_unlock();
+ return(r);
+}
+
+/*
+ * send signal <sig> to background process group if not disowned
+ */
+int job_terminate(register struct process *pw,register int sig)
+{
+ if(pw->p_pgrp && !(pw->p_flag&P_DISOWN))
+ job_kill(pw,sig);
+ return(0);
+}
+
+/*
+ * list the given job
+ * flag JOB_LFLAG for long listing
+ * flag JOB_NFLAG for list only jobs marked for notification
+ * flag JOB_PFLAG for process id(s) only
+ */
+
+int job_list(struct process *pw,register int flag)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct process *px = pw;
+ register int n;
+ register const char *msg;
+ register int msize;
+ if(!pw || pw->p_job<=0)
+ return(1);
+ if(pw->p_env != shp->jobenv)
+ return(0);
+ if((flag&JOB_NFLAG) && (!(px->p_flag&P_NOTIFY)||px->p_pgrp==0))
+ return(0);
+ if((flag&JOB_PFLAG))
+ {
+#if SHOPT_COSHELL
+ sfprintf(outfile,"%s\n",sh_pid2str(shp,px->p_pgrp?px->p_pgrp:px->p_pid));
+#else
+ sfprintf(outfile,"%d\n",px->p_pgrp?px->p_pgrp:px->p_pid);
+#endif /* SHOPT_COSHELL */
+ return(0);
+ }
+ if((px->p_flag&P_DONE) && job.waitall && !(flag&JOB_LFLAG))
+ return(0);
+ job_lock();
+ n = px->p_job;
+ if(px==job.pwlist)
+ msize = '+';
+ else if(px==job.pwlist->p_nxtjob)
+ msize = '-';
+ else
+ msize = ' ';
+ if(flag&JOB_NLFLAG)
+ sfputc(outfile,'\n');
+ sfprintf(outfile,"[%d] %c ",n, msize);
+ do
+ {
+ n = 0;
+ if(flag&JOB_LFLAG)
+#if SHOPT_COSHELL
+ sfprintf(outfile,"%s\t",sh_pid2str(shp,px->p_pid));
+#else
+ sfprintf(outfile,"%d\t",px->p_pid);
+#endif /* SHOPT_COSHELL */
+ if(px->p_flag&P_SIGNALLED)
+ msg = job_sigmsg((int)(px->p_exit));
+ else if(px->p_flag&P_NOTIFY)
+ {
+ msg = sh_translate(e_done);
+ n = px->p_exit;
+ }
+ else
+ msg = sh_translate(e_running);
+ px->p_flag &= ~P_NOTIFY;
+ sfputr(outfile,msg,-1);
+ msize = strlen(msg);
+ if(n)
+ {
+ sfprintf(outfile,"(%d)",(int)n);
+ msize += (3+(n>10)+(n>100));
+ }
+ if(px->p_flag&P_COREDUMP)
+ {
+ msg = sh_translate(e_coredump);
+ sfputr(outfile, msg, -1);
+ msize += strlen(msg);
+ }
+ sfnputc(outfile,' ',MAXMSG>msize?MAXMSG-msize:1);
+ if(flag&JOB_LFLAG)
+ px = px->p_nxtproc;
+ else
+ {
+ while(px=px->p_nxtproc)
+ px->p_flag &= ~P_NOTIFY;
+ px = 0;
+ }
+ if(!px)
+ hist_list(shgd->hist_ptr,outfile,pw->p_name,0,";");
+ else
+ sfputr(outfile, e_nlspace, -1);
+ }
+ while(px);
+ job_unlock();
+ return(0);
+}
+
+/*
+ * get the process group given the job number
+ * This routine returns the process group number or -1
+ */
+static struct process *job_bystring(register char *ajob)
+{
+ register struct process *pw=job.pwlist;
+ register int c;
+ if(*ajob++ != '%' || !pw)
+ return(NIL(struct process*));
+ c = *ajob;
+ if(isdigit(c))
+ pw = job_byjid((int)strtol(ajob, (char**)0, 10));
+ else if(c=='+' || c=='%')
+ ;
+ else if(c=='-')
+ {
+ if(pw)
+ pw = job.pwlist->p_nxtjob;
+ }
+ else
+ pw = job_byname(ajob);
+ if(pw && pw->p_flag)
+ return(pw);
+ return(NIL(struct process*));
+}
+
+/*
+ * Kill a job or process
+ */
+
+int job_kill(register struct process *pw,register int sig)
+{
+ Shell_t *shp = pw->p_shp;
+ register pid_t pid;
+ register int r;
+ const char *msg;
+#ifdef SIGTSTP
+ int stopsig = (sig==SIGSTOP||sig==SIGTSTP||sig==SIGTTIN||sig==SIGTTOU);
+#else
+# define stopsig 1
+#endif /* SIGTSTP */
+ job_lock();
+ errno = ECHILD;
+ if(pw==0)
+ goto error;
+ pid = pw->p_pid;
+#if SHOPT_COSHELL
+ if(pw->p_cojob)
+ r = cokill(pw->p_cojob->coshell,pw->p_cojob,sig);
+ else
+#endif /* SHOPT_COSHELL */
+ if(by_number)
+ {
+ if(pid==0 && job.jobcontrol)
+ r = job_walk(outfile, job_kill,sig, (char**)0);
+#ifdef SIGTSTP
+ if(sig==SIGSTOP && pid==shp->gd->pid && shp->gd->ppid==1)
+ {
+ /* can't stop login shell */
+ errno = EPERM;
+ r = -1;
+ }
+ else
+ {
+ if(pid>=0)
+ {
+ if((r = kill(pid,sig))>=0 && !stopsig)
+ {
+ if(pw->p_flag&P_STOPPED)
+ pw->p_flag &= ~(P_STOPPED|P_SIGNALLED);
+ if(sig)
+ kill(pid,SIGCONT);
+ }
+ }
+ else
+ {
+ if((r = killpg(-pid,sig))>=0 && !stopsig)
+ {
+ job_unstop(job_bypid(pw->p_pid));
+ if(sig)
+ killpg(-pid,SIGCONT);
+ }
+ }
+ }
+#else
+ if(pid>=0)
+ r = kill(pid,sig);
+ else
+ r = killpg(-pid,sig);
+#endif /* SIGTSTP */
+ }
+ else
+ {
+ if(pid = pw->p_pgrp)
+ {
+ r = killpg(pid,sig);
+#ifdef SIGTSTP
+ if(r>=0 && (sig==SIGHUP||sig==SIGTERM || sig==SIGCONT))
+ job_unstop(pw);
+#endif /* SIGTSTP */
+ if(r>=0)
+ sh_delay(.05);
+ }
+ while(pw && pw->p_pgrp==0 && (r=kill(pw->p_pid,sig))>=0)
+ {
+#ifdef SIGTSTP
+ if(sig==SIGHUP || sig==SIGTERM)
+ kill(pw->p_pid,SIGCONT);
+#endif /* SIGTSTP */
+ pw = pw->p_nxtproc;
+ }
+ }
+ if(r<0 && job_string)
+ {
+ error:
+ if(pw && by_number)
+ msg = sh_translate(e_no_proc);
+ else
+ msg = sh_translate(e_no_job);
+ if(errno == EPERM)
+ msg = sh_translate(e_access);
+ sfprintf(sfstderr,"kill: %s: %s\n",job_string, msg);
+ r = 2;
+ }
+ sh_delay(.001);
+ job_unlock();
+ return(r);
+}
+
+/*
+ * Get process structure from first letters of jobname
+ *
+ */
+
+static struct process *job_byname(char *name)
+{
+ register struct process *pw = job.pwlist;
+ register struct process *pz = 0;
+ register int *flag = 0;
+ register char *cp = name;
+ int offset;
+ if(!shgd->hist_ptr)
+ return(NIL(struct process*));
+ if(*cp=='?')
+ cp++,flag= &offset;
+ for(;pw;pw=pw->p_nxtjob)
+ {
+ if(hist_match(shgd->hist_ptr,pw->p_name,cp,flag)>=0)
+ {
+ if(pz)
+ errormsg(SH_DICT,ERROR_exit(1),e_jobusage,name-1);
+ pz = pw;
+ }
+ }
+ return(pz);
+}
+
+#else
+# define job_set(x)
+# define job_reset(x)
+#endif /* JOBS */
+
+
+
+/*
+ * Initialize the process posting array
+ */
+
+void job_clear(void)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct process *pw, *px;
+ register struct process *pwnext;
+ register int j = BYTE(shp->gd->lim.child_max);
+ register struct jobsave *jp,*jpnext;
+ job_lock();
+ for(pw=job.pwlist; pw; pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ while(px=pw)
+ {
+ pw = pw->p_nxtproc;
+ free((void*)px);
+ }
+ }
+ for(jp=bck.list; jp;jp=jpnext)
+ {
+ jpnext = jp->next;
+ free((void*)jp);
+ }
+ bck.list = 0;
+ if(njob_savelist < NJOB_SAVELIST)
+ init_savelist();
+ job.pwlist = NIL(struct process*);
+ job.numpost=0;
+#ifdef SHOPT_BGX
+ job.numbjob = 0;
+#endif /* SHOPT_BGX */
+ job.waitall = 0;
+ job.curpgid = 0;
+ job.toclear = 0;
+ if(!job.freejobs)
+ job.freejobs = (unsigned char*)malloc((unsigned)(j+1));
+ while(j >=0)
+ job.freejobs[j--] = 0;
+ job_unlock();
+}
+
+/*
+ * put the process <pid> on the process list and return the job number
+ * if non-zero, <join> is the process id of the job to join
+ */
+
+int job_post(Shell_t *shp,pid_t pid, pid_t join)
+{
+ register struct process *pw;
+ register History_t *hp = shp->gd->hist_ptr;
+#ifdef SHOPT_BGX
+ int val,bg=0;
+#else
+ int val;
+#endif
+ shp->jobenv = shp->curenv;
+ if(job.toclear)
+ {
+ job_clear();
+ return(0);
+ }
+ job_lock();
+#ifdef SHOPT_BGX
+ if(join==1)
+ {
+ join = 0;
+ bg = P_BG;
+ job.numbjob++;
+ }
+#endif /* SHOPT_BGX */
+ if(njob_savelist < NJOB_SAVELIST)
+ init_savelist();
+ if(pw = job_bypid(pid))
+ job_unpost(pw,0);
+ if(join)
+ {
+ if(pw=job_bypid(join))
+ val = pw->p_job;
+ else
+ val = job.curjobid;
+ /* if job to join is not first move it to front */
+ if(val && (pw=job_byjid(val)) != job.pwlist)
+ {
+ job_unlink(pw);
+ pw->p_nxtjob = job.pwlist;
+ job.pwlist = pw;
+ }
+ }
+ if(pw=freelist)
+ freelist = pw->p_nxtjob;
+ else
+ pw = new_of(struct process,0);
+ pw->p_flag = 0;
+ job.numpost++;
+ if(join && job.pwlist)
+ {
+ /* join existing current job */
+ pw->p_nxtjob = job.pwlist->p_nxtjob;
+ pw->p_nxtproc = job.pwlist;
+ pw->p_job = job.pwlist->p_job;
+ }
+ else
+ {
+ /* create a new job */
+ while((pw->p_job = job_alloc()) < 0)
+ job_wait((pid_t)1);
+ pw->p_nxtjob = job.pwlist;
+ pw->p_nxtproc = 0;
+ }
+ pw->p_exitval = job.exitval;
+#if SHOPT_COSHELL
+ pw->p_cojob = 0;
+ if(shp->coshell && (pid&COPID_BIT))
+ {
+ pw->p_cojob = ((struct cosh*)shp->coshell)->cojob;
+ job.curpgid = sh_isstate(SH_MONITOR)?pid:0;
+ }
+#endif /* SHOPT_COSHELL */
+ job.pwlist = pw;
+ pw->p_shp = shp;
+ pw->p_env = shp->curenv;
+ pw->p_pid = pid;
+ if(!shp->outpipe || shp->cpid==pid)
+ pw->p_flag = P_EXITSAVE;
+ pw->p_exitmin = shp->xargexit;
+ pw->p_exit = 0;
+ if(sh_isstate(SH_MONITOR))
+ {
+ if(killpg(job.curpgid,0)<0 && errno==ESRCH)
+ job.curpgid = pid;
+ pw->p_fgrp = job.curpgid;
+ }
+ else
+ pw->p_fgrp = 0;
+ pw->p_pgrp = pw->p_fgrp;
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: post pid=%d critical=%d job=%d pid=%d pgid=%d savesig=%d join=%d\n",__LINE__,getpid(),job.in_critical,pw->p_job,
+ pw->p_pid,pw->p_pgrp,job.savesig,join);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+#ifdef JOBS
+ if(hp && !sh_isstate(SH_PROFILE))
+ pw->p_name=hist_tell(shgd->hist_ptr,(int)hp->histind-1);
+ else
+ pw->p_name = -1;
+#endif /* JOBS */
+ if ((val = job_chksave(pid))>=0 && !jobfork)
+ {
+ pw->p_exit = val;
+ if(pw->p_exit==SH_STOPSIG)
+ {
+ pw->p_flag |= (P_SIGNALLED|P_STOPPED);
+ pw->p_exit = 0;
+ }
+ else if(pw->p_exit >= SH_EXITSIG)
+ {
+ pw->p_flag |= P_DONE|P_SIGNALLED;
+ pw->p_exit &= SH_EXITMASK;
+ }
+ else
+ pw->p_flag |= (P_DONE|P_NOTIFY);
+ }
+#ifdef SHOPT_BGX
+ if(bg)
+ {
+ if(pw->p_flag&P_DONE)
+ job.numbjob--;
+ else
+ pw->p_flag |= P_BG;
+ }
+#endif /* SHOPT_BGX */
+ lastpid = 0;
+ job_unlock();
+ return(pw->p_job);
+}
+
+/*
+ * Returns a process structure give a process id
+ */
+
+static struct process *job_bypid(pid_t pid)
+{
+ register struct process *pw, *px;
+ for(pw=job.pwlist; pw; pw=pw->p_nxtjob)
+ for(px=pw; px; px=px->p_nxtproc)
+ {
+ if(px->p_pid==pid)
+ return(px);
+ }
+ return(NIL(struct process*));
+}
+
+/*
+ * return a pointer to a job given the job id
+ */
+
+static struct process *job_byjid(int jobid)
+{
+ register struct process *pw;
+ for(pw=job.pwlist;pw; pw = pw->p_nxtjob)
+ {
+ if(pw->p_job==jobid)
+ break;
+ }
+ return(pw);
+}
+
+/*
+ * print a signal message
+ */
+static void job_prmsg(register struct process *pw)
+{
+ if(pw->p_exit!=SIGINT && pw->p_exit!=SIGPIPE)
+ {
+ register const char *msg, *dump;
+ msg = job_sigmsg((int)(pw->p_exit));
+ msg = sh_translate(msg);
+ if(pw->p_flag&P_COREDUMP)
+ dump = sh_translate(e_coredump);
+ else
+ dump = "";
+ if(sh_isstate(SH_INTERACTIVE))
+ sfprintf(sfstderr,"%s%s\n",msg,dump);
+ else
+ errormsg(SH_DICT,2,"%d: %s%s",pw->p_pid,msg,dump);
+ }
+}
+
+/*
+ * Wait for process pid to complete
+ * If pid < -1, then wait can be interrupted, -pid is waited for (wait builtin)
+ * pid=0 to unpost all done processes
+ * pid=1 to wait for at least one process to complete
+ * pid=-1 to wait for all runing processes
+ */
+
+int job_wait(register pid_t pid)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct process *pw=0,*px;
+ register int jobid = 0;
+ int nochild = 1;
+ char intr = 0;
+ if(pid < 0)
+ {
+ pid = -pid;
+ intr = 1;
+ }
+ job_lock();
+ if(pid==0)
+ {
+ if(!job.waitall || !job.curjobid || !(pw = job_byjid(job.curjobid)))
+ {
+ job_unlock();
+ goto done;
+ }
+ jobid = pw->p_job;
+ job.curjobid = 0;
+ if(!(pw->p_flag&(P_DONE|P_STOPPED)))
+ job_reap(job.savesig);
+ }
+ if(pid > 1)
+ {
+ if(pid==shp->spid)
+ shp->spid = 0;
+ if(!(pw=job_bypid(pid)))
+ {
+ /* check to see whether job status has been saved */
+ if((shp->exitval = job_chksave(pid)) < 0)
+ shp->exitval = ERROR_NOENT;
+ exitset();
+ job_unlock();
+ return(nochild);
+ }
+ else if(intr && pw->p_env!=shp->curenv)
+ {
+ shp->exitval = ERROR_NOENT;
+ job_unlock();
+ return(nochild);
+ }
+ jobid = pw->p_job;
+ if(!intr)
+ pw->p_flag &= ~P_EXITSAVE;
+ if(pw->p_pgrp && job.parent!= (pid_t)-1)
+ job_set(job_byjid(jobid));
+ }
+ pwfg = pw;
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d job=%d pid=%d\n",__LINE__,getpid(),job.in_critical,jobid,pid);
+ if(pw)
+ sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d flags=%o\n",__LINE__,getpid(),job.in_critical,pw->p_flag);
+#endif /* DEBUG*/
+ errno = 0;
+ if(shp->coutpipe>=0 && lastpid && shp->cpid==lastpid)
+ {
+ sh_close(shp->coutpipe);
+ sh_close(shp->cpipe[1]);
+ shp->cpipe[1] = shp->coutpipe = -1;
+ }
+ while(1)
+ {
+ if(job.waitsafe)
+ {
+ for(px=job.pwlist;px; px = px->p_nxtjob)
+ {
+ if(px!=pw && (px->p_flag&P_NOTIFY))
+ {
+ if(sh_isoption(SH_NOTIFY))
+ {
+ outfile = sfstderr;
+ job_list(px,JOB_NFLAG|JOB_NLFLAG);
+ sfsync(sfstderr);
+ }
+ else if(!sh_isoption(SH_INTERACTIVE) && (px->p_flag&P_SIGNALLED))
+ {
+ job_prmsg(px);
+ px->p_flag &= ~P_NOTIFY;
+ }
+ }
+ }
+ }
+ if(pw && (pw->p_flag&(P_DONE|P_STOPPED)))
+ {
+#ifdef SIGTSTP
+ if(pw->p_flag&P_STOPPED)
+ {
+ pw->p_flag |= P_EXITSAVE;
+ if(sh_isoption(SH_INTERACTIVE) && !sh_isstate(SH_FORKED))
+ {
+ if( pw->p_exit!=SIGTTIN && pw->p_exit!=SIGTTOU)
+ break;
+
+ killpg(pw->p_pgrp,SIGCONT);
+ }
+ else /* ignore stop when non-interactive */
+ pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED|P_EXITSAVE);
+ }
+ else
+#endif /* SIGTSTP */
+ {
+ if(pw->p_flag&P_SIGNALLED)
+ {
+ pw->p_flag &= ~P_NOTIFY;
+ job_prmsg(pw);
+ }
+ else if(pw->p_flag&P_DONE)
+ pw->p_flag &= ~P_NOTIFY;
+ if(pw->p_job==jobid)
+ {
+ px = job_byjid(jobid);
+ /* last process in job */
+ if(px!=pw)
+ px = 0;
+ if(px)
+ {
+ shp->exitval=px->p_exit;
+ if(px->p_flag&P_SIGNALLED)
+ shp->exitval |= SH_EXITSIG;
+ if(intr)
+ px->p_flag &= ~P_EXITSAVE;
+ }
+ }
+ px = job_unpost(pw,1);
+ if(!px || !job.waitall)
+ break;
+ pw = px;
+ continue;
+ }
+ }
+ sfsync(sfstderr);
+ job.waitsafe = 0;
+ nochild = job_reap(job.savesig);
+ if(job.waitsafe)
+ continue;
+ if(nochild)
+ break;
+ if(shp->sigflag[SIGALRM]&SH_SIGTRAP)
+ sh_timetraps(shp);
+ if((intr && shp->trapnote) || (pid==1 && !intr))
+ break;
+ }
+ if(intr && shp->trapnote)
+ shp->exitval = 1;
+ pwfg = 0;
+ job_unlock();
+ if(pid==1)
+ return(nochild);
+ exitset();
+ if(pid==0)
+ goto done;
+ if(pw->p_pgrp)
+ {
+ job_reset(pw);
+ /* propogate keyboard interrupts to parent */
+ if((pw->p_flag&P_SIGNALLED) && pw->p_exit==SIGINT && !(shp->sigflag[SIGINT]&SH_SIGOFF))
+ sh_fault(SIGINT);
+#ifdef SIGTSTP
+ else if((pw->p_flag&P_STOPPED) && pw->p_exit==SIGTSTP)
+ {
+ job.parent = 0;
+ sh_fault(SIGTSTP);
+ }
+#endif /* SIGTSTP */
+ }
+ else
+ {
+ if(pw->p_pid == tcgetpgrp(JOBTTY))
+ {
+ if(pw->p_pgrp==0)
+ pw->p_pgrp = pw->p_pid;
+ job_reset(pw);
+ }
+ tty_set(-1, 0, NIL(struct termios*));
+ }
+done:
+ if(!job.waitall && sh_isoption(SH_PIPEFAIL))
+ return(nochild);
+ if(!shp->intrap)
+ {
+ job_lock();
+ for(pw=job.pwlist; pw; pw=px)
+ {
+ px = pw->p_nxtjob;
+ job_unpost(pw,0);
+ }
+ job_unlock();
+ }
+ return(nochild);
+}
+
+/*
+ * move job to foreground if bgflag == 'f'
+ * move job to background if bgflag == 'b'
+ * disown job if bgflag == 'd'
+ */
+
+int job_switch(register struct process *pw,int bgflag)
+{
+ register const char *msg;
+ job_lock();
+ if(!pw || !(pw=job_byjid((int)pw->p_job)))
+ {
+ job_unlock();
+ return(1);
+ }
+ if(bgflag=='d')
+ {
+ for(; pw; pw=pw->p_nxtproc)
+ pw->p_flag |= P_DISOWN;
+ job_unlock();
+ return(0);
+ }
+#ifdef SIGTSTP
+ if(bgflag=='b')
+ {
+ sfprintf(outfile,"[%d]\t",(int)pw->p_job);
+ sh.bckpid = pw->p_pid;
+#ifdef SHOPT_BGX
+ pw->p_flag |= P_BG;
+#endif
+ msg = "&";
+ }
+ else
+ {
+ job_unlink(pw);
+ pw->p_nxtjob = job.pwlist;
+ job.pwlist = pw;
+ msg = "";
+ }
+ hist_list(shgd->hist_ptr,outfile,pw->p_name,'&',";");
+ sfputr(outfile,msg,'\n');
+ sfsync(outfile);
+ if(bgflag=='f')
+ {
+ if(!(pw=job_unpost(pw,1)))
+ {
+ job_unlock();
+ return(1);
+ }
+ job.waitall = 1;
+ pw->p_flag |= P_FG;
+#ifdef SHOPT_BGX
+ pw->p_flag &= ~P_BG;
+#endif
+ job_wait(pw->p_pid);
+ job.waitall = 0;
+ }
+ else if(pw->p_flag&P_STOPPED)
+ job_unstop(pw);
+#endif /* SIGTSTP */
+ job_unlock();
+ return(0);
+}
+
+
+#ifdef SIGTSTP
+/*
+ * Set the foreground group associated with a job
+ */
+
+static void job_fgrp(register struct process *pw, int newgrp)
+{
+ for(; pw; pw=pw->p_nxtproc)
+ pw->p_fgrp = newgrp;
+}
+
+/*
+ * turn off STOP state of a process group and send CONT signals
+ */
+
+static void job_unstop(register struct process *px)
+{
+ register struct process *pw;
+ register int num = 0;
+ for(pw=px ;pw ;pw=pw->p_nxtproc)
+ {
+ if(pw->p_flag&P_STOPPED)
+ {
+ num++;
+ pw->p_flag &= ~(P_STOPPED|P_SIGNALLED|P_NOTIFY);
+ }
+ }
+ if(num!=0)
+ {
+ if(px->p_fgrp != px->p_pgrp)
+ killpg(px->p_fgrp,SIGCONT);
+ killpg(px->p_pgrp,SIGCONT);
+ }
+}
+#endif /* SIGTSTP */
+
+/*
+ * remove a job from table
+ * If all the processes have not completed, unpost first non-completed process
+ * Otherwise the job is removed and job_unpost returns NULL.
+ * pwlist is reset if the first job is removed
+ * if <notify> is non-zero, then jobs with pending notifications are unposted
+ */
+
+static struct process *job_unpost(register struct process *pwtop,int notify)
+{
+ register struct process *pw;
+ /* make sure all processes are done */
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: drop pid=%d critical=%d pid=%d env=%d\n",__LINE__,getpid(),job.in_critical,pwtop->p_pid,pwtop->p_env);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ pwtop = pw = job_byjid((int)pwtop->p_job);
+#ifdef SHOPT_BGX
+ if(pw->p_flag&P_BG)
+ return(pw);
+#endif /* SHOPT_BGX */
+ for(; pw && (pw->p_flag&P_DONE)&&(notify||!(pw->p_flag&P_NOTIFY)||pw->p_env); pw=pw->p_nxtproc);
+ if(pw)
+ return(pw);
+ if(pwtop->p_job == job.curjobid)
+ return(0);
+ /* all processes complete, unpost job */
+ job_unlink(pwtop);
+ for(pw=pwtop; pw; pw=pw->p_nxtproc)
+ {
+ if(pw && pw->p_exitval)
+ *pw->p_exitval = pw->p_exit;
+ /* save the exit status for background jobs */
+ if((pw->p_flag&P_EXITSAVE) || pw->p_pid==sh.spid)
+ {
+ struct jobsave *jp;
+ /* save status for future wait */
+ if(jp = jobsave_create(pw->p_pid))
+ {
+ jp->exitval = pw->p_exit;
+ if(pw->p_flag&P_SIGNALLED)
+ jp->exitval |= SH_EXITSIG;
+ }
+ pw->p_flag &= ~P_EXITSAVE;
+ }
+ pw->p_flag &= ~P_DONE;
+ job.numpost--;
+ pw->p_nxtjob = freelist;
+ freelist = pw;
+ }
+ pwtop->p_pid = 0;
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: free pid=%d critical=%d job=%d\n",__LINE__,getpid(),job.in_critical,pwtop->p_job);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ job_free((int)pwtop->p_job);
+ return((struct process*)0);
+}
+
+/*
+ * unlink a job form the job list
+ */
+static void job_unlink(register struct process *pw)
+{
+ register struct process *px;
+ if(pw==job.pwlist)
+ {
+ job.pwlist = pw->p_nxtjob;
+ job.curpgid = 0;
+ return;
+ }
+ for(px=job.pwlist;px;px=px->p_nxtjob)
+ if(px->p_nxtjob == pw)
+ {
+ px->p_nxtjob = pw->p_nxtjob;
+ return;
+ }
+}
+
+/*
+ * get an unused job number
+ * freejobs is a bit vector, 0 is unused
+ */
+
+static int job_alloc(void)
+{
+ register int j=0;
+ register unsigned mask = 1;
+ register unsigned char *freeword;
+ register int jmax = BYTE(shgd->lim.child_max);
+ /* skip to first word with a free slot */
+ for(j=0;job.freejobs[j] == UCHAR_MAX; j++);
+ if(j >= jmax)
+ {
+ register struct process *pw;
+ for(j=1; j < shgd->lim.child_max; j++)
+ {
+ if((pw=job_byjid(j))&& !job_unpost(pw,0))
+ break;
+ }
+ j /= CHAR_BIT;
+ if(j >= jmax)
+ return(-1);
+ }
+ freeword = &job.freejobs[j];
+ j *= CHAR_BIT;
+ for(j++;mask&(*freeword);j++,mask <<=1);
+ *freeword |= mask;
+ return(j);
+}
+
+/*
+ * return a job number
+ */
+
+static void job_free(register int n)
+{
+ register int j = (--n)/CHAR_BIT;
+ register unsigned mask;
+ n -= j*CHAR_BIT;
+ mask = 1 << n;
+ job.freejobs[j] &= ~mask;
+}
+
+static char *job_sigmsg(int sig)
+{
+ static char signo[40];
+#ifdef apollo
+ /*
+ * This code handles the formatting for the apollo specific signal
+ * SIGAPOLLO.
+ */
+ extern char *apollo_error(void);
+
+ if ( sig == SIGAPOLLO )
+ return( apollo_error() );
+#endif /* apollo */
+ if(sig<=shgd->sigmax && shgd->sigmsg[sig])
+ return(shgd->sigmsg[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if(sig>=sh.gd->sigruntime[SH_SIGRTMIN] && sig<=sh.gd->sigruntime[SH_SIGRTMAX])
+ {
+ static char sigrt[20];
+ if(sig>sh.gd->sigruntime[SH_SIGRTMIN]+(sh.gd->sigruntime[SH_SIGRTMAX]-sig<=sh.gd->sigruntime[SH_SIGRTMIN])/2)
+ sfsprintf(sigrt,sizeof(sigrt),"SIGRTMAX-%d",sh.gd->sigruntime[SH_SIGRTMAX]-sig);
+ else
+ sfsprintf(sigrt,sizeof(sigrt),"SIGRTMIN+%d",sig-sh.gd->sigruntime[SH_SIGRTMIN]);
+ return(sigrt);
+ }
+#endif
+ sfsprintf(signo,sizeof(signo),sh_translate(e_signo),sig);
+ return(signo);
+}
+
+/*
+ * see whether exit status has been saved and delete it
+ * if pid==0, then oldest saved process is deleted
+ * If pid is not found a -1 is returned.
+ */
+static int job_chksave(register pid_t pid)
+{
+ register struct jobsave *jp = bck.list, *jpold=0;
+ register int r= -1;
+ register int count=bck.count;
+ struct back_save *bp= &bck;
+again:
+ while(jp && count-->0)
+ {
+ if(jp->pid==pid)
+ break;
+ if(pid==0 && !jp->next)
+ break;
+ jpold = jp;
+ jp = jp->next;
+ }
+ if(!jp && pid && (bp=bp->prev))
+ {
+ count = bp->count;
+ jp = bp->list;
+ goto again;
+ }
+ if(jp)
+ {
+ r = 0;
+ if(pid)
+ r = jp->exitval;
+ if(jpold)
+ jpold->next = jp->next;
+ else
+ bp->list = jp->next;
+ bp->count--;
+ if(njob_savelist < NJOB_SAVELIST)
+ {
+ njob_savelist++;
+ jp->next = job_savelist;
+ job_savelist = jp;
+ }
+ else
+ free((void*)jp);
+ }
+ return(r);
+}
+
+void *job_subsave(void)
+{
+ struct back_save *bp = new_of(struct back_save,0);
+ job_lock();
+ *bp = bck;
+ bp->prev = bck.prev;
+ bck.count = 0;
+ bck.list = 0;
+ bck.prev = bp;
+ job_unlock();
+ return((void*)bp);
+}
+
+void job_subrestore(void* ptr)
+{
+ register struct jobsave *jp, *jpnext;
+ register struct back_save *bp = (struct back_save*)ptr;
+ register struct process *pw, *px, *pwnext;
+ struct jobsave *end=NULL;
+ job_lock();
+ for(jp=bck.list; jp; jp=jp->next)
+ {
+ if (!jp->next)
+ end = jp;
+ }
+ if(end)
+ end->next = bp->list;
+ else
+ bck.list = bp->list;
+ bck.count += bp->count;
+ bck.prev = bp->prev;
+ while(bck.count > shgd->lim.child_max)
+ job_chksave(0);
+ for(pw=job.pwlist; pw; pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ if(pw->p_env != sh.curenv || pw->p_pid==sh.pipepid)
+ continue;
+ for(px=pw; px; px=px->p_nxtproc)
+ px->p_flag |= P_DONE;
+ job_unpost(pw,0);
+ }
+
+ free((void*)bp);
+ job_unlock();
+}
+
+int sh_waitsafe(void)
+{
+ return(job.waitsafe);
+}
+
+void job_fork(pid_t parent)
+{
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: fork pid=%d critical=%d parent=%d\n",__LINE__,getpid(),job.in_critical,parent);
+#endif /* DEBUG */
+ switch (parent)
+ {
+ case -1:
+ job_lock();
+ jobfork++;
+ break;
+ case 0:
+ jobfork=0;
+ job_unlock();
+ job.waitsafe = 0;
+ job.in_critical = 0;
+ break;
+ default:
+ jobfork=0;
+ job_unlock();
+ break;
+ }
+}
+
diff --git a/src/cmd/ksh93/sh/lex.c b/src/cmd/ksh93/sh/lex.c
new file mode 100644
index 0000000..cbefd16
--- /dev/null
+++ b/src/cmd/ksh93/sh/lex.c
@@ -0,0 +1,2516 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * KornShell lexical analyzer
+ *
+ * Written by David Korn
+ * AT&T Labs
+ *
+ */
+
+#include <ast.h>
+#include <stak.h>
+#include <fcin.h>
+#include <nval.h>
+#include "FEATURE/options"
+
+#if KSHELL
+# include "defs.h"
+#else
+# include <shell.h>
+# define nv_getval(np) ((np)->nvalue)
+ Shell_t sh = {1};
+#endif /* KSHELL */
+
+#include "argnod.h"
+#include "test.h"
+#include "lexstates.h"
+#include "io.h"
+
+#define TEST_RE 3
+#define SYNBAD 3 /* exit value for syntax errors */
+#define STACK_ARRAY 3 /* size of depth match stack growth */
+
+#if _lib_iswblank < 0 /* set in lexstates.h to enable this code */
+
+int
+local_iswblank(wchar_t wc)
+{
+ static int initialized;
+ static wctype_t wt;
+
+ if (!initialized)
+ {
+ initialized = 1;
+ wt = wctype("blank");
+ }
+ return(iswctype(wc, wt));
+}
+
+#endif
+
+/*
+ * This structure allows for arbitrary depth nesting of (...), {...}, [...]
+ */
+struct lexstate
+{
+ char incase; /* 1 for case pattern, 2 after case */
+ char intest; /* 1 inside [[...]] */
+ char testop1; /* 1 when unary test op legal */
+ char testop2; /* 1 when binary test op legal */
+ char reservok; /* >0 for reserved word legal */
+ char skipword; /* next word can't be reserved */
+ char last_quote; /* last multi-line quote character */
+ char nestedbrace; /* ${var op {...}} */
+};
+
+struct lexdata
+{
+ char nocopy;
+ char paren;
+ char dolparen;
+ char nest;
+ char docword;
+ char nested_tilde;
+ char *docend;
+ char noarg;
+ char balance;
+ char warn;
+ char message;
+ char arith;
+ char *first;
+ int level;
+ int lastc;
+ int lex_max;
+ int *lex_match;
+ int lex_state;
+ int docextra;
+#if SHOPT_KIA
+ off_t kiaoff;
+#endif
+};
+
+#define _SHLEX_PRIVATE \
+ struct lexdata lexd; \
+ struct lexstate lex;
+
+#include "shlex.h"
+
+
+#define pushlevel(lp,c,s) ((lp->lexd.level>=lp->lexd.lex_max?stack_grow(lp):1) &&\
+ ((lp->lexd.lex_match[lp->lexd.level++]=lp->lexd.lastc),\
+ lp->lexd.lastc=(((s)<<CHAR_BIT)|(c))))
+#define oldmode(lp) (lp->lexd.lastc>>CHAR_BIT)
+#define endchar(lp) (lp->lexd.lastc&0xff)
+#define setchar(lp,c) (lp->lexd.lastc = ((lp->lexd.lastc&~0xff)|(c)))
+#define poplevel(lp) (lp->lexd.lastc=lp->lexd.lex_match[--lp->lexd.level])
+
+static char *fmttoken(Lex_t*, int, char*);
+#ifdef SF_BUFCONST
+ static int alias_exceptf(Sfio_t*, int, void*, Sfdisc_t*);
+#else
+ static int alias_exceptf(Sfio_t*, int, Sfdisc_t*);
+#endif
+static void setupalias(Lex_t*,const char*, Namval_t*);
+static int comsub(Lex_t*,int);
+static void nested_here(Lex_t*);
+static int here_copy(Lex_t*, struct ionod*);
+static int stack_grow(Lex_t*);
+static const Sfdisc_t alias_disc = { NULL, NULL, NULL, alias_exceptf, NULL };
+
+#if SHOPT_KIA
+
+static void refvar(Lex_t *lp, int type)
+{
+ register Shell_t *shp = lp->sh;
+ register Stk_t *stkp = shp->stk;
+ off_t off = (fcseek(0)-(type+1))-(lp->lexd.first?lp->lexd.first:fcfirst());
+ unsigned long r;
+ if(lp->lexd.first)
+ {
+ off = (fcseek(0)-(type+1)) - lp->lexd.first;
+ r=kiaentity(lp,lp->lexd.first+lp->lexd.kiaoff+type,off-lp->lexd.kiaoff,'v',-1,-1,lp->current,'v',0,"");
+ }
+ else
+ {
+ int n,offset = stktell(stkp);
+ char *savptr,*begin;
+ off = offset + (fcseek(0)-(type+1)) - fcfirst();
+ if(lp->lexd.kiaoff < offset)
+ {
+ /* variable starts on stak, copy remainder */
+ if(off>offset)
+ sfwrite(stkp,fcfirst()+type,off-offset);
+ n = stktell(stkp)-lp->lexd.kiaoff;
+ begin = stkptr(stkp,lp->lexd.kiaoff);
+ }
+ else
+ {
+ /* variable in data buffer */
+ begin = fcfirst()+(type+lp->lexd.kiaoff-offset);
+ n = off-lp->lexd.kiaoff;
+ }
+ savptr = stkfreeze(stkp,0);
+ r=kiaentity(lp,begin,n,'v',-1,-1,lp->current,'v',0,"");
+ stkset(stkp,savptr,offset);
+ }
+ sfprintf(lp->kiatmp,"p;%..64d;v;%..64d;%d;%d;r;\n",lp->current,r,shp->inlineno,shp->inlineno);
+}
+#endif /* SHOPT_KIA */
+
+/*
+ * This routine gets called when reading across a buffer boundary
+ * If lexd.nocopy is off, then current token is saved on the stack
+ */
+static void lex_advance(Sfio_t *iop, const char *buff, register int size, void *context)
+{
+ register Lex_t *lp = (Lex_t*)context;
+ register Shell_t *shp = lp->sh;
+ register Sfio_t *log= shp->funlog;
+ Stk_t *stkp = shp->stk;
+#if KSHELL
+ /* write to history file and to stderr if necessary */
+ if(iop && !sfstacked(iop))
+ {
+ if(sh_isstate(SH_HISTORY) && shp->gd->hist_ptr)
+ log = shp->gd->hist_ptr->histfp;
+ sfwrite(log, (void*)buff, size);
+ if(sh_isstate(SH_VERBOSE))
+ sfwrite(sfstderr, buff, size);
+ }
+#endif
+ if(lp->lexd.nocopy)
+ return;
+ if(lp->lexd.dolparen && lp->lexd.docword && lp->lexd.docend)
+ {
+ int n = size - (lp->lexd.docend-(char*)buff);
+ sfwrite(shp->strbuf,lp->lexd.docend,n);
+ lp->lexd.docextra += n;
+ if(sffileno(iop)>=0)
+ lp->lexd.docend = sfsetbuf(iop,(Void_t*)iop,0);
+ else
+ lp->lexd.docend = fcfirst();
+ }
+ if(lp->lexd.first)
+ {
+ size -= (lp->lexd.first-(char*)buff);
+ buff = lp->lexd.first;
+ if(!lp->lexd.noarg)
+ lp->arg = (struct argnod*)stkseek(stkp,ARGVAL);
+#if SHOPT_KIA
+ lp->lexd.kiaoff += ARGVAL;
+#endif /* SHOPT_KIA */
+ }
+ if(size>0 && (lp->arg||lp->lexd.noarg))
+ {
+ sfwrite(stkp,buff,size);
+ lp->lexd.first = 0;
+ }
+}
+
+/*
+ * fill up another input buffer
+ * preserves lexical state
+ */
+static int lexfill(Lex_t *lp)
+{
+ register int c;
+ Lex_t savelex;
+ struct argnod *ap;
+ int aok,docextra;
+ savelex = *lp;
+ ap = lp->arg;
+ c = fcfill();
+ if(ap)
+ lp->arg = ap;
+ docextra = lp->lexd.docextra;
+ lp->lex = savelex.lex;
+ lp->lexd = savelex.lexd;
+ if(fcfile() || c)
+ lp->lexd.first = 0;
+ aok= lp->aliasok;
+ ap = lp->arg;
+ memcpy(lp, &savelex, offsetof(Lex_t,lexd));
+ lp->arg = ap;
+ lp->aliasok = aok;
+ if(lp->lexd.docword && docextra)
+ {
+ lp->lexd.docextra = docextra;
+ lp->lexd.docend = fcseek(0)-1;
+ }
+ return(c);
+}
+
+/*
+ * mode=1 for reinitialization
+ */
+Lex_t *sh_lexopen(Lex_t *lp, Shell_t *sp, int mode)
+{
+ if(!lp)
+ {
+ lp = (Lex_t*)newof(0,Lex_t,1,0);
+ lp->sh = sp;
+ }
+ fcnotify(lex_advance,lp);
+ lp->lex.intest = lp->lex.incase = lp->lex.skipword = lp->lexd.warn = 0;
+ lp->comp_assign = 0;
+ lp->lex.reservok = 1;
+ if(!sh_isoption(SH_DICTIONARY) && sh_isoption(SH_NOEXEC))
+ lp->lexd.warn=1;
+ if(!mode)
+ {
+ lp->lexd.noarg = lp->lexd.level= lp->lexd.dolparen = lp->lexd.balance = 0;
+ lp->lexd.nocopy = lp->lexd.docword = lp->lexd.nest = lp->lexd.paren = 0;
+ lp->lexd.lex_state = lp->lexd.lastc=0;
+ lp->lexd.docend = 0;
+ lp->lexd.nested_tilde = 0;
+ }
+ lp->comsub = 0;
+ return(lp);
+}
+
+#ifdef DBUG
+extern int lextoken(Lex_t*);
+int sh_lex(Lex_t *lp)
+{
+ Shell_t *shp = lp->sh;
+ register int flag;
+ char *quoted, *macro, *split, *expand;
+ char tokstr[3];
+ register int tok = lextoken(lp);
+ quoted = macro = split = expand = "";
+ if(tok==0 && (flag=lp->arg->argflag))
+ {
+ if(flag&ARG_MAC)
+ macro = "macro:";
+ if(flag&ARG_EXP)
+ expand = "expand:";
+ if(flag&ARG_QUOTED)
+ quoted = "quoted:";
+ }
+ sfprintf(sfstderr,"%d: line %d: %o:%s%s%s%s %s\n",getpid(),shp->inlineno,tok,quoted,
+ macro, split, expand, fmttoken(lp,tok,tokstr));
+ return(tok);
+}
+#define sh_lex lextoken
+#endif
+
+/*
+ * Get the next word and put it on the top of the stak
+ * A pointer to the current word is stored in lp->arg
+ * Returns the token type
+ */
+int sh_lex(Lex_t* lp)
+{
+ register Shell_t *shp = lp->sh;
+ register const char *state;
+ register int n, c, mode=ST_BEGIN, wordflags=0;
+ Stk_t *stkp = shp->stk;
+ int inlevel=lp->lexd.level, assignment=0, ingrave=0;
+ Sfio_t *sp;
+#if SHOPT_MULTIBYTE
+ LEN=1;
+#endif /* SHOPT_MULTIBYTE */
+ if(lp->lexd.paren)
+ {
+ lp->lexd.paren = 0;
+ return(lp->token=LPAREN);
+ }
+ if(lp->lex.incase)
+ lp->assignok = 0;
+ else
+ lp->assignok |= lp->lex.reservok;
+ if(lp->comp_assign==2)
+ lp->comp_assign = lp->lex.reservok = 0;
+ lp->lexd.arith = (lp->lexd.nest==1);
+ if(lp->lexd.nest)
+ {
+ pushlevel(lp,lp->lexd.nest,ST_NONE);
+ lp->lexd.nest = 0;
+ mode = lp->lexd.lex_state;
+ }
+ else if(lp->lexd.docword)
+ {
+ if(fcgetc(c)=='-' || c=='#')
+ {
+ lp->lexd.docword++;
+ lp->digits=(c=='#'?3:1);
+ }
+ else if(c=='<')
+ {
+ lp->digits=2;
+ lp->lexd.docword=0;
+ }
+ else if(c>0)
+ fcseek(-LEN);
+ }
+ if(!lp->lexd.dolparen)
+ {
+ lp->arg = 0;
+ if(mode!=ST_BEGIN)
+ lp->lexd.first = fcseek(0);
+ else
+ lp->lexd.first = 0;
+ }
+ lp->lastline = lp->sh->inlineno;
+ if(lp->noreserv)
+ lp->lex.reservok = 0;
+ while(1)
+ {
+ /* skip over characters in the current state */
+ state = sh_lexstates[mode];
+ while((n=STATE(state,c))==0);
+ switch(n)
+ {
+ case S_BREAK:
+ fcseek(-LEN);
+ goto breakloop;
+ case S_EOF:
+ sp = fcfile();
+ if((n=lexfill(lp)) > 0)
+ {
+ fcseek(-1);
+ continue;
+ }
+ /* check for zero byte in file */
+ if(n==0 && fcfile())
+ {
+ if(shp->readscript)
+ {
+ char *cp = error_info.id;
+ errno = ENOEXEC;
+ error_info.id = shp->readscript;
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,cp);
+ }
+ else
+ {
+ lp->token = -1;
+ sh_syntax(lp);
+ }
+ }
+ /* end-of-file */
+ if(mode==ST_BEGIN)
+ return(lp->token=EOFSYM);
+ if(mode >ST_NORM && lp->lexd.level>0)
+ {
+ switch(c=endchar(lp))
+ {
+ case '$':
+ if(mode==ST_LIT)
+ {
+ c = '\'';
+ break;
+ }
+ mode = oldmode(lp);
+ poplevel(lp);
+ continue;
+ case RBRACT:
+ c = LBRACT;
+ break;
+ case 1: /* for ((...)) */
+ case RPAREN:
+ c = LPAREN;
+ break;
+ default:
+ c = LBRACE;
+ break;
+ case '"': case '`': case '\'':
+ lp->lexd.balance = c;
+ break;
+ }
+ if(sp && !(sfset(sp,0,0)&SF_STRING))
+ {
+ lp->lasttok = c;
+ lp->token = EOFSYM;
+ sh_syntax(lp);
+ }
+ lp->lexd.balance = c;
+ }
+ goto breakloop;
+ case S_COM:
+ /* skip one or more comment line(s) */
+ lp->lex.reservok = !lp->lex.intest;
+ if((n=lp->lexd.nocopy) && lp->lexd.dolparen)
+ lp->lexd.nocopy--;
+ do
+ {
+ while(fcgetc(c)>0 && c!='\n');
+ if(c<=0 || lp->heredoc)
+ {
+ shp->inlineno++;
+ break;
+ }
+ while(shp->inlineno++,fcpeek(0)=='\n')
+ fcseek(1);
+ while(state[c=fcpeek(0)]==0)
+ fcseek(1);
+ }
+ while(c=='#');
+ lp->lexd.nocopy = n;
+ if(c<0)
+ return(lp->token=EOFSYM);
+ n = S_NLTOK;
+ shp->inlineno--;
+ /* FALL THRU */
+ case S_NLTOK:
+ /* check for here-document */
+ if(lp->heredoc)
+ {
+ if(!lp->lexd.dolparen)
+ lp->lexd.nocopy++;
+ c = shp->inlineno;
+ if(here_copy(lp,lp->heredoc)<=0 && lp->lasttok)
+ {
+ lp->lasttok = IODOCSYM;
+ lp->token = EOFSYM;
+ lp->lastline = c;
+ sh_syntax(lp);
+ }
+ if(!lp->lexd.dolparen)
+ lp->lexd.nocopy--;
+ lp->heredoc = 0;
+ }
+ lp->lex.reservok = !lp->lex.intest;
+ lp->lex.skipword = 0;
+ /* FALL THRU */
+ case S_NL:
+ /* skip over new-lines */
+ lp->lex.last_quote = 0;
+ while(shp->inlineno++,fcget()=='\n');
+ fcseek(-LEN);
+ if(n==S_NLTOK)
+ {
+ lp->comp_assign = 0;
+ return(lp->token='\n');
+ }
+ case S_BLNK:
+ if(lp->lex.incase<=TEST_RE)
+ continue;
+ /* implicit RPAREN for =~ test operator */
+ if(inlevel+1==lp->lexd.level)
+ {
+ if(lp->lex.intest)
+ fcseek(-LEN);
+ c = RPAREN;
+ goto do_pop;
+ }
+ continue;
+ case S_OP:
+ /* return operator token */
+ if(c=='<' || c=='>')
+ {
+ if(lp->lex.testop2)
+ lp->lex.testop2 = 0;
+ else
+ {
+ lp->digits = (c=='>');
+ lp->lex.skipword = 1;
+ lp->aliasok = lp->lex.reservok;
+ lp->lex.reservok = 0;
+ }
+ }
+ else
+ {
+ lp->lex.reservok = !lp->lex.intest;
+ if(c==RPAREN)
+ {
+ if(!lp->lexd.dolparen)
+ lp->lex.incase = 0;
+ return(lp->token=c);
+ }
+ lp->lex.testop1 = lp->lex.intest;
+ }
+ if(fcgetc(n)>0)
+ fcseek(-LEN);
+ if(state[n]==S_OP || n=='#')
+ {
+ if(n==c)
+ {
+ if(c=='<')
+ lp->lexd.docword=1;
+ else if(n==LPAREN)
+ {
+ if(lp->lex.intest)
+ return(c);
+ lp->lexd.nest=1;
+ lp->lastline = shp->inlineno;
+ lp->lexd.lex_state = ST_NESTED;
+ fcseek(1);
+ return(sh_lex(lp));
+ }
+ c |= SYMREP;
+ }
+ else if(c=='(' || c==')')
+ return(lp->token=c);
+ else if(c=='&')
+ {
+ if(!sh_isoption(SH_POSIX) && n=='>' && (sh_isoption(SH_BASH) || sh_isstate(SH_PROFILE)))
+ {
+ if(!sh_isoption(SH_BASH) && !lp->nonstandard)
+ {
+ lp->nonstandard = 1;
+ errormsg(SH_DICT,ERROR_warn(0),e_lexnonstandard,shp->inlineno);
+ }
+ lp->digits = -1;
+ c = '>';
+ }
+ else if(n=='|')
+ c |= SYMPIPE;
+ else
+ n = 0;
+ }
+ else if(n=='&')
+ c |= SYMAMP;
+ else if(c!='<' && c!='>')
+ n = 0;
+ else if(n==LPAREN)
+ {
+ c |= SYMLPAR;
+ lp->lex.reservok = 1;
+ lp->lex.skipword = 0;
+ }
+ else if(n=='|')
+ c |= SYMPIPE;
+ else if(c=='<' && n=='>')
+ {
+ lp->digits = 1;
+ c = IORDWRSYM;
+ fcgetc(n);
+ if(fcgetc(n)==';')
+ {
+ lp->token = c = IORDWRSYMT;
+ if(lp->inexec)
+ sh_syntax(lp);
+ }
+ else if(n>0)
+ fcseek(-LEN);
+ n= 0;
+ }
+ else if(n=='#' && (c=='<'||c=='>'))
+ c |= SYMSHARP;
+ else if(n==';' && c=='>')
+ {
+ c |= SYMSEMI;
+ if(lp->inexec)
+ {
+ lp->token = c;
+ sh_syntax(lp);
+ }
+ }
+ else
+ n = 0;
+ if(n)
+ {
+ fcseek(1);
+ lp->lex.incase = (c==BREAKCASESYM || c==FALLTHRUSYM);
+ }
+ else
+ {
+ if(lp->lexd.warn && (n=fcpeek(0))!=RPAREN && n!=' ' && n!='\t')
+ errormsg(SH_DICT,ERROR_warn(0),e_lexspace,shp->inlineno,c,n);
+ }
+ }
+ if(c==LPAREN && lp->comp_assign && !lp->lex.intest && !lp->lex.incase)
+ lp->comp_assign = 2;
+ else
+ lp->comp_assign = 0;
+ return(lp->token=c);
+ case S_ESC:
+ /* check for \<new-line> */
+ fcgetc(n);
+ c=2;
+#if SHOPT_CRNL
+ if(n=='\r')
+ {
+ if(fcgetc(n)=='\n')
+ c=3;
+ else
+ {
+ n='\r';
+ fcseek(-LEN);
+ }
+ }
+#endif /* SHOPT_CRNL */
+ if(n=='\n')
+ {
+ Sfio_t *sp;
+ struct argnod *ap;
+ shp->inlineno++;
+ /* synchronize */
+ if(!(sp=fcfile()))
+ state=fcseek(0);
+ fcclose();
+ ap = lp->arg;
+ if(sp)
+ fcfopen(sp);
+ else
+ fcsopen((char*)state);
+ /* remove \new-line */
+ n = stktell(stkp)-c;
+ stkseek(stkp,n);
+ lp->arg = ap;
+ if(n<=ARGVAL)
+ {
+ mode = 0;
+ lp->lexd.first = 0;
+ }
+ continue;
+ }
+ wordflags |= ARG_QUOTED;
+ if(mode==ST_DOL)
+ goto err;
+#ifndef STR_MAXIMAL
+ else if(mode==ST_NESTED && lp->lexd.warn &&
+ endchar(lp)==RBRACE &&
+ sh_lexstates[ST_DOL][n]==S_DIG
+ )
+ errormsg(SH_DICT,ERROR_warn(0),e_lexfuture,shp->inlineno,n);
+#endif /* STR_MAXIMAL */
+ break;
+ case S_NAME:
+ if(!lp->lex.skipword)
+ lp->lex.reservok *= 2;
+ /* FALL THRU */
+ case S_TILDE:
+ if(c=='~' && mode==ST_NESTED)
+ {
+ if(endchar(lp)==RBRACE)
+ {
+ lp->lexd.nested_tilde++;
+ goto tilde;
+ }
+ continue;
+ }
+ case S_RES:
+ if(!lp->lexd.dolparen)
+ lp->lexd.first = fcseek(0)-LEN;
+ else if(lp->lexd.docword)
+ lp->lexd.docend = fcseek(0)-LEN;
+ mode = ST_NAME;
+ if(c=='.')
+ fcseek(-LEN);
+ if(n!=S_TILDE)
+ continue;
+ tilde:
+ fcgetc(n);
+ if(n>0)
+ {
+ if(c=='~' && n==LPAREN)
+ {
+ if(lp->lexd.nested_tilde)
+ lp->lexd.nested_tilde++;
+ else if(lp->lex.incase)
+ lp->lex.incase = TEST_RE;
+ }
+ fcseek(-LEN);
+ if(lp->lexd.nested_tilde)
+ {
+ lp->lexd.nested_tilde--;
+ continue;
+ }
+ }
+ if(n==LPAREN)
+ goto epat;
+ wordflags = ARG_MAC;
+ mode = ST_NORM;
+ continue;
+ case S_REG:
+ if(mode==ST_BEGIN)
+ {
+ do_reg:
+ /* skip new-line joining */
+ if(c=='\\' && fcpeek(0)=='\n')
+ {
+ shp->inlineno++;
+ fcseek(1);
+ continue;
+ }
+ fcseek(-LEN);
+ if(!lp->lexd.dolparen)
+ lp->lexd.first = fcseek(0);
+ else if(lp->lexd.docword)
+ lp->lexd.docend = fcseek(0);
+ if(c=='[' && lp->assignok>=SH_ASSIGN)
+ {
+ mode = ST_NAME;
+ continue;
+ }
+ }
+ mode = ST_NORM;
+ continue;
+ case S_LIT:
+ if(oldmode(lp)==ST_NONE && !lp->lexd.noarg) /* in ((...)) */
+ {
+ if((c=fcpeek(0))==LPAREN || c==RPAREN || c=='$' || c==LBRACE || c==RBRACE || c=='[' || c==']')
+ {
+ if(fcpeek(1)=='\'')
+ fcseek(2);
+ }
+ continue;
+ }
+ wordflags |= ARG_QUOTED;
+ if(mode==ST_DOL)
+ {
+ if(endchar(lp)!='$')
+ goto err;
+ if(oldmode(lp)==ST_QUOTE) /* $' within "" or `` */
+ {
+ if(lp->lexd.warn)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexslash,shp->inlineno);
+ mode = ST_LIT;
+ }
+ }
+ if(mode!=ST_LIT)
+ {
+ if(lp->lexd.warn && lp->lex.last_quote && shp->inlineno > lp->lastline && fcpeek(-2)!='$')
+ errormsg(SH_DICT,ERROR_warn(0),e_lexlongquote,lp->lastline,lp->lex.last_quote);
+ lp->lex.last_quote = 0;
+ lp->lastline = shp->inlineno;
+ if(mode!=ST_DOL)
+ pushlevel(lp,'\'',mode);
+ mode = ST_LIT;
+ continue;
+ }
+ /* check for multi-line single-quoted string */
+ else if(shp->inlineno > lp->lastline)
+ lp->lex.last_quote = '\'';
+ mode = oldmode(lp);
+ poplevel(lp);
+ break;
+ case S_ESC2:
+ /* \ inside '' */
+ if(endchar(lp)=='$')
+ {
+ fcgetc(n);
+ if(n=='\n')
+ shp->inlineno++;
+ }
+ continue;
+ case S_GRAVE:
+ if(lp->lexd.warn && (mode!=ST_QUOTE || endchar(lp)!='`'))
+ errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete1,shp->inlineno);
+ wordflags |=(ARG_MAC|ARG_EXP);
+ if(mode==ST_QUOTE)
+ ingrave = !ingrave;
+ /* FALL THRU */
+ case S_QUOTE:
+ if(oldmode(lp)==ST_NONE && lp->lexd.arith) /* in ((...)) */
+ {
+ if(n!=S_GRAVE || fcpeek(0)=='\'')
+ continue;
+ }
+ if(n==S_QUOTE)
+ wordflags |=ARG_QUOTED;
+ if(mode!=ST_QUOTE)
+ {
+ if(c!='"' || mode!=ST_QNEST)
+ {
+ if(lp->lexd.warn && lp->lex.last_quote && shp->inlineno > lp->lastline)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexlongquote,lp->lastline,lp->lex.last_quote);
+ lp->lex.last_quote=0;
+ lp->lastline = shp->inlineno;
+ pushlevel(lp,c,mode);
+ }
+ ingrave ^= (c=='`');
+ mode = ST_QUOTE;
+ continue;
+ }
+ else if((n=endchar(lp))==c)
+ {
+ if(shp->inlineno > lp->lastline)
+ lp->lex.last_quote = c;
+ mode = oldmode(lp);
+ poplevel(lp);
+ }
+ else if(c=='"' && n==RBRACE)
+ mode = ST_QNEST;
+ break;
+ case S_DOL:
+ /* don't check syntax inside `` */
+ if(mode==ST_QUOTE && ingrave)
+ continue;
+#if SHOPT_KIA
+ if(lp->lexd.first)
+ lp->lexd.kiaoff = fcseek(0)-lp->lexd.first;
+ else
+ lp->lexd.kiaoff = stktell(stkp)+fcseek(0)-fcfirst();
+#endif /* SHOPT_KIA */
+ pushlevel(lp,'$',mode);
+ mode = ST_DOL;
+ continue;
+ case S_PAR:
+ do_comsub:
+ wordflags |= ARG_MAC;
+ mode = oldmode(lp);
+ poplevel(lp);
+ fcseek(-LEN);
+ wordflags |= comsub(lp,c);
+ continue;
+ case S_RBRA:
+ if((n=endchar(lp)) == '$')
+ goto err;
+ if(mode!=ST_QUOTE || n==RBRACE)
+ {
+ mode = oldmode(lp);
+ poplevel(lp);
+ }
+ break;
+ case S_EDOL:
+ /* end $identifier */
+#if SHOPT_KIA
+ if(lp->kiafile)
+ refvar(lp,0);
+#endif /* SHOPT_KIA */
+ if(lp->lexd.warn && c==LBRACT && !lp->lex.intest && !lp->lexd.arith && oldmode(lp)!= ST_NESTED)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexusebrace,shp->inlineno);
+ fcseek(-LEN);
+ mode = oldmode(lp);
+ poplevel(lp);
+ break;
+ case S_DOT:
+ /* make sure next character is alpha */
+ if(fcgetc(n)>0)
+ {
+ if(n=='.')
+ fcgetc(n);
+ if(n>0)
+ fcseek(-LEN);
+ }
+ if(isaletter(n) || n==LBRACT)
+ continue;
+ if(mode==ST_NAME)
+ {
+ if(n=='=')
+ continue;
+ break;
+ }
+ else if(n==RBRACE)
+ continue;
+ if(isastchar(n))
+ continue;
+ goto err;
+ case S_SPC1:
+ wordflags |= ARG_MAC;
+ if(endchar(lp)==RBRACE)
+ {
+ setchar(lp,c);
+ continue;
+ }
+ /* FALL THRU */
+ case S_ALP:
+ if(c=='.' && endchar(lp)=='$')
+ goto err;
+ case S_SPC2:
+ case S_DIG:
+ wordflags |= ARG_MAC;
+ switch(endchar(lp))
+ {
+ case '$':
+ if(n==S_ALP) /* $identifier */
+ mode = ST_DOLNAME;
+ else
+ {
+ mode = oldmode(lp);
+ poplevel(lp);
+ }
+ break;
+#if SHOPT_TYPEDEF
+ case '@':
+#endif /* SHOPT_TYPEDEF */
+ case '!':
+ if(n!=S_ALP)
+ goto dolerr;
+ case '#':
+ if(c=='#')
+ n = S_ALP;
+ case RBRACE:
+ if(n==S_ALP)
+ {
+ setchar(lp,RBRACE);
+ if(c=='.')
+ fcseek(-LEN);
+ mode = ST_BRACE;
+ }
+ else
+ {
+ if(fcgetc(c)>0)
+ fcseek(-LEN);
+ if(state[c]==S_ALP)
+ goto err;
+ if(n==S_DIG)
+ setchar(lp,'0');
+ else
+ setchar(lp,'!');
+ }
+ break;
+ case '0':
+ if(n==S_DIG)
+ break;
+ default:
+ goto dolerr;
+ }
+ break;
+ dolerr:
+ case S_ERR:
+ if((n=endchar(lp)) == '$')
+ goto err;
+ if(c=='*' || (n=sh_lexstates[ST_BRACE][c])!=S_MOD1 && n!=S_MOD2)
+ {
+ /* see whether inside `...` */
+ mode = oldmode(lp);
+ poplevel(lp);
+ if((n = endchar(lp)) != '`')
+ goto err;
+ pushlevel(lp,RBRACE,mode);
+ }
+ else
+ setchar(lp,RBRACE);
+ mode = ST_NESTED;
+ continue;
+ case S_MOD1:
+ if(oldmode(lp)==ST_QUOTE || oldmode(lp)==ST_NONE)
+ {
+ /* allow ' inside "${...}" */
+ if(c==':' && fcgetc(n)>0)
+ {
+ n = state[n];
+ fcseek(-LEN);
+ }
+ if(n==S_MOD1)
+ {
+ mode = ST_QUOTE;
+ continue;
+ }
+ }
+ /* FALL THRU */
+ case S_MOD2:
+#if SHOPT_KIA
+ if(lp->kiafile)
+ refvar(lp,1);
+#endif /* SHOPT_KIA */
+ if(c!=':' && fcgetc(n)>0)
+ {
+ if(n!=c)
+ c = 0;
+ if(!c || (fcgetc(n)>0))
+ {
+ fcseek(-LEN);
+ if(n==LPAREN)
+ {
+ if(c!='%')
+ {
+ lp->token = n;
+ sh_syntax(lp);
+ }
+ else if(lp->lexd.warn)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexquote,shp->inlineno,'%');
+ }
+ }
+ }
+ lp->lex.nestedbrace = 0;
+ mode = ST_NESTED;
+ continue;
+ case S_LBRA:
+ if((c=endchar(lp)) == '$')
+ {
+ if(fcgetc(c)>0)
+ fcseek(-LEN);
+ setchar(lp,RBRACE);
+ if(state[c]!=S_ERR && c!=RBRACE)
+ continue;
+ if((n=sh_lexstates[ST_BEGIN][c])==0 || n==S_OP || n==S_NLTOK)
+ {
+ c = LBRACE;
+ goto do_comsub;
+ }
+ }
+ err:
+ if(iswalpha(c))
+ continue;
+ n = endchar(lp);
+ mode = oldmode(lp);
+ poplevel(lp);
+ if(n!='$')
+ {
+ lp->token = c;
+ sh_syntax(lp);
+ }
+ else
+ {
+ if(lp->lexd.warn && c!='/' && sh_lexstates[ST_NORM][c]!=S_BREAK && (c!='"' || mode==ST_QUOTE))
+ errormsg(SH_DICT,ERROR_warn(0),e_lexslash,shp->inlineno);
+ else if(c=='"' && mode!=ST_QUOTE && !ingrave)
+ wordflags |= ARG_MESSAGE;
+ fcseek(-LEN);
+ }
+ continue;
+ case S_META:
+ if(lp->lexd.warn && endchar(lp)==RBRACE && !lp->lexd.nested_tilde)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexusequote,shp->inlineno,c);
+ continue;
+ case S_PUSH:
+ pushlevel(lp,RPAREN,mode);
+ mode = ST_NESTED;
+ continue;
+ case S_POP:
+ do_pop:
+ if(c==RBRACE && mode==ST_NESTED && lp->lex.nestedbrace)
+ {
+ lp->lex.nestedbrace--;
+ continue;
+ }
+ if(lp->lexd.level <= inlevel)
+ break;
+ if(lp->lexd.level==inlevel+1 && lp->lex.incase>=TEST_RE && !lp->lex.intest)
+ {
+ fcseek(-LEN);
+ goto breakloop;
+ }
+ n = endchar(lp);
+ if(c==RBRACT && !(n==RBRACT || n==RPAREN))
+ continue;
+ if((c==RBRACE||c==RPAREN) && n==RPAREN)
+ {
+ if(fcgetc(n)==LPAREN)
+ {
+ if(c!=RPAREN)
+ fcseek(-LEN);
+ continue;
+ }
+ if(n>0)
+ fcseek(-LEN);
+ n = RPAREN;
+ }
+ if(c==RBRACE)
+ lp->lexd.nested_tilde = 0;
+ if(c==';' && n!=';')
+ {
+ if(lp->lexd.warn && n==RBRACE)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexusequote,shp->inlineno,c);
+ continue;
+ }
+ if(mode==ST_QNEST)
+ {
+ if(lp->lexd.warn)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexescape,shp->inlineno,c);
+ continue;
+ }
+ mode = oldmode(lp);
+ poplevel(lp);
+ /* quotes in subscript need expansion */
+ if(mode==ST_NAME && (wordflags&ARG_QUOTED))
+ wordflags |= ARG_MAC;
+ /* check for ((...)) */
+ if(n==1 && c==RPAREN)
+ {
+ if(fcgetc(n)==RPAREN)
+ {
+ if(mode==ST_NONE && !lp->lexd.dolparen)
+ goto breakloop;
+ lp->lex.reservok = 1;
+ lp->lex.skipword = 0;
+ return(lp->token=EXPRSYM);
+ }
+ /* backward compatibility */
+ {
+ if(lp->lexd.warn)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexnested,shp->inlineno);
+ if(!(state=lp->lexd.first))
+ state = fcfirst();
+ else
+ {
+ n = state-fcseek(0);
+ fcseek(n);
+ }
+ lp->lexd.paren = 1;
+ }
+ return(lp->token=LPAREN);
+ }
+ if(mode==ST_NONE)
+ return(0);
+ if(c!=n)
+ {
+ lp->token = c;
+ sh_syntax(lp);
+ }
+ if(c==RBRACE && (mode==ST_NAME||mode==ST_NORM))
+ goto epat;
+ continue;
+ case S_EQ:
+ assignment = lp->assignok;
+ /* FALL THRU */
+ case S_COLON:
+ if(assignment)
+ {
+ if(fcgetc(c)=='~')
+ wordflags |= ARG_MAC;
+ else if(c!=LPAREN && assignment==SH_COMPASSIGN)
+ assignment = 0;
+ if(c!=EOF)
+ fcseek(-LEN);
+ }
+ break;
+ case S_LABEL:
+ if(lp->lex.reservok && !lp->lex.incase)
+ {
+ c = fcget();
+ fcseek(-LEN);
+ if(state[c]==S_BREAK)
+ {
+ assignment = -1;
+ goto breakloop;
+ }
+ }
+ break;
+ case S_BRACT:
+ /* check for possible subscript */
+ if((n=endchar(lp))==RBRACT || n==RPAREN ||
+ (mode==ST_BRACE) ||
+ (oldmode(lp)==ST_NONE) ||
+ (mode==ST_NAME && (lp->assignok||lp->lexd.level)))
+ {
+ if(mode==ST_NAME)
+ {
+ fcgetc(n);
+ if(n>0)
+ {
+ if(n==']')
+ errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax1, shp->inlineno, "[]", "empty subscript");
+ fcseek(-LEN);
+ }
+ }
+ pushlevel(lp,RBRACT,mode);
+ wordflags |= ARG_QUOTED;
+ mode = ST_NESTED;
+ continue;
+ }
+ wordflags |= ARG_EXP;
+ break;
+ case S_BRACE:
+ {
+ int isfirst;
+ if(lp->lexd.dolparen)
+ {
+ if(mode==ST_BEGIN && (lp->lex.reservok||lp->comsub))
+ {
+ if(lp->comsub)
+ return(lp->token=c);
+ fcgetc(n);
+ if(n>0)
+ fcseek(-LEN);
+ else
+ n = '\n';
+ if(n==RBRACT || sh_lexstates[ST_NORM][n])
+ return(lp->token=c);
+ }
+ break;
+ }
+ else if(mode==ST_NESTED && endchar(lp)==RBRACE)
+ {
+ lp->lex.nestedbrace++;
+ continue;
+ }
+ else if(mode==ST_BEGIN)
+ {
+ if(lp->comsub && c==RBRACE)
+ return(lp->token=c);
+ goto do_reg;
+ }
+ isfirst = (lp->lexd.first&&fcseek(0)==lp->lexd.first+1);
+ fcgetc(n);
+ /* check for {} */
+ if(c==LBRACE && n==RBRACE)
+ break;
+ if(n>0)
+ fcseek(-LEN);
+ else if(lp->lex.reservok)
+ break;
+ /* check for reserved word { or } */
+ if(lp->lex.reservok && state[n]==S_BREAK && isfirst)
+ break;
+ if(sh_isoption(SH_BRACEEXPAND) && c==LBRACE && !assignment && state[n]!=S_BREAK
+ && !lp->lex.incase && !lp->lex.intest
+ && !lp->lex.skipword)
+ {
+ wordflags |= ARG_EXP;
+ }
+ if(c==RBRACE && n==LPAREN)
+ goto epat;
+ break;
+ }
+ case S_PAT:
+ wordflags |= ARG_EXP;
+ /* FALL THRU */
+ case S_EPAT:
+ epat:
+ if(fcgetc(n)==LPAREN && c!='[')
+ {
+ if(lp->lex.incase==TEST_RE)
+ {
+ lp->lex.incase++;
+ pushlevel(lp,RPAREN,ST_NORM);
+ mode = ST_NESTED;
+ }
+ wordflags |= ARG_EXP;
+ pushlevel(lp,RPAREN,mode);
+ mode = ST_NESTED;
+ continue;
+ }
+ if(lp->lexd.warn && c=='[' && n=='^')
+ errormsg(SH_DICT,ERROR_warn(0),e_lexcharclass,shp->inlineno);
+ if(n>0)
+ fcseek(-LEN);
+ if(n=='=' && c=='+' && mode==ST_NAME)
+ continue;
+ break;
+ }
+ lp->comp_assign = 0;
+ if(mode==ST_NAME)
+ mode = ST_NORM;
+ else if(mode==ST_NONE)
+ return(0);
+ }
+breakloop:
+ if(lp->lexd.nocopy)
+ {
+ lp->lexd.balance = 0;
+ return(0);
+ }
+ if(lp->lexd.dolparen)
+ {
+ lp->lexd.balance = 0;
+ if(lp->lexd.docword)
+ nested_here(lp);
+ lp->lexd.message = (wordflags&ARG_MESSAGE);
+ return(lp->token=0);
+ }
+ if(!(state=lp->lexd.first))
+ state = fcfirst();
+ n = fcseek(0)-(char*)state;
+ if(!lp->arg)
+ lp->arg = (struct argnod*)stkseek(stkp,ARGVAL);
+ if(n>0)
+ sfwrite(stkp,state,n);
+ /* add balancing character if necessary */
+ if(lp->lexd.balance)
+ {
+ sfputc(stkp,lp->lexd.balance);
+ lp->lexd.balance = 0;
+ }
+ sfputc(stkp,0);
+ stkseek(stkp,stktell(stkp)-1);
+ state = stkptr(stkp,ARGVAL);
+ n = stktell(stkp)-ARGVAL;
+ lp->lexd.first=0;
+ if(n==1)
+ {
+ /* check for numbered redirection */
+ n = state[0];
+ if((c=='<' || c=='>') && isadigit(n))
+ {
+ c = sh_lex(lp);
+ lp->digits = (n-'0');
+ return(c);
+ }
+ if(n==LBRACT)
+ c = 0;
+ else if(n==RBRACE && lp->comsub)
+ return(lp->token=n);
+ else if(n=='~')
+ c = ARG_MAC;
+ else
+ c = (wordflags&ARG_EXP);
+ n = 1;
+ }
+ else if(n>2 && state[0]=='{' && state[n-1]=='}' && !lp->lex.intest && !lp->lex.incase && (c=='<' || c== '>') && sh_isoption(SH_BRACEEXPAND))
+ {
+ if(!strchr(state,','))
+ {
+ stkseek(stkp,stktell(stkp)-1);
+ lp->arg = (struct argnod*)stkfreeze(stkp,1);
+ return(lp->token=IOVNAME);
+ }
+ c = wordflags;
+ }
+ else
+ c = wordflags;
+ if(assignment<0)
+ {
+ stkseek(stkp,stktell(stkp)-1);
+ lp->arg = (struct argnod*)stkfreeze(stkp,1);
+ lp->lex.reservok = 1;
+ return(lp->token=LABLSYM);
+ }
+ if(assignment || (lp->lex.intest&&!lp->lex.incase) || mode==ST_NONE)
+ c &= ~ARG_EXP;
+ if((c&ARG_EXP) && (c&ARG_QUOTED))
+ c |= ARG_MAC;
+ if(mode==ST_NONE)
+ {
+ /* eliminate trailing )) */
+ stkseek(stkp,stktell(stkp)-2);
+ }
+ if(c&ARG_MESSAGE)
+ {
+ if(sh_isoption(SH_DICTIONARY))
+ lp->arg = sh_endword(shp,2);
+ c |= ARG_MAC;
+ }
+ if(c==0 || (c&(ARG_MAC|ARG_EXP|ARG_MESSAGE)))
+ {
+ lp->arg = (struct argnod*)stkfreeze(stkp,1);
+ lp->arg->argflag = (c?c:ARG_RAW);
+ }
+ else if(mode==ST_NONE)
+ lp->arg = sh_endword(shp,-1);
+ else
+ lp->arg = sh_endword(shp,0);
+ state = lp->arg->argval;
+ lp->comp_assign = assignment;
+ if(assignment)
+ lp->arg->argflag |= ARG_ASSIGN;
+ else if(!lp->lex.skipword)
+ lp->assignok = 0;
+ lp->arg->argchn.cp = 0;
+ lp->arg->argnxt.ap = 0;
+ if(mode==ST_NONE)
+ return(lp->token=EXPRSYM);
+ if(lp->lex.intest)
+ {
+ if(lp->lex.testop1)
+ {
+ lp->lex.testop1 = 0;
+ if(n==2 && state[0]=='-' && state[2]==0 &&
+ strchr(test_opchars,state[1]))
+ {
+ if(lp->lexd.warn && state[1]=='a')
+ errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete2,shp->inlineno);
+ lp->digits = state[1];
+ lp->token = TESTUNOP;
+ }
+ else if(n==1 && state[0]=='!' && state[1]==0)
+ {
+ lp->lex.testop1 = 1;
+ lp->token = '!';
+ }
+ else
+ {
+ lp->lex.testop2 = 1;
+ lp->token = 0;
+ }
+ return(lp->token);
+ }
+ lp->lex.incase = 0;
+ c = sh_lookup(state,shtab_testops);
+ switch(c)
+ {
+ case TEST_END:
+ lp->lex.testop2 = lp->lex.intest = 0;
+ lp->lex.reservok = 1;
+ lp->token = ETESTSYM;
+ return(lp->token);
+
+ case TEST_SEQ:
+ if(lp->lexd.warn && state[1]==0)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete3,shp->inlineno);
+ /* FALL THRU */
+ default:
+ if(lp->lex.testop2)
+ {
+ if(lp->lexd.warn && (c&TEST_ARITH))
+ errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete4,shp->inlineno,state);
+ if(c&TEST_PATTERN)
+ lp->lex.incase = 1;
+ else if(c==TEST_REP)
+ lp->lex.incase = TEST_RE;
+ lp->lex.testop2 = 0;
+ lp->digits = c;
+ lp->token = TESTBINOP;
+ return(lp->token);
+ }
+
+ case TEST_OR: case TEST_AND:
+ case 0:
+ return(lp->token=0);
+ }
+ }
+ if(lp->lex.reservok /* && !lp->lex.incase*/ && n<=2)
+ {
+ /* check for {, }, ! */
+ c = state[0];
+ if(n==1 && (c=='{' || c=='}' || c=='!'))
+ {
+ if(lp->lexd.warn && c=='{' && lp->lex.incase==2)
+ errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete6,shp->inlineno);
+ if(lp->lex.incase==1 && c==RBRACE)
+ lp->lex.incase = 0;
+ return(lp->token=c);
+ }
+ else if(!lp->lex.incase && c==LBRACT && state[1]==LBRACT)
+ {
+ lp->lex.intest = lp->lex.testop1 = 1;
+ lp->lex.testop2 = lp->lex.reservok = 0;
+ return(lp->token=BTESTSYM);
+ }
+ }
+ c = 0;
+ if(!lp->lex.skipword)
+ {
+ if(n>1 && lp->lex.reservok==1 && mode==ST_NAME &&
+ (c=sh_lookup(state,shtab_reserved)))
+ {
+ if(lp->lex.incase)
+ {
+ if(lp->lex.incase >1)
+ lp->lex.incase = 1;
+ else if(c==ESACSYM)
+ lp->lex.incase = 0;
+ else
+ c = 0;
+ }
+ else if(c==FORSYM || c==CASESYM || c==SELECTSYM || c==FUNCTSYM || c==NSPACESYM)
+ {
+ lp->lex.skipword = 1;
+ lp->lex.incase = 2*(c==CASESYM);
+ }
+ else
+ lp->lex.skipword = 0;
+ if(c==INSYM)
+ lp->lex.reservok = 0;
+ else if(c==TIMESYM)
+ {
+ /* yech - POSIX requires time -p */
+ while(fcgetc(n)==' ' || n=='\t');
+ if(n>0)
+ fcseek(-LEN);
+ if(n=='-')
+ c=0;
+ }
+ return(lp->token=c);
+ }
+ if(!(wordflags&ARG_QUOTED) && (lp->lex.reservok||lp->aliasok))
+ {
+ /* check for aliases */
+ Namval_t* np;
+ if(!lp->lex.incase && !assignment && fcpeek(0)!=LPAREN &&
+ (np=nv_search(state,shp->alias_tree,HASH_SCOPE))
+ && !nv_isattr(np,NV_NOEXPAND)
+#if KSHELL
+ && (!sh_isstate(SH_NOALIAS) || nv_isattr(np,NV_NOFREE))
+#endif /* KSHELL */
+ && (state=nv_getval(np)))
+ {
+ setupalias(lp,state,np);
+ nv_onattr(np,NV_NOEXPAND);
+ lp->lex.reservok = 1;
+ lp->assignok |= lp->lex.reservok;
+ return(sh_lex(lp));
+ }
+ }
+ lp->lex.reservok = 0;
+ }
+ lp->lex.skipword = lp->lexd.docword = 0;
+ return(lp->token=c);
+}
+
+/*
+ * read to end of command substitution
+ */
+static int comsub(register Lex_t *lp, int endtok)
+{
+ register int n,c,count=1;
+ register int line=lp->sh->inlineno;
+ char *first,*cp=fcseek(0),word[5];
+ int off, messages=0, assignok=lp->assignok, csub;
+ struct lexstate save;
+ save = lp->lex;
+ csub = lp->comsub;
+ sh_lexopen(lp,lp->sh,1);
+ lp->lexd.dolparen++;
+ lp->lex.incase=0;
+ pushlevel(lp,0,0);
+ lp->comsub = (endtok==LBRACE);
+ if(first=lp->lexd.first)
+ off = cp-first;
+ else
+ off = cp-fcfirst();
+ if(off<0)
+ c=*cp, *cp=0;
+ n = sh_lex(lp);
+ if(off<0)
+ *cp = c;
+ if(n==endtok || off<0)
+ {
+ if(endtok==LPAREN && lp->lexd.paren)
+ {
+
+ if(first==lp->lexd.first)
+ {
+ n = cp+1-(char*)fcseek(0);
+ fcseek(n);
+ }
+ count++;
+ lp->lexd.paren = 0;
+ fcgetc(c);
+ }
+ while(1)
+ {
+ /* look for case and esac */
+ n=0;
+ while(1)
+ {
+ fcgetc(c);
+ /* skip leading white space */
+ if(n==0 && !sh_lexstates[ST_BEGIN][c])
+ continue;
+ if(n==4)
+ break;
+ if(sh_lexstates[ST_NAME][c])
+ goto skip;
+ word[n++] = c;
+ }
+ if(sh_lexstates[ST_NAME][c]==S_BREAK)
+ {
+ if(memcmp(word,"case",4)==0)
+ lp->lex.incase=1;
+ else if(memcmp(word,"esac",4)==0)
+ lp->lex.incase=0;
+ }
+ skip:
+ if(c && (c!='#' || n==0))
+ fcseek(-LEN);
+ if(c==RBRACE && lp->lex.incase)
+ lp->lex.incase=0;
+ c=sh_lex(lp);
+ switch(c)
+ {
+ case LBRACE:
+ if(endtok==LBRACE && !lp->lex.incase)
+ {
+ lp->comsub = 0;
+ count++;
+ }
+ break;
+ case RBRACE:
+ rbrace:
+ if(endtok==LBRACE && --count<=0)
+ goto done;
+ if(count==1)
+ lp->comsub = endtok==LBRACE;
+ break;
+ case IPROCSYM: case OPROCSYM:
+ case LPAREN:
+ if(endtok==LPAREN && !lp->lex.incase)
+ count++;
+ break;
+ case RPAREN:
+ if(lp->lex.incase)
+ lp->lex.incase=0;
+ else if(endtok==LPAREN && --count<=0)
+ goto done;
+ break;
+ case EOFSYM:
+ lp->lastline = line;
+ lp->lasttok = endtok;
+ sh_syntax(lp);
+ case IOSEEKSYM:
+ if(fcgetc(c)!='#' && c>0)
+ fcseek(-LEN);
+ break;
+ case IODOCSYM:
+ lp->lexd.docextra = 0;
+ sh_lex(lp);
+ break;
+ case 0:
+ lp->lex.reservok = 0;
+ messages |= lp->lexd.message;
+ break;
+ case ';':
+ do
+ fcgetc(c);
+ while(!sh_lexstates[ST_BEGIN][c]);
+ if(c==RBRACE && endtok==LBRACE)
+ goto rbrace;
+ if(c>0)
+ fcseek(-LEN);
+ /* fall through*/
+ default:
+ lp->lex.reservok = 1;
+ }
+ }
+ }
+done:
+ poplevel(lp);
+ lp->comsub = csub;
+ lp->lastline = line;
+ lp->lexd.dolparen--;
+ lp->lex = save;
+ lp->assignok = (endchar(lp)==RBRACT?assignok:0);
+ if(lp->heredoc)
+ errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax5,lp->sh->inlineno,lp->heredoc->ioname);
+ return(messages);
+}
+
+/*
+ * here-doc nested in $(...)
+ * allocate ionode with delimiter filled in without disturbing stak
+ */
+static void nested_here(register Lex_t *lp)
+{
+ register struct ionod *iop;
+ register int n=0,offset;
+ struct argnod *arg = lp->arg;
+ Stk_t *stkp = lp->sh->stk;
+ char *base;
+ if(offset=stktell(stkp))
+ base = stkfreeze(stkp,0);
+ if(lp->lexd.docend)
+ n = fcseek(0)-lp->lexd.docend;
+ iop = newof(0,struct ionod,1,lp->lexd.docextra+n+ARGVAL);
+ iop->iolst = lp->heredoc;
+ stkseek(stkp,ARGVAL);
+ if(lp->lexd.docextra)
+ {
+ sfseek(lp->sh->strbuf,(Sfoff_t)0, SEEK_SET);
+ sfmove(lp->sh->strbuf,stkp,lp->lexd.docextra,-1);
+ sfseek(lp->sh->strbuf,(Sfoff_t)0, SEEK_SET);
+ }
+ sfwrite(stkp,lp->lexd.docend,n);
+ lp->arg = sh_endword(lp->sh,0);
+ iop->ioname = (char*)(iop+1);
+ strcpy(iop->ioname,lp->arg->argval);
+ iop->iofile = (IODOC|IORAW);
+ if(lp->lexd.docword>1)
+ iop->iofile |= IOSTRIP;
+ lp->heredoc = iop;
+ lp->arg = arg;
+ lp->lexd.docword = 0;
+ if(offset)
+ stkset(stkp,base,offset);
+ else
+ stkseek(stkp,0);
+}
+
+/*
+ * skip to <close> character
+ * if <copy> is non,zero, then the characters are copied to the stack
+ * <state> is the initial lexical state
+ */
+void sh_lexskip(Lex_t *lp,int close, register int copy, int state)
+{
+ register char *cp;
+ lp->lexd.nest = close;
+ lp->lexd.lex_state = state;
+ lp->lexd.noarg = 1;
+ if(copy)
+ fcnotify(lex_advance,lp);
+ else
+ lp->lexd.nocopy++;
+ sh_lex(lp);
+ lp->lexd.noarg = 0;
+ if(copy)
+ {
+ fcnotify(0,lp);
+ if(!(cp=lp->lexd.first))
+ cp = fcfirst();
+ if((copy = fcseek(0)-cp) > 0)
+ sfwrite(lp->sh->stk,cp,copy);
+ }
+ else
+ lp->lexd.nocopy--;
+}
+
+#if SHOPT_CRNL
+ ssize_t _sfwrite(Sfio_t *sp, const Void_t *buff, size_t n)
+ {
+ const char *cp = (const char*)buff, *next=cp, *ep = cp + n;
+ int m=0,k;
+ while(next = (const char*)memchr(next,'\r',ep-next))
+ if(*++next=='\n')
+ {
+ if(k=next-cp-1)
+ {
+ if((k=sfwrite(sp,cp,k)) < 0)
+ return(m>0?m:-1);
+ m += k;
+ }
+ cp = next;
+ }
+ if((k=sfwrite(sp,cp,ep-cp)) < 0)
+ return(m>0?m:-1);
+ return(m+k);
+ }
+# define sfwrite _sfwrite
+#endif /* SHOPT_CRNL */
+
+/*
+ * read in here-document from script
+ * quoted here documents, and here-documents without special chars are
+ * noted with the IOQUOTE flag
+ * returns 1 for complete here-doc, 0 for EOF
+ */
+
+static int here_copy(Lex_t *lp,register struct ionod *iop)
+{
+ register const char *state;
+ register int c,n;
+ register char *bufp,*cp;
+ register Sfio_t *sp=lp->sh->heredocs, *funlog;
+ int stripcol=0,stripflg, nsave, special=0;
+ if(funlog=lp->sh->funlog)
+ {
+ if(fcfill()>0)
+ fcseek(-LEN);
+ lp->sh->funlog = 0;
+ }
+ if(iop->iolst)
+ here_copy(lp,iop->iolst);
+ iop->iooffset = sfseek(sp,(off_t)0,SEEK_END);
+ iop->iosize = 0;
+ iop->iodelim=iop->ioname;
+ /* check for and strip quoted characters in delimiter string */
+ if(stripflg=iop->iofile&IOSTRIP)
+ {
+ while(*iop->iodelim=='\t')
+ iop->iodelim++;
+ /* skip over leading tabs in document */
+ if(iop->iofile&IOLSEEK)
+ {
+ iop->iofile &= ~IOLSEEK;
+ while(fcgetc(c)=='\t' || c==' ')
+ {
+ if(c==' ')
+ stripcol++;
+ else
+ stripcol += 8 - stripcol%8;
+ }
+ }
+ else
+ while(fcgetc(c)=='\t');
+ if(c>0)
+ fcseek(-LEN);
+ }
+ if(iop->iofile&IOQUOTE)
+ state = sh_lexstates[ST_LIT];
+ else
+ state = sh_lexstates[ST_QUOTE];
+ bufp = fcseek(0);
+ n = S_NL;
+ while(1)
+ {
+ if(n!=S_NL)
+ {
+ /* skip over regular characters */
+#if SHOPT_MULTIBYTE
+ do
+ {
+ if(fcleft()< MB_LEN_MAX && mbsize(fcseek(0))<0)
+ {
+ n = S_EOF;
+ LEN = -fcleft();
+ break;
+ }
+ }
+#endif /* SHOPT_MULTIBYTE */
+
+ while((n=STATE(state,c))==0);
+ }
+ if(n==S_EOF || !(c=fcget()))
+ {
+ if(LEN < 0)
+ c = fclast()-bufp;
+ else
+ c= (fcseek(0)-1)-bufp;
+ if(!lp->lexd.dolparen && c)
+ {
+ if(n==S_ESC)
+ c--;
+ if(!lp->lexd.dolparen && (c=sfwrite(sp,bufp,c))>0)
+ iop->iosize += c;
+ }
+#if SHOPT_MULTIBYTE
+ if(LEN==0)
+ LEN=1;
+ if(LEN < 0)
+ {
+ n = LEN;
+ c = fcmbget(&LEN);
+ LEN += n;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ c = lexfill(lp);
+ if(c<0)
+ break;
+ if(n==S_ESC)
+ {
+#if SHOPT_CRNL
+ if(c=='\r' && (c=fcget())!=NL)
+ fcseek(-LEN);
+#endif /* SHOPT_CRNL */
+ if(c==NL)
+ fcseek(1);
+ else if(!lp->lexd.dolparen)
+ {
+ iop->iosize++;
+ sfputc(sp,'\\');
+ }
+ }
+ bufp = fcseek(-LEN);
+ }
+ else
+ fcseek(-LEN);
+ switch(n)
+ {
+ case S_NL:
+ lp->sh->inlineno++;
+ if((stripcol && c==' ') || (stripflg && c=='\t'))
+ {
+ if(!lp->lexd.dolparen)
+ {
+ /* write out line */
+ n = fcseek(0)-bufp;
+ if((n=sfwrite(sp,bufp,n))>0)
+ iop->iosize += n;
+ }
+ /* skip over tabs */
+ if(stripcol)
+ {
+ int col=0;
+ do
+ {
+ fcgetc(c);
+ if(c==' ')
+ col++;
+ else
+ col += 8 - col%8;
+ if(col>stripcol)
+ break;
+ }
+ while (c==' ' || c=='\t');
+ }
+ else while(c=='\t')
+ fcgetc(c);
+ if(c<=0)
+ goto done;
+ bufp = fcseek(-LEN);
+ }
+ if(c!=iop->iodelim[0])
+ break;
+ cp = fcseek(0);
+ nsave = n = 0;
+ while(1)
+ {
+ if(!(c=fcget()))
+ {
+ if(!lp->lexd.dolparen && (c=cp-bufp))
+ {
+ if((c=sfwrite(sp,cp=bufp,c))>0)
+ iop->iosize+=c;
+ }
+ nsave = n;
+ if((c=lexfill(lp))<=0)
+ {
+ c = iop->iodelim[n]==0;
+ goto done;
+ }
+ }
+#if SHOPT_CRNL
+ if(c=='\r' && (c=fcget())!=NL)
+ {
+ if(c)
+ fcseek(-LEN);
+ c='\r';
+ }
+#endif /* SHOPT_CRNL */
+ if(c==NL)
+ lp->sh->inlineno++;
+ if(iop->iodelim[n]==0 && (c==NL||c==RPAREN))
+ {
+ if(!lp->lexd.dolparen && (n=cp-bufp))
+ {
+ if((n=sfwrite(sp,bufp,n))>0)
+ iop->iosize += n;
+ }
+ lp->sh->inlineno--;
+ if(c==RPAREN)
+ fcseek(-LEN);
+ goto done;
+ }
+ if(iop->iodelim[n++]!=c)
+ {
+ /*
+ * The match for delimiter failed.
+ * nsave>0 only when a buffer boundary
+ * was crossed while checking the
+ * delimiter
+ */
+ if(!lp->lexd.dolparen && nsave>0)
+ {
+ if((n=sfwrite(sp,iop->iodelim,nsave))>0)
+ iop->iosize += n;
+ bufp = fcfirst();
+ }
+ if(c==NL)
+ fcseek(-LEN);
+ break;
+ }
+ }
+ break;
+ case S_ESC:
+ n=1;
+#if SHOPT_CRNL
+ if(c=='\r')
+ {
+ fcseek(1);
+ if(c=fcget())
+ fcseek(-LEN);
+ if(c==NL)
+ n=2;
+ else
+ {
+ special++;
+ break;
+ }
+ }
+#endif /* SHOPT_CRNL */
+ if(c==NL)
+ {
+ /* new-line joining */
+ lp->sh->inlineno++;
+ if(!lp->lexd.dolparen && (n=(fcseek(0)-bufp)-n)>=0)
+ {
+ if(n && (n=sfwrite(sp,bufp,n))>0)
+ iop->iosize += n;
+ bufp = fcseek(0)+1;
+ }
+ }
+ else
+ special++;
+ fcget();
+ break;
+
+ case S_GRAVE:
+ case S_DOL:
+ special++;
+ break;
+ }
+ n=0;
+ }
+done:
+ lp->sh->funlog = funlog;
+ if(lp->lexd.dolparen)
+ free((void*)iop);
+ else if(!special)
+ iop->iofile |= IOQUOTE;
+ return(c);
+}
+
+/*
+ * generates string for given token
+ */
+static char *fmttoken(Lex_t *lp, register int sym, char *tok)
+{
+ int n=1;
+ if(sym < 0)
+ return((char*)sh_translate(e_lexzerobyte));
+ if(sym==0)
+ return(lp->arg?lp->arg->argval:"?");
+ if(lp->lex.intest && lp->arg && *lp->arg->argval)
+ return(lp->arg->argval);
+ if(sym&SYMRES)
+ {
+ register const Shtable_t *tp=shtab_reserved;
+ while(tp->sh_number && tp->sh_number!=sym)
+ tp++;
+ return((char*)tp->sh_name);
+ }
+ if(sym==EOFSYM)
+ return((char*)sh_translate(e_endoffile));
+ if(sym==NL)
+ return((char*)sh_translate(e_newline));
+ tok[0] = sym;
+ if(sym&SYMREP)
+ tok[n++] = sym;
+ else
+ {
+ switch(sym&SYMMASK)
+ {
+ case SYMAMP:
+ sym = '&';
+ break;
+ case SYMPIPE:
+ sym = '|';
+ break;
+ case SYMGT:
+ sym = '>';
+ break;
+ case SYMLPAR:
+ sym = LPAREN;
+ break;
+ case SYMSHARP:
+ sym = '#';
+ break;
+ case SYMSEMI:
+ if(tok[0]=='<')
+ tok[n++] = '>';
+ sym = ';';
+ break;
+ default:
+ sym = 0;
+ }
+ tok[n++] = sym;
+ }
+ tok[n] = 0;
+ return(tok);
+}
+
+/*
+ * print a bad syntax message
+ */
+
+void sh_syntax(Lex_t *lp)
+{
+ register Shell_t *shp = lp->sh;
+ register const char *cp = sh_translate(e_unexpected);
+ register char *tokstr;
+ register int tok = lp->token;
+ char tokbuf[3];
+ Sfio_t *sp;
+ if((tok==EOFSYM) && lp->lasttok)
+ {
+ tok = lp->lasttok;
+ cp = sh_translate(e_unmatched);
+ }
+ else
+ lp->lastline = shp->inlineno;
+ tokstr = fmttoken(lp,tok,tokbuf);
+ if((sp=fcfile()) || (shp->infd>=0 && (sp=shp->sftable[shp->infd])))
+ {
+ /* clear out any pending input */
+ register Sfio_t *top;
+ while(fcget()>0);
+ fcclose();
+ while(top=sfstack(sp,SF_POPSTACK))
+ sfclose(top);
+ }
+ else
+ fcclose();
+ shp->inlineno = lp->inlineno;
+ shp->st.firstline = lp->firstline;
+#if KSHELL
+ if(!sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_PROFILE))
+#else
+ if(shp->inlineno!=1)
+#endif
+ errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax1,lp->lastline,tokstr,cp);
+ else
+ errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax2,tokstr,cp);
+}
+
+static char *stack_shift(Stk_t *stkp, register char *sp,char *dp)
+{
+ register char *ep;
+ register int offset = stktell(stkp);
+ register int left = offset-(sp-stkptr(stkp,0));
+ register int shift = (dp+1-sp);
+ offset += shift;
+ stkseek(stkp,offset);
+ sp = stkptr(stkp,offset);
+ ep = sp - shift;
+ while(left--)
+ *--sp = *--ep;
+ return(sp);
+}
+
+/*
+ * Assumes that current word is unfrozen on top of the stak
+ * If <mode> is zero, gets rid of quoting and consider argument as string
+ * and returns pointer to frozen arg
+ * If mode==1, just replace $"..." strings with international strings
+ * The result is left on the stak
+ * If mode==2, the each $"" string is printed on standard output
+ */
+struct argnod *sh_endword(Shell_t *shp,int mode)
+{
+ register const char *state = sh_lexstates[ST_NESTED];
+ register int n;
+ register char *sp,*dp;
+ register int inquote=0, inlit=0; /* set within quoted strings */
+ struct argnod* argp=0;
+ char *ep=0, *xp=0;
+ int bracket=0;
+ Stk_t *stkp=shp->stk;
+ sfputc(stkp,0);
+ sp = stkptr(stkp,ARGVAL);
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ do
+ {
+ int len;
+ switch(len = mbsize(sp))
+ {
+ case -1: /* illegal multi-byte char */
+ case 0:
+ case 1:
+ n=state[*sp++];
+ break;
+ default:
+ /*
+ * None of the state tables contain
+ * entries for multibyte characters,
+ * however, they should be treated
+ * the same as any other alph
+ * character. Therefore, we'll use
+ * the state of the 'a' character.
+ */
+ n=state['a'];
+ sp += len;
+ }
+ }
+ while(n == 0);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ while((n=state[*sp++])==0);
+ dp = sp;
+ if(mode<0)
+ inquote = 1;
+ while(1)
+ {
+ switch(n)
+ {
+ case S_EOF:
+ stkseek(stkp,dp-stkptr(stkp,0));
+ if(mode<=0)
+ {
+ argp = (struct argnod*)stkfreeze(stkp,0);
+ argp->argflag = ARG_RAW|ARG_QUOTED;
+ }
+ return(argp);
+ case S_LIT:
+ if(!(inquote&1))
+ {
+ inlit = !inlit;
+ if(mode==0 || (mode<0 && bracket))
+ {
+ dp--;
+ if(ep)
+ {
+ *dp = 0;
+ stresc(ep);
+ dp = ep+ strlen(ep);
+ }
+ ep = 0;
+ }
+ }
+ break;
+ case S_QUOTE:
+ if(mode<0 && !bracket)
+ break;
+ if(!inlit)
+ {
+ if(mode<=0)
+ dp--;
+ inquote = inquote^1;
+ if(ep)
+ {
+ char *msg;
+ if(mode==2)
+ {
+ sfprintf(sfstdout,"%.*s\n",dp-ep,ep);
+ ep = 0;
+ break;
+ }
+ *--dp = 0;
+#if ERROR_VERSION >= 20000317L
+ msg = ERROR_translate(0,error_info.id,0,ep);
+#else
+# if ERROR_VERSION >= 20000101L
+ msg = ERROR_translate(error_info.id,ep);
+# else
+ msg = ERROR_translate(ep,2);
+# endif
+#endif
+ n = strlen(msg);
+ dp = ep+n;
+ if(sp-dp <= 1)
+ {
+ sp = stack_shift(stkp,sp,dp);
+ dp = sp-1;
+ ep = dp-n;
+ }
+ memmove(ep,msg,n);
+ *dp++ = '"';
+ }
+ ep = 0;
+ }
+ break;
+ case S_DOL: /* check for $'...' and $"..." */
+ if(inlit)
+ break;
+ if(*sp==LPAREN || *sp==LBRACE)
+ {
+ inquote <<= 1;
+ break;
+ }
+ if(inquote&1)
+ break;
+ if(*sp=='\'' || *sp=='"')
+ {
+ if(*sp=='"')
+ inquote |= 1;
+ else
+ inlit = 1;
+ sp++;
+ if((mode==0||(mode<0&&bracket)) || (inquote&1))
+ {
+ if(mode==2)
+ ep = dp++;
+ else if(mode==1)
+ (ep=dp)[-1] = '"';
+ else
+ ep = --dp;
+ }
+ }
+ break;
+ case S_ESC:
+#if SHOPT_CRNL
+ if(*sp=='\r' && sp[1]=='\n')
+ sp++;
+#endif /* SHOPT_CRNL */
+ if(inlit || mode>0)
+ {
+ if(mode<0)
+ {
+ if(dp>=sp)
+ {
+ sp = stack_shift(stkp,sp,dp+1);
+ dp = sp-2;
+ }
+ *dp++ = '\\';
+ }
+ if(ep)
+ *dp++ = *sp++;
+ break;
+ }
+ n = *sp;
+#if SHOPT_DOS
+ if(!(inquote&1) && sh_lexstates[ST_NORM][n]==0)
+ break;
+#endif /* SHOPT_DOS */
+ if(!(inquote&1) || (sh_lexstates[ST_QUOTE][n] && n!=RBRACE))
+ {
+ if(n=='\n')
+ dp--;
+ else
+ dp[-1] = n;
+ sp++;
+ }
+ break;
+ case S_POP:
+ if(sp[-1]!=RBRACT)
+ break;
+ if(!inlit && !(inquote&1))
+ {
+ inquote >>= 1;
+ if(xp)
+ dp = sh_checkid(xp,dp);
+ xp = 0;
+ if(--bracket<=0 && mode<0)
+ inquote = 1;
+ }
+ else if((inlit||inquote) && mode<0)
+ {
+ dp[-1] = '\\';
+ if(dp>=sp)
+ {
+ sp = stack_shift(stkp,sp,dp);
+ dp = sp-1;
+ }
+ *dp++ = ']';
+ }
+ break;
+ case S_BRACT:
+ if(dp[-2]=='.')
+ xp = dp;
+ if(mode<0)
+ {
+ if(inlit || (bracket&&inquote))
+ {
+ dp[-1] = '\\';
+ if(dp>=sp)
+ {
+ sp = stack_shift(stkp,sp,dp);
+ dp = sp-1;
+ }
+ *dp++ = '[';
+ }
+ else if(bracket++==0)
+ inquote = 0;
+ }
+ break;
+ }
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ do
+ {
+ int len;
+ switch(len = mbsize(sp))
+ {
+ case -1: /* illegal multi-byte char */
+ case 0:
+ case 1:
+ n=state[*dp++ = *sp++];
+ break;
+ default:
+ /*
+ * None of the state tables contain
+ * entries for multibyte characters,
+ * however, they should be treated
+ * the same as any other alph
+ * character. Therefore, we'll use
+ * the state of the 'a' character.
+ */
+ while(len--)
+ *dp++ = *sp++;
+ n=state['a'];
+ }
+ }
+ while(n == 0);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ while((n=state[*dp++ = *sp++])==0);
+ }
+}
+
+struct alias
+{
+ Sfdisc_t disc;
+ Namval_t *np;
+ int nextc;
+ int line;
+ char buf[2];
+ Lex_t *lp;
+};
+
+/*
+ * This code gets called whenever an end of string is found with alias
+ */
+
+#ifndef SF_ATEXIT
+# define SF_ATEXIT 0
+#endif
+/*
+ * This code gets called whenever an end of string is found with alias
+ */
+#ifdef SF_BUFCONST
+static int alias_exceptf(Sfio_t *iop,int type,void *data, Sfdisc_t *handle)
+#else
+static int alias_exceptf(Sfio_t *iop,int type,Sfdisc_t *handle)
+#endif
+{
+ register struct alias *ap = (struct alias*)handle;
+ register Namval_t *np;
+ register Lex_t *lp;
+ if(type==0 || type==SF_ATEXIT || !ap)
+ return(0);
+ lp = ap->lp;
+ np = ap->np;
+ if(type!=SF_READ)
+ {
+ if(type==SF_CLOSING)
+ {
+ register Sfdisc_t *dp = sfdisc(iop,SF_POPDISC);
+ if(dp!=handle)
+ sfdisc(iop,dp);
+ }
+ else if(type==SF_FINAL)
+ free((void*)ap);
+ goto done;
+ }
+ if(ap->nextc)
+ {
+ /* if last character is a blank, then next work can be alias */
+ register int c = fcpeek(-1);
+ if(isblank(c))
+ lp->aliasok = 1;
+ *ap->buf = ap->nextc;
+ ap->nextc = 0;
+ sfsetbuf(iop,ap->buf,1);
+ return(1);
+ }
+done:
+ if(np)
+ nv_offattr(np,NV_NOEXPAND);
+ return(0);
+}
+
+
+static void setupalias(Lex_t *lp, const char *string,Namval_t *np)
+{
+ register Sfio_t *iop, *base;
+ struct alias *ap = (struct alias*)malloc(sizeof(struct alias));
+ ap->disc = alias_disc;
+ ap->lp = lp;
+ ap->buf[1] = 0;
+ if(ap->np = np)
+ {
+#if SHOPT_KIA
+ if(lp->kiafile)
+ {
+ unsigned long r;
+ r=kiaentity(lp,nv_name(np),-1,'p',0,0,lp->current,'a',0,"");
+ sfprintf(lp->kiatmp,"p;%..64d;p;%..64d;%d;%d;e;\n",lp->current,r,lp->sh->inlineno,lp->sh->inlineno);
+ }
+#endif /* SHOPT_KIA */
+ if((ap->nextc=fcget())==0)
+ ap->nextc = ' ';
+ }
+ else
+ ap->nextc = 0;
+ iop = sfopen(NIL(Sfio_t*),(char*)string,"s");
+ sfdisc(iop, &ap->disc);
+ lp->lexd.nocopy++;
+ if(!(base=fcfile()))
+ base = sfopen(NIL(Sfio_t*),fcseek(0),"s");
+ fcclose();
+ sfstack(base,iop);
+ fcfopen(base);
+ lp->lexd.nocopy--;
+}
+
+/*
+ * grow storage stack for nested constructs by STACK_ARRAY
+ */
+static int stack_grow(Lex_t *lp)
+{
+ lp->lexd.lex_max += STACK_ARRAY;
+ if(lp->lexd.lex_match)
+ lp->lexd.lex_match = (int*)realloc((char*)lp->lexd.lex_match,sizeof(int)*lp->lexd.lex_max);
+ else
+ lp->lexd.lex_match = (int*)malloc(sizeof(int)*STACK_ARRAY);
+ return(lp->lexd.lex_match!=0);
+}
+
diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c
new file mode 100644
index 0000000..9adf5f5
--- /dev/null
+++ b/src/cmd/ksh93/sh/macro.c
@@ -0,0 +1,2814 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Shell macro expander
+ * expands ~
+ * expands ${...}
+ * expands $(...)
+ * expands $((...))
+ * expands `...`
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <fcin.h>
+#include <pwd.h>
+#include <ctype.h>
+#include "name.h"
+#include "variables.h"
+#include "shlex.h"
+#include "io.h"
+#include "jobs.h"
+#include "shnodes.h"
+#include "path.h"
+#include "national.h"
+#include "streval.h"
+
+#undef STR_GROUP
+#ifndef STR_GROUP
+# define STR_GROUP 0
+#endif
+
+#if SHOPT_MULTIBYTE
+# undef isascii
+# define isacii(c) ((c)<=UCHAR_MAX)
+#else
+# define mbchar(p) (*(unsigned char*)p++)
+#endif /* SHOPT_MULTIBYTE */
+
+#if _WINIX
+ static int Skip;
+#endif /*_WINIX */
+
+static int _c_;
+typedef struct _mac_
+{
+ Shell_t *shp; /* pointer to shell interpreter */
+ Sfio_t *sp; /* stream pointer for here-document */
+ struct argnod **arghead; /* address of head of argument list */
+ char *ifsp; /* pointer to IFS value */
+ int fields; /* number of fields */
+ short quoted; /* set when word has quotes */
+ unsigned char ifs; /* first char of IFS */
+ char atmode; /* when processing $@ */
+ char quote; /* set within double quoted contexts */
+ char lit; /* set within single quotes */
+ char split; /* set when word splittin is possible */
+ char pattern; /* set when file expansion follows */
+ char patfound; /* set if pattern character found */
+ char assign; /* set for assignments */
+ char arith; /* set for ((...)) */
+ char let; /* set when expanding let arguments */
+ char zeros; /* strip leading zeros when set */
+ char arrayok; /* $x[] ok for arrays */
+ char subcopy; /* set when copying subscript */
+ int dotdot; /* set for .. in subscript */
+ void *nvwalk; /* for name space walking*/
+} Mac_t;
+
+#undef ESCAPE
+#define ESCAPE '\\'
+#define isescchar(s) ((s)>S_QUOTE)
+#define isqescchar(s) ((s)>=S_QUOTE)
+#define isbracechar(c) ((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2)
+#define ltos(x) fmtbase((long)(x),0,0)
+
+/* type of macro expansions */
+#define M_BRACE 1 /* ${var} */
+#define M_TREE 2 /* ${var.} */
+#define M_SIZE 3 /* ${#var} */
+#define M_VNAME 4 /* ${!var} */
+#define M_SUBNAME 5 /* ${!var[sub]} */
+#define M_NAMESCAN 6 /* ${!var*} */
+#define M_NAMECOUNT 7 /* ${#var*} */
+#define M_TYPE 8 /* ${@var} */
+
+static int substring(const char*, const char*, int[], int);
+static void copyto(Mac_t*, int, int);
+static void comsubst(Mac_t*, Shnode_t*, int);
+static int varsub(Mac_t*);
+static void mac_copy(Mac_t*,const char*, int);
+static void tilde_expand2(Shell_t*,int);
+static char *sh_tilde(Shell_t*,const char*);
+static char *special(Shell_t *,int);
+static void endfield(Mac_t*,int);
+static void mac_error(Namval_t*);
+static char *mac_getstring(char*);
+static int charlen(const char*,int);
+#if SHOPT_MULTIBYTE
+ static char *lastchar(const char*,const char*);
+#endif /* SHOPT_MULTIBYTE */
+
+void *sh_macopen(Shell_t *shp)
+{
+ void *addr = newof(0,Mac_t,1,0);
+ Mac_t *mp = (Mac_t*)addr;
+ mp->shp = shp;
+ return(addr);
+}
+
+/*
+ * perform only parameter substitution and catch failures
+ */
+char *sh_mactry(Shell_t *shp,register char *string)
+{
+ if(string)
+ {
+ int jmp_val;
+ int savexit = shp->savexit;
+ struct checkpt buff;
+ sh_pushcontext(shp,&buff,SH_JMPSUB);
+ jmp_val = sigsetjmp(buff.buff,0);
+ if(jmp_val == 0)
+ string = sh_mactrim(shp,string,0);
+ sh_popcontext(shp,&buff);
+ shp->savexit = savexit;
+ return(string);
+ }
+ return("");
+}
+
+/*
+ * Perform parameter expansion, command substitution, and arithmetic
+ * expansion on <str>.
+ * If <mode> greater than 1 file expansion is performed if the result
+ * yields a single pathname.
+ * If <mode> negative, than expansion rules for assignment are applied.
+ */
+char *sh_mactrim(Shell_t *shp, char *str, register int mode)
+{
+ register Mac_t *mp = (Mac_t*)shp->mac_context;
+ Stk_t *stkp = shp->stk;
+ Mac_t savemac;
+ savemac = *mp;
+ stkseek(stkp,0);
+ mp->arith = (mode==3);
+ mp->let = 0;
+ shp->argaddr = 0;
+ mp->pattern = (mode==1||mode==2);
+ mp->patfound = 0;
+ mp->assign = 0;
+ if(mode<0)
+ mp->assign = -mode;
+ mp->quoted = mp->lit = mp->split = mp->quote = 0;
+ mp->sp = 0;
+ if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
+ mp->ifs = *mp->ifsp;
+ else
+ mp->ifs = ' ';
+ stkseek(stkp,0);
+ fcsopen(str);
+ copyto(mp,0,mp->arith);
+ str = stkfreeze(stkp,1);
+ if(mode==2)
+ {
+ /* expand only if unique */
+ struct argnod *arglist=0;
+ if((mode=path_expand(shp,str,&arglist))==1)
+ str = arglist->argval;
+ else if(mode>1)
+ errormsg(SH_DICT,ERROR_exit(1),e_ambiguous,str);
+ sh_trim(str);
+ }
+ *mp = savemac;
+ return(str);
+}
+
+/*
+ * Perform all the expansions on the argument <argp>
+ */
+int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arghead,int flag)
+{
+ register int flags = argp->argflag;
+ register char *str = argp->argval;
+ register Mac_t *mp = (Mac_t*)shp->mac_context;
+ char **saveargaddr = shp->argaddr;
+ Mac_t savemac;
+ Stk_t *stkp = shp->stk;
+ savemac = *mp;
+ mp->sp = 0;
+ if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
+ mp->ifs = *mp->ifsp;
+ else
+ mp->ifs = ' ';
+ if((flag&ARG_OPTIMIZE) && !shp->indebug && !(flags&ARG_MESSAGE))
+ shp->argaddr = (char**)&argp->argchn.ap;
+ else
+ shp->argaddr = 0;
+ mp->arghead = arghead;
+ mp->quoted = mp->lit = mp->quote = 0;
+ mp->arith = ((flag&ARG_ARITH)!=0);
+ mp->let = ((flag&ARG_LET)!=0);
+ mp->split = !(flag&ARG_ASSIGN);
+ mp->assign = !mp->split;
+ mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
+ mp->arrayok = mp->arith || (flag&ARG_ARRAYOK);
+ str = argp->argval;
+ fcsopen(str);
+ mp->fields = 0;
+ mp->atmode = 0;
+ if(!arghead)
+ {
+ mp->split = 0;
+ mp->pattern = ((flag&ARG_EXP)!=0);
+ stkseek(stkp,0);
+ }
+ else
+ {
+ stkseek(stkp,ARGVAL);
+ *stkptr(stkp,ARGVAL-1) = 0;
+ }
+ mp->patfound = 0;
+ if(mp->pattern)
+ mp->arrayok = 0;
+ copyto(mp,0,mp->arith);
+ if(!arghead)
+ {
+ argp->argchn.cp = stkfreeze(stkp,1);
+ if(shp->argaddr)
+ argp->argflag |= ARG_MAKE;
+ }
+ else
+ {
+ endfield(mp,mp->quoted|mp->atmode);
+ flags = mp->fields;
+ if(flags==1 && shp->argaddr)
+ argp->argchn.ap = *arghead;
+ }
+ shp->argaddr = saveargaddr;
+ *mp = savemac;
+ return(flags);
+}
+
+/*
+ * Expand here document which is stored in <infile> or <string>
+ * The result is written to <outfile>
+ */
+void sh_machere(Shell_t *shp,Sfio_t *infile, Sfio_t *outfile, char *string)
+{
+ register int c,n;
+ register const char *state = sh_lexstates[ST_QUOTE];
+ register char *cp;
+ register Mac_t *mp = (Mac_t*)shp->mac_context;
+ Lex_t *lp = (Lex_t*)mp->shp->lex_context;
+ Fcin_t save;
+ Mac_t savemac;
+ Stk_t *stkp = shp->stk;
+ savemac = *mp;
+ stkseek(stkp,0);
+ shp->argaddr = 0;
+ mp->sp = outfile;
+ mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0;
+ mp->quote = 1;
+ mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
+ mp->ifs = ' ';
+ fcsave(&save);
+ if(infile)
+ fcfopen(infile);
+ else
+ fcsopen(string);
+ fcnotify(0,lp);
+ cp = fcseek(0);
+ while(1)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ do
+ {
+ ssize_t len;
+ switch(len = mbsize(cp))
+ {
+ case -1: /* illegal multi-byte char */
+ case 0:
+ case 1:
+ n=state[*(unsigned char*)cp++];
+ break;
+ default:
+ /* use state of alpha character */
+ n=state['a'];
+ cp += len;
+ }
+ }
+ while(n == 0);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ while((n=state[*(unsigned char*)cp++])==0);
+ if(n==S_NL || n==S_QUOTE || n==S_RBRA)
+ continue;
+ if(c=(cp-1)-fcseek(0))
+ sfwrite(outfile,fcseek(0),c);
+ cp = fcseek(c+1);
+ switch(n)
+ {
+ case S_EOF:
+ if((n=fcfill()) <=0)
+ {
+ /* ignore 0 byte when reading from file */
+ if(n==0 && fcfile())
+ continue;
+ fcrestore(&save);
+ *mp = savemac;
+ return;
+ }
+ cp = fcseek(-1);
+ continue;
+ case S_ESC:
+ fcgetc(c);
+ cp=fcseek(-1);
+ if(c>0)
+ cp++;
+ if(!isescchar(state[c]))
+ sfputc(outfile,ESCAPE);
+ continue;
+ case S_GRAVE:
+ comsubst(mp,(Shnode_t*)0,0);
+ break;
+ case S_DOL:
+ c = fcget();
+ if(c=='.')
+ goto regular;
+ again:
+ switch(n=sh_lexstates[ST_DOL][c])
+ {
+ case S_ALP: case S_SPC1: case S_SPC2:
+ case S_DIG: case S_LBRA:
+ {
+ Fcin_t save2;
+ int offset = stktell(stkp);
+ int offset2;
+ fcnotify(0,lp);
+ sfputc(stkp,c);
+ if(n==S_LBRA)
+ {
+ c = fcget();
+ fcseek(-1);
+ if(sh_lexstates[ST_NORM][c]==S_BREAK)
+ {
+ comsubst(mp,(Shnode_t*)0,2);
+ break;
+ }
+ sh_lexskip(lp,RBRACE,1,ST_BRACE);
+ }
+ else if(n==S_ALP)
+ {
+ while(fcgetc(c),isaname(c))
+ sfputc(stkp,c);
+ fcseek(-1);
+ }
+ sfputc(stkp,0);
+ offset2 = stktell(stkp);
+ fcsave(&save2);
+ fcsopen(stkptr(stkp,offset));
+ varsub(mp);
+ if(c=stktell(stkp)-offset2)
+ sfwrite(outfile,(char*)stkptr(stkp,offset2),c);
+ fcrestore(&save2);
+ stkseek(stkp,offset);
+ break;
+ }
+ case S_PAR:
+ comsubst(mp,(Shnode_t*)0,1);
+ break;
+ case S_EOF:
+ if((c=fcfill()) > 0)
+ goto again;
+ /* FALL THRU */
+ default:
+ regular:
+ sfputc(outfile,'$');
+ fcseek(-1);
+ break;
+ }
+ }
+ cp = fcseek(0);
+ }
+}
+
+/*
+ * expand argument but do not trim pattern characters
+ */
+char *sh_macpat(Shell_t *shp,register struct argnod *arg, int flags)
+{
+ register char *sp = arg->argval;
+ if((arg->argflag&ARG_RAW))
+ return(sp);
+ sh_stats(STAT_ARGEXPAND);
+ if(flags&ARG_OPTIMIZE)
+ arg->argchn.ap=0;
+ if(!(sp=arg->argchn.cp))
+ {
+ sh_macexpand(shp,arg,NIL(struct argnod**),flags|ARG_ARRAYOK);
+ sp = arg->argchn.cp;
+ if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE))
+ arg->argchn.cp = 0;
+ arg->argflag &= ~ARG_MAKE;
+ }
+ else
+ sh_stats(STAT_ARGHITS);
+ return(sp);
+}
+
+/*
+ * Process the characters up to <endch> or end of input string
+ */
+static void copyto(register Mac_t *mp,int endch, int newquote)
+{
+ register int c,n;
+ register const char *state = sh_lexstates[ST_MACRO];
+ register char *cp,*first;
+ Lex_t *lp = (Lex_t*)mp->shp->lex_context;
+ int tilde = -1;
+ int oldquote = mp->quote;
+ int ansi_c = 0;
+ int paren = 0;
+ int ere = 0;
+ int brace = 0;
+ Sfio_t *sp = mp->sp;
+ Stk_t *stkp = mp->shp->stk;
+ char *resume = 0;
+ mp->sp = NIL(Sfio_t*);
+ mp->quote = newquote;
+ first = cp = fcseek(0);
+ if(!mp->quote && *cp=='~' && cp[1]!=LPAREN)
+ tilde = stktell(stkp);
+ /* handle // operator specially */
+ if(mp->pattern==2 && *cp=='/')
+ cp++;
+ while(1)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ ssize_t len;
+ do
+ {
+ switch(len = mbsize(cp))
+ {
+ case -1: /* illegal multi-byte char */
+ case 0:
+ len = 1;
+ case 1:
+ n = state[*(unsigned char*)cp++];
+ break;
+ default:
+ /* treat as if alpha */
+ cp += len;
+ n=state['a'];
+ }
+ }
+ while(n == 0);
+ c = (cp-len) - first;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ {
+ while((n=state[*(unsigned char*)cp++])==0);
+ c = (cp-1) - first;
+ }
+ switch(n)
+ {
+ case S_ESC:
+ if(ansi_c)
+ {
+ /* process ANSI-C escape character */
+ char *addr= --cp;
+ if(c)
+ sfwrite(stkp,first,c);
+ c = chresc(cp,&addr);
+ cp = addr;
+ first = fcseek(cp-first);
+#if SHOPT_MULTIBYTE
+ if(c > UCHAR_MAX && mbwide())
+ {
+ int i;
+ unsigned char mb[8];
+
+ n = mbconv((char*)mb, c);
+ for(i=0;i<n;i++)
+ sfputc(stkp,mb[i]);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ sfputc(stkp,c);
+ if(c==ESCAPE && mp->pattern)
+ sfputc(stkp,ESCAPE);
+ break;
+ }
+ else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.'))
+ break;
+ else if(mp->split && endch && !mp->quote && !mp->lit)
+ {
+ if(c)
+ mac_copy(mp,first,c);
+ cp = fcseek(c+2);
+ if(c= cp[-1])
+ {
+ sfputc(stkp,c);
+ if(c==ESCAPE)
+ sfputc(stkp,ESCAPE);
+ }
+ else
+ cp--;
+ first = cp;
+ break;
+ }
+ n = state[*(unsigned char*)cp];
+ if(n==S_ENDCH && *cp!=endch)
+ n = S_PAT;
+ if(mp->pattern)
+ {
+ /* preserve \digit for pattern matching */
+ /* also \alpha for extended patterns */
+ if(!mp->lit && !mp->quote)
+ {
+ int nc = *(unsigned char*)cp;
+ if((n==S_DIG || ((paren+ere) && (sh_lexstates[ST_DOL][nc]==S_ALP) || nc=='<' || nc=='>')))
+ break;
+ if(ere && mp->pattern==1 && strchr(".[()*+?{|^$&!",*cp))
+ break;
+ }
+ /* followed by file expansion */
+ if(!mp->lit && (n==S_ESC || (!mp->quote &&
+ (n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-'))))
+ {
+ cp += (n!=S_EOF);
+ if(ere && n==S_ESC && *cp =='\\' && cp[1]=='$')
+ {
+ /* convert \\\$ into \$' */
+ sfwrite(stkp,first,c+1);
+ cp = first = fcseek(c+3);
+ }
+ break;
+ }
+ if(!(ere && *cp=='$') && (mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH)))
+ {
+ /* add \ for file expansion */
+ sfwrite(stkp,first,c+1);
+ first = fcseek(c);
+ break;
+ }
+ }
+ if(mp->lit)
+ break;
+ if(!mp->quote || isqescchar(n) || n==S_ENDCH)
+ {
+ /* eliminate \ */
+ if(c)
+ sfwrite(stkp,first,c);
+ /* check new-line joining */
+ first = fcseek(c+1);
+ }
+ cp += (n!=S_EOF);
+ break;
+ case S_GRAVE: case S_DOL:
+ if(mp->lit)
+ break;
+ if(c)
+ {
+ if(mp->split && !mp->quote && endch)
+ mac_copy(mp,first,c);
+ else
+ sfwrite(stkp,first,c);
+ }
+ first = fcseek(c+1);
+ c = mp->pattern;
+ if(n==S_GRAVE)
+ comsubst(mp,(Shnode_t*)0,0);
+ else if((n= *cp) == '"' && !mp->quote)
+ {
+ int off = stktell(stkp);
+ char *dp;
+ cp = first = fcseek(1);
+ mp->quote = 1;
+ if(!ERROR_translating())
+ break;
+ while(n=c, c= *++cp)
+ {
+ if(c=='\\' && n==c)
+ c = 0;
+ else if(c=='"' && n!='\\')
+ break;
+ }
+ n = cp-first;
+ sfwrite(stkp,first,n);
+ sfputc(stkp,0);
+ cp = stkptr(stkp,off);
+ dp = (char*)sh_translate(cp);
+ stkseek(stkp,off);
+ if(dp==cp)
+ {
+ cp = first;
+ break;
+ }
+ resume = fcseek(n);
+ fcclose();
+ fcsopen(dp);
+ cp = first = fcseek(0);
+ break;
+ }
+ else if(n==0 || !varsub(mp))
+ {
+ if(n=='\'' && !mp->quote)
+ ansi_c = 1;
+ else if(mp->quote || n!='"')
+ sfputc(stkp,'$');
+ }
+ cp = first = fcseek(0);
+ if(mp->quote && cp)
+ mp->pattern = c;
+ break;
+ case S_ENDCH:
+ if((mp->lit || cp[-1]!=endch || mp->quote!=newquote))
+ goto pattern;
+ if(endch==RBRACE && *cp==LPAREN && mp->pattern && brace)
+ goto pattern;
+ case S_EOF:
+ if(c)
+ {
+ if(mp->split && !mp->quote && !mp->lit && endch)
+ mac_copy(mp,first,c);
+ else
+ sfwrite(stkp,first,c);
+ }
+ if(n==S_EOF && resume)
+ {
+ fcclose();
+ fcsopen(resume);
+ resume = 0;
+ cp = first = fcseek(0);
+ continue;
+ }
+ c += (n!=S_EOF);
+ first = fcseek(c);
+ if(tilde>=0)
+ tilde_expand2(mp->shp,tilde);
+ goto done;
+ case S_QUOTE:
+ if(mp->lit || mp->arith)
+ break;
+ case S_LIT:
+ if(mp->arith)
+ {
+ if((*cp=='`' || *cp=='[') && cp[1]=='\'')
+ cp +=2;
+ break;
+ }
+ if(n==S_LIT && mp->quote)
+ break;
+ if(c)
+ {
+ if(mp->split && endch && !mp->quote && !mp->lit)
+ mac_copy(mp,first,c);
+ else
+ sfwrite(stkp,first,c);
+ }
+ first = fcseek(c+1);
+ if(n==S_LIT)
+ {
+ if(mp->quote)
+ continue;
+ if(mp->lit)
+ mp->lit = ansi_c = 0;
+ else
+ mp->lit = 1;
+ }
+ else
+ mp->quote = !mp->quote;
+ mp->quoted++;
+ break;
+ case S_BRACT:
+ if(mp->arith || (((mp->assign&1) || endch==RBRACT) &&
+ !(mp->quote || mp->lit)))
+ {
+ int offset=0,oldpat = mp->pattern;
+ int oldarith = mp->arith, oldsub=mp->subcopy;
+ sfwrite(stkp,first,++c);
+ if(mp->assign&1)
+ {
+ if(first[c-2]=='.')
+ offset = stktell(stkp);
+ if(isastchar(*cp) && cp[1]==']')
+ errormsg(SH_DICT,ERROR_exit(1),
+e_badsubscript,*cp);
+ }
+ first = fcseek(c);
+ mp->pattern = 4;
+ mp->arith = 0;
+ mp->subcopy = 0;
+ copyto(mp,RBRACT,0);
+ mp->subcopy = oldsub;
+ mp->arith = oldarith;
+ mp->pattern = oldpat;
+ sfputc(stkp,RBRACT);
+ if(offset)
+ {
+ cp = stkptr(stkp,stktell(stkp));
+ if(sh_checkid(stkptr(stkp,offset),cp)!=cp)
+ stkseek(stkp,stktell(stkp)-2);
+ }
+ cp = first = fcseek(0);
+ break;
+ }
+ case S_PAT:
+ if(mp->pattern && !(mp->quote || mp->lit))
+ {
+ mp->patfound = mp->pattern;
+ if((n=cp[-1])==LPAREN)
+ {
+ paren++;
+ if((cp-first)>1 && cp[-2]=='~')
+ {
+ char *p = cp;
+ while((c=mbchar(p)) && c!=RPAREN)
+ if(c=='A'||c=='E'||c=='K'||c=='P'||c=='X')
+ {
+ ere = 1;
+ break;
+ }
+ }
+ }
+ else if(n==RPAREN)
+ --paren;
+ }
+ goto pattern;
+ case S_COM:
+ if(mp->pattern==4 && (mp->quote || mp->lit))
+ {
+ if(c)
+ {
+ sfwrite(stkp,first,c);
+ first = fcseek(c);
+ }
+ sfputc(stkp,ESCAPE);
+ }
+ break;
+ case S_BRACE:
+ if(!(mp->quote || mp->lit))
+ {
+ mp->patfound = mp->split && sh_isoption(SH_BRACEEXPAND);
+ brace = 1;
+ }
+ pattern:
+ if(!mp->pattern || !(mp->quote || mp->lit))
+ {
+ /* mark beginning of {a,b} */
+ if(n==S_BRACE && endch==0 && mp->pattern)
+ mp->pattern=4;
+ if(n==S_SLASH && mp->pattern==2)
+ mp->pattern=3;
+ break;
+ }
+ if(mp->pattern==3)
+ break;
+ if(c)
+ sfwrite(stkp,first,c);
+ first = fcseek(c);
+ sfputc(stkp,ESCAPE);
+ break;
+ case S_EQ:
+ if(mp->assign==1)
+ {
+ if(*cp=='~' && !endch && !mp->quote && !mp->lit)
+ tilde = stktell(stkp)+(c+1);
+ mp->assign = 2;
+ }
+ break;
+ case S_SLASH:
+ case S_COLON:
+ if(tilde >=0)
+ {
+ if(c)
+ sfwrite(stkp,first,c);
+ first = fcseek(c);
+ tilde_expand2(mp->shp,tilde);
+#if _WINIX
+ if(Skip)
+ {
+ first = cp = fcseek(Skip);
+ Skip = 0;
+ }
+#endif /*_WINIX */
+ tilde = -1;
+ c=0;
+ }
+ if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit)
+ tilde = stktell(stkp)+(c+1);
+ else if(n==S_SLASH && mp->pattern==2)
+#if 0
+ goto pattern;
+#else
+ {
+ if(mp->quote || mp->lit)
+ goto pattern;
+ sfwrite(stkp,first,c+1);
+ first = fcseek(c+1);
+ c = stktell(stkp);
+ sh_lexskip(lp,RBRACE,0,ST_NESTED);
+ stkseek(stkp,c);
+ cp = fcseek(-1);
+ sfwrite(stkp,first,cp-first);
+ first=cp;
+ }
+#endif
+ break;
+ case S_DOT:
+ if(*cp=='.' && mp->subcopy==1)
+ {
+ sfwrite(stkp,first,c);
+ sfputc(stkp,0);
+ mp->dotdot = stktell(stkp);
+ cp = first = fcseek(c+2);
+ }
+ break;
+ }
+ }
+done:
+ mp->sp = sp;
+ mp->quote = oldquote;
+}
+
+/*
+ * copy <str> to stack performing sub-expression substitutions
+ */
+static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize)
+{
+ register int c,n;
+ register char *first=cp;
+ while(1)
+ {
+ while((c= *cp++) && c!=ESCAPE);
+ if(c==0)
+ break;
+ if((n= *cp++)=='\\' || n==RBRACE || (n>='0' && n<='9' && (n-='0')<subsize))
+ {
+ c = cp-first-2;
+ if(c)
+ mac_copy(mp,first,c);
+ first=cp;
+ if(n=='\\' || n==RBRACE)
+ {
+ first--;
+ continue;
+ }
+ if((c=subexp[2*n])>=0)
+ {
+ if((n=subexp[2*n+1]-c)>0)
+ mac_copy(mp,str+c,n);
+ }
+ }
+ else if(n==0)
+ break;
+ }
+ if(n=cp-first-1)
+ mac_copy(mp,first,n);
+}
+
+#if SHOPT_FILESCAN
+#define MAX_OFFSETS (sizeof(shp->offsets)/sizeof(shp->offsets[0]))
+#define MAX_ARGN (32*1024)
+
+/*
+ * compute the arguments $1 ... $n and $# from the current line as needed
+ * save line offsets in the offsets array.
+ */
+static char *getdolarg(Shell_t *shp, int n, int *size)
+{
+ register int c=S_DELIM, d=shp->ifstable['\\'];
+ register unsigned char *first,*last,*cp = (unsigned char*)shp->cur_line;
+ register int m=shp->offsets[0],delim=0;
+ if(m==0)
+ return(0);
+ if(m<0)
+ m = 0;
+ else if(n<=m)
+ m = n-1;
+ else
+ m--;
+ if(m >= MAX_OFFSETS-1)
+ m = MAX_OFFSETS-2;
+ cp += shp->offsets[m+1];
+ n -= m;
+ shp->ifstable['\\'] = 0;
+ shp->ifstable[0] = S_EOF;
+ while(1)
+ {
+ if(c==S_DELIM)
+ while(shp->ifstable[*cp++]==S_SPACE);
+ first = --cp;
+ if(++m < MAX_OFFSETS)
+ shp->offsets[m] = (first-(unsigned char*)shp->cur_line);
+ while((c=shp->ifstable[*cp++])==0);
+ last = cp-1;
+ if(c==S_SPACE)
+ while((c=shp->ifstable[*cp++])==S_SPACE);
+ if(--n==0 || c==S_EOF)
+ {
+ if(last==first && c==S_EOF && (!delim || (m>1)))
+ {
+ n++;
+ m--;
+ }
+ break;
+ }
+ delim = (c==S_DELIM);
+ }
+ shp->ifstable['\\'] = d;
+ if(m > shp->offsets[0])
+ shp->offsets[0] = m;
+ if(n)
+ first = last = 0;
+ if(size)
+ *size = last-first;
+ return((char*)first);
+}
+#endif /* SHOPT_FILESCAN */
+
+/*
+ * get the prefix after name reference resolution
+ */
+static char *prefix(Shell_t *shp, char *id)
+{
+ Namval_t *np;
+ register char *sub=0, *cp = strchr(id,'.');
+ if(cp)
+ {
+ *cp = 0;
+ np = nv_search(id, shp->var_tree,0);
+ *cp = '.';
+ if(isastchar(cp[1]))
+ cp[1] = 0;
+ if(np && nv_isref(np))
+ {
+ int n;
+ char *sp;
+ shp->argaddr = 0;
+ while(nv_isref(np) && np->nvalue.cp)
+ {
+ sub = nv_refsub(np);
+ np = nv_refnode(np);
+ if(sub)
+ nv_putsub(np,sub,0L);
+ }
+ id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+ (sub?strlen(sub)+3:1));
+ memcpy(id,sp,n);
+ if(sub)
+ {
+ id[n++] = '[';
+ strcpy(&id[n],sub);
+ n+= strlen(sub)+1;
+ id[n-1] = ']';
+ }
+ strcpy(&id[n],cp);
+ return(id);
+ }
+ }
+ return(strdup(id));
+}
+
+/*
+ * copy to ']' onto the stack and return offset to it
+ */
+static int subcopy(Mac_t *mp, int flag)
+{
+ int split = mp->split;
+ int xpattern = mp->pattern;
+ int loc = stktell(mp->shp->stk);
+ int xarith = mp->arith;
+ int arrayok = mp->arrayok;
+ mp->split = 0;
+ mp->arith = 0;
+ mp->pattern = flag?4:0;
+ mp->arrayok=1;
+ mp->subcopy++;
+ mp->dotdot = 0;
+ copyto(mp,RBRACT,0);
+ mp->subcopy = 0;
+ mp->pattern = xpattern;
+ mp->split = split;
+ mp->arith = xarith;
+ mp->arrayok = arrayok;
+ return(loc);
+}
+
+/*
+ * if name is a discipline function, run the function and put the results
+ * on the stack so that ${x.foo} behaves like ${ x.foo;}
+ */
+int sh_macfun(Shell_t *shp, const char *name, int offset)
+{
+ Namval_t *np, *nq;
+ np = nv_bfsearch(name,shp->fun_tree,&nq,(char**)0);
+ if(np)
+ {
+ /* treat ${x.foo} as ${x.foo;} */
+ union
+ {
+ struct comnod com;
+ Shnode_t node;
+ } t;
+ union
+ {
+ struct argnod arg;
+ struct dolnod dol;
+ char buff[sizeof(struct dolnod)+sizeof(char*)];
+ } d;
+ memset(&t,0,sizeof(t));
+ memset(&d,0,sizeof(d));
+ t.node.com.comarg = &d.arg;
+ t.node.com.comline = shp->inlineno;
+ d.dol.dolnum = 1;
+ d.dol.dolval[0] = strdup(name);
+ stkseek(shp->stk,offset);
+ comsubst((Mac_t*)shp->mac_context,&t.node,2);
+ free(d.dol.dolval[0]);
+ return(1);
+ }
+ return(0);
+}
+
+static int namecount(Mac_t *mp,const char *prefix)
+{
+ int count = 0;
+ mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
+ while(nv_dirnext(mp->nvwalk))
+ count++;
+ nv_dirclose(mp->nvwalk);
+ return(count);
+}
+
+static char *nextname(Mac_t *mp,const char *prefix, int len)
+{
+ char *cp;
+ if(len==0)
+ {
+ mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
+ return((char*)mp->nvwalk);
+ }
+ if(!(cp=nv_dirnext(mp->nvwalk)))
+ nv_dirclose(mp->nvwalk);
+ return(cp);
+}
+
+/*
+ * This routine handles $param, ${parm}, and ${param op word}
+ * The input stream is assumed to be a string
+ */
+static int varsub(Mac_t *mp)
+{
+ register int c;
+ register int type=0; /* M_xxx */
+ register char *v,*argp=0;
+ register Namval_t *np = NIL(Namval_t*);
+ register int dolg=0, mode=0;
+ Lex_t *lp = (Lex_t*)mp->shp->lex_context;
+ Namarr_t *ap=0;
+ int dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0;
+ char idbuff[3], *id = idbuff, *pattern=0, *repstr=0, *arrmax=0;
+ char *idx = 0;
+ int var=1,addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d;
+ Stk_t *stkp = mp->shp->stk;
+retry1:
+ mp->zeros = 0;
+ idbuff[0] = 0;
+ idbuff[1] = 0;
+ c = fcmbget(&LEN);
+ switch(isascii(c)?sh_lexstates[ST_DOL][c]:S_ALP)
+ {
+ case S_RBRA:
+ if(type<M_SIZE)
+ goto nosub;
+ /* This code handles ${#} */
+ c = mode;
+ mode = type = 0;
+ /* FALL THRU */
+ case S_SPC1:
+ if(type==M_BRACE)
+ {
+ if(isaletter(mode=fcpeek(0)) || mode=='.')
+ {
+ if(c=='#')
+ type = M_SIZE;
+#ifdef SHOPT_TYPEDEF
+ else if(c=='@')
+ {
+ type = M_TYPE;
+ goto retry1;
+ }
+#endif /* SHOPT_TYPEDEF */
+ else
+ type = M_VNAME;
+ mode = c;
+ goto retry1;
+ }
+ else if(c=='#' && (isadigit(mode)||fcpeek(1)==RBRACE))
+ {
+ type = M_SIZE;
+ mode = c;
+ goto retry1;
+ }
+ }
+ /* FALL THRU */
+ case S_SPC2:
+ var = 0;
+ *id = c;
+ v = special(mp->shp,c);
+ if(isastchar(c))
+ {
+ mode = c;
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line)
+ {
+ v = getdolarg(mp->shp,1,(int*)0);
+ dolmax = MAX_ARGN;
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ dolmax = mp->shp->st.dolc+1;
+ mp->atmode = (v && mp->quoted && c=='@');
+ dolg = (v!=0);
+ }
+ break;
+ case S_LBRA:
+ if(type)
+ goto nosub;
+ type = M_BRACE;
+ goto retry1;
+ case S_PAR:
+ if(type)
+ goto nosub;
+ comsubst(mp,(Shnode_t*)0,1);
+ return(1);
+ case S_DIG:
+ var = 0;
+ c -= '0';
+ mp->shp->argaddr = 0;
+ if(type)
+ {
+ register int d;
+ while((d=fcget()),isadigit(d))
+ c = 10*c + (d-'0');
+ fcseek(-1);
+ }
+ idnum = c;
+ if(c==0)
+ v = special(mp->shp,c);
+#if SHOPT_FILESCAN
+ else if(mp->shp->cur_line)
+ {
+ mp->shp->used_pos = 1;
+ v = getdolarg(mp->shp,c,&vsize);
+ }
+#endif /* SHOPT_FILESCAN */
+ else if(c <= mp->shp->st.dolc)
+ {
+ mp->shp->used_pos = 1;
+ v = mp->shp->st.dolv[c];
+ }
+ else
+ v = 0;
+ break;
+ case S_ALP:
+ if(c=='.' && type==0)
+ goto nosub;
+ offset = stktell(stkp);
+ do
+ {
+ register int d;
+ np = 0;
+ do
+ {
+ if(LEN==1)
+ sfputc(stkp,c);
+ else
+ sfwrite(stkp,fcseek(0)-LEN,LEN);
+ }
+ while((d=c,(c=fcmbget(&LEN)),isaname(c))||type && c=='.');
+ while(c==LBRACT && (type||mp->arrayok))
+ {
+ mp->shp->argaddr=0;
+ if((c=fcmbget(&LEN),isastchar(c)) && fcpeek(0)==RBRACT && d!='.')
+ {
+ if(type==M_VNAME)
+ type = M_SUBNAME;
+ idbuff[0] = mode = c;
+ fcget();
+ c = fcmbget(&LEN);
+ if(c=='.' || c==LBRACT)
+ {
+ sfputc(stkp,LBRACT);
+ sfputc(stkp,mode);
+ sfputc(stkp,RBRACT);
+ }
+ else
+ flag = NV_ARRAY;
+ break;
+ }
+ else
+ {
+ fcseek(-LEN);
+ c = stktell(stkp);
+ if(d!='.')
+ sfputc(stkp,LBRACT);
+ v = stkptr(stkp,subcopy(mp,1));
+ if(type && mp->dotdot)
+ {
+ mode = '@';
+ v[-1] = 0;
+ if(type==M_VNAME)
+ type = M_SUBNAME;
+ else if(type==M_SIZE)
+ goto nosub;
+ }
+ else if(d!='.')
+ sfputc(stkp,RBRACT);
+ c = fcmbget(&LEN);
+ if(c==0 && type==M_VNAME)
+ type = M_SUBNAME;
+ }
+ }
+ }
+ while(type && c=='.');
+ if(type!=M_VNAME && c==RBRACE && type && fcpeek(-2)=='.')
+ {
+ /* ${x.} or ${x..} */
+ if(fcpeek(-3) == '.')
+ {
+ stkseek(stkp,stktell(stkp)-2);
+ nv_local = 1;
+ }
+ else
+ {
+ stkseek(stkp,stktell(stkp)-1);
+ type = M_TREE;
+ }
+ }
+ sfputc(stkp,0);
+ id=stkptr(stkp,offset);
+ if(isastchar(c) && type)
+ {
+ if(type==M_VNAME || type==M_SIZE)
+ {
+ idbuff[0] = mode = c;
+ if((d=fcpeek(0))==c)
+ idbuff[1] = fcget();
+ if(type==M_VNAME)
+ type = M_NAMESCAN;
+ else
+ type = M_NAMECOUNT;
+ break;
+ }
+ goto nosub;
+ }
+ flag |= NV_NOASSIGN|NV_VARNAME|NV_NOADD;
+ if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?')))
+ {
+ if(c=='=' || (c==':' && d=='='))
+ flag |= NV_ASSIGN;
+ flag &= ~NV_NOADD;
+ }
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line && *id=='R' && strcmp(id,"REPLY")==0)
+ {
+ mp->shp->argaddr=0;
+ np = REPLYNOD;
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ {
+ if(mp->shp->argaddr)
+ flag &= ~NV_NOADD;
+ np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL);
+ if(!np)
+ {
+ sfprintf(mp->shp->strbuf,"%s%c",id,0);
+ id = sfstruse(mp->shp->strbuf);
+ }
+ }
+ if(isastchar(mode))
+ var = 0;
+ if((!np || nv_isnull(np)) && type==M_BRACE && c==RBRACE && !(flag&NV_ARRAY) && strchr(id,'.'))
+ {
+ if(sh_macfun(mp->shp,id,offset))
+ {
+ fcmbget(&LEN);
+ return(1);
+ }
+ }
+ if(np && (flag&NV_NOADD) && nv_isnull(np))
+ {
+ if(nv_isattr(np,NV_NOFREE))
+ nv_offattr(np,NV_NOFREE);
+#if SHOPT_FILESCAN
+ else if(np!=REPLYNOD || !mp->shp->cur_line)
+#else
+ else
+#endif /* SHOPT_FILESCAN */
+ np = 0;
+ }
+ ap = np?nv_arrayptr(np):0;
+ if(type)
+ {
+ if(mp->dotdot)
+ {
+ Namval_t *nq;
+#if SHOPT_FIXEDARRAY
+ if(ap && !ap->fixed && (nq=nv_opensub(np)))
+#else
+ if(ap && (nq=nv_opensub(np)))
+#endif /* SHOPT_FIXEDARRAY */
+ ap = nv_arrayptr(np=nq);
+ if(ap)
+ {
+ nv_putsub(np,v,ARRAY_SCAN);
+ v = stkptr(stkp,mp->dotdot);
+ dolmax =1;
+ if(array_assoc(ap))
+ arrmax = strdup(v);
+ else if((dolmax = (int)sh_arith(mp->shp,v))<0)
+ dolmax += array_maxindex(np);
+ if(type==M_SUBNAME)
+ bysub = 1;
+ }
+ else
+ {
+ if((int)sh_arith(mp->shp,v))
+ np = 0;
+ }
+ }
+ else if(ap && (isastchar(mode)||type==M_TREE) && !(ap->nelem&ARRAY_SCAN) && type!=M_SIZE)
+ nv_putsub(np,NIL(char*),ARRAY_SCAN);
+ if(!isbracechar(c))
+ goto nosub;
+ else
+ fcseek(-LEN);
+ }
+ else
+ fcseek(-1);
+ if(type<=1 && np && nv_isvtree(np) && mp->pattern==1 && !mp->split)
+ {
+ int cc=fcmbget(&LEN),peek=LEN;
+ if(type && cc=='}')
+ {
+ cc = fcmbget(&LEN);
+ peek++;
+ }
+ if(mp->quote && cc=='"')
+ {
+ cc = fcmbget(&LEN);
+ peek++;
+ }
+ fcseek(-peek);
+ if(cc==0)
+ mp->assign = 1;
+ }
+ if((type==M_VNAME||type==M_SUBNAME) && mp->shp->argaddr && strcmp(nv_name(np),id))
+ mp->shp->argaddr = 0;
+ c = (type>M_BRACE && isastchar(mode));
+ if(np && (type==M_TREE || !c || !ap))
+ {
+ char *savptr;
+ c = *((unsigned char*)stkptr(stkp,offset-1));
+ savptr = stkfreeze(stkp,0);
+ if(type==M_VNAME || (type==M_SUBNAME && ap))
+ {
+ type = M_BRACE;
+ v = nv_name(np);
+ if(ap && !mp->dotdot && !(ap->nelem&ARRAY_UNDEF))
+ addsub = 1;
+ }
+#ifdef SHOPT_TYPEDEF
+ else if(type==M_TYPE)
+ {
+ Namval_t *nq = nv_type(np);
+ type = M_BRACE;
+ if(nq)
+ nv_typename(nq,mp->shp->strbuf);
+ else
+ nv_attribute(np,mp->shp->strbuf,"typeset",1);
+ v = sfstruse(mp->shp->strbuf);
+ }
+#endif /* SHOPT_TYPEDEF */
+#if SHOPT_FILESCAN
+ else if(mp->shp->cur_line && np==REPLYNOD)
+ v = mp->shp->cur_line;
+#endif /* SHOPT_FILESCAN */
+ else if(type==M_TREE)
+ v = nv_getvtree(np,(Namfun_t*)0);
+ else
+ {
+ if(type && fcpeek(0)=='+')
+ {
+ if(ap)
+ v = nv_arrayisset(np,ap)?(char*)"x":0;
+ else
+ v = nv_isnull(np)?0:(char*)"x";
+ }
+ else
+ v = nv_getval(np);
+ mp->atmode = (v && mp->quoted && mode=='@');
+ /* special case --- ignore leading zeros */
+ if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && !nv_isattr(np,NV_INTEGER) && (offset==0 || !isalnum(c)))
+ mp->zeros = 1;
+ }
+ if(savptr==stakptr(0))
+ stkseek(stkp,offset);
+ else
+ stkset(stkp,savptr,offset);
+ }
+ else
+ {
+ if(sh_isoption(SH_NOUNSET) && !isastchar(mode) && (type==M_VNAME || type==M_SIZE))
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ v = 0;
+ if(type==M_VNAME)
+ {
+ v = id;
+ type = M_BRACE;
+ }
+ else if(type==M_TYPE)
+ type = M_BRACE;
+ }
+ stkseek(stkp,offset);
+ if(ap)
+ {
+#if SHOPT_OPTIMIZE
+ if(mp->shp->argaddr)
+ nv_optimize(np);
+#endif
+ if(isastchar(mode) && array_elem(ap)> !c)
+ dolg = -1;
+ else
+ dolg = 0;
+ }
+ break;
+ case S_EOF:
+ fcseek(-1);
+ default:
+ goto nosub;
+ }
+ c = fcmbget(&LEN);
+ if(type>M_TREE)
+ {
+ if(c!=RBRACE)
+ mac_error(np);
+ if(type==M_NAMESCAN || type==M_NAMECOUNT)
+ {
+ mp->shp->last_root = mp->shp->var_tree;
+ id = idx = prefix(mp->shp,id);
+ stkseek(stkp,offset);
+ if(type==M_NAMECOUNT)
+ {
+ c = namecount(mp,id);
+ v = ltos(c);
+ }
+ else
+ {
+ dolmax = strlen(id);
+ dolg = -1;
+ nextname(mp,id,0);
+ v = nextname(mp,id,dolmax);
+ }
+ }
+ else if(type==M_SUBNAME)
+ {
+ if(dolg<0)
+ {
+ v = nv_getsub(np);
+ bysub=1;
+ }
+ else if(v)
+ {
+ if(!ap || isastchar(mode))
+ v = "0";
+ else
+ v = nv_getsub(np);
+ }
+ }
+ else
+ {
+ if(!isastchar(mode))
+ c = charlen(v,vsize);
+ else if(dolg>0)
+ {
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line)
+ {
+ getdolarg(mp->shp,MAX_ARGN,(int*)0);
+ c = mp->shp->offsets[0];
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ c = mp->shp->st.dolc;
+ }
+ else if(dolg<0)
+ c = array_elem(ap);
+ else
+ c = (v!=0);
+ dolg = dolmax = 0;
+ v = ltos(c);
+ }
+ c = RBRACE;
+ }
+ nulflg = 0;
+ if(type && c==':')
+ {
+ c = fcmbget(&LEN);
+ if(isascii(c) &&sh_lexstates[ST_BRACE][c]==S_MOD1 && c!='*' && c!= ':')
+ nulflg=1;
+ else if(c!='%' && c!='#')
+ {
+ fcseek(-LEN);
+ c = ':';
+ }
+ }
+ if(type)
+ {
+ if(!isbracechar(c))
+ {
+ if(!nulflg)
+ mac_error(np);
+ fcseek(-LEN);
+ c = ':';
+ }
+ if(c!=RBRACE)
+ {
+ int newops = (c=='#' || c == '%' || c=='/');
+ offset = stktell(stkp);
+ if(newops && sh_isoption(SH_NOUNSET) && *id && id!=idbuff && (!np || nv_isnull(np)))
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%')))
+ {
+ int newquote = mp->quote;
+ int split = mp->split;
+ int quoted = mp->quoted;
+ int arith = mp->arith;
+ int zeros = mp->zeros;
+ int assign = mp->assign;
+ if(newops)
+ {
+ type = fcget();
+ if(type=='%' || type=='#')
+ {
+ int d = fcmbget(&LEN);
+ fcseek(-LEN);
+ if(d=='(')
+ type = 0;
+ }
+ fcseek(-1);
+ mp->pattern = 1+(c=='/');
+ mp->split = 0;
+ mp->quoted = 0;
+ mp->assign &= ~1;
+ mp->arith = mp->zeros = 0;
+ newquote = 0;
+ }
+ else if(c=='?' || c=='=')
+ mp->split = mp->pattern = 0;
+ copyto(mp,RBRACE,newquote);
+ if(!oldpat)
+ mp->patfound = 0;
+ mp->pattern = oldpat;
+ mp->split = split;
+ mp->quoted = quoted;
+ mp->arith = arith;
+ mp->zeros = zeros;
+ mp->assign = assign;
+ /* add null byte */
+ sfputc(stkp,0);
+ stkseek(stkp,stktell(stkp)-1);
+ }
+ else
+ {
+ sh_lexskip(lp,RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED);
+ stkseek(stkp,offset);
+ }
+ argp=stkptr(stkp,offset);
+ }
+ }
+ else
+ {
+ fcseek(-1);
+ c=0;
+ }
+ if(c==':') /* ${name:expr1[:expr2]} */
+ {
+ char *ptr;
+ type = (int)sh_strnum(argp,&ptr,1);
+ if(isastchar(mode))
+ {
+ if(id==idbuff) /* ${@} or ${*} */
+ {
+ if(type<0 && (type+= dolmax)<0)
+ type = 0;
+ if(type==0)
+ v = special(mp->shp,dolg=0);
+#if SHOPT_FILESCAN
+ else if(mp->shp->cur_line)
+ {
+ v = getdolarg(mp->shp,dolg=type,&vsize);
+ if(!v)
+ dolmax = type;
+ }
+#endif /* SHOPT_FILESCAN */
+ else if(type < dolmax)
+ v = mp->shp->st.dolv[dolg=type];
+ else
+ v = 0;
+ }
+ else if(ap)
+ {
+ if(type<0)
+ {
+ if(array_assoc(ap))
+ type = -type;
+ else
+ type += array_maxindex(np);
+ }
+ if(array_assoc(ap))
+ {
+ while(type-- >0 && (v=0,nv_nextsub(np)))
+ v = nv_getval(np);
+ }
+ else if(type > 0)
+ {
+ if(nv_putsub(np,NIL(char*),type|ARRAY_SCAN))
+ v = nv_getval(np);
+ else
+ v = 0;
+ }
+ }
+ else if(type>0)
+ v = 0;
+ if(!v)
+ mp->atmode = 0;
+ }
+ else if(v)
+ {
+ vsize = charlen(v,vsize);
+ if(type<0 && (type += vsize)<0)
+ type = 0;
+ if(vsize < type)
+ v = 0;
+#if SHOPT_MULTIBYTE
+ else if(mbwide())
+ {
+ mbinit();
+ for(c=type;c;c--)
+ mbchar(v);
+ c = ':';
+ }
+#endif /* SHOPT_MULTIBYTE */
+ else
+ v += type;
+ vsize = v?strlen(v):0;
+ }
+ if(*ptr==':')
+ {
+ if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0)
+ v = 0;
+ else if(isastchar(mode))
+ {
+ if(dolg>=0)
+ {
+ if(dolg+type < dolmax)
+ dolmax = dolg+type;
+ }
+ else
+ dolmax = type;
+ }
+ else if(type < vsize)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ char *vp = v;
+ mbinit();
+ while(type-->0)
+ {
+ if((c=mbsize(vp))<1)
+ c = 1;
+ vp += c;
+ }
+ type = vp-v;
+ c = ':';
+ }
+#endif /* SHOPT_MULTIBYTE */
+ vsize = type;
+ }
+ else
+ vsize = v?strlen(v):0;
+ }
+ if(*ptr)
+ mac_error(np);
+ stkseek(stkp,offset);
+ argp = 0;
+ }
+ /* check for substring operations */
+ else if(c == '#' || c == '%' || c=='/')
+ {
+ if(c=='/')
+ {
+ if(type=='/' || type=='#' || type=='%')
+ {
+ c = type;
+ type = '/';
+ argp++;
+ }
+ else
+ type = 0;
+ }
+ else
+ {
+ if(type==c) /* ## or %% */
+ argp++;
+ else
+ type = 0;
+ }
+ pattern = strdup(argp);
+ if((type=='/' || c=='/') && (repstr = mac_getstring(pattern)))
+ {
+ Mac_t savemac;
+ char *first = fcseek(0);
+ int n = stktell(stkp);
+ savemac = *mp;
+ fcsopen(repstr);
+ mp->pattern = 3;
+ mp->split = 0;
+ copyto(mp,0,0);
+ sfputc(stkp,0);
+ repstr = strdup(stkptr(stkp,n));
+ replen = strlen(repstr);
+ stkseek(stkp,n);
+ *mp = savemac;
+ fcsopen(first);
+ }
+ if(v || c=='/' && offset>=0)
+ stkseek(stkp,offset);
+ }
+ /* check for quoted @ */
+ if(mode=='@' && mp->quote && !v && c!='-')
+ mp->quoted-=2;
+retry2:
+ if(v && (!nulflg || *v ) && c!='+')
+ {
+ register int d = (mode=='@'?' ':mp->ifs);
+ int match[2*(MATCH_MAX+1)], nmatch, nmatch_prev, vsize_last;
+ char *vlast;
+ while(1)
+ {
+ if(!v)
+ v= "";
+ if(c=='/' || c=='#' || c== '%')
+ {
+ int index = 0;
+ flag = (type || c=='/')?(STR_GROUP|STR_MAXIMAL):STR_GROUP;
+ if(c!='/')
+ flag |= STR_LEFT;
+ nmatch = 0;
+ while(1)
+ {
+ vsize = strlen(v);
+ nmatch_prev = nmatch;
+ if(c=='%')
+ nmatch=substring(v,pattern,match,flag&STR_MAXIMAL);
+ else
+ nmatch=strgrpmatch(v,pattern,match,elementsof(match)/2,flag);
+ if(nmatch && replen>0)
+ sh_setmatch(mp->shp,v,vsize,nmatch,match,index++);
+ if(nmatch)
+ {
+ vlast = v;
+ vsize_last = vsize;
+ vsize = match[0];
+ }
+ else if(c=='#')
+ vsize = 0;
+ if(vsize)
+ mac_copy(mp,v,vsize);
+ if(nmatch && replen>0 && (match[1] || !nmatch_prev))
+ mac_substitute(mp,repstr,v,match,nmatch);
+ if(nmatch==0)
+ v += vsize;
+ else
+ v += match[1];
+ if(*v && c=='/' && type)
+ {
+ /* avoid infinite loop */
+ if(nmatch && match[1]==0)
+ {
+ nmatch = 0;
+ mac_copy(mp,v,1);
+ v++;
+ }
+ continue;
+ }
+ vsize = -1;
+ break;
+ }
+ if(replen==0)
+ sh_setmatch(mp->shp,vlast,vsize_last,nmatch,match,index++);
+ }
+ if(vsize)
+ mac_copy(mp,v,vsize>0?vsize:strlen(v));
+ if(addsub)
+ {
+ mp->shp->instance++;
+ sfprintf(mp->shp->strbuf,"[%s]",nv_getsub(np));
+ mp->shp->instance--;
+ v = sfstruse(mp->shp->strbuf);
+ mac_copy(mp, v, strlen(v));
+ }
+ if(dolg==0 && dolmax==0)
+ break;
+ if(mp->dotdot)
+ {
+ if(nv_nextsub(np) == 0)
+ break;
+ if(bysub)
+ v = nv_getsub(np);
+ else
+ v = nv_getval(np);
+ if(array_assoc(ap))
+ {
+ if(strcmp(bysub?v:nv_getsub(np),arrmax)>0)
+ break;
+ }
+ else
+ {
+ if(nv_aindex(np) > dolmax)
+ break;
+ }
+ }
+ else if(dolg>=0)
+ {
+ if(++dolg >= dolmax)
+ break;
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line)
+ {
+ if(dolmax==MAX_ARGN && isastchar(mode))
+ break;
+ if(!(v=getdolarg(mp->shp,dolg,&vsize)))
+ {
+ dolmax = dolg;
+ break;
+ }
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ v = mp->shp->st.dolv[dolg];
+ }
+ else if(!np)
+ {
+ if(!(v = nextname(mp,id,dolmax)))
+ break;
+ }
+ else
+ {
+ if(dolmax && --dolmax <=0)
+ {
+ nv_putsub(np,NIL(char*),ARRAY_UNDEF);
+ break;
+ }
+ if(ap)
+ ap->nelem |= ARRAY_SCAN;
+ if(nv_nextsub(np) == 0)
+ break;
+ if(bysub)
+ v = nv_getsub(np);
+ else
+ v = nv_getval(np);
+ }
+ if(mp->split && (!mp->quote || mode=='@'))
+ {
+ if(!np)
+ mp->pattern = 0;
+ endfield(mp,mp->quoted);
+ mp->atmode = mode=='@';
+ mp->pattern = oldpat;
+ }
+ else if(d)
+ {
+ if(mp->sp)
+ sfputc(mp->sp,d);
+ else
+ sfputc(stkp,d);
+ }
+ }
+ if(arrmax)
+ free((void*)arrmax);
+ }
+ else if(argp)
+ {
+ if(c=='/' && replen>0 && pattern && strmatch("",pattern))
+ mac_substitute(mp,repstr,v,0,0);
+ if(c=='?')
+ {
+ if(np)
+ id = nv_name(np);
+ else if(idnum)
+ id = ltos(idnum);
+ if(*argp)
+ {
+ sfputc(stkp,0);
+ errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp);
+ }
+ else if(v)
+ errormsg(SH_DICT,ERROR_exit(1),e_nullset,id);
+ else
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ }
+ else if(c=='=')
+ {
+ if(np)
+ {
+ if(mp->shp->subshell)
+ np = sh_assignok(np,1);
+ nv_putval(np,argp,0);
+ v = nv_getval(np);
+ nulflg = 0;
+ stkseek(stkp,offset);
+ goto retry2;
+ }
+ else
+ mac_error(np);
+ }
+ }
+ else if(var && sh_isoption(SH_NOUNSET) && type<=M_TREE && (!np || nv_isnull(np) || (nv_isarray(np) && !np->nvalue.cp)))
+ {
+ if(np)
+ {
+ if(nv_isarray(np))
+ {
+ sfprintf(mp->shp->strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np));
+ id = sfstruse(mp->shp->strbuf);
+ }
+ else
+ id = nv_name(np);
+ nv_close(np);
+ }
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ }
+ if(np)
+ nv_close(np);
+ if(pattern)
+ free(pattern);
+ if(repstr)
+ free(repstr);
+ if(idx)
+ free(idx);
+ return(1);
+nosub:
+ if(type==M_BRACE && sh_lexstates[ST_NORM][c]==S_BREAK)
+ {
+ fcseek(-1);
+ comsubst(mp,(Shnode_t*)0,2);
+ return(1);
+ }
+ if(type)
+ mac_error(np);
+ fcseek(-1);
+ nv_close(np);
+ return(0);
+}
+
+/*
+ * This routine handles command substitution
+ * <type> is 0 for older `...` version
+ */
+static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
+{
+ Sfdouble_t num;
+ register int c;
+ register char *str;
+ Sfio_t *sp;
+ Stk_t *stkp = mp->shp->stk;
+ Fcin_t save;
+ struct slnod *saveslp = mp->shp->st.staklist;
+ struct _mac_ savemac;
+ int savtop = stktell(stkp);
+ char lastc=0, *savptr = stkfreeze(stkp,0);
+#if SHOPT_MULTIBYTE
+ wchar_t lastw=0;
+#endif /* SHOPT_MULTIBYTE */
+ int was_history = sh_isstate(SH_HISTORY);
+ int was_verbose = sh_isstate(SH_VERBOSE);
+ int was_interactive = sh_isstate(SH_INTERACTIVE);
+ int newlines,bufsize,nextnewlines;
+ Namval_t *np;
+ mp->shp->argaddr = 0;
+ savemac = *mp;
+ mp->shp->st.staklist=0;
+#ifdef SHOPT_COSHELL
+ if(mp->shp->inpool)
+ return;
+#endif /*SHOPT_COSHELL */
+ if(type)
+ {
+ sp = 0;
+ fcseek(-1);
+ if(!t)
+ t = sh_dolparen((Lex_t*)mp->shp->lex_context);
+ if(t && t->tre.tretyp==TARITH)
+ {
+ mp->shp->inarith = 1;
+ fcsave(&save);
+ if(t->ar.arcomp)
+ num = arith_exec(t->ar.arcomp);
+ else if((t->ar.arexpr->argflag&ARG_RAW))
+ num = sh_arith(mp->shp,t->ar.arexpr->argval);
+ else
+ num = sh_arith(mp->shp,sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
+ mp->shp->inarith = 0;
+ out_offset:
+ stkset(stkp,savptr,savtop);
+ *mp = savemac;
+ if((Sflong_t)num!=num)
+ sfprintf(mp->shp->strbuf,"%.*Lg",LDBL_DIG,num);
+ else if(num)
+ sfprintf(mp->shp->strbuf,"%lld",(Sflong_t)num);
+ else
+ sfprintf(mp->shp->strbuf,"%Lg",num);
+ str = sfstruse(mp->shp->strbuf);
+ mac_copy(mp,str,strlen(str));
+ mp->shp->st.staklist = saveslp;
+ fcrestore(&save);
+ return;
+ }
+ }
+ else
+ {
+ while(fcgetc(c)!='`' && c)
+ {
+ if(c==ESCAPE)
+ {
+ fcgetc(c);
+ if(!(isescchar(sh_lexstates[ST_QUOTE][c]) ||
+ (c=='"' && mp->quote)))
+ sfputc(stkp,ESCAPE);
+ }
+ sfputc(stkp,c);
+ }
+ c = stktell(stkp);
+ str=stkfreeze(stkp,1);
+ /* disable verbose and don't save in history file */
+ sh_offstate(SH_HISTORY);
+ sh_offstate(SH_VERBOSE);
+ if(mp->sp)
+ sfsync(mp->sp); /* flush before executing command */
+ sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ);
+ c = mp->shp->inlineno;
+ mp->shp->inlineno = error_info.line+mp->shp->st.firstline;
+ t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL);
+ mp->shp->inlineno = c;
+ type = 1;
+ }
+#if KSHELL
+ if(t)
+ {
+ fcsave(&save);
+ sfclose(sp);
+ if(t->tre.tretyp==0 && !t->com.comarg && !t->com.comset)
+ {
+ /* special case $(<file) and $(<#file) */
+ register int fd;
+ int r;
+ struct checkpt buff;
+ struct ionod *ip=0;
+ sh_pushcontext(mp->shp,&buff,SH_JMPIO);
+ if((ip=t->tre.treio) &&
+ ((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) &&
+ (r=sigsetjmp(buff.buff,0))==0)
+ fd = sh_redirect(mp->shp,ip,3);
+ else
+ fd = sh_chkopen(e_devnull);
+ sh_popcontext(mp->shp,&buff);
+ if(r==0 && ip && (ip->iofile&IOLSEEK))
+ {
+ if(sp=mp->shp->sftable[fd])
+ num = sftell(sp);
+ else
+ num = lseek(fd, (off_t)0, SEEK_CUR);
+ goto out_offset;
+ }
+ sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC);
+ type = 3;
+ }
+ else
+ sp = sh_subshell(mp->shp,t,sh_isstate(SH_ERREXIT),type);
+ fcrestore(&save);
+ }
+ else
+ sp = sfopen(NIL(Sfio_t*),"","sr");
+ sh_freeup(mp->shp);
+ mp->shp->st.staklist = saveslp;
+ if(was_history)
+ sh_onstate(SH_HISTORY);
+ if(was_verbose)
+ sh_onstate(SH_VERBOSE);
+#else
+ sp = sfpopen(NIL(Sfio_t*),str,"r");
+#endif
+ *mp = savemac;
+ np = sh_scoped(mp->shp,IFSNOD);
+ nv_putval(np,mp->ifsp,NV_RDONLY);
+ mp->ifsp = nv_getval(np);
+ stkset(stkp,savptr,savtop);
+ newlines = 0;
+ sfsetbuf(sp,(void*)sp,0);
+ bufsize = sfvalue(sp);
+ /* read command substitution output and put on stack or here-doc */
+ sfpool(sp, NIL(Sfio_t*), SF_WRITE);
+ sh_offstate(SH_INTERACTIVE);
+ while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c=bufsize=sfvalue(sp))>0)
+ {
+#if SHOPT_CRNL
+ /* eliminate <cr> */
+ register char *dp;
+ char *buff = str;
+ while(c>1 && (*str !='\r'|| str[1]!='\n'))
+ {
+ c--;
+ str++;
+ }
+ dp = str;
+ while(c>1)
+ {
+ str++;
+ c--;
+ while(c>1 && (*str!='\r' || str[1]!='\n'))
+ {
+ c--;
+ *dp++ = *str++;
+ }
+ }
+ if(c)
+ *dp++ = *str++;
+ str = buff;
+ c = dp-str;
+#endif /* SHOPT_CRNL */
+ /* delay appending trailing new-lines */
+ for(nextnewlines=0; c-->0 && str[c]=='\n'; nextnewlines++);
+ if(c < 0)
+ {
+ newlines += nextnewlines;
+ continue;
+ }
+ if(newlines >0)
+ {
+ if(mp->sp)
+ sfnputc(mp->sp,'\n',newlines);
+ else if(!mp->quote && mp->split && mp->shp->ifstable['\n'])
+ endfield(mp,0);
+ else
+ sfnputc(stkp,'\n',newlines);
+ }
+ else if(lastc)
+ {
+#if SHOPT_MULTIBYTE
+ if(lastw)
+ {
+ int n;
+ char mb[8];
+ n = mbconv(mb, lastw);
+ mac_copy(mp,mb,n);
+ lastw = 0;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ mac_copy(mp,&lastc,1);
+ lastc = 0;
+ }
+ newlines = nextnewlines;
+ if(++c < bufsize)
+ str[c] = 0;
+ else
+ {
+ ssize_t len = 1;
+
+ /* can't write past buffer so save last character */
+#if SHOPT_MULTIBYTE
+ if ((len = mbsize(str))>1)
+ {
+ len = mb2wc(lastw,str,len);
+ if (len < 0)
+ {
+ lastw = 0;
+ len = 1;
+ }
+ }
+#endif /* SHOPT_MULTIBYTE */
+ c -= len;
+ lastc = str[c];
+ str[c] = 0;
+ }
+ mac_copy(mp,str,c);
+ }
+ if(was_interactive)
+ sh_onstate(SH_INTERACTIVE);
+ if(--newlines>0 && mp->shp->ifstable['\n']==S_DELIM)
+ {
+ if(mp->sp)
+ sfnputc(mp->sp,'\n',newlines);
+ else if(!mp->quote && mp->split)
+ while(newlines--)
+ endfield(mp,1);
+ else
+ sfnputc(stkp,'\n',newlines);
+ }
+ if(lastc)
+ {
+#if SHOPT_MULTIBYTE
+ if(lastw)
+ {
+ int n;
+ char mb[8];
+ n = mbconv(mb, lastw);
+ mac_copy(mp,mb,n);
+ lastw = 0;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ mac_copy(mp,&lastc,1);
+ lastc = 0;
+ }
+ sfclose(sp);
+ return;
+}
+
+/*
+ * copy <str> onto the stack
+ */
+static void mac_copy(register Mac_t *mp,register const char *str, register int size)
+{
+ register char *state;
+ register const char *cp=str;
+ register int c,n,nopat,len;
+ Stk_t *stkp=mp->shp->stk;
+ int oldpat = mp->pattern;
+ nopat = (mp->quote||(mp->assign==1)||mp->arith);
+ if(mp->zeros)
+ {
+ /* prevent leading 0's from becomming octal constants */
+ while(size>1 && *str=='0')
+ str++,size--;
+ mp->zeros = 0;
+ cp = str;
+ }
+ if(mp->sp)
+ sfwrite(mp->sp,str,size);
+ else if(mp->pattern>=2 || (mp->pattern && nopat) || mp->assign==3)
+ {
+ state = sh_lexstates[ST_MACRO];
+ /* insert \ before file expansion characters */
+ while(size-->0)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide() && (len=mbsize(cp))>1)
+ {
+ cp += len;
+ size -= (len-1);
+ continue;
+ }
+#endif
+ c = state[n= *(unsigned char*)cp++];
+ if(mp->assign==3 && mp->pattern!=4)
+ {
+ if(c==S_BRACT)
+ {
+ nopat = 0;
+ mp->pattern = 4;
+ }
+ continue;
+ }
+ if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3)
+ c=1;
+ else if(mp->pattern==4 && (c==S_ESC||c==S_BRACT||c==S_ENDCH || isastchar(n)))
+ {
+ if(c==S_ENDCH && oldpat!=4)
+ {
+ if(*cp==0 || *cp=='.' || *cp=='[')
+ {
+ mp->pattern = oldpat;
+ c=0;
+ }
+ else
+ c=1;
+ }
+ else
+ c=1;
+ }
+ else if(mp->pattern==2 && c==S_SLASH)
+ c=1;
+ else if(mp->pattern==3 && c==S_ESC && (state[*(unsigned char*)cp]==S_DIG||(*cp==ESCAPE)))
+ {
+ if(!(c=mp->quote))
+ cp++;
+ }
+ else
+ c=0;
+ if(c)
+ {
+ if(c = (cp-1) - str)
+ sfwrite(stkp,str,c);
+ sfputc(stkp,ESCAPE);
+ str = cp-1;
+ }
+ }
+ if(c = cp-str)
+ sfwrite(stkp,str,c);
+ }
+ else if(!mp->quote && mp->split && (mp->ifs||mp->pattern))
+ {
+ /* split words at ifs characters */
+ state = mp->shp->ifstable;
+ if(mp->pattern)
+ {
+ char *sp = "&|()";
+ while(c = *sp++)
+ {
+ if(state[c]==0)
+ state[c] = S_EPAT;
+ }
+ sp = "*?[{";
+ while(c = *sp++)
+ {
+ if(state[c]==0)
+ state[c] = S_PAT;
+ }
+ if(state[ESCAPE]==0)
+ state[ESCAPE] = S_ESC;
+ }
+ while(size-->0)
+ {
+ n=state[c= *(unsigned char*)cp++];
+#if SHOPT_MULTIBYTE
+ if(mbwide() && n!=S_MBYTE && (len=mbsize(cp-1))>1)
+ {
+ sfwrite(stkp,cp-1, len);
+ cp += --len;
+ size -= len;
+ continue;
+ }
+#endif
+ if(n==S_ESC || n==S_EPAT)
+ {
+ /* don't allow extended patterns in this case */
+ mp->patfound = mp->pattern;
+ sfputc(stkp,ESCAPE);
+ }
+ else if(n==S_PAT)
+ mp->patfound = mp->pattern;
+ else if(n && mp->ifs)
+ {
+#if SHOPT_MULTIBYTE
+ if(n==S_MBYTE)
+ {
+ if(sh_strchr(mp->ifsp,cp-1)<0)
+ continue;
+ n = mbsize(cp-1) - 1;
+ if(n==-2)
+ n = 0;
+ cp += n;
+ size -= n;
+ n= S_DELIM;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ if(n==S_SPACE || n==S_NL)
+ {
+ while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
+ size--;
+#if SHOPT_MULTIBYTE
+ if(n==S_MBYTE && sh_strchr(mp->ifsp,cp-1)>=0)
+ {
+ n = mbsize(cp-1) - 1;
+ if(n==-2)
+ n = 0;
+ cp += n;
+ size -= n;
+ n=S_DELIM;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ if(n==S_DELIM)
+ size--;
+ }
+ endfield(mp,n==S_DELIM||mp->quoted);
+ mp->patfound = 0;
+ if(n==S_DELIM)
+ while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
+ size--;
+ if(size<=0)
+ break;
+ cp--;
+ continue;
+
+ }
+ sfputc(stkp,c);
+ }
+ if(mp->pattern)
+ {
+ cp = "&|()";
+ while(c = *cp++)
+ {
+ if(state[c]==S_EPAT)
+ state[c] = 0;
+ }
+ cp = "*?[{";
+ while(c = *cp++)
+ {
+ if(state[c]==S_PAT)
+ state[c] = 0;
+ }
+ if(mp->shp->ifstable[ESCAPE]==S_ESC)
+ mp->shp->ifstable[ESCAPE] = 0;
+ }
+ }
+ else
+ sfwrite(stkp,str,size);
+}
+
+/*
+ * Terminate field.
+ * If field is null count field if <split> is non-zero
+ * Do filename expansion of required
+ */
+static void endfield(register Mac_t *mp,int split)
+{
+ register struct argnod *argp;
+ register int count=0;
+ Stk_t *stkp = mp->shp->stk;
+ if(stktell(stkp) > ARGVAL || split)
+ {
+ argp = (struct argnod*)stkfreeze(stkp,1);
+ argp->argnxt.cp = 0;
+ argp->argflag = 0;
+ mp->atmode = 0;
+ if(mp->patfound)
+ {
+ mp->shp->argaddr = 0;
+#if SHOPT_BRACEPAT
+ count = path_generate(mp->shp,argp,mp->arghead);
+#else
+ count = path_expand(mp->shp,argp->argval,mp->arghead);
+#endif /* SHOPT_BRACEPAT */
+ if(count)
+ mp->fields += count;
+ else if(split) /* pattern is null string */
+ *argp->argval = 0;
+ else /* pattern expands to nothing */
+ count = -1;
+ }
+ if(count==0)
+ {
+ argp->argchn.ap = *mp->arghead;
+ *mp->arghead = argp;
+ mp->fields++;
+ }
+ if(count>=0)
+ {
+ (*mp->arghead)->argflag |= ARG_MAKE;
+ if(mp->assign || sh_isoption(SH_NOGLOB))
+ argp->argflag |= ARG_RAW|ARG_EXP;
+ }
+ stkseek(stkp,ARGVAL);
+ }
+ mp->quoted = mp->quote;
+}
+
+/*
+ * Finds the right substring of STRING using the expression PAT
+ * the longest substring is found when FLAG is set.
+ */
+static int substring(register const char *string,const char *pat,int match[], int flag)
+{
+ register const char *sp=string;
+ register int size,len,nmatch,n;
+ int smatch[2*(MATCH_MAX+1)];
+ if(flag)
+ {
+ if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_MAXIMAL))
+ {
+ memcpy(match,smatch,n*2*sizeof(smatch[0]));
+ return(n);
+ }
+ return(0);
+ }
+ size = len = strlen(sp);
+ sp += size;
+ while(sp>=string)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ sp = lastchar(string,sp);
+#endif /* SHOPT_MULTIBYTE */
+ if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_LEFT|STR_MAXIMAL))
+ {
+ nmatch = n;
+ memcpy(match,smatch,n*2*sizeof(smatch[0]));
+ size = sp-string;
+ break;
+ }
+ sp--;
+ }
+ if(size==len)
+ return(0);
+ if(nmatch)
+ {
+ nmatch *=2;
+ while(--nmatch>=0)
+ match[nmatch] += size;
+ }
+ return(n);
+}
+
+#if SHOPT_MULTIBYTE
+ static char *lastchar(const char *string, const char *endstring)
+ {
+ register char *str = (char*)string;
+ register int c;
+ mbinit();
+ while(*str)
+ {
+ if((c=mbsize(str))<0)
+ c = 1;
+ if(str+c > endstring)
+ break;
+ str += c;
+ }
+ return(str);
+ }
+#endif /* SHOPT_MULTIBYTE */
+static int charlen(const char *string,int len)
+{
+ if(!string)
+ return(0);
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ register const char *str = string, *strmax=string+len;
+ register int n=0;
+ mbinit();
+ if(len>0)
+ {
+ while(str<strmax && mbchar(str))
+ n++;
+ }
+ else while(mbchar(str))
+ n++;
+ return(n);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ {
+ if(len<0)
+ return(strlen(string));
+ return(len);
+ }
+}
+
+/*
+ * This is the default tilde discipline function
+ */
+static int sh_btilde(int argc, char *argv[], Shbltin_t *context)
+{
+ Shell_t *shp = context->shp;
+ char *cp = sh_tilde(shp,argv[1]);
+ NOT_USED(argc);
+ if(!cp)
+ cp = argv[1];
+ sfputr(sfstdout, cp, '\n');
+ return(0);
+}
+
+/*
+ * <offset> is byte offset for beginning of tilde string
+ */
+static void tilde_expand2(Shell_t *shp, register int offset)
+{
+ char shtilde[10], *av[3], *ptr=stkfreeze(shp->stk,1);
+ Sfio_t *iop, *save=sfstdout;
+ Namval_t *np;
+ static int beenhere=0;
+ strcpy(shtilde,".sh.tilde");
+ np = nv_open(shtilde,shp->fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL);
+ if(np && !beenhere)
+ {
+ beenhere = 1;
+ sh_addbuiltin(shtilde,sh_btilde,0);
+ nv_onattr(np,NV_EXPORT);
+ }
+ av[0] = ".sh.tilde";
+ av[1] = &ptr[offset];
+ av[2] = 0;
+ iop = sftmp((IOBSIZE>PATH_MAX?IOBSIZE:PATH_MAX)+1);
+ sfset(iop,SF_READ,0);
+ sfstdout = iop;
+ if(np)
+ sh_fun(np, (Namval_t*)0, av);
+ else
+ sh_btilde(2, av, &shp->bltindata);
+ sfstdout = save;
+ stkset(shp->stk,ptr, offset);
+ sfseek(iop,(Sfoff_t)0,SEEK_SET);
+ sfset(iop,SF_READ,1);
+ if(ptr = sfreserve(iop, SF_UNBOUND, -1))
+ {
+ Sfoff_t n = sfvalue(iop);
+ while(ptr[n-1]=='\n')
+ n--;
+ if(n==1 && fcpeek(0)=='/' && ptr[n-1])
+ n--;
+ if(n)
+ sfwrite(shp->stk,ptr,n);
+ }
+ else
+ sfputr(shp->stk,av[1],0);
+ sfclose(iop);
+}
+
+/*
+ * This routine is used to resolve ~ expansion.
+ * A ~ by itself is replaced with the users login directory.
+ * A ~- is replaced by the previous working directory in shell.
+ * A ~+ is replaced by the present working directory in shell.
+ * If ~name is replaced with login directory of name.
+ * If string doesn't start with ~ or ~... not found then 0 returned.
+ */
+
+static char *sh_tilde(Shell_t *shp,register const char *string)
+{
+ register char *cp;
+ register int c;
+ register struct passwd *pw;
+ register Namval_t *np=0;
+ static Dt_t *logins_tree;
+ if(*string++!='~')
+ return(NIL(char*));
+ if((c = *string)==0)
+ {
+ if(!(cp=nv_getval(sh_scoped(shp,HOME))))
+ cp = getlogin();
+ return(cp);
+ }
+ if((c=='-' || c=='+') && string[1]==0)
+ {
+ if(c=='+')
+ cp = nv_getval(sh_scoped(shp,PWDNOD));
+ else
+ cp = nv_getval(sh_scoped(shp,OLDPWDNOD));
+ return(cp);
+ }
+#if _WINIX
+ if(fcgetc(c)=='/')
+ {
+ char *str;
+ int n=0,offset=staktell();
+ stakputs(string);
+ do
+ {
+ stakputc(c);
+ n++;
+ }
+ while (fcgetc(c) && c!='/');
+ stakputc(0);
+ if(c)
+ fcseek(-1);
+ str = stakseek(offset);
+ Skip = n;
+ if(logins_tree && (np=nv_search(str,logins_tree,0)))
+ return(nv_getval(np));
+ if(pw = getpwnam(str))
+ {
+ string = str;
+ goto skip;
+ }
+ Skip = 0;
+ }
+#endif /* _WINIX */
+ if(logins_tree && (np=nv_search(string,logins_tree,0)))
+ return(nv_getval(np));
+ if(!(pw = getpwnam(string)))
+ return(NIL(char*));
+#if _WINIX
+skip:
+#endif /* _WINIX */
+ if(!logins_tree)
+ logins_tree = dtopen(&_Nvdisc,Dtbag);
+ if(np=nv_search(string,logins_tree,NV_ADD))
+ {
+ c = shp->subshell;
+ shp->subshell = 0;
+ nv_putval(np, pw->pw_dir,0);
+ shp->subshell = c;
+ }
+ return(pw->pw_dir);
+}
+
+/*
+ * return values for special macros
+ */
+static char *special(Shell_t *shp,register int c)
+{
+ if(c!='$')
+ shp->argaddr = 0;
+ switch(c)
+ {
+ case '@':
+ case '*':
+ return(shp->st.dolc>0?shp->st.dolv[1]:NIL(char*));
+ case '#':
+#if SHOPT_FILESCAN
+ if(shp->cur_line)
+ {
+ getdolarg(shp,MAX_ARGN,(int*)0);
+ return(ltos(shp->offsets[0]));
+ }
+#endif /* SHOPT_FILESCAN */
+ return(ltos(shp->st.dolc));
+ case '!':
+ if(shp->bckpid)
+#if SHOPT_COSHELL
+ return(sh_pid2str(shp,shp->bckpid));
+#else
+ return(ltos(shp->bckpid));
+#endif /* SHOPT_COSHELL */
+ break;
+ case '$':
+ if(nv_isnull(SH_DOLLARNOD))
+ return(ltos(shp->gd->pid));
+ return(nv_getval(SH_DOLLARNOD));
+ case '-':
+ return(sh_argdolminus(shp->arg_context));
+ case '?':
+ return(ltos(shp->savexit));
+ case 0:
+ if(sh_isstate(SH_PROFILE) || shp->fn_depth==0 || !shp->st.cmdname)
+ return(shp->shname);
+ else
+ return(shp->st.cmdname);
+ }
+ return(NIL(char*));
+}
+
+/*
+ * Handle macro expansion errors
+ */
+static void mac_error(Namval_t *np)
+{
+ if(np)
+ nv_close(np);
+ errormsg(SH_DICT,ERROR_exit(1),e_subst,fcfirst());
+}
+
+/*
+ * Given pattern/string, replace / with 0 and return pointer to string
+ * \ characters are stripped from string. The \ are stripped in the
+ * replacement string unless followed by a digit or \.
+ */
+static char *mac_getstring(char *pattern)
+{
+ register char *cp=pattern, *rep=0, *dp;
+ register int c;
+ while(c = *cp++)
+ {
+ if(c==ESCAPE && (!rep || (*cp && strchr("&|()[]*?",*cp))))
+ {
+ c = *cp++;
+ }
+ else if(!rep && c=='/')
+ {
+ cp[-1] = 0;
+ rep = dp = cp;
+ continue;
+ }
+ if(rep)
+ *dp++ = c;
+ }
+ if(rep)
+ *dp = 0;
+ return(rep);
+}
diff --git a/src/cmd/ksh93/sh/main.c b/src/cmd/ksh93/sh/main.c
new file mode 100644
index 0000000..85948a6
--- /dev/null
+++ b/src/cmd/ksh93/sh/main.c
@@ -0,0 +1,787 @@
+/***********************************************************************
+* *
+* 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 <ast.h>
+#include <sfio.h>
+#include <stak.h>
+#include <ls.h>
+#include <fcin.h>
+#include "defs.h"
+#include "variables.h"
+#include "path.h"
+#include "io.h"
+#include "jobs.h"
+#include "shlex.h"
+#include "shnodes.h"
+#include "history.h"
+#include "timeout.h"
+#include "FEATURE/time"
+#include "FEATURE/pstat"
+#include "FEATURE/execargs"
+#include "FEATURE/externs"
+#ifdef _hdr_nc
+# include <nc.h>
+#endif /* _hdr_nc */
+
+#define CMD_LENGTH 64
+
+/* These routines are referenced by this module */
+static void exfile(Shell_t*, Sfio_t*,int);
+static void chkmail(Shell_t *shp, char*);
+#if defined(_lib_fork) && !defined(_NEXT_SOURCE)
+ static void fixargs(char**,int);
+#else
+# define fixargs(a,b)
+#endif
+
+#ifndef environ
+ extern char **environ;
+#endif
+
+static struct stat lastmail;
+static time_t mailtime;
+static char beenhere = 0;
+
+#ifdef _lib_sigvec
+ void clearsigmask(register int sig)
+ {
+ struct sigvec vec;
+ if(sigvec(sig,NIL(struct sigvec*),&vec)>=0 && vec.sv_mask)
+ {
+ vec.sv_mask = 0;
+ sigvec(sig,&vec,NIL(struct sigvec*));
+ }
+ }
+#endif /* _lib_sigvec */
+
+#ifdef _lib_fts_notify
+# include <fts.h>
+ /* check for interrupts during tree walks */
+ static int fts_sigcheck(FTS* fp, FTSENT* ep, void* context)
+ {
+ Shell_t *shp = (Shell_t*)context;
+ NOT_USED(fp);
+ NOT_USED(ep);
+ if(shp->trapnote&SH_SIGSET)
+ {
+ errno = EINTR;
+ return(-1);
+ }
+ return(0);
+ }
+#endif /* _lib_fts_notify */
+
+#ifdef PATH_BFPATH
+#define PATHCOMP NIL(Pathcomp_t*)
+#else
+#define PATHCOMP ""
+#endif
+
+/*
+ * search for file and exfile() it if it exists
+ * 1 returned if file found, 0 otherwise
+ */
+
+int sh_source(Shell_t *shp, Sfio_t *iop, const char *file)
+{
+ char* oid;
+ char* nid;
+ int fd;
+
+ if (!file || !*file || (fd = path_open(shp,file, PATHCOMP)) < 0)
+ {
+ REGRESS(source, "sh_source", ("%s:ENOENT", file));
+ return 0;
+ }
+ oid = error_info.id;
+ nid = error_info.id = strdup(file);
+ shp->st.filename = path_fullname(shp,stakptr(PATH_OFFSET));
+ REGRESS(source, "sh_source", ("%s", file));
+ exfile(shp, iop, fd);
+ error_info.id = oid;
+ free(nid);
+ return 1;
+}
+
+#ifdef S_ISSOCK
+#define REMOTE(m) (S_ISSOCK(m)||!(m))
+#else
+#define REMOTE(m) !(m)
+#endif
+
+int sh_main(int ac, char *av[], Shinit_f userinit)
+{
+ register char *name;
+ register int fdin;
+ register Sfio_t *iop;
+ register Shell_t *shp;
+ struct stat statb;
+ int i, rshflag; /* set for restricted shell */
+ char *command;
+ free(malloc(64*1024));
+#ifdef _lib_sigvec
+ /* This is to clear mask that may be left on by rlogin */
+ clearsigmask(SIGALRM);
+ clearsigmask(SIGHUP);
+ clearsigmask(SIGCHLD);
+#endif /* _lib_sigvec */
+#ifdef _hdr_nc
+ _NutConf(_NC_SET_SUFFIXED_SEARCHING, 1);
+#endif /* _hdr_nc */
+ fixargs(av,0);
+ shp = sh_init(ac,av,userinit);
+ time(&mailtime);
+ if(rshflag=sh_isoption(SH_RESTRICTED))
+ sh_offoption(SH_RESTRICTED);
+#ifdef _lib_fts_notify
+ fts_notify(fts_sigcheck,(void*)shp);
+#endif /* _lib_fts_notify */
+ if(sigsetjmp(*((sigjmp_buf*)shp->jmpbuffer),0))
+ {
+ /* begin script execution here */
+ sh_reinit((char**)0);
+ shp->gd->pid = getpid();
+ shp->gd->ppid = getppid();
+ }
+ shp->fn_depth = shp->dot_depth = 0;
+ command = error_info.id;
+ /* set pidname '$$' */
+ srand(shp->gd->pid&0x7fff);
+ if(nv_isnull(PS4NOD))
+ nv_putval(PS4NOD,e_traceprompt,NV_RDONLY);
+ path_pwd(shp,1);
+ iop = (Sfio_t*)0;
+#if SHOPT_BRACEPAT
+ sh_onoption(SH_BRACEEXPAND);
+#endif
+ if((beenhere++)==0)
+ {
+ sh_onstate(SH_PROFILE);
+ ((Lex_t*)shp->lex_context)->nonstandard = 0;
+ if(shp->gd->ppid==1)
+ shp->login_sh++;
+ if(shp->login_sh >= 2)
+ sh_onoption(SH_LOGIN_SHELL);
+ /* decide whether shell is interactive */
+ if(!sh_isoption(SH_INTERACTIVE) && !sh_isoption(SH_TFLAG) && !sh_isoption(SH_CFLAG) &&
+ sh_isoption(SH_SFLAG) && tty_check(0) && tty_check(ERRIO))
+ sh_onoption(SH_INTERACTIVE);
+ if(sh_isoption(SH_INTERACTIVE))
+ {
+ sh_onoption(SH_BGNICE);
+ sh_onoption(SH_RC);
+ }
+ if(!sh_isoption(SH_RC) && (sh_isoption(SH_BASH) && !sh_isoption(SH_POSIX)
+#if SHOPT_REMOTE
+ || !fstat(0, &statb) && REMOTE(statb.st_mode)
+#endif
+ ))
+ sh_onoption(SH_RC);
+ for(i=0; i<elementsof(shp->offoptions.v); i++)
+ shp->options.v[i] &= ~shp->offoptions.v[i];
+ if(sh_isoption(SH_INTERACTIVE))
+ {
+#ifdef SIGXCPU
+ signal(SIGXCPU,SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+ signal(SIGXFSZ,SIG_DFL);
+#endif /* SIGXFSZ */
+ sh_onoption(SH_MONITOR);
+ }
+ job_init(shp,sh_isoption(SH_LOGIN_SHELL));
+ if(sh_isoption(SH_LOGIN_SHELL))
+ {
+ /* system profile */
+ sh_source(shp, iop, e_sysprofile);
+ if(!sh_isoption(SH_NOUSRPROFILE) && !sh_isoption(SH_PRIVILEGED))
+ {
+ char **files = shp->gd->login_files;
+ while ((name = *files++) && !sh_source(shp, iop, sh_mactry(shp,name)));
+ }
+ }
+ /* make sure PWD is set up correctly */
+ path_pwd(shp,1);
+ if(!sh_isoption(SH_NOEXEC))
+ {
+ if(!sh_isoption(SH_NOUSRPROFILE) && !sh_isoption(SH_PRIVILEGED) && sh_isoption(SH_RC))
+ {
+#if SHOPT_BASH
+ if(sh_isoption(SH_BASH) && !sh_isoption(SH_POSIX))
+ {
+#if SHOPT_SYSRC
+ sh_source(shp, iop, e_bash_sysrc);
+#endif
+ sh_source(shp, iop, shp->gd->rcfile ? shp->gd->rcfile : sh_mactry(shp,(char*)e_bash_rc));
+ }
+ else
+#endif
+ {
+ if(name = sh_mactry(shp,nv_getval(ENVNOD)))
+ name = *name ? strdup(name) : (char*)0;
+#if SHOPT_SYSRC
+ if(!strmatch(name, "?(.)/./*"))
+ sh_source(shp, iop, e_sysrc);
+#endif
+ if(name)
+ {
+ sh_source(shp, iop, name);
+ free(name);
+ }
+ }
+ }
+ else if(sh_isoption(SH_INTERACTIVE) && sh_isoption(SH_PRIVILEGED))
+ sh_source(shp, iop, e_suidprofile);
+ }
+ shp->st.cmdname = error_info.id = command;
+ sh_offstate(SH_PROFILE);
+ if(rshflag)
+ sh_onoption(SH_RESTRICTED);
+ /* open input file if specified */
+ if(shp->comdiv)
+ {
+ shell_c:
+ iop = sfnew(NIL(Sfio_t*),shp->comdiv,strlen(shp->comdiv),0,SF_STRING|SF_READ);
+ }
+ else
+ {
+ name = error_info.id;
+ error_info.id = shp->shname;
+ if(sh_isoption(SH_SFLAG))
+ fdin = 0;
+ else
+ {
+ char *sp;
+ /* open stream should have been passed into shell */
+ if(strmatch(name,e_devfdNN))
+ {
+#if !_WINIX
+ char *cp;
+ int type;
+#endif
+ fdin = (int)strtol(name+8, (char**)0, 10);
+ if(fstat(fdin,&statb)<0)
+ errormsg(SH_DICT,ERROR_system(1),e_open,name);
+#if !_WINIX
+ /*
+ * try to undo effect of solaris 2.5+
+ * change for argv for setuid scripts
+ */
+ if(((type = sh_type(cp = av[0])) & SH_TYPE_SH) && (!(name = nv_getval(L_ARGNOD)) || !((type = sh_type(cp = name)) & SH_TYPE_SH)))
+ {
+ av[0] = (type & SH_TYPE_LOGIN) ? cp : path_basename(cp);
+ /* exec to change $0 for ps */
+ execv(pathshell(),av);
+ /* exec fails */
+ shp->st.dolv[0] = av[0];
+ fixargs(shp->st.dolv,1);
+ }
+#endif
+ name = av[0];
+ sh_offoption(SH_VERBOSE);
+ sh_offoption(SH_XTRACE);
+ }
+ else
+ {
+ int isdir = 0;
+ if((fdin=sh_open(name,O_RDONLY,0))>=0 &&(fstat(fdin,&statb)<0 || S_ISDIR(statb.st_mode)))
+ {
+ close(fdin);
+ isdir = 1;
+ fdin = -1;
+ }
+ else
+ shp->st.filename = path_fullname(shp,name);
+ sp = 0;
+ if(fdin < 0 && !strchr(name,'/'))
+ {
+#ifdef PATH_BFPATH
+ if(path_absolute(shp,name,NIL(Pathcomp_t*)))
+ sp = stakptr(PATH_OFFSET);
+#else
+ sp = path_absolute(shp,name,NIL(char*));
+#endif
+ if(sp)
+ {
+ if((fdin=sh_open(sp,O_RDONLY,0))>=0)
+ shp->st.filename = path_fullname(shp,sp);
+ }
+ }
+ if(fdin<0)
+ {
+ if(isdir)
+ errno = EISDIR;
+ error_info.id = av[0];
+ if(sp || errno!=ENOENT)
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_open,name);
+ /* try sh -c 'name "$@"' */
+ sh_onoption(SH_CFLAG);
+ shp->comdiv = (char*)malloc(strlen(name)+7);
+ name = strcopy(shp->comdiv,name);
+ if(shp->st.dolc)
+ strcopy(name," \"$@\"");
+ goto shell_c;
+ }
+ if(fdin==0)
+ fdin = sh_iomovefd(fdin);
+ }
+ shp->readscript = shp->shname;
+ }
+ error_info.id = name;
+ shp->comdiv--;
+#if SHOPT_ACCT
+ sh_accinit();
+ if(fdin != 0)
+ sh_accbegin(error_info.id);
+#endif /* SHOPT_ACCT */
+ }
+ }
+ else
+ {
+ fdin = shp->infd;
+ fixargs(shp->st.dolv,1);
+ }
+ if(sh_isoption(SH_INTERACTIVE))
+ sh_onstate(SH_INTERACTIVE);
+ nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY);
+ exfile(shp,iop,fdin);
+ sh_done(shp,0);
+ /* NOTREACHED */
+ return(0);
+}
+
+/*
+ * iop is not null when the input is a string
+ * fdin is the input file descriptor
+ */
+
+static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno)
+{
+ time_t curtime;
+ Shnode_t *t;
+ int maxtry=IOMAXTRY, tdone=0, execflags;
+ int states,jmpval;
+ struct checkpt buff;
+ sh_pushcontext(shp,&buff,SH_JMPERREXIT);
+ /* open input stream */
+ nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE);
+ if(!iop)
+ {
+ if(fno > 0)
+ {
+ int r;
+ if(fno < 10 && ((r=sh_fcntl(fno,F_DUPFD,10))>=10))
+ {
+ shp->fdstatus[r] = shp->fdstatus[fno];
+ sh_close(fno);
+ fno = r;
+ }
+ fcntl(fno,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fno] |= IOCLEX;
+ iop = sh_iostream((void*)shp,fno);
+ }
+ else
+ iop = sfstdin;
+ }
+ else
+ fno = -1;
+ shp->infd = fno;
+ if(sh_isstate(SH_INTERACTIVE))
+ {
+ if(nv_isnull(PS1NOD))
+ nv_putval(PS1NOD,(shp->gd->euserid?e_stdprompt:e_supprompt),NV_RDONLY);
+ sh_sigdone();
+ if(sh_histinit((void*)shp))
+ sh_onoption(SH_HISTORY);
+ }
+ else
+ {
+ if(!sh_isstate(SH_PROFILE))
+ {
+ buff.mode = SH_JMPEXIT;
+ sh_onoption(SH_TRACKALL);
+ sh_offoption(SH_MONITOR);
+ }
+ sh_offstate(SH_INTERACTIVE);
+ sh_offstate(SH_MONITOR);
+ sh_offstate(SH_HISTORY);
+ sh_offoption(SH_HISTORY);
+ }
+ states = sh_getstate();
+ jmpval = sigsetjmp(buff.buff,0);
+ if(jmpval)
+ {
+ Sfio_t *top;
+ sh_iorestore((void*)shp,0,jmpval);
+ hist_flush(shp->gd->hist_ptr);
+ sfsync(shp->outpool);
+ shp->st.execbrk = shp->st.breakcnt = 0;
+ /* check for return from profile or env file */
+ if(sh_isstate(SH_PROFILE) && (jmpval==SH_JMPFUN || jmpval==SH_JMPEXIT))
+ {
+ sh_setstate(states);
+ goto done;
+ }
+ if(!sh_isoption(SH_INTERACTIVE) || sh_isstate(SH_FORKED) || (jmpval > SH_JMPERREXIT && job_close(shp) >=0))
+ {
+ sh_offstate(SH_INTERACTIVE);
+ sh_offstate(SH_MONITOR);
+ goto done;
+ }
+ /* skip over remaining input */
+ if(top = fcfile())
+ {
+ while(fcget()>0);
+ fcclose();
+ while(top=sfstack(iop,SF_POPSTACK))
+ sfclose(top);
+ }
+ /* make sure that we own the terminal */
+#ifdef SIGTSTP
+ tcsetpgrp(job.fd,shp->gd->pid);
+#endif /* SIGTSTP */
+ }
+ /* error return here */
+ sfclrerr(iop);
+ sh_setstate(states);
+ shp->st.optindex = 1;
+ opt_info.offset = 0;
+ shp->st.loopcnt = 0;
+ shp->trapnote = 0;
+ shp->intrap = 0;
+ error_info.line = 1;
+ shp->inlineno = 1;
+ shp->binscript = 0;
+ if(sfeof(iop))
+ goto eof_or_error;
+ /* command loop */
+ while(1)
+ {
+ shp->nextprompt = 1;
+ sh_freeup(shp);
+ stakset(NIL(char*),0);
+ exitset();
+ sh_offstate(SH_STOPOK);
+ sh_offstate(SH_ERREXIT);
+ sh_offstate(SH_VERBOSE);
+ sh_offstate(SH_TIMING);
+ sh_offstate(SH_GRACE);
+ sh_offstate(SH_TTYWAIT);
+ if(sh_isoption(SH_VERBOSE))
+ sh_onstate(SH_VERBOSE);
+ sh_onstate(SH_ERREXIT);
+ /* -eim flags don't apply to profiles */
+ if(sh_isstate(SH_PROFILE))
+ {
+ sh_offstate(SH_INTERACTIVE);
+ sh_offstate(SH_ERREXIT);
+ sh_offstate(SH_MONITOR);
+ }
+ if(sh_isstate(SH_INTERACTIVE) && !tdone)
+ {
+ register char *mail;
+#ifdef JOBS
+ sh_offstate(SH_MONITOR);
+ if(sh_isoption(SH_MONITOR))
+ sh_onstate(SH_MONITOR);
+ if(job.pwlist)
+ {
+ job_walk(sfstderr,job_list,JOB_NFLAG,(char**)0);
+ job_wait((pid_t)0);
+ }
+#endif /* JOBS */
+ if((mail=nv_getval(MAILPNOD)) || (mail=nv_getval(MAILNOD)))
+ {
+ time(&curtime);
+ if ((curtime - mailtime) >= sh_mailchk)
+ {
+ chkmail(shp,mail);
+ mailtime = curtime;
+ }
+ }
+ if(shp->gd->hist_ptr)
+ hist_eof(shp->gd->hist_ptr);
+ /* sets timeout for command entry */
+ shp->timeout = shp->st.tmout;
+#if SHOPT_TIMEOUT
+ if(shp->timeout <= 0 || shp->timeout > SHOPT_TIMEOUT)
+ shp->timeout = SHOPT_TIMEOUT;
+#endif /* SHOPT_TIMEOUT */
+ shp->inlineno = 1;
+ error_info.line = 1;
+ shp->exitval = 0;
+ shp->trapnote = 0;
+ if(buff.mode == SH_JMPEXIT)
+ {
+ buff.mode = SH_JMPERREXIT;
+#ifdef DEBUG
+ errormsg(SH_DICT,ERROR_warn(0),"%d: mode changed to JMP_EXIT",getpid());
+#endif
+ }
+ }
+ errno = 0;
+ if(tdone || !sfreserve(iop,0,0))
+ {
+ eof_or_error:
+ if(sh_isstate(SH_INTERACTIVE) && !sferror(iop))
+ {
+ if(--maxtry>0 && sh_isoption(SH_IGNOREEOF) &&
+ !sferror(sfstderr) && (shp->fdstatus[fno]&IOTTY))
+ {
+ sfclrerr(iop);
+ errormsg(SH_DICT,0,e_logout);
+ continue;
+ }
+ else if(job_close(shp)<0)
+ continue;
+ }
+ if(errno==0 && sferror(iop) && --maxtry>0)
+ {
+ sfclrlock(iop);
+ sfclrerr(iop);
+ continue;
+ }
+ goto done;
+ }
+ maxtry = IOMAXTRY;
+ if(sh_isstate(SH_INTERACTIVE) && shp->gd->hist_ptr)
+ {
+ job_wait((pid_t)0);
+ hist_eof(shp->gd->hist_ptr);
+ sfsync(sfstderr);
+ }
+ if(sh_isoption(SH_HISTORY))
+ sh_onstate(SH_HISTORY);
+ job.waitall = job.curpgid = 0;
+ error_info.flags |= ERROR_INTERACTIVE;
+ t = (Shnode_t*)sh_parse(shp,iop,0);
+ if(!sh_isstate(SH_INTERACTIVE) && !sh_isoption(SH_CFLAG))
+ error_info.flags &= ~ERROR_INTERACTIVE;
+ shp->readscript = 0;
+ if(sh_isstate(SH_INTERACTIVE) && shp->gd->hist_ptr)
+ hist_flush(shp->gd->hist_ptr);
+ sh_offstate(SH_HISTORY);
+ if(t)
+ {
+ execflags = sh_state(SH_ERREXIT)|sh_state(SH_INTERACTIVE);
+ /* The last command may not have to fork */
+ if(!sh_isstate(SH_PROFILE) && sh_isoption(SH_CFLAG) &&
+ (fno<0 || !(shp->fdstatus[fno]&(IOTTY|IONOSEEK)))
+ && !sfreserve(iop,0,0))
+ {
+ execflags |= sh_state(SH_NOFORK);
+ }
+ shp->st.execbrk = 0;
+ sh_exec(t,execflags);
+ if(shp->forked)
+ {
+ sh_offstate(SH_INTERACTIVE);
+ goto done;
+ }
+ /* This is for sh -t */
+ if(sh_isoption(SH_TFLAG) && !sh_isstate(SH_PROFILE))
+ tdone++;
+ }
+ }
+done:
+ sh_popcontext(shp,&buff);
+ if(sh_isstate(SH_INTERACTIVE))
+ {
+ sfputc(sfstderr,'\n');
+ job_close(shp);
+ }
+ if(jmpval == SH_JMPSCRIPT)
+ siglongjmp(*shp->jmplist,jmpval);
+ else if(jmpval == SH_JMPEXIT)
+ sh_done(shp,0);
+ if(fno>0)
+ sh_close(fno);
+ if(shp->st.filename)
+ free((void*)shp->st.filename);
+ shp->st.filename = 0;
+}
+
+
+/* prints out messages if files in list have been modified since last call */
+static void chkmail(Shell_t *shp, char *files)
+{
+ register char *cp,*sp,*qp;
+ register char save;
+ struct argnod *arglist=0;
+ int offset = staktell();
+ char *savstak=stakptr(0);
+ struct stat statb;
+ if(*(cp=files) == 0)
+ return;
+ sp = cp;
+ do
+ {
+ /* skip to : or end of string saving first '?' */
+ for(qp=0;*sp && *sp != ':';sp++)
+ if((*sp == '?' || *sp=='%') && qp == 0)
+ qp = sp;
+ save = *sp;
+ *sp = 0;
+ /* change '?' to end-of-string */
+ if(qp)
+ *qp = 0;
+ do
+ {
+ /* see if time has been modified since last checked
+ * and the access time <= the modification time
+ */
+ if(stat(cp,&statb) >= 0 && statb.st_mtime >= mailtime
+ && statb.st_atime <= statb.st_mtime)
+ {
+ /* check for directory */
+ if(!arglist && S_ISDIR(statb.st_mode))
+ {
+ /* generate list of directory entries */
+ path_complete(shp,cp,"/*",&arglist);
+ }
+ else
+ {
+ /*
+ * If the file has shrunk,
+ * or if the size is zero
+ * then don't print anything
+ */
+ if(statb.st_size &&
+ ( statb.st_ino != lastmail.st_ino
+ || statb.st_dev != lastmail.st_dev
+ || statb.st_size > lastmail.st_size))
+ {
+ /* save and restore $_ */
+ char *save = shp->lastarg;
+ shp->lastarg = cp;
+ errormsg(SH_DICT,0,sh_mactry(shp,qp?qp+1:(char*)e_mailmsg));
+ shp->lastarg = save;
+ }
+ lastmail = statb;
+ break;
+ }
+ }
+ if(arglist)
+ {
+ cp = arglist->argval;
+ arglist = arglist->argchn.ap;
+ }
+ else
+ cp = 0;
+ }
+ while(cp);
+ if(qp)
+ *qp = '?';
+ *sp++ = save;
+ cp = sp;
+ }
+ while(save);
+ stakset(savstak,offset);
+}
+
+#undef EXECARGS
+#undef PSTAT
+#if defined(_hdr_execargs) && defined(pdp11)
+# include <execargs.h>
+# define EXECARGS 1
+#endif
+
+#if defined(_lib_pstat) && defined(_sys_pstat)
+# include <sys/pstat.h>
+# define PSTAT 1
+#endif
+
+#if defined(_lib_fork) && !defined(_NEXT_SOURCE)
+/*
+ * fix up command line for ps command
+ * mode is 0 for initialization
+ */
+static void fixargs(char **argv, int mode)
+{
+#if EXECARGS
+ *execargs=(char *)argv;
+#else
+ static char *buff;
+ static int command_len;
+ register char *cp;
+ int offset=0,size;
+# ifdef PSTAT
+ union pstun un;
+ if(mode==0)
+ {
+ struct pst_static st;
+ un.pst_static = &st;
+ if(pstat(PSTAT_STATIC, un, sizeof(struct pst_static), 1, 0)<0)
+ return;
+ command_len = st.command_length;
+ return;
+ }
+ stakseek(command_len+2);
+ buff = stakseek(0);
+# else
+ if(mode==0)
+ {
+ buff = argv[0];
+ while(cp = *argv++)
+ command_len += strlen(cp)+1;
+ if(environ && *environ==buff+command_len)
+ {
+ for(argv=environ; cp = *argv; cp++)
+ {
+ if(command_len > CMD_LENGTH)
+ {
+ command_len = CMD_LENGTH;
+ break;
+ }
+ *argv++ = strdup(cp);
+ command_len += strlen(cp)+1;
+ }
+ }
+ command_len -= 1;
+ return;
+ }
+# endif /* PSTAT */
+ if(command_len==0)
+ return;
+ while((cp = *argv++) && offset < command_len)
+ {
+ if(offset + (size=strlen(cp)) >= command_len)
+ size = command_len - offset;
+ memcpy(buff+offset,cp,size);
+ offset += size;
+ buff[offset++] = ' ';
+ }
+ buff[offset-1] = 0;
+# ifdef PSTAT
+ un.pst_command = stakptr(0);
+ pstat(PSTAT_SETCMD,un,0,0,0);
+# endif /* PSTAT */
+#endif /* EXECARGS */
+}
+#endif /* _lib_fork */
diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c
new file mode 100644
index 0000000..77793f4
--- /dev/null
+++ b/src/cmd/ksh93/sh/name.c
@@ -0,0 +1,3684 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * AT&T Labs
+ *
+ */
+
+#define putenv ___putenv
+
+#include "defs.h"
+#include "variables.h"
+#include "path.h"
+#include "lexstates.h"
+#include "timeout.h"
+#include "FEATURE/externs"
+#include "streval.h"
+
+#define NVCACHE 8 /* must be a power of 2 */
+#define Empty ((char*)(e_sptbnl+3))
+static char *savesub = 0;
+static char Null[1];
+static Namval_t NullNode;
+static Dt_t *Refdict;
+static Dtdisc_t _Refdisc =
+{
+ offsetof(struct Namref,np),sizeof(struct Namval_t*),sizeof(struct Namref)
+};
+
+#if !_lib_pathnative && _lib_uwin_path
+
+#define _lib_pathnative 1
+
+extern int uwin_path(const char*, char*, int);
+
+size_t
+pathnative(const char* path, char* buf, size_t siz)
+{
+ return uwin_path(path, buf, siz);
+}
+
+#endif /* _lib_pathnative */
+
+static void attstore(Namval_t*,void*);
+#ifndef _ENV_H
+ static void pushnam(Namval_t*,void*);
+ static char *staknam(Namval_t*, char*);
+#endif
+static void rightjust(char*, int, int);
+static char *lastdot(char*, int);
+
+struct adata
+{
+ Shell_t *sh;
+ Namval_t *tp;
+ char *mapname;
+ char **argnam;
+ int attsize;
+ char *attval;
+};
+
+#if SHOPT_TYPEDEF
+ struct sh_type
+ {
+ void *previous;
+ Namval_t **nodes;
+ Namval_t *rp;
+ short numnodes;
+ short maxnodes;
+ };
+#endif /*SHOPT_TYPEDEF */
+
+#if NVCACHE
+ struct Namcache
+ {
+ struct Cache_entry
+ {
+ Dt_t *root;
+ Dt_t *last_root;
+ char *name;
+ Namval_t *np;
+ Namval_t *last_table;
+ int flags;
+ short size;
+ short len;
+ } entries[NVCACHE];
+ short index;
+ short ok;
+ };
+ static struct Namcache nvcache;
+#endif
+
+char nv_local = 0;
+#ifndef _ENV_H
+static void(*nullscan)(Namval_t*,void*);
+#endif
+
+#if ( SFIO_VERSION <= 20010201L )
+# define _data data
+#endif
+
+#if !SHOPT_MULTIBYTE
+# define mbchar(p) (*(unsigned char*)p++)
+#endif /* SHOPT_MULTIBYTE */
+
+/* ======== name value pair routines ======== */
+
+#include "shnodes.h"
+#include "builtins.h"
+
+static char *getbuf(size_t len)
+{
+ static char *buf;
+ static size_t buflen;
+ if(buflen < len)
+ {
+ if(buflen==0)
+ buf = (char*)malloc(len);
+ else
+ buf = (char*)realloc(buf,len);
+ buflen = len;
+ }
+ return(buf);
+}
+
+#ifdef _ENV_H
+void sh_envput(Env_t* ep,Namval_t *np)
+{
+ int offset = staktell();
+ Namarr_t *ap = nv_arrayptr(np);
+ char *val;
+ if(ap)
+ {
+ if(ap->nelem&ARRAY_UNDEF)
+ nv_putsub(np,"0",0L);
+ else if(!(val=nv_getsub(np)) || strcmp(val,"0"))
+ return;
+ }
+ if(!(val = nv_getval(np)))
+ return;
+ stakputs(nv_name(np));
+ stakputc('=');
+ stakputs(val);
+ stakseek(offset);
+ env_add(ep,stakptr(offset),ENV_STRDUP);
+}
+#endif
+
+/*
+ * output variable name in format for re-input
+ */
+void nv_outname(Sfio_t *out, char *name, int len)
+{
+ const char *cp=name, *sp;
+ int c, offset = staktell();
+ while(sp= strchr(cp,'['))
+ {
+ if(len>0 && cp+len <= sp)
+ break;
+ sfwrite(out,cp,++sp-cp);
+ stakseek(offset);
+ while(c= *sp++)
+ {
+ if(c==']')
+ break;
+ else if(c=='\\')
+ {
+ if(*sp=='[' || *sp==']' || *sp=='\\')
+ c = *sp++;
+ }
+ stakputc(c);
+ }
+ stakputc(0);
+ sfputr(out,sh_fmtq(stakptr(offset)),-1);
+ if(len>0)
+ {
+ sfputc(out,']');
+ return;
+ }
+ cp = sp-1;
+ }
+ if(*cp)
+ {
+ if(len>0)
+ sfwrite(out,cp,len);
+ else
+ sfputr(out,cp,-1);
+ }
+ stakseek(offset);
+}
+
+#if SHOPT_TYPEDEF
+Namval_t *nv_addnode(Namval_t* np, int remove)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct sh_type *sp = (struct sh_type*)shp->mktype;
+ register int i;
+ register char *name=0;
+ if(sp->numnodes==0 && !nv_isnull(np) && shp->last_table)
+ {
+ /* could be an redefine */
+ Dt_t *root = nv_dict(shp->last_table);
+ sp->rp = np;
+ nv_delete(np,root,NV_NOFREE);
+ np = nv_search(sp->rp->nvname,root,NV_ADD);
+ }
+ if(sp->numnodes && memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1))
+ {
+ name = (sp->nodes[0])->nvname;
+ i = strlen(name);
+ if(memcmp(np->nvname,name,i))
+ return(np);
+ }
+ if(sp->rp && sp->numnodes)
+ {
+ /* check for a redefine */
+ if(name && np->nvname[i]=='.' && np->nvname[i+1]=='_' && np->nvname[i+2]==0)
+ sp->rp = 0;
+ else
+ {
+ Dt_t *root = nv_dict(shp->last_table);
+ nv_delete(sp->nodes[0],root,NV_NOFREE);
+ dtinsert(root,sp->rp);
+ errormsg(SH_DICT,ERROR_exit(1),e_redef,sp->nodes[0]->nvname);
+ }
+ }
+ for(i=0; i < sp->numnodes; i++)
+ {
+ if(np == sp->nodes[i])
+ {
+ if(remove)
+ {
+ while(++i < sp->numnodes)
+ sp->nodes[i-1] = sp->nodes[i];
+ sp->numnodes--;
+ }
+ return(np);
+ }
+ }
+ if(remove)
+ return(np);
+ if(sp->numnodes==sp->maxnodes)
+ {
+ sp->maxnodes += 20;
+ sp->nodes = (Namval_t**)realloc(sp->nodes,sizeof(Namval_t*)*sp->maxnodes);
+ }
+ sp->nodes[sp->numnodes++] = np;
+ return(np);
+}
+#endif /* SHOPT_TYPEDEF */
+
+/*
+ * given a list of assignments, determine <name> is on the list
+ returns a pointer to the argnod on the list or NULL
+ */
+struct argnod *nv_onlist(struct argnod *arg, const char *name)
+{
+ char *cp;
+ int len = strlen(name);
+ for(;arg; arg=arg->argnxt.ap)
+ {
+ if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE)))
+ cp = ((struct fornod*)arg->argchn.ap)->fornam;
+ else
+ cp = arg->argval;
+ if(memcmp(cp,name,len)==0 && (cp[len]==0 || cp[len]=='='))
+ return(arg);
+ }
+ return(0);
+}
+
+/*
+ * Perform parameter assignment for a linked list of parameters
+ * <flags> contains attributes for the parameters
+ */
+void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ)
+{
+ Shell_t *shp = sh_getinterp();
+ register char *cp;
+ register Namval_t *np, *mp;
+ char *trap=shp->st.trap[SH_DEBUGTRAP];
+ char *prefix = shp->prefix;
+ int traceon = (sh_isoption(SH_XTRACE)!=0);
+ int array = (flags&(NV_ARRAY|NV_IARRAY));
+ Namarr_t *ap;
+ Namval_t node;
+ struct Namref nr;
+#if SHOPT_TYPEDEF
+ int maketype = flags&NV_TYPE;
+ struct sh_type shtp;
+ if(maketype)
+ {
+ shtp.previous = shp->mktype;
+ shp->mktype=(void*)&shtp;
+ shtp.numnodes=0;
+ shtp.maxnodes = 20;
+ shtp.rp = 0;
+ shtp.nodes =(Namval_t**)malloc(shtp.maxnodes*sizeof(Namval_t*));
+ }
+#endif /* SHOPT_TYPEDEF*/
+#if SHOPT_NAMESPACE
+ if(shp->namespace && nv_dict(shp->namespace)==shp->var_tree)
+ flags |= NV_NOSCOPE;
+#endif /* SHOPT_NAMESPACE */
+ flags &= ~(NV_TYPE|NV_ARRAY|NV_IARRAY);
+ if(sh_isoption(SH_ALLEXPORT))
+ flags |= NV_EXPORT;
+ if(shp->prefix)
+ {
+ flags &= ~(NV_IDENT|NV_EXPORT);
+ flags |= NV_VARNAME;
+ }
+ else
+ shp->prefix_root = shp->first_root = 0;
+ for(;arg; arg=arg->argnxt.ap)
+ {
+ shp->used_pos = 0;
+ if(arg->argflag&ARG_MAC)
+ {
+ shp->prefix = 0;
+ cp = sh_mactrim(shp,arg->argval,(flags&NV_NOREF)?-3:-1);
+ shp->prefix = prefix;
+ }
+ else
+ {
+ stakseek(0);
+ if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE)))
+ {
+ int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN);
+ int sub=0;
+ struct fornod *fp=(struct fornod*)arg->argchn.ap;
+ register Shnode_t *tp=fp->fortre;
+ flag |= (flags&(NV_NOSCOPE|NV_STATIC|NV_FARRAY));
+ if(arg->argflag&ARG_QUOTED)
+ cp = sh_mactrim(shp,fp->fornam,-1);
+ else
+ cp = fp->fornam;
+ error_info.line = fp->fortyp-shp->st.firstline;
+ if(!array && tp->tre.tretyp!=TLST && tp->com.comset && !tp->com.comarg && tp->com.comset->argval[0]==0 && tp->com.comset->argval[1]=='[')
+ array |= (tp->com.comset->argflag&ARG_MESSAGE)?NV_IARRAY:NV_ARRAY;
+ if(prefix && tp->com.comset && *cp=='[')
+ {
+ shp->prefix = 0;
+ np = nv_open(prefix,shp->last_root,flag);
+ shp->prefix = prefix;
+ if(np)
+ {
+ if(nv_isvtree(np) && !nv_isarray(np))
+ {
+ stakputc('.');
+ stakputs(cp);
+ cp = stakfreeze(1);
+ }
+ nv_close(np);
+ }
+ }
+ np = nv_open(cp,shp->var_tree,flag|NV_ASSIGN);
+ if(nv_isattr(np,NV_RDONLY) && np->nvfun && !(flags&NV_RDONLY))
+ errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
+ if(nv_isattr(np,NV_NOFREE) && nv_isnull(np))
+ nv_offattr(np,NV_NOFREE);
+ if(nv_istable(np))
+ _nv_unset(np,0);
+ if(typ && !array && (!shp->prefix || nv_isnull(np) || nv_isarray(np)))
+ {
+ if(!(nv_isnull(np)) && !nv_isarray(np))
+ _nv_unset(np,0);
+ nv_settype(np,typ,0);
+ }
+ if((flags&NV_STATIC) && !nv_isattr(np,NV_EXPORT) && !nv_isnull(np))
+#if SHOPT_TYPEDEF
+ goto check_type;
+#else
+ continue;
+#endif /* SHOPT_TYPEDEF */
+ ap=nv_arrayptr(np);
+#if SHOPT_FIXEDARRAY
+ if(ap && ap->fixed)
+ flags |= NV_FARRAY;
+#endif /* SHOPT_FIXEDARRAY */
+ if(array && (!ap || !ap->hdr.type))
+ {
+#if SHOPT_FIXEDARRAY
+ if(!(arg->argflag&ARG_APPEND) && (!ap || !ap->fixed))
+#else
+ if(!(arg->argflag&ARG_APPEND))
+#endif /* SHOPT_FIXEDARRAY */
+ _nv_unset(np,NV_EXPORT);
+ if(array&NV_ARRAY)
+ {
+ nv_setarray(np,nv_associative);
+ }
+ else
+ {
+ nv_onattr(np,NV_ARRAY);
+ }
+ }
+ if(array && tp->tre.tretyp!=TLST && !tp->com.comset && !tp->com.comarg)
+ {
+#if SHOPT_TYPEDEF
+ goto check_type;
+#else
+ continue;
+#endif /* SHOPT_TYPEDEF */
+ }
+ /* check for array assignment */
+ if(tp->tre.tretyp!=TLST && tp->com.comarg && !tp->com.comset && ((array&NV_IARRAY) || !((mp=tp->com.comnamp) && nv_isattr(mp,BLT_DCL))))
+ {
+ int argc;
+ Dt_t *last_root = shp->last_root;
+ char **argv = sh_argbuild(shp,&argc,&tp->com,0);
+ shp->last_root = last_root;
+#if SHOPT_TYPEDEF
+ if(shp->mktype && shp->dot_depth==0 && np==((struct sh_type*)shp->mktype)->nodes[0])
+ {
+ shp->mktype = 0;
+ errormsg(SH_DICT,ERROR_exit(1),"%s: not a known type name",argv[0]);
+ }
+#endif /* SHOPT_TYPEDEF */
+ if(!(arg->argflag&ARG_APPEND))
+ {
+#if SHOPT_FIXEDARRAY
+ if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && !ap->fixed && (ap->nelem&ARRAY_MASK)))
+#else
+ if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && (ap->nelem&ARRAY_MASK)))
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ if(ap)
+ ap->nelem |= ARRAY_UNDEF;
+ _nv_unset(np,0);
+ }
+ }
+ nv_setvec(np,(arg->argflag&ARG_APPEND),argc,argv);
+ if(traceon || trap)
+ {
+ int n = -1;
+ char *name = nv_name(np);
+ if(arg->argflag&ARG_APPEND)
+ n = '+';
+ if(trap)
+ sh_debug(shp,trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN);
+ if(traceon)
+ {
+ sh_trace(shp,NIL(char**),0);
+ sfputr(sfstderr,name,n);
+ sfwrite(sfstderr,"=( ",3);
+ while(cp= *argv++)
+ sfputr(sfstderr,sh_fmtq(cp),' ');
+ sfwrite(sfstderr,")\n",2);
+ }
+ }
+#if SHOPT_TYPEDEF
+ goto check_type;
+#else
+ continue;
+#endif /* SHOPT_TYPEDEF */
+ }
+ if((tp->tre.tretyp&COMMSK)==TFUN)
+ goto skip;
+ if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[')
+ {
+ if(tp->tre.tretyp!=TLST && !tp->com.comnamp && tp->com.comset && tp->com.comset->argval[0]==0 && tp->com.comset->argchn.ap)
+ {
+ if(prefix)
+ cp = stakcopy(nv_name(np));
+ shp->prefix = cp;
+ if(tp->com.comset->argval[1]=='[')
+ {
+ if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0)))
+ _nv_unset(np,0);
+ if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE))
+ nv_setarray(np,nv_associative);
+ }
+ nv_setlist(tp->com.comset,flags&~NV_STATIC,0);
+ shp->prefix = prefix;
+ if(tp->com.comset->argval[1]!='[')
+ nv_setvtree(np);
+ nv_close(np);
+#if SHOPT_TYPEDEF
+ goto check_type;
+#else
+ continue;
+#endif /* SHOPT_TYPEDEF */
+ }
+ if(*cp!='.' && *cp!='[' && strchr(cp,'['))
+ {
+ cp = stakcopy(nv_name(np));
+ nv_close(np);
+ if(!(arg->argflag&ARG_APPEND))
+ flag &= ~NV_ARRAY;
+ shp->prefix_root = shp->first_root;
+ np = nv_open(cp,shp->prefix_root?shp->prefix_root:shp->var_tree,flag);
+ }
+ if(arg->argflag&ARG_APPEND)
+ {
+ if(nv_isarray(np))
+ {
+ if((sub=nv_aimax(np)) < 0 && nv_arrayptr(np))
+ errormsg(SH_DICT,ERROR_exit(1),e_badappend,nv_name(np));
+ if(sub>=0)
+ sub++;
+ }
+ if(!nv_isnull(np) && np->nvalue.cp!=Empty && !nv_isvtree(np))
+ sub=1;
+ }
+ else if(((np->nvalue.cp && np->nvalue.cp!=Empty)||nv_isvtree(np)) && !nv_type(np))
+ _nv_unset(np,NV_EXPORT);
+ }
+ else
+ {
+ if(!(arg->argflag&ARG_APPEND))
+ _nv_unset(np,NV_EXPORT);
+ if(!sh_isoption(SH_BASH) && !(array&NV_IARRAY) && !nv_isarray(np))
+ nv_setarray(np,nv_associative);
+ }
+ skip:
+ if(sub>0)
+ {
+ sfprintf(stkstd,"%s[%d]",prefix?nv_name(np):cp,sub);
+ shp->prefix = stakfreeze(1);
+ nv_putsub(np,(char*)0,ARRAY_ADD|ARRAY_FILL|sub);
+ }
+ else if(prefix)
+ shp->prefix = stakcopy(nv_name(np));
+ else
+ shp->prefix = cp;
+ shp->last_table = 0;
+ if(shp->prefix)
+ {
+ if(*shp->prefix=='_' && shp->prefix[1]=='.' && nv_isref(L_ARGNOD))
+ {
+ sfprintf(stkstd,"%s%s",nv_name(L_ARGNOD->nvalue.nrp->np),shp->prefix+1);
+ shp->prefix = stkfreeze(stkstd,1);
+ }
+ memset(&nr,0,sizeof(nr));
+ memcpy(&node,L_ARGNOD,sizeof(node));
+ L_ARGNOD->nvalue.nrp = &nr;
+ nr.np = np;
+ nr.root = shp->last_root;
+ nr.table = shp->last_table;
+ L_ARGNOD->nvflag = NV_REF|NV_NOFREE;
+ L_ARGNOD->nvfun = 0;
+ }
+ sh_exec(tp,sh_isstate(SH_ERREXIT));
+#if SHOPT_TYPEDEF
+ if(shp->prefix)
+#endif
+ {
+ L_ARGNOD->nvalue.nrp = node.nvalue.nrp;
+ L_ARGNOD->nvflag = node.nvflag;
+ L_ARGNOD->nvfun = node.nvfun;
+ }
+ shp->prefix = prefix;
+ if(nv_isarray(np) && (mp=nv_opensub(np)))
+ np = mp;
+ while(tp->tre.tretyp==TLST)
+ {
+ if(!tp->lst.lstlef || !tp->lst.lstlef->tre.tretyp==TCOM || tp->lst.lstlef->com.comarg || tp->lst.lstlef->com.comset && tp->lst.lstlef->com.comset->argval[0]!='[')
+ break;
+ tp = tp->lst.lstrit;
+
+ }
+ if(!nv_isarray(np) && !typ && (tp->com.comarg || !tp->com.comset || tp->com.comset->argval[0]!='['))
+ {
+ nv_setvtree(np);
+ if(tp->com.comarg || tp->com.comset)
+ np->nvfun->dsize = 0;
+ }
+#if SHOPT_TYPEDEF
+ goto check_type;
+#else
+ continue;
+#endif /* SHOPT_TYPEDEF */
+ }
+ cp = arg->argval;
+ mp = 0;
+ }
+ np = nv_open(cp,shp->prefix_root?shp->prefix_root:shp->var_tree,flags);
+ if(!np->nvfun && (flags&NV_NOREF))
+ {
+ if(shp->used_pos)
+ nv_onattr(np,NV_PARAM);
+ else
+ nv_offattr(np,NV_PARAM);
+ }
+ if(traceon || trap)
+ {
+ register char *sp=cp;
+ char *name=nv_name(np);
+ char *sub=0;
+ int append = 0;
+ if(nv_isarray(np))
+ sub = savesub;
+ if(cp=lastdot(sp,'='))
+ {
+ if(cp[-1]=='+')
+ append = ARG_APPEND;
+ cp++;
+ }
+ if(traceon)
+ {
+ sh_trace(shp,NIL(char**),0);
+ nv_outname(sfstderr,name,-1);
+ if(sub)
+ sfprintf(sfstderr,"[%s]",sh_fmtq(sub));
+ if(cp)
+ {
+ if(append)
+ sfputc(sfstderr,'+');
+ sfprintf(sfstderr,"=%s\n",sh_fmtq(cp));
+ }
+ }
+ if(trap)
+ {
+ char *av[2];
+ av[0] = cp;
+ av[1] = 0;
+ sh_debug(shp,trap,name,sub,av,append);
+ }
+ }
+#if SHOPT_TYPEDEF
+ check_type:
+ if(maketype)
+ {
+ nv_open(shtp.nodes[0]->nvname,shp->var_tree,NV_ASSIGN|NV_VARNAME|NV_NOADD|NV_NOFAIL);
+ np = nv_mktype(shtp.nodes,shtp.numnodes);
+ free((void*)shtp.nodes);
+ shp->mktype = shtp.previous;
+ maketype = 0;
+ if(shp->namespace)
+ free(shp->prefix);
+ shp->prefix = 0;
+ if(nr.np == np)
+ {
+ L_ARGNOD->nvalue.nrp = node.nvalue.nrp;
+ L_ARGNOD->nvflag = node.nvflag;
+ L_ARGNOD->nvfun = node.nvfun;
+ }
+ }
+#endif /* SHOPT_TYPEDEF */
+ }
+}
+
+/*
+ * copy the subscript onto the stack
+ */
+static void stak_subscript(const char *sub, int last)
+{
+ register int c;
+ stakputc('[');
+ while(c= *sub++)
+ {
+ if(c=='[' || c==']' || c=='\\')
+ stakputc('\\');
+ stakputc(c);
+ }
+ stakputc(last);
+}
+
+/*
+ * construct a new name from a prefix and base name on the stack
+ */
+static char *copystack(const char *prefix, register const char *name, const char *sub)
+{
+ register int last=0,offset = staktell();
+ if(prefix)
+ {
+ stakputs(prefix);
+ if(*stakptr(staktell()-1)=='.')
+ stakseek(staktell()-1);
+ if(*name=='.' && name[1]=='[')
+ last = staktell()+2;
+ if(*name!='[' && *name!='.' && *name!='=' && *name!='+')
+ stakputc('.');
+ if(*name=='.' && (name[1]=='=' || name[1]==0))
+ stakputc('.');
+ }
+ if(last)
+ {
+ stakputs(name);
+ if(sh_checkid(stakptr(last),(char*)0))
+ stakseek(staktell()-2);
+ }
+ if(sub)
+ stak_subscript(sub,']');
+ if(!last)
+ stakputs(name);
+ stakputc(0);
+ return(stakptr(offset));
+}
+
+/*
+ * grow this stack string <name> by <n> bytes and move from cp-1 to end
+ * right by <n>. Returns beginning of string on the stack
+ */
+static char *stack_extend(const char *cname, char *cp, int n)
+{
+ register char *name = (char*)cname;
+ int offset = name - stakptr(0);
+ int m = cp-name;
+ stakseek(strlen(name)+n+1);
+ name = stakptr(offset);
+ cp = name + m;
+ m = strlen(cp)+1;
+ while(m-->0)
+ cp[n+m]=cp[m];
+ return((char*)name);
+}
+
+Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp)
+{
+ Shell_t *shp = sh_getinterp();
+ char *sub=0, *cp=(char*)name, *sp, *xp;
+ register int c;
+ register Namval_t *np=0, *nq=0;
+ Namfun_t *fp=0;
+ long mode, add=0;
+ int copy=0,isref,top=0,noscope=(flags&NV_NOSCOPE);
+ int nofree=0, level=0;
+#if SHOPT_FIXEDARRAY
+ Namarr_t *ap;
+#endif /* SHOPT_FIXEDARRAY */
+ if(root==shp->var_tree)
+ {
+ if(dtvnext(root))
+ top = 1;
+ else
+ flags &= ~NV_NOSCOPE;
+ }
+ if(!dp->disc)
+ copy = dp->nofree&1;
+ if(*cp=='.')
+ cp++;
+ while(1)
+ {
+ switch(c = *(unsigned char*)(sp = cp))
+ {
+ case '[':
+ if(flags&NV_NOARRAY)
+ {
+ dp->last = cp;
+ return(np);
+ }
+ cp = nv_endsubscript((Namval_t*)0,sp,0);
+ if(sp==name || sp[-1]=='.')
+ c = *(sp = cp);
+ goto skip;
+ case '.':
+ if(flags&NV_IDENT)
+ return(0);
+ if(root==shp->var_tree)
+ flags &= ~NV_EXPORT;
+ if(!copy && !(flags&NV_NOREF))
+ {
+ c = sp-name;
+ copy = cp-name;
+ dp->nofree |= 1;
+ name = copystack((const char*)0, name,(const char*)0);
+ cp = (char*)name+copy;
+ sp = (char*)name+c;
+ c = '.';
+ }
+ skip:
+ case '+':
+ case '=':
+ *sp = 0;
+ case 0:
+ isref = 0;
+ dp->last = cp;
+ mode = (c=='.' || (flags&NV_NOADD))?add:NV_ADD;
+ if(level++ || ((flags&NV_NOSCOPE) && c!='.'))
+ mode |= HASH_NOSCOPE;
+ np=0;
+ if(top)
+ {
+ struct Ufunction *rp;
+ if((rp=shp->st.real_fun) && !rp->sdict && (flags&NV_STATIC))
+ {
+ Dt_t *dp = dtview(shp->var_tree,(Dt_t*)0);
+ rp->sdict = dtopen(&_Nvdisc,Dtoset);
+ dtview(rp->sdict,dp);
+ dtview(shp->var_tree,rp->sdict);
+ }
+ if(np = nv_search(name,shp->var_tree,0))
+ {
+#if SHOPT_NAMESPACE
+ if(shp->var_tree->walk==shp->var_base || (shp->var_tree->walk!=shp->var_tree && shp->namespace && nv_dict(shp->namespace)==shp->var_tree->walk))
+#else
+ if(shp->var_tree->walk==shp->var_base)
+#endif /* SHOPT_NAMESPACE */
+ {
+#if SHOPT_NAMESPACE
+ if(!(nq = nv_search((char*)np,shp->var_base,HASH_BUCKET)))
+#endif /* SHOPT_NAMESPACE */
+ nq = np;
+ shp->last_root = shp->var_tree->walk;
+ if((flags&NV_NOSCOPE) && *cp!='.')
+ {
+ if(mode==0)
+ root = shp->var_tree->walk;
+ else
+ {
+ nv_delete(np,(Dt_t*)0,NV_NOFREE);
+ np = 0;
+ }
+ }
+ }
+ else
+ {
+ if(shp->var_tree->walk)
+ root = shp->var_tree->walk;
+ flags |= NV_NOSCOPE;
+ noscope = 1;
+ }
+ }
+ if(rp && rp->sdict && (flags&NV_STATIC))
+ {
+ root = rp->sdict;
+ if(np && shp->var_tree->walk==shp->var_tree)
+ {
+ _nv_unset(np,0);
+ nv_delete(np,shp->var_tree,0);
+ np = 0;
+ }
+ if(!np || shp->var_tree->walk!=root)
+ np = nv_search(name,root,HASH_NOSCOPE|NV_ADD);
+ }
+ }
+#if SHOPT_NAMESPACE
+ if(!np && !noscope && *name!='.' && shp->namespace && root==shp->var_tree)
+ root = nv_dict(shp->namespace);
+#endif /* SHOPT_NAMESPACE */
+ if(np || (np = nv_search(name,root,mode)))
+ {
+ isref = nv_isref(np);
+ shp->openmatch = 1;
+ if(top)
+ {
+ if(nq==np)
+ {
+ flags &= ~NV_NOSCOPE;
+ root = shp->last_root;
+ }
+ else if(nq)
+ {
+ if(nv_isnull(np) && c!='.' && (np->nvfun=nv_cover(nq)))
+ np->nvname = nq->nvname;
+ flags |= NV_NOSCOPE;
+ }
+ }
+ else if(add && nv_isnull(np) && c=='.' && cp[1]!='.')
+ nv_setvtree(np);
+#if SHOPT_NAMESPACE
+ if(shp->namespace && root==nv_dict(shp->namespace))
+ {
+ flags |= NV_NOSCOPE;
+ shp->last_table = shp->namespace;
+ }
+#endif /* SHOPT_NAMESPACE */
+ }
+ if(c)
+ *sp = c;
+ top = 0;
+ if(isref)
+ {
+#if SHOPT_FIXEDARRAY
+ int n=0,dim;
+#endif /* SHOPT_FIXEDARRAY */
+#if NVCACHE
+ nvcache.ok = 0;
+#endif
+ if(c=='.') /* don't optimize */
+ shp->argaddr = 0;
+ else if((flags&NV_NOREF) && (c!='[' && *cp!='.'))
+ {
+ if(c && !(flags&NV_NOADD))
+ nv_unref(np);
+ return(np);
+ }
+ while(nv_isref(np) && np->nvalue.cp)
+ {
+ root = nv_reftree(np);
+ shp->last_root = root;
+ shp->last_table = nv_reftable(np);
+ sub = nv_refsub(np);
+#if SHOPT_FIXEDARRAY
+ n = nv_refindex(np);
+ dim = nv_refdimen(np);
+#endif /* SHOPT_FIXEDARRAY */
+ np = nv_refnode(np);
+#if SHOPT_FIXEDARRAY
+ if(n)
+ {
+ ap = nv_arrayptr(np);
+ ap->nelem = dim;
+ nv_putsub(np,(char*)0,n);
+ }
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if(sub && c!='.')
+ nv_putsub(np,sub,0L);
+ flags |= NV_NOSCOPE;
+ noscope = 1;
+ }
+ shp->first_root = root;
+ if(nv_isref(np) && (c=='[' || c=='.' || !(flags&NV_ASSIGN)))
+ errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np));
+ if(sub && c==0)
+ {
+ if(flags&NV_ARRAY)
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ nq = nv_opensub(np);
+ if((flags&NV_ASSIGN) && (!nq || nv_isnull(nq)))
+ ap->nelem++;
+ if(!nq)
+ goto addsub;
+ }
+ return(np);
+ }
+ if(np==nq)
+ flags &= ~(noscope?0:NV_NOSCOPE);
+#if SHOPT_FIXEDARRAY
+ else if(c || n)
+#else
+ else if(c)
+#endif /* SHOPT_FIXEDARRAY */
+ {
+#if SHOPT_FIXEDARRAY
+ static char null[1] = "";
+#endif /* SHOPT_FIXEDARRAY */
+ c = (cp-sp);
+ copy = strlen(cp=nv_name(np));
+ dp->nofree |= 1;
+#if SHOPT_FIXEDARRAY
+ if(*sp==0)
+ name = cp;
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ name = copystack(cp,sp,sub);
+ sp = (char*)name + copy;
+ cp = sp+c;
+ c = *sp;
+ if(!noscope)
+ flags &= ~NV_NOSCOPE;
+#if SHOPT_FIXEDARRAY
+ if(c==0)
+ nv_endsubscript(np,null,NV_ADD);
+#endif /* SHOPT_FIXEDARRAY */
+ }
+ flags |= NV_NOREF;
+ if(nv_isnull(np) && !nv_isarray(np))
+ nofree = NV_NOFREE;
+ }
+ shp->last_root = root;
+ if(*cp && cp[1]=='.')
+ cp++;
+ if(c=='.' && (cp[1]==0 || cp[1]=='=' || cp[1]=='+'))
+ {
+ nv_local = 1;
+ if(np)
+ nv_onattr(np,nofree);
+ return(np);
+ }
+ if(cp[-1]=='.')
+ cp--;
+ do
+ {
+#if SHOPT_FIXEDARRAY
+ int fixed;
+#endif /* SHOPT_FIXEDARRAY */
+ if(!np)
+ {
+ if(!nq && *sp=='[' && *cp==0 && cp[-1]==']')
+ {
+ /*
+ * for backward compatibility
+ * evaluate subscript for
+ * possible side effects
+ */
+ cp[-1] = 0;
+ sh_arith(shp,sp+1);
+ cp[-1] = ']';
+ }
+ return(np);
+ }
+#if SHOPT_FIXEDARRAY
+ fixed = 0;
+ if((ap=nv_arrayptr(np)) && ap->fixed)
+ fixed = 1;
+#endif /* SHOPT_FIXEDARRAY */
+ if(c=='[' || (c=='.' && nv_isarray(np)))
+ {
+ int n = 0;
+ sub = 0;
+ mode &= ~HASH_NOSCOPE;
+ if(c=='[')
+ {
+#if SHOPT_FIXEDARRAY
+ Namarr_t *ap = nv_arrayptr(np);
+#endif /* SHOPT_FIXEDARRAY */
+#if 0
+ int scan = ap?(ap->nelem&ARRAY_SCAN):0;
+#endif
+ n = mode|nv_isarray(np);
+ if(!mode && (flags&NV_ARRAY) && ((c=sp[1])=='*' || c=='@') && sp[2]==']')
+ {
+ /* not implemented yet */
+ dp->last = cp;
+ return(np);
+ }
+#if SHOPT_FIXEDARRAY
+ if(fixed)
+ flags |= NV_FARRAY;
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if((n&NV_ADD)&&(flags&NV_ARRAY))
+ n |= ARRAY_FILL;
+ if(flags&NV_ASSIGN)
+ n |= NV_ADD;
+ cp = nv_endsubscript(np,sp,n|(flags&(NV_ASSIGN|NV_FARRAY)));
+#if SHOPT_FIXEDARRAY
+ flags &= ~NV_FARRAY;
+ if(fixed)
+ flags &= ~NV_ARRAY;
+
+#endif /* SHOPT_FIXEDARRAY */
+#if 0
+ if(scan)
+ nv_putsub(np,NIL(char*),ARRAY_SCAN);
+#endif
+ }
+ else
+ cp = sp;
+ if((c = *cp)=='.' || (c=='[' && nv_isarray(np)) || (n&ARRAY_FILL) || (flags&NV_ARRAY))
+
+ {
+ int m = cp-sp;
+ sub = m?nv_getsub(np):0;
+ if(!sub)
+ {
+ if(m && !(n&NV_ADD))
+ return(0);
+ sub = "0";
+ }
+ n = strlen(sub)+2;
+ if(!copy)
+ {
+ copy = cp-name;
+ dp->nofree |= 1;
+ name = copystack((const char*)0, name,(const char*)0);
+ cp = (char*)name+copy;
+ sp = cp-m;
+ }
+ if(n <= m)
+ {
+ if(n)
+ {
+ memcpy(sp+1,sub,n-2);
+ sp[n-1] = ']';
+ }
+ if(n < m)
+ {
+ char *dp = sp+n;
+ while(*dp++=*cp++);
+ cp = sp+n;
+ }
+ }
+ else
+ {
+ int r = n-m;
+ m = sp-name;
+ name = stack_extend(name, cp-1, r);
+ sp = (char*)name + m;
+ *sp = '[';
+ memcpy(sp+1,sub,n-2);
+ sp[n-1] = ']';
+ cp = sp+n;
+
+ }
+ }
+ else if(c==0 && mode && (n=nv_aindex(np))>0)
+ nv_putsub(np,(char*)0,n);
+#if SHOPT_FIXEDARRAY
+ else if(n==0 && !fixed && (c==0 || (c=='[' && !nv_isarray(np))))
+#else
+ else if(n==0 && (c==0 || (c=='[' && !nv_isarray(np))))
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ /* subscript must be 0*/
+ cp[-1] = 0;
+ n = sh_arith(shp,sp+1);
+ cp[-1] = ']';
+ if(n)
+ return(0);
+ if(c)
+ sp = cp;
+ }
+ dp->last = cp;
+ if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY)))
+ {
+ addsub:
+ sp = cp;
+ if(!(nq = nv_opensub(np)))
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ if(!sub && (flags&NV_NOADD))
+ return(0);
+ n = mode|((flags&NV_NOADD)?0:NV_ADD);
+ if(!ap && (n&NV_ADD))
+ {
+ nv_putsub(np,sub,ARRAY_FILL);
+ ap = nv_arrayptr(np);
+ }
+ if(n && ap && !ap->table)
+ ap->table = dtopen(&_Nvdisc,Dtoset);
+ if(ap && ap->table && (nq=nv_search(sub,ap->table,n)))
+ nq->nvenv = (char*)np;
+ if(nq && nv_isnull(nq))
+ nq = nv_arraychild(np,nq,c);
+ }
+ if(nq)
+ {
+ if(c=='.' && !nv_isvtree(nq))
+ {
+ if(flags&NV_NOADD)
+ return(0);
+ nv_setvtree(nq);
+ }
+ nv_onattr(np,nofree);
+ nofree = 0;
+ np = nq;
+ }
+ else if(memcmp(cp,"[0]",3))
+ return(nq);
+ else
+ {
+ /* ignore [0] */
+ dp->last = cp += 3;
+ c = *cp;
+ }
+ }
+ }
+#if SHOPT_FIXEDARRAY
+ else if(nv_isarray(np) && (!fixed || cp[-1]!=']'))
+#else
+ else if(nv_isarray(np))
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ if(c==0 && (flags&NV_MOVE))
+ return(np);
+ nv_putsub(np,NIL(char*),ARRAY_UNDEF);
+ }
+ nv_onattr(np,nofree);
+ nofree = 0;
+ if(c=='.' && (fp=np->nvfun))
+ {
+ for(; fp; fp=fp->next)
+ {
+ if(fp->disc && fp->disc->createf)
+ break;
+ }
+ if(fp)
+ {
+ if((nq = (*fp->disc->createf)(np,cp+1,flags,fp)) == np)
+ {
+ add = NV_ADD;
+ shp->last_table = 0;
+ break;
+ }
+ else if(np=nq)
+ {
+ if((c = *(sp=cp=dp->last=fp->last))==0)
+ {
+ if(nv_isarray(np) && sp[-1]!=']')
+ nv_putsub(np,NIL(char*),ARRAY_UNDEF);
+ return(np);
+ }
+ }
+ }
+ }
+ }
+ while(c=='[');
+ if(c!='.' || cp[1]=='.')
+ return(np);
+ cp++;
+ break;
+ default:
+ dp->last = cp;
+ if((c = mbchar(cp)) && !isaletter(c))
+ return(np);
+ while(xp=cp, c=mbchar(cp), isaname(c));
+ cp = xp;
+ }
+ }
+ return(np);
+}
+
+/*
+ * delete the node <np> from the dictionary <root> and clear from the cache
+ * if <root> is NULL, only the cache is cleared
+ * if flags does not contain NV_NOFREE, the node is freed
+ * if np==0 && !root && flags==0, delete the Refdict dictionary
+ */
+void nv_delete(Namval_t* np, Dt_t *root, int flags)
+{
+#if NVCACHE
+ register int c;
+ struct Cache_entry *xp;
+ for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c])
+ {
+ if(xp->np==np)
+ xp->root = 0;
+ }
+#endif
+ if(!np && !root && flags==0)
+ {
+ if(Refdict)
+ dtclose(Refdict);
+ Refdict = 0;
+ return;
+ }
+ if(root || !(flags&NV_NOFREE))
+ {
+ if(!(flags&NV_FUNCTION) && Refdict)
+ {
+ Namval_t **key = &np;
+ struct Namref *rp;
+ while(rp = (struct Namref*)dtmatch(Refdict,(void*)key))
+ {
+ if(rp->sub)
+ free(rp->sub);
+ rp->sub = 0;
+ rp = dtdelete(Refdict,(void*)rp);
+ rp->np = &NullNode;
+ }
+ }
+ }
+ if(root)
+ {
+ if(dtdelete(root,np))
+ {
+ if(!(flags&NV_NOFREE) && ((flags&NV_FUNCTION) || !nv_subsaved(np)))
+ free((void*)np);
+ }
+#if 0
+ else
+ {
+ sfprintf(sfstderr,"%s not deleted\n",nv_name(np));
+ sfsync(sfstderr);
+ }
+#endif
+ }
+}
+
+/*
+ * Put <arg> into associative memory.
+ * If <flags> & NV_ARRAY then follow array to next subscript
+ * If <flags> & NV_NOARRAY then subscript is not allowed
+ * If <flags> & NV_NOSCOPE then use the current scope only
+ * If <flags> & NV_ASSIGN then assignment is allowed
+ * If <flags> & NV_IDENT then name must be an identifier
+ * If <flags> & NV_VARNAME then name must be a valid variable name
+ * If <flags> & NV_NOADD then node will not be added if not found
+ * If <flags> & NV_NOREF then don't follow reference
+ * If <flags> & NV_NOFAIL then don't generate an error message on failure
+ * If <flags> & NV_STATIC then unset before an assignment
+ * If <flags> & NV_UNJUST then unset attributes before assignment
+ * SH_INIT is only set while initializing the environment
+ */
+Namval_t *nv_open(const char *name, Dt_t *root, int flags)
+{
+ Shell_t *shp = sh_getinterp();
+ register char *cp=(char*)name;
+ register int c;
+ register Namval_t *np=0;
+ Namfun_t fun;
+ int append=0;
+ const char *msg = e_varname;
+ char *fname = 0;
+ int offset = staktell();
+ Dt_t *funroot;
+#if NVCACHE
+ struct Cache_entry *xp;
+#endif
+
+ sh_stats(STAT_NVOPEN);
+ memset(&fun,0,sizeof(fun));
+ shp->openmatch = 0;
+ shp->last_table = 0;
+ if(!root)
+ root = shp->var_tree;
+ shp->last_root = root;
+ if(root==shp->fun_tree)
+ {
+ flags |= NV_NOREF;
+ msg = e_badfun;
+ if(strchr(name,'.'))
+ {
+ name = cp = copystack(0,name,(const char*)0);
+ fname = strrchr(cp,'.');
+ *fname = 0;
+ fun.nofree |= 1;
+ flags &= ~NV_IDENT;
+ funroot = root;
+ root = shp->var_tree;
+ }
+ }
+ else if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN)))
+ {
+ long mode = ((flags&NV_NOADD)?0:NV_ADD);
+ if(flags&NV_NOSCOPE)
+ mode |= HASH_SCOPE|HASH_NOSCOPE;
+ np = nv_search(name,root,mode);
+ if(np && !(flags&NV_REF))
+ {
+ while(nv_isref(np))
+ {
+ shp->last_table = nv_reftable(np);
+ np = nv_refnode(np);
+ }
+ }
+ return(np);
+ }
+ else if(shp->prefix && (flags&NV_ASSIGN))
+ {
+ name = cp = copystack(shp->prefix,name,(const char*)0);
+ fun.nofree |= 1;
+ }
+ c = *(unsigned char*)cp;
+ if(root==shp->alias_tree)
+ {
+ msg = e_aliname;
+ while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') &&
+ (c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT || c==S_COLON));
+ if(shp->subshell && c=='=')
+ root = sh_subaliastree(1);
+ if(c= *--cp)
+ *cp = 0;
+ np = nv_search(name, root, (flags&NV_NOADD)?0:NV_ADD);
+ if(c)
+ *cp = c;
+ goto skip;
+ }
+ else if(flags&NV_IDENT)
+ msg = e_ident;
+ else if(c=='.')
+ {
+ c = *++cp;
+ flags |= NV_NOREF;
+ if(root==shp->var_tree)
+ root = shp->var_base;
+ shp->last_table = 0;
+ }
+ if(c= !isaletter(c))
+ goto skip;
+#if NVCACHE
+ for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c])
+ {
+ if(xp->root!=root)
+ continue;
+ if(*name==*xp->name && (flags&(NV_ARRAY|NV_NOSCOPE))==xp->flags && memcmp(xp->name,name,xp->len)==0 && (name[xp->len]==0 || name[xp->len]=='=' || name[xp->len]=='+'))
+ {
+ sh_stats(STAT_NVHITS);
+ np = xp->np;
+ cp = (char*)name+xp->len;
+ if(nv_isarray(np) && !(flags&NV_MOVE))
+ nv_putsub(np,NIL(char*),ARRAY_UNDEF);
+ shp->last_table = xp->last_table;
+ shp->last_root = xp->last_root;
+ goto nocache;
+ }
+ }
+ nvcache.ok = 1;
+#endif
+ np = nv_create(name, root, flags, &fun);
+ cp = fun.last;
+#if NVCACHE
+ if(np && nvcache.ok && cp[-1]!=']')
+ {
+ xp = &nvcache.entries[nvcache.index];
+ if(*cp)
+ {
+ char *sp = strchr(name,*cp);
+ if(!sp)
+ goto nocache;
+ xp->len = sp-name;
+ }
+ else
+ xp->len = strlen(name);
+ c = roundof(xp->len+1,32);
+ if(c > xp->size)
+ {
+ if(xp->size==0)
+ xp->name = malloc(c);
+ else
+ xp->name = realloc(xp->name,c);
+ xp->size = c;
+ }
+ memcpy(xp->name,name,xp->len);
+ xp->name[xp->len] = 0;
+ xp->root = root;
+ xp->np = np;
+ xp->last_table = shp->last_table;
+ xp->last_root = shp->last_root;
+ xp->flags = (flags&(NV_ARRAY|NV_NOSCOPE));
+ nvcache.index = (nvcache.index+1)&(NVCACHE-1);
+ }
+nocache:
+ nvcache.ok = 0;
+#endif
+ if(fname)
+ {
+ c = ((flags&NV_NOSCOPE)?HASH_NOSCOPE:0)|((flags&NV_NOADD)?0:NV_ADD);
+ *fname = '.';
+ np = nv_search(name, funroot, c);
+ *fname = 0;
+ }
+ else
+ {
+ if(*cp=='.' && cp[1]=='.')
+ {
+ append |= NV_NODISC;
+ cp+=2;
+ }
+ if(*cp=='+' && cp[1]=='=')
+ {
+ append |= NV_APPEND;
+ cp++;
+ }
+ }
+ c = *cp;
+skip:
+#if SHOPT_TYPEDEF
+ if(np && shp->mktype)
+ np = nv_addnode(np,0);
+#endif /* SHOPT_TYPEDEF */
+ if(c=='=' && np && (flags&NV_ASSIGN))
+ {
+ cp++;
+ if(sh_isstate(SH_INIT))
+ {
+ nv_putval(np, cp, NV_RDONLY);
+ if(np==PWDNOD)
+ nv_onattr(np,NV_TAGGED);
+ }
+ else
+ {
+ char *sub=0, *prefix= shp->prefix;
+ Namval_t *mp;
+ Namarr_t *ap;
+ int isref;
+ shp->prefix = 0;
+ if((flags&NV_STATIC) && !shp->mktype)
+ {
+ if(!nv_isnull(np))
+ {
+ shp->prefix = prefix;
+ return(np);
+ }
+ }
+ isref = nv_isref(np);
+#if SHOPT_FIXEDARRAY
+ if(sh_isoption(SH_XTRACE) && (ap=nv_arrayptr(np)) && !ap->fixed)
+#else
+ if(sh_isoption(SH_XTRACE) && nv_isarray(np))
+#endif /* SHOPT_FIXEDARRAY */
+ sub = nv_getsub(np);
+ c = msg==e_aliname? 0: (append | (flags&NV_EXPORT));
+ if(isref)
+ nv_offattr(np,NV_REF);
+ if(!append && (flags&NV_UNJUST))
+ {
+ nv_offattr(np,NV_LJUST|NV_RJUST|NV_ZFILL);
+ np->nvsize = 0;
+ }
+ if(flags&NV_MOVE)
+ {
+ if(ap=nv_arrayptr(np))
+ {
+ if(mp=nv_opensub(np))
+ np = mp;
+ else if(!array_assoc(ap) && (mp = nv_open(cp,shp->var_tree,NV_NOFAIL|NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOADD)) && nv_isvtree(np))
+ {
+ ap->nelem |= ARRAY_TREE;
+ nv_putsub(np,(char*)0,ARRAY_ADD|nv_aindex(np));
+ np = nv_opensub(np);
+ ap->nelem &= ~ARRAY_TREE;
+ ap->nelem -= 1;
+ }
+ }
+ _nv_unset(np,NV_EXPORT);
+ }
+ nv_putval(np, cp, c);
+ if(isref)
+ {
+ if(nv_search((char*)np,shp->var_base,HASH_BUCKET))
+ shp->last_root = shp->var_base;
+ nv_setref(np,(Dt_t*)0,NV_VARNAME);
+ }
+ savesub = sub;
+ shp->prefix = prefix;
+ }
+ nv_onattr(np, flags&NV_ATTRIBUTES);
+ }
+ else if(c)
+ {
+ if(flags&NV_NOFAIL)
+ return(0);
+ if(c=='.')
+ msg = e_noparent;
+ else if(c=='[')
+ msg = e_noarray;
+ errormsg(SH_DICT,ERROR_exit(1),msg,name);
+ }
+ if(fun.nofree&1)
+ stakseek(offset);
+ return(np);
+}
+
+#if SHOPT_MULTIBYTE
+ static int ja_size(char*, int, int);
+ static void ja_restore(void);
+ static char *savep;
+ static char savechars[8+1];
+#endif /* SHOPT_MULTIBYTE */
+
+/*
+ * put value <string> into name-value node <np>.
+ * If <np> is an array, then the element given by the
+ * current index is assigned to.
+ * If <flags> contains NV_RDONLY, readonly attribute is ignored
+ * If <flags> contains NV_INTEGER, string is a pointer to a number
+ * If <flags> contains NV_NOFREE, previous value is freed, and <string>
+ * becomes value of node and <flags> becomes attributes
+ */
+void nv_putval(register Namval_t *np, const char *string, int flags)
+{
+ Shell_t *shp = sh_getinterp();
+ register const char *sp=string;
+ register union Value *up;
+ register char *cp;
+ register int size = 0;
+ register int dot;
+ int was_local = nv_local;
+ union Value u;
+#if SHOPT_FIXEDARRAY
+ Namarr_t *ap;
+#endif /* SHOPT_FIXEDARRAY */
+ if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY))
+ errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
+ /* The following could cause the shell to fork if assignment
+ * would cause a side effect
+ */
+ shp->argaddr = 0;
+ if(shp->subshell && !nv_local)
+ np = sh_assignok(np,1);
+ if(np->nvfun && np->nvfun->disc && !(flags&NV_NODISC) && !nv_isref(np))
+ {
+ /* This function contains disc */
+ if(!nv_local)
+ {
+ nv_local=1;
+ nv_putv(np,sp,flags,np->nvfun);
+ if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
+ sh_envput(shp->env,np);
+ return;
+ }
+ /* called from disc, assign the actual value */
+ }
+ flags &= ~NV_NODISC;
+ nv_local=0;
+ if(flags&(NV_NOREF|NV_NOFREE))
+ {
+ if(np->nvalue.cp && np->nvalue.cp!=sp && !nv_isattr(np,NV_NOFREE))
+ free((void*)np->nvalue.cp);
+ np->nvalue.cp = (char*)sp;
+ nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE);
+ return;
+ }
+ up= &np->nvalue;
+ if(nv_isattr(np,NV_INT16P) == NV_INT16)
+ {
+ if(!np->nvalue.up || !nv_isarray(np))
+ {
+ up = &u;
+ up->up = &np->nvalue;
+ }
+ }
+#if SHOPT_FIXEDARRAY
+ else if(np->nvalue.up && nv_isarray(np) && (ap=nv_arrayptr(np)) && !ap->fixed)
+#else
+ else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np))
+#endif /* SHOPT_FIXEDARRAY */
+ up = np->nvalue.up;
+ if(up && up->cp==Empty)
+ up->cp = 0;
+ if(nv_isattr(np,NV_EXPORT))
+ nv_offattr(np,NV_IMPORT);
+ if(nv_isattr (np, NV_INTEGER))
+ {
+ if(nv_isattr(np, NV_DOUBLE) == NV_DOUBLE)
+ {
+ if(nv_isattr(np, NV_LONG) && sizeof(double)<sizeof(Sfdouble_t))
+ {
+ Sfdouble_t ld, old=0;
+ if(flags&NV_INTEGER)
+ {
+ if(flags&NV_LONG)
+ ld = *((Sfdouble_t*)sp);
+ else if(flags&NV_SHORT)
+ ld = *((float*)sp);
+ else
+ ld = *((double*)sp);
+ }
+ else
+ ld = sh_arith(shp,sp);
+ if(!up->ldp)
+ up->ldp = new_of(Sfdouble_t,0);
+ else if(flags&NV_APPEND)
+ old = *(up->ldp);
+ *(up->ldp) = old?ld+old:ld;
+ }
+ else
+ {
+ double d,od=0;
+ if(flags&NV_INTEGER)
+ {
+ if(flags&NV_LONG)
+ d = (double)(*(Sfdouble_t*)sp);
+ else if(flags&NV_SHORT)
+ d = (double)(*(float*)sp);
+ else
+ d = *(double*)sp;
+ }
+ else
+ d = sh_arith(shp,sp);
+ if(!up->dp)
+ up->dp = new_of(double,0);
+ else if(flags&NV_APPEND)
+ od = *(up->dp);
+ *(up->dp) = od?d+od:d;
+ }
+ }
+ else
+ {
+ if(nv_isattr(np, NV_LONG) && sizeof(int32_t)<sizeof(Sflong_t))
+ {
+ Sflong_t ll=0,oll=0;
+ if(flags&NV_INTEGER)
+ {
+ if((flags&NV_DOUBLE) == NV_DOUBLE)
+ {
+ if(flags&NV_LONG)
+ ll = *((Sfdouble_t*)sp);
+ else if(flags&NV_SHORT)
+ ll = *((float*)sp);
+ else
+ ll = *((double*)sp);
+ }
+ else if(nv_isattr(np,NV_UNSIGN))
+ {
+ if(flags&NV_LONG)
+ ll = *((Sfulong_t*)sp);
+ else if(flags&NV_SHORT)
+ ll = *((uint16_t*)sp);
+ else
+ ll = *((uint32_t*)sp);
+ }
+ else
+ {
+ if(flags&NV_LONG)
+ ll = *((Sflong_t*)sp);
+ else if(flags&NV_SHORT)
+ ll = *((uint16_t*)sp);
+ else
+ ll = *((uint32_t*)sp);
+ }
+ }
+ else if(sp)
+ ll = (Sflong_t)sh_arith(shp,sp);
+ if(!up->llp)
+ up->llp = new_of(Sflong_t,0);
+ else if(flags&NV_APPEND)
+ oll = *(up->llp);
+ *(up->llp) = ll+oll;
+ }
+ else
+ {
+ int32_t l=0,ol=0;
+ if(flags&NV_INTEGER)
+ {
+ if((flags&NV_DOUBLE) == NV_DOUBLE)
+ {
+ Sflong_t ll;
+ if(flags&NV_LONG)
+ ll = *((Sfdouble_t*)sp);
+ else if(flags&NV_SHORT)
+ ll = *((float*)sp);
+ else
+ ll = *((double*)sp);
+ l = (int32_t)ll;
+ }
+ else if(nv_isattr(np,NV_UNSIGN))
+ {
+ if(flags&NV_LONG)
+ l = *((Sfulong_t*)sp);
+ else if(flags&NV_SHORT)
+ l = *((uint16_t*)sp);
+ else
+ l = *(uint32_t*)sp;
+ }
+ else
+ {
+ if(flags&NV_LONG)
+ l = *((Sflong_t*)sp);
+ else if(flags&NV_SHORT)
+ l = *((int16_t*)sp);
+ else
+ l = *(int32_t*)sp;
+ }
+ }
+ else if(sp)
+ {
+ Sfdouble_t ld = sh_arith(shp,sp);
+ if(ld<0)
+ l = (int32_t)ld;
+ else
+ l = (uint32_t)ld;
+ }
+ if(nv_size(np) <= 1)
+ nv_setsize(np,10);
+ if(nv_isattr (np, NV_SHORT))
+ {
+ int16_t s=0;
+ if(flags&NV_APPEND)
+ s = *up->sp;
+ *(up->sp) = s+(int16_t)l;
+ nv_onattr(np,NV_NOFREE);
+ }
+ else
+ {
+ if(!up->lp)
+ up->lp = new_of(int32_t,0);
+ else if(flags&NV_APPEND)
+ ol = *(up->lp);
+ *(up->lp) = l+ol;
+ }
+ }
+ }
+ }
+ else
+ {
+ const char *tofree=0;
+ int offset,append;
+#if _lib_pathnative
+ char buff[PATH_MAX];
+#endif /* _lib_pathnative */
+ if(flags&NV_INTEGER)
+ {
+ if((flags&NV_DOUBLE)==NV_DOUBLE)
+ {
+ if(flags&NV_LONG)
+ sfprintf(shp->strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp));
+ else
+ sfprintf(shp->strbuf,"%.*g",DBL_DIG,*((double*)sp));
+ }
+ else if(flags&NV_UNSIGN)
+ {
+ if(flags&NV_LONG)
+ sfprintf(shp->strbuf,"%I*lu",sizeof(Sfulong_t),*((Sfulong_t*)sp));
+ else
+ sfprintf(shp->strbuf,"%lu",(unsigned long)((flags&NV_SHORT)?*((uint16_t*)sp):*((uint32_t*)sp)));
+ }
+ else
+ {
+ if(flags&NV_LONG)
+ sfprintf(shp->strbuf,"%I*ld",sizeof(Sflong_t),*((Sflong_t*)sp));
+ else
+ sfprintf(shp->strbuf,"%ld",(long)((flags&NV_SHORT)?*((int16_t*)sp):*((int32_t*)sp)));
+ }
+ sp = sfstruse(shp->strbuf);
+ }
+ if(nv_isattr(np, NV_HOST|NV_INTEGER)==NV_HOST && sp)
+ {
+#ifdef _lib_pathnative
+ /*
+ * return the host file name given the UNIX name
+ */
+ pathnative(sp,buff,sizeof(buff));
+ if(buff[1]==':' && buff[2]=='/')
+ {
+ buff[2] = '\\';
+ if(*buff>='A' && *buff<='Z')
+ *buff += 'a'-'A';
+ }
+ sp = buff;
+#else
+ ;
+#endif /* _lib_pathnative */
+ }
+ else if((nv_isattr(np, NV_RJUST|NV_ZFILL|NV_LJUST)) && sp)
+ {
+ for(;*sp == ' '|| *sp=='\t';sp++);
+ if((nv_isattr(np,NV_ZFILL)) && (nv_isattr(np,NV_LJUST)))
+ for(;*sp=='0';sp++);
+ size = nv_size(np);
+#if SHOPT_MULTIBYTE
+ if(size)
+ size = ja_size((char*)sp,size,nv_isattr(np,NV_RJUST|NV_ZFILL));
+#endif /* SHOPT_MULTIBYTE */
+ }
+ if(!up->cp || *up->cp==0)
+ flags &= ~NV_APPEND;
+ if(!nv_isattr(np, NV_NOFREE))
+ {
+ /* delay free in case <sp> points into free region */
+ tofree = up->cp;
+ }
+ if(nv_isattr(np,NV_BINARY) && !(flags&NV_RAW))
+ tofree = 0;
+ if(nv_isattr(np,NV_LJUST|NV_RJUST) && nv_isattr(np,NV_LJUST|NV_RJUST)!=(NV_LJUST|NV_RJUST))
+ tofree = 0;
+ if (sp)
+ {
+ append=0;
+ dot = strlen(sp);
+#if (_AST_VERSION>=20030127L)
+ if(nv_isattr(np,NV_BINARY))
+ {
+ int oldsize = (flags&NV_APPEND)?nv_size(np):0;
+ if(flags&NV_RAW)
+ {
+ if(tofree)
+ {
+ free((void*)tofree);
+ nv_offattr(np,NV_NOFREE);
+ }
+ up->cp = sp;
+ return;
+ }
+ size = 0;
+ if(nv_isattr(np,NV_ZFILL))
+ size = nv_size(np);
+ if(size==0)
+ size = oldsize + (3*dot/4);
+ cp = (char*)malloc(size+1);
+ nv_offattr(np,NV_NOFREE);
+ if(oldsize)
+ memcpy((void*)cp,(void*)up->cp,oldsize);
+ up->cp = cp;
+ if(size <= oldsize)
+ return;
+ dot = base64decode(sp,dot, (void**)0, cp+oldsize, size-oldsize,(void**)0);
+ dot += oldsize;
+ if(!nv_isattr(np,NV_ZFILL) || nv_size(np)==0)
+ nv_setsize(np,dot);
+ else if(nv_isattr(np,NV_ZFILL) && (size>dot))
+ memset((void*)&cp[dot],0,size-dot);
+ return;
+ }
+ else
+#endif
+ {
+ if(size==0 && nv_isattr(np,NV_HOST)!=NV_HOST &&nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
+ nv_setsize(np,size=dot);
+ else if(size > dot)
+ dot = size;
+ else if(nv_isattr(np,NV_LJUST|NV_RJUST)==NV_LJUST && dot>size)
+ dot = size;
+ if(flags&NV_APPEND)
+ {
+ if(dot==0)
+ return;
+ append = strlen(up->cp);
+ if(!tofree || size)
+ {
+ offset = staktell();
+ stakputs(up->cp);
+ stakputs(sp);
+ stakputc(0);
+ sp = stakptr(offset);
+ dot += append;
+ append = 0;
+ }
+ else
+ {
+ flags &= ~NV_APPEND;
+ }
+ }
+ }
+ if(size==0 || tofree || dot || !(cp=(char*)up->cp))
+ {
+ if(dot==0 && !nv_isattr(np,NV_LJUST|NV_RJUST))
+ {
+ cp = Null;
+ nv_onattr(np,NV_NOFREE);
+ }
+ else
+ {
+ if(tofree && tofree!=Empty && tofree!=Null)
+ {
+ cp = (char*)realloc((void*)tofree,((unsigned)dot+append+8));
+ tofree = 0;
+ }
+ else
+ cp = (char*)malloc(((unsigned)dot+8));
+ cp[dot+append] = 0;
+ nv_offattr(np,NV_NOFREE);
+ }
+ }
+
+ }
+ else
+ cp = 0;
+ up->cp = cp;
+ if(sp)
+ {
+ int c = cp[dot+append];
+ memmove(cp+append,sp,dot);
+ cp[dot+append] = c;
+ if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL))
+ rightjust(cp,size,'0');
+ else if(nv_isattr(np, NV_LJUST|NV_RJUST)==NV_RJUST)
+ rightjust(cp,size,' ');
+ else if(nv_isattr(np, NV_LJUST|NV_RJUST)==NV_LJUST)
+ {
+ register char *dp;
+ dp = strlen (cp) + cp;
+ cp = cp+size;
+ for (; dp < cp; *dp++ = ' ');
+ }
+#if SHOPT_MULTIBYTE
+ /* restore original string */
+ if(savep)
+ ja_restore();
+#endif /* SHOPT_MULTIBYTE */
+ }
+ if(flags&NV_APPEND)
+ stakseek(offset);
+ if(tofree && tofree!=Empty && tofree!=Null)
+ free((void*)tofree);
+ }
+ if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT)))
+ sh_envput(shp->env,np);
+ return;
+}
+
+/*
+ *
+ * Right-justify <str> so that it contains no more than
+ * <size> characters. If <str> contains fewer than <size>
+ * characters, left-pad with <fill>. Trailing blanks
+ * in <str> will be ignored.
+ *
+ * If the leftmost digit in <str> is not a digit, <fill>
+ * will default to a blank.
+ */
+static void rightjust(char *str, int size, int fill)
+{
+ register int n;
+ register char *cp,*sp;
+ n = strlen(str);
+
+ /* ignore trailing blanks */
+ for(cp=str+n;n && *--cp == ' ';n--);
+ if (n == size)
+ return;
+ if(n > size)
+ {
+ *(str+n) = 0;
+ for (sp = str, cp = str+n-size; sp <= str+size; *sp++ = *cp++);
+ return;
+ }
+ else *(sp = str+size) = 0;
+ if (n == 0)
+ {
+ while (sp > str)
+ *--sp = ' ';
+ return;
+ }
+ while(n--)
+ {
+ sp--;
+ *sp = *cp--;
+ }
+ if(!isdigit(*str))
+ fill = ' ';
+ while(sp>str)
+ *--sp = fill;
+ return;
+}
+
+#if SHOPT_MULTIBYTE
+ /*
+ * handle left and right justified fields for multi-byte chars
+ * given physical size, return a logical size which reflects the
+ * screen width of multi-byte characters
+ * Multi-width characters replaced by spaces if they cross the boundary
+ * <type> is non-zero for right justified fields
+ */
+
+ static int ja_size(char *str,int size,int type)
+ {
+ register char *cp = str;
+ register int c, n=size;
+ register int outsize;
+ register char *oldcp=cp;
+ int oldn;
+ wchar_t w;
+ while(*cp)
+ {
+ oldn = n;
+ w = mbchar(cp);
+ outsize = mbwidth(w);
+ size -= outsize;
+ c = cp-oldcp;
+ n += (c-outsize);
+ oldcp = cp;
+ if(size<=0 && type==0)
+ break;
+ }
+ /* check for right justified fields that need truncating */
+ if(size <0)
+ {
+ if(type==0)
+ {
+ /* left justified and character crosses field boundary */
+ n = oldn;
+ /* save boundary char and replace with spaces */
+ size = c;
+ savechars[size] = 0;
+ while(size--)
+ {
+ savechars[size] = cp[size];
+ cp[size] = ' ';
+ }
+ savep = cp;
+ }
+ size = -size;
+ if(type)
+ n -= (ja_size(str,size,0)-size);
+ }
+ return(n);
+ }
+
+ static void ja_restore(void)
+ {
+ register char *cp = savechars;
+ while(*cp)
+ *savep++ = *cp++;
+ savep = 0;
+ }
+#endif /* SHOPT_MULTIBYTE */
+
+#ifndef _ENV_H
+static char *staknam(register Namval_t *np, char *value)
+{
+ register char *p,*q;
+ q = stakalloc(strlen(nv_name(np))+(value?strlen(value):0)+2);
+ p=strcopy(q,nv_name(np));
+ if(value)
+ {
+ *p++ = '=';
+ strcpy(p,value);
+ }
+ return(q);
+}
+#endif
+
+/*
+ * put the name and attribute into value of attributes variable
+ */
+#ifdef _ENV_H
+static void attstore(register Namval_t *np, void *data)
+{
+ register int flag, c = ' ';
+ NOT_USED(data);
+ if(!(nv_isattr(np,NV_EXPORT)))
+ return;
+ flag = nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
+ stakputc('=');
+ if((flag&NV_DOUBLE) == NV_DOUBLE)
+ {
+ /* export doubles as integers for ksh88 compatibility */
+ stakputc(c+NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE)));
+ }
+ else
+ {
+ stakputc(c+flag);
+ if(flag&NV_INTEGER)
+ c += nv_size(np);
+ }
+ stakputc(c);
+ stakputs(nv_name(np));
+}
+#else
+static void attstore(register Namval_t *np, void *data)
+{
+ register int flag = np->nvflag;
+ register struct adata *ap = (struct adata*)data;
+ ap->sh = sh_getinterp();
+ ap->tp = 0;
+ if(!(flag&NV_EXPORT) || (flag&NV_FUNCT))
+ return;
+ if((flag&(NV_UTOL|NV_LTOU|NV_INTEGER)) == (NV_UTOL|NV_LTOU))
+ {
+ data = (void*)nv_mapchar(np,0);
+ if(strcmp(data,e_tolower) && strcmp(data,e_toupper))
+ return;
+ }
+ flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
+ *ap->attval++ = '=';
+ if((flag&NV_DOUBLE) == NV_DOUBLE)
+ {
+ /* export doubles as integers for ksh88 compatibility */
+ *ap->attval++ = ' '+ NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE));
+ *ap->attval = ' ';
+ }
+ else
+ {
+ *ap->attval++ = ' '+flag;
+ if(flag&NV_INTEGER)
+ *ap->attval = ' ' + nv_size(np);
+ else
+ *ap->attval = ' ';
+ }
+ ap->attval = strcopy(++ap->attval,nv_name(np));
+}
+#endif
+
+#ifndef _ENV_H
+static void pushnam(Namval_t *np, void *data)
+{
+ register char *value;
+ register struct adata *ap = (struct adata*)data;
+ ap->sh = sh_getinterp();
+ ap->tp = 0;
+ if(nv_isattr(np,NV_IMPORT) && np->nvenv)
+ *ap->argnam++ = np->nvenv;
+ else if(value=nv_getval(np))
+ *ap->argnam++ = staknam(np,value);
+ if(nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER))
+ ap->attsize += (strlen(nv_name(np))+4);
+}
+#endif
+
+/*
+ * Generate the environment list for the child.
+ */
+
+#ifdef _ENV_H
+char **sh_envgen(void)
+{
+ Shell_t *shp = sh_getinterp();
+ int offset,tell;
+ register char **er;
+ env_delete(shp->env,"_");
+ er = env_get(shp->env);
+ offset = staktell();
+ stakputs(e_envmarker);
+ tell = staktell();
+ nv_scan(shp->var_tree, attstore,(void*)0,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
+ if(tell ==staktell())
+ stakseek(offset);
+ else
+ *--er = stakfreeze(1)+offset;
+ return(er);
+}
+#else
+char **sh_envgen(void)
+{
+ register char **er;
+ register int namec;
+ register char *cp;
+ struct adata data;
+ Shell_t *shp = sh_getinterp();
+ data.sh = shp;
+ data.tp = 0;
+ data.mapname = 0;
+ /* L_ARGNOD gets generated automatically as full path name of command */
+ nv_offattr(L_ARGNOD,NV_EXPORT);
+ data.attsize = 6;
+ namec = nv_scan(shp->var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT);
+ namec += shp->nenv;
+ er = (char**)stakalloc((namec+4)*sizeof(char*));
+ data.argnam = (er+=2) + shp->nenv;
+ if(shp->nenv)
+ memcpy((void*)er,environ,shp->nenv*sizeof(char*));
+ nv_scan(shp->var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT);
+ *data.argnam = (char*)stakalloc(data.attsize);
+ cp = data.attval = strcopy(*data.argnam,e_envmarker);
+ nv_scan(shp->var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
+ *data.attval = 0;
+ if(cp!=data.attval)
+ data.argnam++;
+ *data.argnam = 0;
+ return(er);
+}
+#endif
+
+struct scan
+{
+ void (*scanfn)(Namval_t*, void*);
+ int scanmask;
+ int scanflags;
+ int scancount;
+ void *scandata;
+};
+
+static int scanfilter(Dt_t *dict, void *arg, void *data)
+{
+ register Namval_t *np = (Namval_t*)arg;
+ register int k=np->nvflag;
+ register struct scan *sp = (struct scan*)data;
+ register struct adata *tp = (struct adata*)sp->scandata;
+ char *cp;
+ NOT_USED(dict);
+#if SHOPT_TYPEDEF
+ if(!is_abuiltin(np) && tp && tp->tp && nv_type(np)!=tp->tp)
+ return(0);
+#endif /*SHOPT_TYPEDEF */
+ if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags)))
+ {
+ if(tp && tp->mapname)
+ {
+ if(sp->scanflags==NV_FUNCTION || sp->scanflags==(NV_NOFREE|NV_BINARY|NV_RAW))
+ {
+ int n = strlen(tp->mapname);
+ if(memcmp(np->nvname,tp->mapname,n) || np->nvname[n]!='.' || strchr(&np->nvname[n+1],'.'))
+ return(0);
+ }
+ else if((sp->scanflags==NV_UTOL||sp->scanflags==NV_LTOU) && (cp=(char*)nv_mapchar(np,0)) && strcmp(cp,tp->mapname))
+ return(0);
+ }
+ if(!np->nvalue.cp && !np->nvfun && !nv_isattr(np,~NV_DEFAULT))
+ return(0);
+ if(sp->scanfn)
+ {
+ if(nv_isarray(np))
+ nv_putsub(np,NIL(char*),0L);
+ (*sp->scanfn)(np,sp->scandata);
+ }
+ sp->scancount++;
+ }
+ return(0);
+}
+
+/*
+ * Walk through the name-value pairs
+ * if <mask> is non-zero, then only nodes with (nvflags&mask)==flags
+ * are visited
+ * If <mask> is zero, and <flags> non-zero, then nodes with one or
+ * more of <flags> is visited
+ * If <mask> and <flags> are zero, then all nodes are visted
+ */
+int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int flags)
+{
+ Dt_t *base=0;
+ struct scan sdata;
+ int (*hashfn)(Dt_t*, void*, void*);
+ sdata.scanmask = mask;
+ sdata.scanflags = flags&~NV_NOSCOPE;
+ sdata.scanfn = fn;
+ sdata.scancount = 0;
+ sdata.scandata = data;
+ hashfn = scanfilter;
+ if(flags&NV_NOSCOPE)
+ base = dtview((Dt_t*)root,0);
+ dtwalk(root, hashfn,&sdata);
+ if(base)
+ dtview((Dt_t*)root,base);
+ return(sdata.scancount);
+}
+
+/*
+ * create a new environment scope
+ */
+void sh_scope(Shell_t *shp, struct argnod *envlist, int fun)
+{
+ register Dt_t *newscope, *newroot=shp->var_base;
+ struct Ufunction *rp;
+#if SHOPT_NAMESPACE
+ if(shp->namespace)
+ {
+ newroot = nv_dict(shp->namespace);
+ dtview(newroot,(Dt_t*)shp->var_base);
+ }
+#endif /* SHOPT_NAMESPACE */
+ newscope = dtopen(&_Nvdisc,Dtoset);
+ if(envlist)
+ {
+ dtview(newscope,(Dt_t*)shp->var_tree);
+ shp->var_tree = newscope;
+ nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN,0);
+ if(!fun)
+ return;
+ shp->var_tree = dtview(newscope,0);
+ }
+ if((rp=shp->st.real_fun) && rp->sdict)
+ {
+ dtview(rp->sdict,newroot);
+ newroot = rp->sdict;
+
+ }
+ dtview(newscope,(Dt_t*)newroot);
+ shp->var_tree = newscope;
+}
+
+/*
+ * Remove freeable local space associated with the nvalue field
+ * of nnod. This includes any strings representing the value(s) of the
+ * node, as well as its dope vector, if it is an array.
+ */
+
+void sh_envnolocal (register Namval_t *np, void *data)
+{
+ struct adata *tp = (struct adata*)data;
+ char *cp=0;
+ if(np==VERSIONNOD && nv_isref(np))
+ return;
+ if(np==L_ARGNOD)
+ return;
+ if(np == tp->sh->namespace)
+ return;
+ if(nv_isref(np))
+ nv_unref(np);
+ if(nv_isattr(np,NV_EXPORT) && nv_isarray(np))
+ {
+ nv_putsub(np,NIL(char*),0);
+ if(cp = nv_getval(np))
+ cp = strdup(cp);
+ }
+ if(nv_isattr(np,NV_EXPORT|NV_NOFREE))
+ {
+ if(nv_isref(np) && np!=VERSIONNOD)
+ {
+ nv_offattr(np,NV_NOFREE|NV_REF);
+ free((void*)np->nvalue.nrp);
+ np->nvalue.cp = 0;
+ }
+ if(!cp)
+ return;
+ }
+ if(nv_isarray(np))
+ nv_putsub(np,NIL(char*),ARRAY_UNDEF);
+ _nv_unset(np,NV_RDONLY);
+ nv_setattr(np,0);
+ if(cp)
+ {
+ nv_putval(np,cp,0);
+ free((void*)cp);
+ }
+}
+
+/*
+ * Currently this is a dummy, but someday will be needed
+ * for reference counting
+ */
+void nv_close(Namval_t *np)
+{
+ NOT_USED(np);
+}
+
+static void table_unset(Shell_t *shp, register Dt_t *root, int flags, Dt_t *oroot)
+{
+ register Namval_t *np,*nq, *npnext;
+ for(np=(Namval_t*)dtfirst(root);np;np=npnext)
+ {
+ if(nq=dtsearch(oroot,np))
+ {
+ if(nv_cover(nq))
+ {
+ int subshell = shp->subshell;
+ shp->subshell = 0;
+ if(nv_isattr(nq, NV_INTEGER))
+ {
+ Sfdouble_t d = nv_getnum(nq);
+ nv_putval(nq,(char*)&d,NV_LDOUBLE);
+ }
+ else if(shp->test&4)
+ nv_putval(nq, strdup(nv_getval(nq)), NV_RDONLY);
+ else
+ nv_putval(nq, nv_getval(nq), NV_RDONLY);
+ shp->subshell = subshell;
+ np->nvfun = 0;
+ }
+ if(nv_isattr(nq,NV_EXPORT))
+ sh_envput(shp->env,nq);
+ }
+ npnext = (Namval_t*)dtnext(root,np);
+ shp->last_root = root;
+ shp->last_table = 0;
+ if(nv_isvtree(np))
+ {
+ int len = strlen(np->nvname);
+ while((nq=npnext) && memcmp(np->nvname,nq->nvname,len)==0 && nq->nvname[len]=='.')
+
+ {
+ npnext = (Namval_t*)dtnext(root,nq);
+ _nv_unset(nq,flags);
+ nv_delete(nq,root,0);
+ }
+ }
+ _nv_unset(np,flags);
+ nv_delete(np,root,0);
+ }
+}
+
+/*
+ *
+ * Set the value of <np> to 0, and nullify any attributes
+ * that <np> may have had. Free any freeable space occupied
+ * by the value of <np>. If <np> denotes an array member, it
+ * will retain its attributes.
+ * <flags> can contain NV_RDONLY to override the readonly attribute
+ * being cleared.
+ * <flags> can contain NV_EXPORT to override preserve nvenv
+ */
+void _nv_unset(register Namval_t *np,int flags)
+{
+ Shell_t *shp = sh_getinterp();
+ register union Value *up;
+#if SHOPT_FIXEDARRAY
+ Namarr_t *ap;
+#endif /* SHOPT_FIXEDARRAY */
+ if(!(flags&NV_RDONLY) && nv_isattr (np,NV_RDONLY))
+ errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np));
+ if(is_afunction(np) && np->nvalue.ip)
+ {
+ register struct slnod *slp = (struct slnod*)(np->nvenv);
+ if(slp && !nv_isattr(np,NV_NOFREE))
+ {
+ struct Ufunction *rq,*rp = np->nvalue.rp;
+ /* free function definition */
+ register char *name=nv_name(np),*cp= strrchr(name,'.');
+ if(cp)
+ {
+ Namval_t *npv;
+ *cp = 0;
+ npv = nv_open(name,shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD);
+ *cp++ = '.';
+ if(npv && npv!=shp->namespace)
+ nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv);
+ }
+ if(rp->fname && shp->fpathdict && (rq = (struct Ufunction*)nv_search(rp->fname,shp->fpathdict,0)))
+ {
+ do
+ {
+ if(rq->np != np)
+ continue;
+ dtdelete(shp->fpathdict,rq);
+ break;
+ }
+ while(rq = (struct Ufunction*)dtnext(shp->fpathdict,rq));
+ }
+ if(rp->sdict)
+ {
+ Namval_t *mp, *nq;
+ for(mp=(Namval_t*)dtfirst(rp->sdict);mp;mp=nq)
+ {
+ nq = dtnext(rp->sdict,mp);
+ _nv_unset(mp,NV_RDONLY);
+ nv_delete(mp,rp->sdict,0);
+ }
+ dtclose(rp->sdict);
+ }
+ stakdelete(slp->slptr);
+ free((void*)np->nvalue.ip);
+ np->nvalue.ip = 0;
+ }
+ goto done;
+ }
+ if(shp->subshell)
+ np = sh_assignok(np,0);
+ nv_offattr(np,NV_NODISC);
+ if(np->nvfun && !nv_isref(np))
+ {
+ /* This function contains disc */
+ if(!nv_local)
+ {
+ nv_local=1;
+ nv_putv(np,NIL(char*),flags,np->nvfun);
+ nv_local=0;
+ return;
+ }
+ /* called from disc, assign the actual value */
+ nv_local=0;
+ }
+ if(nv_isattr(np,NV_INT16P) == NV_INT16)
+ {
+ np->nvalue.cp = nv_isarray(np)?Empty:0;
+ goto done;
+ }
+#if SHOPT_FIXEDARRAY
+ else if(np->nvalue.up && nv_isarray(np) && (ap=nv_arrayptr(np)) && !ap->fixed)
+#else
+ else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np))
+#endif /* SHOPT_FIXEDARRAY */
+ up = np->nvalue.up;
+ else if(nv_isref(np) && !nv_isattr(np,NV_EXPORT|NV_MINIMAL) && np->nvalue.nrp)
+ {
+
+ if(np->nvalue.nrp->root)
+ dtdelete(Refdict,(void*)np->nvalue.nrp);
+ if(np->nvalue.nrp->sub)
+ free(np->nvalue.nrp->sub);
+ free((void*)np->nvalue.nrp);
+ np->nvalue.cp = 0;
+ up = 0;
+ }
+ else
+ up = &np->nvalue;
+ if(up && up->cp)
+ {
+ if(up->cp!=Empty && up->cp!=Null && !nv_isattr(np, NV_NOFREE))
+ free((void*)up->cp);
+ up->cp = 0;
+ }
+done:
+ if(!nv_isarray(np) || !nv_arrayptr(np))
+ {
+ nv_setsize(np,0);
+ if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT))
+ {
+ if(nv_isattr(np,NV_EXPORT) && !strchr(np->nvname,'['))
+ env_delete(shp->env,nv_name(np));
+ if(!(flags&NV_EXPORT) || nv_isattr(np,NV_IMPORT|NV_EXPORT)==(NV_IMPORT|NV_EXPORT))
+ np->nvenv = 0;
+ nv_setattr(np,0);
+ }
+ else
+ {
+ nv_setattr(np,NV_MINIMAL);
+ nv_delete(np,(Dt_t*)0,0);
+ }
+ }
+}
+
+/*
+ * return the node pointer in the highest level scope
+ */
+Namval_t *sh_scoped(Shell_t *shp, register Namval_t *np)
+{
+ if(!dtvnext(shp->var_tree))
+ return(np);
+ return(dtsearch(shp->var_tree,np));
+}
+
+#if 1
+/*
+ * return space separated list of names of variables in given tree
+ */
+static char *tableval(Dt_t *root)
+{
+ static Sfio_t *out;
+ register Namval_t *np;
+ register int first=1;
+ register Dt_t *base = dtview(root,0);
+ if(out)
+ sfseek(out,(Sfoff_t)0,SEEK_SET);
+ else
+ out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
+ for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
+ {
+ if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
+ {
+ if(!first)
+ sfputc(out,' ');
+ else
+ first = 0;
+ sfputr(out,np->nvname,-1);
+ }
+ }
+ sfputc(out,0);
+ if(base)
+ dtview(root,base);
+ return((char*)out->_data);
+}
+#endif
+
+#if SHOPT_OPTIMIZE
+struct optimize
+{
+ Namfun_t hdr;
+ Shell_t *sh;
+ char **ptr;
+ struct optimize *next;
+ Namval_t *np;
+};
+
+static struct optimize *opt_free;
+
+static void optimize_clear(Namval_t* np, Namfun_t *fp)
+{
+ struct optimize *op = (struct optimize*)fp;
+ nv_stack(np,fp);
+ nv_stack(np,(Namfun_t*)0);
+ for(;op && op->np==np; op=op->next)
+ {
+ if(op->ptr)
+ {
+ *op->ptr = 0;
+ op->ptr = 0;
+ }
+ }
+}
+
+static void put_optimize(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ nv_putv(np,val,flags,fp);
+ optimize_clear(np,fp);
+}
+
+static Namfun_t *clone_optimize(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ return((Namfun_t*)0);
+}
+
+static const Namdisc_t optimize_disc = {sizeof(struct optimize),put_optimize,0,0,0,0,clone_optimize};
+
+void nv_optimize(Namval_t *np)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namfun_t *fp;
+ register struct optimize *op, *xp;
+ if(shp->argaddr)
+ {
+ if(np==SH_LINENO)
+ {
+ shp->argaddr = 0;
+ return;
+ }
+ for(fp=np->nvfun; fp; fp = fp->next)
+ {
+ if(fp->disc && (fp->disc->getnum || fp->disc->getval))
+ {
+ shp->argaddr = 0;
+ return;
+ }
+ if(fp->disc== &optimize_disc)
+ break;
+ }
+ if((xp= (struct optimize*)fp) && xp->ptr==shp->argaddr)
+ return;
+ if(op = opt_free)
+ opt_free = op->next;
+ else
+ op=(struct optimize*)calloc(1,sizeof(struct optimize));
+ op->ptr = shp->argaddr;
+ op->np = np;
+ if(xp)
+ {
+ op->hdr.disc = 0;
+ op->next = xp->next;
+ xp->next = op;
+ }
+ else
+ {
+ op->hdr.disc = &optimize_disc;
+ op->next = (struct optimize*)shp->optlist;
+ shp->optlist = (void*)op;
+ nv_stack(np,&op->hdr);
+ }
+ }
+}
+
+void sh_optclear(Shell_t *shp, void *old)
+{
+ register struct optimize *op,*opnext;
+ for(op=(struct optimize*)shp->optlist; op; op = opnext)
+ {
+ opnext = op->next;
+ if(op->ptr && op->hdr.disc)
+ {
+ nv_stack(op->np,&op->hdr);
+ nv_stack(op->np,(Namfun_t*)0);
+ }
+ op->next = opt_free;
+ opt_free = op;
+ }
+ shp->optlist = old;
+}
+
+#else
+# define optimize_clear(np,fp)
+#endif /* SHOPT_OPTIMIZE */
+
+/*
+ * Return a pointer to a character string that denotes the value
+ * of <np>. If <np> refers to an array, return a pointer to
+ * the value associated with the current index.
+ *
+ * If the value of <np> is an integer, the string returned will
+ * be overwritten by the next call to nv_getval.
+ *
+ * If <np> has no value, 0 is returned.
+ */
+
+char *nv_getval(register Namval_t *np)
+{
+ Shell_t *shp = sh_getinterp();
+ register union Value *up= &np->nvalue;
+ register int numeric;
+#if SHOPT_OPTIMIZE
+ if(!nv_local && shp->argaddr)
+ nv_optimize(np);
+#endif /* SHOPT_OPTIMIZE */
+ if((!np->nvfun || !np->nvfun->disc) && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF))
+ goto done;
+ if(nv_isref(np))
+ {
+ if(!np->nvalue.cp)
+ return(0);
+ shp->last_table = nv_reftable(np);
+ return(nv_name(nv_refnode(np)));
+ }
+ if(np->nvfun && np->nvfun->disc)
+ {
+ if(!nv_local)
+ {
+ nv_local=1;
+ return(nv_getv(np, np->nvfun));
+ }
+ nv_local=0;
+ }
+ numeric = ((nv_isattr (np, NV_INTEGER)) != 0);
+ if(numeric)
+ {
+ Sflong_t ll;
+ if(!up->cp)
+ return("0");
+ if(nv_isattr (np,NV_DOUBLE)==NV_DOUBLE)
+ {
+ Sfdouble_t ld;
+ double d;
+ char *format;
+ if(nv_isattr(np,NV_LONG))
+ {
+ ld = *up->ldp;
+ if(nv_isattr (np,NV_EXPNOTE))
+ format = "%.*Lg";
+ else if(nv_isattr (np,NV_HEXFLOAT))
+ format = "%.*La";
+ else
+ format = "%.*Lf";
+ sfprintf(shp->strbuf,format,nv_size(np),ld);
+ }
+ else
+ {
+ d = *up->dp;
+ if(nv_isattr (np,NV_EXPNOTE))
+ format = "%.*g";
+ else if(nv_isattr (np,NV_HEXFLOAT))
+ format = "%.*a";
+ else
+ format = "%.*f";
+ sfprintf(shp->strbuf,format,nv_size(np),d);
+ }
+ return(sfstruse(shp->strbuf));
+ }
+ else if(nv_isattr(np,NV_UNSIGN))
+ {
+ if(nv_isattr (np,NV_LONG))
+ ll = *(Sfulong_t*)up->llp;
+ else if(nv_isattr (np,NV_SHORT))
+ {
+ if(nv_isattr(np,NV_INT16P)==NV_INT16P)
+ ll = *(uint16_t*)(up->sp);
+ else
+ ll = (uint16_t)up->s;
+ }
+ else
+ ll = *(uint32_t*)(up->lp);
+ }
+ else if(nv_isattr (np,NV_LONG))
+ ll = *up->llp;
+ else if(nv_isattr (np,NV_SHORT))
+ {
+ if(nv_isattr(np,NV_INT16P)==NV_INT16P)
+ ll = *up->sp;
+ else
+ ll = up->s;
+ }
+ else
+ ll = *(up->lp);
+ if((numeric=nv_size(np))==10)
+ {
+ if(nv_isattr(np,NV_UNSIGN))
+ {
+ sfprintf(shp->strbuf,"%I*u",sizeof(ll),ll);
+ return(sfstruse(shp->strbuf));
+ }
+ numeric = 0;
+ }
+ return(fmtbase(ll,numeric, numeric&&numeric!=10));
+ }
+done:
+#if (_AST_VERSION>=20030127L)
+ /*
+ * if NV_RAW flag is on, return pointer to binary data
+ * otherwise, base64 encode the data and return this string
+ */
+ if(up->cp && nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW))
+ {
+ char *cp;
+ char *ep;
+ int size= nv_size(np), insize=(4*size)/3+size/45+8;
+ base64encode(up->cp, size, (void**)0, cp=getbuf(insize), insize, (void**)&ep);
+ *ep = 0;
+ return(cp);
+ }
+#endif
+ if(!nv_isattr(np,NV_LJUST|NV_RJUST) && (numeric=nv_size(np)) && up->cp && up->cp[numeric])
+ {
+ char *cp = getbuf(numeric+1);
+ memcpy(cp,up->cp,numeric);
+ cp[numeric]=0;
+ return(cp);
+ }
+ return ((char*)up->cp);
+}
+
+Sfdouble_t nv_getnum(register Namval_t *np)
+{
+ Shell_t *shp = sh_getinterp();
+ register union Value *up;
+ register Sfdouble_t r=0;
+ register char *str;
+#if SHOPT_OPTIMIZE
+ if(!nv_local && shp->argaddr)
+ nv_optimize(np);
+#endif /* SHOPT_OPTIMIZE */
+ if(nv_istable(np))
+ errormsg(SH_DICT,ERROR_exit(1),e_number,nv_name(np));
+ if(np->nvfun && np->nvfun->disc)
+ {
+ if(!nv_local)
+ {
+ nv_local=1;
+ return(nv_getn(np, np->nvfun));
+ }
+ nv_local=0;
+ }
+ if(nv_isref(np))
+ {
+ str = nv_refsub(np);
+ np = nv_refnode(np);
+ if(str)
+ nv_putsub(np,str,0L);
+ }
+ if(nv_isattr (np, NV_INTEGER))
+ {
+ up= &np->nvalue;
+ if(!up->lp || up->cp==Empty)
+ r = 0;
+ else if(nv_isattr(np, NV_DOUBLE)==NV_DOUBLE)
+ {
+ if(nv_isattr(np, NV_LONG))
+ r = *up->ldp;
+ else
+ r = *up->dp;
+ }
+ else if(nv_isattr(np, NV_UNSIGN))
+ {
+ if(nv_isattr(np, NV_LONG))
+ r = (Sflong_t)*((Sfulong_t*)up->llp);
+ else if(nv_isattr(np, NV_SHORT))
+ {
+ if(nv_isattr(np,NV_INT16P)==NV_INT16P)
+ r = (Sflong_t)(*(uint16_t*)up->sp);
+ else
+ r = (Sflong_t)((uint16_t)up->s);
+ }
+ else
+ r = *((uint32_t*)up->lp);
+ }
+ else
+ {
+ if(nv_isattr(np, NV_LONG))
+ r = *up->llp;
+ else if(nv_isattr(np, NV_SHORT))
+ {
+ if(nv_isattr(np,NV_INT16P)==NV_INT16P)
+ r = *up->sp;
+ else
+ r = up->s;
+ }
+ else
+ r = *up->lp;
+ }
+ }
+ else if((str=nv_getval(np)) && *str!=0)
+ {
+ if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
+ {
+ while(*str=='0')
+ str++;
+ }
+ r = sh_arith(shp,str);
+ }
+ return(r);
+}
+
+/*
+ * Give <np> the attributes <newatts,> and change its current
+ * value to conform to <newatts>. The <size> of left and right
+ * justified fields may be given.
+ */
+void nv_newattr (register Namval_t *np, unsigned newatts, int size)
+{
+ Shell_t *shp = sh_getinterp();
+ register char *sp;
+ register char *cp = 0;
+ register unsigned int n;
+ Namval_t *mp = 0;
+ Namarr_t *ap = 0;
+ int oldsize,oldatts,trans;
+ Namfun_t *fp= (newatts&NV_NODISC)?np->nvfun:0;
+ char *prefix = shp->prefix,*sub;
+ newatts &= ~NV_NODISC;
+
+ /* check for restrictions */
+ if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) || sp==nv_name(FPATHNOD)))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np));
+ /* handle attributes that do not change data separately */
+ n = np->nvflag;
+ trans = !(n&NV_INTEGER) && (n&(NV_LTOU|NV_UTOL));
+ if(newatts&NV_EXPORT)
+ nv_offattr(np,NV_IMPORT);
+ if(((n^newatts)&NV_EXPORT))
+ {
+ /* record changes to the environment */
+ if(n&NV_EXPORT)
+ env_delete(shp->env,nv_name(np));
+ else
+ sh_envput(shp->env,np);
+ }
+ oldsize = nv_size(np);
+ if((size==oldsize|| (n&NV_INTEGER)) && !trans && ((n^newatts)&~NV_NOCHANGE)==0)
+ {
+ if(size)
+ nv_setsize(np,size);
+ nv_offattr(np, ~NV_NOFREE);
+ nv_onattr(np, newatts);
+ return;
+ }
+ /* for an array, change all the elements */
+ if((ap=nv_arrayptr(np)) && ap->nelem>0)
+ nv_putsub(np,NIL(char*),ARRAY_SCAN);
+ oldsize = nv_size(np);
+ oldatts = np->nvflag;
+ if(fp)
+ np->nvfun = 0;
+ if(ap) /* add element to prevent array deletion */
+ {
+ ap->nelem++;
+#if SHOPT_FIXEDARRAY
+ if(ap->fixed)
+ {
+ nv_setsize(np,size);
+ np->nvflag &= NV_ARRAY;
+ np->nvflag |= newatts;
+ goto skip;
+ }
+#endif /* SHOPT_TYPEDEF */
+ }
+ do
+ {
+ nv_setsize(np,oldsize);
+ np->nvflag = oldatts;
+ if (sp = nv_getval(np))
+ {
+ if(nv_isattr(np,NV_ZFILL))
+ while(*sp=='0') sp++;
+ cp = (char*)malloc((n=strlen (sp)) + 8);
+ strcpy(cp, sp);
+ if(sp && (mp=nv_opensub(np)))
+ {
+ sub = nv_getsub(mp);
+ if(trans)
+ {
+ nv_disc(np, &ap->hdr,NV_POP);
+ nv_clone(np,mp,0);
+ nv_disc(np, &ap->hdr,NV_FIRST);
+ nv_offattr(mp,NV_ARRAY);
+ }
+ nv_newattr(mp,newatts&~NV_ARRAY,size);
+ }
+ if(!mp)
+ {
+ if(ap)
+ ap->nelem &= ~ARRAY_SCAN;
+ if(!trans)
+ _nv_unset(np,NV_RDONLY|NV_EXPORT);
+ if(ap)
+ ap->nelem |= ARRAY_SCAN;
+ }
+ if(size==0 && (newatts&NV_HOST)!=NV_HOST && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL)))
+ size = n;
+ }
+ else if(!trans)
+ _nv_unset(np,NV_EXPORT);
+ nv_setsize(np,size);
+ np->nvflag &= (NV_ARRAY|NV_NOFREE);
+ np->nvflag |= newatts;
+ if (cp)
+ {
+ if(!mp)
+ nv_putval (np, cp, NV_RDONLY);
+ free(cp);
+ }
+ }
+ while(ap && nv_nextsub(np));
+#if SHOPT_FIXEDARRAY
+skip:
+#endif /* SHOPT_TYPEDEF */
+ if(fp)
+ np->nvfun = fp;
+ if(ap)
+ ap->nelem--;
+ shp->prefix = prefix;
+ return;
+}
+
+static char *oldgetenv(const char *string)
+{
+ register char c0,c1;
+ register const char *cp, *sp;
+ register char **av = environ;
+ if(!string || (c0= *string)==0)
+ return(0);
+ if((c1=*++string)==0)
+ c1= '=';
+ while(cp = *av++)
+ {
+ if(cp[0]!=c0 || cp[1]!=c1)
+ continue;
+ sp = string;
+ while(*sp && *sp++ == *++cp);
+ if(*sp==0 && *++cp=='=')
+ return((char*)(cp+1));
+ }
+ return(0);
+}
+
+/*
+ * This version of getenv uses the hash storage to access environment values
+ */
+char *sh_getenv(const char *name)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namval_t *np;
+ if(!shp->var_tree)
+ {
+#if 0
+ if(name[0] == 'P' && name[1] == 'A' && name[2] == 'T' && name[3] == 'H' && name[4] == 0 || name[0] == 'L' && ((name[1] == 'C' || name[1] == 'D') && name[2] == '_' || name[1] == 'A' && name[1] == 'N') || name[0] == 'V' && name[1] == 'P' && name[2] == 'A' && name[3] == 'T' && name[4] == 'H' && name[5] == 0 || name[0] == '_' && name[1] == 'R' && name[2] == 'L' && name[3] == 'D' || name[0] == '_' && name[1] == 'A' && name[2] == 'S' && name[3] == 'T' && name[4] == '_')
+#endif
+ return(oldgetenv(name));
+ }
+ else if((np = nv_search(name,shp->var_tree,0)) && nv_isattr(np,NV_EXPORT))
+ return(nv_getval(np));
+ return(0);
+}
+
+#ifndef _NEXT_SOURCE
+/*
+ * Some dynamic linkers will make this file see the libc getenv(),
+ * so sh_getenv() is used for the astintercept() callback. Plain
+ * getenv() is provided for static links.
+ */
+char *getenv(const char *name)
+{
+ return sh_getenv(name);
+}
+#endif /* _NEXT_SOURCE */
+
+#undef putenv
+/*
+ * This version of putenv uses the hash storage to assign environment values
+ */
+int putenv(const char *name)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namval_t *np;
+ if(name)
+ {
+ np = nv_open(name,shp->var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
+ if(!strchr(name,'='))
+ _nv_unset(np,0);
+ }
+ return(0);
+}
+
+/*
+ * Override libast setenviron().
+ */
+char* sh_setenviron(const char *name)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namval_t *np;
+ if(name)
+ {
+ np = nv_open(name,shp->var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN);
+ if(strchr(name,'='))
+ return(nv_getval(np));
+ _nv_unset(np,0);
+ }
+ return("");
+}
+
+/*
+ * Same linker dance as with getenv() above.
+ */
+char* setenviron(const char *name)
+{
+ return sh_setenviron(name);
+}
+
+/*
+ * normalize <cp> and return pointer to subscript if any
+ * if <eq> is specified, return pointer to first = not in a subscript
+ */
+static char *lastdot(char *cp, int eq)
+{
+ register char *ep=0;
+ register int c;
+ if(eq)
+ cp++;
+ while(c= mbchar(cp))
+ {
+ if(c=='[')
+ {
+ if(*cp==']')
+ cp++;
+ else
+ cp = nv_endsubscript((Namval_t*)0,ep=cp,0);
+ }
+ else if(c=='.')
+ {
+ if(*cp=='[')
+ {
+ cp = nv_endsubscript((Namval_t*)0,ep=cp,0);
+ if((ep=sh_checkid(ep+1,cp)) < cp)
+ cp=strcpy(ep,cp);
+ }
+ ep = 0;
+ }
+ else if(eq && c == '=')
+ return(cp-1);
+ }
+ return(eq?0:ep);
+}
+
+int nv_rename(register Namval_t *np, int flags)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namval_t *mp=0,*nr=0;
+ register char *cp;
+ int r=0,arraynp=0,arraynr,index= -1;
+ Namval_t *last_table = shp->last_table;
+ Dt_t *last_root = shp->last_root;
+ Dt_t *hp = 0;
+ char *nvenv=0,*prefix=shp->prefix;
+ Namarr_t *ap,*aq=0;
+ if(nv_isattr(np,NV_PARAM) && shp->st.prevst)
+ {
+ if(!(hp=(Dt_t*)shp->st.prevst->save_tree))
+ hp = dtvnext(shp->var_tree);
+ }
+ if(!nv_isattr(np,NV_MINIMAL))
+ nvenv = np->nvenv;
+ if(nvenv || (cp = nv_name(np)) && nv_isarray(np) && cp[strlen(cp)-1] == ']')
+ arraynp = 1;
+ if(!(cp=nv_getval(np)))
+ {
+ if(flags&NV_MOVE)
+ errormsg(SH_DICT,ERROR_exit(1),e_varname,"");
+ return(0);
+ }
+ if(lastdot(cp,0) && nv_isattr(np,NV_MINIMAL))
+ errormsg(SH_DICT,ERROR_exit(1),e_varname,nv_name(np));
+ arraynr = cp[strlen(cp)-1] == ']';
+ if(nv_isarray(np) && !(mp=nv_opensub(np)))
+ index=nv_aindex(np);
+ shp->prefix = 0;
+ if(!hp)
+ hp = shp->var_tree;
+ if(!(nr = nv_open(cp, hp, flags|NV_ARRAY|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
+ {
+#if SHOPT_NAMESPACE
+ if(shp->namespace)
+ hp = nv_dict(shp->namespace);
+ else
+#endif /* SHOPT_NAMESPACE */
+ hp = shp->var_base;
+ }
+ else if(shp->last_root)
+ hp = shp->last_root;
+ if(!nr)
+ nr= nv_open(cp, hp, flags|NV_NOREF|((flags&NV_MOVE)?0:NV_NOFAIL));
+ shp->prefix = prefix;
+ if(!nr)
+ {
+ if(!nv_isvtree(np))
+ _nv_unset(np,0);
+ return(0);
+ }
+ if(!mp && index>=0 && nv_isvtree(nr))
+ {
+ sfprintf(shp->strbuf,"%s[%d]%c",nv_name(np),index,0);
+ /* create a virtual node */
+ if(mp = nv_open(sfstruse(shp->strbuf),shp->var_tree,NV_VARNAME|NV_ADD|NV_ARRAY))
+ {
+ if(ap = nv_arrayptr(np))
+ ap->nelem++;
+ mp->nvenv = nvenv = (void*)np;
+ }
+ }
+ if(mp)
+ {
+ nvenv = (char*)np;
+ np = mp;
+ }
+ if(nr==np)
+ {
+ if(index<0)
+ return(0);
+ if(cp = nv_getval(np))
+ cp = strdup(cp);
+ }
+ _nv_unset(np,NV_EXPORT);
+ if(nr==np)
+ {
+ nv_putsub(np,(char*)0, index);
+ nv_putval(np,cp,0);
+ free((void*)cp);
+ return(1);
+ }
+ shp->prev_table = shp->last_table;
+ shp->prev_root = shp->last_root;
+ shp->last_table = last_table;
+ shp->last_root = last_root;
+ if(flags&NV_MOVE)
+ {
+ if(arraynp && !nv_isattr(np,NV_MINIMAL) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)))
+ ap->nelem++;
+ }
+ if(((aq=nv_arrayptr(nr)) && !arraynr) || nv_isvtree(nr))
+ {
+ if(ap=nv_arrayptr(np))
+ {
+ if(!ap->table)
+ ap->table = dtopen(&_Nvdisc,Dtoset);
+ if(ap->table)
+ mp = nv_search(nv_getsub(np),ap->table,NV_ADD);
+ nv_arraychild(np,mp,0);
+ nvenv = (void*)np;
+ }
+ else
+ mp = np;
+ nv_clone(nr,mp,(flags&NV_MOVE)|NV_COMVAR);
+ mp->nvenv = nvenv;
+ if(flags&NV_MOVE)
+ {
+ if(arraynr && !nv_isattr(nr,NV_MINIMAL) && (mp=(Namval_t*)nr->nvenv) && (ap=nv_arrayptr(mp)))
+ {
+ nv_putsub(mp,nr->nvname,0);
+ _nv_unset(mp,0);
+ }
+ nv_delete(nr,(Dt_t*)0,NV_NOFREE);
+ }
+ }
+ else
+ {
+ nv_putval(np,nv_getval(nr),0);
+ if(flags&NV_MOVE)
+ _nv_unset(nr,0);
+ }
+ return(1);
+}
+
+/*
+ * Create a reference node from <np> to $np in dictionary <hp>
+ */
+void nv_setref(register Namval_t *np, Dt_t *hp, int flags)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namval_t *nq=0, *nr=0;
+ register char *ep,*cp;
+ Dt_t *root = shp->last_root, *hpnext=0;
+ Namarr_t *ap=0;
+ int openmatch;
+ if(nv_isref(np))
+ return;
+ if(nv_isarray(np))
+ errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np));
+ if(!(cp=nv_getval(np)))
+ {
+ _nv_unset(np,0);
+ nv_onattr(np,NV_REF);
+ return;
+ }
+ if((ep = lastdot(cp,0)) && nv_isattr(np,NV_MINIMAL))
+ errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np));
+ if(hp)
+ hpnext = dtvnext(hp);
+ if((nr=nv_open(cp, hp?hp:shp->var_tree, flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
+ nq = nr;
+ else if(hpnext && dtvnext(hpnext)==shp->var_base && (nr=nv_open(cp,hpnext,flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
+ nq = nr;
+ else if((openmatch=shp->openmatch) && hpnext==shp->var_base && (nr=nv_open(cp,hpnext,flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)))
+ nq = nr;
+ if(nq)
+ hp = shp->last_root;
+ else
+ hp = hp?(openmatch?hp:shp->var_base):shp->var_tree;
+ if(nr==np)
+ {
+ if(shp->namespace && nv_dict(shp->namespace)==hp)
+ errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np));
+ /* bind to earlier scope, or add to global scope */
+ if(!(hp=dtvnext(hp)) || (nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET))==np)
+ errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np));
+ if(nv_isarray(nq))
+ nv_putsub(nq,(char*)0,ARRAY_UNDEF);
+ }
+#if SHOPT_FIXEDARRAY
+ if(nq && ep && nv_isarray(nq) && !((ap=nv_arrayptr(nq)) && ap->fixed) && !nv_getsub(nq))
+#else
+ if(nq && ep && nv_isarray(nq) && !nv_getsub(nq))
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ if(!nv_arrayptr(nq))
+ {
+ nv_putsub(nq,"1",ARRAY_FILL);
+ _nv_unset(nq,NV_RDONLY);
+ }
+ nv_endsubscript(nq,ep-1,NV_ARRAY);
+ }
+ if(!nr)
+ {
+ shp->last_root = 0;
+ nr= nq = nv_open(cp, hp, flags);
+ if(shp->last_root)
+ hp = shp->last_root;
+ }
+ if(shp->last_root == shp->var_tree && root!=shp->var_tree)
+ {
+ _nv_unset(np,NV_RDONLY);
+ nv_onattr(np,NV_REF);
+ errormsg(SH_DICT,ERROR_exit(1),e_globalref,nv_name(np));
+ }
+ shp->instance = 1;
+ if(nq && !ep && (ap=nv_arrayptr(nq)) && !(ap->nelem&(ARRAY_UNDEF|ARRAY_SCAN)))
+ ep = nv_getsub(nq);
+#if SHOPT_FIXEDARRAY
+ if(ep && !(ap && ap->fixed))
+#else
+ if(ep)
+#endif /* SHOPT_FIXEDARRAY */
+ {
+ /* cause subscript evaluation and return result */
+ if(nv_isarray(nq))
+ ep = nv_getsub(nq);
+ else
+ {
+ int n;
+ ep[n=strlen(ep)-1] = 0;
+ nv_putsub(nr, ep, ARRAY_FILL);
+ ep[n] = ']';
+ if(nq = nv_opensub(nr))
+ ep = 0;
+ else
+ ep = nv_getsub(nq=nr);
+ }
+ }
+ shp->instance = 0;
+ shp->last_root = root;
+ _nv_unset(np,0);
+ nv_delete(np,(Dt_t*)0,0);
+ np->nvalue.nrp = newof(0,struct Namref,1,sizeof(Dtlink_t));
+ np->nvalue.nrp->np = nq;
+ np->nvalue.nrp->root = hp;
+ if(ep)
+ {
+#if SHOPT_FIXEDARRAY
+ if(ap && ap->fixed)
+ np->nvalue.nrp->curi = ARRAY_FIXED|nv_arrfixed(nq,(Sfio_t*)0,1,&np->nvalue.nrp->dim);
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ np->nvalue.nrp->sub = strdup(ep);
+ }
+ np->nvalue.nrp->table = shp->last_table;
+ nv_onattr(np,NV_REF|NV_NOFREE);
+ if(!Refdict)
+ {
+ NullNode.nvname = ".deleted";
+ NullNode.nvflag = NV_RDONLY;
+ Refdict = dtopen(&_Refdisc,Dtobag);
+ }
+ dtinsert(Refdict,np->nvalue.nrp);
+}
+
+/*
+ * get the scope corresponding to <index>
+ * whence uses the same values as lseeek()
+ */
+Shscope_t *sh_getscope(int index, int whence)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct sh_scoped *sp, *topmost;
+ if(whence==SEEK_CUR)
+ sp = &shp->st;
+ else
+ {
+ if ((struct sh_scoped*)shp->topscope != shp->st.self)
+ topmost = (struct sh_scoped*)shp->topscope;
+ else
+ topmost = &(shp->st);
+ sp = topmost;
+ if(whence==SEEK_SET)
+ {
+ int n =0;
+ while(sp = sp->prevst)
+ n++;
+ index = n - index;
+ sp = topmost;
+ }
+ }
+ if(index < 0)
+ return((Shscope_t*)0);
+ while(index-- && (sp = sp->prevst));
+ return((Shscope_t*)sp);
+}
+
+/*
+ * make <scoped> the top scope and return previous scope
+ */
+Shscope_t *sh_setscope(Shscope_t *scope)
+{
+ Shell_t *shp = sh_getinterp();
+ Shscope_t *old = (Shscope_t*)shp->st.self;
+ *shp->st.self = shp->st;
+ shp->st = *((struct sh_scoped*)scope);
+ shp->var_tree = scope->var_tree;
+ SH_PATHNAMENOD->nvalue.cp = shp->st.filename;
+ SH_FUNNAMENOD->nvalue.cp = shp->st.funname;
+ return(old);
+}
+
+void sh_unscope(Shell_t *shp)
+{
+ register Dt_t *root = shp->var_tree;
+ register Dt_t *dp = dtview(root,(Dt_t*)0);
+ table_unset(shp,root,NV_RDONLY|NV_NOSCOPE,dp);
+ if(shp->st.real_fun && dp==shp->st.real_fun->sdict)
+ {
+ dp = dtview(dp,(Dt_t*)0);
+ shp->st.real_fun->sdict->view = dp;
+ }
+ shp->var_tree=dp;
+ dtclose(root);
+}
+
+/*
+ * The inverse of creating a reference node
+ */
+void nv_unref(register Namval_t *np)
+{
+ Namval_t *nq;
+ if(!nv_isref(np))
+ return;
+ nv_offattr(np,NV_NOFREE|NV_REF);
+ if(!np->nvalue.nrp)
+ return;
+ nq = nv_refnode(np);
+ if(Refdict)
+ {
+ if(np->nvalue.nrp->sub)
+ free(np->nvalue.nrp->sub);
+ dtdelete(Refdict,(void*)np->nvalue.nrp);
+ }
+ free((void*)np->nvalue.nrp);
+ np->nvalue.cp = strdup(nv_name(nq));
+#if SHOPT_OPTIMIZE
+ {
+ Namfun_t *fp;
+ for(fp=nq->nvfun; fp; fp = fp->next)
+ {
+ if(fp->disc== &optimize_disc)
+ {
+ optimize_clear(nq,fp);
+ return;
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * These following are for binary compatibility with the old hash library
+ * They will be removed someday
+ */
+
+#if defined(__IMPORT__) && defined(__EXPORT__)
+# define extern __EXPORT__
+#endif
+
+#undef hashscope
+
+extern Dt_t *hashscope(Dt_t *root)
+{
+ return(dtvnext(root));
+}
+
+#undef hashfree
+
+extern Dt_t *hashfree(Dt_t *root)
+{
+ Dt_t *dp = dtvnext(root);
+ dtclose(root);
+ return(dp);
+}
+
+#undef hashname
+
+extern char *hashname(void *obj)
+{
+ Namval_t *np = (Namval_t*)obj;
+ return(np->nvname);
+}
+
+#undef hashlook
+
+extern void *hashlook(Dt_t *root, const char *name, int mode,int size)
+{
+ NOT_USED(size);
+ return((void*)nv_search(name,root,mode));
+}
+
+char *nv_name(register Namval_t *np)
+{
+ Shell_t *shp = sh_getinterp();
+ register Namval_t *table;
+ register Namfun_t *fp;
+#if SHOPT_FIXEDARRAY
+ Namarr_t *ap;
+#endif /* SHOPT_FIXEDARRAY */
+ char *cp;
+ if(is_abuiltin(np) || is_afunction(np))
+ {
+#if SHOPT_NAMESPACE
+ if(shp->namespace && is_afunction(np))
+ {
+ char *name = nv_name(shp->namespace);
+ int n = strlen(name);
+ if(memcmp(np->nvname,name,n)==0 && np->nvname[n]=='.')
+ return(np->nvname+n+1);
+ }
+#endif /* SHOPT_NAMESPACE */
+ return(np->nvname);
+ }
+#if SHOPT_FIXEDARRAY
+ ap = nv_arrayptr(np);
+#endif /* SHOPT_FIXEDARRAY */
+ if(!nv_isattr(np,NV_MINIMAL|NV_EXPORT) && np->nvenv)
+ {
+ Namval_t *nq= shp->last_table, *mp= (Namval_t*)np->nvenv;
+ if(np==shp->last_table)
+ shp->last_table = 0;
+ if(nv_isarray(mp))
+ sfprintf(shp->strbuf,"%s[%s]",nv_name(mp),np->nvname);
+ else
+ sfprintf(shp->strbuf,"%s.%s",nv_name(mp),np->nvname);
+ shp->last_table = nq;
+ return(sfstruse(shp->strbuf));
+ }
+ if(nv_istable(np))
+#if 1
+ shp->last_table = nv_parent(np);
+#else
+ shp->last_table = nv_create(np,0, NV_LAST,(Namfun_t*)0);
+#endif
+ else if(!nv_isref(np))
+ {
+ for(fp= np->nvfun ; fp; fp=fp->next)
+ if(fp->disc && fp->disc->namef)
+ {
+ if(np==shp->last_table)
+ shp->last_table = 0;
+ return((*fp->disc->namef)(np,fp));
+ }
+ }
+ if(!(table=shp->last_table) || *np->nvname=='.' || table==shp->namespace || np==table)
+ {
+#if SHOPT_FIXEDARRAY
+ if(!ap || !ap->fixed || (ap->nelem&ARRAY_UNDEF))
+ return(np->nvname);
+ table = 0;
+#else
+ return(np->nvname);
+#endif /* SHOPT_FIXEDARRAY */
+ }
+ if(table)
+ {
+ cp = nv_name(table);
+ sfprintf(shp->strbuf,"%s.%s",cp,np->nvname);
+ }
+ else
+ sfprintf(shp->strbuf,"%s",np->nvname);
+#if SHOPT_FIXEDARRAY
+ if(ap && ap->fixed)
+ nv_arrfixed(np,shp->strbuf,1,(char*)0);
+#endif /* SHOPT_FIXEDARRAY */
+ return(sfstruse(shp->strbuf));
+}
+
+Namval_t *nv_lastdict(void)
+{
+ Shell_t *shp = sh_getinterp();
+ return(shp->last_table);
+}
+
+#undef nv_context
+/*
+ * returns the data context for a builtin
+ */
+void *nv_context(Namval_t *np)
+{
+ return((void*)np->nvfun);
+}
+
+#define DISABLE /* proto workaround */
+
+int nv_isnull DISABLE (register Namval_t *np)
+{
+ return(nv_isnull(np));
+}
+
+#undef nv_setsize
+int nv_setsize(register Namval_t *np, int size)
+{
+ int oldsize = nv_size(np);
+ if(size>=0)
+ np->nvsize = size;
+ return(oldsize);
+}
+
+Shell_t *nv_shell(Namval_t *np)
+{
+ Namfun_t *fp;
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if(!fp->disc)
+ return((Shell_t*)fp->last);
+ }
+ return(0);
+}
+
+#undef nv_unset
+
+void nv_unset(register Namval_t *np)
+{
+ _nv_unset(np,0);
+ return;
+}
diff --git a/src/cmd/ksh93/sh/nvdisc.c b/src/cmd/ksh93/sh/nvdisc.c
new file mode 100644
index 0000000..45a7edb
--- /dev/null
+++ b/src/cmd/ksh93/sh/nvdisc.c
@@ -0,0 +1,1468 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include "variables.h"
+#include "builtins.h"
+#include "path.h"
+
+static void assign(Namval_t*,const char*,int,Namfun_t*);
+
+int nv_compare(Dt_t* dict, Void_t *sp, Void_t *dp, Dtdisc_t *disc)
+{
+ if(sp==dp)
+ return(0);
+ return(strcmp((char*)sp,(char*)dp));
+}
+
+/*
+ * call the next getval function in the chain
+ */
+char *nv_getv(Namval_t *np, register Namfun_t *nfp)
+{
+ register Namfun_t *fp;
+ register char *cp;
+ if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
+ fp = nfp = nfp->next;
+ nv_local=0;
+ for(; fp; fp=fp->next)
+ {
+ if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
+ continue;
+ if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
+ break;
+ }
+ if(fp && fp->disc->getval)
+ cp = (*fp->disc->getval)(np,fp);
+ else if(fp && fp->disc->getnum)
+ {
+ sfprintf(sh.strbuf,"%.*Lg",12,(*fp->disc->getnum)(np,fp));
+ cp = sfstruse(sh.strbuf);
+ }
+ else
+ {
+ nv_local=1;
+ cp = nv_getval(np);
+ }
+ return(cp);
+}
+
+/*
+ * call the next getnum function in the chain
+ */
+Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
+{
+ register Namfun_t *fp;
+ register Sfdouble_t d=0;
+ Shell_t *shp = sh_getinterp();
+ char *str;
+ if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
+ fp = nfp = nfp->next;
+ nv_local=0;
+ for(; fp; fp=fp->next)
+ {
+ if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
+ continue;
+ if(!fp->disc->getnum && nv_isattr(np,NV_INTEGER))
+ continue;
+ if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
+ break;
+ }
+ if(fp && fp->disc && fp->disc->getnum)
+ d = (*fp->disc->getnum)(np,fp);
+ else if(nv_isattr(np,NV_INTEGER))
+ {
+ nv_local = 1;
+ d = nv_getnum(np);
+ }
+ else
+ {
+ if(fp && fp->disc && fp->disc->getval)
+ str = (*fp->disc->getval)(np,fp);
+ else
+ str = nv_getv(np,fp?fp:nfp);
+ if(str && *str)
+ {
+ if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
+ {
+ while(*str=='0')
+ str++;
+ }
+ d = sh_arith(shp,str);
+ }
+ }
+ return(d);
+}
+
+/*
+ * call the next assign function in the chain
+ */
+void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp)
+{
+ register Namfun_t *fp, *fpnext;
+ Namarr_t *ap;
+ if((fp=nfp) != NIL(Namfun_t*) && !nv_local)
+ fp = nfp = nfp->next;
+ nv_local=0;
+ if(flags&NV_NODISC)
+ fp = 0;
+ for(; fp; fp=fpnext)
+ {
+ fpnext = fp->next;
+ if(!fp->disc || !fp->disc->putval)
+ {
+ if(!value && (!(ap=nv_arrayptr(np)) || ap->nelem==0))
+ {
+ if(fp->disc || !(fp->nofree&1))
+ nv_disc(np,fp,NV_POP);
+ if(!(fp->nofree&1))
+ free((void*)fp);
+ }
+ continue;
+ }
+ if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
+ break;
+ }
+ if(!value && (flags&NV_TYPE) && fp && fp->disc->putval==assign)
+ fp = 0;
+ if(fp && fp->disc->putval)
+ (*fp->disc->putval)(np,value, flags, fp);
+ else
+ {
+ nv_local=1;
+ if(value)
+ nv_putval(np, value, flags);
+ else
+ _nv_unset(np, flags&(NV_RDONLY|NV_EXPORT));
+ }
+}
+
+#define LOOKUPS 0
+#define ASSIGN 1
+#define APPEND 2
+#define UNASSIGN 3
+#define LOOKUPN 4
+#define BLOCKED ((Namval_t*)&nv_local)
+
+struct vardisc
+{
+ Namfun_t fun;
+ Namval_t *disc[5];
+};
+
+struct blocked
+{
+ struct blocked *next;
+ Namval_t *np;
+ int flags;
+ void *sub;
+ int isub;
+};
+
+static struct blocked *blist;
+
+#define isblocked(bp,type) ((bp)->flags & (1<<(type)))
+#define block(bp,type) ((bp)->flags |= (1<<(type)))
+#define unblock(bp,type) ((bp)->flags &= ~(1<<(type)))
+
+/*
+ * returns pointer to blocking structure
+ */
+static struct blocked *block_info(Namval_t *np, struct blocked *pp)
+{
+ register struct blocked *bp;
+ void *sub=0;
+ int isub=0;
+ if(nv_isarray(np) && (isub=nv_aindex(np)) < 0)
+ sub = nv_associative(np,(const char*)0,NV_ACURRENT);
+ for(bp=blist ; bp; bp=bp->next)
+ {
+ if(bp->np==np && bp->sub==sub && bp->isub==isub)
+ return(bp);
+ }
+ if(pp)
+ {
+ pp->np = np;
+ pp->flags = 0;
+ pp->isub = isub;
+ pp->sub = sub;
+ pp->next = blist;
+ blist = pp;
+ }
+ return(pp);
+}
+
+static void block_done(struct blocked *bp)
+{
+ blist = bp = bp->next;
+ if(bp && (bp->isub>=0 || bp->sub))
+ nv_putsub(bp->np, bp->sub,(bp->isub<0?0:bp->isub)|ARRAY_SETSUB);
+}
+
+/*
+ * free discipline if no more discipline functions
+ */
+static void chktfree(register Namval_t *np, register struct vardisc *vp)
+{
+ register int n;
+ for(n=0; n< sizeof(vp->disc)/sizeof(*vp->disc); n++)
+ {
+ if(vp->disc[n])
+ break;
+ }
+ if(n>=sizeof(vp->disc)/sizeof(*vp->disc))
+ {
+ /* no disc left so pop */
+ Namfun_t *fp;
+ if((fp=nv_stack(np, NIL(Namfun_t*))) && !(fp->nofree&1))
+ free((void*)fp);
+ }
+}
+
+/*
+ * This function performs an assignment disc on the given node <np>
+ */
+static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle)
+{
+ int type = (flags&NV_APPEND)?APPEND:ASSIGN;
+ register struct vardisc *vp = (struct vardisc*)handle;
+ register Namval_t *nq = vp->disc[type];
+ struct blocked block, *bp = block_info(np, &block);
+ Namval_t node;
+ union Value *up = np->nvalue.up;
+#if SHOPT_TYPEDEF
+ Namval_t *tp, *nr;
+ if(val && (tp=nv_type(np)) && (nr=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)) && tp==nv_type(nr))
+ {
+ char *sub = nv_getsub(np);
+ _nv_unset(np,0);
+ if(sub)
+ {
+ nv_putsub(np, sub, ARRAY_ADD);
+ nv_putval(np,nv_getval(nr), 0);
+ }
+ else
+ nv_clone(nr,np,0);
+ goto done;
+ }
+#endif /* SHOPT_TYPEDEF */
+ if(val || isblocked(bp,type))
+ {
+ if(!nq || isblocked(bp,type))
+ {
+ nv_putv(np,val,flags,handle);
+ goto done;
+ }
+ node = *SH_VALNOD;
+ if(!nv_isnull(SH_VALNOD))
+ {
+ nv_onattr(SH_VALNOD,NV_NOFREE);
+ _nv_unset(SH_VALNOD,0);
+ }
+ if(flags&NV_INTEGER)
+ nv_onattr(SH_VALNOD,(flags&(NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_HEXFLOAT|NV_SHORT)));
+ nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?flags:NV_NOFREE);
+ }
+ else
+ nq = vp->disc[type=UNASSIGN];
+ if(nq && !isblocked(bp,type))
+ {
+ int bflag=0;
+ block(bp,type);
+ if (type==APPEND && (bflag= !isblocked(bp,LOOKUPS)))
+ block(bp,LOOKUPS);
+ sh_fun(nq,np,(char**)0);
+ unblock(bp,type);
+ if(bflag)
+ unblock(bp,LOOKUPS);
+ if(!vp->disc[type])
+ chktfree(np,vp);
+ }
+ if(nv_isarray(np))
+ np->nvalue.up = up;
+ if(val)
+ {
+ register char *cp;
+ Sfdouble_t d;
+ if(nv_isnull(SH_VALNOD))
+ cp=0;
+ else if(flags&NV_INTEGER)
+ {
+ d = nv_getnum(SH_VALNOD);
+ cp = (char*)(&d);
+ flags |= (NV_LONG|NV_DOUBLE);
+ flags &= ~NV_SHORT;
+ }
+ else
+ cp = nv_getval(SH_VALNOD);
+ if(cp)
+ nv_putv(np,cp,flags|NV_RDONLY,handle);
+ _nv_unset(SH_VALNOD,0);
+ /* restore everything but the nvlink field */
+ memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
+ }
+ else if(sh_isstate(SH_INIT) || np==SH_FUNNAMENOD)
+ {
+ /* don't free functions during reinitialization */
+ nv_putv(np,val,flags,handle);
+ }
+ else if(!nq || !isblocked(bp,type))
+ {
+ Dt_t *root = sh_subfuntree(1);
+ int n;
+ Namarr_t *ap;
+ block(bp,type);
+ nv_disc(np,handle,NV_POP);
+ nv_putv(np, val, flags, handle);
+ if(sh.subshell)
+ goto done;
+ if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
+ goto done;
+ for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++)
+ {
+ if((nq=vp->disc[n]) && !nv_isattr(nq,NV_NOFREE))
+ {
+ _nv_unset(nq,0);
+ dtdelete(root,nq);
+ }
+ }
+ unblock(bp,type);
+ if(!(handle->nofree&1))
+ free(handle);
+ }
+done:
+ if(bp== &block)
+ block_done(bp);
+}
+
+/*
+ * This function executes a lookup disc and then performs
+ * the lookup on the given node <np>
+ */
+static char* lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle)
+{
+ register struct vardisc *vp = (struct vardisc*)handle;
+ struct blocked block, *bp = block_info(np, &block);
+ register Namval_t *nq = vp->disc[type];
+ register char *cp=0;
+ Namval_t node;
+ union Value *up = np->nvalue.up;
+ if(nq && !isblocked(bp,type))
+ {
+ node = *SH_VALNOD;
+ if(!nv_isnull(SH_VALNOD))
+ {
+ nv_onattr(SH_VALNOD,NV_NOFREE);
+ _nv_unset(SH_VALNOD,0);
+ }
+ if(type==LOOKUPN)
+ {
+ nv_onattr(SH_VALNOD,NV_DOUBLE|NV_INTEGER);
+ nv_setsize(SH_VALNOD,10);
+ }
+ block(bp,type);
+ sh_fun(nq,np,(char**)0);
+ unblock(bp,type);
+ if(!vp->disc[type])
+ chktfree(np,vp);
+ if(type==LOOKUPN)
+ {
+ cp = (char*)(SH_VALNOD->nvalue.cp);
+ *dp = nv_getnum(SH_VALNOD);
+ }
+ else if(cp = nv_getval(SH_VALNOD))
+ cp = stkcopy(stkstd,cp);
+ _nv_unset(SH_VALNOD,NV_RDONLY);
+ if(!nv_isnull(&node))
+ {
+ /* restore everything but the nvlink field */
+ memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
+ }
+ }
+ if(nv_isarray(np))
+ np->nvalue.up = up;
+ if(!cp)
+ {
+ if(type==LOOKUPS)
+ cp = nv_getv(np,handle);
+ else
+ *dp = nv_getn(np,handle);
+ }
+ if(bp== &block)
+ block_done(bp);
+ return(cp);
+}
+
+static char* lookups(Namval_t *np, Namfun_t *handle)
+{
+ return(lookup(np,LOOKUPS,(Sfdouble_t*)0,handle));
+}
+
+static Sfdouble_t lookupn(Namval_t *np, Namfun_t *handle)
+{
+ Sfdouble_t d;
+ lookup(np,LOOKUPN, &d ,handle);
+ return(d);
+}
+
+
+/*
+ * Set disc on given <event> to <action>
+ * If action==np, the current disc is returned
+ * A null return value indicates that no <event> is known for <np>
+ * If <event> is NULL, then return the event name after <action>
+ * If <event> is NULL, and <action> is NULL, return the first event
+ */
+char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
+{
+ register struct vardisc *vp = (struct vardisc*)np->nvfun;
+ register int type;
+ char *empty = "";
+ while(vp)
+ {
+ if(vp->fun.disc && (vp->fun.disc->setdisc || vp->fun.disc->putval == assign))
+ break;
+ vp = (struct vardisc*)vp->fun.next;
+ }
+ if(vp && !vp->fun.disc)
+ vp = 0;
+ if(np == (Namval_t*)fp)
+ {
+ register const char *name;
+ register int getname=0;
+ /* top level call, check for get/set */
+ if(!event)
+ {
+ if(!action)
+ return((char*)nv_discnames[0]);
+ getname=1;
+ event = (char*)action;
+ }
+ for(type=0; name=nv_discnames[type]; type++)
+ {
+ if(strcmp(event,name)==0)
+ break;
+ }
+ if(getname)
+ {
+ event = 0;
+ if(name && !(name = nv_discnames[++type]))
+ action = 0;
+ }
+ if(!name)
+ {
+ for(fp=(Namfun_t*)vp; fp; fp=fp->next)
+ {
+ if(fp->disc && fp->disc->setdisc)
+ return((*fp->disc->setdisc)(np,event,action,fp));
+ }
+ }
+ else if(getname)
+ return((char*)name);
+ }
+ if(!fp)
+ return(NIL(char*));
+ if(np != (Namval_t*)fp)
+ {
+ /* not the top level */
+ while(fp = fp->next)
+ {
+ if(fp->disc && fp->disc->setdisc)
+ return((*fp->disc->setdisc)(np,event,action,fp));
+ }
+ return(NIL(char*));
+ }
+ /* Handle GET/SET/APPEND/UNSET disc */
+ if(vp && vp->fun.disc->putval!=assign)
+ vp = 0;
+ if(!vp)
+ {
+ Namdisc_t *dp;
+ if(action==np)
+ return((char*)action);
+ if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,sizeof(Namdisc_t))))
+ return(0);
+ dp = (Namdisc_t*)(vp+1);
+ vp->fun.disc = dp;
+ memset(dp,0,sizeof(*dp));
+ dp->dsize = sizeof(struct vardisc);
+ dp->putval = assign;
+ if(nv_isarray(np) && !nv_arrayptr(np))
+ nv_putsub(np,(char*)0, 1);
+ nv_stack(np, (Namfun_t*)vp);
+ }
+ if(action==np)
+ {
+ action = vp->disc[type];
+ empty = 0;
+ }
+ else if(action)
+ {
+ Namdisc_t *dp = (Namdisc_t*)vp->fun.disc;
+ if(type==LOOKUPS)
+ dp->getval = lookups;
+ else if(type==LOOKUPN)
+ dp->getnum = lookupn;
+ vp->disc[type] = action;
+ }
+ else
+ {
+ struct blocked *bp;
+ action = vp->disc[type];
+ vp->disc[type] = 0;
+ if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
+ chktfree(np,vp);
+ }
+ return(action?(char*)action:empty);
+}
+
+/*
+ * Set disc on given <event> to <action>
+ * If action==np, the current disc is returned
+ * A null return value indicates that no <event> is known for <np>
+ * If <event> is NULL, then return the event name after <action>
+ * If <event> is NULL, and <action> is NULL, return the first event
+ */
+static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
+{
+ register Nambfun_t *vp = (Nambfun_t*)fp;
+ register int type,getname=0;
+ register const char *name;
+ const char **discnames = vp->bnames;
+ /* top level call, check for discipline match */
+ if(!event)
+ {
+ if(!action)
+ return((char*)discnames[0]);
+ getname=1;
+ event = (char*)action;
+ }
+ for(type=0; name=discnames[type]; type++)
+ {
+ if(strcmp(event,name)==0)
+ break;
+ }
+ if(getname)
+ {
+ event = 0;
+ if(name && !(name = discnames[++type]))
+ action = 0;
+ }
+ if(!name)
+ return(nv_setdisc(np,event,action,fp));
+ else if(getname)
+ return((char*)name);
+ /* Handle the disciplines */
+ if(action==np)
+ action = vp->bltins[type];
+ else if(action)
+ {
+ Namval_t *tp = nv_type(np);
+ if(tp && (np = (Namval_t*)vp->bltins[type]) && nv_isattr(np,NV_STATICF))
+ errormsg(SH_DICT,ERROR_exit(1),e_staticfun,name,tp->nvname);
+ vp->bltins[type] = action;
+ }
+ else
+ {
+ action = vp->bltins[type];
+ vp->bltins[type] = 0;
+ }
+ return(action?(char*)action:"");
+}
+
+static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp)
+{
+ nv_putv(np,val,flag,fp);
+ if(!val && !(flag&NV_NOFREE))
+ {
+ register Nambfun_t *vp = (Nambfun_t*)fp;
+ register int i;
+ for(i=0; vp->bnames[i]; i++)
+ {
+ register Namval_t *mp;
+ if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE))
+ {
+ if(is_abuiltin(mp))
+ {
+ if(mp->nvfun && !nv_isattr(mp,NV_NOFREE))
+ free((void*)mp->nvfun);
+ dtdelete(sh.bltin_tree,mp);
+ free((void*)mp);
+ }
+ }
+ }
+ nv_disc(np,fp,NV_POP);
+ if(!(fp->nofree&1))
+ free((void*)fp);
+
+ }
+}
+
+static const Namdisc_t Nv_bdisc = { 0, putdisc, 0, 0, setdisc };
+
+Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags)
+{
+ register Namfun_t *nfp;
+ register int size;
+ if(!fp->disc && !fp->next && (fp->nofree&1))
+ return(fp);
+ if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize)))
+ size = sizeof(Namfun_t);
+ if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t))))
+ return(0);
+ memcpy(nfp,fp,size);
+ nfp->nofree &= ~1;
+ nfp->nofree |= (flags&NV_RDONLY)?1:0;
+ return(nfp);
+}
+
+int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs)
+{
+ register Nambfun_t *vp;
+ register int n=0;
+ register const char **av=names;
+ if(av)
+ {
+ while(*av++)
+ n++;
+ }
+ if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*))))
+ return(0);
+ vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*);
+ vp->fun.nofree |= 2;
+ vp->num = n;
+ if(funs)
+ memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*));
+ else while(n>=0)
+ vp->bltins[n--] = 0;
+ vp->fun.disc = &Nv_bdisc;
+ vp->bnames = names;
+ nv_stack(np,&vp->fun);
+ return(1);
+}
+
+/*
+ * push, pop, clne, or reorder disciplines onto node <np>
+ * mode can be one of
+ * NV_FIRST: Move or push <fp> to top of the stack or delete top
+ * NV_LAST: Move or push <fp> to bottom of stack or delete last
+ * NV_POP: Delete <fp> from top of the stack
+ * NV_CLONE: Replace fp with a copy created my malloc() and return it
+ */
+Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode)
+{
+ Namfun_t *lp, **lpp;
+ if(nv_isref(np))
+ return(0);
+ if(mode==NV_CLONE && !fp)
+ return(0);
+ if(fp)
+ {
+ fp->subshell = sh.subshell;
+ if((lp=np->nvfun)==fp)
+ {
+ if(mode==NV_CLONE)
+ {
+ lp = nv_clone_disc(fp,0);
+ return(np->nvfun=lp);
+ }
+ if(mode==NV_FIRST || mode==0)
+ return(fp);
+ np->nvfun = lp->next;
+ if(mode==NV_POP)
+ return(fp);
+ if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0))
+ return(fp);
+ }
+ /* see if <fp> is on the list already */
+ lpp = &np->nvfun;
+ if(lp)
+ {
+ while(lp->next && lp->next->disc)
+ {
+ if(lp->next==fp)
+ {
+ if(mode==NV_LAST && fp->next==0)
+ return(fp);
+ if(mode==NV_CLONE)
+ {
+ fp = nv_clone_disc(fp,0);
+ lp->next = fp;
+ return(fp);
+ }
+ lp->next = fp->next;
+ if(mode==NV_POP)
+ return(fp);
+ if(mode!=NV_LAST)
+ break;
+ }
+ lp = lp->next;
+ }
+ if(mode==NV_LAST && lp->disc)
+ lpp = &lp->next;
+ }
+ if(mode==NV_POP)
+ return(0);
+ /* push */
+ nv_offattr(np,NV_NODISC);
+ if(mode==NV_LAST)
+ {
+ if(lp && !lp->disc)
+ fp->next = lp;
+ else
+ fp->next = 0;
+ }
+ else
+ {
+ if((fp->nofree&1) && *lpp)
+ fp = nv_clone_disc(fp,0);
+ fp->next = *lpp;
+ }
+ *lpp = fp;
+ }
+ else
+ {
+ if(mode==NV_FIRST)
+ return(np->nvfun);
+ else if(mode==NV_LAST)
+ for(lp=np->nvfun; lp; fp=lp,lp=lp->next);
+ else if(fp = np->nvfun)
+ np->nvfun = fp->next;
+ }
+ return(fp);
+}
+
+/*
+ * returns discipline pointer if discipline with specified functions
+ * is on the discipline stack
+ */
+Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp)
+{
+ register Namfun_t *fp;
+ for(fp=np->nvfun; fp; fp = fp->next)
+ {
+ if(fp->disc== dp)
+ return(fp);
+ }
+ return(0);
+}
+
+struct notify
+{
+ Namfun_t hdr;
+ char **ptr;
+};
+
+static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ struct notify *pp = (struct notify*)fp;
+ nv_putv(np,val,flags,fp);
+ nv_stack(np,fp);
+ nv_stack(np,(Namfun_t*)0);
+ *pp->ptr = 0;
+ if(!(fp->nofree&1))
+ free((void*)fp);
+}
+
+static const Namdisc_t notify_disc = { 0, put_notify };
+
+int nv_unsetnotify(Namval_t *np, char **addr)
+{
+ register Namfun_t *fp;
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr)
+ {
+ nv_stack(np,fp);
+ nv_stack(np,(Namfun_t*)0);
+ if(!(fp->nofree&1))
+ free((void*)fp);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+int nv_setnotify(Namval_t *np, char **addr)
+{
+ struct notify *pp = newof(0,struct notify, 1,0);
+ if(!pp)
+ return(0);
+ pp->ptr = addr;
+ pp->hdr.disc = &notify_disc;
+ nv_stack(np,&pp->hdr);
+ return(1);
+}
+
+static void *newnode(const char *name)
+{
+ register int s;
+ register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1);
+ if(np)
+ {
+ np->nvname = (char*)np+sizeof(Namval_t);
+ memcpy(np->nvname,name,s);
+ }
+ return((void*)np);
+}
+
+/*
+ * clone a numeric value
+ */
+static void *num_clone(register Namval_t *np, void *val)
+{
+ register int size;
+ void *nval;
+ if(!val)
+ return(0);
+ if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
+ {
+ if(nv_isattr(np,NV_LONG))
+ size = sizeof(Sfdouble_t);
+ else if(nv_isattr(np,NV_SHORT))
+ size = sizeof(float);
+ else
+ size = sizeof(double);
+ }
+ else
+ {
+ if(nv_isattr(np,NV_LONG))
+ size = sizeof(Sflong_t);
+ else if(nv_isattr(np,NV_SHORT))
+ {
+ if(nv_isattr(np,NV_INT16P)==NV_INT16P)
+ size = sizeof(short);
+ else
+ return((void*)np->nvalue.ip);
+ }
+ else
+ size = sizeof(int32_t);
+ }
+ if(!(nval = malloc(size)))
+ return(0);
+ memcpy(nval,val,size);
+ return(nval);
+}
+
+void clone_all_disc( Namval_t *np, Namval_t *mp, int flags)
+{
+ register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext;
+ for(fp=np->nvfun; fp;fp=fpnext)
+ {
+ fpnext = fp->next;
+ if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
+ return;
+ if((fp->nofree&2) && (flags&NV_NODISC))
+ nfp = 0;
+ if(fp->disc && fp->disc->clonef)
+ nfp = (*fp->disc->clonef)(np,mp,flags,fp);
+ else if(flags&NV_MOVE)
+ nfp = fp;
+ else
+ nfp = nv_clone_disc(fp,flags);
+ if(!nfp)
+ continue;
+ nfp->next = 0;
+ *mfp = nfp;
+ mfp = &nfp->next;
+ }
+}
+
+/*
+ * clone <mp> from <np> flags can be one of the following
+ * NV_APPEND - append <np> onto <mp>
+ * NV_MOVE - move <np> to <mp>
+ * NV_NOFREE - mark the new node as nofree
+ * NV_NODISC - discplines with funs non-zero will not be copied
+ * NV_COMVAR - cloning a compound variable
+ */
+int nv_clone(Namval_t *np, Namval_t *mp, int flags)
+{
+ Namfun_t *fp, *fpnext;
+ const char *val = mp->nvalue.cp;
+ unsigned short flag = mp->nvflag;
+ unsigned short size = mp->nvsize;
+ for(fp=mp->nvfun; fp; fp=fpnext)
+ {
+ fpnext = fp->next;
+ if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
+ break;
+ if(!(fp->nofree&1))
+ free((void*)fp);
+ }
+ mp->nvfun = fp;
+ if(fp=np->nvfun)
+ {
+ if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL))
+ {
+ mp->nvenv = 0;
+ nv_offattr(mp,NV_MINIMAL);
+ }
+ if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL)))
+ mp->nvenv = np->nvenv;
+ mp->nvflag &= NV_MINIMAL;
+ mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE);
+ flag = mp->nvflag;
+ clone_all_disc(np, mp, flags);
+ }
+ if(flags&NV_APPEND)
+ return(1);
+ if(mp->nvsize == size)
+ nv_setsize(mp,nv_size(np));
+ if(mp->nvflag == flag)
+ mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL);
+ if(nv_isattr(np,NV_EXPORT))
+ mp->nvflag |= (np->nvflag&NV_MINIMAL);
+ if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER))
+ {
+ if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE))
+ {
+ if(size)
+ mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size);
+ else
+ mp->nvalue.cp = strdup(np->nvalue.cp);
+ nv_offattr(mp,NV_NOFREE);
+ }
+ else if((np->nvfun || !nv_isattr(np,NV_ARRAY)) && !(mp->nvalue.cp = np->nvalue.cp))
+ nv_offattr(mp,NV_NOFREE);
+ }
+ if(flags&NV_MOVE)
+ {
+ if(nv_isattr(np,NV_INTEGER))
+ mp->nvalue.ip = np->nvalue.ip;
+ np->nvfun = 0;
+ np->nvalue.cp = 0;
+ if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
+ {
+ mp->nvenv = np->nvenv;
+ np->nvflag = 0;
+ }
+ else
+ np->nvflag &= NV_MINIMAL;
+ nv_setsize(np,0);
+ return(1);
+ }
+ else if((flags&NV_ARRAY) && !nv_isattr(np,NV_MINIMAL))
+ mp->nvenv = np->nvenv;
+ if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip && np->nvalue.cp!=Empty)
+ {
+ mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip);
+ nv_offattr(mp,NV_NOFREE);
+ }
+ else if((flags&NV_NOFREE) && !nv_arrayptr(np))
+ nv_onattr(np,NV_NOFREE);
+ return(1);
+}
+
+/*
+ * The following discipline is for copy-on-write semantics
+ */
+static char* clone_getv(Namval_t *np, Namfun_t *handle)
+{
+ return(np->nvalue.np?nv_getval(np->nvalue.np):0);
+}
+
+static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
+{
+ return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
+}
+
+static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle)
+{
+ Namfun_t *dp = nv_stack(np,(Namfun_t*)0);
+ Namval_t *mp = np->nvalue.np;
+ if(!sh.subshell)
+ free((void*)dp);
+ if(val)
+ nv_clone(mp,np,NV_NOFREE);
+ np->nvalue.cp = 0;
+ nv_putval(np,val,flags);
+}
+
+static const Namdisc_t clone_disc =
+{
+ 0,
+ clone_putv,
+ clone_getv,
+ clone_getn
+};
+
+Namval_t *nv_mkclone(Namval_t *mp)
+{
+ Namval_t *np;
+ Namfun_t *dp;
+ np = newof(0,Namval_t,1,0);
+ np->nvflag = mp->nvflag;
+ np->nvsize = mp->nvsize;
+ np->nvname = mp->nvname;
+ np->nvalue.np = mp;
+ np->nvflag = mp->nvflag;
+ dp = newof(0,Namfun_t,1,0);
+ dp->disc = &clone_disc;
+ nv_stack(np,dp);
+ dtinsert(nv_dict(sh.namespace),np);
+ return(np);
+}
+
+Namval_t *nv_search(const char *name, Dt_t *root, int mode)
+{
+ register Namval_t *np;
+ register Dt_t *dp = 0;
+ if(mode&HASH_NOSCOPE)
+ dp = dtview(root,0);
+ if(mode&HASH_BUCKET)
+ {
+ Namval_t *mp = (void*)name;
+ if(!(np = dtsearch(root,mp)) && (mode&NV_ADD))
+ name = nv_name(mp);
+ }
+ else
+ {
+ if(*name=='.' && root==sh.var_tree && !dp)
+ root = sh.var_base;
+ np = dtmatch(root,(void*)name);
+ }
+#if SHOPT_COSHELL
+ if(sh.inpool)
+ mode |= HASH_NOSCOPE;
+#endif /* SHOPT_COSHELL */
+ if(!np && (mode&NV_ADD))
+ {
+ if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree)
+ root = nv_dict(sh.namespace);
+ else if(!dp && !(mode&HASH_NOSCOPE))
+ {
+ register Dt_t *next;
+ while(next=dtvnext(root))
+ root = next;
+ }
+ np = (Namval_t*)dtinsert(root,newnode(name));
+ }
+ if(dp)
+ dtview(root,dp);
+ return(np);
+}
+
+/*
+ * finds function or builtin for given name and the discipline variable
+ * if var!=0 the variable pointer is returned and the built-in name
+ * is put onto the stack at the current offset.
+ * otherwise, a pointer to the builtin (variable or type) is returned
+ * and var contains the poiner to the variable
+ * if last==0 and first component of name is a reference, nv_bfsearch()
+ will return 0.
+ */
+Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last)
+{
+ Shell_t *shp = sh_getinterp();
+ int c,offset = staktell();
+ register char *sp, *cp=0;
+ Namval_t *np, *nq;
+ char *dname=0;
+ if(var)
+ *var = 0;
+ /* check for . in the name before = */
+ for(sp=(char*)name+1; *sp; sp++)
+ {
+ if(*sp=='=')
+ return(0);
+ if(*sp=='[')
+ {
+ while(*sp=='[')
+ {
+ sp = nv_endsubscript((Namval_t*)0,(char*)sp,0);
+ if(sp[-1]!=']')
+ return(0);
+ }
+ if(*sp==0)
+ break;
+ if(*sp!='.')
+ return(0);
+ cp = sp;
+ }
+ else if(*sp=='.')
+ cp = sp;
+ }
+ if(!cp)
+ return(var?nv_search(name,root,0):0);
+ stakputs(name);
+ stakputc(0);
+ dname = cp+1;
+ cp = stakptr(offset) + (cp-name);
+ if(last)
+ *last = cp;
+ c = *cp;
+ *cp = 0;
+ nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_NOASSIGN|NV_NOADD|NV_NOFAIL);
+ *cp = c;
+ if(!nq)
+ {
+ np = 0;
+ goto done;
+ }
+ if(!var)
+ {
+ np = nq;
+ goto done;
+ }
+ *var = nq;
+ if(c=='[')
+ nv_endsubscript(nq, cp,NV_NOADD);
+ stakseek(offset);
+#if SHOPT_NAMESPACE
+ if(nv_istable(nq))
+ {
+ Namval_t *nsp = shp->namespace;
+ if(last==0)
+ return(nv_search(name,root,0));
+ shp->namespace = 0;
+ stakputs(nv_name(nq));
+ shp->namespace = nsp;
+ stakputs(dname-1);
+ stakputc(0);
+ np = nv_search(stakptr(offset),root,0);
+ stakseek(offset);
+ return(np);
+ }
+#endif /* SHOPT_NAMESPACE */
+ while(nv_isarray(nq) && !nv_isattr(nq,NV_MINIMAL|NV_EXPORT) && nq->nvenv && nv_isarray((Namval_t*)nq->nvenv))
+ nq = (Namval_t*)nq->nvenv;
+ return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq));
+done:
+ stakseek(offset);
+ return(np);
+}
+
+/*
+ * add or replace built-in version of command corresponding to <path>
+ * The <bltin> argument is a pointer to the built-in
+ * if <extra>==1, the built-in will be deleted
+ * Special builtins cannot be added or deleted return failure
+ * The return value for adding builtins is a pointer to the node or NULL on
+ * failure. For delete NULL means success and the node that cannot be
+ * deleted is returned on failure.
+ */
+Namval_t *sh_addbuiltin(const char *path, Shbltin_f bltin, void *extra)
+{
+ register const char *name = path_basename(path);
+ char *cp;
+ register Namval_t *np, *nq=0;
+ int offset=staktell();
+ if(name==path && bltin!=(Shbltin_f)SYSTYPESET->nvalue.bfp && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp)))
+ path = name = stakptr(offset);
+ if(np = nv_search(path,sh.bltin_tree,0))
+ {
+ /* exists without a path */
+ if(extra == (void*)1)
+ {
+ if(np->nvfun && !nv_isattr(np,NV_NOFREE))
+ free((void*)np->nvfun);
+ dtdelete(sh.bltin_tree,np);
+ return(0);
+ }
+ if(!bltin)
+ return(np);
+ }
+ else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np))
+ {
+ if(strcmp(name,path_basename(nv_name(np))))
+ continue;
+ /* exists probably with different path so delete it */
+ if(strcmp(path,nv_name(np)))
+ {
+ if(nv_isattr(np,BLT_SPC))
+ return(np);
+ if(!bltin)
+ bltin = (Shbltin_f)np->nvalue.bfp;
+ if(np->nvenv)
+ dtdelete(sh.bltin_tree,np);
+ if(extra == (void*)1)
+ return(0);
+ np = 0;
+ }
+ break;
+ }
+ if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0)))
+ return(0);
+ if(nv_isattr(np,BLT_SPC))
+ {
+ if(extra)
+ np->nvfun = (Namfun_t*)extra;
+ return(np);
+ }
+ np->nvenv = 0;
+ np->nvfun = 0;
+ if(bltin)
+ {
+ np->nvalue.bfp = (Nambfp_f)bltin;
+ nv_onattr(np,NV_BLTIN|NV_NOFREE);
+ np->nvfun = (Namfun_t*)extra;
+ }
+ if(nq)
+ {
+ cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
+ nv_close(nq);
+ if(!cp)
+ errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name);
+ }
+ if(extra == (void*)1)
+ return(0);
+ return(np);
+}
+
+#undef nv_stack
+extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
+{
+ return(nv_disc(np,fp,0));
+}
+
+struct table
+{
+ Namfun_t fun;
+ Namval_t *parent;
+ Shell_t *shp;
+ Dt_t *dict;
+};
+
+static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp)
+{
+ struct table *tp = (struct table *)fp;
+ if(root)
+ return((Namval_t*)dtnext(root,np));
+ else
+ return((Namval_t*)dtfirst(tp->dict));
+}
+
+static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp)
+{
+ struct table *tp = (struct table *)fp;
+ tp->shp->last_table = np;
+ return(nv_create(name, tp->dict, flags, fp));
+}
+
+static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ struct table *tp = (struct table*)fp;
+ struct table *ntp = (struct table*)nv_clone_disc(fp,0);
+ Dt_t *oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset);
+ if(!nroot)
+ return(0);
+ memcpy((void*)ntp,(void*)fp,sizeof(struct table));
+ ntp->dict = nroot;
+ ntp->parent = nv_lastdict();
+ for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np))
+ {
+ mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname));
+ nv_clone(np,mp,flags);
+ }
+ return(&ntp->fun);
+}
+
+struct adata
+{
+ Shell_t *sh;
+ Namval_t *tp;
+ char *mapname;
+ char **argnam;
+ int attsize;
+ char *attval;
+};
+
+static void delete_fun(Namval_t *np, void *data)
+{
+ Shell_t *shp = ((struct adata*)data)->sh;
+ nv_delete(np,shp->fun_tree,NV_NOFREE);
+}
+
+static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp)
+{
+ register Dt_t *root = ((struct table*)fp)->dict;
+ register Namval_t *nq, *mp;
+ Namarr_t *ap;
+ struct adata data;
+ nv_putv(np,val,flags,fp);
+ if(val)
+ return;
+ if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap))
+ return;
+ memset(&data,0,sizeof(data));
+ data.mapname = nv_name(np);
+ data.sh = ((struct table*)fp)->shp;
+ nv_scan(data.sh->fun_tree,delete_fun,(void*)&data,NV_FUNCTION,NV_FUNCTION|NV_NOSCOPE);
+ for(mp=(Namval_t*)dtfirst(root);mp;mp=nq)
+ {
+ _nv_unset(mp,flags);
+ nq = (Namval_t*)dtnext(root,mp);
+ dtdelete(root,mp);
+ free((void*)mp);
+ }
+ dtclose(root);
+ if(!(fp->nofree&1))
+ free((void*)fp);
+ np->nvfun = 0;
+}
+
+/*
+ * return space separated list of names of variables in given tree
+ */
+static char *get_table(Namval_t *np, Namfun_t *fp)
+{
+ register Dt_t *root = ((struct table*)fp)->dict;
+ static Sfio_t *out;
+ register int first=1;
+ register Dt_t *base = dtview(root,0);
+ if(out)
+ sfseek(out,(Sfoff_t)0,SEEK_SET);
+ else
+ out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
+ for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
+ {
+ if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
+ {
+ if(!first)
+ sfputc(out,' ');
+ else
+ first = 0;
+ sfputr(out,np->nvname,-1);
+ }
+ }
+ sfputc(out,0);
+ if(base)
+ dtview(root,base);
+ return((char*)out->_data);
+}
+
+static const Namdisc_t table_disc =
+{
+ sizeof(struct table),
+ put_table,
+ get_table,
+ 0,
+ 0,
+ create_table,
+ clone_table,
+ 0,
+ next_table,
+};
+
+Namval_t *nv_parent(Namval_t *np)
+{
+ struct table *tp = (struct table *)nv_hasdisc(np,&table_disc);
+ if(tp)
+ return(tp->parent);
+ return(0);
+}
+
+Dt_t *nv_dict(Namval_t* np)
+{
+ Shell_t *shp=sh_getinterp();
+ struct table *tp = (struct table*)nv_hasdisc(np,&table_disc);
+ if(tp)
+ return(tp->dict);
+ np = shp->last_table;
+ while(np)
+ {
+ if(tp = (struct table*)nv_hasdisc(np,&table_disc))
+ return(tp->dict);
+#if 0
+ np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0);
+#else
+ break;
+#endif
+ }
+ return(shp->var_tree);
+}
+
+int nv_istable(Namval_t *np)
+{
+ return(nv_hasdisc(np,&table_disc)!=0);
+}
+
+/*
+ * create a mountable name-value pair tree
+ */
+Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict)
+{
+ Namval_t *mp, *pp;
+ struct table *tp;
+ if(nv_hasdisc(np,&table_disc))
+ pp = np;
+ else
+ pp = nv_lastdict();
+ if(!(tp = newof((struct table*)0, struct table,1,0)))
+ return(0);
+ if(name)
+ {
+ Namfun_t *fp = pp->nvfun;
+ mp = (*fp->disc->createf)(pp,name,0,fp);
+ }
+ else
+ mp = np;
+ nv_offattr(mp,NV_TABLE);
+ if(!nv_isnull(mp))
+ _nv_unset(mp,NV_RDONLY);
+ tp->shp = sh_getinterp();
+ tp->dict = dict;
+ tp->parent = pp;
+ tp->fun.disc = &table_disc;
+ nv_disc(mp, &tp->fun, NV_FIRST);
+ return(mp);
+}
+
+const Namdisc_t *nv_discfun(int which)
+{
+ switch(which)
+ {
+ case NV_DCADD:
+ return(&Nv_bdisc);
+ case NV_DCRESTRICT:
+ return(&RESTRICTED_disc);
+ }
+ return(0);
+}
+
+int nv_hasget(Namval_t *np)
+{
+ register Namfun_t *fp;
+ for(fp=np->nvfun; fp; fp=fp->next)
+ {
+ if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
+ continue;
+ return(1);
+ }
+ return(0);
+}
+
+#if SHOPT_NAMESPACE
+Namval_t *sh_fsearch(Shell_t *shp, const char *fname, int add)
+{
+ Stk_t *stkp = shp->stk;
+ int offset = stktell(stkp);
+ sfputr(stkp,nv_name(shp->namespace),'.');
+ sfputr(stkp,fname,0);
+ fname = stkptr(stkp,offset);
+ return(nv_search(fname,sh_subfuntree(add&NV_ADD),add));
+}
+#endif /* SHOPT_NAMESPACE */
diff --git a/src/cmd/ksh93/sh/nvtree.c b/src/cmd/ksh93/sh/nvtree.c
new file mode 100644
index 0000000..4cc3a52
--- /dev/null
+++ b/src/cmd/ksh93/sh/nvtree.c
@@ -0,0 +1,1161 @@
+/***********************************************************************
+* *
+* 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
+
+/*
+ * code for tree nodes and name walking
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include "name.h"
+#include "argnod.h"
+#include "lexstates.h"
+
+struct nvdir
+{
+ Dt_t *root;
+ Namval_t *hp;
+ Namval_t *table;
+ Namval_t *otable;
+ Namval_t *(*nextnode)(Namval_t*,Dt_t*,Namfun_t*);
+ Namfun_t *fun;
+ struct nvdir *prev;
+ int len;
+ char *data;
+};
+
+static int Indent;
+char *nv_getvtree(Namval_t*, Namfun_t *);
+static void put_tree(Namval_t*, const char*, int,Namfun_t*);
+static char *walk_tree(Namval_t*, Namval_t*, int);
+
+static int read_tree(Namval_t* np, Sfio_t *iop, int n, Namfun_t *dp)
+{
+ Sfio_t *sp;
+ char *cp;
+ int c;
+ if(n>=0)
+ return(-1);
+ while((c = sfgetc(iop)) && isblank(c));
+ sfungetc(iop,c);
+ sfprintf(sh.strbuf,"%s=%c",nv_name(np),0);
+ cp = sfstruse(sh.strbuf);
+ sp = sfopen((Sfio_t*)0,cp,"s");
+ sfstack(iop,sp);
+ c=sh_eval(iop,SH_READEVAL);
+ return(c);
+}
+
+static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp)
+{
+ register Namfun_t *fp=dp;
+ fp->dsize = 0;
+ while(fp=fp->next)
+ {
+ if(fp->disc && fp->disc->createf)
+ {
+ if(np=(*fp->disc->createf)(np,name,flag,fp))
+ dp->last = fp->last;
+ return(np);
+ }
+ }
+ return((flag&NV_NOADD)?0:np);
+}
+
+static Namfun_t *clone_tree(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp){
+ Namfun_t *dp;
+ if ((flags&NV_MOVE) && nv_type(np))
+ return(fp);
+ dp = nv_clone_disc(fp,flags);
+ if((flags&NV_COMVAR) && !(flags&NV_RAW))
+ {
+ walk_tree(np,mp,flags);
+ if((flags&NV_MOVE) && !(fp->nofree&1))
+ free((void*)fp);
+ }
+ return(dp);
+}
+
+static const Namdisc_t treedisc =
+{
+ 0,
+ put_tree,
+ nv_getvtree,
+ 0,
+ 0,
+ create_tree,
+ clone_tree
+ ,0,0,0,
+ read_tree
+};
+
+static char *nextdot(const char *str)
+{
+ register char *cp;
+ register int c;
+ if(*str=='.')
+ str++;
+ for(cp=(char*)str;c= *cp; cp++)
+ {
+ if(c=='[')
+ {
+ cp = nv_endsubscript((Namval_t*)0,(char*)cp,0);
+ return(*cp=='.'?cp:0);
+ }
+ if(c=='.')
+ return(cp);
+ }
+ return(0);
+}
+
+static Namfun_t *nextdisc(Namval_t *np)
+{
+ register Namfun_t *fp;
+ if(nv_isref(np))
+ return(0);
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if(fp && fp->disc && fp->disc->nextf)
+ return(fp);
+ }
+ return(0);
+}
+
+void *nv_diropen(Namval_t *np,const char *name)
+{
+ char *next,*last;
+ int c,len=strlen(name);
+ struct nvdir *save, *dp = new_of(struct nvdir,len+1);
+ Namval_t *nq=0,fake;
+ Namfun_t *nfp=0;
+ if(!dp)
+ return(0);
+ memset((void*)dp, 0, sizeof(*dp));
+ dp->data = (char*)(dp+1);
+ if(name[len-1]=='*' || name[len-1]=='@')
+ len -= 1;
+ name = memcpy(dp->data,name,len);
+ dp->data[len] = 0;
+ dp->len = len;
+ dp->root = sh.last_root?sh.last_root:sh.var_tree;
+#if 1
+ while(1)
+ {
+ dp->table = sh.last_table;
+ sh.last_table = 0;
+ if(*(last=(char*)name)==0)
+ break;
+ if(!(next=nextdot(last)))
+ break;
+ *next = 0;
+ np = nv_open(name, dp->root, NV_NOFAIL);
+ *next = '.';
+ if(!np || !nv_istable(np))
+ break;
+ dp->root = nv_dict(np);
+ name = next+1;
+ }
+#else
+ dp->table = sh.last_table;
+ sh.last_table = 0;
+ last = dp->data;
+#endif
+ if(*name)
+ {
+ fake.nvname = (char*)name;
+ if(dp->hp = (Namval_t*)dtprev(dp->root,&fake))
+ {
+ char *cp = nv_name(dp->hp);
+ c = strlen(cp);
+ if(memcmp(name,cp,c) || name[c]!='[')
+ dp->hp = (Namval_t*)dtnext(dp->root,dp->hp);
+ else
+ {
+ np = dp->hp;
+ last = 0;
+ }
+ }
+ else
+ dp->hp = (Namval_t*)dtfirst(dp->root);
+ }
+ else
+ dp->hp = (Namval_t*)dtfirst(dp->root);
+ while(1)
+ {
+ if(!last)
+ next = 0;
+ else if(next= nextdot(last))
+ {
+ c = *next;
+ *next = 0;
+ }
+ if(!np)
+ {
+ if(nfp && nfp->disc && nfp->disc->createf)
+ {
+ np = (*nfp->disc->createf)(nq,last,0,nfp);
+ if(*nfp->last == '[')
+ {
+ nv_endsubscript(np,nfp->last,NV_NOADD);
+ if(nq = nv_opensub(np))
+ np = nq;
+ }
+ }
+ else
+ np = nv_search(last,dp->root,0);
+ }
+ if(next)
+ *next = c;
+ if(np==dp->hp && !next)
+ dp->hp = (Namval_t*)dtnext(dp->root,dp->hp);
+ if(np && ((nfp=nextdisc(np)) || nv_istable(np)))
+ {
+ if(!(save = new_of(struct nvdir,0)))
+ return(0);
+ *save = *dp;
+ dp->prev = save;
+ if(nv_istable(np))
+ dp->root = nv_dict(np);
+ else
+ dp->root = (Dt_t*)np;
+ if(nfp)
+ {
+ dp->nextnode = nfp->disc->nextf;
+ dp->table = np;
+ dp->otable = sh.last_table;
+ dp->fun = nfp;
+ dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
+ }
+ else
+ dp->nextnode = 0;
+ }
+ else
+ break;
+ if(!next || next[1]==0)
+ break;
+ last = next+1;
+ nq = np;
+ np = 0;
+ }
+ return((void*)dp);
+}
+
+
+static Namval_t *nextnode(struct nvdir *dp)
+{
+ if(dp->nextnode)
+ return((*dp->nextnode)(dp->hp,dp->root,dp->fun));
+ if(dp->len && memcmp(dp->data, dp->hp->nvname, dp->len))
+ return(0);
+ return((Namval_t*)dtnext(dp->root,dp->hp));
+}
+
+char *nv_dirnext(void *dir)
+{
+ register struct nvdir *save, *dp = (struct nvdir*)dir;
+ register Namval_t *np, *last_table;
+ register char *cp;
+ Namfun_t *nfp;
+ Namval_t *nq;
+ while(1)
+ {
+ while(np=dp->hp)
+ {
+#if 0
+ char *sptr;
+#endif
+ if(nv_isarray(np))
+ nv_putsub(np,(char*)0, ARRAY_UNDEF);
+ dp->hp = nextnode(dp);
+ if(nv_isnull(np) && !nv_isarray(np) && !nv_isattr(np,NV_INTEGER))
+ continue;
+ last_table = sh.last_table;
+#if 0
+ if(dp->table && dp->otable && !nv_isattr(dp->table,NV_MINIMAL))
+ {
+ sptr = dp->table->nvenv;
+ dp->table->nvenv = (char*)dp->otable;
+ }
+#endif
+ sh.last_table = dp->table;
+ cp = nv_name(np);
+#if 0
+ if(dp->table && dp->otable && !nv_isattr(dp->table,NV_MINIMAL))
+ dp->table->nvenv = sptr;
+#endif
+ if(dp->nextnode && !dp->hp && (nq = (Namval_t*)dp->table))
+ {
+ Namarr_t *ap = nv_arrayptr(nq);
+ if(ap && (ap->nelem&ARRAY_SCAN) && nv_nextsub(nq))
+ dp->hp = (*dp->nextnode)(np,(Dt_t*)0,dp->fun);
+ }
+ sh.last_table = last_table;
+ if(!dp->len || memcmp(cp,dp->data,dp->len)==0)
+ {
+ if((nfp=nextdisc(np)) && (nfp->disc->getval||nfp->disc->getnum) && nv_isvtree(np) && strcmp(cp,dp->data))
+ nfp = 0;
+ if(nfp || nv_istable(np))
+ {
+ Dt_t *root;
+ int len;
+ if(nv_istable(np))
+ root = nv_dict(np);
+ else
+ root = (Dt_t*)np;
+ /* check for recursive walk */
+ for(save=dp; save; save=save->prev)
+ {
+ if(save->root==root)
+ break;
+ }
+ if(save)
+ return(cp);
+ len = strlen(cp);
+ if(!(save = new_of(struct nvdir,len+1)))
+ return(0);
+ *save = *dp;
+ dp->prev = save;
+ dp->root = root;
+ dp->len = len-1;
+ dp->data = (char*)(save+1);
+ memcpy(dp->data,cp,len+1);
+ if(nfp && np->nvfun)
+ {
+#if 0
+ Namarr_t *ap = nv_arrayptr(np);
+ if(ap && (ap->nelem&ARRAY_UNDEF))
+ nv_putsub(np,(char*)0,ARRAY_SCAN);
+#endif
+ dp->nextnode = nfp->disc->nextf;
+ dp->otable = dp->table;
+ dp->table = np;
+ dp->fun = nfp;
+ dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
+ }
+ else
+ dp->nextnode = 0;
+ }
+ return(cp);
+ }
+ }
+ if(!(save=dp->prev))
+ break;
+ *dp = *save;
+ free((void*)save);
+ }
+ return(0);
+}
+
+void nv_dirclose(void *dir)
+{
+ struct nvdir *dp = (struct nvdir*)dir;
+ if(dp->prev)
+ nv_dirclose((void*)dp->prev);
+ free(dir);
+}
+
+static void outtype(Namval_t *np, Namfun_t *fp, Sfio_t* out, const char *prefix)
+{
+ char *type=0;
+ Namval_t *tp = fp->type;
+ if(!tp && fp->disc && fp->disc->typef)
+ tp = (*fp->disc->typef)(np,fp);
+ for(fp=fp->next;fp;fp=fp->next)
+ {
+ if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp)))
+ {
+ outtype(np,fp,out,prefix);
+ break;
+ }
+ }
+ if(prefix && *prefix=='t')
+ type = "-T";
+ else if(!prefix)
+ type = "type";
+ if(type)
+ {
+ char *cp=tp->nvname;
+ if(cp=strrchr(cp,'.'))
+ cp++;
+ else
+ cp = tp->nvname;
+ sfprintf(out,"%s %s ",type,cp);
+ }
+}
+
+/*
+ * print the attributes of name value pair give by <np>
+ */
+void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname)
+{
+ register const Shtable_t *tp;
+ register char *cp;
+ register unsigned val,mask,attr;
+ char *ip=0;
+ Namfun_t *fp=0;
+ Namval_t *typep=0;
+#if SHOPT_FIXEDARRAY
+ int fixed=0;
+#endif /* SHOPT_FIXEDARRAY */
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if((typep=fp->type) || (fp->disc && fp->disc->typef && (typep=(*fp->disc->typef)(np,fp))))
+ break;
+ }
+ if(np==typep)
+ {
+
+ fp = 0;
+ typep = 0;
+ }
+ if(!fp && !nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)))
+ {
+ if(prefix && *prefix)
+ {
+ if(nv_isvtree(np))
+ sfprintf(out,"%s -C ",prefix);
+ else if((!np->nvalue.cp||np->nvalue.cp==Empty) && nv_isattr(np,~NV_NOFREE)==NV_MINIMAL && strcmp(np->nvname,"_"))
+ sfputr(out,prefix,' ');
+ }
+ return;
+ }
+
+ if ((attr=nv_isattr(np,~NV_NOFREE)) || fp)
+ {
+ if((attr&NV_NOPRINT|NV_INTEGER)==NV_NOPRINT)
+ attr &= ~NV_NOPRINT;
+ if(!attr && !fp)
+ return;
+ if(fp)
+ {
+ prefix = Empty;
+ attr &= NV_RDONLY|NV_ARRAY;
+ if(nv_isattr(np,NV_REF|NV_TAGGED)==(NV_REF|NV_TAGGED))
+ attr |= (NV_REF|NV_TAGGED);
+ if(typep)
+ {
+ char *cp = typep->nvname;
+ if(cp = strrchr(cp,'.'))
+ cp++;
+ else
+ cp = typep->nvname;
+ sfputr(out,cp,' ');
+ fp = 0;
+ }
+ }
+ else if(prefix && *prefix)
+ sfputr(out,prefix,' ');
+ for(tp = shtab_attributes; *tp->sh_name;tp++)
+ {
+ val = tp->sh_number;
+ mask = val;
+ if(fp && (val&NV_INTEGER))
+ break;
+ /*
+ * the following test is needed to prevent variables
+ * with E attribute from being given the F
+ * attribute as well
+ */
+ if(val==NV_DOUBLE && (attr&(NV_EXPNOTE|NV_HEXFLOAT)))
+ continue;
+ if(val&NV_INTEGER)
+ mask |= NV_DOUBLE;
+ else if(val&NV_HOST)
+ mask = NV_HOST;
+ if((attr&mask)==val)
+ {
+ if(val==NV_ARRAY)
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ char **xp=0;
+ if(ap && array_assoc(ap))
+ {
+ if(tp->sh_name[1]!='A')
+ continue;
+ }
+ else if(tp->sh_name[1]=='A')
+ continue;
+ if((ap && (ap->nelem&ARRAY_TREE)) || (!ap && nv_isattr(np,NV_NOFREE)))
+ {
+ if(prefix && *prefix)
+ sfwrite(out,"-C ",3);
+ }
+#if SHOPT_FIXEDARRAY
+ if(ap && ap->fixed)
+ fixed++;
+ else
+#endif /* SHOPT_FIXEDARRAY */
+ if(ap && !array_assoc(ap) && (xp=(char**)(ap+1)) && *xp)
+ ip = nv_namptr(*xp,0)->nvname;
+ }
+ if(val==NV_UTOL || val==NV_LTOU)
+ {
+ if((cp = (char*)nv_mapchar(np,0)) && strcmp(cp,tp->sh_name+2))
+ {
+ sfprintf(out,"-M %s ",cp);
+ continue;
+ }
+ }
+ if(prefix)
+ {
+ if(*tp->sh_name=='-')
+ sfprintf(out,"%.2s ",tp->sh_name);
+ if(ip)
+ {
+ sfprintf(out,"[%s] ",ip);
+ ip = 0;
+ }
+ }
+ else
+ sfputr(out,tp->sh_name+2,' ');
+ if ((val&(NV_LJUST|NV_RJUST|NV_ZFILL)) && !(val&NV_INTEGER) && val!=NV_HOST)
+ sfprintf(out,"%d ",nv_size(np));
+ if(val==(NV_REF|NV_TAGGED))
+ attr &= ~(NV_REF|NV_TAGGED);
+ }
+ if(val==NV_INTEGER && nv_isattr(np,NV_INTEGER))
+ {
+ if(nv_size(np) != 10)
+ {
+ if(nv_isattr(np, NV_DOUBLE)== NV_DOUBLE)
+ cp = "precision";
+ else
+ cp = "base";
+ if(!prefix)
+ sfputr(out,cp,' ');
+ sfprintf(out,"%d ",nv_size(np));
+ }
+ break;
+ }
+ }
+#if SHOPT_FIXEDARRAY
+ if(fixed)
+ {
+ sfprintf(out,"%s",nv_name(np));
+ nv_arrfixed(np,out,0,(char*)0);
+ sfputc(out,';');
+ }
+#endif /* SHOPT_FIXEDARRAY */
+ if(fp)
+ outtype(np,fp,out,prefix);
+ if(noname)
+ return;
+ sfputr(out,nv_name(np),'\n');
+ }
+}
+
+struct Walk
+{
+ Shell_t *shp;
+ Sfio_t *out;
+ Dt_t *root;
+ int noscope;
+ int indent;
+ int nofollow;
+ int array;
+ int flags;
+};
+
+void nv_outnode(Namval_t *np, Sfio_t* out, int indent, int special)
+{
+ char *fmtq,*ep,*xp;
+ Namval_t *mp;
+ Namarr_t *ap = nv_arrayptr(np);
+ int scan,tabs=0,c,more,associative = 0;
+ int saveI = Indent;
+ Indent = indent;
+ if(ap)
+ {
+ if(!(ap->nelem&ARRAY_SCAN))
+ nv_putsub(np,NIL(char*),ARRAY_SCAN);
+ sfputc(out,'(');
+ if(indent>=0)
+ {
+ sfputc(out,'\n');
+ tabs=1;
+ }
+ if(!(associative =(array_assoc(ap)!=0)))
+ {
+ if(array_elem(ap) < nv_aimax(np)+1)
+ associative=1;
+ }
+ }
+ mp = nv_opensub(np);
+ while(1)
+ {
+ if(mp && special && nv_isvtree(mp) && !nv_isarray(mp))
+ {
+ if(!nv_nextsub(np))
+ break;
+ mp = nv_opensub(np);
+ continue;
+ }
+ if(tabs)
+ sfnputc(out,'\t',Indent = ++indent);
+ tabs=0;
+ if(associative||special)
+ {
+ if(!(fmtq = nv_getsub(np)))
+ break;
+ sfprintf(out,"[%s]",sh_fmtq(fmtq));
+ sfputc(out,'=');
+ }
+ if(ap && !array_assoc(ap))
+ scan = ap->nelem&ARRAY_SCAN;
+ if(mp && nv_isarray(mp))
+ {
+ nv_outnode(mp, out, indent,0);
+ if(indent>0)
+ sfnputc(out,'\t',indent);
+ sfputc(out,')');
+ sfputc(out,indent>=0?'\n':' ');
+ if(ap && !array_assoc(ap))
+ ap->nelem |= scan;
+ more = nv_nextsub(np);
+ goto skip;
+ }
+ if(mp && nv_isvtree(mp))
+ {
+ if(indent<0)
+ nv_onattr(mp,NV_EXPORT);
+ nv_onattr(mp,NV_TABLE);
+ }
+ ep = nv_getval(mp?mp:np);
+ if(ep==Empty && !(ap && ap->fixed))
+ ep = 0;
+ xp = 0;
+ if(!ap && nv_isattr(np,NV_INTEGER|NV_LJUST)==NV_LJUST)
+ {
+ xp = ep+nv_size(np);
+ while(--xp>ep && *xp==' ');
+ if(xp>ep || *xp!=' ')
+ xp++;
+ if(xp < (ep+nv_size(np)))
+ *xp = 0;
+ else
+ xp = 0;
+ }
+ if(mp && nv_isvtree(mp))
+ fmtq = ep;
+ else if(!(fmtq = sh_fmtq(ep)))
+ fmtq = "";
+ else if(!associative && (ep=strchr(fmtq,'=')))
+ {
+ char *qp = strchr(fmtq,'\'');
+ if(!qp || qp>ep)
+ {
+ sfwrite(out,fmtq,ep-fmtq);
+ sfputc(out,'\\');
+ fmtq = ep;
+ }
+ }
+ if(ap && !array_assoc(ap))
+ ap->nelem |= scan;
+ more = nv_nextsub(np);
+ c = '\n';
+ if(indent<0)
+ {
+ c = indent < -1?-1:';';
+ if(ap)
+ c = more?' ':-1;
+ }
+ sfputr(out,fmtq,c);
+ if(xp)
+ *xp = ' ';
+ skip:
+ if(!more)
+ break;
+ mp = nv_opensub(np);
+ if(indent>0 && !(mp && special && nv_isvtree(mp)))
+ sfnputc(out,'\t',indent);
+ }
+ Indent = saveI;
+}
+
+static void outval(char *name, const char *vname, struct Walk *wp)
+{
+ register Namval_t *np, *nq, *last_table=wp->shp->last_table;
+ register Namfun_t *fp;
+ int isarray=0, special=0,mode=0;
+ if(*name!='.' || vname[strlen(vname)-1]==']')
+ mode = NV_ARRAY;
+ if(!(np=nv_open(vname,wp->root,mode|NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL|wp->noscope)))
+ {
+ wp->shp->last_table = last_table;
+ return;
+ }
+ if(!wp->out)
+ wp->shp->last_table = last_table;
+ fp = nv_hasdisc(np,&treedisc);
+ if(*name=='.')
+ {
+ if(nv_isattr(np,NV_BINARY))
+ return;
+ if(fp && np->nvalue.cp && np->nvalue.cp!=Empty)
+ {
+ nv_local = 1;
+ fp = 0;
+ }
+ if(fp)
+ return;
+ if(nv_isarray(np))
+ return;
+ }
+ if(!special && fp && !nv_isarray(np))
+ {
+ Namfun_t *xp;
+ if(!wp->out)
+ {
+ fp = nv_stack(np,fp);
+ if(fp = nv_stack(np,NIL(Namfun_t*)))
+ free((void*)fp);
+ np->nvfun = 0;
+ return;
+ }
+ for(xp=fp->next; xp; xp = xp->next)
+ {
+ if(xp->disc && (xp->disc->getval || xp->disc->getnum))
+ break;
+ }
+ if(!xp)
+ return;
+ }
+ if(nv_isnull(np) && !nv_isarray(np) && !nv_isattr(np,NV_INTEGER))
+ return;
+ if(special || (nv_isarray(np) && nv_arrayptr(np)))
+ {
+ isarray=1;
+ if(array_elem(nv_arrayptr(np))==0)
+ isarray=2;
+ else
+ nq = nv_putsub(np,NIL(char*),ARRAY_SCAN|(wp->out?ARRAY_NOCHILD:0));
+ }
+ if(!wp->out)
+ {
+ _nv_unset(np,NV_RDONLY);
+ if(sh.subshell || (wp->flags!=NV_RDONLY) || nv_isattr(np,NV_MINIMAL|NV_NOFREE))
+ wp->root = 0;
+ nv_delete(np,wp->root,NV_NOFREE);
+ return;
+ }
+ if(isarray==1 && !nq)
+ {
+ sfputc(wp->out,'(');
+ if(wp->indent>=0)
+ sfputc(wp->out,'\n');
+ return;
+ }
+ if(isarray==0 && nv_isarray(np) && (nv_isnull(np)||np->nvalue.cp==Empty)) /* empty array */
+ isarray = 2;
+ special |= wp->nofollow;
+ if(!wp->array && wp->indent>0)
+ sfnputc(wp->out,'\t',wp->indent);
+ if(!special)
+ {
+ if(*name!='.')
+ nv_attribute(np,wp->out,"typeset",'=');
+ nv_outname(wp->out,name,-1);
+ if((np->nvalue.cp && np->nvalue.cp!=Empty) || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)) || nv_isvtree(np))
+ sfputc(wp->out,(isarray==2?(wp->indent>=0?'\n':';'):'='));
+ if(isarray==2)
+ return;
+ }
+ fp = np->nvfun;
+ if(*name=='.' && !isarray)
+ np->nvfun = 0;
+ nv_outnode(np, wp->out, wp->indent, special);
+ if(*name=='.' && !isarray)
+ np->nvfun = fp;
+ if(isarray && !special)
+ {
+ if(wp->indent>0)
+ {
+ sfnputc(wp->out,'\t',wp->indent);
+ sfwrite(wp->out,")\n",2);
+ }
+ else
+ sfwrite(wp->out,");",2);
+ }
+}
+
+/*
+ * format initialization list given a list of assignments <argp>
+ */
+static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp)
+{
+ register char *cp,*nextcp,*arg;
+ register Sfio_t *outfile = wp->out;
+ register int m,r,l;
+ if(n==0)
+ m = strlen(prefix);
+ else if(cp=nextdot(prefix))
+ m = cp-prefix;
+ else
+ m = strlen(prefix)-1;
+ m++;
+ if(outfile && !wp->array)
+ {
+ sfputc(outfile,'(');
+ if(wp->indent>=0)
+ {
+ wp->indent++;
+ sfputc(outfile,'\n');
+ }
+ }
+ for(; arg= *argv; argv++)
+ {
+ cp = arg + n;
+ if(n==0 && cp[m-1]!='.')
+ continue;
+ if(n && cp[m-1]==0)
+ break;
+ if(n==0 || strncmp(arg,prefix-n,m+n)==0)
+ {
+ cp +=m;
+ r = 0;
+ if(*cp=='.')
+ cp++,r++;
+ if(wp->indent < 0 && argv[1]==0)
+ wp->indent--;
+ if(nextcp=nextdot(cp))
+ {
+ if(outfile)
+ {
+ Namval_t *np,*tp;
+ *nextcp = 0;
+ np=nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL|wp->noscope);
+ if(!np || (nv_isarray(np) && (!(tp=nv_opensub(np)) || !nv_isvtree(tp))))
+ {
+ *nextcp = '.';
+ continue;
+ }
+ if(wp->indent>=0)
+ sfnputc(outfile,'\t',wp->indent);
+ if(*cp!='[' && (tp = nv_type(np)))
+ {
+ char *sp;
+ if(sp = strrchr(tp->nvname,'.'))
+ sp++;
+ else
+ sp = tp->nvname;
+ sfputr(outfile,sp,' ');
+ }
+ nv_outname(outfile,cp,nextcp-cp);
+ sfputc(outfile,'=');
+ *nextcp = '.';
+ }
+ else
+ {
+ outval(cp,arg,wp);
+ continue;
+ }
+ argv = genvalue(argv,cp,n+m+r,wp);
+ if(wp->indent>=0)
+ sfputc(outfile,'\n');
+ if(*argv)
+ continue;
+ break;
+ }
+ else if(outfile && !wp->nofollow && argv[1] && memcmp(arg,argv[1],l=strlen(arg))==0 && argv[1][l]=='[')
+ {
+ int k=1;
+ Namarr_t *ap=0;
+ Namval_t *np = nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope);
+ if(!np)
+ continue;
+ if((wp->array = nv_isarray(np)) && (ap=nv_arrayptr(np)))
+ k = array_elem(ap);
+
+ if(wp->indent>0)
+ sfnputc(outfile,'\t',wp->indent);
+ nv_attribute(np,outfile,"typeset",1);
+ nv_close(np);
+ sfputr(outfile,arg+m+r+(n?n:0),(k?'=':'\n'));
+ if(!k)
+ {
+ wp->array=0;
+ continue;
+ }
+ wp->nofollow=1;
+ argv = genvalue(argv,cp,cp-arg ,wp);
+ sfputc(outfile,wp->indent<0?';':'\n');
+ }
+ else if(outfile && *cp=='[' && cp[-1]!='.')
+ {
+ /* skip multi-dimensional arrays */
+ if(*nv_endsubscript((Namval_t*)0,cp,0)=='[')
+ continue;
+ if(wp->indent>0)
+ sfnputc(outfile,'\t',wp->indent);
+ if(cp[-1]=='.')
+ cp--;
+ sfputr(outfile,cp,'=');
+ if(*cp=='.')
+ cp++;
+ argv = genvalue(++argv,cp,cp-arg ,wp);
+ sfputc(outfile,wp->indent>0?'\n':';');
+ }
+ else
+ {
+ outval(cp,arg,wp);
+ if(wp->array)
+ {
+ if(wp->indent>=0)
+ wp->indent++;
+ else
+ sfputc(outfile,' ');
+ wp->array = 0;
+ }
+ }
+ }
+ else
+ break;
+ wp->nofollow = 0;
+ }
+ wp->array = 0;
+ if(outfile)
+ {
+ int c = prefix[m-1];
+ cp = (char*)prefix;
+ if(c=='.')
+ cp[m-1] = 0;
+ outval(".",prefix-n,wp);
+ if(c=='.')
+ cp[m-1] = c;
+ if(wp->indent>0)
+ sfnputc(outfile,'\t',--wp->indent);
+ sfputc(outfile,')');
+ }
+ return(--argv);
+}
+
+/*
+ * walk the virtual tree and print or delete name-value pairs
+ */
+static char *walk_tree(register Namval_t *np, Namval_t *xp, int flags)
+{
+ static Sfio_t *out;
+ struct Walk walk;
+ Sfio_t *outfile;
+ Sfoff_t off = 0;
+ int len, savtop = staktell();
+ char *savptr = stakfreeze(0);
+ register struct argnod *ap=0;
+ struct argnod *arglist=0;
+ char *name,*cp, **argv;
+ char *subscript=0;
+ void *dir;
+ int n=0, noscope=(flags&NV_NOSCOPE);
+ Namarr_t *arp = nv_arrayptr(np);
+ Dt_t *save_tree = sh.var_tree;
+ Namval_t *mp=0;
+ Shell_t *shp = sh_getinterp();
+ char *xpname = xp?stakcopy(nv_name(xp)):0;
+ walk.shp = shp;
+ if(xp)
+ {
+ shp->last_root = shp->prev_root;
+ shp->last_table = shp->prev_table;
+ }
+ if(shp->last_table)
+ shp->last_root = nv_dict(shp->last_table);
+ if(shp->last_root)
+ shp->var_tree = shp->last_root;
+ stakputs(nv_name(np));
+ if(arp && !(arp->nelem&ARRAY_SCAN) && (subscript = nv_getsub(np)))
+ {
+ mp = nv_opensub(np);
+ stakputc('[');
+ stakputs(subscript);
+ stakputc(']');
+ stakputc('.');
+ }
+ else if(*stakptr(staktell()-1) == ']')
+ mp = np;
+ name = stakfreeze(1);
+ len = strlen(name);
+ shp->last_root = 0;
+ dir = nv_diropen(mp,name);
+ walk.root = shp->last_root?shp->last_root:shp->var_tree;
+ if(subscript)
+ name[strlen(name)-1] = 0;
+ while(cp = nv_dirnext(dir))
+ {
+ if(cp[len]!='.')
+ continue;
+ if(xp)
+ {
+ Dt_t *dp = shp->var_tree;
+ Namval_t *nq, *mq;
+ if(strlen(cp)<=len)
+ continue;
+ nq = nv_open(cp,walk.root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL);
+ if(!nq && (flags&NV_MOVE))
+ nq = nv_search(cp,walk.root,NV_NOADD);
+ stakseek(0);
+ stakputs(xpname);
+ stakputs(cp+len);
+ stakputc(0);
+ shp->var_tree = save_tree;
+ mq = nv_open(stakptr(0),shp->prev_root,NV_VARNAME|NV_NOASSIGN|NV_NOFAIL);
+ shp->var_tree = dp;
+ if(nq && mq)
+ {
+ nv_clone(nq,mq,flags|NV_RAW);
+ if(flags&NV_MOVE)
+ nv_delete(nq,walk.root,0);
+ }
+ continue;
+ }
+ stakseek(ARGVAL);
+ stakputs(cp);
+ ap = (struct argnod*)stakfreeze(1);
+ ap->argflag = ARG_RAW;
+ ap->argchn.ap = arglist;
+ n++;
+ arglist = ap;
+ }
+ nv_dirclose(dir);
+ if(xp)
+ {
+ shp->var_tree = save_tree;
+ return((char*)0);
+ }
+ argv = (char**)stakalloc((n+1)*sizeof(char*));
+ argv += n;
+ *argv = 0;
+ for(; ap; ap=ap->argchn.ap)
+ *--argv = ap->argval;
+ if(flags&1)
+ outfile = 0;
+ else if(!(outfile=out))
+ outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
+ else if(flags&NV_TABLE)
+ off = sftell(outfile);
+ else
+ sfseek(outfile,0L,SEEK_SET);
+ walk.out = outfile;
+ walk.indent = (flags&NV_EXPORT)?-1:Indent;
+ walk.nofollow = 0;
+ walk.noscope = noscope;
+ walk.array = 0;
+ walk.flags = flags;
+ genvalue(argv,name,0,&walk);
+ stakset(savptr,savtop);
+ shp->var_tree = save_tree;
+ if(!outfile)
+ return((char*)0);
+ sfputc(out,0);
+ sfseek(out,off,SEEK_SET);
+ return((char*)out->_data+off);
+}
+
+Namfun_t *nv_isvtree(Namval_t *np)
+{
+ if(np)
+ return(nv_hasdisc(np,&treedisc));
+ return(0);
+}
+
+/*
+ * get discipline for compound initializations
+ */
+char *nv_getvtree(register Namval_t *np, Namfun_t *fp)
+{
+ int flags=0, dsize=fp?fp->dsize:0;
+ for(; fp && fp->next; fp=fp->next)
+ {
+ if(fp->next->disc && (fp->next->disc->getnum || fp->next->disc->getval))
+ return(nv_getv(np,fp));
+ }
+ if(nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW))
+ return(nv_getv(np,fp));
+ if(nv_isattr(np,NV_ARRAY) && !nv_type(np) && nv_arraychild(np,(Namval_t*)0,0)==np)
+ return(nv_getv(np,fp));
+ if(flags = nv_isattr(np,NV_EXPORT))
+ nv_offattr(np,NV_EXPORT);
+ if(flags |= nv_isattr(np,NV_TABLE))
+ nv_offattr(np,NV_TABLE);
+ if(dsize && (flags&NV_EXPORT))
+ return("()");
+ return(walk_tree(np,(Namval_t*)0,flags));
+}
+
+/*
+ * put discipline for compound initializations
+ */
+static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t *fp)
+{
+ struct Namarray *ap;
+ int nleft = 0;
+ if(!val && !fp->next && nv_isattr(np,NV_NOFREE))
+ return;
+ if(!nv_isattr(np,(NV_INTEGER|NV_BINARY)))
+ {
+ Shell_t *shp = sh_getinterp();
+ Namval_t *last_table = shp->last_table;
+ Dt_t *last_root = shp->last_root;
+ Namval_t *mp = val?nv_open(val,shp->var_tree,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_ARRAY|NV_NOFAIL):0;
+ if(mp && nv_isvtree(mp))
+ {
+ shp->prev_table = shp->last_table;
+ shp->prev_root = shp->last_root;
+ shp->last_table = last_table;
+ shp->last_root = last_root;
+ if(!(flags&NV_APPEND))
+ walk_tree(np,(Namval_t*)0,(flags&NV_NOSCOPE)|1);
+ nv_clone(mp,np,NV_COMVAR);
+ return;
+ }
+ walk_tree(np,(Namval_t*)0,(flags&NV_NOSCOPE)|1);
+ }
+ nv_putv(np, val, flags,fp);
+ if(val && nv_isattr(np,(NV_INTEGER|NV_BINARY)))
+ return;
+ if(ap= nv_arrayptr(np))
+ nleft = array_elem(ap);
+ if(nleft==0)
+ {
+ fp = nv_stack(np,fp);
+ if(fp = nv_stack(np,NIL(Namfun_t*)))
+ free((void*)fp);
+ }
+}
+
+/*
+ * Insert discipline to cause $x to print current tree
+ */
+void nv_setvtree(register Namval_t *np)
+{
+ register Namfun_t *nfp;
+ if(sh.subshell)
+ sh_assignok(np,1);
+ if(nv_hasdisc(np, &treedisc))
+ return;
+ nfp = newof(NIL(void*),Namfun_t,1,0);
+ nfp->disc = &treedisc;
+ nfp->dsize = sizeof(Namfun_t);
+ nv_stack(np, nfp);
+}
+
diff --git a/src/cmd/ksh93/sh/nvtype.c b/src/cmd/ksh93/sh/nvtype.c
new file mode 100644
index 0000000..3b7f34e
--- /dev/null
+++ b/src/cmd/ksh93/sh/nvtype.c
@@ -0,0 +1,1709 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * David Korn
+ * AT&T Labs
+ *
+ */
+#include "defs.h"
+#include "io.h"
+#include "variables.h"
+
+static const char sh_opttype[] =
+"[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-07-01 $\n]"
+USAGE_LICENSE
+"[+NAME?\f?\f - set the type of variables to \b\f?\f\b]"
+"[+DESCRIPTION?\b\f?\f\b sets the type on each of the variables specified "
+ "by \aname\a to \b\f?\f\b. If \b=\b\avalue\a is specified, "
+ "the variable \aname\a is set to \avalue\a before the variable "
+ "is converted to \b\f?\f\b.]"
+"[+?If no \aname\as are specified then the names and values of all "
+ "variables of this type are written to standard output.]"
+"[+?\b\f?\f\b is built-in to the shell as a declaration command so that "
+ "field splitting and pathname expansion are not performed on "
+ "the arguments. Tilde expansion occurs on \avalue\a.]"
+"[r?Enables readonly. Once enabled, the value cannot be changed or unset.]"
+"[a]:?[type?Indexed array. Each \aname\a will converted to an index "
+ "array of type \b\f?\f\b. If a variable already exists, the current "
+ "value will become index \b0\b. If \b[\b\atype\a\b]]\b is "
+ "specified, each subscript is interpreted as a value of enumeration "
+ "type \atype\a.]"
+"[A?Associative array. Each \aname\a will converted to an associate "
+ "array of type \b\f?\f\b. If a variable already exists, the current "
+ "value will become subscript \b0\b.]"
+"[h]:[string?Used within a type definition to provide a help string "
+ "for variable \aname\a. Otherwise, it is ignored.]"
+"[S?Used with a type definition to indicate that the variable is shared by "
+ "each instance of the type. When used inside a function defined "
+ "with the \bfunction\b reserved word, the specified variables "
+ "will have function static scope. Otherwise, the variable is "
+ "unset prior to processing the assignment list.]"
+"[+DETAILS]\ftypes\f"
+"\n"
+"\n[name[=value]...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Successful completion.]"
+ "[+>0?An error occurred.]"
+"}"
+
+"[+SEE ALSO?\fother\f \breadonly\b(1), \btypeset\b(1)]"
+;
+
+typedef struct Namtype Namtype_t;
+typedef struct Namchld
+{
+ Namfun_t fun;
+ Namtype_t *ptype;
+ Namtype_t *ttype;
+} Namchld_t;
+
+struct Namtype
+{
+ Namfun_t fun;
+ Shell_t *sh;
+ Namval_t *np;
+ Namval_t *parent;
+ Namval_t *bp;
+ Namval_t *cp;
+#if SHOPT_NAMESPACE
+ Namval_t *nsp;
+#endif /* SHOPT_NAMESPACE */
+ char *nodes;
+ char *data;
+ Namchld_t childfun;
+ int numnodes;
+ char **names;
+ size_t dsize;
+ short strsize;
+ unsigned short ndisc;
+ unsigned short current;
+ unsigned short nref;
+};
+
+#if 0
+struct type
+{
+ Namtype_t hdr;
+ unsigned short ndisc;
+ unsigned short current;
+ unsigned short nref;
+};
+#endif
+
+typedef struct
+{
+ char _cSfdouble_t;
+ Sfdouble_t _dSfdouble_t;
+ char _cdouble;
+ double _ddouble;
+ char _cfloat;
+ float _dfloat;
+ char _cSflong_t;
+ Sflong_t _dSflong_t;
+ char _clong;
+ long _dlong;
+ char _cshort;
+ short _dshort;
+ char _cpointer;
+ char *_dpointer;
+} _Align_;
+
+#define alignof(t) ((char*)&((_Align_*)0)->_d##t-(char*)&((_Align_*)0)->_c##t)
+
+static void put_type(Namval_t*, const char*, int, Namfun_t*);
+static Namval_t* create_type(Namval_t*, const char*, int, Namfun_t*);
+static Namfun_t* clone_type(Namval_t*, Namval_t*, int, Namfun_t*);
+static Namval_t* next_type(Namval_t*, Dt_t*, Namfun_t*);
+
+static const Namdisc_t type_disc =
+{
+ sizeof(Namtype_t),
+ put_type,
+ 0,
+ 0,
+ 0,
+ create_type,
+ clone_type,
+ 0,
+ next_type,
+ 0,
+#if 0
+ read_type
+#endif
+};
+
+size_t nv_datasize(Namval_t *np, size_t *offset)
+{
+ size_t s=0, a=0;
+ if(nv_isattr(np,NV_INTEGER))
+ {
+ if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
+ {
+ if(nv_isattr(np, NV_LONG))
+ {
+ a = alignof(Sfdouble_t);
+ s = sizeof(Sfdouble_t);
+ }
+ else if(nv_isattr(np, NV_SHORT))
+ {
+ a = alignof(float);
+ s = sizeof(float);
+ }
+ else
+ {
+ a = alignof(double);
+ s = sizeof(double);
+ }
+ }
+ else
+ {
+ if(nv_isattr(np, NV_LONG))
+ {
+ a = alignof(Sflong_t);
+ s = sizeof(Sflong_t);
+ }
+ else if(nv_isattr(np, NV_SHORT))
+ {
+ a = alignof(short);
+ s = sizeof(short);
+ }
+ else
+ {
+ a = alignof(long);
+ s = sizeof(long);
+ }
+ }
+ }
+ else if(nv_isattr(np, NV_BINARY) || nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
+ s = nv_size(np);
+ else
+ {
+ a = alignof(pointer);
+ s = nv_size(np);
+ }
+ if(a>1 && offset)
+ *offset = a*((*offset +a-1)/a);
+ return(s);
+}
+
+static char *name_chtype(Namval_t *np, Namfun_t *fp)
+{
+ Namchld_t *pp = (Namchld_t*)fp;
+ char *cp, *sub;
+ Namval_t *tp = sh.last_table;
+ Namval_t *nq = pp->ptype->np;
+ Namarr_t *ap;
+ if(nv_isattr(np,NV_REF|NV_TAGGED)==(NV_REF|NV_TAGGED))
+ sh.last_table = 0;
+ cp = nv_name(nq);
+ if((ap = nv_arrayptr(nq)) && !(ap->nelem&ARRAY_UNDEF) && (sub= nv_getsub(nq)))
+ sfprintf(sh.strbuf,"%s[%s].%s",cp,sub,np->nvname);
+ else
+ sfprintf(sh.strbuf,"%s.%s",cp,np->nvname);
+#if SHOPT_FIXEDARRAY
+ if((ap=nv_arrayptr(np)) && ap->fixed)
+ nv_arrfixed(np,sh.strbuf,1,(char*)0);
+#endif /* SHOPT_FIXEDARRAY */
+ sh.last_table = tp;
+ return(sfstruse(sh.strbuf));
+}
+
+static void put_chtype(Namval_t* np, const char* val, int flag, Namfun_t* fp)
+{
+ if(!val && nv_isattr(np,NV_REF))
+ return;
+ nv_putv(np,val,flag,fp);
+ if(!val)
+ {
+ Namchld_t *pp = (Namchld_t*)fp;
+ size_t dsize=0,offset = (char*)np-(char*)pp->ptype;
+ Namval_t *mp = (Namval_t*)((char*)pp->ttype+offset);
+ dsize = nv_datasize(mp,&dsize);
+ if(mp->nvalue.cp >= pp->ttype->data && mp->nvalue.cp < (char*)pp+pp->ttype->fun.dsize)
+ {
+ np->nvalue.cp = pp->ptype->data + (mp->nvalue.cp-pp->ptype->data);
+ if(np->nvalue.cp!=mp->nvalue.cp)
+ memcpy((char*)np->nvalue.cp,mp->nvalue.cp,dsize);
+ }
+ else if(!nv_isarray(mp) && mp->nvalue.cp)
+ {
+ np->nvalue.cp = mp->nvalue.cp;
+ nv_onattr(np,NV_NOFREE);
+ }
+ np->nvsize = mp->nvsize;
+ np->nvflag = mp->nvflag&~NV_RDONLY;
+ }
+}
+
+static Namfun_t *clone_chtype(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ if(flags&NV_NODISC)
+ return(0);
+ return(nv_clone_disc(fp,flags));
+}
+
+static const Namdisc_t chtype_disc =
+{
+ sizeof(Namchld_t),
+ put_chtype,
+ 0,
+ 0,
+ 0,
+ 0,
+ clone_chtype,
+ name_chtype
+};
+
+static Namval_t *findref(void *nodes, int n)
+{
+ Namval_t *tp,*np = nv_namptr(nodes,n);
+ char *name = np->nvname;
+ int i=n, len= strrchr(name,'.')-name;
+ Namtype_t *pp;
+ while(--i>0)
+ {
+ np = nv_namptr(nodes,i);
+ if(np->nvname[len]==0)
+ {
+ tp = nv_type(np);
+ pp = (Namtype_t*)nv_hasdisc(tp,&type_disc);
+ return(nv_namptr(pp->nodes,n-i-1));
+ }
+ }
+ return(0);
+}
+
+static int fixnode(Namtype_t *dp, Namtype_t *pp, int i, struct Namref *nrp,int flag)
+{
+ Namval_t *nq = nv_namptr(dp->nodes,i);
+ Namfun_t *fp;
+ if(fp=nv_hasdisc(nq,&chtype_disc))
+ nv_disc(nq, fp, NV_POP);
+ if(nv_isattr(nq,NV_REF))
+ {
+ nq->nvalue.nrp = nrp++;
+ nv_setsize(nq,0);
+ if(strchr(nq->nvname,'.'))
+ nq->nvalue.nrp->np = findref(dp->nodes,i);
+ else
+ nq->nvalue.nrp->np = nv_namptr(pp->childfun.ttype->nodes,i);
+ nq->nvalue.nrp->root = sh.last_root;
+ nq->nvalue.nrp->table = pp->np;
+ nq ->nvflag = NV_REF|NV_NOFREE|NV_MINIMAL;
+ return(1);
+ }
+ if(nq->nvalue.cp || nq->nvfun)
+ {
+ const char *data = nq->nvalue.cp;
+ if(nq->nvfun)
+ {
+ Namval_t *np = nv_namptr(pp->nodes,i);
+ if(nv_isarray(nq))
+ nq->nvalue.cp = 0;
+ nq->nvfun = 0;
+ if(nv_isarray(nq) && ((flag&NV_IARRAY) || nv_type(np)))
+ clone_all_disc(np,nq,flag&~NV_TYPE);
+ else
+ clone_all_disc(np,nq,flag);
+ if(fp)
+ nv_disc(np, fp, NV_LAST);
+ }
+#if 0
+ if(nq->nvalue.cp >= pp->data && nq->nvalue.cp < (char*)pp +pp->fun.dsize)
+ nq->nvalue.cp = dp->data + (nq->nvalue.cp-pp->data);
+#else
+ if(data >= pp->data && data < (char*)pp +pp->fun.dsize)
+ nq->nvalue.cp = dp->data + (data-pp->data);
+#endif
+ else if(!nq->nvfun && pp->childfun.ttype!=pp->childfun.ptype)
+ {
+ Namval_t *nr = nv_namptr( pp->childfun.ttype->nodes,i);
+ if(nr->nvalue.cp!=nq->nvalue.cp)
+ {
+ if(i=nv_size(nq))
+ {
+ const char *cp = nq->nvalue.cp;
+ nq->nvalue.cp = (char*)malloc(i);
+ memcpy((char*)nq->nvalue.cp,cp,i);
+ }
+ else
+ nq->nvalue.cp = strdup(nq->nvalue.cp);
+ nv_offattr(nq,NV_NOFREE);
+ }
+ }
+ else if(nq->nvalue.cp==Empty)
+ nv_offattr(nq,NV_NOFREE);
+
+ }
+ if(fp)
+ nv_disc(nq, &dp->childfun.fun, NV_LAST);
+ return(0);
+}
+
+static Namfun_t *clone_type(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ Namtype_t *dp, *pp=(Namtype_t*)fp;
+ register int i;
+ register Namval_t *nq, *nr;
+ size_t size = fp->dsize;
+ int save, offset=staktell();
+ char *cp;
+ Dt_t *root = sh.last_root;
+ Namval_t *last_table = sh.last_table;
+ struct Namref *nrp = 0;
+ Namarr_t *ap;
+ if(flags&NV_MOVE)
+ {
+ pp->np = mp;
+ pp->childfun.ptype = pp;
+ return(fp);
+ }
+ if(flags&NV_TYPE)
+ return(nv_clone_disc(fp,flags));
+ if(size==0 && (!fp->disc || (size=fp->disc->dsize)==0))
+ size = sizeof(Namfun_t);
+ dp = (Namtype_t*)malloc(size+pp->nref*sizeof(struct Namref));
+ if(pp->nref)
+ {
+ nrp = (struct Namref*)((char*)dp + size);
+ memset((void*)nrp,0,pp->nref*sizeof(struct Namref));
+ }
+ memcpy((void*)dp,(void*)pp,size);
+#if 0
+ dp->parent = nv_lastdict();
+#else
+ dp->parent = mp;
+#endif
+ dp->fun.nofree = (flags&NV_RDONLY?1:0);
+ dp->np = mp;
+ dp->childfun.ptype = dp;
+#if 0
+ dp->childfun.ttype = (Namtype_t*)nv_hasdisc(dp->fun.type,&type_disc);
+#endif
+ dp->nodes = (char*)(dp+1);
+ dp->data = (char*)dp + (pp->data - (char*)pp);
+ for(i=dp->numnodes; --i >= 0; )
+ {
+ nq = nv_namptr(dp->nodes,i);
+ if(fixnode(dp,pp,i,nrp,NV_TYPE|(flags&NV_IARRAY)))
+ {
+ nrp++;
+ nq = nq->nvalue.nrp->np;
+ }
+ if(flags==(NV_NOFREE|NV_ARRAY))
+ continue;
+ if(nq->nvalue.cp || !nv_isvtree(nq) || nv_isattr(nq,NV_RDONLY))
+ {
+ /* see if default value has been overwritten */
+ if(!mp->nvname)
+ continue;
+ sh.last_table = last_table;
+ if(pp->strsize<0)
+ cp = nv_name(np);
+ else
+ cp = nv_name(mp);
+ stakputs(cp);
+ stakputc('.');
+ stakputs(nq->nvname);
+ stakputc(0);
+ root = nv_dict(mp);
+ save = fp->nofree;
+ fp->nofree = 1;
+ nr = nv_create(stakptr(offset),root,NV_VARNAME|NV_NOADD,fp);
+ fp->nofree = save;
+ stakseek(offset);
+ if(nr)
+ {
+ if(nv_isattr(nq,NV_RDONLY) && (nq->nvalue.cp || nv_isattr(nq,NV_INTEGER)))
+ errormsg(SH_DICT,ERROR_exit(1),e_readonly, nq->nvname);
+ if(nv_isref(nq))
+ nq = nv_refnode(nq);
+ if((size = nv_datasize(nr,(size_t*)0)) && size==nv_datasize(nq,(size_t*)0))
+ memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size);
+ else if(ap=nv_arrayptr(nr))
+ {
+ nv_putsub(nr,NIL(char*),ARRAY_SCAN|ARRAY_NOSCOPE);
+ do
+ {
+ if(array_assoc(ap))
+ cp = (char*)((*ap->fun)(nr,NIL(char*),NV_ANAME));
+ else
+ cp = nv_getsub(nr);
+ nv_putsub(nq,cp,ARRAY_ADD|ARRAY_NOSCOPE);
+ if(array_assoc(ap))
+ {
+ Namval_t *mp = (Namval_t*)((*ap->fun)(nr,NIL(char*),NV_ACURRENT));
+ Namval_t *mq = (Namval_t*)((*ap->fun)(nq,NIL(char*),NV_ACURRENT));
+ nv_clone(mp,mq,NV_MOVE);
+ ap->nelem--;
+ nv_delete(mp,ap->table,0);
+ }
+ else
+ {
+ cp = nv_getval(nr);
+ nv_putval(nq,cp,0);
+ }
+ }
+ while(nv_nextsub(nr));
+ }
+ else
+ nv_putval(nq,nv_getval(nr),NV_RDONLY);
+#if SHOPT_TYPEDEF
+ if(sh.mktype)
+ nv_addnode(nr,1);
+#endif /* SHOPT_TYPEDEF */
+ if(pp->strsize<0)
+ continue;
+ _nv_unset(nr,0);
+ if(!nv_isattr(nr,NV_MINIMAL))
+ nv_delete(nr,sh.last_root,0);
+ }
+ else if(nv_isattr(nq,NV_RDONLY) && !nq->nvalue.cp && !nv_isattr(nq,NV_INTEGER))
+ errormsg(SH_DICT,ERROR_exit(1),e_required,nq->nvname,nv_name(mp));
+ }
+ }
+ if(nv_isattr(mp,NV_BINARY))
+ mp->nvalue.cp = dp->data;
+ if(pp->strsize<0)
+ dp->strsize = -pp->strsize;
+ return(&dp->fun);
+}
+
+
+/*
+ * return Namval_t* corresponding to child <name> in <np>
+ */
+static Namval_t *create_type(Namval_t *np,const char *name,int flag,Namfun_t *fp)
+{
+ Namtype_t *dp = (Namtype_t*)fp;
+ register const char *cp=name;
+ register int i=0,n;
+ Namval_t *nq=0;
+ if(!name)
+ return(dp->parent);
+ while((n=*cp++) && n != '=' && n != '+' && n!='[');
+ n = (cp-1) -name;
+ if(dp->numnodes && dp->strsize<0)
+ {
+ char *base = (char*)np-sizeof(Dtlink_t);
+ int m=strlen(np->nvname);
+ while((nq=nv_namptr(base,++i)) && memcmp(nq->nvname,np->nvname,m)==0)
+ {
+ if(nq->nvname[m]=='.' && memcmp(name,&nq->nvname[m+1],n)==0 && nq->nvname[m+n+1]==0)
+ goto found;
+ }
+ nq = 0;
+ }
+ else for(i=0; i < dp->numnodes; i++)
+ {
+ nq = nv_namptr(dp->nodes,i);
+ if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0)
+ {
+ while(nv_isref(nq))
+ nq = nq->nvalue.nrp->np;
+ goto found;
+ }
+ }
+ nq = 0;
+found:
+ if(nq)
+ {
+ fp->last = (char*)&name[n];
+ sh.last_table = dp->parent;
+ }
+ else
+ {
+ if(name[n]!='=') for(i=0; i < dp->ndisc; i++)
+ {
+ if((memcmp(name,dp->names[i],n)==0) && dp->names[i][n]==0)
+ return(nq);
+ }
+ errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np));
+ }
+ return(nq);
+}
+
+static void put_type(Namval_t* np, const char* val, int flag, Namfun_t* fp)
+{
+ Namval_t *nq;
+ if(val && (nq=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)))
+ {
+ Namfun_t *pp;
+ if((pp=nv_hasdisc(nq,fp->disc)) && pp->type==fp->type)
+
+ {
+ if(!nq->nvenv)
+ flag |= NV_EXPORT;
+ _nv_unset(np, flag);
+ nv_clone(nq,np,NV_IARRAY);
+ return;
+ }
+ }
+ nv_putv(np,val,flag,fp);
+ if(!val)
+ {
+ Namtype_t *dp = (Namtype_t*)fp;
+ Namval_t *nq;
+ Namarr_t *ap;
+ int i;
+ if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
+ return;
+ for(i=0; i < dp->numnodes; i++)
+ {
+ nq = nv_namptr(dp->nodes,i);
+ if(ap=nv_arrayptr(nq))
+ ap->nelem |= ARRAY_UNDEF;
+ if(!nv_hasdisc(nq,&type_disc))
+ _nv_unset(nq,flag|NV_TYPE|nv_isattr(nq,NV_RDONLY));
+ }
+ nv_disc(np,fp,NV_POP);
+ if(!(fp->nofree&1))
+ free((void*)fp);
+ }
+}
+
+static Namval_t *next_type(register Namval_t* np, Dt_t *root,Namfun_t *fp)
+{
+ Namtype_t *dp = (Namtype_t*)fp;
+ if(!root)
+ {
+ Namarr_t *ap = nv_arrayptr(np);
+ if(ap && (ap->nelem&ARRAY_UNDEF))
+ nv_putsub(np,(char*)0,ARRAY_SCAN);
+ dp->current = 0;
+ }
+ else if(++dp->current>=dp->numnodes)
+ return(0);
+ return(nv_namptr(dp->nodes,dp->current));
+}
+
+static Namfun_t *clone_inttype(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
+{
+ Namfun_t *pp= (Namfun_t*)malloc(fp->dsize);
+ memcpy((void*)pp, (void*)fp, fp->dsize);
+ fp->nofree &= ~1;
+ if(nv_isattr(mp,NV_NOFREE) && mp->nvalue.cp)
+ memcpy((void*)mp->nvalue.cp,np->nvalue.cp, fp->dsize-sizeof(*fp));
+ else
+ mp->nvalue.cp = (char*)(fp+1);
+ if(!nv_isattr(mp,NV_MINIMAL))
+ mp->nvenv = 0;
+ nv_offattr(mp,NV_RDONLY);
+ return(pp);
+}
+
+static int typeinfo(Opt_t* op, Sfio_t *out, const char *str, Optdisc_t *fp)
+{
+ char *cp,**help,buffer[256];
+ Namtype_t *dp;
+ Namval_t *np,*nq,*tp;
+ int n, i, offset=staktell();
+ Sfio_t *sp;
+
+ np = *(Namval_t**)(fp+1);
+ stakputs(NV_CLASS);
+ stakputc('.');
+ stakputs(np->nvname);
+ stakputc(0);
+ np = nv_open(cp=stakptr(offset), sh.var_tree, NV_NOADD|NV_VARNAME);
+ stakseek(offset);
+ if(!np)
+ {
+ sfprintf(sfstderr,"%s: no such variable\n",cp);
+ return(-1);
+ }
+ if(!(dp=(Namtype_t*)nv_hasdisc(np,&type_disc)))
+ {
+ Namfun_t *fp;
+ for(fp=np->nvfun;fp;fp=fp->next)
+ {
+ if(fp->disc && fp->disc->clonef==clone_inttype)
+ break;
+ }
+ if(!fp)
+ {
+ sfprintf(sfstderr,"%s: not a type\n",np->nvname);
+ return(-1);
+ }
+ if(strcmp(str,"other")==0)
+ return(0);
+ tp = fp->type;
+ nv_offattr(np,NV_RDONLY);
+ fp->type = 0;
+ if(np->nvenv)
+ sfprintf(out,"[+?\b%s\b is a %s.]\n", tp->nvname, np->nvenv);
+ cp = (char*)out->_next;
+ sfprintf(out,"[+?\b%s\b is a %n ", tp->nvname, &i);
+ nv_attribute(np,out,(char*)0, 1);
+ if(cp[i+1]=='i')
+ cp[i-1]='n';
+ fp->type = tp;
+ nv_onattr(np,NV_RDONLY);
+ sfprintf(out," with default value \b%s\b.]",nv_getval(np));
+ return(0);
+ }
+ if(strcmp(str,"other")==0)
+ {
+ Nambfun_t *bp;
+ if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD)))
+ {
+ for(i=0; i < bp->num; i++)
+ {
+ if(nv_isattr(bp->bltins[i],NV_OPTGET))
+ sfprintf(out,"\b%s.%s\b(3), ",np->nvname,bp->bnames[i]);
+ }
+ }
+ return(0);
+ }
+ help = &dp->names[dp->ndisc];
+ sp = sfnew((Sfio_t*)0,buffer,sizeof(buffer),-1,SF_STRING|SF_WRITE);
+ sfprintf(out,"[+?\b%s\b defines the following fields:]{\n",np->nvname);
+ for(i=0; i < dp->numnodes; i++)
+ {
+ nq = nv_namptr(dp->nodes,i);
+ if(tp=nv_type(nq))
+ {
+ Namfun_t *pp = nv_hasdisc(nq,&type_disc);
+ sfprintf(out,"\t[+%s?%s.\n",nq->nvname,tp->nvname);
+ n = strlen(nq->nvname);
+ while((cp=nv_namptr(dp->nodes,i+1)->nvname) && memcmp(cp,nq->nvname,n)==0 && cp[n]=='.')
+ i++;
+ }
+ else
+ {
+ sfseek(sp,(Sfoff_t)0, SEEK_SET);
+ nv_attribute(nq,sp,(char*)0,1);
+ cp = 0;
+ if(!nv_isattr(nq,NV_REF))
+ cp = sh_fmtq(nv_getval(nq));
+ sfputc(sp,0);
+ for(n=strlen(buffer); n>0 && buffer[n-1]==' '; n--);
+ buffer[n] = 0;
+ if(cp)
+ sfprintf(out,"\t[+%s?%s, default value is %s.\n",nq->nvname,*buffer?buffer:"string",cp);
+ else
+ sfprintf(out,"\t[+%s?%s.\n",nq->nvname,*buffer?buffer:"string");
+ }
+ if(help[i])
+ sfprintf(out," %s.",help[i]);
+ sfputc(out,']');
+ }
+ sfprintf(out,"}\n");
+ if(dp->ndisc>0)
+ {
+ stakseek(offset);
+ stakputs(NV_CLASS);
+ stakputc('.');
+ stakputs(np->nvname);
+ stakputc('.');
+ n = staktell();
+ sfprintf(out,"[+?\b%s\b defines the following discipline functions:]{\n",np->nvname);
+ for(i=0; i < dp->ndisc; i++)
+ {
+ stakputs(dp->names[i]);
+ stakputc(0);
+ cp = 0;
+ if((nq = nv_search(stakptr(offset),sh.fun_tree,0)) && nq->nvalue.cp)
+ cp = nq->nvalue.rp->help;
+ if(nq && nv_isattr(nq,NV_STATICF))
+ sfprintf(out,"\t[+%s?:static:%s]\n",dp->names[i],cp?cp:Empty);
+ else
+ sfprintf(out,"\t[+%s?%s]\n",dp->names[i],cp?cp:Empty);
+ if(cp)
+ sfputc(out,'.');
+ stakseek(n);
+ }
+ sfprintf(out,"}\n");
+ }
+ stakseek(offset);
+ sfclose(sp);
+ return(0);
+}
+
+static int std_disc(Namval_t *mp, Namtype_t *pp)
+{
+ register const char *sp, *cp = strrchr(mp->nvname,'.');
+ register const char **argv;
+ register int i;
+ Namval_t *np=0,*nq;
+ if(cp)
+ cp++;
+ else
+ cp = mp->nvname;
+ if(strcmp(cp,"create")==0)
+ {
+ if(pp)
+ pp->cp = mp;
+ return(0);
+ }
+ for(argv=nv_discnames; sp=*argv; argv++)
+ {
+ if(strcmp(cp,sp)==0)
+ {
+ if(!pp)
+ return(1);
+ goto found;
+ }
+ }
+ return(0);
+found:
+ if(memcmp(sp=mp->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
+ sp += sizeof(NV_CLASS);
+ sp += strlen(pp->fun.type->nvname)+1;
+ if(sp == cp)
+ np = pp->fun.type;
+ else for(i=1; i < pp->numnodes; i++)
+ {
+ nq = nv_namptr(pp->nodes,i);
+ if(memcmp(nq->nvname, sp, cp-sp-1)==0)
+ {
+ np = nq;
+ break;
+ }
+ }
+ if(np)
+ {
+ nv_onattr(mp,NV_NOFREE);
+ if(!nv_setdisc(np,cp, mp, (Namfun_t*)np))
+ sfprintf(sfstderr," nvsetdisc failed name=%s sp=%s cp=%s\n",np->nvname,sp,cp);
+ }
+ else
+ sfprintf(sfstderr,"can't set discipline %s cp=%s \n",sp,cp);
+ return(1);
+}
+
+
+void nv_addtype(Namval_t *np, const char *optstr, Optdisc_t *op, size_t optsz)
+{
+ Namdecl_t *cp = newof((Namdecl_t*)0,Namdecl_t,1,optsz);
+ Optdisc_t *dp = (Optdisc_t*)(cp+1);
+ Shell_t *shp = sh_getinterp();
+ Namval_t *mp,*bp;
+ char *name;
+ if(optstr)
+ cp->optstring = optstr;
+ else
+ cp->optstring = sh_opttype;
+ memcpy((void*)dp,(void*)op, optsz);
+ cp->optinfof = (void*)dp;
+ cp->tp = np;
+ mp = nv_search("typeset",shp->bltin_tree,0);
+ if(name=strrchr(np->nvname,'.'))
+ name++;
+ else
+ name = np->nvname;
+#if SHOPT_NAMESPACE
+ if(bp=(Namval_t*)shp->namespace)
+ {
+ Namtype_t *tp = (Namtype_t*)nv_hasdisc(np, &type_disc);
+ if(tp)
+ tp->nsp = bp;
+ if(!shp->strbuf2)
+ shp->strbuf2 = sfstropen();
+ sfprintf(shp->strbuf2,"%s.%s%c\n",nv_name(bp)+1,name,0);
+ name = sfstruse(shp->strbuf2);
+ }
+#endif /* SHOPT_NAMESPACE */
+ if((bp=nv_search(name,shp->fun_tree,NV_NOSCOPE)) && !bp->nvalue.ip)
+ nv_delete(bp,shp->fun_tree,0);
+ bp = sh_addbuiltin(name, (Shbltin_f)mp->nvalue.bfp, (void*)cp);
+ nv_onattr(bp,nv_isattr(mp,NV_PUBLIC));
+ nv_onattr(np, NV_RDONLY);
+}
+
+void nv_newtype(Namval_t *mp)
+{
+ struct {
+ Optdisc_t opt;
+ Namval_t *np;
+ } optdisc;
+ memset(&optdisc,0,sizeof(optdisc));
+ optdisc.opt.infof = typeinfo;
+ optdisc.np = mp;
+ nv_addtype(mp,sh_opttype, &optdisc.opt, sizeof(optdisc));
+}
+
+/*
+ * This function creates a type out of the <numnodes> nodes in the
+ * array <nodes>. The first node is the name for the type
+ */
+Namval_t *nv_mktype(Namval_t **nodes, int numnodes)
+{
+ Namval_t *mp=nodes[0], *bp=0, *np, *nq, **mnodes=nodes;
+ int i,j,k,m,n,nd=0,nref=0,iref=0,inherit=0;
+ int size=sizeof(NV_DATA), dsize=0, nnodes;
+ size_t offset=0;
+ char *name=0, *cp, *sp, **help;
+ Namtype_t *pp,*qp=0,*dp,*tp;
+ Dt_t *root = nv_dict(mp);
+ struct Namref *nrp = 0;
+ Namfun_t *fp;
+ m = strlen(mp->nvname)+1;
+ if(numnodes < 2)
+ {
+ cp = nodes[0]->nvname;
+ _nv_unset(nodes[0],NV_RDONLY);
+ errormsg(SH_DICT,ERROR_exit(1),e_badtypedef,cp);
+ }
+ for(nnodes=1,i=1; i <numnodes; i++)
+ {
+ np=nodes[i];
+ if(is_afunction(np))
+ {
+ if(!std_disc(np, (Namtype_t*)0))
+ {
+ size += strlen(np->nvname+m)+1;
+ if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
+ size -= sizeof(NV_CLASS);
+ nd++;
+ }
+ continue;
+ }
+ if(nv_isattr(np,NV_REF))
+ iref++;
+ if(np->nvenv)
+ size += strlen((char*)np->nvenv)+1;
+ if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np))
+ continue;
+ if(qp)
+ { /* delete duplicates */
+ for(j=0; j < qp->numnodes;j++)
+ {
+ nq = nv_namptr(qp->nodes,j);
+ if(strcmp(nq->nvname,&np->nvname[m])==0)
+ break;
+ }
+ if(j < qp->numnodes)
+ continue;
+ }
+ nnodes++;
+ if(name && memcmp(&name[m],&np->nvname[m],n)==0 && np->nvname[m+n]=='.')
+ offset -= sizeof(char*);
+ dsize = nv_datasize(np,&offset);
+ if(!nv_isarray(np) && (dp=(Namtype_t*)nv_hasdisc(np, &type_disc)))
+ {
+ nnodes += dp->numnodes;
+ if((n=dp->strsize)<0)
+ n = -n;
+ iref = nref += dp->nref;
+ if(np->nvname[m]=='_' && np->nvname[m+1]==0 && (bp=nv_type(np)))
+ {
+ qp = dp;
+ nd = dp->ndisc;
+ nnodes = dp->numnodes;
+ offset = 0;
+ dsize = nv_size(np);
+ size += n;
+ }
+ else
+ size += n + dp->numnodes*(strlen(&np->nvname[m])+1);
+ n = strlen(np->nvname);
+ while((i+1) < numnodes && (cp=nodes[i+1]->nvname) && memcmp(cp,np->nvname,n)==0 && cp[n]=='.')
+ i++;
+ }
+ else if(nv_isattr(np,NV_REF))
+ nref++;
+ offset += (dsize?dsize:4);
+ size += (n=strlen(name=np->nvname)-m+1);
+ }
+ offset = roundof(offset,sizeof(char*));
+ nv_setsize(mp,offset);
+ if(nd)
+ nd++;
+ k = roundof(sizeof(Namtype_t),sizeof(Sfdouble_t)) - sizeof(Namtype_t);
+ pp = newof(NiL, Namtype_t, 1, nnodes*NV_MINSZ + offset + size + (nnodes+nd)*sizeof(char*) + iref*sizeof(struct Namref)+k);
+ pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +offset+k;
+ pp->fun.type = mp;
+ pp->parent = nv_lastdict();
+ pp->np = mp;
+ pp->bp = bp;
+ pp->childfun.fun.disc = &chtype_disc;
+ pp->childfun.fun.nofree = 1;
+ pp->childfun.ttype = pp;
+ pp->childfun.ptype = pp;
+ pp->fun.disc = &type_disc;
+ pp->nodes = (char*)(pp+1);
+ pp->numnodes = nnodes;
+ pp->data = pp->nodes + nnodes*NV_MINSZ +k;
+ pp->dsize = offset;
+ nrp = (struct Namref*)(pp->data+offset);
+ pp->names = (char**)(nrp+iref);
+ help = &pp->names[nd];
+ pp->strsize = size;
+ cp = (char*)&pp->names[nd+nnodes];
+ if(qp)
+ mnodes = newof(NiL, Namval_t*, nd+1, 0);
+ nd = 0;
+ nq = nv_namptr(pp->nodes,0);
+ nq->nvname = cp;
+ nv_onattr(nq,NV_MINIMAL);
+ cp = strcopy(cp,NV_DATA);
+ *cp++ = 0;
+ for(name=0, offset=0, k=i=1; i < numnodes; i++)
+ {
+ np=nodes[i];
+ if(is_afunction(np))
+ {
+ sp = np->nvname+m;
+ if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
+ sp += sizeof(NV_CLASS);
+ if(!std_disc(np, pp))
+ {
+ /* see if discipline already defined */
+ for(j=0; j< nd; j++)
+ {
+ if(strcmp(sp,pp->names[j])==0)
+ {
+ mnodes[j] = nodes[i];
+ break;
+ }
+ }
+ if(j>=nd)
+ {
+ pp->names[nd] = cp;
+ mnodes[nd++] = nodes[i];
+ cp = strcopy(cp,sp);
+ *cp++ = 0;
+ }
+ nv_onattr(mnodes[j],NV_NOFREE);
+ }
+ continue;
+ }
+ if(inherit)
+ {
+ for(j=0; j < k ; j++)
+ {
+ nq = nv_namptr(pp->nodes,j);
+ if(strcmp(nq->nvname,&np->nvname[m])==0)
+ break;
+ }
+ if(j < k)
+ {
+ sp = nv_getval(np);
+ if(nv_isvtree(np))
+ sfprintf(sfstderr,"initialization not implemented\n");
+ else if(sp)
+ nv_putval(nq,sp,0);
+ goto skip;
+ }
+ }
+ if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np))
+ {
+ char *val=nv_getval(np);
+ nq = nv_namptr(pp->nodes,0);
+ nq->nvfun = 0;
+ nv_putval(nq,(val?val:0),nv_isattr(np,~(NV_IMPORT|NV_EXPORT|NV_ARRAY)));
+ nq->nvflag = np->nvflag|NV_NOFREE|NV_MINIMAL;
+ goto skip;
+ }
+ if(qp)
+ {
+ Nambfun_t *bp;
+ dp = (Namtype_t*)nv_hasdisc(nv_type(np), &type_disc);
+ memcpy(pp->nodes,dp->nodes,dp->numnodes*NV_MINSZ);
+ offset = nv_size(np);
+ memcpy(pp->data,dp->data,offset);
+ for(k=0;k < dp->numnodes; k++)
+ {
+ Namval_t *nr = nv_namptr(qp->nodes,k);
+ nq = nv_namptr(pp->nodes,k);
+ if(fixnode(pp,dp,k,nrp,0))
+ {
+ nrp++;
+ nq = nq->nvalue.nrp->np;
+ }
+ if(!nv_isattr(nr,NV_REF) && !nv_hasdisc(nr,&type_disc))
+ {
+ if(nr->nvsize)
+ memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size=nv_datasize(nr,(size_t*)0));
+ else
+ {
+ nq->nvalue.cp = nr->nvalue.cp;
+ nv_onattr(nq,NV_NOFREE);
+ }
+ }
+ }
+ if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD)))
+ {
+ for(j=0; j < bp->num; j++)
+ {
+ pp->names[nd++] = (char*)bp->bnames[j];
+ mnodes[j] = bp->bltins[j];
+ }
+ }
+ qp = 0;
+ inherit=1;
+ goto skip;
+ }
+ nq = nv_namptr(pp->nodes,k);
+ if(np->nvenv)
+ {
+ /* need to save the string pointer */
+ nv_offattr(np,NV_EXPORT);
+ help[k] = cp;
+ cp = strcopy(cp,np->nvenv);
+ j = *help[k];
+ if(islower(j))
+ *help[k] = toupper(j);
+ *cp++ = 0;
+ np->nvenv = 0;
+ }
+ nq->nvname = cp;
+ if(name && memcmp(name,&np->nvname[m],n)==0 && np->nvname[m+n]=='.')
+ offset -= sizeof(char*);
+ dsize = nv_datasize(np,&offset);
+ cp = strcopy(name=cp, &np->nvname[m]);
+ n = cp-name;
+ *cp++ = 0;
+ nq->nvsize = np->nvsize;
+ nq->nvflag = (np->nvflag&~(NV_IMPORT|NV_EXPORT))|NV_NOFREE|NV_MINIMAL;
+ if(dp = (Namtype_t*)nv_hasdisc(np, &type_disc))
+ {
+ int r,kfirst=k;
+ char *cname = &np->nvname[m];
+ /*
+ * If field is a type, mark the type by setting
+ * strsize<0. This changes create_type()
+ */
+ clone_all_disc(np,nq,NV_RDONLY);
+ if(nv_isarray(np))
+ {
+ nv_disc(nq, &pp->childfun.fun, NV_LAST);
+ k++;
+ goto skip;
+ }
+ if(fp=nv_hasdisc(nq,&chtype_disc))
+ nv_disc(nq, &pp->childfun.fun, NV_LAST);
+ if(tp = (Namtype_t*)nv_hasdisc(nq, &type_disc))
+ tp->strsize = -tp->strsize;
+else sfprintf(sfstderr,"tp==NULL\n");
+ for(r=0; r < dp->numnodes; r++)
+ {
+ Namval_t *nr = nv_namptr(dp->nodes,r);
+ nq = nv_namptr(pp->nodes,++k);
+ nq->nvname = cp;
+ dsize = nv_datasize(nr,&offset);
+ nq->nvflag = nr->nvflag;
+ if(nr->nvalue.cp)
+ {
+ Namchld_t *xp = (Namchld_t*)nv_hasdisc(nr,&chtype_disc);
+ if(xp && nr->nvalue.cp >= xp->ptype->data && nr->nvalue.cp < xp->ptype->data+xp->ptype->fun.dsize)
+ {
+ nq->nvalue.cp = pp->data+offset;
+ memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,dsize);
+ nv_onattr(nq,NV_NOFREE);
+ }
+ else
+ nq->nvalue.cp = strdup(nr->nvalue.cp);
+ nv_disc(nq, &pp->childfun.fun, NV_LAST);
+ }
+ nq->nvsize = nr->nvsize;
+ offset += dsize;
+ if(*cname!='_' || cname[1])
+ {
+ cp = strcopy(cp,cname);
+ *cp++ = '.';
+ }
+ cp = strcopy(cp,nr->nvname);
+ *cp++ = 0;
+ }
+ while((i+1) < numnodes && (cname=&nodes[i+1]->nvname[m]) && memcmp(cname,&np->nvname[m],n)==0 && cname[n]=='.')
+ {
+ int j=kfirst;
+ nv_unset(np);
+ nv_delete(np,root,0);
+ np = nodes[++i];
+ while(j < k)
+ {
+ nq = nv_namptr(pp->nodes,++j);
+ if(strcmp(nq->nvname,cname)==0)
+ {
+ sfprintf(sfstderr,"%s found at k=%d\n",nq->nvname,k);
+ if(nq->nvalue.cp>=pp->data && nq->nvalue.cp< (char*)pp->names)
+ memcpy((char*)nq->nvalue.cp,np->nvalue.cp,nv_datasize(np,0));
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ j = nv_isattr(np,NV_NOFREE);
+ nq->nvfun = np->nvfun;
+ np->nvfun = 0;
+ nv_disc(nq, &pp->childfun.fun, NV_LAST);
+ if(nq->nvfun)
+ {
+ for(fp=nq->nvfun; fp; fp = fp->next)
+ fp->nofree |= 1;
+ }
+ nq->nvalue.cp = np->nvalue.cp;
+ if(dsize && (np->nvalue.cp || !nv_isarray(np)))
+ {
+ nq->nvalue.cp = pp->data+offset;
+ sp = (char*)np->nvalue.cp;
+ if(nv_isattr(np,NV_INT16P) ==NV_INT16)
+ {
+ sp= (char*)&np->nvalue;
+ nv_onattr(nq,NV_INT16P);
+ j = 1;
+ }
+ if(sp)
+ memcpy((char*)nq->nvalue.cp,sp,dsize);
+ else if(nv_isattr(np,NV_LJUST|NV_RJUST))
+ memset((char*)nq->nvalue.cp,' ',dsize);
+ if(!j)
+ free((void*)np->nvalue.cp);
+ }
+ if(!nq->nvalue.cp && nq->nvfun== &pp->childfun.fun)
+ nq->nvalue.cp = Empty;
+ np->nvalue.cp = 0;
+#if 0
+ offset += dsize;
+#else
+ offset += (dsize?dsize:4);
+#endif
+ }
+ k++;
+ skip:
+ if(!nv_isnull(np))
+ _nv_unset(np,0);
+ nv_delete(np,root,0);
+ }
+ pp->ndisc = nd;
+ pp->nref = nref;
+ if(k>1)
+ {
+ nv_setsize(mp,offset);
+ mp->nvalue.cp = pp->data;
+ nv_onattr(mp,NV_NOFREE|NV_BINARY|NV_RAW);
+ }
+ else if(!mp->nvalue.cp)
+ mp->nvalue.cp = Empty;
+ nv_disc(mp, &pp->fun, NV_LAST);
+ if(nd>0)
+ {
+ pp->names[nd] = 0;
+ nv_adddisc(mp, (const char**)pp->names, mnodes);
+ }
+ if(mnodes!=nodes)
+ free((void*)mnodes);
+ nv_newtype(mp);
+ return(mp);
+}
+
+Namval_t *nv_mkinttype(char *name, size_t size, int sign, const char *help, Namdisc_t *ep)
+{
+ Namval_t *mp;
+ Namfun_t *fp;
+ Namdisc_t *dp;
+ int offset=staktell();
+ stakputs(NV_CLASS);
+ stakputc('.');
+ stakputs(name);
+ stakputc(0);
+ mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
+ stakseek(offset);
+ offset = size + sizeof(Namdisc_t);
+ fp = newof(NiL, Namfun_t, 1, offset);
+ fp->type = mp;
+ fp->nofree |= 1;
+ fp->dsize = sizeof(Namfun_t)+size;
+ dp = (Namdisc_t*)(fp+1);
+ if(ep)
+ *dp = *ep;
+ dp->clonef = clone_inttype;
+ fp->disc = dp;
+ mp->nvalue.cp = (char*)(fp+1) + sizeof(Namdisc_t);
+ nv_setsize(mp,10);
+ mp->nvenv = (char*)help;
+ nv_onattr(mp,NV_NOFREE|NV_RDONLY|NV_INTEGER|NV_EXPORT);
+ if(size==16)
+ nv_onattr(mp,NV_INT16P);
+ else if(size==64)
+ nv_onattr(mp,NV_INT64);
+ if(!sign)
+ nv_onattr(mp,NV_UNSIGN);
+ nv_disc(mp, fp, NV_LAST);
+ nv_newtype(mp);
+ return(mp);
+}
+
+void nv_typename(Namval_t *tp, Sfio_t *out)
+{
+ char *v,*cp;
+ Namtype_t *dp;
+ cp = nv_name(tp);
+ if(v=strrchr(cp,'.'))
+ cp = v+1;
+ if((dp = (Namtype_t*)nv_hasdisc(tp,&type_disc)) && dp->bp)
+ {
+ nv_typename(dp->bp,out);
+ sfprintf(out,"%s.%s",sfstruse(out),cp);
+ }
+ else
+ sfputr(out,cp,-1);
+}
+
+Namval_t *nv_type(Namval_t *np)
+{
+ Namfun_t *fp;
+ if(nv_isattr(np,NV_BLTIN|BLT_DCL)==(NV_BLTIN|BLT_DCL))
+ {
+ Namdecl_t *ntp = (Namdecl_t*)nv_context(np);
+ return(ntp?ntp->tp:0);
+ }
+ for(fp=np->nvfun; fp; fp=fp->next)
+ {
+ if(fp->type)
+ return(fp->type);
+ if(fp->disc && fp->disc->typef && (np= (*fp->disc->typef)(np,fp)))
+ return(np);
+ }
+ return(0);
+}
+
+/*
+ * call any and all create functions
+ */
+static void type_init(Namval_t *np)
+{
+ int i;
+ Namtype_t *dp, *pp=(Namtype_t*)nv_hasdisc(np,&type_disc);
+ Namval_t *nq;
+ if(!pp)
+ return;
+ for(i=0; i < pp->numnodes; i++)
+ {
+ nq = nv_namptr(pp->nodes,i);
+ if((dp=(Namtype_t*)nv_hasdisc(nq,&type_disc)) && dp->cp)
+ sh_fun(dp->cp,nq, (char**)0);
+ }
+ if(pp->cp)
+ sh_fun(pp->cp, np, (char**)0);
+}
+
+/*
+ * This function turns variable <np> to the type <tp>
+ */
+int nv_settype(Namval_t* np, Namval_t *tp, int flags)
+{
+ int isnull = nv_isnull(np);
+ int rdonly = nv_isattr(np,NV_RDONLY);
+ char *val=0;
+ Namarr_t *ap=0;
+ Shell_t *shp = sh_getinterp();
+ int nelem=0,subshell=shp->subshell;
+#if SHOPT_TYPEDEF
+ Namval_t *tq;
+ if(nv_type(np)==tp)
+ return(0);
+ if(nv_isarray(np) && (tq=nv_type(np)))
+ {
+ if(tp==tq)
+ return(0);
+ errormsg(SH_DICT,ERROR_exit(1),e_redef,nv_name(np));
+ }
+ if((ap=nv_arrayptr(np)) && ap->nelem>0)
+ {
+ nv_putsub(np,NIL(char*),ARRAY_SCAN);
+ ap->hdr.type = tp;
+ do
+ {
+ nv_arraysettype(np, tp, nv_getsub(np),flags);
+ }
+ while(nv_nextsub(np));
+ }
+ else if(ap || nv_isarray(np))
+ {
+ flags &= ~NV_APPEND;
+ if(!ap)
+ {
+ if(subshell)
+ {
+ sh_assignok(np,1);
+ shp->subshell = 0;
+ }
+ nv_putsub(np,"0",ARRAY_FILL);
+ ap = nv_arrayptr(np);
+ nelem = 1;
+
+ }
+ }
+ else
+#endif /*SHOPT_TYPEDEF */
+ {
+ if(isnull)
+ flags &= ~NV_APPEND;
+ else if(!nv_isvtree(np))
+ {
+ val = strdup(nv_getval(np));
+ if(!(flags&NV_APPEND))
+ _nv_unset(np, NV_RDONLY);
+ }
+ if(!nv_clone(tp,np,flags|NV_NOFREE))
+ return(0);
+ }
+ if(ap)
+ {
+ int nofree;
+ nv_disc(np,&ap->hdr,NV_POP);
+ np->nvalue.up = 0;
+ nv_clone(tp,np,flags|NV_NOFREE);
+ if(np->nvalue.cp && np->nvalue.cp!=Empty && !nv_isattr(np,NV_NOFREE))
+ free((void*)np->nvalue.cp);
+ np->nvalue.up = 0;
+ nofree = ap->hdr.nofree;
+ ap->hdr.nofree = 0;
+ ap->hdr.type = tp;
+ nv_disc(np, &ap->hdr, NV_FIRST);
+ ap->hdr.nofree = nofree;
+ nv_onattr(np,NV_ARRAY);
+ if(nelem)
+ {
+ ap->nelem++;
+ nv_putsub(np,"0",0);
+ _nv_unset(np,NV_RDONLY|NV_TYPE);
+ ap->nelem--;
+ shp->subshell = subshell;
+ }
+ }
+ type_init(np);
+ if(!rdonly)
+ nv_offattr(np,NV_RDONLY);
+ if(val)
+ {
+ nv_putval(np,val,NV_RDONLY);
+ free((void*)val);
+ }
+ return(0);
+}
+
+#define S(x) #x
+#define FIELD(x,y) { S(y##x), S(x##_t), offsetof(struct stat,st_##y##x) }
+typedef struct _field_
+{
+ char *name;
+ char *type;
+ int offset;
+} Fields_t;
+
+Fields_t foo[]=
+{
+ FIELD(dev,),
+ FIELD(ino,),
+ FIELD(nlink,),
+ FIELD(mode,),
+ FIELD(uid,),
+ FIELD(gid,),
+ FIELD(size,),
+ FIELD(time,a),
+ FIELD(time,m),
+ FIELD(time,c),
+#if 0
+ FIELD(blksize,),
+ FIELD(blocks,),
+#endif
+ 0
+};
+
+
+Namval_t *nv_mkstruct(const char *name, int rsize, Fields_t *fields)
+{
+ Namval_t *mp, *nq, *nr, *tp;
+ Fields_t *fp;
+ Namtype_t *dp, *pp;
+ char *cp, *sp;
+ int nnodes=0, offset=staktell(), n, r, i, j;
+ size_t m, size=0;
+ stakputs(NV_CLASS);
+ stakputc('.');
+ r = staktell();
+ stakputs(name);
+ stakputc(0);
+ mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
+ stakseek(r);
+
+ for(fp=fields; fp->name; fp++)
+ {
+ m = strlen(fp->name)+1;
+ size += m;
+ nnodes++;
+ if(memcmp(fp->type,"typeset",7))
+ {
+ stakputs(fp->type);
+ stakputc(0);
+ tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME|NV_NOADD|NV_NOFAIL);
+ stakseek(r);
+ if(!tp)
+ errormsg(SH_DICT,ERROR_exit(1),e_unknowntype,strlen(fp->type),fp->type);
+ if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc))
+ {
+ nnodes += dp->numnodes;
+ if((i=dp->strsize) < 0)
+ i = -i;
+ size += i + dp->numnodes*m;
+ }
+ }
+ }
+ pp = newof(NiL,Namtype_t, 1, nnodes*NV_MINSZ + rsize + size);
+ pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +rsize;
+ pp->fun.type = mp;
+ pp->np = mp;
+ pp->childfun.fun.disc = &chtype_disc;
+ pp->childfun.fun.nofree = 1;
+ pp->childfun.ttype = pp;
+ pp->childfun.ptype = pp;
+ pp->fun.disc = &type_disc;
+ pp->nodes = (char*)(pp+1);
+ pp->numnodes = nnodes;
+ pp->strsize = size;
+ pp->data = pp->nodes + nnodes*NV_MINSZ;
+ cp = pp->data + rsize;
+ for(i=0,fp=fields; fp->name; fp++)
+ {
+ nq = nv_namptr(pp->nodes,i++);
+ nq->nvname = cp;
+ nq->nvalue.cp = pp->data + fp->offset;
+ nv_onattr(nq,NV_MINIMAL|NV_NOFREE);
+ m = strlen(fp->name)+1;
+ memcpy(cp, fp->name, m);
+ cp += m;
+ if(memcmp(fp->type,"typeset",7))
+ {
+ stakputs(fp->type);
+ stakputc(0);
+ tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
+ stakseek(r);
+ clone_all_disc(tp,nq,NV_RDONLY);
+ nq->nvflag = tp->nvflag|NV_MINIMAL|NV_NOFREE;
+ nq->nvsize = tp->nvsize;
+ if(dp = (Namtype_t*)nv_hasdisc(nq,&type_disc))
+ dp->strsize = -dp->strsize;
+ if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc))
+ {
+ if(nv_hasdisc(nq,&chtype_disc))
+ nv_disc(nq, &pp->childfun.fun, NV_LAST);
+ sp = (char*)nq->nvalue.cp;
+ memcpy(sp, dp->data, nv_size(tp));
+ for(j=0; j < dp->numnodes; j++)
+ {
+ nr = nv_namptr(dp->nodes,j);
+ nq = nv_namptr(pp->nodes,i++);
+ nq->nvname = cp;
+ memcpy(cp,fp->name,m);
+ cp[m-1] = '.';
+ cp += m;
+ n = strlen(nr->nvname)+1;
+ memcpy(cp,nr->nvname,n);
+ cp += n;
+ if(nr->nvalue.cp>=dp->data && nr->nvalue.cp < (char*)pp + pp->fun.dsize)
+ {
+ nq->nvalue.cp = sp + (nr->nvalue.cp-dp->data);
+ }
+ nq->nvflag = nr->nvflag;
+ nq->nvsize = nr->nvsize;
+ }
+ }
+ }
+ else if(strmatch(fp->type+7,"*-*i*")==0)
+ {
+ nv_onattr(nq,NV_NOFREE|NV_RDONLY|NV_INTEGER);
+ if(strmatch(fp->type+7,"*-*s*")==0)
+ nv_onattr(nq,NV_INT16P);
+ else if(strmatch(fp->type+7,"*-*l*")==0)
+ nv_onattr(nq,NV_INT64);
+ if(strmatch(fp->type+7,"*-*u*")==0)
+ nv_onattr(nq,NV_UNSIGN);
+ }
+
+ }
+ stakseek(offset);
+ nv_onattr(mp,NV_RDONLY|NV_NOFREE|NV_BINARY);
+ nv_setsize(mp,rsize);
+ nv_disc(mp, &pp->fun, NV_LAST);
+ mp->nvalue.cp = pp->data;
+ nv_newtype(mp);
+ return(mp);
+}
+
+static void put_stat(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
+{
+ if(val)
+ {
+ if(stat(val,(struct stat*)np->nvalue.cp)<0)
+ sfprintf(sfstderr,"stat of %s failed\n",val);
+ return;
+ }
+ nv_putv(np,val,flag,nfp);
+ nv_disc(np,nfp,NV_POP);
+ if(!(nfp->nofree&1))
+ free((void*)nfp);
+}
+
+static const Namdisc_t stat_disc =
+{
+ 0,
+ put_stat
+};
+
+
+void nv_mkstat(void)
+{
+ Namval_t *tp;
+ Namfun_t *fp;
+ tp = nv_mkstruct("stat_t", sizeof(struct stat), foo);
+ nv_offattr(tp,NV_RDONLY);
+ nv_setvtree(tp);
+ fp = newof(NiL,Namfun_t,1,0);
+ fp->type = tp;
+ fp->disc = &stat_disc;
+ nv_disc(tp,fp,NV_FIRST);
+ nv_putval(tp,e_devnull,0);
+ nv_onattr(tp,NV_RDONLY);
+}
+
+static void write_indent(Sfio_t *out,char *str,int n,int indent)
+{
+ register int c, first=1;
+ register char *cp = str;
+ while(n-- && (c = *str++))
+ {
+ if(c=='\n')
+ {
+ if(!first)
+ sfnputc(out,'\t',indent);
+ first = 0;
+ sfwrite(out,cp,str-cp);
+ cp = str;
+ }
+ }
+ if(cp > str)
+ {
+ sfnputc(out,'\t',indent);
+ sfwrite(out,cp,str-cp);
+ }
+}
+
+int sh_outtype(Shell_t *shp,Sfio_t *out)
+{
+ Namval_t node,*mp,*tp;
+ Dt_t *dp;
+ char *cp,*sp,*xp,nvtype[sizeof(NV_CLASS)];
+ Sfio_t *iop=0;
+ int n=0,indent = 0;
+ if(cp=shp->prefix)
+ {
+ indent=1;
+ while(*cp)
+ {
+ if(*cp++ =='.')
+ indent++;
+ }
+ n = cp-shp->prefix+1;
+ }
+ strcpy(nvtype,NV_CLASS);
+ if(!(mp = nv_open(nvtype, shp->var_base,NV_NOADD|NV_VARNAME)))
+ return(0);
+ memcpy(&node,L_ARGNOD,sizeof(node));
+ L_ARGNOD->nvfun = 0;
+ L_ARGNOD->nvalue.cp = 0;
+ dp = nv_dict(mp);
+ if(indent==0)
+ for(tp = (Namval_t*)dtfirst(dp); tp; tp = (Namval_t*)dtnext(dp,tp))
+ {
+ if(!nv_search(tp->nvname,shp->bltin_tree,0))
+ continue;
+ sfprintf(out,"typeset -T %s\n",tp->nvname);
+ }
+ for(tp = (Namval_t*)dtfirst(dp); tp; tp = (Namval_t*)dtnext(dp,tp))
+ {
+ if(nv_isnull(tp))
+ continue;
+ if(indent && (memcmp(tp->nvname,shp->prefix,n-1) || tp->nvname[n-1]!='.' || strchr(tp->nvname+n,'.')))
+ continue;
+ nv_settype(L_ARGNOD,tp,0);
+ if(indent)
+ sfnputc(out,'\t',indent);
+ sfprintf(out,"typeset -T %s=",tp->nvname+n);
+ shp->last_table = 0;
+ cp = nv_getval(L_ARGNOD);
+ if(indent)
+ write_indent(out,cp,strlen(cp)-1,indent);
+ else
+ sfprintf(out,"%.*s",strlen(cp)-1,cp);
+ _nv_unset(L_ARGNOD,NV_RDONLY);
+ for(sp=0; sp=nv_setdisc(tp,(char*)0,(Namval_t*)sp,(Namfun_t*)tp);)
+ {
+ mp = (Namval_t*)nv_setdisc(tp,sp,tp,(Namfun_t*)tp);
+ if(!mp || mp==tp)
+ continue;
+ if(cp=strrchr(mp->nvname,'.'))
+ cp++;
+ else
+ cp = mp->nvname;
+ if(indent)
+ sfnputc(out,'\t',indent);
+ if(nv_isattr(mp,NV_FPOSIX))
+ sfprintf(out,"\t%s()",cp);
+ else
+ sfprintf(out,"\tfunction %s",cp);
+ xp = 0;
+ if(mp->nvalue.ip && mp->nvalue.rp->hoffset>=0)
+ {
+ if(nv_isattr(mp,NV_FTMP))
+ iop = shp->heredocs;
+ else if(xp=mp->nvalue.rp->fname)
+ iop = sfopen(iop,xp,"r");
+ else if(shp->gd->hist_ptr)
+ iop = (shp->gd->hist_ptr)->histfp;
+ if(iop && sfseek(iop,(Sfoff_t)mp->nvalue.rp->hoffset,SEEK_SET)>=0)
+ sfmove(iop,out, nv_size(mp), -1);
+ else
+ sfputc(iop,'\n');
+ if(xp)
+ sfclose(iop);
+ if(nv_isattr(mp,NV_STATICF|NV_TAGGED))
+ {
+ if(indent)
+ sfnputc(out,'\t',indent);
+ sfwrite(out,"\ttypeset -f",11);
+ if(nv_isattr(mp,NV_STATICF))
+ sfputc(out,'S');
+ if(nv_isattr(mp,NV_TAGGED))
+ sfputc(out,'t');
+ if(mp->nvalue.rp->help)
+ sfprintf(out,"h '%s'",mp->nvalue.rp->help);
+ sfprintf(out," %s\n",cp);
+ }
+ iop = 0;
+ }
+ }
+ if(indent)
+ sfnputc(out,'\t',indent);
+ sfwrite(out,")\n",2);
+ }
+ dtdelete(shp->var_base,L_ARGNOD);
+ memcpy(L_ARGNOD,&node,sizeof(node));
+ dtinsert(shp->var_base,L_ARGNOD);
+ return(0);
+}
diff --git a/src/cmd/ksh93/sh/parse.c b/src/cmd/ksh93/sh/parse.c
new file mode 100644
index 0000000..6ef3ef9
--- /dev/null
+++ b/src/cmd/ksh93/sh/parse.c
@@ -0,0 +1,2082 @@
+/***********************************************************************
+* *
+* 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
+ * Otherise 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->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;
+ }
+ /* 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;
+ else
+ lexp->aliasok = 1;
+ 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|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==';')
+ tok=sh_lex(lexp);
+ 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(fornod);
+ t->for_.fortyp=TNSPACE;
+ t->for_.forlst=0;
+ if(sh_lex(lexp))
+ sh_syntax(lexp);
+ t->for_.fornam=(char*) lexp->arg->argval;
+ while((tok=sh_lex(lexp))==NL);
+ if(tok!=LBRACE)
+ sh_syntax(lexp);
+ t->for_.fortre = 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;
+ }
+ }
+ *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
+ 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))
+ 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 */
diff --git a/src/cmd/ksh93/sh/path.c b/src/cmd/ksh93/sh/path.c
new file mode 100644
index 0000000..28d61ae
--- /dev/null
+++ b/src/cmd/ksh93/sh/path.c
@@ -0,0 +1,1824 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <fcin.h>
+#include <ls.h>
+#include <nval.h>
+#include "variables.h"
+#include "path.h"
+#include "io.h"
+#include "jobs.h"
+#include "history.h"
+#include "test.h"
+#include "FEATURE/dynamic"
+#include "FEATURE/externs"
+#if SHOPT_PFSH
+# ifdef _hdr_exec_attr
+# include <exec_attr.h>
+# endif
+# if _lib_vfork
+# include <ast_vfork.h>
+# else
+# define vfork() fork()
+# endif
+#endif
+
+#define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
+#define LIBCMD "cmd"
+
+
+static int canexecute(Shell_t*,char*,int);
+static void funload(Shell_t*,int,const char*);
+static void exscript(Shell_t*,char*, char*[], char**);
+static int path_chkpaths(Shell_t*,Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);
+static void path_checkdup(Shell_t *shp,register Pathcomp_t*);
+
+static const char *std_path;
+
+static int onstdpath(const char *name)
+{
+ register const char *cp = std_path, *sp;
+ if(cp)
+ while(*cp)
+ {
+ for(sp=name; *sp && (*cp == *sp); sp++,cp++);
+ if(*sp==0 && (*cp==0 || *cp==':'))
+ return(1);
+ while(*cp && *cp++!=':');
+ }
+ return(0);
+}
+
+#if SHOPT_PFSH
+int path_xattr(Shell_t *shp, const char *path, char *rpath)
+{
+ char resolvedpath[PATH_MAX + 1];
+ if (shp->gd->user && *shp->gd->user)
+ {
+ execattr_t *pf;
+ if(!rpath)
+ rpath = resolvedpath;
+ if (!realpath(path, resolvedpath))
+ return -1;
+ if(pf=getexecuser(shp->gd->user, KV_COMMAND, resolvedpath, GET_ONE))
+ {
+ if (!pf->attr || pf->attr->length == 0)
+ {
+ free_execattr(pf);
+ return(0);
+ }
+ free_execattr(pf);
+ return(1);
+ }
+ }
+ errno = ENOENT;
+ return(-1);
+}
+#endif /* SHOPT_PFSH */
+
+static pid_t path_pfexecve(Shell_t *shp,const char *path, char *argv[],char *const envp[],int spawn)
+{
+#if SHOPT_PFSH
+ char resolvedpath[PATH_MAX + 1];
+ pid_t pid;
+ if(spawn)
+ {
+ while((pid = vfork()) < 0)
+ _sh_fork(shp,pid, 0, (int*)0);
+ if(pid)
+ return(pid);
+ }
+ if(!sh_isoption(SH_PFSH))
+ return(execve(path, argv, envp));
+ /* Solaris implements realpath(3C) using the resolvepath(2) */
+ /* system call so we can save us to call access(2) first */
+
+ /* we can exec the command directly instead of via pfexec(1) if */
+ /* there is a matching entry without attributes in exec_attr(4) */
+ if(!path_xattr(shp,path,resolvedpath))
+ return(execve(path, argv, envp));
+ --argv;
+ argv[0] = argv[1];
+ argv[1] = resolvedpath;
+ return(execve("/usr/bin/pfexec", argv, envp));
+#else
+ return(execve(path, argv, envp));
+#endif
+}
+
+
+static pid_t _spawnveg(Shell_t *shp,const char *path, char* const argv[], char* const envp[], pid_t pgid)
+{
+ int waitsafe = job.waitsafe;
+ pid_t pid;
+ job_lock();
+ while(1)
+ {
+ sh_stats(STAT_SPAWN);
+ pid = spawnveg(path,argv,envp,pgid);
+ if(pid>=0 || errno!=EAGAIN)
+ break;
+ _sh_fork(shp,pid, 0, (int*)0);
+ }
+ job.waitsafe = waitsafe;
+ if(pid>0)
+ job_fork(pid);
+ else
+ job_unlock();
+ return(pid);
+}
+/*
+ * used with command -x to run the command in multiple passes
+ * spawn is non-zero when invoked via spawn
+ * the exitval is set to the maximum for each execution
+ */
+static pid_t path_xargs(Shell_t *shp,const char *path, char *argv[],char *const envp[], int spawn)
+{
+ register char *cp, **av, **xv;
+ char **avlast= &argv[shp->xargmax], **saveargs=0;
+ char *const *ev;
+ long size, left;
+ int nlast=1,n,exitval=0;
+ pid_t pid;
+ if(shp->xargmin < 0)
+ return((pid_t)-1);
+ size = shp->gd->lim.arg_max-1024;
+ for(ev=envp; cp= *ev; ev++)
+ size -= strlen(cp)-1;
+ for(av=argv; (cp= *av) && av< &argv[shp->xargmin]; av++)
+ size -= strlen(cp)-1;
+ for(av=avlast; cp= *av; av++,nlast++)
+ size -= strlen(cp)-1;
+ av = &argv[shp->xargmin];
+ if(!spawn)
+ job_clear();
+ shp->exitval = 0;
+ while(av<avlast)
+ {
+ for(xv=av,left=size; left>0 && av<avlast;)
+ left -= strlen(*av++)+1;
+ /* leave at least two for last */
+ if(left<0 && (avlast-av)<2)
+ av--;
+ if(xv==&argv[shp->xargmin])
+ {
+ n = nlast*sizeof(char*);
+ saveargs = (char**)malloc(n);
+ memcpy((void*)saveargs, (void*)av, n);
+ memcpy((void*)av,(void*)avlast,n);
+ }
+ else
+ {
+ for(n=shp->xargmin; xv < av; xv++)
+ argv[n++] = *xv;
+ for(xv=avlast; cp= *xv; xv++)
+ argv[n++] = cp;
+ argv[n] = 0;
+ }
+ if(saveargs || av<avlast || (exitval && !spawn))
+ {
+ if((pid=_spawnveg(shp,path,argv,envp,0)) < 0)
+ return(-1);
+ job_post(shp,pid,0);
+ job_wait(pid);
+ if(shp->exitval>exitval)
+ exitval = shp->exitval;
+ if(saveargs)
+ {
+ memcpy((void*)av,saveargs,n);
+ free((void*)saveargs);
+ saveargs = 0;
+ }
+ }
+ else if(spawn && !sh_isoption(SH_PFSH))
+ {
+ shp->xargexit = exitval;
+ if(saveargs)
+ free((void*)saveargs);
+ return(_spawnveg(shp,path,argv,envp,spawn>>1));
+ }
+ else
+ {
+ if(saveargs)
+ free((void*)saveargs);
+ return(path_pfexecve(shp,path,argv,envp,spawn));
+ }
+ }
+ if(!spawn)
+ exit(exitval);
+ return((pid_t)-1);
+}
+
+/*
+ * make sure PWD is set up correctly
+ * Return the present working directory
+ * Invokes getcwd() if flag==0 and if necessary
+ * Sets the PWD variable to this value
+ */
+char *path_pwd(Shell_t *shp,int flag)
+{
+ register char *cp;
+ register char *dfault = (char*)e_dot;
+ register int count = 0;
+ if(shp->pwd)
+ return((char*)shp->pwd);
+ while(1)
+ {
+ /* try from lowest to highest */
+ switch(count++)
+ {
+ case 0:
+ cp = nv_getval(PWDNOD);
+ break;
+ case 1:
+ cp = nv_getval(HOME);
+ break;
+ case 2:
+ cp = "/";
+ break;
+ case 3:
+ cp = (char*)e_crondir;
+ if(flag) /* skip next case when non-zero flag */
+ ++count;
+ break;
+ case 4:
+ {
+ if(cp=getcwd(NIL(char*),0))
+ {
+ nv_offattr(PWDNOD,NV_NOFREE);
+ _nv_unset(PWDNOD,0);
+ PWDNOD->nvalue.cp = cp;
+ goto skip;
+ }
+ break;
+ }
+ case 5:
+ return(dfault);
+ }
+ if(cp && *cp=='/' && test_inode(cp,e_dot))
+ break;
+ }
+ if(count>1)
+ {
+ nv_offattr(PWDNOD,NV_NOFREE);
+ nv_putval(PWDNOD,cp,NV_RDONLY);
+ }
+skip:
+ nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
+ shp->pwd = (char*)(PWDNOD->nvalue.cp);
+ return(cp);
+}
+
+static void free_bltin(Namval_t *np,void *data)
+{
+ register Pathcomp_t *pp= (Pathcomp_t*)data;
+ if(pp->flags&PATH_STD_DIR)
+ {
+ int offset=staktell();;
+ if(strcmp(pp->name,"/bin")==0 || memcmp(pp->name,np->nvname,pp->len) || np->nvname[pp->len]!='/')
+ return;
+ stakputs("/bin");
+ stakputs(np->nvname+pp->len+1);
+ stakputc(0);
+ sh_addbuiltin(stakptr(offset),(Shbltin_f)np->nvalue.bfp,NiL);
+ stakseek(offset);
+ return;
+ }
+ if((void*)np->nvenv==pp->bltin_lib)
+ nv_delete(np,sh_bltin_tree(),NV_NOFREE);
+}
+
+/*
+ * delete current Pathcomp_t structure
+ */
+void path_delete(Pathcomp_t *first)
+{
+ register Pathcomp_t *pp=first, *old=0, *ppnext;
+ while(pp)
+ {
+ ppnext = pp->next;
+ if(--pp->refcount<=0)
+ {
+ if(pp->lib)
+ free((void*)pp->lib);
+ if(pp->blib)
+ free((void*)pp->blib);
+ if(pp->bltin_lib || (pp->flags&PATH_STD_DIR))
+ {
+ nv_scan(sh_bltin_tree(),free_bltin,pp,0,0);
+#if SHOPT_DYNAMIC
+ if(pp->bltin_lib)
+ dlclose(pp->bltin_lib);
+#endif /* SHOPT_DYNAMIC */
+ }
+ free((void*)pp);
+ if(old)
+ old->next = ppnext;
+ }
+ else
+ old = pp;
+ pp = ppnext;
+ }
+}
+
+/*
+ * returns library variable from .paths
+ * The value might be returned on the stack overwriting path
+ */
+static char *path_lib(Shell_t *shp,Pathcomp_t *pp, char *path)
+{
+ register char *last = strrchr(path,'/');
+ register int r;
+ struct stat statb;
+ if(last)
+ *last = 0;
+ else
+ path = ".";
+ r = stat(path,&statb);
+ if(last)
+ *last = '/';
+ if(r>=0)
+ {
+ Pathcomp_t pcomp;
+ char save[8];
+ for( ;pp; pp=pp->next)
+ {
+ path_checkdup(shp,pp);
+ if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime)
+ return(pp->lib);
+ }
+ pcomp.len = 0;
+ if(last)
+ pcomp.len = last-path;
+ memcpy((void*)save, (void*)stakptr(PATH_OFFSET+pcomp.len),sizeof(save));
+ if(path_chkpaths(shp,(Pathcomp_t*)0,(Pathcomp_t*)0,&pcomp,PATH_OFFSET))
+ return(stakfreeze(1));
+ memcpy((void*)stakptr(PATH_OFFSET+pcomp.len),(void*)save,sizeof(save));
+ }
+ return(0);
+}
+
+#if 0
+void path_dump(register Pathcomp_t *pp)
+{
+ sfprintf(sfstderr,"dump\n");
+ while(pp)
+ {
+ sfprintf(sfstderr,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n",
+ pp,pp->dev,pp->ino,pp->len,pp->flags,pp->len,pp->name);
+ pp = pp->next;
+ }
+}
+#endif
+
+/*
+ * check for duplicate directories on PATH
+ */
+static void path_checkdup(Shell_t *shp,register Pathcomp_t *pp)
+{
+ register char *name = pp->name;
+ register Pathcomp_t *oldpp,*first;
+ register int flag=0;
+ struct stat statb;
+ if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode))
+ {
+ pp->flags |= PATH_SKIP;
+ pp->dev = *name=='/';
+ return;
+ }
+ pp->mtime = statb.st_mtime;
+ pp->ino = statb.st_ino;
+ pp->dev = statb.st_dev;
+ if(*name=='/' && onstdpath(name))
+ flag = PATH_STD_DIR;
+ first = (pp->flags&PATH_CDPATH)?(Pathcomp_t*)shp->cdpathlist:path_get(shp,"");
+ for(oldpp=first; oldpp && oldpp!=pp; oldpp=oldpp->next)
+ {
+ if(pp->ino==oldpp->ino && pp->dev==oldpp->dev && pp->mtime==oldpp->mtime)
+ {
+ flag |= PATH_SKIP;
+ break;
+ }
+ }
+ pp->flags |= flag;
+ if(((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH))
+ {
+ int offset = staktell();
+ stakputs(name);
+ path_chkpaths(shp,first,0,pp,offset);
+ stakseek(offset);
+ }
+}
+
+/*
+ * write the next path to search on the current stack
+ * if last is given, all paths that come before <last> are skipped
+ * the next pathcomp is returned.
+ */
+Pathcomp_t *path_nextcomp(Shell_t *shp,register Pathcomp_t *pp, const char *name, Pathcomp_t *last)
+{
+ Pathcomp_t *ppnext;
+ stakseek(PATH_OFFSET);
+ if(*name=='/')
+ pp = 0;
+ else
+ {
+ for(;pp && pp!=last;pp=ppnext)
+ {
+ ppnext = pp->next;
+ if(!pp->dev && !pp->ino)
+ path_checkdup(shp,pp);
+ if(pp->flags&PATH_SKIP)
+ continue;
+ if(!last || *pp->name!='/')
+ break;
+ }
+ if(!pp) /* this should not happen */
+ pp = last;
+ }
+ if(pp && (pp->name[0]!='.' || pp->name[1]))
+ {
+ if(*pp->name!='/')
+ {
+ stakputs(path_pwd(shp,1));
+ if(*stakptr(staktell()-1)!='/')
+ stakputc('/');
+ }
+ stakwrite(pp->name,pp->len);
+ if(pp->name[pp->len-1]!='/')
+ stakputc('/');
+ }
+ stakputs(name);
+ stakputc(0);
+ while(pp && pp!=last && (pp=pp->next))
+ {
+ if(!(pp->flags&PATH_SKIP))
+ return(pp);
+ }
+ return((Pathcomp_t*)0);
+}
+
+static Pathcomp_t* defpath_init(Shell_t *shp)
+{
+ Pathcomp_t *pp = (void*)path_addpath(shp,(Pathcomp_t*)0,(std_path),PATH_PATH);
+ return(pp);
+}
+
+static void path_init(Shell_t *shp)
+{
+ const char *val;
+ Pathcomp_t *pp;
+ if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*))))
+ std_path = e_defpath;
+ if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp)
+ {
+ shp->pathlist = pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH);
+ }
+ else
+ {
+ if(!(pp=(Pathcomp_t*)shp->defpathlist))
+ pp = defpath_init(shp);
+ shp->pathlist = (void*)path_dup(pp);
+ }
+ if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp)
+ {
+ pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
+ }
+}
+
+/*
+ * returns that pathlist to search
+ */
+Pathcomp_t *path_get(register Shell_t *shp,register const char *name)
+{
+ register Pathcomp_t *pp=0;
+ if(*name && strchr(name,'/'))
+ return(0);
+ if(!sh_isstate(SH_DEFPATH))
+ {
+ if(!shp->pathlist)
+ path_init(shp);
+ pp = (Pathcomp_t*)shp->pathlist;
+ }
+ if(!pp && (!(sh_scoped(shp,PATHNOD)->nvalue.cp)) || sh_isstate(SH_DEFPATH))
+ {
+ if(!(pp=(Pathcomp_t*)shp->defpathlist))
+ pp = defpath_init(shp);
+ }
+ return(pp);
+}
+
+/*
+ * open file corresponding to name using path give by <pp>
+ */
+static int path_opentype(Shell_t *shp,const char *name, register Pathcomp_t *pp, int fun)
+{
+ register int fd= -1;
+ struct stat statb;
+ Pathcomp_t *oldpp;
+ if(!pp && !shp->pathlist)
+ path_init(shp);
+ if(!fun && strchr(name,'/'))
+ {
+ if(sh_isoption(SH_RESTRICTED))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,name);
+ }
+ do
+ {
+ pp = path_nextcomp(shp,oldpp=pp,name,0);
+ while(oldpp && (oldpp->flags&PATH_SKIP))
+ oldpp = oldpp->next;
+ if(fun && (!oldpp || !(oldpp->flags&PATH_FPATH)))
+ continue;
+ if((fd = sh_open(path_relative(shp,stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0)
+ {
+ if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode))
+ {
+ errno = EISDIR;
+ sh_close(fd);
+ fd = -1;
+ }
+ }
+ }
+ while( fd<0 && pp);
+ if(fd>=0 && (fd = sh_iomovefd(fd)) > 0)
+ {
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] |= IOCLEX;
+ }
+ return(fd);
+}
+
+/*
+ * open file corresponding to name using path give by <pp>
+ */
+int path_open(Shell_t *shp,const char *name, register Pathcomp_t *pp)
+{
+ return(path_opentype(shp,name,pp,0));
+}
+
+/*
+ * given a pathname return the base name
+ */
+
+char *path_basename(register const char *name)
+{
+ register const char *start = name;
+ while (*name)
+ if ((*name++ == '/') && *name) /* don't trim trailing / */
+ start = name;
+ return ((char*)start);
+}
+
+char *path_fullname(Shell_t *shp,const char *name)
+{
+ int len=strlen(name)+1,dirlen=0;
+ char *path,*pwd;
+ if(*name!='/')
+ {
+ pwd = path_pwd(shp,1);
+ dirlen = strlen(pwd)+1;
+ }
+ path = (char*)malloc(len+dirlen);
+ if(dirlen)
+ {
+ memcpy((void*)path,(void*)pwd,dirlen);
+ path[dirlen-1] = '/';
+ }
+ memcpy((void*)&path[dirlen],(void*)name,len);
+ pathcanon(path,0);
+ return(path);
+}
+
+/*
+ * load functions from file <fno>
+ */
+static void funload(Shell_t *shp,int fno, const char *name)
+{
+ char *pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
+ Namval_t *np;
+ struct Ufunction *rp,*rpfirst;
+ int savestates = sh_getstate(), oldload=shp->funload;
+ pname = path_fullname(shp,stakptr(PATH_OFFSET));
+ if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname)))
+ {
+ Dt_t *funtree = sh_subfuntree(1);
+ while(1)
+ {
+ rpfirst = dtprev(shp->fpathdict,rp);
+ if(!rpfirst || strcmp(pname,rpfirst->fname))
+ break;
+ rp = rpfirst;
+ }
+ do
+ {
+ if((np = dtsearch(funtree,rp->np)) && is_afunction(np))
+ {
+ if(np->nvalue.rp)
+ np->nvalue.rp->fdict = 0;
+ nv_delete(np,funtree,NV_NOFREE);
+ }
+ dtinsert(funtree,rp->np);
+ rp->fdict = funtree;
+ }
+ while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0);
+ sh_close(fno);
+ return;
+ }
+ sh_onstate(SH_NOLOG);
+ sh_onstate(SH_NOALIAS);
+ shp->readscript = (char*)name;
+ shp->st.filename = pname;
+ shp->funload = 1;
+ error_info.line = 0;
+ sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),SH_FUNEVAL);
+ sh_close(fno);
+ shp->readscript = 0;
+#if SHOPT_NAMESPACE
+ if(shp->namespace)
+ np = sh_fsearch(shp,name,0);
+ else
+#endif /* SHOPT_NAMESPACE */
+ np = nv_search(name,shp->fun_tree,0);
+ if(!np || !np->nvalue.ip)
+ pname = stakcopy(shp->st.filename);
+ else
+ pname = 0;
+ free((void*)shp->st.filename);
+ shp->funload = oldload;
+ shp->st.filename = oldname;
+ sh_setstate(savestates);
+ if(pname)
+ errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),e_funload,name,pname);
+}
+
+/*
+ * do a path search and track alias if requested
+ * if flag is 0, or if name not found, then try autoloading function
+ * if flag==2 or 3, returns 1 if name found on FPATH
+ * if flag==3 no tracked alias will be set
+ * returns 1, if function was autoloaded.
+ * If oldpp is not NULL, it will contain a pointer to the path component
+ * where it was found.
+ */
+
+int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int flag)
+{
+ register Namval_t *np;
+ register int fno;
+ Pathcomp_t *pp=0;
+ if(name && strchr(name,'/'))
+ {
+ stakseek(PATH_OFFSET);
+ stakputs(name);
+ if(canexecute(shp,stakptr(PATH_OFFSET),0)<0)
+ {
+ *stakptr(PATH_OFFSET) = 0;
+ return(0);
+ }
+ if(*name=='/')
+ return(1);
+ stakseek(PATH_OFFSET);
+ stakputs(path_pwd(shp,1));
+ stakputc('/');
+ stakputs(name);
+ stakputc(0);
+ return(0);
+ }
+ if(sh_isstate(SH_DEFPATH))
+ {
+ if(!shp->defpathlist)
+ defpath_init(shp);
+ }
+ else if(!shp->pathlist)
+ path_init(shp);
+ if(flag)
+ {
+ if(!(flag&1) && (np=nv_search(name,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && (pp=(Pathcomp_t*)np->nvalue.cp))
+ {
+ stakseek(PATH_OFFSET);
+ path_nextcomp(shp,pp,name,pp);
+ if(oldpp)
+ *oldpp = pp;
+ stakputc(0);
+ return(0);
+ }
+ pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*));
+ if(oldpp)
+ *oldpp = pp;
+ if(!pp && (np=nv_search(name,shp->fun_tree,0))&&np->nvalue.ip)
+ return(1);
+ if(!pp)
+ *stakptr(PATH_OFFSET) = 0;
+ }
+ if(flag==0 || !pp || (pp->flags&PATH_FPATH))
+ {
+ if(!pp)
+ pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist;
+ if(pp && strmatch(name,e_alphanum) && (fno=path_opentype(shp,name,pp,1))>=0)
+ {
+ if(flag==2)
+ {
+ sh_close(fno);
+ return(1);
+ }
+ funload(shp,fno,name);
+ return(1);
+ }
+ *stakptr(PATH_OFFSET) = 0;
+ return(0);
+ }
+ else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/' && flag<3)
+ {
+ if(np=nv_search(name,shp->track_tree,NV_ADD))
+ path_alias(np,pp);
+ }
+ return(0);
+}
+
+/*
+ * do a path search and find the full pathname of file name
+ */
+Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp)
+{
+ register int f,isfun;
+ int noexec=0;
+ Pathcomp_t *oldpp;
+ Namval_t *np;
+ char *cp;
+ shp->path_err = ENOENT;
+ if(!pp && !(pp=path_get(shp,"")))
+ return(0);
+ shp->path_err = 0;
+ while(1)
+ {
+ sh_sigcheck(shp);
+ isfun = (pp->flags&PATH_FPATH);
+ if(oldpp=pp)
+ {
+ pp = path_nextcomp(shp,pp,name,0);
+ while(oldpp->flags&PATH_SKIP)
+ {
+ if(!(oldpp=oldpp->next))
+ {
+ shp->path_err = ENOENT;
+ return(0);
+ }
+ }
+ }
+
+ if(!isfun && !sh_isoption(SH_RESTRICTED))
+ {
+ if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
+ return(oldpp);
+#if SHOPT_DYNAMIC
+ if(oldpp->blib)
+ {
+ Shbltin_f addr;
+ int n = staktell();
+ char *cp;
+ stakputs("b_");
+ stakputs(name);
+ stakputc(0);
+ if(!oldpp->bltin_lib)
+ {
+ if(cp = strrchr(oldpp->blib,'/'))
+ cp++;
+ else
+ cp = oldpp->blib;
+ if(!strcmp(cp,LIBCMD) && (addr=(Shbltin_f)dlllook((void*)0,stakptr(n))))
+ {
+ if((np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) && nv_isattr(np,NV_BLTINOPT))
+ return(oldpp);
+ }
+#ifdef SH_PLUGIN_VERSION
+ if (oldpp->bltin_lib = dllplugin(SH_ID, oldpp->blib, NiL, SH_PLUGIN_VERSION, NiL, RTLD_LAZY, NiL, 0))
+ sh_addlib(shp,oldpp->bltin_lib);
+#else
+#if (_AST_VERSION>=20040404)
+ if (oldpp->bltin_lib = dllplug(SH_ID, oldpp->blib, NiL, RTLD_LAZY, NiL, 0))
+#else
+ if (oldpp->bltin_lib = dllfind(oldpp->blib, NiL, RTLD_LAZY, NiL, 0))
+#endif
+ {
+ /*
+ * this detects the 2007-05-11 builtin context change and also
+ * the 2008-03-30 opt_info.num change that hit libcmd::b_head
+ */
+
+ if (libcmd && !dlllook(oldpp->bltin_lib, "b_pids"))
+ {
+ dlclose(oldpp->bltin_lib);
+ oldpp->bltin_lib = 0;
+ oldpp->blib = 0;
+ }
+ else
+ sh_addlib(shp,oldpp->bltin_lib);
+ }
+#endif
+ }
+ if(oldpp->bltin_lib &&
+ (addr=(Shbltin_f)dlllook(oldpp->bltin_lib,stakptr(n))) &&
+ (!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=(Nambfp_f)addr) &&
+ (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)))
+ {
+ np->nvenv = oldpp->bltin_lib;
+ return(oldpp);
+ }
+ }
+#endif /* SHOPT_DYNAMIC */
+ }
+ sh_stats(STAT_PATHS);
+ f = canexecute(shp,stakptr(PATH_OFFSET),isfun);
+ if(isfun && f>=0 && (cp = strrchr(name,'.')))
+ {
+ *cp = 0;
+ if(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE))
+ f = -1;
+ *cp = '.';
+ }
+ if(isfun && f>=0)
+ {
+ nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION);
+ funload(shp,f,name);
+ close(f);
+ f = -1;
+ return(0);
+ }
+ else if(f>=0 && (oldpp->flags & PATH_STD_DIR))
+ {
+ int n = staktell();
+ stakputs("/bin/");
+ stakputs(name);
+ stakputc(0);
+ np = nv_search(stakptr(n),shp->bltin_tree,0);
+ stakseek(n);
+ if(np)
+ {
+ n = np->nvflag;
+ np = sh_addbuiltin(stakptr(PATH_OFFSET),(Shbltin_f)np->nvalue.bfp,nv_context(np));
+ np->nvflag = n;
+ }
+ }
+ if(!pp || f>=0)
+ break;
+ if(errno!=ENOENT)
+ noexec = errno;
+ }
+ if(f<0)
+ {
+ shp->path_err = (noexec?noexec:ENOENT);
+ return(0);
+ }
+ stakputc(0);
+ return(oldpp);
+}
+
+/*
+ * returns 0 if path can execute
+ * sets exec_err if file is found but can't be executable
+ */
+#undef S_IXALL
+#ifdef S_IXUSR
+# define S_IXALL (S_IXUSR|S_IXGRP|S_IXOTH)
+#else
+# ifdef S_IEXEC
+# define S_IXALL (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
+# else
+# define S_IXALL 0111
+# endif /*S_EXEC */
+#endif /* S_IXUSR */
+
+static int canexecute(Shell_t *shp,register char *path, int isfun)
+{
+ struct stat statb;
+ register int fd=0;
+ path = path_relative(shp,path);
+ if(isfun)
+ {
+ if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0)
+ goto err;
+ }
+ else if(stat(path,&statb) < 0)
+ {
+#if _WINIX
+ /* check for .exe or .bat suffix */
+ char *cp;
+ if(errno==ENOENT && (!(cp=strrchr(path,'.')) || strlen(cp)>4 || strchr(cp,'/')))
+ {
+ int offset = staktell()-1;
+ stakseek(offset);
+ stakputs(".bat");
+ path = stakptr(PATH_OFFSET);
+ if(stat(path,&statb) < 0)
+ {
+ if(errno!=ENOENT)
+ goto err;
+ memcpy(stakptr(offset),".sh",4);
+ if(stat(path,&statb) < 0)
+ goto err;
+ }
+ }
+ else
+#endif /* _WINIX */
+ goto err;
+ }
+ errno = EPERM;
+ if(S_ISDIR(statb.st_mode))
+ errno = EISDIR;
+ else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0)
+ return(fd);
+ if(isfun && fd>=0)
+ sh_close(fd);
+err:
+ return(-1);
+}
+
+/*
+ * Return path relative to present working directory
+ */
+
+char *path_relative(Shell_t *shp,register const char* file)
+{
+ register const char *pwd;
+ register const char *fp = file;
+ /* can't relpath when shp->pwd not set */
+ if(!(pwd=shp->pwd))
+ return((char*)fp);
+ while(*pwd==*fp)
+ {
+ if(*pwd++==0)
+ return((char*)e_dot);
+ fp++;
+ }
+ if(*pwd==0 && *fp == '/')
+ {
+ while(*++fp=='/');
+ if(*fp)
+ return((char*)fp);
+ return((char*)e_dot);
+ }
+ return((char*)file);
+}
+
+void path_exec(Shell_t *shp,register const char *arg0,register char *argv[],struct argnod *local)
+{
+ char **envp;
+ const char *opath;
+ Pathcomp_t *libpath, *pp=0;
+ int slash=0;
+ nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN,0);
+ envp = sh_envgen();
+ if(strchr(arg0,'/'))
+ {
+ slash=1;
+ /* name containing / not allowed for restricted shell */
+ if(sh_isoption(SH_RESTRICTED))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,arg0);
+ }
+ else
+ pp=path_get(shp,arg0);
+ shp->path_err= ENOENT;
+ sfsync(NIL(Sfio_t*));
+ timerdel(NIL(void*));
+ /* find first path that has a library component */
+ while(pp && (pp->flags&PATH_SKIP))
+ pp = pp->next;
+ if(pp || slash) do
+ {
+ sh_sigcheck(shp);
+ if(libpath=pp)
+ {
+ pp = path_nextcomp(shp,pp,arg0,0);
+ opath = stakfreeze(1)+PATH_OFFSET;
+ }
+ else
+ opath = arg0;
+ path_spawn(shp,opath,argv,envp,libpath,0);
+ while(pp && (pp->flags&PATH_FPATH))
+ pp = path_nextcomp(shp,pp,arg0,0);
+ }
+ while(pp);
+ /* force an exit */
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
+ if((errno=shp->path_err)==ENOENT)
+ errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found,arg0);
+ else
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arg0);
+}
+
+pid_t path_spawn(Shell_t *shp,const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn)
+{
+ register char *path;
+ char **xp=0, *xval, *libenv = (libpath?libpath->lib:0);
+ Namval_t* np;
+ char *s, *v;
+ int r, n, pidsize;
+ pid_t pid= -1;
+ /* leave room for inserting _= pathname in environment */
+ envp--;
+#if _lib_readlink
+ /* save original pathname */
+ stakseek(PATH_OFFSET);
+ pidsize = sfprintf(stkstd,"*%d*",spawn?getpid():getppid());
+ stakputs(opath);
+ opath = stakfreeze(1)+PATH_OFFSET+pidsize;
+ np=nv_search(argv[0],shp->track_tree,0);
+ while(libpath && !libpath->lib)
+ libpath=libpath->next;
+ if(libpath && (!np || nv_size(np)>0))
+ {
+ /* check for symlink and use symlink name */
+ char buff[PATH_MAX+1];
+ char save[PATH_MAX+1];
+ stakseek(PATH_OFFSET);
+ stakputs(opath);
+ path = stakptr(PATH_OFFSET);
+ while((n=readlink(path,buff,PATH_MAX))>0)
+ {
+ buff[n] = 0;
+ n = PATH_OFFSET;
+ r = 0;
+ if((v=strrchr(path,'/')) && *buff!='/')
+ {
+ if(buff[0]=='.' && buff[1]=='.' && (r = strlen(path) + 1) <= PATH_MAX)
+ memcpy(save, path, r);
+ else
+ r = 0;
+ n += (v+1-path);
+ }
+ stakseek(n);
+ stakputs(buff);
+ stakputc(0);
+ path = stakptr(PATH_OFFSET);
+ if(v && buff[0]=='.' && buff[1]=='.')
+ {
+ pathcanon(path, 0);
+ if(r && access(path,X_OK))
+ {
+ memcpy(path, save, r);
+ break;
+ }
+ }
+ if(libenv = path_lib(shp,libpath,path))
+ break;
+ }
+ stakseek(0);
+ }
+#endif
+ if(libenv && (v = strchr(libenv,'=')))
+ {
+ n = v - libenv;
+ *v = 0;
+ np = nv_open(libenv,shp->var_tree,0);
+ *v = '=';
+ s = nv_getval(np);
+ stakputs(libenv);
+ if(s)
+ {
+ stakputc(':');
+ stakputs(s);
+ }
+ v = stakfreeze(1);
+ r = 1;
+ xp = envp + 1;
+ while (s = *xp++)
+ {
+ if (strneq(s, v, n) && s[n] == '=')
+ {
+ xval = *--xp;
+ *xp = v;
+ r = 0;
+ break;
+ }
+ }
+ if (r)
+ {
+ *envp-- = v;
+ xp = 0;
+ }
+ }
+ if(!opath)
+ opath = stakptr(PATH_OFFSET);
+ envp[0] = (char*)opath-(PATH_OFFSET+pidsize);
+ envp[0][0] = '_';
+ envp[0][1] = '=';
+ sfsync(sfstderr);
+ sh_sigcheck(shp);
+ path = path_relative(shp,opath);
+#ifdef SHELLMAGIC
+ if(*path!='/' && path!=opath)
+ {
+ /*
+ * The following code because execv(foo,) and execv(./foo,)
+ * may not yield the same results
+ */
+ char *sp = (char*)malloc(strlen(path)+3);
+ sp[0] = '.';
+ sp[1] = '/';
+ strcpy(sp+2,path);
+ path = sp;
+ }
+#endif /* SHELLMAGIC */
+ if(spawn && !sh_isoption(SH_PFSH))
+ pid = _spawnveg(shp,opath, &argv[0],envp, spawn>>1);
+ else
+ pid = path_pfexecve(shp,opath, &argv[0] ,envp,spawn);
+ if(xp)
+ *xp = xval;
+#ifdef SHELLMAGIC
+ if(*path=='.' && path!=opath)
+ {
+ free(path);
+ path = path_relative(shp,opath);
+ }
+#endif /* SHELLMAGIC */
+ if(pid>0)
+ return(pid);
+retry:
+ switch(shp->path_err = errno)
+ {
+#ifdef apollo
+ /*
+ * On apollo's execve will fail with eacces when
+ * file has execute but not read permissions. So,
+ * for now we will pretend that EACCES and ENOEXEC
+ * mean the same thing.
+ */
+ case EACCES:
+#endif /* apollo */
+ case ENOEXEC:
+#if SHOPT_SUID_EXEC
+ case EPERM:
+ /* some systems return EPERM if setuid bit is on */
+#endif
+ errno = ENOEXEC;
+ if(spawn)
+ {
+#ifdef _lib_fork
+ if(shp->subshell)
+ return(-1);
+ do
+ {
+ if((pid=fork())>0)
+ return(pid);
+ }
+ while(_sh_fork(shp,pid,0,(int*)0) < 0);
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
+#else
+ return(-1);
+#endif
+ }
+ exscript(shp,path,argv,envp);
+#ifndef apollo
+ case EACCES:
+ {
+ struct stat statb;
+ if(stat(path,&statb)>=0)
+ {
+ if(S_ISDIR(statb.st_mode))
+ errno = EISDIR;
+#ifdef S_ISSOCK
+ if(S_ISSOCK(statb.st_mode))
+ exscript(shp,path,argv,envp);
+#endif
+ }
+ }
+ /* FALL THROUGH */
+#endif /* !apollo */
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG:
+#endif /* ENAMETOOLONG */
+#if !SHOPT_SUID_EXEC
+ case EPERM:
+#endif
+ shp->path_err = errno;
+ return(-1);
+ case ENOTDIR:
+ case ENOENT:
+ case EINTR:
+#ifdef EMLINK
+ case EMLINK:
+#endif /* EMLINK */
+ return(-1);
+ case E2BIG:
+ if(shp->xargmin)
+ {
+ pid = path_xargs(shp,opath, &argv[0] ,envp,spawn);
+ if(pid<0)
+ goto retry;
+ return(pid);
+ }
+ default:
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
+ }
+ return 0;
+}
+
+/*
+ * File is executable but not machine code.
+ * Assume file is a Shell script and execute it.
+ */
+
+static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp)
+{
+ register Sfio_t *sp;
+ path = path_relative(shp,path);
+ shp->comdiv=0;
+ shp->bckpid = 0;
+ shp->coshell = 0;
+ shp->st.ioset=0;
+ /* clean up any cooperating processes */
+ if(shp->cpipe[0]>0)
+ sh_pclose(shp->cpipe);
+ if(shp->cpid && shp->outpipe)
+ sh_close(*shp->outpipe);
+ shp->cpid = 0;
+ if(sp=fcfile())
+ while(sfstack(sp,SF_POPSTACK));
+ job_clear();
+ if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX))
+ sh_close(shp->infd);
+ sh_setstate(sh_state(SH_FORKED));
+ sfsync(sfstderr);
+#if SHOPT_SUID_EXEC && !SHOPT_PFSH
+ /* check if file cannot open for read or script is setuid/setgid */
+ {
+ static char name[] = "/tmp/euidXXXXXXXXXX";
+ register int n;
+ register uid_t euserid;
+ char *savet=0;
+ struct stat statb;
+ if((n=sh_open(path,O_RDONLY,0)) >= 0)
+ {
+ /* move <n> if n=0,1,2 */
+ n = sh_iomovefd(n);
+ if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
+ goto openok;
+ sh_close(n);
+ }
+ if((euserid=geteuid()) != shp->gd->userid)
+ {
+ strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
+ /* create a suid open file with owner equal effective uid */
+ if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0)
+ goto fail;
+ unlink(name);
+ /* make sure that file has right owner */
+ if(fstat(n,&statb)<0 || statb.st_uid != euserid)
+ goto fail;
+ if(n!=10)
+ {
+ sh_close(10);
+ fcntl(n, F_DUPFD, 10);
+ sh_close(n);
+ n=10;
+ }
+ }
+ savet = *--argv;
+ *argv = path;
+ path_pfexecve(shp,e_suidexec,argv,envp,0);
+ fail:
+ /*
+ * The following code is just for compatibility
+ */
+ if((n=open(path,O_RDONLY,0)) < 0)
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
+ if(savet)
+ *argv++ = savet;
+ openok:
+ shp->infd = n;
+ }
+#else
+ if((shp->infd = sh_open(path,O_RDONLY,0)) < 0)
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
+#endif
+ shp->infd = sh_iomovefd(shp->infd);
+#if SHOPT_ACCT
+ sh_accbegin(path) ; /* reset accounting */
+#endif /* SHOPT_ACCT */
+ shp->arglist = sh_argcreate(argv);
+ shp->lastarg = strdup(path);
+ /* save name of calling command */
+ shp->readscript = error_info.id;
+ /* close history file if name has changed */
+ if(shp->gd->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->gd->hist_ptr->histname))
+ {
+ hist_close(shp->gd->hist_ptr);
+ (HISTCUR)->nvalue.lp = 0;
+ }
+ sh_offstate(SH_FORKED);
+ if(shp->sigflag[SIGCHLD]==SH_SIGOFF)
+ shp->sigflag[SIGCHLD] = SH_SIGFAULT;
+ siglongjmp(*shp->jmplist,SH_JMPSCRIPT);
+}
+
+#if SHOPT_ACCT
+# include <sys/acct.h>
+# include "FEATURE/time"
+
+ static struct acct sabuf;
+ static struct tms buffer;
+ static clock_t before;
+ static char *SHACCT; /* set to value of SHACCT environment variable */
+ static shaccton; /* non-zero causes accounting record to be written */
+ static int compress(time_t);
+ /*
+ * initialize accounting, i.e., see if SHACCT variable set
+ */
+ void sh_accinit(void)
+ {
+ SHACCT = getenv("SHACCT");
+ }
+ /*
+ * suspend accounting until turned on by sh_accbegin()
+ */
+ void sh_accsusp(void)
+ {
+ shaccton=0;
+#ifdef AEXPAND
+ sabuf.ac_flag |= AEXPND;
+#endif /* AEXPAND */
+ }
+
+ /*
+ * begin an accounting record by recording start time
+ */
+ void sh_accbegin(const char *cmdname)
+ {
+ if(SHACCT)
+ {
+ sabuf.ac_btime = time(NIL(time_t *));
+ before = times(&buffer);
+ sabuf.ac_uid = getuid();
+ sabuf.ac_gid = getgid();
+ strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
+ sizeof(sabuf.ac_comm));
+ shaccton = 1;
+ }
+ }
+ /*
+ * terminate an accounting record and append to accounting file
+ */
+ void sh_accend(void)
+ {
+ int fd;
+ clock_t after;
+
+ if(shaccton)
+ {
+ after = times(&buffer);
+ sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
+ sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
+ sabuf.ac_etime = compress( (time_t)(after-before));
+ fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
+ write(fd, (const char*)&sabuf, sizeof( sabuf ));
+ close( fd);
+ }
+ }
+
+ /*
+ * Produce a pseudo-floating point representation
+ * with 3 bits base-8 exponent, 13 bits fraction.
+ */
+ static int compress(register time_t t)
+ {
+ register int exp = 0, rund = 0;
+
+ while (t >= 8192)
+ {
+ exp++;
+ rund = t&04;
+ t >>= 3;
+ }
+ if (rund)
+ {
+ t++;
+ if (t >= 8192)
+ {
+ t >>= 3;
+ exp++;
+ }
+ }
+ return((exp<<13) + t);
+ }
+#endif /* SHOPT_ACCT */
+
+
+
+/*
+ * add a pathcomponent to the path search list and eliminate duplicates
+ * and non-existing absolute paths.
+ */
+static Pathcomp_t *path_addcomp(Shell_t *shp,Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag)
+{
+ register Pathcomp_t *pp, *oldpp;
+ int len, offset=staktell();
+ if(!(flag&PATH_BFPATH))
+ {
+ register const char *cp = name;
+ while(*cp && *cp!=':')
+ stakputc(*cp++);
+ len = staktell()-offset;
+ stakputc(0);
+ stakseek(offset);
+ name = (const char*)stakptr(offset);
+ }
+ else
+ len = strlen(name);
+ for(pp=first; pp; pp=pp->next)
+ {
+ if(memcmp(name,pp->name,len)==0 && (pp->name[len]==':' || pp->name[len]==0))
+ {
+ pp->flags |= flag;
+ return(first);
+ }
+ }
+ for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next);
+ pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);
+ pp->shp = shp;
+ pp->refcount = 1;
+ memcpy((char*)(pp+1),name,len+1);
+ pp->name = (char*)(pp+1);
+ pp->len = len;
+ if(oldpp)
+ oldpp->next = pp;
+ else
+ first = pp;
+ pp->flags = flag;
+ if(strcmp(name,SH_CMDLIB_DIR)==0)
+ {
+ pp->dev = 1;
+ pp->flags |= PATH_BUILTIN_LIB;
+ pp->blib = malloc(4);
+ strcpy(pp->blib,LIBCMD);
+ return(first);
+ }
+ if((old||shp->pathinit) && ((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH))
+ path_chkpaths(shp,first,old,pp,offset);
+ return(first);
+}
+
+/*
+ * This function checks for the .paths file in directory in <pp>
+ * it assumes that the directory is on the stack at <offset>
+ */
+static int path_chkpaths(Shell_t *shp,Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset)
+{
+ struct stat statb;
+ int k,m,n,fd;
+ char *sp,*cp,*ep;
+ stakseek(offset+pp->len);
+ if(pp->len==1 && *stakptr(offset)=='/')
+ stakseek(offset);
+ stakputs("/.paths");
+ if((fd=open(stakptr(offset),O_RDONLY))>=0)
+ {
+ fstat(fd,&statb);
+ n = statb.st_size;
+ stakseek(offset+pp->len+n+2);
+ sp = stakptr(offset+pp->len);
+ *sp++ = '/';
+ n=read(fd,cp=sp,n);
+ sp[n] = 0;
+ close(fd);
+ for(ep=0; n--; cp++)
+ {
+ if(*cp=='=')
+ {
+ ep = cp+1;
+ continue;
+ }
+ else if(*cp!='\r' && *cp!='\n')
+ continue;
+ if(*sp=='#' || sp==cp)
+ {
+ sp = cp+1;
+ continue;
+ }
+ *cp = 0;
+ m = ep ? (ep-sp) : 0;
+ if(m==0 || m==6 && memcmp((void*)sp,(void*)"FPATH=",6)==0)
+ {
+ if(first)
+ {
+ char *ptr = stakptr(offset+pp->len+1);
+ if(ep)
+ strcpy(ptr,ep);
+ path_addcomp(shp,first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH);
+ }
+ }
+ else if(m==12 && memcmp((void*)sp,(void*)"BUILTIN_LIB=",12)==0)
+ {
+ if(!(pp->flags & PATH_BUILTIN_LIB) || strchr(ep,'-'))
+ {
+ if ((pp->flags & (PATH_BUILTIN_LIB|PATH_STD_DIR)) == PATH_BUILTIN_LIB)
+ {
+ free(pp->blib);
+ pp->blib = 0;
+ }
+ pp->flags |= PATH_BUILTIN_LIB;
+ if (*ep == '.' && !*(ep + 1))
+ pp->flags |= PATH_STD_DIR;
+ else
+ {
+ k = strlen(ep)+1;
+ if (*ep != '/')
+ k += pp->len+1;
+ pp->blib = sp = malloc(k);
+ if (*ep != '/')
+ {
+ strcpy(pp->blib,pp->name);
+ sp += pp->len;
+ *sp++ = '/';
+ }
+ strcpy(sp,ep);
+ }
+ }
+ }
+ else if(m)
+ {
+ pp->lib = (char*)malloc(cp-sp+pp->len+2);
+ memcpy((void*)pp->lib,(void*)sp,m);
+ memcpy((void*)&pp->lib[m],stakptr(offset),pp->len);
+ pp->lib[k=m+pp->len] = '/';
+ strcpy((void*)&pp->lib[k+1],ep);
+ pathcanon(&pp->lib[m],0);
+ if(!first)
+ {
+ stakseek(0);
+ stakputs(pp->lib);
+ free((void*)pp->lib);
+ return(1);
+ }
+ }
+ sp = cp+1;
+ ep = 0;
+ }
+ }
+ return(0);
+}
+
+
+Pathcomp_t *path_addpath(Shell_t *shp,Pathcomp_t *first, register const char *path,int type)
+{
+ register const char *cp;
+ Pathcomp_t *old=0;
+ int offset = staktell();
+ char *savptr;
+
+ if(!path && type!=PATH_PATH)
+ return(first);
+ if(type!=PATH_FPATH)
+ {
+ old = first;
+ first = 0;
+ }
+ if(offset)
+ savptr = stakfreeze(0);
+ if(path) while(*(cp=path))
+ {
+ if(*cp==':')
+ {
+ if(type!=PATH_FPATH)
+ first = path_addcomp(shp,first,old,".",type);
+ while(*++path == ':');
+ }
+ else
+ {
+ int c;
+ while(*path && *path!=':')
+ path++;
+ c = *path++;
+ first = path_addcomp(shp,first,old,cp,type);
+ if(c==0)
+ break;
+ if(*path==0)
+ path--;
+ }
+ }
+ if(old)
+ {
+ if(!first && !path)
+ {
+ Pathcomp_t *pp = (Pathcomp_t*)shp->defpathlist;
+ if(!pp)
+ pp = defpath_init(shp);
+ first = path_dup(pp);
+ }
+ if(cp=(sh_scoped(shp,FPATHNOD))->nvalue.cp)
+ first = (void*)path_addpath(shp,(Pathcomp_t*)first,cp,PATH_FPATH);
+ path_delete(old);
+ }
+ if(offset)
+ stakset(savptr,offset);
+ else
+ stakseek(0);
+ return(first);
+}
+
+/*
+ * duplicate the path give by <first> by incremented reference counts
+ */
+Pathcomp_t *path_dup(Pathcomp_t *first)
+{
+ register Pathcomp_t *pp=first;
+ while(pp)
+ {
+ pp->refcount++;
+ pp = pp->next;
+ }
+ return(first);
+}
+
+/*
+ * called whenever the directory is changed
+ */
+void path_newdir(Shell_t *shp,Pathcomp_t *first)
+{
+ register Pathcomp_t *pp=first, *next, *pq;
+ struct stat statb;
+ for(pp=first; pp; pp=pp->next)
+ {
+ pp->flags &= ~PATH_SKIP;
+ if(*pp->name=='/')
+ continue;
+ /* delete .paths component */
+ if((next=pp->next) && (next->flags&PATH_BFPATH))
+ {
+ pp->next = next->next;
+ if(--next->refcount<=0)
+ free((void*)next);
+ }
+ if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode))
+ {
+ pp->dev = 0;
+ pp->ino = 0;
+ continue;
+ }
+ pp->dev = statb.st_dev;
+ pp->ino = statb.st_ino;
+ pp->mtime = statb.st_mtime;
+ for(pq=first;pq!=pp;pq=pq->next)
+ {
+ if(pp->ino==pq->ino && pp->dev==pq->dev)
+ pp->flags |= PATH_SKIP;
+ }
+ for(pq=pp;pq=pq->next;)
+ {
+ if(pp->ino==pq->ino && pp->dev==pq->dev)
+ pq->flags |= PATH_SKIP;
+ }
+ if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH)
+ {
+ /* try to insert .paths component */
+ int offset = staktell();
+ stakputs(pp->name);
+ stakseek(offset);
+ next = pp->next;
+ pp->next = 0;
+ path_chkpaths(shp,first,(Pathcomp_t*)0,pp,offset);
+ if(pp->next)
+ pp = pp->next;
+ pp->next = next;
+ }
+ }
+#if 0
+ path_dump(first);
+#endif
+}
+
+Pathcomp_t *path_unsetfpath(Shell_t *shp)
+{
+ Pathcomp_t *first = (Pathcomp_t*)shp->pathlist;
+ register Pathcomp_t *pp=first, *old=0;
+ if(shp->fpathdict)
+ {
+ struct Ufunction *rp, *rpnext;
+ for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext)
+ {
+ rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp);
+ if(rp->fdict)
+ nv_delete(rp->np,rp->fdict,NV_NOFREE);
+ rp->fdict = 0;
+ }
+ }
+ while(pp)
+ {
+ if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH))
+ {
+ if(pp->flags&PATH_PATH)
+ pp->flags &= ~PATH_FPATH;
+ else
+ {
+ Pathcomp_t *ppsave=pp;
+ if(old)
+ old->next = pp->next;
+ else
+ first = pp->next;
+ pp = pp->next;
+ if(--ppsave->refcount<=0)
+ {
+ if(ppsave->lib)
+ free((void*)ppsave->lib);
+ free((void*)ppsave);
+ }
+ continue;
+ }
+
+ }
+ old = pp;
+ pp = pp->next;
+ }
+ return(first);
+}
+
+Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c)
+{
+ register Pathcomp_t *pp=first;
+ while(pp)
+ {
+ if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c)
+ return(pp);
+ pp = pp->next;
+ }
+ return(0);
+}
+
+/*
+ * get discipline for tracked alias
+ */
+static char *talias_get(Namval_t *np, Namfun_t *nvp)
+{
+ Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
+ char *ptr;
+ if(!pp)
+ return(NULL);
+ path_nextcomp(pp->shp,pp,nv_name(np),pp);
+ ptr = stakfreeze(0);
+ return(ptr+PATH_OFFSET);
+}
+
+static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ if(!val && np->nvalue.cp)
+ {
+ Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
+ if(--pp->refcount<=0)
+ free((void*)pp);
+ }
+ nv_putv(np,val,flags,fp);
+}
+
+static const Namdisc_t talias_disc = { 0, talias_put, talias_get };
+static Namfun_t talias_init = { &talias_disc, 1 };
+
+/*
+ * set tracked alias node <np> to value <pp>
+ */
+void path_alias(register Namval_t *np,register Pathcomp_t *pp)
+{
+ if(pp)
+ {
+ struct stat statb;
+ char *sp;
+ nv_offattr(np,NV_NOPRINT);
+ nv_stack(np,&talias_init);
+ np->nvalue.cp = (char*)pp;
+ pp->refcount++;
+ nv_setattr(np,NV_TAGGED|NV_NOFREE);
+ path_nextcomp(pp->shp,pp,nv_name(np),pp);
+ sp = stakptr(PATH_OFFSET);
+ if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode))
+ nv_setsize(np,statb.st_size+1);
+ else
+ nv_setsize(np,0);
+ }
+ else
+ _nv_unset(np,0);
+}
+
diff --git a/src/cmd/ksh93/sh/pmain.c b/src/cmd/ksh93/sh/pmain.c
new file mode 100644
index 0000000..8742195
--- /dev/null
+++ b/src/cmd/ksh93/sh/pmain.c
@@ -0,0 +1,46 @@
+/***********************************************************************
+* *
+* 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
+
+#include <shell.h>
+
+#include "FEATURE/externs"
+
+#if defined(__sun) && _sys_mman && _lib_memcntl && defined(MHA_MAPSIZE_STACK) && defined(MC_HAT_ADVISE)
+# undef VM_FLAGS /* solaris vs vmalloc.h symbol clash */
+# include <sys/mman.h>
+#else
+# undef _lib_memcntl
+#endif
+
+typedef int (*Shnote_f)(int, long, int);
+
+int main(int argc, char *argv[])
+{
+#if _lib_memcntl
+ /* advise larger stack size */
+ struct memcntl_mha mha;
+ mha.mha_cmd = MHA_MAPSIZE_STACK;
+ mha.mha_flags = 0;
+ mha.mha_pagesize = 64 * 1024;
+ (void)memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0);
+#endif
+ return(sh_main(argc, argv, (Shinit_f)0));
+}
diff --git a/src/cmd/ksh93/sh/shcomp.c b/src/cmd/ksh93/sh/shcomp.c
new file mode 100644
index 0000000..184637b
--- /dev/null
+++ b/src/cmd/ksh93/sh/shcomp.c
@@ -0,0 +1,177 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * David Korn
+ * AT&T Labs
+ *
+ * shell script to shell binary converter
+ *
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: shcomp (AT&T Research) 2003-03-02 $\n]"
+USAGE_LICENSE
+"[+NAME?shcomp - compile a shell script]"
+"[+DESCRIPTION?Unless \b-D\b is specified, \bshcomp\b takes a shell script, "
+ "\ainfile\a, and creates a binary format file, \aoutfile\a, that "
+ "\bksh\b can read and execute with the same effect as the original "
+ "script.]"
+"[+?Since aliases are processed as the script is read, alias definitions "
+ "whose value requires variable expansion will not work correctly.]"
+"[+?If \b-D\b is specified, all double quoted strings that are preceded by "
+ "\b$\b are output. These are the messages that need to be "
+ "translated to locale specific versions for internationalization.]"
+"[+?If \aoutfile\a is omitted, then the results will be written to "
+ "standard output. If \ainfile\a is also omitted, the shell script "
+ "will be read from standard input.]"
+"[D:dictionary?Generate a list of strings that need to be placed in a message "
+ "catalog for internationalization.]"
+"[n:noexec?Displays warning messages for obsolete or non-conforming "
+ "constructs.] "
+"[v:verbose?Displays input from \ainfile\a onto standard error as it "
+ "reads it.]"
+"\n"
+"\n[infile [outfile]]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Successful completion.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bksh\b(1)]"
+;
+
+#include <shell.h>
+#include "defs.h"
+#include "shnodes.h"
+#include "sys/stat.h"
+
+#define CNTL(x) ((x)&037)
+#define VERSION 3
+static const char header[6] = { CNTL('k'),CNTL('s'),CNTL('h'),0,VERSION,0 };
+
+int main(int argc, char *argv[])
+{
+ Sfio_t *in, *out;
+ Shell_t *shp;
+ Namval_t *np;
+ Shnode_t *t;
+ char *cp;
+ int n, nflag=0, vflag=0, dflag=0;
+ error_info.id = argv[0];
+ while(n = optget(argv, usage )) switch(n)
+ {
+ case 'D':
+ dflag=1;
+ break;
+ case 'v':
+ vflag=1;
+ break;
+ case 'n':
+ nflag=1;
+ break;
+ case ':':
+ errormsg(SH_DICT,2,"%s",opt_info.arg);
+ break;
+ case '?':
+ errormsg(SH_DICT,ERROR_usage(2),"%s",opt_info.arg);
+ break;
+ }
+ shp = sh_init(argc,argv,(Shinit_f)0);
+ shp->shcomp = 1;
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if(error_info.errors || argc>2)
+ errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
+ if(cp= *argv)
+ {
+ argv++;
+ in = sh_pathopen(cp);
+ }
+ else
+ in = sfstdin;
+ if(cp= *argv)
+ {
+ struct stat statb;
+ if(!(out = sfopen((Sfio_t*)0,cp,"w")))
+ errormsg(SH_DICT,ERROR_system(1),"%s: cannot create",cp);
+ if(fstat(sffileno(out),&statb) >=0)
+ chmod(cp,(statb.st_mode&~S_IFMT)|S_IXUSR|S_IXGRP|S_IXOTH);
+ }
+ else
+ out = sfstdout;
+ if(dflag)
+ {
+ sh_onoption(SH_DICTIONARY);
+ sh_onoption(SH_NOEXEC);
+ }
+ if(nflag)
+ sh_onoption(SH_NOEXEC);
+ if(vflag)
+ sh_onoption(SH_VERBOSE);
+ if(!dflag)
+ sfwrite(out,header,sizeof(header));
+ shp->inlineno = 1;
+#if SHOPT_BRACEPAT
+ sh_onoption(SH_BRACEEXPAND);
+#endif
+ while(1)
+ {
+ stakset((char*)0,0);
+ if(t = (Shnode_t*)sh_parse(shp,in,0))
+ {
+ if((t->tre.tretyp&(COMMSK|COMSCAN))==0 && t->com.comnamp && strcmp(nv_name((Namval_t*)t->com.comnamp),"alias")==0)
+ sh_exec(t,0);
+ if(!dflag && sh_tdump(out,t) < 0)
+ errormsg(SH_DICT,ERROR_exit(1),"dump failed");
+ }
+ else if(sfeof(in))
+ break;
+ if(sferror(in))
+ errormsg(SH_DICT,ERROR_system(1),"I/O error");
+ if(t && ((t->tre.tretyp&COMMSK)==TCOM) && (np=t->com.comnamp) && (cp=nv_name(np)))
+ {
+ if(strcmp(cp,"exit")==0)
+ break;
+ /* check for exec of a command */
+ if(strcmp(cp,"exec")==0)
+ {
+ if(t->com.comtyp&COMSCAN)
+ {
+ if(t->com.comarg->argnxt.ap)
+ break;
+ }
+ else
+ {
+ struct dolnod *ap = (struct dolnod*)t->com.comarg;
+ if(ap->dolnum>1)
+ break;
+ }
+ }
+ }
+ }
+ /* copy any remaining input */
+ sfmove(in,out,SF_UNBOUND,-1);
+ if(in!=sfstdin)
+ sfclose(in);
+ if(out!=sfstdout)
+ sfclose(out);
+ return(0);
+}
diff --git a/src/cmd/ksh93/sh/streval.c b/src/cmd/ksh93/sh/streval.c
new file mode 100644
index 0000000..d13e0f8
--- /dev/null
+++ b/src/cmd/ksh93/sh/streval.c
@@ -0,0 +1,1023 @@
+/***********************************************************************
+* *
+* 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
+
+/*
+ * D. G. Korn
+ * AT&T Labs
+ *
+ * arithmetic expression evaluator
+ *
+ * this version compiles the expression onto a stack
+ * and has a separate executor
+ */
+
+#include "streval.h"
+#include <ctype.h>
+#include <error.h>
+#include <stak.h>
+#include "FEATURE/externs"
+#include "defs.h" /* for sh.decomma */
+
+#ifndef ERROR_dictionary
+# define ERROR_dictionary(s) (s)
+#endif
+#ifndef SH_DICT
+# define SH_DICT "libshell"
+#endif
+
+#define MAXLEVEL 9
+#define SMALL_STACK 12
+
+/*
+ * The following are used with tokenbits() macro
+ */
+#define T_OP 0x3f /* mask for operator number */
+#define T_BINARY 0x40 /* binary operators */
+#define T_NOFLOAT 0x80 /* non floating point operator */
+#define A_LVALUE (2*MAXPREC+2)
+
+#define pow2size(x) ((x)<=2?2:(x)<=4?4:(x)<=8?8:(x)<=16?16:(x)<=32?32:64)
+#define round(x,size) (((x)+(size)-1)&~((size)-1))
+#define stakpush(v,val,type) ((((v)->offset=round(staktell(),pow2size(sizeof(type)))),\
+ stakseek((v)->offset+sizeof(type)), \
+ *((type*)stakptr((v)->offset)) = (val)),(v)->offset)
+#define roundptr(ep,cp,type) (((unsigned char*)(ep))+round(cp-((unsigned char*)(ep)),pow2size(sizeof(type))))
+
+static int level;
+
+struct vars /* vars stacked per invocation */
+{
+ Shell_t *shp;
+ const char *expr; /* current expression */
+ const char *nextchr; /* next char in current expression */
+ const char *errchr; /* next char after error */
+ const char *errstr; /* error string */
+ struct lval errmsg; /* error message text */
+ int offset; /* offset for pushchr macro */
+ int staksize; /* current stack size needed */
+ int stakmaxsize; /* maximum stack size needed */
+ unsigned char paren; /* parenthesis level */
+ char infun; /* incremented by comma inside function */
+ int emode;
+ Sfdouble_t (*convert)(const char**,struct lval*,int,Sfdouble_t);
+};
+
+typedef Sfdouble_t (*Math_f)(Sfdouble_t,...);
+typedef Sfdouble_t (*Math_1f_f)(Sfdouble_t);
+typedef int (*Math_1i_f)(Sfdouble_t);
+typedef Sfdouble_t (*Math_2f_f)(Sfdouble_t,Sfdouble_t);
+typedef Sfdouble_t (*Math_2f_i)(Sfdouble_t,int);
+typedef int (*Math_2i_f)(Sfdouble_t,Sfdouble_t);
+typedef Sfdouble_t (*Math_3f_f)(Sfdouble_t,Sfdouble_t,Sfdouble_t);
+typedef int (*Math_3i_f)(Sfdouble_t,Sfdouble_t,Sfdouble_t);
+
+#define getchr(vp) (*(vp)->nextchr++)
+#define peekchr(vp) (*(vp)->nextchr)
+#define ungetchr(vp) ((vp)->nextchr--)
+
+#if ('a'==97) /* ASCII encodings */
+# define getop(c) (((c) >= sizeof(strval_states))? \
+ ((c)=='|'?A_OR:((c)=='^'?A_XOR:((c)=='~'?A_TILDE:A_REG))):\
+ strval_states[(c)])
+#else
+# define getop(c) (isdigit(c)?A_DIG:((c==' '||c=='\t'||c=='\n'||c=='"')?0: \
+ (c=='<'?A_LT:(c=='>'?A_GT:(c=='='?A_ASSIGN: \
+ (c=='+'?A_PLUS:(c=='-'?A_MINUS:(c=='*'?A_TIMES: \
+ (c=='/'?A_DIV:(c=='%'?A_MOD:(c==','?A_COMMA: \
+ (c=='&'?A_AND:(c=='!'?A_NOT:(c=='('?A_LPAR: \
+ (c==')'?A_RPAR:(c==0?A_EOF:(c==':'?A_COLON: \
+ (c=='?'?A_QUEST:(c=='|'?A_OR:(c=='^'?A_XOR: \
+ (c=='\''?A_LIT: \
+ (c=='.'?A_DOT:(c=='~'?A_TILDE:A_REG)))))))))))))))))))))))
+#endif
+
+#define seterror(v,msg) _seterror(v,ERROR_dictionary(msg))
+#define ERROR(vp,msg) return(seterror((vp),msg))
+
+/*
+ * set error message string and return(0)
+ */
+static int _seterror(struct vars *vp,const char *msg)
+{
+ if(!vp->errmsg.value)
+ vp->errmsg.value = (char*)msg;
+ vp->errchr = vp->nextchr;
+ vp->nextchr = "";
+ level = 0;
+ return(0);
+}
+
+
+static void arith_error(const char *message,const char *expr, int mode)
+{
+ level = 0;
+ mode = (mode&3)!=0;
+ errormsg(SH_DICT,ERROR_exit(mode),message,expr);
+}
+
+#if _ast_no_um2fm
+static Sfdouble_t U2F(Sfulong_t u)
+{
+ Sflong_t s = u;
+ Sfdouble_t f;
+
+ if (s >= 0)
+ return s;
+ s = u / 2;
+ f = s;
+ f *= 2;
+ if (u & 1)
+ f++;
+ return f;
+}
+#else
+#define U2F(x) x
+#endif
+
+Sfdouble_t arith_exec(Arith_t *ep)
+{
+ register Sfdouble_t num=0,*dp,*sp;
+ register unsigned char *cp = ep->code;
+ register int c,type=0;
+ register char *tp;
+ Sfdouble_t small_stack[SMALL_STACK+1],arg[9];
+ const char *ptr = "";
+ char *lastval=0;
+ int lastsub;
+ Math_f fun;
+ struct lval node;
+ Shell_t *shp = ep->shp;
+ node.shp = shp;
+ node.emode = ep->emode;
+ node.expr = ep->expr;
+ node.elen = ep->elen;
+ node.value = 0;
+ node.nosub = 0;
+ node.ptr = 0;
+ node.eflag = 0;
+ if(level++ >=MAXLEVEL)
+ {
+ arith_error(e_recursive,ep->expr,ep->emode);
+ return(0);
+ }
+ if(ep->staksize < SMALL_STACK)
+ sp = small_stack;
+ else
+ sp = (Sfdouble_t*)stakalloc(ep->staksize*(sizeof(Sfdouble_t)+1));
+ tp = (char*)(sp+ep->staksize);
+ tp--,sp--;
+ while(c = *cp++)
+ {
+ if(c&T_NOFLOAT)
+ {
+ if(type==1 || ((c&T_BINARY) && (c&T_OP)!=A_MOD && tp[-1]==1))
+ arith_error(e_incompatible,ep->expr,ep->emode);
+ }
+ switch(c&T_OP)
+ {
+ case A_JMP: case A_JMPZ: case A_JMPNZ:
+ c &= T_OP;
+ cp = roundptr(ep,cp,short);
+ if((c==A_JMPZ && num) || (c==A_JMPNZ &&!num))
+ cp += sizeof(short);
+ else
+ cp = (unsigned char*)ep + *((short*)cp);
+ continue;
+ case A_NOTNOT:
+ num = (num!=0);
+ type=0;
+ break;
+ case A_PLUSPLUS:
+ node.nosub = -1;
+ (*ep->fun)(&ptr,&node,ASSIGN,num+1);
+ break;
+ case A_MINUSMINUS:
+ node.nosub = -1;
+ (*ep->fun)(&ptr,&node,ASSIGN,num-1);
+ break;
+ case A_INCR:
+ num = num+1;
+ node.nosub = -1;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,num);
+ break;
+ case A_DECR:
+ num = num-1;
+ node.nosub = -1;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,num);
+ break;
+ case A_SWAP:
+ num = sp[-1];
+ sp[-1] = *sp;
+ type = tp[-1];
+ tp[-1] = *tp;
+ break;
+ case A_POP:
+ sp--;
+ continue;
+ case A_ASSIGNOP1:
+ node.emode |= ARITH_ASSIGNOP;
+ case A_PUSHV:
+ cp = roundptr(ep,cp,Sfdouble_t*);
+ dp = *((Sfdouble_t**)cp);
+ cp += sizeof(Sfdouble_t*);
+ c = *(short*)cp;
+ cp += sizeof(short);
+ lastval = node.value = (char*)dp;
+ if(node.flag = c)
+ lastval = 0;
+ node.isfloat=0;
+ node.level = level;
+ node.nosub = 0;
+ num = (*ep->fun)(&ptr,&node,VALUE,num);
+ if(node.emode&ARITH_ASSIGNOP)
+ {
+ lastsub = node.nosub;
+ node.nosub = 0;
+ node.emode &= ~ARITH_ASSIGNOP;
+ }
+ if(node.value != (char*)dp)
+ arith_error(node.value,ptr,ep->emode);
+ *++sp = num;
+ type = node.isfloat;
+ if(num > LDBL_ULLONG_MAX || num < LDBL_LLONG_MIN)
+ type = 1;
+ else
+ {
+ Sfdouble_t d=num;
+ if(num > LDBL_LLONG_MAX && num <= LDBL_ULLONG_MAX)
+ {
+ type = 2;
+ d -= LDBL_LLONG_MAX;
+ }
+ if((Sflong_t)d!=d)
+ type = 1;
+ }
+ *++tp = type;
+ c = 0;
+ break;
+ case A_ENUM:
+ node.eflag = 1;
+ continue;
+ case A_ASSIGNOP:
+ node.nosub = lastsub;
+ case A_STORE:
+ cp = roundptr(ep,cp,Sfdouble_t*);
+ dp = *((Sfdouble_t**)cp);
+ cp += sizeof(Sfdouble_t*);
+ c = *(short*)cp;
+ if(c<0)
+ c = 0;
+ cp += sizeof(short);
+ node.value = (char*)dp;
+ node.flag = c;
+ if(lastval)
+ node.eflag = 1;
+ node.ptr = 0;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,num);
+ if(lastval && node.ptr)
+ {
+ Sfdouble_t r;
+ node.flag = 0;
+ node.value = lastval;
+ r = (*ep->fun)(&ptr,&node,VALUE,num);
+ if(r!=num)
+ {
+ node.flag=c;
+ node.value = (char*)dp;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,r);
+ }
+
+ }
+ lastval = 0;
+ c=0;
+ break;
+ case A_PUSHF:
+ cp = roundptr(ep,cp,Math_f);
+ *++sp = (Sfdouble_t)(cp-ep->code);
+ cp += sizeof(Math_f);
+ *++tp = *cp++;
+ continue;
+ case A_PUSHN:
+ cp = roundptr(ep,cp,Sfdouble_t);
+ num = *((Sfdouble_t*)cp);
+ cp += sizeof(Sfdouble_t);
+ *++sp = num;
+ *++tp = type = *cp++;
+ break;
+ case A_NOT:
+ type=0;
+ num = !num;
+ break;
+ case A_UMINUS:
+ num = -num;
+ break;
+ case A_TILDE:
+ num = ~((Sflong_t)(num));
+ break;
+ case A_PLUS:
+ num += sp[-1];
+ break;
+ case A_MINUS:
+ num = sp[-1] - num;
+ break;
+ case A_TIMES:
+ num *= sp[-1];
+ break;
+ case A_POW:
+ num = pow(sp[-1],num);
+ break;
+ case A_MOD:
+ if(!(Sflong_t)num)
+ arith_error(e_divzero,ep->expr,ep->emode);
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) % (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) % (Sflong_t)(num);
+ break;
+ case A_DIV:
+ if(type==1 || tp[-1]==1)
+ {
+ num = sp[-1]/num;
+ type = 1;
+ }
+ else if((Sfulong_t)(num)==0)
+ arith_error(e_divzero,ep->expr,ep->emode);
+ else if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) / (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) / (Sflong_t)(num);
+ break;
+ case A_LSHIFT:
+ if(tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) << (long)(num));
+ else
+ num = (Sflong_t)(sp[-1]) << (long)(num);
+ break;
+ case A_RSHIFT:
+ if(tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) >> (long)(num));
+ else
+ num = (Sflong_t)(sp[-1]) >> (long)(num);
+ break;
+ case A_XOR:
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) ^ (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) ^ (Sflong_t)(num);
+ break;
+ case A_OR:
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) | (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) | (Sflong_t)(num);
+ break;
+ case A_AND:
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) & (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) & (Sflong_t)(num);
+ break;
+ case A_EQ:
+ num = (sp[-1]==num);
+ type=0;
+ break;
+ case A_NEQ:
+ num = (sp[-1]!=num);
+ type=0;
+ break;
+ case A_LE:
+ num = (sp[-1]<=num);
+ type=0;
+ break;
+ case A_GE:
+ num = (sp[-1]>=num);
+ type=0;
+ break;
+ case A_GT:
+ num = (sp[-1]>num);
+ type=0;
+ break;
+ case A_LT:
+ num = (sp[-1]<num);
+ type=0;
+ break;
+ case A_CALL1F:
+ sp--,tp--;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ if(c&T_BINARY)
+ {
+ c &= ~T_BINARY;
+ arg[0] = num;
+ arg[1] = 0;
+ num = sh_mathfun(shp,(void*)fun,1,arg);
+ break;
+ }
+ num = (*((Math_1f_f)fun))(num);
+ break;
+ case A_CALL1I:
+ sp--,tp--;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ num = (*((Math_1i_f)fun))(num);
+ break;
+ case A_CALL2F:
+ sp-=2,tp-=2;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ if(c&T_BINARY)
+ {
+ c &= ~T_BINARY;
+ arg[0] = sp[1];
+ arg[1] = num;
+ arg[2] = 0;
+ num = sh_mathfun(shp,(void*)fun,2,arg);
+ break;
+ }
+ if(c&T_NOFLOAT)
+ num = (*((Math_2f_i)fun))(sp[1],(int)num);
+ else
+ num = (*((Math_2f_f)fun))(sp[1],num);
+ break;
+ case A_CALL2I:
+ sp-=2,tp-=2;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ num = (*((Math_2i_f)fun))(sp[1],num);
+ break;
+ case A_CALL3F:
+ sp-=3,tp-=3;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ if(c&T_BINARY)
+ {
+ c &= ~T_BINARY;
+ arg[0] = sp[1];
+ arg[1] = sp[2];
+ arg[2] = num;
+ arg[3] = 0;
+ num = sh_mathfun(shp,(void*)fun,3,arg);
+ break;
+ }
+ num = (*((Math_3f_f)fun))(sp[1],sp[2],num);
+ break;
+ }
+ if(c)
+ lastval = 0;
+ if(c&T_BINARY)
+ {
+ node.ptr = 0;
+ sp--,tp--;
+ type |= (*tp!=0);
+ }
+ *sp = num;
+ *tp = type;
+ }
+ if(level>0)
+ level--;
+ return(num);
+}
+
+/*
+ * This returns operator tokens or A_REG or A_NUM
+ */
+static int gettok(register struct vars *vp)
+{
+ register int c,op;
+ vp->errchr = vp->nextchr;
+ while(1)
+ {
+ c = getchr(vp);
+ switch(op=getop(c))
+ {
+ case 0:
+ vp->errchr = vp->nextchr;
+ continue;
+ case A_EOF:
+ vp->nextchr--;
+ break;
+ case A_COMMA:
+ if(vp->shp->decomma && (c=peekchr(vp))>='0' && c<='9')
+ {
+ op = A_DIG;
+ goto keep;
+ }
+ break;
+ case A_DOT:
+ if((c=peekchr(vp))>='0' && c<='9')
+ op = A_DIG;
+ else
+ op = A_REG;
+ /*FALL THRU*/
+ case A_DIG: case A_REG: case A_LIT:
+ keep:
+ ungetchr(vp);
+ break;
+ case A_QUEST:
+ if(peekchr(vp)==':')
+ {
+ getchr(vp);
+ op = A_QCOLON;
+ }
+ break;
+ case A_LT: case A_GT:
+ if(peekchr(vp)==c)
+ {
+ getchr(vp);
+ op -= 2;
+ break;
+ }
+ /* FALL THRU */
+ case A_NOT: case A_COLON:
+ c = '=';
+ /* FALL THRU */
+ case A_ASSIGN:
+ case A_TIMES:
+ case A_PLUS: case A_MINUS:
+ case A_OR: case A_AND:
+ if(peekchr(vp)==c)
+ {
+ getchr(vp);
+ op--;
+ }
+ }
+ return(op);
+ }
+}
+
+/*
+ * evaluate a subexpression with precedence
+ */
+
+static int expr(register struct vars *vp,register int precedence)
+{
+ register int c, op;
+ int invalid,wasop=0;
+ struct lval lvalue,assignop;
+ const char *pos;
+ Sfdouble_t d;
+
+ lvalue.value = 0;
+ lvalue.nargs = 0;
+ lvalue.fun = 0;
+ lvalue.shp = vp->shp;
+again:
+ op = gettok(vp);
+ c = 2*MAXPREC+1;
+ switch(op)
+ {
+ case A_PLUS:
+ goto again;
+ case A_EOF:
+ if(precedence>2)
+ ERROR(vp,e_moretokens);
+ return(1);
+ case A_MINUS:
+ op = A_UMINUS;
+ goto common;
+ case A_NOT:
+ goto common;
+ case A_MINUSMINUS:
+ c = A_LVALUE;
+ op = A_DECR|T_NOFLOAT;
+ goto common;
+ case A_PLUSPLUS:
+ c = A_LVALUE;
+ op = A_INCR|T_NOFLOAT;
+ /* FALL THRU */
+ case A_TILDE:
+ op |= T_NOFLOAT;
+ common:
+ if(!expr(vp,c))
+ return(0);
+ stakputc(op);
+ break;
+ default:
+ vp->nextchr = vp->errchr;
+ wasop = 1;
+ }
+ invalid = wasop;
+ while(1)
+ {
+ assignop.value = 0;
+ op = gettok(vp);
+ if(op==A_DIG || op==A_REG || op==A_LIT)
+ {
+ if(!wasop)
+ ERROR(vp,e_synbad);
+ goto number;
+ }
+ if(wasop++ && op!=A_LPAR)
+ ERROR(vp,e_synbad);
+ /* check for assignment operation */
+ if(peekchr(vp)== '=' && !(strval_precedence[op]&NOASSIGN))
+ {
+ if((!lvalue.value || precedence > 3))
+ ERROR(vp,e_notlvalue);
+ if(precedence==3)
+ precedence = 2;
+ assignop = lvalue;
+ getchr(vp);
+ c = 3;
+ }
+ else
+ {
+ c = (strval_precedence[op]&PRECMASK);
+ if(c==MAXPREC || op==A_POW)
+ c++;
+ c *= 2;
+ }
+ /* from here on c is the new precedence level */
+ if(lvalue.value && (op!=A_ASSIGN))
+ {
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ if(op==A_EQ || op==A_NEQ)
+ stakputc(A_ENUM);
+ stakputc(assignop.value?A_ASSIGNOP1:A_PUSHV);
+ stakpush(vp,lvalue.value,char*);
+ if(lvalue.flag<0)
+ lvalue.flag = 0;
+ stakpush(vp,lvalue.flag,short);
+ if(vp->nextchr==0)
+ ERROR(vp,e_badnum);
+ if(!(strval_precedence[op]&SEQPOINT))
+ lvalue.value = 0;
+ invalid = 0;
+ }
+ else if(precedence==A_LVALUE)
+ ERROR(vp,e_notlvalue);
+ if(invalid && op>A_ASSIGN)
+ ERROR(vp,e_synbad);
+ if(precedence >= c)
+ goto done;
+ if(strval_precedence[op]&RASSOC)
+ c--;
+ if((c < (2*MAXPREC+1)) && !(strval_precedence[op]&SEQPOINT))
+ {
+ wasop = 0;
+ if(!expr(vp,c))
+ return(0);
+ }
+ switch(op)
+ {
+ case A_RPAR:
+ if(!vp->paren)
+ ERROR(vp,e_paren);
+ if(invalid)
+ ERROR(vp,e_synbad);
+ goto done;
+
+ case A_COMMA:
+ wasop = 0;
+ if(vp->infun)
+ vp->infun++;
+ else
+ {
+ stakputc(A_POP);
+ vp->staksize--;
+ }
+ if(!expr(vp,c))
+ {
+ stakseek(staktell()-1);
+ return(0);
+ }
+ lvalue.value = 0;
+ break;
+
+ case A_LPAR:
+ {
+ int infun = vp->infun;
+ int userfun=0;
+ Sfdouble_t (*fun)(Sfdouble_t,...);
+ int nargs = lvalue.nargs;
+ if(nargs<0)
+ nargs = -nargs;
+ fun = lvalue.fun;
+ lvalue.fun = 0;
+ if(fun)
+ {
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ vp->infun=1;
+ if((int)lvalue.nargs<0)
+ userfun = T_BINARY;
+ else if((int)lvalue.nargs&040)
+ userfun = T_NOFLOAT;
+ stakputc(A_PUSHF);
+ stakpush(vp,fun,Math_f);
+ stakputc(1);
+ }
+ else
+ vp->infun = 0;
+ if(!invalid)
+ ERROR(vp,e_synbad);
+ vp->paren++;
+ if(!expr(vp,1))
+ return(0);
+ vp->paren--;
+ if(fun)
+ {
+ int x= (nargs&010)?2:-1;
+ nargs &= 7;
+ if(vp->infun != nargs)
+ ERROR(vp,e_argcount);
+ if((vp->staksize+=nargs)>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize+nargs;
+ stakputc(A_CALL1F+userfun+nargs+x);
+ vp->staksize -= nargs;
+ }
+ vp->infun = infun;
+ if (gettok(vp) != A_RPAR)
+ ERROR(vp,e_paren);
+ wasop = 0;
+ break;
+ }
+
+ case A_PLUSPLUS:
+ case A_MINUSMINUS:
+ wasop=0;
+ op |= T_NOFLOAT;
+ case A_ASSIGN:
+ if(!lvalue.value)
+ ERROR(vp,e_notlvalue);
+ if(op==A_ASSIGN)
+ {
+ stakputc(A_STORE);
+ stakpush(vp,lvalue.value,char*);
+ stakpush(vp,lvalue.flag,short);
+ vp->staksize--;
+ }
+ else
+ stakputc(op);
+ lvalue.value = 0;
+ break;
+
+ case A_QUEST:
+ {
+ int offset1,offset2;
+ stakputc(A_JMPZ);
+ offset1 = stakpush(vp,0,short);
+ stakputc(A_POP);
+ if(!expr(vp,1))
+ return(0);
+ if(gettok(vp)!=A_COLON)
+ ERROR(vp,e_questcolon);
+ stakputc(A_JMP);
+ offset2 = stakpush(vp,0,short);
+ *((short*)stakptr(offset1)) = staktell();
+ stakputc(A_POP);
+ if(!expr(vp,3))
+ return(0);
+ *((short*)stakptr(offset2)) = staktell();
+ lvalue.value = 0;
+ wasop = 0;
+ break;
+ }
+
+ case A_COLON:
+ ERROR(vp,e_badcolon);
+ break;
+
+ case A_QCOLON:
+ case A_ANDAND:
+ case A_OROR:
+ {
+ int offset;
+ if(op==A_ANDAND)
+ op = A_JMPZ;
+ else
+ op = A_JMPNZ;
+ stakputc(op);
+ offset = stakpush(vp,0,short);
+ stakputc(A_POP);
+ if(!expr(vp,c))
+ return(0);
+ *((short*)stakptr(offset)) = staktell();
+ if(op!=A_QCOLON)
+ stakputc(A_NOTNOT);
+ lvalue.value = 0;
+ wasop=0;
+ break;
+ }
+ case A_AND: case A_OR: case A_XOR: case A_LSHIFT:
+ case A_RSHIFT: case A_MOD:
+ op |= T_NOFLOAT;
+ /* FALL THRU */
+ case A_PLUS: case A_MINUS: case A_TIMES: case A_DIV:
+ case A_EQ: case A_NEQ: case A_LT: case A_LE:
+ case A_GT: case A_GE: case A_POW:
+ stakputc(op|T_BINARY);
+ vp->staksize--;
+ break;
+ case A_NOT: case A_TILDE:
+ default:
+ ERROR(vp,e_synbad);
+ number:
+ wasop = 0;
+ if(*vp->nextchr=='L' && vp->nextchr[1]=='\'')
+ {
+ vp->nextchr++;
+ op = A_LIT;
+ }
+ pos = vp->nextchr;
+ lvalue.isfloat = 0;
+ lvalue.expr = vp->expr;
+ lvalue.emode = vp->emode;
+ if(op==A_LIT)
+ {
+ /* character constants */
+ if(pos[1]=='\\' && pos[2]=='\'' && pos[3]!='\'')
+ {
+ d = '\\';
+ vp->nextchr +=2;
+ }
+ else
+ d = chresc(pos+1,(char**)&vp->nextchr);
+ /* posix allows the trailing ' to be optional */
+ if(*vp->nextchr=='\'')
+ vp->nextchr++;
+ }
+ else
+ d = (*vp->convert)(&vp->nextchr, &lvalue, LOOKUP, 0);
+ if (vp->nextchr == pos)
+ {
+ if(vp->errmsg.value = lvalue.value)
+ vp->errstr = pos;
+ ERROR(vp,op==A_LIT?e_charconst:e_synbad);
+ }
+ if(op==A_DIG || op==A_LIT)
+ {
+ stakputc(A_PUSHN);
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ stakpush(vp,d,Sfdouble_t);
+ stakputc(lvalue.isfloat);
+ }
+
+ /* check for function call */
+ if(lvalue.fun)
+ continue;
+ break;
+ }
+ invalid = 0;
+ if(assignop.value)
+ {
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ if(assignop.flag<0)
+ assignop.flag = 0;
+ stakputc(c&1?A_ASSIGNOP:A_STORE);
+ stakpush(vp,assignop.value,char*);
+ stakpush(vp,assignop.flag,short);
+ }
+ }
+ done:
+ vp->nextchr = vp->errchr;
+ return(1);
+}
+
+Arith_t *arith_compile(Shell_t *shp,const char *string,char **last,Sfdouble_t(*fun)(const char**,struct lval*,int,Sfdouble_t),int emode)
+{
+ struct vars cur;
+ register Arith_t *ep;
+ int offset;
+ memset((void*)&cur,0,sizeof(cur));
+ cur.shp = shp;
+ cur.expr = cur.nextchr = string;
+ cur.convert = fun;
+ cur.emode = emode;
+ cur.errmsg.value = 0;
+ cur.errmsg.emode = emode;
+ stakseek(sizeof(Arith_t));
+ if(!expr(&cur,0) && cur.errmsg.value)
+ {
+ if(cur.errstr)
+ string = cur.errstr;
+ if((*fun)( &string , &cur.errmsg, MESSAGE, 0) < 0)
+ {
+ stakseek(0);
+ *last = (char*)Empty;
+ return(0);
+ }
+ cur.nextchr = cur.errchr;
+ }
+ stakputc(0);
+ offset = staktell();
+ ep = (Arith_t*)stakfreeze(0);
+ ep->shp = shp;
+ ep->expr = string;
+ ep->elen = strlen(string);
+ ep->code = (unsigned char*)(ep+1);
+ ep->fun = fun;
+ ep->emode = emode;
+ ep->size = offset - sizeof(Arith_t);
+ ep->staksize = cur.stakmaxsize+1;
+ if(last)
+ *last = (char*)(cur.nextchr);
+ return(ep);
+}
+
+/*
+ * evaluate an integer arithmetic expression in s
+ *
+ * (Sfdouble_t)(*convert)(char** end, struct lval* string, int type, Sfdouble_t value)
+ * is a user supplied conversion routine that is called when unknown
+ * chars are encountered.
+ * *end points to the part to be converted and must be adjusted by convert to
+ * point to the next non-converted character; if typ is MESSAGE then string
+ * points to an error message string
+ *
+ * NOTE: (*convert)() may call strval()
+ */
+
+Sfdouble_t strval(Shell_t *shp,const char *s,char **end,Sfdouble_t(*conv)(const char**,struct lval*,int,Sfdouble_t),int emode)
+{
+ Arith_t *ep;
+ Sfdouble_t d;
+ char *sp=0;
+ int offset;
+ if(offset=staktell())
+ sp = stakfreeze(1);
+ ep = arith_compile(shp,s,end,conv,emode);
+ ep->emode = emode;
+ d = arith_exec(ep);
+ stakset(sp?sp:(char*)ep,offset);
+ return(d);
+}
+
+#if _mem_name__exception
+#undef _mem_name_exception
+#define _mem_name_exception 1
+#undef exception
+#define exception _exception
+#undef matherr
+#endif
+
+#if _mem_name_exception
+
+#undef error
+
+#if _BLD_shell && defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#ifndef DOMAIN
+#define DOMAIN _DOMAIN
+#endif
+#ifndef OVERFLOW
+#define OVERFLOW _OVERFLOW
+#endif
+#ifndef SING
+#define SING _SING
+#endif
+
+ extern int matherr(struct exception *ep)
+ {
+ const char *message;
+ switch(ep->type)
+ {
+#ifdef DOMAIN
+ case DOMAIN:
+ message = ERROR_dictionary(e_domain);
+ break;
+#endif
+#ifdef OVERFLOW
+ case OVERFLOW:
+ message = ERROR_dictionary(e_overflow);
+ break;
+#endif
+#ifdef SING
+ case SING:
+ message = ERROR_dictionary(e_singularity);
+ break;
+#endif
+ default:
+ return(1);
+ }
+ level=0;
+ errormsg(SH_DICT,ERROR_exit(1),message,ep->name);
+ return(0);
+ }
+
+#undef extern
+
+#endif /* _mem_name_exception */
diff --git a/src/cmd/ksh93/sh/string.c b/src/cmd/ksh93/sh/string.c
new file mode 100644
index 0000000..2765e8d
--- /dev/null
+++ b/src/cmd/ksh93/sh/string.c
@@ -0,0 +1,730 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * string processing routines for Korn shell
+ *
+ */
+
+#include <ast.h>
+#include <ast_wchar.h>
+#include "defs.h"
+#include <stak.h>
+#include <ccode.h>
+#include "shtable.h"
+#include "lexstates.h"
+#include "national.h"
+
+#if _hdr_wctype
+# include <wctype.h>
+#endif
+
+#if !_lib_iswprint && !defined(iswprint)
+# define iswprint(c) (((c)&~0377) || isprint(c))
+#endif
+
+
+/*
+ * Table lookup routine
+ * <table> is searched for string <sp> and corresponding value is returned
+ * This is only used for small tables and is used to save non-sharable memory
+ */
+
+const Shtable_t *sh_locate(register const char *sp,const Shtable_t *table,int size)
+{
+ register int first;
+ register const Shtable_t *tp;
+ register int c;
+ static const Shtable_t empty = {0,0};
+ if(sp==0 || (first= *sp)==0)
+ return(&empty);
+ tp=table;
+ while((c= *tp->sh_name) && (CC_NATIVE!=CC_ASCII || c <= first))
+ {
+ if(first == c && strcmp(sp,tp->sh_name)==0)
+ return(tp);
+ tp = (Shtable_t*)((char*)tp+size);
+ }
+ return(&empty);
+}
+
+/*
+ * shtab_options lookup routine
+ */
+
+#define sep(c) ((c)=='-'||(c)=='_')
+
+int sh_lookopt(register const char *sp, int *invert)
+{
+ register int first;
+ register const Shtable_t *tp;
+ register int c;
+ register const char *s, *t, *sw, *tw;
+ int amb;
+ int hit;
+ int inv;
+ int no;
+ if(sp==0)
+ return(0);
+ if(*sp=='n' && *(sp+1)=='o' && (*(sp+2)!='t' || *(sp+3)!='i'))
+ {
+ sp+=2;
+ if(sep(*sp))
+ sp++;
+ *invert = !*invert;
+ }
+ if((first= *sp)==0)
+ return(0);
+ tp=shtab_options;
+ amb=hit=0;
+ for(;;)
+ {
+ t=tp->sh_name;
+ if(no = *t=='n' && *(t+1)=='o' && *(t+2)!='t')
+ t+=2;
+ if(!(c= *t))
+ break;
+ if(first == c)
+ {
+ if(strcmp(sp,t)==0)
+ {
+ *invert ^= no;
+ return(tp->sh_number);
+ }
+ s=sw=sp;
+ tw=t;
+ for(;;)
+ {
+ if(!*s || *s=='=')
+ {
+ if (*s == '=' && !strtol(s+1, NiL, 0))
+ no = !no;
+ if (!*t)
+ {
+ *invert ^= no;
+ return(tp->sh_number);
+ }
+ if (hit || amb)
+ {
+ hit = 0;
+ amb = 1;
+ }
+ else
+ {
+ hit = tp->sh_number;
+ inv = no;
+ }
+ break;
+ }
+ else if(!*t)
+ break;
+ else if(sep(*s))
+ sw = ++s;
+ else if(sep(*t))
+ tw = ++t;
+ else if(*s==*t)
+ {
+ s++;
+ t++;
+ }
+ else if(s==sw && t==tw)
+ break;
+ else
+ {
+ if(t!=tw)
+ {
+ while(*t && !sep(*t))
+ t++;
+ if(!*t)
+ break;
+ tw = ++t;
+ }
+ while (s>sw && *s!=*t)
+ s--;
+ }
+ }
+ }
+ tp = (Shtable_t*)((char*)tp+sizeof(*shtab_options));
+ }
+ if(hit)
+ *invert ^= inv;
+ return(hit);
+}
+
+/*
+ * look for the substring <oldsp> in <string> and replace with <newsp>
+ * The new string is put on top of the stack
+ */
+char *sh_substitute(const char *string,const char *oldsp,char *newsp)
+/*@
+ assume string!=NULL && oldsp!=NULL && newsp!=NULL;
+ return x satisfying x==NULL ||
+ strlen(x)==(strlen(in string)+strlen(in newsp)-strlen(in oldsp));
+@*/
+{
+ register const char *sp = string;
+ register const char *cp;
+ const char *savesp = 0;
+ stakseek(0);
+ if(*sp==0)
+ return((char*)0);
+ if(*(cp=oldsp) == 0)
+ goto found;
+#if SHOPT_MULTIBYTE
+ mbinit();
+#endif /* SHOPT_MULTIBYTE */
+ do
+ {
+ /* skip to first character which matches start of oldsp */
+ while(*sp && (savesp==sp || *sp != *cp))
+ {
+#if SHOPT_MULTIBYTE
+ /* skip a whole character at a time */
+ int c = mbsize(sp);
+ if(c < 0)
+ sp++;
+ while(c-- > 0)
+#endif /* SHOPT_MULTIBYTE */
+ stakputc(*sp++);
+ }
+ if(*sp == 0)
+ return((char*)0);
+ savesp = sp;
+ for(;*cp;cp++)
+ {
+ if(*cp != *sp++)
+ break;
+ }
+ if(*cp==0)
+ /* match found */
+ goto found;
+ sp = savesp;
+ cp = oldsp;
+ }
+ while(*sp);
+ return((char*)0);
+
+found:
+ /* copy new */
+ stakputs(newsp);
+ /* copy rest of string */
+ stakputs(sp);
+ return(stakfreeze(1));
+}
+
+/*
+ * TRIM(sp)
+ * Remove escape characters from characters in <sp> and eliminate quoted nulls.
+ */
+
+void sh_trim(register char *sp)
+/*@
+ assume sp!=NULL;
+ promise strlen(in sp) <= in strlen(sp);
+@*/
+{
+ register char *dp;
+ register int c;
+ if(sp)
+ {
+ dp = sp;
+ while(c= *sp)
+ {
+#if SHOPT_MULTIBYTE
+ int len;
+ if(mbwide() && (len=mbsize(sp))>1)
+ {
+ memmove(dp, sp, len);
+ dp += len;
+ sp += len;
+ continue;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ sp++;
+ if(c == '\\')
+ c = *sp++;
+ if(c)
+ *dp++ = c;
+ }
+ *dp = 0;
+ }
+}
+
+/*
+ * copy <str1> to <str2> changing upper case to lower case
+ * <str2> must be big enough to hold <str1>
+ * <str1> and <str2> may point to the same place.
+ */
+
+void sh_utol(register char const *str1,register char *str2)
+/*@
+ assume str1!=0 && str2!=0
+ return x satisfying strlen(in str1)==strlen(in str2);
+@*/
+{
+ register int c;
+ for(; c= *((unsigned char*)str1); str1++,str2++)
+ {
+ if(isupper(c))
+ *str2 = tolower(c);
+ else
+ *str2 = c;
+ }
+ *str2 = 0;
+}
+
+/*
+ * format string as a csv field
+ */
+static char *sh_fmtcsv(const char *string)
+{
+ register const char *cp = string, *op;
+ register int c;
+ int offset;
+ if(!cp)
+ return((char*)0);
+ offset = staktell();
+ while((c=mbchar(cp)),isaname(c));
+ if(c==0)
+ return((char*)string);
+ stakputc('"');
+ stakwrite(string,cp-string);
+ if(c=='"')
+ stakputc('"');
+ string = cp;
+ while(c=mbchar(cp))
+ {
+ if(c=='"')
+ {
+ stakwrite(string,cp-string);
+ string = cp;
+ stakputc('"');
+ }
+ }
+ if(--cp>string)
+ stakwrite(string,cp-string);
+ stakputc('"');
+ stakputc(0);
+ return(stakptr(offset));
+}
+
+/*
+ * print <str> quoting chars so that it can be read by the shell
+ * puts null terminated result on stack, but doesn't freeze it
+ */
+char *sh_fmtq(const char *string)
+{
+ register const char *cp = string, *op;
+ register int c, state;
+ int offset;
+ if(!cp)
+ return((char*)0);
+ offset = staktell();
+ state = ((c= mbchar(cp))==0);
+ if(isaletter(c))
+ {
+ while((c=mbchar(cp)),isaname(c));
+ if(c==0)
+ return((char*)string);
+ if(c=='=')
+ {
+ if(*cp==0)
+ return((char*)string);
+ if(*cp=='=')
+ cp++;
+ c = cp - string;
+ stakwrite(string,c);
+ string = cp;
+ c = mbchar(cp);
+ }
+ }
+ if(c==0 || c=='#' || c=='~')
+ state = 1;
+ for(;c;c= mbchar(cp))
+ {
+#if SHOPT_MULTIBYTE
+ if(c=='\'' || !iswprint(c))
+#else
+ if(c=='\'' || !isprint(c))
+#endif /* SHOPT_MULTIBYTE */
+ state = 2;
+ else if(c==']' || c=='=' || (c!=':' && c<=0x7f && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT))
+ state |=1;
+ }
+ if(state<2)
+ {
+ if(state==1)
+ stakputc('\'');
+ if(c = --cp - string)
+ stakwrite(string,c);
+ if(state==1)
+ stakputc('\'');
+ }
+ else
+ {
+ stakwrite("$'",2);
+ cp = string;
+#if SHOPT_MULTIBYTE
+ while(op = cp, c= mbchar(cp))
+#else
+ while(op = cp, c= *(unsigned char*)cp++)
+#endif
+ {
+ state=1;
+ switch(c)
+ {
+ case ('a'==97?'\033':39):
+ c = 'E';
+ break;
+ case '\n':
+ c = 'n';
+ break;
+ case '\r':
+ c = 'r';
+ break;
+ case '\t':
+ c = 't';
+ break;
+ case '\f':
+ c = 'f';
+ break;
+ case '\b':
+ c = 'b';
+ break;
+ case '\a':
+ c = 'a';
+ break;
+ case '\\': case '\'':
+ break;
+ default:
+#if SHOPT_MULTIBYTE
+ if(!iswprint(c))
+ {
+ while(op<cp)
+ sfprintf(staksp,"\\%.3o",*(unsigned char*)op++);
+ continue;
+ }
+#else
+ if(!isprint(c))
+ {
+ sfprintf(staksp,"\\%.3o",c);
+ continue;
+ }
+#endif
+ state=0;
+ break;
+ }
+ if(state)
+ {
+ stakputc('\\');
+ stakputc(c);
+ }
+ else
+ stakwrite(op, cp-op);
+ }
+ stakputc('\'');
+ }
+ stakputc(0);
+ return(stakptr(offset));
+}
+
+/*
+ * print <str> quoting chars so that it can be read by the shell
+ * puts null terminated result on stack, but doesn't freeze it
+ * single!=0 limits quoting to '...'
+ * fold>0 prints raw newlines and inserts appropriately
+ * escaped newlines every (fold-x) chars
+ */
+char *sh_fmtqf(const char *string, int single, int fold)
+{
+ register const char *cp = string;
+ register const char *bp;
+ register const char *vp;
+ register int c;
+ register int n;
+ register int q;
+ register int a;
+ int offset;
+
+ if (--fold < 8)
+ fold = 0;
+ if(single)
+ return sh_fmtcsv(cp);
+ if (!cp || !*cp || !fold || fold && strlen(string) < fold)
+ return sh_fmtq(cp);
+ offset = staktell();
+ single = single ? 1 : 3;
+ c = mbchar(string);
+ a = isaletter(c) ? '=' : 0;
+ vp = cp + 1;
+ do
+ {
+ q = 0;
+ n = fold;
+ bp = cp;
+ while ((!n || n-- > 0) && (c = mbchar(cp)))
+ {
+ if (a && !isaname(c))
+ a = 0;
+#if SHOPT_MULTIBYTE
+ if (c >= 0x200)
+ continue;
+ if (c == '\'' || !iswprint(c))
+#else
+ if (c == '\'' || !isprint(c))
+#endif /* SHOPT_MULTIBYTE */
+ {
+ q = single;
+ break;
+ }
+ if (c == '\n')
+ q = 1;
+ else if (c == a)
+ {
+ stakwrite(bp, cp - bp);
+ bp = cp;
+ vp = cp + 1;
+ a = 0;
+ }
+ else if ((c == '#' || c == '~') && cp == vp || c == ']' || c != ':' && (c = sh_lexstates[ST_NORM][c]) && c != S_EPAT)
+ q = 1;
+ }
+ if (q & 2)
+ {
+ stakputc('$');
+ stakputc('\'');
+ cp = bp;
+ n = fold - 3;
+ q = 1;
+ while (c = mbchar(cp))
+ {
+ switch (c)
+ {
+ case ('a'==97?'\033':39):
+ c = 'E';
+ break;
+ case '\n':
+ q = 0;
+ n = fold - 1;
+ break;
+ case '\r':
+ c = 'r';
+ break;
+ case '\t':
+ c = 't';
+ break;
+ case '\f':
+ c = 'f';
+ break;
+ case '\b':
+ c = 'b';
+ break;
+ case '\a':
+ c = 'a';
+ break;
+ case '\\':
+ if (*cp == 'n')
+ {
+ c = '\n';
+ q = 0;
+ n = fold - 1;
+ break;
+ }
+ case '\'':
+ break;
+ default:
+#if SHOPT_MULTIBYTE
+ if(!iswprint(c))
+#else
+ if(!isprint(c))
+#endif
+ {
+ if ((n -= 4) <= 0)
+ {
+ stakwrite("'\\\n$'", 5);
+ n = fold - 7;
+ }
+ sfprintf(staksp, "\\%03o", c);
+ continue;
+ }
+ q = 0;
+ break;
+ }
+ if ((n -= q + 1) <= 0)
+ {
+ if (!q)
+ {
+ stakputc('\'');
+ cp = bp;
+ break;
+ }
+ stakwrite("'\\\n$'", 5);
+ n = fold - 5;
+ }
+ if (q)
+ stakputc('\\');
+ else
+ q = 1;
+ stakputc(c);
+ bp = cp;
+ }
+ if (!c)
+ stakputc('\'');
+ }
+ else if (q & 1)
+ {
+ stakputc('\'');
+ cp = bp;
+ n = fold ? (fold - 2) : 0;
+ while (c = mbchar(cp))
+ {
+ if (c == '\n')
+ n = fold - 1;
+ else if (n && --n <= 0)
+ {
+ n = fold - 2;
+ stakwrite(bp, --cp - bp);
+ bp = cp;
+ stakwrite("'\\\n'", 4);
+ }
+ else if (n == 1 && *cp == '\'')
+ {
+ n = fold - 5;
+ stakwrite(bp, --cp - bp);
+ bp = cp;
+ stakwrite("'\\\n\\''", 6);
+ }
+ else if (c == '\'')
+ {
+ stakwrite(bp, cp - bp - 1);
+ bp = cp;
+ if (n && (n -= 4) <= 0)
+ {
+ n = fold - 5;
+ stakwrite("'\\\n\\''", 6);
+ }
+ else
+ stakwrite("'\\''", 4);
+ }
+ }
+ stakwrite(bp, cp - bp - 1);
+ stakputc('\'');
+ }
+ else if (n = fold)
+ {
+ cp = bp;
+ while (c = mbchar(cp))
+ {
+ if (--n <= 0)
+ {
+ n = fold;
+ stakwrite(bp, --cp - bp);
+ bp = cp;
+ stakwrite("\\\n", 2);
+ }
+ }
+ stakwrite(bp, cp - bp - 1);
+ }
+ else
+ stakwrite(bp, cp - bp);
+ if (c)
+ {
+ stakputc('\\');
+ stakputc('\n');
+ }
+ } while (c);
+ stakputc(0);
+ return(stakptr(offset));
+}
+
+#if SHOPT_MULTIBYTE
+ int sh_strchr(const char *string, register const char *dp)
+ {
+ wchar_t c, d;
+ register const char *cp=string;
+ mbinit();
+ d = mbchar(dp);
+ mbinit();
+ while(c = mbchar(cp))
+ {
+ if(c==d)
+ return(cp-string);
+ }
+ if(d==0)
+ return(cp-string);
+ return(-1);
+ }
+#endif /* SHOPT_MULTIBYTE */
+
+const char *_sh_translate(const char *message)
+{
+#if ERROR_VERSION >= 20000317L
+ return(ERROR_translate(0,0,e_dict,message));
+#else
+#if ERROR_VERSION >= 20000101L
+ return(ERROR_translate(e_dict,message));
+#else
+ return(ERROR_translate(message,1));
+#endif
+#endif
+}
+
+/*
+ * change '['identifier']' to identifier
+ * character before <str> must be a '['
+ * returns pointer to last character
+ */
+char *sh_checkid(char *str, char *last)
+{
+ register unsigned char *cp = (unsigned char*)str;
+ register unsigned char *v = cp;
+ register int c;
+ if(c=mbchar(cp),isaletter(c))
+ while(c=mbchar(cp),isaname(c));
+ if(c==']' && (!last || ((char*)cp==last)))
+ {
+ /* eliminate [ and ] */
+ while(v < cp)
+ {
+ v[-1] = *v;
+ v++;
+ }
+ if(last)
+ last -=2;
+ else
+ {
+ while(*v)
+ {
+ v[-2] = *v;
+ v++;
+ }
+ v[-2] = 0;
+ last = (char*)v;
+ }
+ }
+ return(last);
+}
+
+#if _AST_VERSION <= 20000317L
+char *fmtident(const char *string)
+{
+ return((char*)string);
+}
+#endif
diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c
new file mode 100644
index 0000000..c7c022b
--- /dev/null
+++ b/src/cmd/ksh93/sh/subshell.c
@@ -0,0 +1,780 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * Create and manage subshells avoiding forks when possible
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <ls.h>
+#include "io.h"
+#include "fault.h"
+#include "shnodes.h"
+#include "shlex.h"
+#include "jobs.h"
+#include "variables.h"
+#include "path.h"
+
+#ifndef PIPE_BUF
+# define PIPE_BUF 512
+#endif
+
+/*
+ * Note that the following structure must be the same
+ * size as the Dtlink_t structure
+ */
+struct Link
+{
+ struct Link *next;
+ Namval_t *child;
+ Dt_t *dict;
+ Namval_t *node;
+};
+
+/*
+ * The following structure is used for command substitution and (...)
+ */
+static struct subshell
+{
+ Shell_t *shp; /* shell interpreter */
+ struct subshell *prev; /* previous subshell data */
+ struct subshell *pipe; /* subshell where output goes to pipe on fork */
+ Dt_t *var; /* variable table at time of subshell */
+ struct Link *svar; /* save shell variable table */
+ Dt_t *sfun; /* function scope for subshell */
+ Dt_t *salias;/* alias scope for subshell */
+ Pathcomp_t *pathlist; /* for PATH variable */
+#if (ERROR_VERSION >= 20030214L)
+ struct Error_context_s *errcontext;
+#else
+ struct errorcontext *errcontext;
+#endif
+ Shopt_t options;/* save shell options */
+ pid_t subpid; /* child process id */
+ Sfio_t* saveout;/*saved standard output */
+ char *pwd; /* present working directory */
+ const char *shpwd; /* saved pointer to sh.pwd */
+ void *jobs; /* save job info */
+ mode_t mask; /* saved umask */
+ short tmpfd; /* saved tmp file descriptor */
+ short pipefd; /* read fd if pipe is created */
+ char jobcontrol;
+ char monitor;
+ unsigned char fdstatus;
+ int fdsaved; /* bit make for saved files */
+ int sig; /* signal for $$ */
+ pid_t bckpid;
+ pid_t cpid;
+ int coutpipe;
+ int cpipe;
+ int nofork;
+ int subdup;
+ char subshare;
+ char comsub;
+#if SHOPT_COSHELL
+ void *coshell;
+#endif /* SHOPT_COSHELL */
+} *subshell_data;
+
+static int subenv;
+
+
+/*
+ * This routine will turn the sftmp() file into a real /tmp file or pipe
+ * if the /tmp file create fails
+ */
+void sh_subtmpfile(Shell_t *shp)
+{
+ if(sfset(sfstdout,0,0)&SF_STRING)
+ {
+ register int fd;
+ register struct checkpt *pp = (struct checkpt*)shp->jmplist;
+ register struct subshell *sp = subshell_data->pipe;
+ /* save file descriptor 1 if open */
+ if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
+ {
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX;
+ close(1);
+ }
+ else if(errno!=EBADF)
+ errormsg(SH_DICT,ERROR_system(1),e_toomany);
+ /* popping a discipline forces a /tmp file create */
+ sfdisc(sfstdout,SF_POPDISC);
+ if((fd=sffileno(sfstdout))<0)
+ {
+ /* unable to create the /tmp file so use a pipe */
+ int fds[3];
+ Sfoff_t off;
+ fds[2] = 0;
+ sh_pipe(fds);
+ sp->pipefd = fds[0];
+ sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
+ /* write the data to the pipe */
+ if(off = sftell(sfstdout))
+ write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
+ sfclose(sfstdout);
+ if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
+ errormsg(SH_DICT,ERROR_system(1),e_file+4);
+ sh_close(fds[1]);
+ }
+ else
+ {
+ shp->fdstatus[fd] = IOREAD|IOWRITE;
+ sfsync(sfstdout);
+ if(fd==1)
+ fcntl(1,F_SETFD,0);
+ else
+ {
+ sfsetfd(sfstdout,1);
+ shp->fdstatus[1] = shp->fdstatus[fd];
+ shp->fdstatus[fd] = IOCLOSE;
+ }
+ }
+ sh_iostream(shp,1);
+ sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
+ sfpool(sfstdout,shp->outpool,SF_WRITE);
+ if(pp && pp->olist && pp->olist->strm == sfstdout)
+ pp->olist->strm = 0;
+ }
+}
+
+
+/*
+ * This routine creates a temp file if necessary and creates a subshell.
+ * The parent routine longjmps back to sh_subshell()
+ * The child continues possibly with its standard output replaced by temp file
+ */
+void sh_subfork(void)
+{
+ register struct subshell *sp = subshell_data;
+ Shell_t *shp = sp->shp;
+ int curenv = shp->curenv;
+ pid_t pid;
+ char *trap = shp->st.trapcom[0];
+ if(trap)
+ trap = strdup(trap);
+ /* see whether inside $(...) */
+ if(sp->pipe)
+ sh_subtmpfile(shp);
+ shp->curenv = 0;
+ shp->savesig = -1;
+ if(pid = sh_fork(shp,FSHOWME,NIL(int*)))
+ {
+ shp->curenv = curenv;
+ /* this is the parent part of the fork */
+ if(sp->subpid==0)
+ sp->subpid = pid;
+ if(trap)
+ free((void*)trap);
+ siglongjmp(*shp->jmplist,SH_JMPSUB);
+ }
+ else
+ {
+ /* this is the child part of the fork */
+ /* setting subpid to 1 causes subshell to exit when reached */
+ sh_onstate(SH_FORKED);
+ sh_onstate(SH_NOLOG);
+ sh_offoption(SH_MONITOR);
+ sh_offstate(SH_MONITOR);
+ subshell_data = 0;
+ shp->subshell = 0;
+ shp->comsub = 0;
+ SH_SUBSHELLNOD->nvalue.s = 0;
+ sp->subpid=0;
+ shp->st.trapcom[0] = trap;
+ shp->savesig = 0;
+ }
+}
+
+int nv_subsaved(register Namval_t *np)
+{
+ register struct subshell *sp;
+ register struct Link *lp;
+ for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev)
+ {
+ for(lp=sp->svar; lp; lp = lp->next)
+ {
+ if(lp->node==np)
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * This routine will make a copy of the given node in the
+ * layer created by the most recent subshell_fork if the
+ * node hasn't already been copied
+ */
+Namval_t *sh_assignok(register Namval_t *np,int add)
+{
+ register Namval_t *mp;
+ register struct Link *lp;
+ register struct subshell *sp = (struct subshell*)subshell_data;
+ Shell_t *shp = sp->shp;
+ Dt_t *dp= shp->var_tree;
+ Namval_t *mpnext;
+ Namarr_t *ap;
+ int save;
+ /* don't bother with this */
+ if(!sp->shpwd || np==SH_LEVELNOD || np==L_ARGNOD || np==SH_SUBSCRNOD || np==SH_NAMENOD)
+ return(np);
+ /* don't bother to save if in newer scope */
+ if(sp->var!=shp->var_tree && sp->var!=shp->var_base && shp->last_root==shp->var_tree)
+ return(np);
+ if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np)))
+ {
+ shp->last_root = ap->table;
+ sh_assignok(mp,add);
+ if(!add || array_assoc(ap))
+ return(np);
+ }
+ for(lp=sp->svar; lp;lp = lp->next)
+ {
+ if(lp->node==np)
+ return(np);
+ }
+ /* first two pointers use linkage from np */
+ lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*));
+ memset(lp,0, sizeof(*mp)+2*sizeof(void*));
+ lp->node = np;
+ if(!add && nv_isvtree(np))
+ {
+ Namval_t fake;
+ Dt_t *walk, *root=shp->var_tree;
+ char *name = nv_name(np);
+ int len = strlen(name);
+ fake.nvname = name;
+ mpnext = dtnext(root,&fake);
+ dp = root->walk?root->walk:root;
+ while(mp=mpnext)
+ {
+ walk = root->walk?root->walk:root;
+ mpnext = dtnext(root,mp);
+ if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.')
+ break;
+ nv_delete(mp,walk,NV_NOFREE);
+ *((Namval_t**)mp) = lp->child;
+ lp->child = mp;
+
+ }
+ }
+ lp->dict = dp;
+ mp = (Namval_t*)&lp->dict;
+ lp->next = subshell_data->svar;
+ subshell_data->svar = lp;
+ save = shp->subshell;
+ shp->subshell = 0;
+ mp->nvname = np->nvname;
+ if(nv_isattr(np,NV_NOFREE))
+ nv_onattr(mp,NV_IDENT);
+ nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE));
+ shp->subshell = save;
+ return(np);
+}
+
+/*
+ * restore the variables
+ */
+static void nv_restore(struct subshell *sp)
+{
+ register struct Link *lp, *lq;
+ register Namval_t *mp, *np;
+ const char *save = sp->shpwd;
+ Namval_t *mpnext;
+ int flags,nofree;
+ sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */
+ for(lp=sp->svar; lp; lp=lq)
+ {
+ np = (Namval_t*)&lp->dict;
+ lq = lp->next;
+ mp = lp->node;
+ if(!mp->nvname)
+ continue;
+ flags = 0;
+ if(nv_isattr(mp,NV_MINIMAL) && !nv_isattr(np,NV_EXPORT))
+ flags |= NV_MINIMAL;
+ if(nv_isarray(mp))
+ nv_putsub(mp,NIL(char*),ARRAY_SCAN);
+ nofree = mp->nvfun?mp->nvfun->nofree:0;
+ _nv_unset(mp,NV_RDONLY|NV_CLONE);
+ if(nv_isarray(np))
+ {
+ nv_clone(np,mp,NV_MOVE);
+ goto skip;
+ }
+ nv_setsize(mp,nv_size(np));
+ if(!(flags&NV_MINIMAL))
+ mp->nvenv = np->nvenv;
+ if(!nofree)
+ mp->nvfun = np->nvfun;
+ if(nv_isattr(np,NV_IDENT))
+ {
+ nv_offattr(np,NV_IDENT);
+ flags |= NV_NOFREE;
+ }
+ mp->nvflag = np->nvflag|(flags&NV_MINIMAL);
+ if(nv_cover(mp))
+ nv_putval(mp, nv_getval(np),np->nvflag|NV_NOFREE);
+ else
+ mp->nvalue.cp = np->nvalue.cp;
+ if(nofree && np->nvfun && !np->nvfun->nofree)
+ free((char*)np->nvfun);
+ np->nvfun = 0;
+ if(nv_isattr(mp,NV_EXPORT))
+ {
+ char *name = nv_name(mp);
+ sh_envput(sp->shp->env,mp);
+ if(*name=='_' && strcmp(name,"_AST_FEATURES")==0)
+ astconf(NiL, NiL, NiL);
+ }
+ else if(nv_isattr(np,NV_EXPORT))
+ env_delete(sp->shp->env,nv_name(mp));
+ nv_onattr(mp,flags);
+ skip:
+ for(mp=lp->child; mp; mp=mpnext)
+ {
+ mpnext = *((Namval_t**)mp);
+ dtinsert(lp->dict,mp);
+ }
+ free((void*)lp);
+ sp->svar = lq;
+ }
+ sp->shpwd=save;
+}
+
+/*
+ * return pointer to alias tree
+ * create new one if in a subshell and one doesn't exist and create is non-zero
+ */
+Dt_t *sh_subaliastree(int create)
+{
+ register struct subshell *sp = subshell_data;
+ if(!sp || sp->shp->curenv==0)
+ return(sh.alias_tree);
+ if(!sp->salias && create)
+ {
+ sp->salias = dtopen(&_Nvdisc,Dtoset);
+ dtview(sp->salias,sp->shp->alias_tree);
+ sp->shp->alias_tree = sp->salias;
+ }
+ return(sp->salias);
+}
+
+/*
+ * return pointer to function tree
+ * create new one if in a subshell and one doesn't exist and create is non-zero
+ */
+Dt_t *sh_subfuntree(int create)
+{
+ register struct subshell *sp = subshell_data;
+ if(!sp || sp->shp->curenv==0)
+ return(sh.fun_tree);
+ if(!sp->sfun && create)
+ {
+ sp->sfun = dtopen(&_Nvdisc,Dtoset);
+ dtview(sp->sfun,sp->shp->fun_tree);
+ sp->shp->fun_tree = sp->sfun;
+ }
+ return(sp->shp->fun_tree);
+}
+
+static void table_unset(register Dt_t *root,int fun)
+{
+ register Namval_t *np,*nq;
+ int flag;
+ for(np=(Namval_t*)dtfirst(root);np;np=nq)
+ {
+ nq = (Namval_t*)dtnext(root,np);
+ flag=0;
+ if(fun && np->nvalue.rp && np->nvalue.rp->fname && *np->nvalue.rp->fname=='/')
+ {
+ np->nvalue.rp->fdict = 0;
+ flag = NV_NOFREE;
+ }
+ else
+ _nv_unset(np,NV_RDONLY);
+ nv_delete(np,root,flag|NV_FUNCTION);
+ }
+}
+
+int sh_subsavefd(register int fd)
+{
+ register struct subshell *sp = subshell_data;
+ register int old=0;
+ if(sp)
+ {
+ old = !(sp->fdsaved&(1<<(fd-1)));
+ sp->fdsaved |= (1<<(fd-1));
+ }
+ return(old);
+}
+
+void sh_subjobcheck(pid_t pid)
+{
+ register struct subshell *sp = subshell_data;
+ while(sp)
+ {
+ if(sp->cpid==pid)
+ {
+ sh_close(sp->coutpipe);
+ sh_close(sp->cpipe);
+ sp->coutpipe = sp->cpipe = -1;
+ return;
+ }
+ sp = sp->prev;
+ }
+}
+
+/*
+ * Run command tree <t> in a virtual sub-shell
+ * If comsub is not null, then output will be placed in temp file (or buffer)
+ * If comsub is not null, the return value will be a stream consisting of
+ * output of command <t>. Otherwise, NULL will be returned.
+ */
+
+Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, int flags, int comsub)
+{
+ struct subshell sub_data;
+ register struct subshell *sp = &sub_data;
+ int jmpval,nsig=0,duped=0;
+ int savecurenv = shp->curenv;
+ int savejobpgid = job.curpgid;
+ int *saveexitval = job.exitval;
+ int16_t subshell;
+ char *savsig;
+ Sfio_t *iop=0;
+ struct checkpt buff;
+ struct sh_scoped savst;
+ struct dolnod *argsav=0;
+ int argcnt;
+ memset((char*)sp, 0, sizeof(*sp));
+ sfsync(shp->outpool);
+ sh_sigcheck(shp);
+ shp->savesig = -1;
+ if(argsav = sh_arguse(shp))
+ argcnt = argsav->dolrefcnt;
+ if(shp->curenv==0)
+ {
+ subshell_data=0;
+ subenv = 0;
+ }
+ shp->curenv = ++subenv;
+ savst = shp->st;
+ sh_pushcontext(shp,&buff,SH_JMPSUB);
+ subshell = shp->subshell+1;
+ SH_SUBSHELLNOD->nvalue.s = subshell;
+ shp->subshell = subshell;
+ sp->prev = subshell_data;
+ sp->shp = shp;
+ sp->sig = 0;
+ subshell_data = sp;
+ sp->errcontext = &buff.err;
+ sp->var = shp->var_tree;
+ sp->options = shp->options;
+ sp->jobs = job_subsave();
+ sp->subdup = shp->subdup;
+#if SHOPT_COSHELL
+ sp->coshell = shp->coshell;
+ shp->coshell = 0;
+#endif /* SHOPT_COSHELL */
+ /* make sure initialization has occurred */
+ if(!shp->pathlist)
+ {
+ shp->pathinit = 1;
+ path_get(shp,".");
+ shp->pathinit = 0;
+ }
+ sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist);
+ if(!shp->pwd)
+ path_pwd(shp,0);
+ sp->bckpid = shp->bckpid;
+ if(comsub)
+ sh_stats(STAT_COMSUB);
+ else
+ job.curpgid = 0;
+ sp->subshare = shp->subshare;
+ sp->comsub = shp->comsub;
+ shp->subshare = comsub==2 || (comsub==1 && sh_isoption(SH_SUBSHARE));
+ if(comsub)
+ shp->comsub = comsub;
+ if(!comsub || !shp->subshare)
+ {
+ sp->shpwd = shp->pwd;
+ sp->pwd = (shp->pwd?strdup(shp->pwd):0);
+ sp->mask = shp->mask;
+ sh_stats(STAT_SUBSHELL);
+ /* save trap table */
+ shp->st.otrapcom = 0;
+ if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
+ {
+ nsig += sizeof(char*);
+ memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
+ /* this nonsense needed for $(trap) */
+ shp->st.otrapcom = (char**)savsig;
+ }
+ sp->cpid = shp->cpid;
+ sp->coutpipe = shp->coutpipe;
+ sp->cpipe = shp->cpipe[1];
+ shp->cpid = 0;
+ sh_sigreset(0);
+ }
+ jmpval = sigsetjmp(buff.buff,0);
+ if(jmpval==0)
+ {
+ if(comsub)
+ {
+ /* disable job control */
+ shp->spid = 0;
+ sp->jobcontrol = job.jobcontrol;
+ sp->monitor = (sh_isstate(SH_MONITOR)!=0);
+ job.jobcontrol=0;
+ sh_offstate(SH_MONITOR);
+ sp->pipe = sp;
+ /* save sfstdout and status */
+ sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
+ sp->fdstatus = shp->fdstatus[1];
+ sp->tmpfd = -1;
+ sp->pipefd = -1;
+ /* use sftmp() file for standard output */
+ if(!(iop = sftmp(PIPE_BUF)))
+ {
+ sfswap(sp->saveout,sfstdout);
+ errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
+ }
+ sfswap(iop,sfstdout);
+ sfset(sfstdout,SF_READ,0);
+ shp->fdstatus[1] = IOWRITE;
+ if(!(sp->nofork = sh_state(SH_NOFORK)))
+ sh_onstate(SH_NOFORK);
+ flags |= sh_state(SH_NOFORK);
+ }
+ else if(sp->prev)
+ {
+ sp->pipe = sp->prev->pipe;
+ flags &= ~sh_state(SH_NOFORK);
+ }
+ if(shp->savesig < 0)
+ {
+ shp->savesig = 0;
+ sh_exec(t,flags);
+ }
+ }
+ if(comsub!=2 && jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell)
+ {
+ /* trap on EXIT not handled by child */
+ char *trap=shp->st.trapcom[0];
+ shp->st.trapcom[0] = 0; /* prevent recursion */
+ shp->oldexit = shp->exitval;
+ sh_trap(trap,0);
+ free(trap);
+ }
+ sh_popcontext(shp,&buff);
+ if(shp->subshell==0) /* must be child process */
+ {
+ subshell_data = sp->prev;
+ if(jmpval==SH_JMPSCRIPT)
+ siglongjmp(*shp->jmplist,jmpval);
+ shp->exitval &= SH_EXITMASK;
+ sh_done(shp,0);
+ }
+ if(!shp->savesig)
+ shp->savesig = -1;
+ if(comsub)
+ {
+ /* re-enable job control */
+ if(!sp->nofork)
+ sh_offstate(SH_NOFORK);
+ job.jobcontrol = sp->jobcontrol;
+ if(sp->monitor)
+ sh_onstate(SH_MONITOR);
+ if(sp->pipefd>=0)
+ {
+ /* sftmp() file has been returned into pipe */
+ iop = sh_iostream(shp,sp->pipefd);
+ sfclose(sfstdout);
+ }
+ else
+ {
+ /* move tmp file to iop and restore sfstdout */
+ iop = sfswap(sfstdout,NIL(Sfio_t*));
+ if(!iop)
+ {
+ /* maybe locked try again */
+ sfclrlock(sfstdout);
+ iop = sfswap(sfstdout,NIL(Sfio_t*));
+ }
+ if(iop && sffileno(iop)==1)
+ {
+ int fd=sfsetfd(iop,3);
+ if(fd<0)
+ {
+ shp->toomany = 1;
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
+ errormsg(SH_DICT,ERROR_system(1),e_toomany);
+ }
+ if(fd >= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,fd);
+ shp->sftable[fd] = iop;
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX);
+ shp->fdstatus[1] = IOCLOSE;
+ }
+ sfset(iop,SF_READ,1);
+ }
+ sfswap(sp->saveout,sfstdout);
+ /* check if standard output was preserved */
+ if(sp->tmpfd>=0)
+ {
+ close(1);
+ if (fcntl(sp->tmpfd,F_DUPFD,1) != 1)
+ duped++;
+ sh_close(sp->tmpfd);
+ }
+ shp->fdstatus[1] = sp->fdstatus;
+ }
+ path_delete((Pathcomp_t*)shp->pathlist);
+ shp->pathlist = (void*)sp->pathlist;
+ job_subrestore(sp->jobs);
+ shp->jobenv = savecurenv;
+ job.curpgid = savejobpgid;
+ job.exitval = saveexitval;
+ shp->bckpid = sp->bckpid;
+ if(sp->shpwd) /* restore environment if saved */
+ {
+ int n;
+ shp->options = sp->options;
+ nv_restore(sp);
+ if(sp->salias)
+ {
+ shp->alias_tree = dtview(sp->salias,0);
+ table_unset(sp->salias,0);
+ dtclose(sp->salias);
+ }
+ if(sp->sfun)
+ {
+ shp->fun_tree = dtview(sp->sfun,0);
+ table_unset(sp->sfun,1);
+ dtclose(sp->sfun);
+ }
+ n = shp->st.trapmax-savst.trapmax;
+ sh_sigreset(1);
+ if(n>0)
+ memset(&shp->st.trapcom[savst.trapmax],0,n*sizeof(char*));
+ shp->st = savst;
+ shp->curenv = savecurenv;
+ if(nsig)
+ {
+ memcpy((char*)&shp->st.trapcom[0],savsig,nsig);
+ free((void*)savsig);
+ }
+ shp->options = sp->options;
+ if(!shp->pwd || strcmp(sp->pwd,shp->pwd))
+ {
+ /* restore PWDNOD */
+ Namval_t *pwdnod = sh_scoped(shp,PWDNOD);
+ if(shp->pwd)
+ {
+ chdir(shp->pwd=sp->pwd);
+ path_newdir(shp,shp->pathlist);
+ }
+ if(nv_isattr(pwdnod,NV_NOFREE))
+ pwdnod->nvalue.cp = (const char*)sp->pwd;
+ }
+ else if(sp->shpwd != shp->pwd)
+ {
+ shp->pwd = sp->pwd;
+ if(PWDNOD->nvalue.cp==sp->shpwd)
+ PWDNOD->nvalue.cp = sp->pwd;
+ }
+ else
+ free((void*)sp->pwd);
+ if(sp->mask!=shp->mask)
+ umask(shp->mask=sp->mask);
+ if(shp->coutpipe!=sp->coutpipe)
+ {
+ sh_close(shp->coutpipe);
+ sh_close(shp->cpipe[1]);
+ }
+ shp->cpid = sp->cpid;
+ shp->cpipe[1] = sp->cpipe;
+ shp->coutpipe = sp->coutpipe;
+ }
+ shp->subshare = sp->subshare;
+ shp->comsub = sp->comsub;
+ shp->subdup = sp->subdup;
+#if SHOPT_COSHELL
+ shp->coshell = sp->coshell;
+#endif /* SHOPT_COSHELL */
+ if(shp->subshell)
+ SH_SUBSHELLNOD->nvalue.s = --shp->subshell;
+ subshell = shp->subshell;
+ subshell_data = sp->prev;
+ if(!argsav || argsav->dolrefcnt==argcnt)
+ sh_argfree(shp,argsav,0);
+ if(shp->topfd != buff.topfd)
+ sh_iorestore(shp,buff.topfd|IOSUBSHELL,jmpval);
+ if(sp->sig)
+ {
+ if(sp->prev)
+ sp->prev->sig = sp->sig;
+ else
+ {
+ sh_fault(sp->sig);
+ sh_chktrap(shp);
+ }
+ }
+ sh_sigcheck(shp);
+ shp->trapnote = 0;
+ nsig = shp->savesig;
+ shp->savesig = 0;
+ if(nsig>0)
+ sh_fault(nsig);
+ if(sp->subpid)
+ job_wait(sp->subpid);
+ if(comsub && iop && sp->pipefd<0)
+ sfseek(iop,(off_t)0,SEEK_SET);
+ if(shp->trapnote)
+ sh_chktrap(shp);
+ if(shp->exitval > SH_EXITSIG)
+ {
+ int sig = shp->exitval&SH_EXITMASK;
+ if(sig==SIGINT || sig== SIGQUIT)
+ sh_fault(sig);
+ }
+ if(duped)
+ {
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
+ shp->toomany = 1;
+ errormsg(SH_DICT,ERROR_system(1),e_redirect);
+ }
+ if(shp->ignsig)
+ sh_fault(shp->ignsig);
+ if(jmpval==SH_JMPSUB && shp->lastsig)
+ sh_fault(shp->lastsig);
+ if(jmpval && shp->toomany)
+ siglongjmp(*shp->jmplist,jmpval);
+ return(iop);
+}
diff --git a/src/cmd/ksh93/sh/suid_exec.c b/src/cmd/ksh93/sh/suid_exec.c
new file mode 100644
index 0000000..a635b63
--- /dev/null
+++ b/src/cmd/ksh93/sh/suid_exec.c
@@ -0,0 +1,509 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * This is a program to execute 'execute only' and suid/sgid shell scripts.
+ * This program must be owned by root and must have the set uid bit set.
+ * It must not have the set group id bit set. This program must be installed
+ * where the define parameter THISPROG indicates to work correctly on system V
+ *
+ * Written by David Korn
+ * AT&T Labs
+ * Enhanced by Rob Stampfli
+ */
+
+/* The file name of the script to execute is argv[0]
+ * Argv[1] is the program name
+ * The basic idea is to open the script as standard input, set the effective
+ * user and group id correctly, and then exec the shell.
+ * The complicated part is getting the effective uid of the caller and
+ * setting the effective uid/gid. The program which execs this program
+ * may pass file descriptor FDIN as an open file with mode SPECIAL if
+ * the effective user id is not the real user id. The effective
+ * user id for authentication purposes will be the owner of this
+ * open file. On systems without the setreuid() call, e[ug]id is set
+ * by copying this program to a /tmp/file, making it a suid and/or sgid
+ * program, and then execing this program.
+ * A forked version of this program waits until it can unlink the /tmp
+ * file and then exits. Actually, we fork() twice so the parent can
+ * wait for the child to complete. A pipe is used to guarantee that we
+ * do not remove the /tmp file too soon.
+ */
+
+#include <ast.h>
+#include "FEATURE/externs"
+#include <ls.h>
+#include <sig.h>
+#include <error.h>
+#include <sys/wait.h>
+#include "version.h"
+
+#define SPECIAL 04100 /* setuid execute only by owner */
+#define FDIN 10 /* must be same as /dev/fd below */
+#undef FDSYNC
+#define FDSYNC 11 /* used on sys5 to synchronize cleanup */
+#define FDVERIFY 12 /* used to validate /tmp process */
+#undef BLKSIZE
+#define BLKSIZE sizeof(char*)*1024
+#define THISPROG "/etc/suid_exec"
+#define DEFSHELL "/bin/sh"
+
+static void error_exit(const char*);
+static int in_dir(const char*, const char*);
+static int endsh(const char*);
+#ifndef _lib_setregid
+# undef _lib_setreuid
+#endif
+#ifndef _lib_setreuid
+ static void setids(int,uid_t,gid_t);
+ static int mycopy(int, int);
+ static void maketemp(char*);
+#else
+ static void setids(int,int,int);
+#endif /* _lib_setreuid */
+
+static const char version[] = "\n@(#)$Id: suid_exec "SH_RELEASE" $\n";
+static const char badopen[] = "cannot open";
+static const char badexec[] = "cannot exec";
+static const char devfd[] = "/dev/fd/10"; /* must match FDIN above */
+static char tmpname[] = "/tmp/SUIDXXXXXX";
+static char **arglist;
+
+static char *shell;
+static char *command;
+static uid_t ruserid;
+static uid_t euserid;
+static gid_t rgroupid;
+static gid_t egroupid;
+static struct stat statb;
+
+int main(int argc,char *argv[])
+{
+ register int m,n;
+ register char *p;
+ struct stat statx;
+ int mode;
+ uid_t effuid;
+ gid_t effgid;
+ NOT_USED(argc);
+ arglist = argv;
+ if((command = argv[1]) == 0)
+ error_exit(badexec);
+ ruserid = getuid();
+ euserid = geteuid();
+ rgroupid = getgid();
+ egroupid = getegid();
+ p = argv[0];
+#ifndef _lib_setreuid
+ maketemp(tmpname);
+ if(strcmp(p,tmpname)==0)
+ {
+ /* At this point, the presumption is that we are the
+ * version of THISPROG copied into /tmp, with the owner,
+ * group, and setuid/gid bits correctly set. This copy of
+ * the program is executable by anyone, so we must be careful
+ * not to allow just any invocation of it to succeed, since
+ * it is setuid/gid. Validate the proper execution by
+ * examining the FDVERIFY file descriptor -- if it is owned
+ * by root and is mode SPECIAL, then this is proof that it was
+ * passed by a program with superuser privileges -- hence we
+ * can presume legitimacy. Otherwise, bail out, as we suspect
+ * an impostor.
+ */
+ if(fstat(FDVERIFY,&statb) < 0 || statb.st_uid != 0 ||
+ (statb.st_mode & ~S_IFMT) != SPECIAL || close(FDVERIFY)<0)
+ error_exit(badexec);
+ /* This enables the grandchild to clean up /tmp file */
+ close(FDSYNC);
+ /* Make sure that this is a valid invocation of the clone.
+ * Perhaps unnecessary, given FDVERIFY, but what the heck...
+ */
+ if(stat(tmpname,&statb) < 0 || statb.st_nlink != 1 ||
+ !S_ISREG(statb.st_mode))
+ error_exit(badexec);
+ if(ruserid != euserid &&
+ ((statb.st_mode & S_ISUID) == 0 || statb.st_uid != euserid))
+ error_exit(badexec);
+ goto exec;
+ }
+ /* Make sure that this is the real setuid program, not the clone.
+ * It is possible by clever hacking to get past this point in the
+ * clone, but it doesn't do the hacker any good that I can see.
+ */
+ if(euserid)
+ error_exit(badexec);
+#endif /* _lib_setreuid */
+ /* Open the script for reading first and then validate it. This
+ * prevents someone from pulling a switcheroo while we are validating.
+ */
+ n = open(p,0);
+ if(n == FDIN)
+ {
+ n = dup(n);
+ close(FDIN);
+ }
+ if(n < 0)
+ error_exit(badopen);
+ /* validate execution rights to this script */
+ if(fstat(FDIN,&statb) < 0 || (statb.st_mode & ~S_IFMT) != SPECIAL)
+ euserid = ruserid;
+ else
+ euserid = statb.st_uid;
+ /* do it the easy way if you can */
+ if(euserid == ruserid && egroupid == rgroupid)
+ {
+ if(access(p,X_OK) < 0)
+ error_exit(badexec);
+ }
+ else
+ {
+ /* have to check access on each component */
+ while(*p++)
+ {
+ if(*p == '/' || *p == 0)
+ {
+ m = *p;
+ *p = 0;
+ if(eaccess(argv[0],X_OK) < 0)
+ error_exit(badexec);
+ *p = m;
+ }
+ }
+ p = argv[0];
+ }
+ if(fstat(n, &statb) < 0 || !S_ISREG(statb.st_mode))
+ error_exit(badopen);
+ if(stat(p, &statx) < 0 ||
+ statb.st_ino != statx.st_ino || statb.st_dev != statx.st_dev)
+ error_exit(badexec);
+ if(stat(THISPROG, &statx) < 0 ||
+ (statb.st_ino == statx.st_ino && statb.st_dev == statx.st_dev))
+ error_exit(badexec);
+ close(FDIN);
+ if(fcntl(n,F_DUPFD,FDIN) != FDIN)
+ error_exit(badexec);
+ close(n);
+
+ /* compute the desired new effective user and group id */
+ effuid = euserid;
+ effgid = egroupid;
+ mode = 0;
+ if(statb.st_mode & S_ISUID)
+ effuid = statb.st_uid;
+ if(statb.st_mode & S_ISGID)
+ effgid = statb.st_gid;
+
+ /* see if group needs setting */
+ if(effgid != egroupid)
+ if(effgid != rgroupid || setgid(rgroupid) < 0)
+ mode = S_ISGID;
+
+ /* now see if the uid needs setting */
+ if(mode)
+ {
+ if(effuid != ruserid)
+ mode |= S_ISUID;
+ }
+ else if(effuid)
+ {
+ if(effuid != ruserid || setuid(ruserid) < 0)
+ mode = S_ISUID;
+ }
+
+ if(mode)
+ setids(mode, effuid, effgid);
+#ifndef _lib_setreuid
+exec:
+#endif /* _lib_setreuid */
+ /* only use SHELL if file is in trusted directory and ends in sh */
+ shell = getenv("SHELL");
+ if(shell == 0 || !endsh(shell) || (
+ !in_dir("/bin",shell) &&
+ !in_dir("/usr/bin",shell) &&
+ !in_dir("/usr/lbin",shell) &&
+ !in_dir("/usr/local/bin",shell)))
+ shell = DEFSHELL;
+ argv[0] = command;
+ argv[1] = (char*)devfd;
+ execv(shell,argv);
+ error_exit(badexec);
+}
+
+/*
+ * return true of shell ends in sh of ksh
+ */
+
+static int endsh(register const char *shell)
+{
+ while(*shell)
+ shell++;
+ if(*--shell != 'h' || *--shell != 's')
+ return(0);
+ if(*--shell=='/')
+ return(1);
+ if(*shell=='k' && *--shell=='/')
+ return(1);
+ return(0);
+}
+
+
+/*
+ * return true of shell is in <dir> directory
+ */
+
+static int in_dir(register const char *dir,register const char *shell)
+{
+ while(*dir)
+ {
+ if(*dir++ != *shell++)
+ return(0);
+ }
+ /* return true if next character is a '/' */
+ return(*shell=='/');
+}
+
+static void error_exit(const char *message)
+{
+ sfprintf(sfstdout,"%s: %s\n",command,message);
+ exit(126);
+}
+
+
+/*
+ * This version of access checks against effective uid and effective gid
+ */
+
+int eaccess(register const char *name, register int mode)
+{
+ struct stat statb;
+ if (stat(name, &statb) == 0)
+ {
+ if(euserid == 0)
+ {
+ if(!S_ISREG(statb.st_mode) || mode != 1)
+ return(0);
+ /* root needs execute permission for someone */
+ mode = (S_IXUSR|S_IXGRP|S_IXOTH);
+ }
+ else if(euserid == statb.st_uid)
+ mode <<= 6;
+ else if(egroupid == statb.st_gid)
+ mode <<= 3;
+#ifdef _lib_getgroups
+ /* on some systems you can be in several groups */
+ else
+ {
+ static int maxgroups;
+ gid_t *groups=0;
+ register int n;
+ if(maxgroups==0)
+ {
+ /* first time */
+ if((maxgroups=getgroups(0,groups)) < 0)
+ {
+ /* pre-POSIX system */
+ maxgroups=NGROUPS_MAX;
+ }
+ }
+ groups = (gid_t*)malloc((maxgroups+1)*sizeof(gid_t));
+ n = getgroups(maxgroups,groups);
+ while(--n >= 0)
+ {
+ if(groups[n] == statb.st_gid)
+ {
+ mode <<= 3;
+ break;
+ }
+ }
+ }
+#endif /* _lib_getgroups */
+ if(statb.st_mode & mode)
+ return(0);
+ }
+ return(-1);
+}
+
+#ifdef _lib_setreuid
+static void setids(int mode,int owner,int group)
+{
+ if(mode & S_ISGID)
+ setregid(rgroupid,group);
+
+ /* set effective uid even if S_ISUID is not set. This is because
+ * we are *really* executing EUID root at this point. Even if S_ISUID
+ * is not set, the value for owner that is passsed should be correct.
+ */
+ setreuid(ruserid,owner);
+}
+
+#else
+/*
+ * This version of setids creats a /tmp file and copies itself into it.
+ * The "clone" file is made executable with appropriate suid/sgid bits.
+ * Finally, the clone is exec'ed. This file is unlinked by a grandchild
+ * of this program, who waits around until the text is free.
+ */
+
+static void setids(int mode,uid_t owner,gid_t group)
+{
+ register int n,m;
+ int pv[2];
+
+ /*
+ * Create a token to pass to the new program for validation.
+ * This token can only be procured by someone running with an
+ * effective userid of root, and hence gives the clone a way to
+ * certify that it was really invoked by THISPROG. Someone who
+ * is already root could spoof us, but why would they want to?
+ *
+ * Since we are root here, we must be careful: What if someone
+ * linked a valuable file to tmpname?
+ */
+ unlink(tmpname); /* should normally fail */
+#ifdef O_EXCL
+ if((n = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, SPECIAL)) < 0 ||
+ unlink(tmpname) < 0)
+#else
+ if((n = open(tmpname, O_WRONLY | O_CREAT ,SPECIAL)) < 0 || unlink(tmpname) < 0)
+#endif
+ error_exit(badexec);
+ if(n != FDVERIFY)
+ {
+ close(FDVERIFY);
+ if(fcntl(n,F_DUPFD,FDVERIFY) != FDVERIFY)
+ error_exit(badexec);
+ }
+ mode |= S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6);
+ /* create a pipe for synchronization */
+ if(pipe(pv) < 0)
+ error_exit(badexec);
+ if((n=fork()) == 0)
+ { /* child */
+ close(FDVERIFY);
+ close(pv[1]);
+ if((n=fork()) == 0)
+ { /* grandchild -- cleans up clone file */
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ read(pv[0],pv,1); /* wait for clone to close pipe */
+ while(unlink(tmpname) < 0 && errno == ETXTBSY)
+ sleep(1);
+ exit(0);
+ }
+ else if(n == -1)
+ exit(1);
+ else
+ {
+ /* Create a set[ug]id file that will become the clone.
+ * To make this atomic, without need for chown(), the
+ * child takes on desired user and group. The only
+ * downsize of this that I can see is that it may
+ * screw up some per- * user accounting.
+ */
+ if((m = open(THISPROG, O_RDONLY)) < 0)
+ exit(1);
+ if((mode & S_ISGID) && setgid(group) < 0)
+ exit(1);
+ if((mode & S_ISUID) && owner && setuid(owner) < 0)
+ exit(1);
+#ifdef O_EXCL
+ if((n = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, mode)) < 0)
+#else
+ unlink(tmpname);
+ if((n = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0)
+#endif /* O_EXCL */
+ exit(1);
+ /* populate the clone */
+ m = mycopy(m,n);
+ if(chmod(tmpname,mode) <0)
+ exit(1);
+ exit(m);
+ }
+ }
+ else if(n == -1)
+ error_exit(badexec);
+ else
+ {
+ arglist[0] = (char*)tmpname;
+ close(pv[0]);
+ /* move write end of pipe into FDSYNC */
+ if(pv[1] != FDSYNC)
+ {
+ close(FDSYNC);
+ if(fcntl(pv[1],F_DUPFD,FDSYNC) != FDSYNC)
+ error_exit(badexec);
+ }
+ /* wait for child to die */
+ while((m = wait(0)) != n)
+ if(m == -1 && errno != EINTR)
+ break;
+ /* Kill any setuid status at this point. That way, if the
+ * clone is not setuid, we won't exec it as root. Also, don't
+ * neglect to consider that someone could have switched the
+ * clone file on us.
+ */
+ if(setuid(ruserid) < 0)
+ error_exit(badexec);
+ execv(tmpname,arglist);
+ error_exit(badexec);
+ }
+}
+
+/*
+ * create a unique name into the <template>
+ */
+
+static void maketemp(char *template)
+{
+ register char *cp = template;
+ register pid_t n = getpid();
+ /* skip to end of string */
+ while(*++cp);
+ /* convert process id to string */
+ while(n > 0)
+ {
+ *--cp = (n%10) + '0';
+ n /= 10;
+ }
+
+}
+
+/*
+ * copy THISPROG into the open file number <fdo> and close <fdo>
+ */
+
+static int mycopy(int fdi, int fdo)
+{
+ char buffer[BLKSIZE];
+ register int n;
+
+ while((n = read(fdi,buffer,BLKSIZE)) > 0)
+ if(write(fdo,buffer,n) != n)
+ break;
+ close(fdi);
+ close(fdo);
+ return n;
+}
+
+#endif /* _lib_setreuid */
+
+
diff --git a/src/cmd/ksh93/sh/tdump.c b/src/cmd/ksh93/sh/tdump.c
new file mode 100644
index 0000000..d3d82d8
--- /dev/null
+++ b/src/cmd/ksh93/sh/tdump.c
@@ -0,0 +1,262 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * David Korn
+ * AT&T Labs
+ *
+ * shell parse tree dump
+ *
+ */
+
+#include "defs.h"
+#include "shnodes.h"
+#include "path.h"
+#include "io.h"
+#include <ccode.h>
+
+static int p_comlist(const struct dolnod*);
+static int p_arg(const struct argnod*);
+static int p_comarg(const struct comnod*);
+static int p_redirect(const struct ionod*);
+static int p_switch(const struct regnod*);
+static int p_tree(const Shnode_t*);
+static int p_string(const char*);
+
+static Sfio_t *outfile;
+
+int sh_tdump(Sfio_t *out, const Shnode_t *t)
+{
+ outfile = out;
+ return(p_tree(t));
+}
+
+/*
+ * convert to ASCII to write and back again if needed
+ */
+static int outstring(Sfio_t *out, const char *string, int n)
+{
+ int r;
+ char *cp = (char*)string;
+ ccmaps(cp, n, CC_NATIVE, CC_ASCII);
+ r = sfwrite(out,cp,n);
+ ccmaps(cp, n, CC_ASCII, CC_NATIVE);
+ return(r);
+}
+
+/*
+ * print script corresponding to shell tree <t>
+ */
+static int p_tree(register const Shnode_t *t)
+{
+ if(!t)
+ return(sfputl(outfile,-1));
+ if(sfputl(outfile,t->tre.tretyp)<0)
+ return(-1);
+ switch(t->tre.tretyp&COMMSK)
+ {
+ case TTIME:
+ case TPAR:
+ return(p_tree(t->par.partre));
+ case TCOM:
+ return(p_comarg((struct comnod*)t));
+ case TSETIO:
+ case TFORK:
+ if(sfputu(outfile,t->fork.forkline)<0)
+ return(-1);
+ if(p_tree(t->fork.forktre)<0)
+ return(-1);
+ return(p_redirect(t->fork.forkio));
+ case TIF:
+ if(p_tree(t->if_.iftre)<0)
+ return(-1);
+ if(p_tree(t->if_.thtre)<0)
+ return(-1);
+ return(p_tree(t->if_.eltre));
+ case TWH:
+ if(t->wh.whinc)
+ {
+ if(p_tree((Shnode_t*)(t->wh.whinc))<0)
+ return(-1);
+ }
+ else
+ {
+ if(sfputl(outfile,-1)<0)
+ return(-1);
+ }
+ if(p_tree(t->wh.whtre)<0)
+ return(-1);
+ return(p_tree(t->wh.dotre));
+ case TLST:
+ case TAND:
+ case TORF:
+ case TFIL:
+ if(p_tree(t->lst.lstlef)<0)
+ return(-1);
+ return(p_tree(t->lst.lstrit));
+ case TARITH:
+ if(sfputu(outfile,t->ar.arline)<0)
+ return(-1);
+ return(p_arg(t->ar.arexpr));
+ case TFOR:
+ if(sfputu(outfile,t->for_.forline)<0)
+ return(-1);
+ if(p_tree(t->for_.fortre)<0)
+ return(-1);
+ if(p_string(t->for_.fornam)<0)
+ return(-1);
+ return(p_tree((Shnode_t*)t->for_.forlst));
+ case TSW:
+ if(sfputu(outfile,t->sw.swline)<0)
+ return(-1);
+ if(p_arg(t->sw.swarg)<0)
+ return(-1);
+ return(p_switch(t->sw.swlst));
+ case TFUN:
+ if(sfputu(outfile,t->funct.functline)<0)
+ return(-1);
+ if(p_string(t->funct.functnam)<0)
+ return(-1);
+ if(p_tree(t->funct.functtre)<0)
+ return(-1);
+ return(p_tree((Shnode_t*)t->funct.functargs));
+ case TTST:
+ if(sfputu(outfile,t->tst.tstline)<0)
+ return(-1);
+ if((t->tre.tretyp&TPAREN)==TPAREN)
+ return(p_tree(t->lst.lstlef));
+ else
+ {
+ if(p_arg(&(t->lst.lstlef->arg))<0)
+ return(-1);
+ if((t->tre.tretyp&TBINARY))
+ return(p_arg(&(t->lst.lstrit->arg)));
+ return(0);
+ }
+ }
+ return(-1);
+}
+
+static int p_arg(register const struct argnod *arg)
+{
+ register int n;
+ struct fornod *fp;
+ while(arg)
+ {
+ if((n = strlen(arg->argval)) || (arg->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED)))
+ fp=0;
+ else
+ {
+ fp=(struct fornod*)arg->argchn.ap;
+ n = strlen(fp->fornam)+1;
+ }
+ sfputu(outfile,n+1);
+ if(fp)
+ {
+ sfputc(outfile,0);
+ outstring(outfile,fp->fornam,n-1);
+ }
+ else
+ outstring(outfile,arg->argval,n);
+ sfputc(outfile,arg->argflag);
+ if(fp)
+ {
+ sfputu(outfile,fp->fortyp);
+ p_tree(fp->fortre);
+ }
+ else if(n==0 && (arg->argflag&ARG_EXP) && arg->argchn.ap)
+ p_tree((Shnode_t*)arg->argchn.ap);
+ arg = arg->argnxt.ap;
+ }
+ return(sfputu(outfile,0));
+}
+
+static int p_redirect(register const struct ionod *iop)
+{
+ while(iop)
+ {
+ if(iop->iovname)
+ sfputl(outfile,iop->iofile|IOVNM);
+ else
+ sfputl(outfile,iop->iofile);
+ p_string(iop->ioname);
+ if(iop->iodelim)
+ {
+ p_string(iop->iodelim);
+ sfputl(outfile,iop->iosize);
+ sfseek(sh.heredocs,iop->iooffset,SEEK_SET);
+ sfmove(sh.heredocs,outfile, iop->iosize,-1);
+ }
+ else
+ sfputu(outfile,0);
+ if(iop->iovname)
+ p_string(iop->iovname);
+ iop = iop->ionxt;
+ }
+ return(sfputl(outfile,-1));
+}
+
+static int p_comarg(register const struct comnod *com)
+{
+ p_redirect(com->comio);
+ p_arg(com->comset);
+ if(!com->comarg)
+ sfputl(outfile,-1);
+ else if(com->comtyp&COMSCAN)
+ p_arg(com->comarg);
+ else
+ p_comlist((struct dolnod*)com->comarg);
+ return(sfputu(outfile,com->comline));
+}
+
+static int p_comlist(const struct dolnod *dol)
+{
+ register char *cp, *const*argv;
+ register int n;
+ argv = dol->dolval+ARG_SPARE;
+ while(cp = *argv)
+ argv++;
+ n = argv - (dol->dolval+1);
+ sfputl(outfile,n);
+ argv = dol->dolval+ARG_SPARE;
+ while(cp = *argv++)
+ p_string(cp);
+ return(sfputu(outfile,0));
+}
+
+static int p_switch(register const struct regnod *reg)
+{
+ while(reg)
+ {
+ sfputl(outfile,reg->regflag);
+ p_arg(reg->regptr);
+ p_tree(reg->regcom);
+ reg = reg->regnxt;
+ }
+ return(sfputl(outfile,-1));
+}
+
+static int p_string(register const char *string)
+{
+ register size_t n=strlen(string);
+ if(sfputu(outfile,n+1)<0)
+ return(-1);
+ return(outstring(outfile,string,n));
+}
diff --git a/src/cmd/ksh93/sh/timers.c b/src/cmd/ksh93/sh/timers.c
new file mode 100644
index 0000000..d9ace52
--- /dev/null
+++ b/src/cmd/ksh93/sh/timers.c
@@ -0,0 +1,248 @@
+/***********************************************************************
+* *
+* 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
+
+#include <ast.h>
+#include <sig.h>
+#include <error.h>
+#include "fault.h"
+#include "defs.h"
+#include "FEATURE/sigfeatures"
+#include "FEATURE/time"
+
+typedef struct _timer
+{
+ double wakeup;
+ double incr;
+ struct _timer *next;
+ void (*action)(void*);
+ void *handle;
+} Timer_t;
+
+#define IN_ADDTIMEOUT 1
+#define IN_SIGALRM 2
+#define DEFER_SIGALRM 4
+#define SIGALRM_CALL 8
+
+static Timer_t *tptop, *tpmin, *tpfree;
+static char time_state;
+
+static double getnow(void)
+{
+ register double now;
+#ifdef timeofday
+ struct timeval tp;
+ timeofday(&tp);
+ now = tp.tv_sec + 1.e-6*tp.tv_usec;
+
+#else
+ now = (double)time((time_t*)0);
+#endif /* timeofday */
+ return(now+.001);
+}
+
+/*
+ * set an alarm for <t> seconds
+ */
+static double setalarm(register double t)
+{
+#if defined(_lib_setitimer) && defined(ITIMER_REAL)
+ struct itimerval tnew, told;
+ tnew.it_value.tv_sec = t;
+ tnew.it_value.tv_usec = 1.e6*(t- (double)tnew.it_value.tv_sec);
+ if(t && tnew.it_value.tv_sec==0 && tnew.it_value.tv_usec<1000)
+ tnew.it_value.tv_usec = 1000;
+ tnew.it_interval.tv_sec = 0;
+ tnew.it_interval.tv_usec = 0;
+ if(setitimer(ITIMER_REAL,&tnew,&told) < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_alarm);
+ t = told.it_value.tv_sec + 1.e-6*told.it_value.tv_usec;
+#else
+ unsigned seconds = (unsigned)t;
+ if(t && seconds<1)
+ seconds=1;
+ t = (double)alarm(seconds);
+#endif
+ return(t);
+}
+
+/* signal handler for alarm call */
+static void sigalrm(int sig)
+{
+ register Timer_t *tp, *tplast, *tpold, *tpnext;
+ double now;
+ static double left;
+ NOT_USED(sig);
+ left = 0;
+ if(time_state&SIGALRM_CALL)
+ time_state &= ~SIGALRM_CALL;
+ else if(alarm(0))
+ sh_fault(SIGALRM|SH_TRAP);
+ if(time_state)
+ {
+ if(time_state&IN_ADDTIMEOUT)
+ time_state |= DEFER_SIGALRM;
+ errno = EINTR;
+ return;
+ }
+ time_state |= IN_SIGALRM;
+ sigrelease(SIGALRM);
+ while(1)
+ {
+ now = getnow();
+ tpold = tpmin = 0;
+ for(tplast=0,tp=tptop; tp; tp=tpnext)
+ {
+ tpnext = tp->next;
+ if(tp->action)
+ {
+ if(tp->wakeup <=now)
+ {
+ if(!tpold || tpold->wakeup>tp->wakeup)
+ tpold = tp;
+ }
+ else
+ {
+ if(!tpmin || tpmin->wakeup>tp->wakeup)
+ tpmin=tp;
+ }
+ tplast = tp;
+ }
+ else
+ {
+ if(tplast)
+ tplast->next = tp->next;
+ else
+ tptop = tp->next;
+ tp->next = tpfree;
+ tpfree = tp;
+ }
+ }
+ if((tp=tpold) && tp->incr)
+ {
+ while((tp->wakeup += tp->incr) <= now);
+ if(!tpmin || tpmin->wakeup>tp->wakeup)
+ tpmin=tp;
+ }
+ if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left))))
+ {
+ if(left==0)
+ signal(SIGALRM,sigalrm);
+ left = setalarm(tpmin->wakeup-now);
+ if(left && (now+left) < tpmin->wakeup)
+ setalarm(left);
+ else
+ left=tpmin->wakeup-now;
+ }
+ if(tp)
+ {
+ void (*action)(void*);
+ action = tp->action;
+ if(!tp->incr)
+ tp->action = 0;
+ errno = EINTR;
+ time_state &= ~IN_SIGALRM;
+ (*action)(tp->handle);
+ time_state |= IN_SIGALRM;
+ }
+ else
+ break;
+ }
+ if(!tpmin)
+ signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL);
+ time_state &= ~IN_SIGALRM;
+ errno = EINTR;
+}
+
+static void oldalrm(void *handle)
+{
+ Handler_t fn = *(Handler_t*)handle;
+ free(handle);
+ (*fn)(SIGALRM);
+}
+
+void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle)
+{
+ register Timer_t *tp;
+ double t;
+ Handler_t fn;
+ t = ((double)msec)/1000.;
+ if(t<=0 || !action)
+ return((void*)0);
+ if(tp=tpfree)
+ tpfree = tp->next;
+ else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t))))
+ return((void*)0);
+ tp->wakeup = getnow() + t;
+ tp->incr = (flags?t:0);
+ tp->action = action;
+ tp->handle = handle;
+ time_state |= IN_ADDTIMEOUT;
+ tp->next = tptop;
+ tptop = tp;
+ if(!tpmin || tp->wakeup < tpmin->wakeup)
+ {
+ tpmin = tp;
+ fn = (Handler_t)signal(SIGALRM,sigalrm);
+ if((t= setalarm(t))>0 && fn && fn!=(Handler_t)sigalrm)
+ {
+ Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t));
+ if(hp)
+ {
+ *hp = fn;
+ sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp);
+ }
+ }
+ tp = tptop;
+ }
+ else if(tpmin && !tpmin->action)
+ time_state |= DEFER_SIGALRM;
+ time_state &= ~IN_ADDTIMEOUT;
+ if(time_state&DEFER_SIGALRM)
+ {
+ time_state=SIGALRM_CALL;
+ sigalrm(SIGALRM);
+ if(tp!=tptop)
+ tp=0;
+ }
+ return((void*)tp);
+}
+
+/*
+ * delete timer <tp>. If <tp> is NULL, all timers are deleted
+ */
+void timerdel(void *handle)
+{
+ register Timer_t *tp = (Timer_t*)handle;
+ if(tp)
+ tp->action = 0;
+ else
+ {
+ for(tp=tptop; tp; tp=tp->next)
+ tp->action = 0;
+ if(tpmin)
+ {
+ tpmin = 0;
+ setalarm((double)0);
+ }
+ signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL);
+ }
+}
+
diff --git a/src/cmd/ksh93/sh/trestore.c b/src/cmd/ksh93/sh/trestore.c
new file mode 100644
index 0000000..c209b73
--- /dev/null
+++ b/src/cmd/ksh93/sh/trestore.c
@@ -0,0 +1,353 @@
+/***********************************************************************
+* *
+* 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
+/*
+ * David Korn
+ * AT&T Labs
+ *
+ * shell intermediate code reader
+ *
+ */
+
+#include "defs.h"
+#include "shnodes.h"
+#include "path.h"
+#include "io.h"
+#include <ccode.h>
+
+static struct dolnod *r_comlist(Shell_t*);
+static struct argnod *r_arg(Shell_t*);
+static struct ionod *r_redirect(Shell_t*);
+static struct regnod *r_switch(Shell_t*);
+static Shnode_t *r_tree(Shell_t*);
+static char *r_string(Stk_t*);
+static void r_comarg(Shell_t*,struct comnod*);
+
+static Sfio_t *infile;
+
+#define getnode(s,type) ((Shnode_t*)stkalloc((s),sizeof(struct type)))
+
+Shnode_t *sh_trestore(Shell_t *shp,Sfio_t *in)
+{
+ Shnode_t *t;
+ infile = in;
+ t = r_tree(shp);
+ return(t);
+}
+/*
+ * read in a shell tree
+ */
+static Shnode_t *r_tree(Shell_t *shp)
+{
+ long l = sfgetl(infile);
+ register int type;
+ register Shnode_t *t=0;
+ if(l<0)
+ return(t);
+ type = l;
+ switch(type&COMMSK)
+ {
+ case TTIME:
+ case TPAR:
+ t = getnode(shp->stk,parnod);
+ t->par.partre = r_tree(shp);
+ break;
+ case TCOM:
+ t = getnode(shp->stk,comnod);
+ t->tre.tretyp = type;
+ r_comarg(shp,(struct comnod*)t);
+ break;
+ case TSETIO:
+ case TFORK:
+ t = getnode(shp->stk,forknod);
+ t->fork.forkline = sfgetu(infile);
+ t->fork.forktre = r_tree(shp);
+ t->fork.forkio = r_redirect(shp);
+ break;
+ case TIF:
+ t = getnode(shp->stk,ifnod);
+ t->if_.iftre = r_tree(shp);
+ t->if_.thtre = r_tree(shp);
+ t->if_.eltre = r_tree(shp);
+ break;
+ case TWH:
+ t = getnode(shp->stk,whnod);
+ t->wh.whinc = (struct arithnod*)r_tree(shp);
+ t->wh.whtre = r_tree(shp);
+ t->wh.dotre = r_tree(shp);
+ break;
+ case TLST:
+ case TAND:
+ case TORF:
+ case TFIL:
+ t = getnode(shp->stk,lstnod);
+ t->lst.lstlef = r_tree(shp);
+ t->lst.lstrit = r_tree(shp);
+ break;
+ case TARITH:
+ t = getnode(shp->stk,arithnod);
+ t->ar.arline = sfgetu(infile);
+ t->ar.arexpr = r_arg(shp);
+ t->ar.arcomp = 0;
+ if((t->ar.arexpr)->argflag&ARG_RAW)
+ t->ar.arcomp = sh_arithcomp(shp,(t->ar.arexpr)->argval);
+ break;
+ case TFOR:
+ t = getnode(shp->stk,fornod);
+ t->for_.forline = 0;
+ if(type&FLINENO)
+ t->for_.forline = sfgetu(infile);
+ t->for_.fortre = r_tree(shp);
+ t->for_.fornam = r_string(shp->stk);
+ t->for_.forlst = (struct comnod*)r_tree(shp);
+ break;
+ case TSW:
+ t = getnode(shp->stk,swnod);
+ t->sw.swline = 0;
+ if(type&FLINENO)
+ t->sw.swline = sfgetu(infile);
+ t->sw.swarg = r_arg(shp);
+ if(type&COMSCAN)
+ t->sw.swio = r_redirect(shp);
+ else
+ t->sw.swio = 0;
+ t->sw.swlst = r_switch(shp);
+ break;
+ case TFUN:
+ {
+ Stak_t *savstak;
+ struct slnod *slp;
+ struct functnod *fp;
+ t = getnode(shp->stk,functnod);
+ t->funct.functloc = -1;
+ t->funct.functline = sfgetu(infile);
+ t->funct.functnam = r_string(shp->stk);
+ savstak = stakcreate(STAK_SMALL);
+ savstak = stakinstall(savstak, 0);
+ slp = (struct slnod*)stkalloc(shp->stk,sizeof(struct slnod)+sizeof(struct functnod));
+ slp->slchild = 0;
+ slp->slnext = shp->st.staklist;
+ shp->st.staklist = 0;
+ fp = (struct functnod*)(slp+1);
+ memset(fp, 0, sizeof(*fp));
+ fp->functtyp = TFUN|FAMP;
+ if(shp->st.filename)
+ fp->functnam = stkcopy(shp->stk,shp->st.filename);
+ t->funct.functtre = r_tree(shp);
+ t->funct.functstak = slp;
+ t->funct.functargs = (struct comnod*)r_tree(shp);
+ slp->slptr = stakinstall(savstak,0);
+ slp->slchild = shp->st.staklist;
+ break;
+ }
+ case TTST:
+ t = getnode(shp->stk,tstnod);
+ t->tst.tstline = sfgetu(infile);
+ if((type&TPAREN)==TPAREN)
+ t->lst.lstlef = r_tree(shp);
+ else
+ {
+ t->lst.lstlef = (Shnode_t*)r_arg(shp);
+ if((type&TBINARY))
+ t->lst.lstrit = (Shnode_t*)r_arg(shp);
+ }
+ }
+ if(t)
+ t->tre.tretyp = type;
+ return(t);
+}
+
+static struct argnod *r_arg(Shell_t *shp)
+{
+ register struct argnod *ap=0, *apold, *aptop=0;
+ register long l;
+ Stk_t *stkp=shp->stk;
+ while((l=sfgetu(infile))>0)
+ {
+ ap = (struct argnod*)stkseek(stkp,(unsigned)l+ARGVAL);
+ if(!aptop)
+ aptop = ap;
+ else
+ apold->argnxt.ap = ap;
+ if(--l > 0)
+ {
+ sfread(infile,ap->argval,(size_t)l);
+ ccmaps(ap->argval, l, CC_ASCII, CC_NATIVE);
+ }
+ ap->argval[l] = 0;
+ ap->argchn.cp = 0;
+ ap->argflag = sfgetc(infile);
+#if 0
+ if((ap->argflag&ARG_MESSAGE) && *ap->argval)
+ {
+ /* replace international messages */
+ sh_endword(shp,1);
+ ap->argflag &= ~ARG_MESSAGE;
+ if(!(ap->argflag&(ARG_MAC|ARG_EXP)))
+ ap = sh_endword(shp,0);
+ else
+ {
+ ap = (struct argnod*)stkfreeze(stkp,0);
+ if(ap->argflag==0)
+ ap->argflag = ARG_RAW;
+ }
+ }
+ else
+#endif
+ ap = (struct argnod*)stkfreeze(stkp,0);
+ if(*ap->argval==0 && (ap->argflag&ARG_EXP))
+ ap->argchn.ap = (struct argnod*)r_tree(shp);
+ else if(*ap->argval==0 && (ap->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED))==0)
+ {
+ struct fornod *fp = (struct fornod*)getnode(shp->stk,fornod);
+ fp->fortyp = sfgetu(infile);
+ fp->fortre = r_tree(shp);
+ fp->fornam = ap->argval+1;
+ ap->argchn.ap = (struct argnod*)fp;
+ }
+ apold = ap;
+ }
+ if(ap)
+ ap->argnxt.ap = 0;
+ return(aptop);
+}
+
+static struct ionod *r_redirect(Shell_t* shp)
+{
+ register long l;
+ register struct ionod *iop=0, *iopold, *ioptop=0;
+ while((l=sfgetl(infile))>=0)
+ {
+ iop = (struct ionod*)getnode(shp->stk,ionod);
+ if(!ioptop)
+ ioptop = iop;
+ else
+ iopold->ionxt = iop;
+ iop->iofile = l;
+ iop->ioname = r_string(shp->stk);
+ if(iop->iodelim = r_string(shp->stk))
+ {
+ iop->iosize = sfgetl(infile);
+ if(shp->heredocs)
+ iop->iooffset = sfseek(shp->heredocs,(off_t)0,SEEK_END);
+ else
+ {
+ shp->heredocs = sftmp(512);
+ iop->iooffset = 0;
+ }
+ sfmove(infile,shp->heredocs, iop->iosize, -1);
+ }
+ iopold = iop;
+ if(iop->iofile&IOVNM)
+ iop->iovname = r_string(shp->stk);
+ else
+ iop->iovname = 0;
+ iop->iofile &= ~IOVNM;
+ }
+ if(iop)
+ iop->ionxt = 0;
+ return(ioptop);
+}
+
+static void r_comarg(Shell_t *shp,struct comnod *com)
+{
+ char *cmdname=0;
+ com->comio = r_redirect(shp);
+ com->comset = r_arg(shp);
+ com->comstate = 0;
+ if(com->comtyp&COMSCAN)
+ {
+ com->comarg = r_arg(shp);
+ if(com->comarg->argflag==ARG_RAW)
+ cmdname = com->comarg->argval;
+ }
+ else if(com->comarg = (struct argnod*)r_comlist(shp))
+ cmdname = ((struct dolnod*)(com->comarg))->dolval[ARG_SPARE];
+ com->comline = sfgetu(infile);
+ com->comnamq = 0;
+ if(cmdname)
+ {
+ char *cp;
+ com->comnamp = (void*)nv_search(cmdname,shp->fun_tree,0);
+ if(com->comnamp && (cp =strrchr(cmdname+1,'.')))
+ {
+ *cp = 0;
+ com->comnamp = (void*)nv_open(cmdname,shp->var_tree,NV_VARNAME|NV_NOADD|NV_NOARRAY);
+ *cp = '.';
+ }
+ }
+ else
+ com->comnamp = 0;
+}
+
+static struct dolnod *r_comlist(Shell_t *shp)
+{
+ register struct dolnod *dol=0;
+ register long l;
+ register char **argv;
+ if((l=sfgetl(infile))>0)
+ {
+ dol = (struct dolnod*)stkalloc(shp->stk,sizeof(struct dolnod) + sizeof(char*)*(l+ARG_SPARE));
+ dol->dolnum = l;
+ dol->dolbot = ARG_SPARE;
+ argv = dol->dolval+ARG_SPARE;
+ while(*argv++ = r_string(shp->stk));
+ }
+ return(dol);
+}
+
+static struct regnod *r_switch(Shell_t *shp)
+{
+ register long l;
+ struct regnod *reg=0,*regold,*regtop=0;
+ while((l=sfgetl(infile))>=0)
+ {
+ reg = (struct regnod*)getnode(shp->stk,regnod);
+ if(!regtop)
+ regtop = reg;
+ else
+ regold->regnxt = reg;
+ reg->regflag = l;
+ reg->regptr = r_arg(shp);
+ reg->regcom = r_tree(shp);
+ regold = reg;
+ }
+ if(reg)
+ reg->regnxt = 0;
+ return(regtop);
+}
+
+static char *r_string(Stk_t *stkp)
+{
+ register Sfio_t *in = infile;
+ register unsigned long l = sfgetu(in);
+ register char *ptr;
+ if(l == 0)
+ return(NIL(char*));
+ ptr = stkalloc(stkp,(unsigned)l);
+ if(--l > 0)
+ {
+ if(sfread(in,ptr,(size_t)l)!=(size_t)l)
+ return(NIL(char*));
+ ccmaps(ptr, l, CC_ASCII, CC_NATIVE);
+ }
+ ptr[l] = 0;
+ return(ptr);
+}
diff --git a/src/cmd/ksh93/sh/waitevent.c b/src/cmd/ksh93/sh/waitevent.c
new file mode 100644
index 0000000..e868116
--- /dev/null
+++ b/src/cmd/ksh93/sh/waitevent.c
@@ -0,0 +1,54 @@
+/***********************************************************************
+* *
+* 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
+
+#include "defs.h"
+/*
+ * This installs a hook to allow the processing of events when
+ * the shell is waiting for input and when the shell is
+ * waiting for job completion.
+ * The previous waitevent hook function is returned
+ */
+
+
+void *sh_waitnotify(int(*newevent)(int,long,int))
+{
+ int (*old)(int,long,int);
+ old = shgd->waitevent;
+ shgd->waitevent = newevent;
+ return((void*)old);
+}
+
+#if __OBSOLETE__ < 20080101
+/*
+ * this used to be a private symbol
+ * retain the old name for a bit for a smooth transition
+ */
+
+#if defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+extern void *_sh_waitnotify(int(*newevent)(int,long,int))
+{
+ return sh_waitnotify(newevent);
+}
+
+#endif
diff --git a/src/cmd/ksh93/sh/xec.c b/src/cmd/ksh93/sh/xec.c
new file mode 100644
index 0000000..28ad1e1
--- /dev/null
+++ b/src/cmd/ksh93/sh/xec.c
@@ -0,0 +1,3991 @@
+/***********************************************************************
+* *
+* 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 parse tree executer
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <fcin.h>
+#include "variables.h"
+#include "path.h"
+#include "name.h"
+#include "io.h"
+#include "shnodes.h"
+#include "jobs.h"
+#include "test.h"
+#include "builtins.h"
+#include "FEATURE/time"
+#include "FEATURE/externs"
+#include "FEATURE/locale"
+#include "streval.h"
+
+#if !_std_malloc
+# include <vmalloc.h>
+#endif
+
+#if _lib_vfork
+# include <ast_vfork.h>
+#else
+# define vfork() fork()
+#endif
+
+#define SH_NTFORK SH_TIMING
+#define NV_BLTPFSH NV_ARRAY
+
+#if _lib_nice
+ extern int nice(int);
+#endif /* _lib_nice */
+#if !_lib_spawnveg
+# define spawnveg(a,b,c,d) spawnve(a,b,c)
+#endif /* !_lib_spawnveg */
+#if SHOPT_SPAWN
+ static pid_t sh_ntfork(Shell_t*,const Shnode_t*,char*[],int*,int);
+#endif /* SHOPT_SPAWN */
+
+static void sh_funct(Shell_t *,Namval_t*, int, char*[], struct argnod*,int);
+static int trim_eq(const char*, const char*);
+static void coproc_init(Shell_t*, int pipes[]);
+
+static void *timeout;
+static char pipejob;
+static char nopost;
+static int restorefd;
+
+struct funenv
+{
+ Namval_t *node;
+ struct argnod *env;
+ Namval_t **nref;
+};
+
+/* ======== command execution ========*/
+
+#if !SHOPT_DEVFD
+ static void fifo_check(void *handle)
+ {
+ Shell_t *shp = (Shell_t*)handle;
+ pid_t pid = getppid();
+ if(pid==1)
+ {
+ unlink(shp->fifo);
+ sh_done(shp,0);
+ }
+ }
+#endif /* !SHOPT_DEVFD */
+
+/*
+ * The following two functions allow command substituion for non-builtins
+ * to use a pipe and to wait for the pipe to close before restoring to a
+ * temp file.
+ */
+static int subpipe[3] = {-1};
+static int subdup,tsetio,usepipe;
+static void iousepipe(Shell_t *shp)
+{
+ int i;
+ usepipe++;
+ fcntl(subpipe[0],F_SETFD,FD_CLOEXEC);
+ subpipe[2] = fcntl(1,F_DUPFD,10);
+ fcntl(subpipe[2],F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[subpipe[2]] = shp->fdstatus[1];
+ close(1);
+ fcntl(subpipe[1],F_DUPFD,1);
+ shp->fdstatus[1] = shp->fdstatus[subpipe[1]];
+ sh_close(subpipe[1]);
+ if(subdup=shp->subdup) for(i=0; i < 10; i++)
+ {
+ if(subdup&(1<<i))
+ {
+ sh_close(i);
+ fcntl(1,F_DUPFD,i);
+ shp->fdstatus[i] = shp->fdstatus[1];
+ }
+ }
+}
+
+static void iounpipe(Shell_t *shp)
+{
+ int n;
+ char buff[SF_BUFSIZE];
+ usepipe = 0;
+ close(1);
+ fcntl(subpipe[2], F_DUPFD, 1);
+ shp->fdstatus[1] = shp->fdstatus[subpipe[2]];
+ if(subdup) for(n=0; n < 10; n++)
+ {
+ if(subdup&(1<<n))
+ {
+ sh_close(n);
+ fcntl(1, F_DUPFD, n);
+ shp->fdstatus[n] = shp->fdstatus[1];
+ }
+ }
+ shp->subdup = 0;
+ sh_close(subpipe[2]);
+ while((n = read(subpipe[0],buff,sizeof(buff)))!=0)
+ {
+ if(n>0)
+ sfwrite(sfstdout,buff,n);
+ else if(errno!=EINTR)
+ break;
+ }
+ sh_close(subpipe[0]);
+ subpipe[0] = -1;
+ tsetio = 0;
+}
+
+/*
+ * print time <t> in h:m:s format with precision <p>
+ */
+static void l_time(Sfio_t *outfile,register clock_t t,int p)
+{
+ register int min, sec, frac;
+ register int hr;
+ if(p)
+ {
+ frac = t%shgd->lim.clk_tck;
+ frac = (frac*100)/shgd->lim.clk_tck;
+ }
+ t /= shgd->lim.clk_tck;
+ sec = t%60;
+ t /= 60;
+ min = t%60;
+ if(hr=t/60)
+ sfprintf(outfile,"%dh",hr);
+ if(p)
+ sfprintf(outfile,"%dm%d%c%0*ds",min,sec,GETDECIMAL(0),p,frac);
+ else
+ sfprintf(outfile,"%dm%ds",min,sec);
+}
+
+static int p_time(Shell_t *shp, Sfio_t *out, const char *format, clock_t *tm)
+{
+ int c,p,l,n,offset = staktell();
+ const char *first;
+ double d;
+ Stk_t *stkp = shp->stk;
+ for(first=format ; c= *format; format++)
+ {
+ if(c!='%')
+ continue;
+ sfwrite(stkp, first, format-first);
+ n = l = 0;
+ p = 3;
+ if((c= *++format) == '%')
+ {
+ first = format;
+ continue;
+ }
+ if(c>='0' && c <='9')
+ {
+ p = (c>'3')?3:(c-'0');
+ c = *++format;
+ }
+ else if(c=='P')
+ {
+ if(d=tm[0])
+ d = 100.*(((double)(tm[1]+tm[2]))/d);
+ p = 2;
+ goto skip;
+ }
+ if(c=='l')
+ {
+ l = 1;
+ c = *++format;
+ }
+ if(c=='U')
+ n = 1;
+ else if(c=='S')
+ n = 2;
+ else if(c!='R')
+ {
+ stkseek(stkp,offset);
+ errormsg(SH_DICT,ERROR_exit(0),e_badtformat,c);
+ return(0);
+ }
+ d = (double)tm[n]/shp->gd->lim.clk_tck;
+ skip:
+ if(l)
+ l_time(stkp, tm[n], p);
+ else
+ sfprintf(stkp,"%.*f",p, d);
+ first = format+1;
+ }
+ if(format>first)
+ sfwrite(stkp,first, format-first);
+ sfputc(stkp,'\n');
+ n = stktell(stkp)-offset;
+ sfwrite(out,stkptr(stkp,offset),n);
+ stkseek(stkp,offset);
+ return(n);
+}
+
+#if SHOPT_OPTIMIZE
+/*
+ * clear argument pointers that point into the stack
+ */
+static int p_arg(struct argnod*,int);
+static int p_switch(struct regnod*);
+static int p_comarg(register struct comnod *com)
+{
+ Namval_t *np=com->comnamp;
+ int n = p_arg(com->comset,ARG_ASSIGN);
+ if(com->comarg && (com->comtyp&COMSCAN))
+ n+= p_arg(com->comarg,0);
+ if(com->comstate && np)
+ {
+ /* call builtin to cleanup state */
+ Shbltin_t *bp = &sh.bltindata;
+ void *save_ptr = bp->ptr;
+ void *save_data = bp->data;
+ bp->bnode = np;
+ bp->vnode = com->comnamq;
+ bp->ptr = nv_context(np);
+ bp->data = com->comstate;
+ bp->flags = SH_END_OPTIM;
+ ((Shbltin_f)funptr(np))(0,(char**)0, bp);
+ bp->ptr = save_ptr;
+ bp->data = save_data;
+ }
+ com->comstate = 0;
+ if(com->comarg && !np)
+ n++;
+ return(n);
+}
+
+extern void sh_optclear(Shell_t*, void*);
+
+static int sh_tclear(register Shnode_t *t)
+{
+ int n=0;
+ if(!t)
+ return(0);
+ switch(t->tre.tretyp&COMMSK)
+ {
+ case TTIME:
+ case TPAR:
+ return(sh_tclear(t->par.partre));
+ case TCOM:
+ return(p_comarg((struct comnod*)t));
+ case TSETIO:
+ case TFORK:
+ return(sh_tclear(t->fork.forktre));
+ case TIF:
+ n=sh_tclear(t->if_.iftre);
+ n+=sh_tclear(t->if_.thtre);
+ n+=sh_tclear(t->if_.eltre);
+ return(n);
+ case TWH:
+ if(t->wh.whinc)
+ n=sh_tclear((Shnode_t*)(t->wh.whinc));
+ n+=sh_tclear(t->wh.whtre);
+ n+=sh_tclear(t->wh.dotre);
+ return(n);
+ case TLST:
+ case TAND:
+ case TORF:
+ case TFIL:
+ n=sh_tclear(t->lst.lstlef);
+ return(n+sh_tclear(t->lst.lstrit));
+ case TARITH:
+ return(p_arg(t->ar.arexpr,ARG_ARITH));
+ case TFOR:
+ n=sh_tclear(t->for_.fortre);
+ return(n+sh_tclear((Shnode_t*)t->for_.forlst));
+ case TSW:
+ n=p_arg(t->sw.swarg,0);
+ return(n+p_switch(t->sw.swlst));
+ case TFUN:
+ n=sh_tclear(t->funct.functtre);
+ return(n+sh_tclear((Shnode_t*)t->funct.functargs));
+ case TTST:
+ if((t->tre.tretyp&TPAREN)==TPAREN)
+ return(sh_tclear(t->lst.lstlef));
+ else
+ {
+ n=p_arg(&(t->lst.lstlef->arg),0);
+ if(t->tre.tretyp&TBINARY)
+ n+=p_arg(&(t->lst.lstrit->arg),0);
+ }
+ }
+ return(n);
+}
+
+static int p_arg(register struct argnod *arg,int flag)
+{
+ while(arg)
+ {
+ if(strlen(arg->argval) || (arg->argflag==ARG_RAW))
+ arg->argchn.ap = 0;
+ else if(flag==0)
+ sh_tclear((Shnode_t*)arg->argchn.ap);
+ else
+ sh_tclear(((struct fornod*)arg->argchn.ap)->fortre);
+ arg = arg->argnxt.ap;
+ }
+ return(0);
+}
+
+static int p_switch(register struct regnod *reg)
+{
+ int n=0;
+ while(reg)
+ {
+ n+=p_arg(reg->regptr,0);
+ n+=sh_tclear(reg->regcom);
+ reg = reg->regnxt;
+ }
+ return(n);
+}
+# define OPTIMIZE_FLAG (ARG_OPTIMIZE)
+# define OPTIMIZE (flags&OPTIMIZE_FLAG)
+#else
+# define OPTIMIZE_FLAG (0)
+# define OPTIMIZE (0)
+# define sh_tclear(x)
+#endif /* SHOPT_OPTIMIZE */
+
+static void out_pattern(Sfio_t *iop, register const char *cp, int n)
+{
+ register int c;
+ do
+ {
+ switch(c= *cp)
+ {
+ case 0:
+ if(n<0)
+ return;
+ c = n;
+ break;
+ case '\n':
+ sfputr(iop,"$'\\n",'\'');
+ continue;
+ case '\\':
+ if (!(c = *++cp))
+ c = '\\';
+ /*FALLTHROUGH*/
+ case ' ':
+ case '<': case '>': case ';':
+ case '$': case '`': case '\t':
+ sfputc(iop,'\\');
+ break;
+ }
+ sfputc(iop,c);
+ }
+ while(*cp++);
+}
+
+static void out_string(Sfio_t *iop, register const char *cp, int c, int quoted)
+{
+ if(quoted)
+ {
+ int n = stktell(stkstd);
+ cp = sh_fmtq(cp);
+ if(iop==stkstd && cp==stkptr(stkstd,n))
+ {
+ *stkptr(stkstd,stktell(stkstd)-1) = c;
+ return;
+ }
+ }
+ sfputr(iop,cp,c);
+}
+
+struct Level
+{
+ Namfun_t hdr;
+ short maxlevel;
+};
+
+/*
+ * this is for a debugger but it hasn't been tested yet
+ * if a debug script sets .sh.level it should set up the scope
+ * as if you were executing in that level
+ */
+static void put_level(Namval_t* np,const char *val,int flags,Namfun_t *fp)
+{
+ Shscope_t *sp;
+ struct Level *lp = (struct Level*)fp;
+ int16_t level, oldlevel = (int16_t)nv_getnum(np);
+ nv_putv(np,val,flags,fp);
+ if(!val)
+ {
+ fp = nv_stack(np, NIL(Namfun_t*));
+ if(fp && !fp->nofree)
+ free((void*)fp);
+ return;
+ }
+ level = nv_getnum(np);
+ if(level<0 || level > lp->maxlevel)
+ {
+ nv_putv(np, (char*)&oldlevel, NV_INT16, fp);
+ /* perhaps this should be an error */
+ return;
+ }
+ if(level==oldlevel)
+ return;
+ if(sp = sh_getscope(level,SEEK_SET))
+ {
+ sh_setscope(sp);
+ error_info.id = sp->cmdname;
+
+ }
+}
+
+static const Namdisc_t level_disc = { sizeof(struct Level), put_level };
+
+static struct Level *init_level(Shell_t *shp,int level)
+{
+ struct Level *lp = newof(NiL,struct Level,1,0);
+ lp->maxlevel = level;
+ _nv_unset(SH_LEVELNOD,0);
+ nv_onattr(SH_LEVELNOD,NV_INT16|NV_NOFREE);
+ shp->last_root = nv_dict(DOTSHNOD);
+ nv_putval(SH_LEVELNOD,(char*)&lp->maxlevel,NV_INT16);
+ lp->hdr.disc = &level_disc;
+ nv_disc(SH_LEVELNOD,&lp->hdr,NV_FIRST);
+ return(lp);
+}
+
+/*
+ * write the current command on the stack and make it available as .sh.command
+ */
+int sh_debug(Shell_t *shp, const char *trap, const char *name, const char *subscript, char *const argv[], int flags)
+{
+ Stk_t *stkp=shp->stk;
+ struct sh_scoped savst;
+ Namval_t *np = SH_COMMANDNOD;
+ char *sav = stkptr(stkp,0);
+ int n=4, offset=stktell(stkp);
+ const char *cp = "+=( ";
+ Sfio_t *iop = stkstd;
+ short level;
+ if(shp->indebug)
+ return(0);
+ shp->indebug = 1;
+ if(name)
+ {
+ sfputr(iop,name,-1);
+ if(subscript)
+ {
+ sfputc(iop,'[');
+ out_string(iop,subscript,']',1);
+ }
+ if(!(flags&ARG_APPEND))
+ cp+=1, n-=1;
+ if(!(flags&ARG_ASSIGN))
+ n -= 2;
+ sfwrite(iop,cp,n);
+ }
+ if(*argv && !(flags&ARG_RAW))
+ out_string(iop, *argv++,' ', 0);
+ n = (flags&ARG_ARITH);
+ while(cp = *argv++)
+ {
+ if((flags&ARG_EXP) && argv[1]==0)
+ out_pattern(iop, cp,' ');
+ else
+ out_string(iop, cp,' ',n?0: (flags&(ARG_RAW|ARG_NOGLOB))||*argv);
+ }
+ if(flags&ARG_ASSIGN)
+ sfputc(iop,')');
+ else if(iop==stkstd)
+ *stkptr(stkp,stktell(stkp)-1) = 0;
+ np->nvalue.cp = stkfreeze(stkp,1);
+ /* now setup .sh.level variable */
+ shp->st.lineno = error_info.line;
+ level = shp->fn_depth+shp->dot_depth;
+ shp->last_root = nv_dict(DOTSHNOD);
+ if(!SH_LEVELNOD->nvfun || !SH_LEVELNOD->nvfun->disc || nv_isattr(SH_LEVELNOD,NV_INT16|NV_NOFREE)!=(NV_INT16|NV_NOFREE))
+ init_level(shp,level);
+ else
+ nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16);
+ savst = shp->st;
+ shp->st.trap[SH_DEBUGTRAP] = 0;
+ n = sh_trap(trap,0);
+ np->nvalue.cp = 0;
+ shp->indebug = 0;
+ if(shp->st.cmdname)
+ error_info.id = shp->st.cmdname;
+ nv_putval(SH_PATHNAMENOD,shp->st.filename,NV_NOFREE);
+ nv_putval(SH_FUNNAMENOD,shp->st.funname,NV_NOFREE);
+ shp->st = savst;
+ if(sav != stkptr(stkp,0))
+ stkset(stkp,sav,0);
+ else
+ stkseek(stkp,offset);
+ return(n);
+}
+
+/*
+ * Given stream <iop> compile and execute
+ */
+int sh_eval(register Sfio_t *iop, int mode)
+{
+ register Shnode_t *t;
+ Shell_t *shp = sh_getinterp();
+ struct slnod *saveslp = shp->st.staklist;
+ int jmpval;
+ struct checkpt *pp = (struct checkpt*)shp->jmplist;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ static Sfio_t *io_save;
+ volatile int traceon=0, lineno=0;
+ int binscript=shp->binscript;
+ char comsub = shp->comsub;
+ io_save = iop; /* preserve correct value across longjmp */
+ shp->binscript = 0;
+ shp->comsub = 0;
+#define SH_TOPFUN 0x8000 /* this is a temporary tksh hack */
+ if (mode & SH_TOPFUN)
+ {
+ mode ^= SH_TOPFUN;
+ shp->fn_reset = 1;
+ }
+ sh_pushcontext(shp,buffp,SH_JMPEVAL);
+ buffp->olist = pp->olist;
+ jmpval = sigsetjmp(buffp->buff,0);
+ while(jmpval==0)
+ {
+ if(mode&SH_READEVAL)
+ {
+ lineno = shp->inlineno;
+ if(traceon=sh_isoption(SH_XTRACE))
+ sh_offoption(SH_XTRACE);
+ }
+ t = (Shnode_t*)sh_parse(shp,iop,(mode&(SH_READEVAL|SH_FUNEVAL))?mode&SH_FUNEVAL:SH_NL);
+ if(!(mode&SH_FUNEVAL) || !sfreserve(iop,0,0))
+ {
+ if(!(mode&SH_READEVAL))
+ sfclose(iop);
+ io_save = 0;
+ mode &= ~SH_FUNEVAL;
+ }
+ mode &= ~SH_READEVAL;
+ if(!sh_isoption(SH_VERBOSE))
+ sh_offstate(SH_VERBOSE);
+ if((mode&~SH_FUNEVAL) && shp->gd->hist_ptr)
+ {
+ hist_flush(shp->gd->hist_ptr);
+ mode = sh_state(SH_INTERACTIVE);
+ }
+ sh_exec(t,sh_isstate(SH_ERREXIT)|sh_isstate(SH_NOFORK)|(mode&~SH_FUNEVAL));
+ if(!(mode&SH_FUNEVAL))
+ break;
+ }
+ sh_popcontext(shp,buffp);
+ shp->binscript = binscript;
+ shp->comsub = comsub;
+ if(traceon)
+ sh_onoption(SH_XTRACE);
+ if(lineno)
+ shp->inlineno = lineno;
+ if(io_save)
+ sfclose(io_save);
+ sh_freeup(shp);
+ shp->st.staklist = saveslp;
+ shp->fn_reset = 0;
+ if(jmpval>SH_JMPEVAL)
+ siglongjmp(*shp->jmplist,jmpval);
+ return(shp->exitval);
+}
+
+/*
+ * returns 1 when option -<c> is specified
+ */
+static int checkopt(char *argv[], int c)
+{
+ char *cp;
+ while(cp = *++argv)
+ {
+ if(*cp=='+')
+ continue;
+ if(*cp!='-' || cp[1]=='-')
+ break;
+ if(strchr(++cp,c))
+ return(1);
+ if(*cp=='h' && cp[1]==0 && *++argv==0)
+ break;
+ }
+ return(0);
+}
+
+static void free_list(struct openlist *olist)
+{
+ struct openlist *item,*next;
+ for(item=olist;item;item=next)
+ {
+ next = item->next;
+ free((void*)item);
+ }
+}
+
+/*
+ * set ${.sh.name} and ${.sh.subscript}
+ * set _ to reference for ${.sh.name}[$.sh.subscript]
+ */
+static int set_instance(Shell_t *shp,Namval_t *nq, Namval_t *node, struct Namref *nr)
+{
+ char *sp=0,*cp;
+ Namarr_t *ap;
+ Namval_t *np;
+ if(!nv_isattr(nq,NV_MINIMAL|NV_EXPORT|NV_ARRAY) && (np=(Namval_t*)nq->nvenv) && nv_isarray(np))
+ nq = np;
+ cp = nv_name(nq);
+ memset(nr,0,sizeof(*nr));
+ nr->np = nq;
+ nr->root = shp->var_tree;
+ nr->table = shp->last_table;
+#if SHOPT_NAMESPACE
+ if(!nr->table && shp->namespace)
+ nr->table = shp->namespace;
+#endif /* SHOPT_NAMESPACE */
+ shp->instance = 1;
+ if((ap=nv_arrayptr(nq)) && (sp = nv_getsub(nq)))
+ sp = strdup(sp);
+ shp->instance = 0;
+ if(shp->var_tree!=shp->var_base && !nv_search((char*)nq,nr->root,HASH_BUCKET|HASH_NOSCOPE))
+ {
+#if SHOPT_NAMESPACE
+ nr->root = shp->namespace?nv_dict(shp->namespace):shp->var_base;
+#else
+ nr->root = shp->var_base;
+#endif /* SHOPT_NAMESPACE */
+ }
+ nv_putval(SH_NAMENOD, cp, NV_NOFREE);
+ memcpy(node,L_ARGNOD,sizeof(*node));
+ L_ARGNOD->nvalue.nrp = nr;
+ L_ARGNOD->nvflag = NV_REF|NV_NOFREE;
+ L_ARGNOD->nvfun = 0;
+ L_ARGNOD->nvenv = 0;
+ if(sp)
+ {
+ nv_putval(SH_SUBSCRNOD,nr->sub=sp,NV_NOFREE);
+ return(ap->nelem&ARRAY_SCAN);
+ }
+ return(0);
+}
+
+static void unset_instance(Namval_t *nq, Namval_t *node, struct Namref *nr,long mode)
+{
+ L_ARGNOD->nvalue.nrp = node->nvalue.nrp;
+ L_ARGNOD->nvflag = node->nvflag;
+ L_ARGNOD->nvfun = node->nvfun;
+ if(nr->sub)
+ {
+ nv_putsub(nr->np, nr->sub, mode);
+ free((void*)nr->sub);
+ }
+ _nv_unset(SH_NAMENOD,0);
+ _nv_unset(SH_SUBSCRNOD,0);
+}
+
+#if SHOPT_COSHELL
+uintmax_t coused;
+/*
+ * print out function definition
+ */
+static void print_fun(register Namval_t* np, void *data)
+{
+ register char *format;
+ NOT_USED(data);
+ if(!is_afunction(np) || !np->nvalue.ip)
+ return;
+ if(nv_isattr(np,NV_FPOSIX))
+ format="%s()\n{ ";
+ else
+ format="function %s\n{ ";
+ sfprintf(sfstdout,format,nv_name(np));
+ sh_deparse(sfstdout,(Shnode_t*)(nv_funtree(np)),0);
+ sfwrite(sfstdout,"}\n",2);
+}
+
+static void *sh_coinit(Shell_t *shp,char **argv)
+{
+ struct cosh *csp = job.colist;
+ const char *name = argv?argv[0]:0;
+ int id, open=1;
+ if(!name)
+ return(0);
+ if(*name=='-')
+ {
+ name++;
+ open=0;
+ }
+ nv_open(name,shp->var_tree,NV_IDENT|NV_NOADD);
+ while(csp)
+ {
+ if(strcmp(name,csp->name)==0)
+ {
+ if(open)
+ {
+ coattr(csp->coshell,argv[1]);
+ return((void*)csp);
+ }
+ coclose(csp->coshell);
+ return(0);
+ }
+ csp = csp->next;
+ }
+ if(!open)
+ errormsg(SH_DICT,ERROR_exit(1),"%s: unknown namespace",name);
+ environ[0][2]=0;
+ csp = newof(0,struct cosh,1,strlen(name)+1);
+ if(!(csp->coshell = coopen(NULL,CO_SHELL|CO_SILENT,argv[1])))
+ {
+ free((void*)csp);
+ errormsg(SH_DICT,ERROR_exit(1),"%s: unable to create namespace",name);
+ }
+ csp->coshell->data = (void*)csp;
+ csp->name = (char*)(csp+1);
+ strcpy(csp->name,name);
+ for(id=0; coused&(1<<id); id++);
+ coused |= (1<<id);
+ csp->id = id;
+ csp->next = job.colist;
+ job.colist = csp;
+ return((void*)csp);
+}
+
+int sh_coaddfile(Shell_t *shp, char *name)
+{
+ Namval_t *np = dtmatch(shp->inpool,name);
+ if(!np)
+ {
+ np = (Namval_t*)stakalloc(sizeof(Dtlink_t)+sizeof(char*));
+ np->nvname = name;
+ (Namval_t*)dtinsert(shp->inpool,np);
+ shp->poolfiles++;
+ return(1);
+ }
+ return(0);
+}
+
+static int sh_coexec(Shell_t *shp,const Shnode_t *t, int filt)
+{
+ struct cosh *csp = ((struct cosh*)shp->coshell);
+ Cojob_t *cjp;
+ char *str,*trap,host[PATH_MAX];
+ int lineno,sig,trace = sh_isoption(SH_XTRACE);
+ int verbose = sh_isoption(SH_VERBOSE);
+ sh_offoption(SH_XTRACE);
+ sh_offoption(SH_VERBOSE);
+ if(!shp->strbuf2)
+ shp->strbuf2 = sfstropen();
+ sfswap(shp->strbuf2,sfstdout);
+ sh_trap("typeset -p\nprint cd \"$PWD\"\nprint .sh.dollar=$$\nprint umask $(umask)",0);
+ for(sig=shp->st.trapmax;--sig>0;)
+ {
+ if((trap=shp->st.trapcom[sig]) && *trap==0)
+ sfprintf(sfstdout,"trap '' %d\n",sig);
+ }
+ if(t->tre.tretyp==TFIL)
+ lineno = ((struct forknod*)t->lst.lstlef)->forkline;
+ else
+ lineno = t->fork.forkline;
+ if(filt)
+ {
+ if(gethostname(host,sizeof(host)) < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_pipe);
+ if(shp->inpipe[2]>=20000)
+ sfprintf(sfstdout,"command exec < /dev/tcp/%s/%d || print -u2 'cannot create pipe'\n",host,shp->inpipe[2]);
+ sfprintf(sfstdout,"command exec > /dev/tcp/%s/%d || print -u2 'cannot create pipe'\n",host,shp->outpipe[2]);
+ if(filt==3)
+ t = t->fork.forktre;
+ }
+ else
+ t = t->fork.forktre;
+ nv_scan(shp->fun_tree, print_fun, (void*)0,0, 0);
+ if(1)
+ {
+ Dt_t *top = shp->var_tree;
+ sh_scope(shp,(struct argnod*)0,0);
+ shp->inpool = dtopen(&_Nvdisc,Dtset);
+ sh_exec(t,filt==1||filt==2?SH_NOFORK:0);
+ if(shp->poolfiles)
+ {
+ Namval_t *np;
+ sfprintf(sfstdout,"[[ ${.sh} == *pool* ]] && .sh.pool.files=(\n");
+ for(np=(Namval_t*)dtfirst(shp->inpool);np;np=(Namval_t*)dtnext(shp->inpool,np))
+ {
+ sfprintf(sfstdout,"\t%s\n",sh_fmtq(np->nvname));
+ }
+ sfputr(sfstdout,")",'\n');
+ ;
+ }
+ dtclose(shp->inpool);
+ shp->inpool = 0;
+ shp->poolfiles = 0;
+ sh_unscope(shp);
+ shp->var_tree = top;
+ }
+ sfprintf(sfstdout,"typeset -f .sh.pool.init && .sh.pool.init\n");
+ sfprintf(sfstdout,"LINENO=%d\n",lineno);
+ if(trace)
+ sh_onoption(SH_XTRACE);
+ if(verbose)
+ sh_onoption(SH_VERBOSE);
+ sh_trap("set +o",0);
+ sh_deparse(sfstdout,t,filt==1||filt==2?FALTPIPE:0);
+ sfputc(sfstdout,0);
+ sfswap(shp->strbuf2,sfstdout);
+ str = sfstruse(shp->strbuf2);
+ if(cjp=coexec(csp->coshell,str,0,NULL,NULL,NULL))
+ {
+ csp->cojob = cjp;
+ cjp->local = shp->coshell;
+ if(filt)
+ {
+ if(filt>1)
+ sh_coaccept(shp,shp->inpipe,1);
+ sh_coaccept(shp,shp->outpipe,0);
+ if(filt > 2)
+ {
+ shp->coutpipe = shp->inpipe[1];
+ shp->fdptrs[shp->coutpipe] = &shp->coutpipe;
+ }
+ }
+ return(sh_copid(csp));
+ }
+ return(-1);
+}
+#endif /*SHOPT_COSHELL*/
+
+int sh_exec(register const Shnode_t *t, int flags)
+{
+ register Shell_t *shp = sh_getinterp();
+ Stk_t *stkp = shp->stk;
+ sh_sigcheck(shp);
+ if(t && !shp->st.execbrk && !sh_isoption(SH_NOEXEC))
+ {
+ register int type = flags;
+ register char *com0 = 0;
+ int errorflg = (type&sh_state(SH_ERREXIT))|OPTIMIZE;
+ int execflg = (type&sh_state(SH_NOFORK));
+ int execflg2 = (type&sh_state(SH_FORKED));
+ int mainloop = (type&sh_state(SH_INTERACTIVE));
+#if SHOPT_AMP || SHOPT_SPAWN
+ int ntflag = (type&sh_state(SH_NTFORK));
+#else
+ int ntflag = 0;
+#endif
+ int topfd = shp->topfd;
+ char *sav=stkptr(stkp,0);
+ char *cp=0, **com=0, *comn;
+ int argn;
+ int skipexitset = 0;
+ int was_interactive = 0;
+ int was_errexit = sh_isstate(SH_ERREXIT);
+ int was_monitor = sh_isstate(SH_MONITOR);
+ int echeck = 0;
+ if(flags&sh_state(SH_INTERACTIVE))
+ {
+ if(pipejob==2)
+ job_unlock();
+ pipejob = 0;
+ job.curpgid = 0;
+ job.curjobid = 0;
+ flags &= ~sh_state(SH_INTERACTIVE);
+ }
+ sh_offstate(SH_ERREXIT);
+ sh_offstate(SH_DEFPATH);
+ if(was_errexit&flags)
+ sh_onstate(SH_ERREXIT);
+ if(was_monitor&flags)
+ sh_onstate(SH_MONITOR);
+ type = t->tre.tretyp;
+ if(!shp->intrap)
+ shp->oldexit=shp->exitval;
+ shp->exitval=0;
+ shp->lastsig = 0;
+ shp->lastpath = 0;
+ switch(type&COMMSK)
+ {
+ case TCOM:
+ {
+ register struct argnod *argp;
+ char *trap;
+ Namval_t *np, *nq, *last_table;
+ struct ionod *io;
+ int command=0, flgs=NV_ASSIGN;
+ shp->bltindata.invariant = type>>(COMBITS+2);
+ type &= (COMMSK|COMSCAN);
+ sh_stats(STAT_SCMDS);
+ error_info.line = t->com.comline-shp->st.firstline;
+ com = sh_argbuild(shp,&argn,&(t->com),OPTIMIZE);
+ echeck = 1;
+ if(t->tre.tretyp&COMSCAN)
+ {
+ argp = t->com.comarg;
+ if(argp && *com && !(argp->argflag&ARG_RAW))
+ sh_sigcheck(shp);
+ }
+ np = (Namval_t*)(t->com.comnamp);
+ nq = (Namval_t*)(t->com.comnamq);
+ com0 = com[0];
+ shp->xargexit = 0;
+ while(np==SYSCOMMAND)
+ {
+ register int n = b_command(0,com,&shp->bltindata);
+ if(n==0)
+ break;
+ command += n;
+ np = 0;
+ if(!(com0= *(com+=n)))
+ break;
+ np = nv_bfsearch(com0, shp->bltin_tree, &nq, &cp);
+ }
+ if(shp->xargexit)
+ {
+ shp->xargmin -= command;
+ shp->xargmax -= command;
+ }
+ else
+ shp->xargmin = 0;
+ argn -= command;
+#if SHOPT_COSHELL
+ if(argn && shp->inpool)
+ {
+ if(io=t->tre.treio)
+ sh_redirect(shp,io,0);
+ if(!np || !is_abuiltin(np) || *np->nvname=='/' || np==SYSCD)
+ {
+ char **argv, *cp;
+ for(argv=com+1; cp= *argv; argv++)
+ {
+ if(cp && *cp && *cp!='-')
+ sh_coaddfile(shp,*argv);
+ }
+ break;
+ }
+ if(np->nvalue.bfp!=SYSTYPESET->nvalue.bfp)
+ break;
+ }
+ if(t->tre.tretyp&FAMP)
+ {
+ shp->coshell = sh_coinit(shp,com);
+ com0 = 0;
+ break;
+ }
+#endif /* SHOPT_COSHELL */
+ if(np && is_abuiltin(np))
+ {
+ if(!command)
+ {
+ Namval_t *mp;
+#if SHOPT_NAMESPACE
+ if(shp->namespace && (mp=sh_fsearch(shp,np->nvname,0)))
+ np = mp;
+ else
+#endif /* SHOPT_NAMESPACE */
+ np = dtsearch(shp->fun_tree,np);
+ }
+#if SHOPT_PFSH
+ if(sh_isoption(SH_PFSH) && nv_isattr(np,NV_BLTINOPT) && !nv_isattr(np,NV_BLTPFSH))
+ {
+ if(path_xattr(shp,np->nvname,(char*)0))
+ {
+ dtdelete(shp->bltin_tree,np);
+ np = 0;
+ }
+ else
+ nv_onattr(np,NV_BLTPFSH);
+
+ }
+#endif /* SHOPT_PFSH */
+ }
+ if(com0)
+ {
+ if(!np && !strchr(com0,'/'))
+ {
+ Dt_t *root = command?shp->bltin_tree:shp->fun_tree;
+ np = nv_bfsearch(com0, root, &nq, &cp);
+#if SHOPT_NAMESPACE
+ if(shp->namespace && !nq && !cp)
+ np = sh_fsearch(shp,com0,0);
+#endif /* SHOPT_NAMESPACE */
+ }
+ comn = com[argn-1];
+ }
+ io = t->tre.treio;
+ if(shp->envlist = argp = t->com.comset)
+ {
+ if(argn==0 || (np && nv_isattr(np,(BLT_DCL|BLT_SPC))))
+ {
+ Namval_t *tp=0;
+ if(argn)
+ {
+ if(checkopt(com,'A'))
+ flgs |= NV_ARRAY;
+ else if(checkopt(com,'a'))
+ flgs |= NV_IARRAY;
+ }
+#if SHOPT_BASH
+ if(np==SYSLOCAL)
+ {
+ if(!nv_getval(SH_FUNNAMENOD))
+ errormsg(SH_DICT,ERROR_exit(1),"%s: can only be used in a function",com0);
+ if(!shp->st.var_local)
+ {
+ sh_scope(shp,(struct argnod*)0,0);
+ shp->st.var_local = shp->var_tree;
+ }
+
+ }
+#endif /* SHOPT_BASH */
+ if(np==SYSTYPESET || (np && np->nvalue.bfp==SYSTYPESET->nvalue.bfp))
+ {
+ if(np!=SYSTYPESET)
+ {
+ shp->typeinit = np;
+ tp = nv_type(np);
+ }
+ if(checkopt(com,'C'))
+ flgs |= NV_COMVAR;
+ if(checkopt(com,'S'))
+ flgs |= NV_STATIC;
+ if(checkopt(com,'m'))
+ flgs |= NV_MOVE;
+ if(checkopt(com,'n'))
+ flgs |= NV_NOREF;
+ else if(!shp->typeinit && (checkopt(com,'L') || checkopt(com,'R') || checkopt(com,'Z')))
+ flgs |= NV_UNJUST;
+#if SHOPT_TYPEDEF
+ else if(argn>=3 && checkopt(com,'T'))
+ {
+# if SHOPT_NAMESPACE
+ if(shp->namespace)
+ {
+ if(!shp->strbuf2)
+ shp->strbuf2 = sfstropen();
+ sfprintf(shp->strbuf2,"%s%s%c",NV_CLASS,nv_name(shp->namespace),0);
+ shp->prefix = strdup(sfstruse(shp->strbuf2));
+ nv_open(shp->prefix,shp->var_base,NV_VARNAME);
+ }
+ else
+# endif /* SHOPT_NAMESPACE */
+ shp->prefix = NV_CLASS;
+ flgs |= NV_TYPE;
+
+ }
+#endif /* SHOPT_TYPEDEF */
+ if((shp->fn_depth && !shp->prefix) || np==SYSLOCAL)
+ flgs |= NV_NOSCOPE;
+ }
+ else if(np==SYSEXPORT)
+ flgs |= NV_EXPORT;
+ if(flgs&(NV_EXPORT|NV_NOREF))
+ flgs |= NV_IDENT;
+ else
+ flgs |= NV_VARNAME;
+#if 0
+ if(OPTIMIZE)
+ flgs |= NV_TAGGED;
+#endif
+ nv_setlist(argp,flgs,tp);
+ if(np==shp->typeinit)
+ shp->typeinit = 0;
+ shp->envlist = argp;
+ argp = NULL;
+ }
+ }
+ last_table = shp->last_table;
+ shp->last_table = 0;
+ if((io||argn))
+ {
+ Shbltin_t *bp=0;
+ static char *argv[1];
+ int tflags = 1;
+ if(np && nv_isattr(np,BLT_DCL))
+ tflags |= 2;
+ if(argn==0)
+ {
+ /* fake 'true' built-in */
+ np = SYSTRUE;
+ *argv = nv_name(np);
+ com = argv;
+ }
+ /* set +x doesn't echo */
+ else if((t->tre.tretyp&FSHOWME) && sh_isoption(SH_SHOWME))
+ {
+ int ison = sh_isoption(SH_XTRACE);
+ if(!ison)
+ sh_onoption(SH_XTRACE);
+ sh_trace(shp,com-command,tflags);
+ if(io)
+ sh_redirect(shp,io,SH_SHOWME);
+ if(!ison)
+ sh_offoption(SH_XTRACE);
+ break;
+ }
+ else if((np!=SYSSET) && sh_isoption(SH_XTRACE))
+ sh_trace(shp,com-command,tflags);
+ if(trap=shp->st.trap[SH_DEBUGTRAP])
+ {
+ int n = sh_debug(shp,trap,(char*)0,(char*)0, com, ARG_RAW);
+ if(n==255 && shp->fn_depth+shp->dot_depth)
+ {
+ np = SYSRETURN;
+ argn = 1;
+ com[0] = np->nvname;
+ com[1] = 0;
+ io = 0;
+ argp = 0;
+ }
+ else if(n==2)
+ break;
+ }
+ if(io)
+ sfsync(shp->outpool);
+ shp->lastpath = 0;
+ if(!np && !strchr(com0,'/'))
+ {
+ if(path_search(shp,com0,NIL(Pathcomp_t**),1))
+ {
+ error_info.line = t->com.comline-shp->st.firstline;
+#if SHOPT_NAMESPACE
+ if(!shp->namespace || !(np=sh_fsearch(shp,com0,0)))
+#endif /* SHOPT_NAMESPACE */
+ np=nv_search(com0,shp->fun_tree,0);
+ if(!np || !np->nvalue.ip)
+ {
+ Namval_t *mp=nv_search(com0,shp->bltin_tree,0);
+ if(mp)
+ np = mp;
+ }
+ }
+ else
+ {
+ if((np=nv_search(com0,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && np->nvalue.cp)
+ np=nv_search(nv_getval(np),shp->bltin_tree,0);
+ else
+ np = 0;
+ }
+ }
+ if(np && pipejob==2)
+ {
+ job_unlock();
+ pipejob = 1;
+ }
+ /* check for builtins */
+ if(np && is_abuiltin(np))
+ {
+ volatile int scope=0, share=0;
+ volatile void *save_ptr;
+ volatile void *save_data;
+ int jmpval, save_prompt;
+ int was_nofork = execflg?sh_isstate(SH_NOFORK):0;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ unsigned long was_vi=0, was_emacs=0, was_gmacs=0;
+ struct stat statb;
+ bp = &shp->bltindata;
+ save_ptr = bp->ptr;
+ save_data = bp->data;
+ memset(&statb, 0, sizeof(struct stat));
+ if(strchr(nv_name(np),'/'))
+ {
+ /*
+ * disable editors for built-in
+ * versions of commands on PATH
+ */
+ was_vi = sh_isoption(SH_VI);
+ was_emacs = sh_isoption(SH_EMACS);
+ was_gmacs = sh_isoption(SH_GMACS);
+ sh_offoption(SH_VI);
+ sh_offoption(SH_EMACS);
+ sh_offoption(SH_GMACS);
+ }
+ if(execflg)
+ sh_onstate(SH_NOFORK);
+ sh_pushcontext(shp,buffp,SH_JMPCMD);
+ jmpval = sigsetjmp(buffp->buff,1);
+ if(jmpval == 0)
+ {
+ if(!(nv_isattr(np,BLT_ENV)))
+ error_info.flags |= ERROR_SILENT;
+ errorpush(&buffp->err,0);
+ if(io)
+ {
+ struct openlist *item;
+ if(np==SYSLOGIN)
+ type=1;
+ else if(np==SYSEXEC)
+ type=1+!com[1];
+ else
+ type = (execflg && !shp->subshell && !shp->st.trapcom[0]);
+ shp->redir0 = 1;
+ sh_redirect(shp,io,type);
+ for(item=buffp->olist;item;item=item->next)
+ item->strm=0;
+ }
+ if(!(nv_isattr(np,BLT_ENV)))
+ {
+ if(bp->nosfio)
+ {
+ if(!shp->pwd)
+ path_pwd(shp,0);
+ if(shp->pwd)
+ stat(".",&statb);
+ }
+ sfsync(NULL);
+ share = sfset(sfstdin,SF_SHARE,0);
+ sh_onstate(SH_STOPOK);
+ sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE);
+ sfset(sfstderr,SF_LINE,1);
+ save_prompt = shp->nextprompt;
+ shp->nextprompt = 0;
+ }
+ if(argp)
+ {
+ scope++;
+ sh_scope(shp,argp,0);
+ }
+ opt_info.index = opt_info.offset = 0;
+ opt_info.disc = 0;
+ error_info.id = *com;
+ if(argn)
+ shp->exitval = 0;
+ shp->bltinfun = (Shbltin_f)funptr(np);
+ bp->bnode = np;
+ bp->vnode = nq;
+ bp->ptr = nv_context(np);
+ bp->data = t->com.comstate;
+ bp->sigset = 0;
+ bp->notify = 0;
+ bp->flags = (OPTIMIZE!=0);
+ if(shp->subshell && nv_isattr(np,BLT_NOSFIO))
+ sh_subtmpfile(shp);
+ if(execflg && !shp->subshell &&
+ !shp->st.trapcom[0] && !shp->st.trap[SH_ERRTRAP] && shp->fn_depth==0 && !nv_isattr(np,BLT_ENV))
+ {
+ /* do close-on-exec */
+ int fd;
+ for(fd=0; fd < shp->gd->lim.open_max; fd++)
+ if((shp->fdstatus[fd]&IOCLEX)&&fd!=shp->infd)
+ sh_close(fd);
+ }
+ if(argn)
+ shp->exitval = (*shp->bltinfun)(argn,com,(void*)bp);
+ if(error_info.flags&ERROR_INTERACTIVE)
+ tty_check(ERRIO);
+ ((Shnode_t*)t)->com.comstate = shp->bltindata.data;
+ bp->data = (void*)save_data;
+ if(sh.exitval && errno==EINTR && shp->lastsig)
+ sh.exitval = SH_EXITSIG|shp->lastsig;
+ else if(!nv_isattr(np,BLT_EXIT) && shp->exitval!=SH_RUNPROG)
+ shp->exitval &= SH_EXITMASK;
+ }
+ else
+ {
+ struct openlist *item;
+ for(item=buffp->olist;item;item=item->next)
+ {
+ if(item->strm)
+ {
+ sfclrlock(item->strm);
+ if(shp->gd->hist_ptr && item->strm == shp->gd->hist_ptr->histfp)
+ hist_close(shp->gd->hist_ptr);
+ else
+ sfclose(item->strm);
+ }
+ }
+ if(shp->bltinfun && (error_info.flags&ERROR_NOTIFY))
+ (*shp->bltinfun)(-2,com,(void*)bp);
+ /* failure on special built-ins fatal */
+ if(jmpval<=SH_JMPCMD && (!nv_isattr(np,BLT_SPC) || command))
+ jmpval=0;
+ }
+ if(bp)
+ {
+ bp->bnode = 0;
+ if( bp->ptr!= nv_context(np))
+ np->nvfun = (Namfun_t*)bp->ptr;
+ }
+ if(execflg && !was_nofork)
+ sh_offstate(SH_NOFORK);
+ if(!(nv_isattr(np,BLT_ENV)))
+ {
+ if(bp->nosfio && shp->pwd)
+ {
+ struct stat stata;
+ stat(".",&stata);
+ /* restore directory changed */
+ if(statb.st_ino!=stata.st_ino || statb.st_dev!=stata.st_dev)
+ chdir(shp->pwd);
+ }
+ sh_offstate(SH_STOPOK);
+ if(share&SF_SHARE)
+ sfset(sfstdin,SF_PUBLIC|SF_SHARE,1);
+ sfset(sfstderr,SF_LINE,0);
+ sfpool(sfstderr,shp->outpool,SF_WRITE);
+ sfpool(sfstdin,NIL(Sfio_t*),SF_WRITE);
+ shp->nextprompt = save_prompt;
+ }
+ sh_popcontext(shp,buffp);
+ errorpop(&buffp->err);
+ error_info.flags &= ~(ERROR_SILENT|ERROR_NOTIFY);
+ shp->bltinfun = 0;
+ if(buffp->olist)
+ free_list(buffp->olist);
+ if(was_vi)
+ sh_onoption(SH_VI);
+ else if(was_emacs)
+ sh_onoption(SH_EMACS);
+ else if(was_gmacs)
+ sh_onoption(SH_GMACS);
+ if(scope)
+ sh_unscope(shp);
+ bp->ptr = (void*)save_ptr;
+ bp->data = (void*)save_data;
+ /* don't restore for subshell exec */
+ if((shp->topfd>topfd) && !(shp->subshell && np==SYSEXEC))
+ sh_iorestore(shp,topfd,jmpval);
+
+ shp->redir0 = 0;
+ if(jmpval)
+ siglongjmp(*shp->jmplist,jmpval);
+#if 0
+ if(flgs&NV_STATIC)
+ ((Shnode_t*)t)->com.comset = 0;
+#endif
+ if(shp->exitval >=0)
+ goto setexit;
+ np = 0;
+ type=0;
+ }
+ /* check for functions */
+ if(!command && np && nv_isattr(np,NV_FUNCTION))
+ {
+ volatile int indx;
+ int jmpval=0;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+#if SHOPT_NAMESPACE
+ Namval_t node,*namespace=shp->namespace;
+#else
+ Namval_t node;
+#endif /* SHOPT_NAMESPACE */
+ struct Namref nr;
+ long mode;
+ register struct slnod *slp;
+ if(!np->nvalue.ip)
+ {
+ indx = path_search(shp,com0,NIL(Pathcomp_t**),0);
+ if(indx==1)
+ {
+#if SHOPT_NAMESPACE
+ if(shp->namespace)
+ np = sh_fsearch(shp,com0,0);
+ else
+#endif /* SHOPT_NAMESPACE */
+ np = nv_search(com0,shp->fun_tree,HASH_NOSCOPE);
+ }
+
+ if(!np->nvalue.ip)
+ {
+ if(indx==1)
+ {
+ errormsg(SH_DICT,ERROR_exit(0),e_defined,com0);
+ shp->exitval = ERROR_NOEXEC;
+ }
+ else
+ {
+ errormsg(SH_DICT,ERROR_exit(0),e_found,"function");
+ shp->exitval = ERROR_NOENT;
+ }
+ goto setexit;
+ }
+ }
+ /* increase refcnt for unset */
+ slp = (struct slnod*)np->nvenv;
+ sh_funstaks(slp->slchild,1);
+ staklink(slp->slptr);
+ if(nq)
+ {
+ Namval_t *mp=0;
+ if(nv_isattr(np,NV_STATICF) && (mp=nv_type(nq)))
+ nq = mp;
+ shp->last_table = last_table;
+ mode = set_instance(shp,nq,&node,&nr);
+ }
+ if(io)
+ {
+ indx = shp->topfd;
+ sh_pushcontext(shp,buffp,SH_JMPCMD);
+ jmpval = sigsetjmp(buffp->buff,0);
+ }
+ if(jmpval == 0)
+ {
+ if(io)
+ indx = sh_redirect(shp,io,execflg);
+#if SHOPT_NAMESPACE
+ if(nq && nv_istable(nq))
+ shp->namespace = nq;
+#endif /* SHOPT_NAMESPACE */
+ sh_funct(shp,np,argn,com,t->com.comset,(flags&~OPTIMIZE_FLAG));
+ }
+#if SHOPT_NAMESPACE
+ shp->namespace = namespace;
+#endif /* SHOPT_NAMESPACE */
+ if(io)
+ {
+ if(buffp->olist)
+ free_list(buffp->olist);
+ sh_popcontext(shp,buffp);
+ sh_iorestore(shp,indx,jmpval);
+ }
+ if(nq)
+ unset_instance(nq,&node,&nr,mode);
+ sh_funstaks(slp->slchild,-1);
+ stakdelete(slp->slptr);
+ if(jmpval > SH_JMPFUN)
+ siglongjmp(*shp->jmplist,jmpval);
+ goto setexit;
+ }
+ }
+ else if(!io)
+ {
+ setexit:
+ exitset();
+ break;
+ }
+ }
+ case TFORK:
+ {
+ register pid_t parent;
+ int no_fork,jobid;
+ int pipes[3];
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ sh_exec(t->fork.forktre,0);
+ break;
+ }
+#endif /* SHOPT_COSHELL */
+ if(shp->subshell)
+ {
+ sh_subtmpfile(shp);
+ if(!usepipe)
+ {
+ subpipe[0] = -1;
+ if(shp->comsub==1 && !(shp->fdstatus[1]&IONOSEEK) && sh_pipe(subpipe)>=0)
+ iousepipe(shp);
+ }
+ if((type&(FAMP|TFORK))==(FAMP|TFORK))
+ sh_subfork();
+ }
+ no_fork = !ntflag && !(type&(FAMP|FPOU)) &&
+ !(shp->st.trapcom[SIGINT] && *shp->st.trapcom[SIGINT]) &&
+ !shp->st.trapcom[0] && !shp->st.trap[SH_ERRTRAP] &&
+ ((struct checkpt*)shp->jmplist)->mode!=SH_JMPEVAL &&
+ (execflg2 || (execflg &&
+ !shp->subshell && shp->fn_depth==0 &&
+ !(pipejob && sh_isoption(SH_PIPEFAIL))
+ ));
+ if(sh_isstate(SH_PROFILE) || shp->dot_depth)
+ {
+ /* disable foreground job monitor */
+ if(!(type&FAMP))
+ sh_offstate(SH_MONITOR);
+#if SHOPT_DEVFD
+ else if(!(type&FINT))
+ sh_offstate(SH_MONITOR);
+#endif /* SHOPT_DEVFD */
+ }
+ if(no_fork)
+ job.parent=parent=0;
+ else
+ {
+#ifdef SHOPT_BGX
+ int maxjob;
+ if(((type&(FAMP|FINT)) == (FAMP|FINT)) && (maxjob=nv_getnum(JOBMAXNOD))>0)
+ {
+ while(job.numbjob >= maxjob)
+ {
+ job_lock();
+ job_reap(0);
+ job_unlock();
+ }
+ }
+#endif /* SHOPT_BGX */
+ nv_getval(RANDNOD);
+ restorefd = shp->topfd;
+ if(type&FCOOP)
+ {
+ pipes[2] = 0;
+#if SHOPT_COSHELL
+ if(shp->coshell)
+ {
+ if(shp->cpipe[0]<0 || shp->cpipe[1] < 0)
+ {
+ sh_copipe(shp,shp->outpipe=shp->cpipe,0);
+ shp->fdptrs[shp->cpipe[0]] = shp->cpipe;
+ }
+ sh_copipe(shp,shp->inpipe=pipes,0);
+ parent = sh_coexec(shp,t,3);
+ shp->cpid = parent;
+ jobid = job_post(shp,parent,0);
+ goto skip;
+ }
+#endif /* SHOPT_COSHELL */
+ coproc_init(shp,pipes);
+ }
+#if SHOPT_COSHELL
+ if((type&(FAMP|FINT)) == (FAMP|FINT))
+ {
+ if(shp->coshell)
+ {
+ parent = sh_coexec(shp,t,0);
+ jobid = job_post(shp,parent,0);
+ goto skip;
+ }
+ }
+#endif /* SHOPT_COSHELL */
+#if SHOPT_AMP
+ if((type&(FAMP|FINT)) == (FAMP|FINT))
+ parent = sh_ntfork(shp,t,com,&jobid,ntflag);
+ else
+ parent = sh_fork(shp,type,&jobid);
+ if(parent<0)
+ {
+ if(shp->comsub==1 && subpipe[0]>=0)
+ iounpipe(shp);
+ break;
+ }
+#else
+#if SHOPT_SPAWN
+# ifdef _lib_fork
+ if(com)
+ parent = sh_ntfork(shp,t,com,&jobid,ntflag);
+ else
+ parent = sh_fork(shp,type,&jobid);
+# else
+ if((parent = sh_ntfork(shp,t,com,&jobid,ntflag))<=0)
+ break;
+# endif /* _lib_fork */
+ if(parent<0)
+ {
+ if(shp->comsub==1 && subpipe[0]>=0)
+ iounpipe(shp);
+ break;
+ }
+#else
+ parent = sh_fork(shp,type,&jobid);
+#endif /* SHOPT_SPAWN */
+#endif
+ }
+#if SHOPT_COSHELL
+ skip:
+#endif /* SHOPT_COSHELL */
+ if(job.parent=parent)
+ /* This is the parent branch of fork
+ * It may or may not wait for the child
+ */
+ {
+ if(pipejob==2)
+ {
+ pipejob = 1;
+ job_unlock();
+ }
+ if(type&FPCL)
+ sh_close(shp->inpipe[0]);
+ if(type&(FCOOP|FAMP))
+ shp->bckpid = parent;
+ else if(!(type&(FAMP|FPOU)))
+ {
+ if(!sh_isoption(SH_MONITOR))
+ {
+ if(!(shp->sigflag[SIGINT]&(SH_SIGFAULT|SH_SIGOFF)))
+ sh_sigtrap(SIGINT);
+ shp->trapnote |= SH_SIGIGNORE;
+ }
+ if(shp->pipepid)
+ shp->pipepid = parent;
+ else
+ job_wait(parent);
+ if(shp->topfd > topfd)
+ sh_iorestore(shp,topfd,sh.exitval);
+ if(usepipe && tsetio && subdup)
+ iounpipe(shp);
+ if(!sh_isoption(SH_MONITOR))
+ {
+ shp->trapnote &= ~SH_SIGIGNORE;
+ if(shp->exitval == (SH_EXITSIG|SIGINT))
+ sh_fault(SIGINT);
+ }
+ }
+ if(type&FAMP)
+ {
+ if(sh_isstate(SH_PROFILE) || sh_isstate(SH_INTERACTIVE))
+ {
+ /* print job number */
+#ifdef JOBS
+# if SHOPT_COSHELL
+ sfprintf(sfstderr,"[%d]\t%s\n",jobid,sh_pid2str(shp,parent));
+# else
+ sfprintf(sfstderr,"[%d]\t%d\n",jobid,parent);
+# endif /* SHOPT_COSHELL */
+#else
+ sfprintf(sfstderr,"%d\n",parent);
+#endif /* JOBS */
+ }
+ }
+ break;
+ }
+ else
+ /*
+ * this is the FORKED branch (child) of execute
+ */
+ {
+ volatile int jmpval;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ struct ionod *iop;
+ int rewrite=0;
+ if(no_fork)
+ sh_sigreset(2);
+ sh_pushcontext(shp,buffp,SH_JMPEXIT);
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval)
+ goto done;
+ if((type&FINT) && !sh_isstate(SH_MONITOR))
+ {
+ /* default std input for & */
+ signal(SIGINT,SIG_IGN);
+ signal(SIGQUIT,SIG_IGN);
+ if(!shp->st.ioset)
+ {
+ if(sh_close(0)>=0)
+ sh_chkopen(e_devnull);
+ }
+ }
+ sh_offstate(SH_MONITOR);
+ /* pipe in or out */
+#ifdef _lib_nice
+ if((type&FAMP) && sh_isoption(SH_BGNICE))
+ nice(4);
+#endif /* _lib_nice */
+#if !SHOPT_DEVFD
+ if(shp->fifo && (type&(FPIN|FPOU)))
+ {
+ int fn,fd = (type&FPIN)?0:1;
+ void *fifo_timer=sh_timeradd(500,1,fifo_check,(void*)shp);
+ fn = sh_open(shp->fifo,fd?O_WRONLY:O_RDONLY);
+ timerdel(fifo_timer);
+ sh_iorenumber(shp,fn,fd);
+ sh_close(fn);
+ sh_delay(.001);
+ unlink(shp->fifo);
+ free(shp->fifo);
+ shp->fifo = 0;
+ type &= ~(FPIN|FPOU);
+ }
+#endif /* !SHOPT_DEVFD */
+ if(type&FPIN)
+ {
+#if SHOPT_COSHELL
+ if(shp->inpipe[2]>20000)
+ sh_coaccept(shp,shp->inpipe,0);
+#endif /* SHOPT_COSHELL */
+ sh_iorenumber(shp,shp->inpipe[0],0);
+ if(!(type&FPOU) || (type&FCOOP))
+ sh_close(shp->inpipe[1]);
+ }
+ if(type&FPOU)
+ {
+#if SHOPT_COSHELL
+ if(shp->outpipe[2]>20000)
+ sh_coaccept(shp,shp->outpipe,1);
+#endif /* SHOPT_COSHELL */
+ sh_iorenumber(shp,shp->outpipe[1],1);
+ sh_pclose(shp->outpipe);
+ }
+ if((type&COMMSK)!=TCOM)
+ error_info.line = t->fork.forkline-shp->st.firstline;
+ if(shp->topfd)
+ sh_iounsave(shp);
+ topfd = shp->topfd;
+ if(com0 && (iop=t->tre.treio))
+ {
+ for(;iop;iop=iop->ionxt)
+ {
+ if(iop->iofile&IOREWRITE)
+ rewrite = 1;
+ }
+ }
+ sh_redirect(shp,t->tre.treio,1);
+ if(rewrite)
+ {
+ job_lock();
+ while((parent = vfork()) < 0)
+ _sh_fork(shp,parent, 0, (int*)0);
+ if(parent)
+ {
+ job_post(shp,parent,0);
+ job_wait(parent);
+ sh_iorestore(shp,topfd,SH_JMPCMD);
+ sh_done(shp,(shp->exitval&SH_EXITSIG)?(shp->exitval&SH_EXITMASK):0);
+
+ }
+ job_unlock();
+ }
+ if((type&COMMSK)!=TCOM)
+ {
+ /* don't clear job table for out
+ pipes so that jobs comand can
+ be used in a pipeline
+ */
+ if(!no_fork && !(type&FPOU))
+ job_clear();
+ sh_exec(t->fork.forktre,flags|sh_state(SH_NOFORK)|sh_state(SH_FORKED));
+ }
+ else if(com0)
+ {
+ sh_offoption(SH_ERREXIT);
+ sh_freeup(shp);
+ path_exec(shp,com0,com,t->com.comset);
+ }
+ done:
+ sh_popcontext(shp,buffp);
+ if(jmpval>SH_JMPEXIT)
+ siglongjmp(*shp->jmplist,jmpval);
+ sh_done(shp,0);
+ }
+ }
+
+ case TSETIO:
+ {
+ /*
+ * don't create a new process, just
+ * save and restore io-streams
+ */
+ pid_t pid;
+ int jmpval, waitall;
+ int simple = (t->fork.forktre->tre.tretyp&COMMSK)==TCOM;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ sh_redirect(shp,t->fork.forkio,0);
+ sh_exec(t->fork.forktre,0);
+ break;
+ }
+#endif /*SHOPT_COSHELL */
+ if(shp->subshell)
+ execflg = 0;
+ sh_pushcontext(shp,buffp,SH_JMPIO);
+ if(type&FPIN)
+ {
+ was_interactive = sh_isstate(SH_INTERACTIVE);
+ sh_offstate(SH_INTERACTIVE);
+ sh_iosave(shp,0,shp->topfd,(char*)0);
+ shp->pipepid = simple;
+ sh_iorenumber(shp,shp->inpipe[0],0);
+ /*
+ * if read end of pipe is a simple command
+ * treat as non-sharable to improve performance
+ */
+ if(simple)
+ sfset(sfstdin,SF_PUBLIC|SF_SHARE,0);
+ waitall = job.waitall;
+ job.waitall = 0;
+ pid = job.parent;
+ }
+ else
+ error_info.line = t->fork.forkline-shp->st.firstline;
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval==0)
+ {
+ if(shp->comsub==1)
+ tsetio = 1;
+ sh_redirect(shp,t->fork.forkio,execflg);
+ (t->fork.forktre)->tre.tretyp |= t->tre.tretyp&FSHOWME;
+ sh_exec(t->fork.forktre,flags&~simple);
+ }
+ else
+ sfsync(shp->outpool);
+ sh_popcontext(shp,buffp);
+ sh_iorestore(shp,buffp->topfd,jmpval);
+ if(buffp->olist)
+ free_list(buffp->olist);
+ if(type&FPIN)
+ {
+ job.waitall = waitall;
+ type = shp->exitval;
+ if(!(type&SH_EXITSIG))
+ {
+ /* wait for remainder of pipline */
+ if(shp->pipepid>1)
+ {
+ job_wait(shp->pipepid);
+ type = shp->exitval;
+ }
+ else
+ job_wait(waitall?pid:0);
+ if(type || !sh_isoption(SH_PIPEFAIL))
+ shp->exitval = type;
+ }
+ if(shp->comsub==1 && subpipe[0]>=0)
+ iounpipe(shp);
+ shp->pipepid = 0;
+ shp->st.ioset = 0;
+ if(simple && was_errexit)
+ {
+ echeck = 1;
+ sh_onstate(SH_ERREXIT);
+ }
+ }
+ if(jmpval>SH_JMPIO)
+ siglongjmp(*shp->jmplist,jmpval);
+ break;
+ }
+
+ case TPAR:
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ sh_exec(t->par.partre,0);
+ break;
+ }
+#endif /* SHOPT_COSHELL */
+ echeck = 1;
+ flags &= ~OPTIMIZE_FLAG;
+ if(!shp->subshell && !shp->st.trapcom[0] && !shp->st.trap[SH_ERRTRAP] && (flags&sh_state(SH_NOFORK)))
+ {
+ char *savsig;
+ int nsig,jmpval;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ shp->st.otrapcom = 0;
+ if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
+ {
+ nsig += sizeof(char*);
+ memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
+ shp->st.otrapcom = (char**)savsig;
+ }
+ sh_sigreset(0);
+ sh_pushcontext(shp,buffp,SH_JMPEXIT);
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval==0)
+ sh_exec(t->par.partre,flags);
+ sh_popcontext(shp,buffp);
+ if(jmpval > SH_JMPEXIT)
+ siglongjmp(*shp->jmplist,jmpval);
+ if(shp->exitval > 256)
+ shp->exitval -= 128;
+ sh_done(shp,0);
+ }
+ else if(((type=t->par.partre->tre.tretyp)&FAMP) && ((type&COMMSK)==TFORK))
+ {
+ pid_t pid;
+ sfsync(NIL(Sfio_t*));
+ while((pid=fork())< 0)
+ _sh_fork(shp,pid,0,0);
+ if(pid==0)
+ {
+ sh_exec(t->par.partre,flags);
+ shp->st.trapcom[0]=0;
+ sh_done(shp,0);
+ }
+ }
+ else
+ sh_subshell(shp,t->par.partre,flags,0);
+ break;
+
+ case TFIL:
+ {
+ /*
+ * This code sets up a pipe.
+ * All elements of the pipe are started by the parent.
+ * The last element executes in current environment
+ */
+ int pvo[3]; /* old pipe for multi-stage */
+ int pvn[3]; /* current set up pipe */
+ int savepipe = pipejob;
+ int showme = t->tre.tretyp&FSHOWME;
+ int n,waitall,savewaitall=job.waitall;
+ int savejobid = job.curjobid;
+ int *exitval=0,*saveexitval = job.exitval;
+ pid_t savepgid = job.curpgid;
+#if SHOPT_COSHELL
+ int copipe=0;
+ Shnode_t *tt;
+#endif /* SHOPT_COSHELL */
+ job.exitval = 0;
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ do
+ {
+ sh_exec(t->lst.lstlef, 0);
+ t = t->lst.lstrit;
+ if(flags && (t->tre.tretyp!=TFIL || !(t->lst.lstlef->tre.tretyp&FALTPIPE)))
+ goto coskip1;
+ }
+ while(t->tre.tretyp==TFIL);
+ sh_exec(t,0);
+ coskip1:
+ break;
+ }
+ pvo[2] = pvn[2] = 0;
+#endif /* SHOPT_COSHELL */
+ job.curjobid = 0;
+ if(shp->subshell)
+ {
+ sh_subtmpfile(shp);
+ if(!usepipe)
+ {
+ subpipe[0] = -1;
+ if(shp->comsub==1 && !(shp->fdstatus[1]&IONOSEEK) && sh_pipe(subpipe)>=0)
+ iousepipe(shp);
+ }
+ }
+ shp->inpipe = pvo;
+ shp->outpipe = pvn;
+ pvo[1] = -1;
+ if(sh_isoption(SH_PIPEFAIL))
+ {
+ const Shnode_t* tn=t;
+ job.waitall = 2;
+ job.curpgid = 0;
+ while((tn=tn->lst.lstrit) && tn->tre.tretyp==TFIL)
+ job.waitall++;
+ exitval = job.exitval = (int*)stakalloc(job.waitall*sizeof(int));
+ memset(exitval,0,job.waitall*sizeof(int));
+ }
+ else
+ job.waitall |= !pipejob && sh_isstate(SH_MONITOR);
+ job_lock();
+ do
+ {
+ /* create the pipe */
+#if SHOPT_COSHELL
+ tt = t->lst.lstrit;
+ if(shp->coshell && !showme)
+ {
+ if(t->lst.lstlef->tre.tretyp&FALTPIPE)
+ {
+ sh_copipe(shp,pvn,0);
+ type = sh_coexec(shp,t,1+copipe);
+ pvn[1] = -1;
+ pipejob=1;
+ if(type>0)
+ {
+ job_post(shp,type,0);
+ type = 0;
+ }
+ copipe = 1;
+ pvo[0] = pvn[0];
+ while(tt->tre.tretyp==TFIL && tt->lst.lstlef->tre.tretyp&FALTPIPE)
+ tt = tt->lst.lstrit;
+ t = tt;
+ continue;
+ }
+ else if(tt->tre.tretyp==TFIL && tt->lst.lstlef->tre.tretyp&FALTPIPE)
+ {
+ sh_copipe(shp,pvn,0);
+ pvo[2] = pvn[2];
+ copipe = 0;
+ goto coskip2;
+ }
+ }
+#endif /* SHOPT_COSHELL */
+ sh_pipe(pvn);
+#if SHOPT_COSHELL
+ pvn[2] = 0;
+ coskip2:
+#endif /* SHOPT_COSHELL */
+ /* execute out part of pipe no wait */
+ (t->lst.lstlef)->tre.tretyp |= showme;
+ type = sh_exec(t->lst.lstlef, errorflg);
+ /* close out-part of pipe */
+ sh_close(pvn[1]);
+ pipejob=1;
+ /* save the pipe stream-ids */
+ pvo[0] = pvn[0];
+ /* pipeline all in one process group */
+ t = t->lst.lstrit;
+ }
+ /* repeat until end of pipeline */
+ while(!type && t->tre.tretyp==TFIL);
+ shp->inpipe = pvn;
+ shp->outpipe = 0;
+ pipejob = 2;
+ waitall = job.waitall;
+ job.waitall = 0;
+ if(type == 0)
+ {
+ /*
+ * execute last element of pipeline
+ * in the current process
+ */
+ ((Shnode_t*)t)->tre.tretyp |= showme;
+ sh_exec(t,flags);
+ }
+ else
+ /* execution failure, close pipe */
+ sh_pclose(pvn);
+ if(pipejob==2)
+ job_unlock();
+ pipejob = savepipe;
+ n = shp->exitval;
+ if(job.waitall = waitall)
+ {
+ if(sh_isstate(SH_MONITOR))
+ job_wait(0);
+ else
+ {
+ shp->intrap++;
+ job_wait(0);
+ shp->intrap--;
+ }
+ }
+ if(n==0 && exitval)
+ {
+ while(exitval <= --job.exitval)
+ {
+ if(*job.exitval)
+ {
+ n = *job.exitval;
+ break;
+ }
+ }
+ }
+ shp->exitval = n;
+#ifdef SIGTSTP
+ if(!pipejob && sh_isstate(SH_MONITOR))
+ tcsetpgrp(JOBTTY,shp->gd->pid);
+#endif /*SIGTSTP */
+ job.curpgid = savepgid;
+ job.exitval = saveexitval;
+ job.waitall = savewaitall;
+ job.curjobid = savejobid;
+ break;
+ }
+
+ case TLST:
+ {
+ /* a list of commands are executed here */
+ do
+ {
+ sh_exec(t->lst.lstlef,errorflg|OPTIMIZE);
+ t = t->lst.lstrit;
+ }
+ while(t->tre.tretyp == TLST);
+ sh_exec(t,flags);
+ break;
+ }
+
+ case TAND:
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ andor:
+ sh_exec(t->lst.lstlef,0);
+ sh_exec(t->lst.lstrit,0);
+ break;
+ }
+#endif /* SHOPT_COSHELL */
+ if(type&TTEST)
+ skipexitset++;
+ if(sh_exec(t->lst.lstlef,OPTIMIZE)==0)
+ sh_exec(t->lst.lstrit,flags);
+ break;
+
+ case TORF:
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ goto andor;
+#endif /* SHOPT_COSHELL */
+ if(type&TTEST)
+ skipexitset++;
+ if(sh_exec(t->lst.lstlef,OPTIMIZE)!=0)
+ sh_exec(t->lst.lstrit,flags);
+ break;
+
+ case TFOR: /* for and select */
+ {
+ register char **args;
+ register int nargs;
+ register Namval_t *np;
+ int flag = errorflg|OPTIMIZE_FLAG;
+ struct dolnod *argsav=0;
+ struct comnod *tp;
+ char *cp, *trap, *nullptr = 0;
+ int nameref, refresh=1;
+ char *av[5];
+#if SHOPT_COSHELL
+ int poolfiles;
+#endif /* SHOPT_COSHELL */
+#if SHOPT_OPTIMIZE
+ int jmpval = ((struct checkpt*)shp->jmplist)->mode;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ void *optlist = shp->optlist;
+ shp->optlist = 0;
+ sh_tclear(t->for_.fortre);
+ sh_pushcontext(shp,buffp,jmpval);
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval)
+ goto endfor;
+#endif /* SHOPT_OPTIMIZE */
+ error_info.line = t->for_.forline-shp->st.firstline;
+ if(!(tp=t->for_.forlst))
+ {
+ args=shp->st.dolv+1;
+ nargs = shp->st.dolc;
+ argsav=sh_arguse(shp);
+ }
+ else
+ {
+ args=sh_argbuild(shp,&argn,tp,0);
+ nargs = argn;
+ }
+ np = nv_open(t->for_.fornam, shp->var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME|NV_NOREF);
+ nameref = nv_isref(np)!=0;
+ shp->st.loopcnt++;
+ cp = *args;
+ while(cp && shp->st.execbrk==0)
+ {
+ if(t->tre.tretyp&COMSCAN)
+ {
+ char *val;
+ int save_prompt;
+ /* reuse register */
+ if(refresh)
+ {
+ sh_menu(sfstderr,nargs,args);
+ refresh = 0;
+ }
+ save_prompt = shp->nextprompt;
+ shp->nextprompt = 3;
+ shp->timeout = 0;
+ shp->exitval=sh_readline(shp,&nullptr,0,1,1000*shp->st.tmout);
+ shp->nextprompt = save_prompt;
+ if(shp->exitval||sfeof(sfstdin)||sferror(sfstdin))
+ {
+ shp->exitval = 1;
+ break;
+ }
+ if(!(val=nv_getval(sh_scoped(shp,REPLYNOD))))
+ continue;
+ else
+ {
+ if(*(cp=val) == 0)
+ {
+ refresh++;
+ goto check;
+ }
+ while(type = *cp++)
+ if(type < '0' && type > '9')
+ break;
+ if(type!=0)
+ type = nargs;
+ else
+ type = (int)strtol(val, (char**)0, 10)-1;
+ if(type<0 || type >= nargs)
+ cp = "";
+ else
+ cp = args[type];
+ }
+ }
+ if(nameref)
+ nv_offattr(np,NV_REF);
+ else if(nv_isattr(np, NV_ARRAY))
+ nv_putsub(np,NIL(char*),0L);
+ nv_putval(np,cp,0);
+ if(nameref)
+ nv_setref(np,(Dt_t*)0,NV_VARNAME);
+ if(trap=shp->st.trap[SH_DEBUGTRAP])
+ {
+ av[0] = (t->tre.tretyp&COMSCAN)?"select":"for";
+ av[1] = t->for_.fornam;
+ av[2] = "in";
+ av[3] = cp;
+ av[4] = 0;
+ sh_debug(shp,trap,(char*)0,(char*)0,av,0);
+ }
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ poolfiles = shp->poolfiles;
+ sh_exec(t->for_.fortre,0);
+ if(poolfiles==shp->poolfiles)
+ break;
+ }
+#endif /* SHOPT_COSHELL */
+ sh_exec(t->for_.fortre,flag);
+ flag &= ~OPTIMIZE_FLAG;
+ if(t->tre.tretyp&COMSCAN)
+ {
+ if((cp=nv_getval(sh_scoped(shp,REPLYNOD))) && *cp==0)
+ refresh++;
+ }
+ else
+ cp = *++args;
+ check:
+ if(shp->st.breakcnt<0)
+ shp->st.execbrk = (++shp->st.breakcnt !=0);
+ }
+#if SHOPT_OPTIMIZE
+ endfor:
+ sh_popcontext(shp,buffp);
+ sh_tclear(t->for_.fortre);
+ sh_optclear(shp,optlist);
+ if(jmpval)
+ siglongjmp(*shp->jmplist,jmpval);
+#endif /*SHOPT_OPTIMIZE */
+ if(shp->st.breakcnt>0)
+ shp->st.execbrk = (--shp->st.breakcnt !=0);
+ shp->st.loopcnt--;
+ sh_argfree(shp,argsav,0);
+ nv_close(np);
+ break;
+ }
+
+ case TWH: /* while and until */
+ {
+ volatile int r=0;
+ int first = OPTIMIZE_FLAG;
+ Shnode_t *tt = t->wh.whtre;
+#if SHOPT_FILESCAN
+ Sfio_t *iop=0;
+ int savein,fd;
+#endif /*SHOPT_FILESCAN*/
+#if SHOPT_OPTIMIZE
+ int jmpval = ((struct checkpt*)shp->jmplist)->mode;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ void *optlist = shp->optlist;
+#endif /* SHOPT_OPTIMIZE */
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ int poolfiles;
+# if SHOPT_FILESCAN
+ if(type==TWH && tt->tre.tretyp==TCOM && !tt->com.comarg && tt->com.comio)
+ {
+ sh_redirect(shp,tt->com.comio,0);
+ break;
+ }
+# endif /* SHOPT_FILESCAN */
+ sh_exec(tt,0);
+ do
+ {
+ if((sh_exec(tt,0)==0)!=(type==TWH))
+ break;
+ poolfiles = shp->poolfiles;
+ sh_exec(t->wh.dotre,0);
+ if(t->wh.whinc)
+ sh_exec((Shnode_t*)t->wh.whinc,0);
+ }
+ while(poolfiles != shp->poolfiles);
+ break;
+ }
+#endif /*SHOPT_COSHELL */
+#if SHOPT_OPTIMIZE
+ shp->optlist = 0;
+ sh_tclear(t->wh.whtre);
+ sh_tclear(t->wh.dotre);
+ sh_pushcontext(shp,buffp,jmpval);
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval)
+ goto endwhile;
+#endif /* SHOPT_OPTIMIZE */
+#if SHOPT_FILESCAN
+ if(type==TWH && tt->tre.tretyp==TCOM && !tt->com.comarg && tt->com.comio)
+ {
+ fd = sh_redirect(shp,tt->com.comio,3);
+ savein = dup(0);
+ if(fd==0)
+ fd = savein;
+ iop = sfnew(NULL,NULL,SF_UNBOUND,fd,SF_READ);
+ close(0);
+ open(e_devnull,O_RDONLY);
+ shp->offsets[0] = -1;
+ shp->offsets[1] = 0;
+ if(tt->com.comset)
+ nv_setlist(tt->com.comset,NV_IDENT|NV_ASSIGN,0);
+ }
+#endif /*SHOPT_FILESCAN */
+ shp->st.loopcnt++;
+ while(shp->st.execbrk==0)
+ {
+#if SHOPT_FILESCAN
+ if(iop)
+ {
+ if(!(shp->cur_line=sfgetr(iop,'\n',SF_STRING)))
+ break;
+ }
+ else
+#endif /*SHOPT_FILESCAN */
+ if((sh_exec(tt,first)==0)!=(type==TWH))
+ break;
+ r = sh_exec(t->wh.dotre,first|errorflg);
+ if(shp->st.breakcnt<0)
+ shp->st.execbrk = (++shp->st.breakcnt !=0);
+ /* This is for the arithmetic for */
+ if(shp->st.execbrk==0 && t->wh.whinc)
+ sh_exec((Shnode_t*)t->wh.whinc,first);
+ first = 0;
+ errorflg &= ~OPTIMIZE_FLAG;
+#if SHOPT_FILESCAN
+ shp->offsets[0] = -1;
+ shp->offsets[1] = 0;
+#endif /*SHOPT_FILESCAN */
+ }
+#if SHOPT_OPTIMIZE
+ endwhile:
+ sh_popcontext(shp,buffp);
+ sh_tclear(t->wh.whtre);
+ sh_tclear(t->wh.dotre);
+ sh_optclear(shp,optlist);
+ if(jmpval)
+ siglongjmp(*shp->jmplist,jmpval);
+#endif /*SHOPT_OPTIMIZE */
+ if(shp->st.breakcnt>0)
+ shp->st.execbrk = (--shp->st.breakcnt !=0);
+ shp->st.loopcnt--;
+ shp->exitval= r;
+#if SHOPT_FILESCAN
+ if(iop)
+ {
+ sfclose(iop);
+ close(0);
+ dup(savein);
+ shp->cur_line = 0;
+ }
+#endif /*SHOPT_FILESCAN */
+ break;
+ }
+ case TARITH: /* (( expression )) */
+ {
+ register char *trap;
+ char *arg[4];
+ error_info.line = t->ar.arline-shp->st.firstline;
+ arg[0] = "((";
+ if(!(t->ar.arexpr->argflag&ARG_RAW))
+ arg[1] = sh_macpat(shp,t->ar.arexpr,OPTIMIZE|ARG_ARITH);
+ else
+ arg[1] = t->ar.arexpr->argval;
+ arg[2] = "))";
+ arg[3] = 0;
+ if(trap=shp->st.trap[SH_DEBUGTRAP])
+ sh_debug(shp,trap,(char*)0, (char*)0, arg, ARG_ARITH);
+ if(sh_isoption(SH_XTRACE))
+ {
+ sh_trace(shp,NIL(char**),0);
+ sfprintf(sfstderr,"((%s))\n",arg[1]);
+ }
+ if(t->ar.arcomp)
+ shp->exitval = !arith_exec((Arith_t*)t->ar.arcomp);
+ else
+ shp->exitval = !sh_arith(shp,arg[1]);
+ break;
+ }
+
+ case TIF:
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ sh_exec(t->if_.thtre,0);
+ if(t->if_.eltre)
+ sh_exec(t->if_.eltre, 0);
+ break;
+ }
+#endif /*SHOPT_COSHELL */
+ if(sh_exec(t->if_.iftre,OPTIMIZE)==0)
+ sh_exec(t->if_.thtre,flags);
+ else if(t->if_.eltre)
+ sh_exec(t->if_.eltre, flags);
+ else
+ shp->exitval=0; /* force zero exit for if-then-fi */
+ break;
+
+ case TSW:
+ {
+ Shnode_t *tt = (Shnode_t*)t;
+ char *trap, *r = sh_macpat(shp,tt->sw.swarg,OPTIMIZE);
+ error_info.line = t->sw.swline-shp->st.firstline;
+ t= (Shnode_t*)(tt->sw.swlst);
+ if(trap=shp->st.trap[SH_DEBUGTRAP])
+ {
+ char *av[4];
+ av[0] = "case";
+ av[1] = r;
+ av[2] = "in";
+ av[3] = 0;
+ sh_debug(shp,trap, (char*)0, (char*)0, av, 0);
+ }
+ while(t)
+ {
+ register struct argnod *rex=(struct argnod*)t->reg.regptr;
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ sh_exec(t->reg.regcom,0);
+ continue;
+ }
+#endif /*SHOPT_COSHELL */
+ while(rex)
+ {
+ register char *s;
+ if(rex->argflag&ARG_MAC)
+ {
+ s = sh_macpat(shp,rex,OPTIMIZE|ARG_EXP);
+ while(*s=='\\' && s[1]==0)
+ s+=2;
+ }
+ else
+ s = rex->argval;
+ type = (rex->argflag&ARG_RAW);
+ if((type && strcmp(r,s)==0) ||
+ (!type && (strmatch(r,s)
+ || trim_eq(r,s))))
+ {
+ do sh_exec(t->reg.regcom,(t->reg.regflag?(flags&sh_state(SH_ERREXIT)):flags));
+ while(t->reg.regflag &&
+ (t=(Shnode_t*)t->reg.regnxt));
+ t=0;
+ break;
+ }
+ else
+ rex=rex->argnxt.ap;
+ }
+ if(t)
+ t=(Shnode_t*)t->reg.regnxt;
+ }
+ break;
+ }
+
+ case TTIME:
+ {
+ /* time the command */
+ struct tms before,after;
+ const char *format = e_timeformat;
+ clock_t at, tm[3];
+#ifdef timeofday
+ struct timeval tb,ta;
+#else
+ clock_t bt;
+#endif /* timeofday */
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ if(t->par.partre)
+ sh_exec(t->par.partre,0);
+ break;
+ }
+#endif /*SHOPT_COSHELL */
+ if(type!=TTIME)
+ {
+ sh_exec(t->par.partre,OPTIMIZE);
+ shp->exitval = !shp->exitval;
+ break;
+ }
+ if(t->par.partre)
+ {
+ long timer_on;
+ if(shp->subshell && shp->comsub==1)
+ sh_subfork();
+ timer_on = sh_isstate(SH_TIMING);
+#ifdef timeofday
+ timeofday(&tb);
+ times(&before);
+#else
+ bt = times(&before);
+#endif /* timeofday */
+ job.waitall = 1;
+ sh_onstate(SH_TIMING);
+ sh_exec(t->par.partre,OPTIMIZE);
+ if(!timer_on)
+ sh_offstate(SH_TIMING);
+ job.waitall = 0;
+ }
+ else
+ {
+#ifndef timeofday
+ bt = 0;
+#endif /* timeofday */
+ before.tms_utime = before.tms_cutime = 0;
+ before.tms_stime = before.tms_cstime = 0;
+ }
+#ifdef timeofday
+ times(&after);
+ timeofday(&ta);
+ at = shp->gd->lim.clk_tck*(ta.tv_sec-tb.tv_sec);
+ at += ((shp->gd->lim.clk_tck*(((1000000L/2)/shp->gd->lim.clk_tck)+(ta.tv_usec-tb.tv_usec)))/1000000L);
+#else
+ at = times(&after) - bt;
+#endif /* timeofday */
+ tm[0] = at;
+ if(t->par.partre)
+ {
+ Namval_t *np = nv_open("TIMEFORMAT",shp->var_tree,NV_NOADD);
+ if(np)
+ {
+ format = nv_getval(np);
+ nv_close(np);
+ }
+ if(!format)
+ format = e_timeformat;
+ }
+ else
+ format = strchr(format+1,'\n')+1;
+ tm[1] = after.tms_utime - before.tms_utime;
+ tm[1] += after.tms_cutime - before.tms_cutime;
+ tm[2] = after.tms_stime - before.tms_stime;
+ tm[2] += after.tms_cstime - before.tms_cstime;
+ if(format && *format)
+ p_time(shp,sfstderr,sh_translate(format),tm);
+ break;
+ }
+ case TFUN:
+ {
+ register Namval_t *np=0;
+ register struct slnod *slp;
+ register char *fname = ((struct functnod*)t)->functnam;
+ register char *cp = strrchr(fname,'.');
+ register Namval_t *npv=0,*mp;
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ {
+ sh_exec(t->funct.functtre,0);
+ break;
+ }
+#endif /* SHOPT_COSHELL */
+#if SHOPT_NAMESPACE
+ if(t->tre.tretyp==TNSPACE)
+ {
+ Dt_t *root,*oldroot, *bot=0;
+ Namval_t *oldnspace = shp->namespace;
+ int offset = stktell(stkp);
+ long optindex = shp->st.optindex;
+ int flags=NV_NOASSIGN|NV_NOARRAY|NV_VARNAME;
+ if(cp)
+ errormsg(SH_DICT,ERROR_exit(1),e_ident,fname);
+ if(!shp->namespace)
+ sfputc(stkp,'.');
+ else
+ flags |= NV_NOSCOPE;
+ sfputr(stkp,fname,0);
+ np = nv_open(stkptr(stkp,offset),shp->var_tree,flags);
+ offset = stktell(stkp);
+ shp->namespace = np;
+ if(nv_istable(np))
+ root = nv_dict(np);
+ else
+ {
+ root = dtopen(&_Nvdisc,Dtoset);
+ nv_mount(np, (char*)0, root);
+ np->nvalue.cp = Empty;
+ shp->st.optindex = 1;
+ }
+ if(oldnspace && dtvnext(dtvnext(shp->var_tree)))
+ bot = dtview(shp->var_tree,0);
+ else if(dtvnext(shp->var_tree))
+ bot = dtview(shp->var_tree,0);
+ oldroot = shp->var_tree;
+ dtview(root,shp->var_base);
+ shp->var_tree = root;
+ if(bot)
+ dtview(shp->var_tree,bot);
+ sh_exec(t->for_.fortre,flags|sh_state(SH_ERREXIT));
+ if(dtvnext(shp->var_tree))
+ bot = dtview(shp->var_tree,0);
+ shp->var_tree = oldroot;
+ if(bot)
+ dtview(shp->var_tree,bot);
+ shp->namespace = oldnspace;
+ shp->st.optindex = optindex;
+ break;
+ }
+#endif /* SHOPT_NAMESPACE */
+ /* look for discipline functions */
+ error_info.line = t->funct.functline-shp->st.firstline;
+ /* Function names cannot be special builtin */
+ if(cp || shp->prefix)
+ {
+ int offset = stktell(stkp);
+ if(shp->prefix)
+ {
+ cp = shp->prefix;
+ shp->prefix = 0;
+ npv = nv_open(cp,shp->var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME);
+ shp->prefix = cp;
+ cp = fname;
+ }
+ else
+ {
+ sfwrite(stkp,fname,cp++-fname);
+ sfputc(stkp,0);
+ npv = nv_open(stkptr(stkp,offset),shp->var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME);
+ }
+ offset = stktell(stkp);
+ sfprintf(stkp,"%s.%s%c",nv_name(npv),cp,0);
+ fname = stkptr(stkp,offset);
+ }
+ else if((mp=nv_search(fname,shp->bltin_tree,0)) && nv_isattr(mp,BLT_SPC))
+ errormsg(SH_DICT,ERROR_exit(1),e_badfun,fname);
+#if SHOPT_NAMESPACE
+ if(shp->namespace && !shp->prefix && *fname!='.')
+ np = sh_fsearch(shp,fname,NV_ADD|HASH_NOSCOPE);
+ if(!np)
+#endif /* SHOPT_NAMESPACE */
+ np = nv_open(fname,sh_subfuntree(1),NV_NOASSIGN|NV_NOARRAY|NV_VARNAME|NV_NOSCOPE);
+ if(npv)
+ {
+ if(!shp->mktype)
+ cp = nv_setdisc(npv,cp,np,(Namfun_t*)npv);
+ if(!cp)
+ errormsg(SH_DICT,ERROR_exit(1),e_baddisc,fname);
+ }
+ if(np->nvalue.rp)
+ {
+ struct Ufunction *rp = np->nvalue.rp;
+ slp = (struct slnod*)np->nvenv;
+ sh_funstaks(slp->slchild,-1);
+ stakdelete(slp->slptr);
+ if(shp->funload)
+ {
+ free((void*)np->nvalue.rp);
+ np->nvalue.rp = 0;
+ }
+ if(rp->sdict)
+ {
+ Namval_t *mp, *nq;
+ shp->last_root = rp->sdict;
+ for(mp=(Namval_t*)dtfirst(rp->sdict);mp;mp=nq)
+ {
+ nq = dtnext(rp->sdict,mp);
+ _nv_unset(mp,NV_RDONLY);
+ nv_delete(mp,rp->sdict,0);
+ }
+ dtclose(rp->sdict);
+ rp->sdict = 0;
+ }
+ }
+ if(!np->nvalue.rp)
+ {
+ np->nvalue.rp = new_of(struct Ufunction,shp->funload?sizeof(Dtlink_t):0);
+ memset((void*)np->nvalue.rp,0,sizeof(struct Ufunction));
+ }
+ if(t->funct.functstak)
+ {
+ static Dtdisc_t _Rpdisc =
+ {
+ offsetof(struct Ufunction,fname), -1, sizeof(struct Ufunction)
+ };
+ struct functnod *fp;
+ struct comnod *ac = t->funct.functargs;
+ slp = t->funct.functstak;
+ sh_funstaks(slp->slchild,1);
+ staklink(slp->slptr);
+ np->nvenv = (char*)slp;
+ nv_funtree(np) = (int*)(t->funct.functtre);
+ np->nvalue.rp->hoffset = t->funct.functloc;
+ np->nvalue.rp->lineno = t->funct.functline;
+ np->nvalue.rp->nspace = shp->namespace;
+ np->nvalue.rp->fname = 0;
+ np->nvalue.rp->argv = ac?((struct dolnod*)ac->comarg)->dolval+1:0;
+ np->nvalue.rp->argc = ac?((struct dolnod*)ac->comarg)->dolnum:0;
+ np->nvalue.rp->fdict = shp->fun_tree;
+ fp = (struct functnod*)(slp+1);
+ if(fp->functtyp==(TFUN|FAMP))
+ np->nvalue.rp->fname = fp->functnam;
+ nv_setsize(np,fp->functline);
+ nv_offattr(np,NV_FPOSIX);
+ if(shp->funload)
+ {
+ struct Ufunction *rp = np->nvalue.rp;
+ rp->np = np;
+ if(!shp->fpathdict)
+ shp->fpathdict = dtopen(&_Rpdisc,Dtobag);
+ if(shp->fpathdict)
+ dtinsert(shp->fpathdict,rp);
+ }
+ }
+ else
+ _nv_unset(np,0);
+ if(type&FPOSIX)
+ nv_onattr(np,NV_FUNCTION|NV_FPOSIX);
+ else
+ nv_onattr(np,NV_FUNCTION);
+ if(type&FPIN)
+ nv_onattr(np,NV_FTMP);
+ if(type&FOPTGET)
+ nv_onattr(np,NV_OPTGET);
+ break;
+ }
+
+ /* new test compound command */
+ case TTST:
+ {
+ register int n;
+ register char *left;
+ int negate = (type&TNEGATE)!=0;
+#if SHOPT_COSHELL
+ if(shp->inpool)
+ break;
+#endif /* SHOPT_COSHELL */
+ if(type&TTEST)
+ skipexitset++;
+ error_info.line = t->tst.tstline-shp->st.firstline;
+ echeck = 1;
+ if((type&TPAREN)==TPAREN)
+ {
+ sh_exec(t->lst.lstlef,OPTIMIZE);
+ n = !shp->exitval;
+ }
+ else
+ {
+ register int traceon=0;
+ register char *right;
+ register char *trap;
+ char *argv[6];
+ n = type>>TSHIFT;
+ left = sh_macpat(shp,&(t->lst.lstlef->arg),OPTIMIZE);
+ if(type&TBINARY)
+ right = sh_macpat(shp,&(t->lst.lstrit->arg),((n==TEST_PEQ||n==TEST_PNE)?ARG_EXP:0)|OPTIMIZE);
+ if(trap=shp->st.trap[SH_DEBUGTRAP])
+ argv[0] = (type&TNEGATE)?((char*)e_tstbegin):"[[";
+ if(sh_isoption(SH_XTRACE))
+ {
+ traceon = sh_trace(shp,NIL(char**),0);
+ sfwrite(sfstderr,e_tstbegin,(type&TNEGATE?5:3));
+ }
+ if(type&TUNARY)
+ {
+ if(traceon)
+ sfprintf(sfstderr,"-%c %s",n,sh_fmtq(left));
+ if(trap)
+ {
+ char unop[3];
+ unop[0] = '-';
+ unop[1] = n;
+ unop[2] = 0;
+ argv[1] = unop;
+ argv[2] = left;
+ argv[3] = "]]";
+ argv[4] = 0;
+ sh_debug(shp,trap,(char*)0,(char*)0,argv, 0);
+ }
+ n = test_unop(shp,n,left);
+ }
+ else if(type&TBINARY)
+ {
+ char *op;
+ int pattern = 0;
+ if(trap || traceon)
+ op = (char*)(shtab_testops+(n&037)-1)->sh_name;
+ type >>= TSHIFT;
+ if(type==TEST_PEQ || type==TEST_PNE)
+ pattern=ARG_EXP;
+ if(trap)
+ {
+ argv[1] = left;
+ argv[2] = op;
+ argv[3] = right;
+ argv[4] = "]]";
+ argv[5] = 0;
+ sh_debug(shp,trap,(char*)0,(char*)0,argv, pattern);
+ }
+ n = test_binop(shp,n,left,right);
+ if(traceon)
+ {
+ sfprintf(sfstderr,"%s %s ",sh_fmtq(left),op);
+ if(pattern)
+ out_pattern(sfstderr,right,-1);
+ else
+ sfputr(sfstderr,sh_fmtq(right),-1);
+ }
+ }
+ if(traceon)
+ sfwrite(sfstderr,e_tstend,4);
+ }
+ shp->exitval = ((!n)^negate);
+ if(!skipexitset)
+ exitset();
+ break;
+ }
+ }
+ if(shp->trapnote || (shp->exitval && sh_isstate(SH_ERREXIT)) &&
+ t && echeck)
+ sh_chktrap(shp);
+ /* set $_ */
+ if(mainloop && com0)
+ {
+ /* store last argument here if it fits */
+ static char lastarg[32];
+ if(sh_isstate(SH_FORKED))
+ sh_done(shp,0);
+ if(shp->lastarg!= lastarg && shp->lastarg)
+ free(shp->lastarg);
+ if(strlen(comn) < sizeof(lastarg))
+ {
+ nv_onattr(L_ARGNOD,NV_NOFREE);
+ shp->lastarg = strcpy(lastarg,comn);
+ }
+ else
+ {
+ nv_offattr(L_ARGNOD,NV_NOFREE);
+ shp->lastarg = strdup(comn);
+ }
+ }
+ if(!skipexitset)
+ exitset();
+#if SHOPT_COSHELL
+ if(!shp->inpool && !(OPTIMIZE))
+#else
+ if(!(OPTIMIZE))
+#endif /* SHOPT_COSHELL */
+ {
+ if(sav != stkptr(stkp,0))
+ stkset(stkp,sav,0);
+ else if(stktell(stkp))
+ stkseek(stkp,0);
+ }
+ if(shp->trapnote&SH_SIGSET)
+ sh_exit(SH_EXITSIG|shp->lastsig);
+ if(was_interactive)
+ sh_onstate(SH_INTERACTIVE);
+ if(was_monitor && sh_isoption(SH_MONITOR))
+ sh_onstate(SH_MONITOR);
+ if(was_errexit)
+ sh_onstate(SH_ERREXIT);
+ }
+ return(shp->exitval);
+}
+
+int sh_run(int argn, char *argv[])
+{
+ Shell_t *shp = sh_getinterp();
+ register struct dolnod *dp;
+ register struct comnod *t = (struct comnod*)stakalloc(sizeof(struct comnod));
+ int savtop = staktell();
+ char *savptr = stakfreeze(0);
+ Opt_t *op, *np = optctx(0, 0);
+ Shbltin_t bltindata;
+ bltindata = shp->bltindata;
+ op = optctx(np, 0);
+ memset(t, 0, sizeof(struct comnod));
+ dp = (struct dolnod*)stakalloc((unsigned)sizeof(struct dolnod) + ARG_SPARE*sizeof(char*) + argn*sizeof(char*));
+ dp->dolnum = argn;
+ dp->dolbot = ARG_SPARE;
+ memcpy(dp->dolval+ARG_SPARE, argv, (argn+1)*sizeof(char*));
+ t->comarg = (struct argnod*)dp;
+ if(!strchr(argv[0],'/'))
+ t->comnamp = (void*)nv_bfsearch(argv[0],shp->fun_tree,(Namval_t**)&t->comnamq,(char**)0);
+ argn=sh_exec((Shnode_t*)t,sh_isstate(SH_ERREXIT));
+ optctx(op,np);
+ shp->bltindata = bltindata;
+ if(savptr!=stakptr(0))
+ stakset(savptr,savtop);
+ else
+ stakseek(savtop);
+ return(argn);
+}
+
+/*
+ * test for equality with second argument trimmed
+ * returns 1 if r == trim(s) otherwise 0
+ */
+
+static int trim_eq(register const char *r,register const char *s)
+{
+ register char c;
+ while(c = *s++)
+ {
+ if(c=='\\')
+ c = *s++;
+ if(c && c != *r++)
+ return(0);
+ }
+ return(*r==0);
+}
+
+/*
+ * print out the command line if set -x is on
+ */
+
+int sh_trace(Shell_t *shp,register char *argv[], register int nl)
+{
+ register char *cp;
+ register int bracket = 0;
+ int decl = (nl&2);
+ nl &= ~2;
+ if(sh_isoption(SH_XTRACE))
+ {
+ /* make this trace atomic */
+ sfset(sfstderr,SF_SHARE|SF_PUBLIC,0);
+ if(!(cp=nv_getval(sh_scoped(shp,PS4NOD))))
+ cp = "+ ";
+ else
+ {
+ sh_offoption(SH_XTRACE);
+ cp = sh_mactry(shp,cp);
+ sh_onoption(SH_XTRACE);
+ }
+ if(*cp)
+ sfputr(sfstderr,cp,-1);
+ if(argv)
+ {
+ char *argv0 = *argv;
+ nl = (nl?'\n':-1);
+ /* don't quote [ and [[ */
+ if(*(cp=argv[0])=='[' && (!cp[1] || !cp[2]&&cp[1]=='['))
+ {
+ sfputr(sfstderr,cp,*++argv?' ':nl);
+ bracket = 1;
+ }
+ while(cp = *argv++)
+ {
+ if(bracket==0 || *argv || *cp!=']')
+ cp = sh_fmtq(cp);
+ if(decl && shp->prefix && cp!=argv0 && *cp!='-')
+ {
+ if(*cp=='.' && cp[1]==0)
+ cp = shp->prefix;
+ else
+ sfputr(sfstderr,shp->prefix,'.');
+ }
+ sfputr(sfstderr,cp,*argv?' ':nl);
+ }
+ sfset(sfstderr,SF_SHARE|SF_PUBLIC,1);
+ }
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * This routine creates a subshell by calling fork() or vfork()
+ * If ((flags&COMASK)==TCOM), then vfork() is permitted
+ * If fork fails, the shell sleeps for exponentially longer periods
+ * and tries again until a limit is reached.
+ * SH_FORKLIM is the max period between forks - power of 2 usually.
+ * Currently shell tries after 2,4,8,16, and 32 seconds and then quits
+ * Failures cause the routine to error exit.
+ * Parent links to here-documents are removed by the child
+ * Traps are reset by the child
+ * The process-id of the child is returned to the parent, 0 to the child.
+ */
+
+static void timed_out(void *handle)
+{
+ NOT_USED(handle);
+ timeout = 0;
+}
+
+
+/*
+ * called by parent and child after fork by sh_fork()
+ */
+pid_t _sh_fork(Shell_t *shp,register pid_t parent,int flags,int *jobid)
+{
+ static long forkcnt = 1000L;
+ pid_t curpgid = job.curpgid;
+ pid_t postid = (flags&FAMP)?0:curpgid;
+ int sig,nochild;
+ if(parent<0)
+ {
+ sh_sigcheck(shp);
+ if((forkcnt *= 2) > 1000L*SH_FORKLIM)
+ {
+ forkcnt=1000L;
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_nofork);
+ }
+ timeout = (void*)sh_timeradd(forkcnt, 0, timed_out, NIL(void*));
+ nochild = job_wait((pid_t)1);
+ if(timeout)
+ {
+ if(nochild)
+ pause();
+ else if(forkcnt>1000L)
+ forkcnt /= 2;
+ timerdel(timeout);
+ timeout = 0;
+ }
+ return(-1);
+ }
+ forkcnt = 1000L;
+ if(parent)
+ {
+ int myjob,waitall=job.waitall;
+ shp->gd->nforks++;
+ if(job.toclear)
+ job_clear();
+ job.waitall = waitall;
+#ifdef JOBS
+ /* first process defines process group */
+ if(sh_isstate(SH_MONITOR))
+ {
+ /*
+ * errno==EPERM means that an earlier processes
+ * completed. Make parent the job group id.
+ */
+ if(postid==0)
+ job.curpgid = parent;
+ if(job.jobcontrol || (flags&FAMP))
+ {
+ if(setpgid(parent,job.curpgid)<0 && errno==EPERM)
+ setpgid(parent,parent);
+ }
+ }
+#endif /* JOBS */
+ if(!sh_isstate(SH_MONITOR) && job.waitall && postid==0)
+ job.curpgid = parent;
+ if(flags&FCOOP)
+ shp->cpid = parent;
+ if(!postid && job.curjobid && (flags&FPOU))
+ postid = job.curpgid;
+#ifdef SHOPT_BGX
+ if(!postid && (flags&(FAMP|FINT)) == (FAMP|FINT))
+ postid = 1;
+ myjob = job_post(shp,parent,postid);
+ if(postid==1)
+ postid = 0;
+#else
+ myjob = job_post(shp,parent,postid);
+#endif /* SHOPT_BGX */
+ if(job.waitall && (flags&FPOU))
+ {
+ if(!job.curjobid)
+ job.curjobid = myjob;
+ if(job.exitval)
+ job.exitval++;
+ }
+ if(flags&FAMP)
+ job.curpgid = curpgid;
+ if(jobid)
+ *jobid = myjob;
+ if(shp->comsub==1 && subpipe[0]>=0)
+ {
+ if(!tsetio || !subdup)
+ {
+ if(shp->topfd > restorefd)
+ sh_iorestore(shp,restorefd,sh.exitval);
+ iounpipe(shp);
+ }
+ }
+ return(parent);
+ }
+#if !_std_malloc
+ vmtrace(-1);
+#endif
+ shp->outpipepid = ((flags&FPOU)?getpid():0);
+ /* This is the child process */
+ if(shp->trapnote&SH_SIGTERM)
+ sh_exit(SH_EXITSIG|SIGTERM);
+ shp->gd->nforks=0;
+ timerdel(NIL(void*));
+#ifdef JOBS
+ if(!job.jobcontrol && !(flags&FAMP))
+ sh_offstate(SH_MONITOR);
+ if(sh_isstate(SH_MONITOR))
+ {
+ parent = getpid();
+ if(postid==0)
+ job.curpgid = parent;
+ while(setpgid(0,job.curpgid)<0 && job.curpgid!=parent)
+ job.curpgid = parent;
+# ifdef SIGTSTP
+ if(job.curpgid==parent && !(flags&FAMP))
+ tcsetpgrp(job.fd,job.curpgid);
+# endif /* SIGTSTP */
+ }
+# ifdef SIGTSTP
+ if(job.jobcontrol)
+ {
+ signal(SIGTTIN,SIG_DFL);
+ signal(SIGTTOU,SIG_DFL);
+ signal(SIGTSTP,SIG_DFL);
+ }
+# endif /* SIGTSTP */
+ job.jobcontrol = 0;
+#endif /* JOBS */
+ job.toclear = 1;
+ shp->login_sh = 0;
+ sh_offoption(SH_LOGIN_SHELL);
+ sh_onstate(SH_FORKED);
+ sh_onstate(SH_NOLOG);
+ if (shp->fn_reset)
+ shp->fn_depth = shp->fn_reset = 0;
+#if SHOPT_ACCT
+ sh_accsusp();
+#endif /* SHOPT_ACCT */
+ /* Reset remaining signals to parent */
+ /* except for those `lost' by trap */
+ if(!(flags&FSHOWME))
+ sh_sigreset(2);
+ shp->subshell = 0;
+ shp->comsub = 0;
+ shp->spid = 0;
+ if((flags&FAMP) && shp->coutpipe>1)
+ sh_close(shp->coutpipe);
+ sig = shp->savesig;
+ shp->savesig = 0;
+ if(sig>0)
+ sh_fault(sig);
+ sh_sigcheck(shp);
+ usepipe=0;
+ return(0);
+}
+
+pid_t sh_fork(Shell_t *shp,int flags, int *jobid)
+{
+ register pid_t parent;
+ register int sig;
+ if(!shp->pathlist)
+ path_get(shp,"");
+ sfsync(NIL(Sfio_t*));
+ shp->trapnote &= ~SH_SIGTERM;
+ job_fork(-1);
+ shp->savesig = -1;
+ while(_sh_fork(shp,parent=fork(),flags,jobid) < 0);
+ sh_stats(STAT_FORKS);
+ if(!shp->subshell)
+ {
+ sig = shp->savesig;
+ shp->savesig = 0;
+ if(sig>0)
+ sh_fault(sig);
+ }
+ job_fork(parent);
+ return(parent);
+}
+
+struct Tdata
+{
+ Shell_t *sh;
+ Namval_t *tp;
+ void *extra[2];
+};
+
+/*
+ * add exports from previous scope to the new scope
+ */
+static void local_exports(register Namval_t *np, void *data)
+{
+ Shell_t *shp = ((struct Tdata*)data)->sh;
+ register Namval_t *mp;
+ register char *cp;
+ if(nv_isarray(np))
+ nv_putsub(np,NIL(char*),0);
+ if((cp = nv_getval(np)) && (mp = nv_search(nv_name(np), shp->var_tree, NV_ADD|HASH_NOSCOPE)) && nv_isnull(mp))
+ nv_putval(mp, cp, 0);
+}
+
+/*
+ * This routine executes .sh.math functions from within ((...)))
+*/
+Sfdouble_t sh_mathfun(Shell_t *shp,void *fp, int nargs, Sfdouble_t *arg)
+{
+ Sfdouble_t d;
+ Namval_t node,*mp,*np, *nref[9], **nr=nref;
+ char *argv[2];
+ struct funenv funenv;
+ int i;
+ np = (Namval_t*)fp;
+ funenv.node = np;
+ funenv.nref = nref;
+ funenv.env = 0;
+ memcpy(&node,SH_VALNOD,sizeof(node));
+ SH_VALNOD->nvfun = 0;
+ SH_VALNOD->nvenv = 0;
+ SH_VALNOD->nvflag = NV_LDOUBLE|NV_NOFREE;
+ SH_VALNOD->nvalue.ldp = 0;
+ for(i=0; i < nargs; i++)
+ {
+ *nr++ = mp = nv_namptr(shp->mathnodes,i);
+ mp->nvalue.ldp = arg++;
+ }
+ *nr = 0;
+ SH_VALNOD->nvalue.ldp = &d;
+ argv[0] = np->nvname;
+ argv[1] = 0;
+ sh_funscope(1,argv,0,&funenv,0);
+ while(mp= *nr++)
+ mp->nvalue.ldp = 0;
+ SH_VALNOD->nvfun = node.nvfun;
+ SH_VALNOD->nvflag = node.nvflag;
+ SH_VALNOD->nvenv = node.nvenv;
+ SH_VALNOD->nvalue.ldp = node.nvalue.ldp;
+ return(d);
+}
+
+/*
+ * This routine is used to execute the given function <fun> in a new scope
+ * If <fun> is NULL, then arg points to a structure containing a pointer
+ * to a function that will be executed in the current environment.
+ */
+int sh_funscope(int argn, char *argv[],int(*fun)(void*),void *arg,int execflg)
+{
+ register char *trap;
+ register int nsig;
+ register Shell_t *shp = sh_getinterp();
+ struct dolnod *argsav=0,*saveargfor;
+ struct sh_scoped savst, *prevscope = shp->st.self;
+ struct argnod *envlist=0;
+ int jmpval;
+ volatile int r = 0;
+ int n;
+ char *savstak;
+ struct funenv *fp = 0;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ Namval_t *nspace = shp->namespace;
+ Dt_t *last_root = shp->last_root;
+ Shopt_t options;
+#if SHOPT_NAMESPACE
+ Namval_t *np;
+#endif /* SHOPT_NAMESPACE */
+ options = shp->options;
+ if(shp->fn_depth==0)
+ shp->glob_options = shp->options;
+ else
+ shp->options = shp->glob_options;
+#if 0
+ shp->st.lineno = error_info.line;
+#endif
+ *prevscope = shp->st;
+ sh_offoption(SH_ERREXIT);
+ shp->st.prevst = prevscope;
+ shp->st.self = &savst;
+ shp->topscope = (Shscope_t*)shp->st.self;
+ shp->st.opterror = shp->st.optchar = 0;
+ shp->st.optindex = 1;
+ shp->st.loopcnt = 0;
+ if(!fun)
+ {
+ fp = (struct funenv*)arg;
+ shp->st.real_fun = (fp->node)->nvalue.rp;
+ envlist = fp->env;
+ }
+ prevscope->save_tree = shp->var_tree;
+ n = dtvnext(prevscope->save_tree)!= (shp->namespace?shp->var_base:0);
+#if SHOPT_NAMESPACE
+ if(n && fp && (np=(fp->node)->nvalue.rp->nspace) && np!=shp->namespace)
+ shp->namespace = np;
+#endif /* SHOPT_NAMESPACE */
+ sh_scope(shp,envlist,1);
+ if(n)
+ {
+ struct Tdata tdata;
+ memset(&tdata,0,sizeof(tdata));
+ tdata.sh = shp;
+ /* eliminate parent scope */
+ nv_scan(prevscope->save_tree, local_exports,&tdata, NV_EXPORT, NV_EXPORT|NV_NOSCOPE);
+ }
+ shp->st.save_tree = shp->var_tree;
+ if(!fun)
+ {
+ if(nv_isattr(fp->node,NV_TAGGED))
+ sh_onoption(SH_XTRACE);
+ else
+ sh_offoption(SH_XTRACE);
+ }
+ shp->st.cmdname = argv[0];
+ /* save trap table */
+ if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
+ {
+ nsig += sizeof(char*);
+ memcpy(savstak=stakalloc(nsig),(char*)&shp->st.trapcom[0],nsig);
+ }
+ sh_sigreset(0);
+ argsav = sh_argnew(shp,argv,&saveargfor);
+ sh_pushcontext(shp,buffp,SH_JMPFUN);
+ errorpush(&buffp->err,0);
+ error_info.id = argv[0];
+ shp->st.var_local = shp->var_tree;
+ if(!fun)
+ {
+ shp->st.filename = fp->node->nvalue.rp->fname;
+ shp->st.funname = nv_name(fp->node);
+ shp->last_root = nv_dict(DOTSHNOD);
+ nv_putval(SH_PATHNAMENOD,shp->st.filename,NV_NOFREE);
+ nv_putval(SH_FUNNAMENOD,shp->st.funname,NV_NOFREE);
+ }
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval == 0)
+ {
+ if(shp->fn_depth++ > MAXDEPTH)
+ {
+ shp->toomany = 1;
+ siglongjmp(*shp->jmplist,SH_JMPERRFN);
+ }
+ else if(fun)
+ r= (*fun)(arg);
+ else
+ {
+ char **arg = shp->st.real_fun->argv;
+ Namval_t *np, *nq, **nref;
+ if(nref=fp->nref)
+ {
+ shp->last_root = 0;
+ for(r=0; arg[r]; r++)
+ {
+ np = nv_search(arg[r],shp->var_tree,HASH_NOSCOPE|NV_ADD);
+ if(np && (nq=*nref++))
+ {
+ np->nvalue.nrp = newof(0,struct Namref,1,0);
+ np->nvalue.nrp->np = nq;
+ nv_onattr(np,NV_REF|NV_NOFREE);
+ }
+ }
+ }
+ sh_exec((Shnode_t*)(nv_funtree((fp->node))),execflg|SH_ERREXIT);
+ r = shp->exitval;
+ }
+ }
+ if(--shp->fn_depth==1 && jmpval==SH_JMPERRFN)
+ errormsg(SH_DICT,ERROR_exit(1),e_toodeep,argv[0]);
+ sh_popcontext(shp,buffp);
+ if (shp->st.self != &savst)
+ shp->var_tree = (Dt_t*)savst.save_tree;
+ sh_unscope(shp);
+ shp->namespace = nspace;
+ shp->var_tree = (Dt_t*)prevscope->save_tree;
+ if(shp->topscope != (Shscope_t*)shp->st.self)
+ sh_setscope(shp->topscope);
+ sh_argreset(shp,argsav,saveargfor);
+ trap = shp->st.trapcom[0];
+ shp->st.trapcom[0] = 0;
+ sh_sigreset(1);
+ if (shp->st.self != &savst)
+ *shp->st.self = shp->st;
+ shp->st = *prevscope;
+ shp->topscope = (Shscope_t*)prevscope;
+ nv_getval(sh_scoped(shp,IFSNOD));
+ if(nsig)
+ memcpy((char*)&shp->st.trapcom[0],savstak,nsig);
+ shp->trapnote=0;
+ if(nsig)
+ stakset(savstak,0);
+ shp->options = options;
+ shp->last_root = last_root;
+ if(jmpval == SH_JMPSUB)
+ siglongjmp(*shp->jmplist,jmpval);
+ if(trap)
+ {
+ sh_trap(trap,0);
+ free(trap);
+ }
+ if(jmpval)
+ r=shp->exitval;
+ if(r>SH_EXITSIG && ((r&SH_EXITMASK)==SIGINT || ((r&SH_EXITMASK)==SIGQUIT)))
+ sh_fault(r&SH_EXITMASK);
+ if(jmpval > SH_JMPFUN)
+ {
+ sh_chktrap(shp);
+ siglongjmp(*shp->jmplist,jmpval);
+ }
+ return(r);
+}
+
+static void sh_funct(Shell_t *shp,Namval_t *np,int argn, char *argv[],struct argnod *envlist,int execflg)
+{
+ struct funenv fun;
+ char *fname = nv_getval(SH_FUNNAMENOD);
+ struct Level *lp =(struct Level*)(SH_LEVELNOD->nvfun);
+ int level, pipepid=shp->pipepid, comsub=shp->comsub;
+ shp->comsub = 0;
+ shp->pipepid = 0;
+ sh_stats(STAT_FUNCT);
+ if(!lp->hdr.disc)
+ lp = init_level(shp,0);
+ if((struct sh_scoped*)shp->topscope != shp->st.self)
+ sh_setscope(shp->topscope);
+ level = lp->maxlevel = shp->dot_depth + shp->fn_depth+1;
+ SH_LEVELNOD->nvalue.s = lp->maxlevel;
+ shp->st.lineno = error_info.line;
+ if(nv_isattr(np,NV_FPOSIX))
+ {
+ char *save;
+ int loopcnt = shp->st.loopcnt;
+ shp->posix_fun = np;
+ save = argv[-1];
+ argv[-1] = 0;
+ shp->st.funname = nv_name(np);
+ shp->last_root = nv_dict(DOTSHNOD);
+ nv_putval(SH_FUNNAMENOD, nv_name(np),NV_NOFREE);
+ opt_info.index = opt_info.offset = 0;
+ error_info.errors = 0;
+ shp->st.loopcnt = 0;
+ b_dot_cmd(argn+1,argv-1,&shp->bltindata);
+ shp->st.loopcnt = loopcnt;
+ argv[-1] = save;
+ }
+ else
+ {
+ fun.env = envlist;
+ fun.node = np;
+ fun.nref = 0;
+ sh_funscope(argn,argv,0,&fun,execflg);
+ }
+ if(level-- != nv_getnum(SH_LEVELNOD))
+ {
+ Shscope_t *sp = sh_getscope(0,SEEK_END);
+ sh_setscope(sp);
+ }
+ lp->maxlevel = level;
+ SH_LEVELNOD->nvalue.s = lp->maxlevel;
+ shp->last_root = nv_dict(DOTSHNOD);
+ shp->comsub = comsub;
+#if 0
+ nv_putval(SH_FUNNAMENOD,shp->st.funname,NV_NOFREE);
+#else
+ nv_putval(SH_FUNNAMENOD,fname,NV_NOFREE);
+#endif
+ nv_putval(SH_PATHNAMENOD,shp->st.filename,NV_NOFREE);
+ shp->pipepid = pipepid;
+}
+
+/*
+ * external interface to execute a function without arguments
+ * <np> is the function node
+ * If <nq> is not-null, then sh.name and sh.subscript will be set
+ */
+int sh_fun(Namval_t *np, Namval_t *nq, char *argv[])
+{
+ Shell_t *shp = sh_getinterp();
+ register int offset;
+ register char *base;
+ Namval_t node;
+ struct Namref nr;
+ long mode;
+ char *prefix = shp->prefix;
+ int n=0;
+ char *av[3];
+ Fcin_t save;
+ fcsave(&save);
+ if((offset=staktell())>0)
+ base=stakfreeze(0);
+ shp->prefix = 0;
+ if(!argv)
+ {
+ argv = av+1;
+ argv[1]=0;
+ }
+ argv[0] = nv_name(np);
+ while(argv[n])
+ n++;
+ if(nq)
+ mode = set_instance(shp,nq,&node, &nr);
+ if(is_abuiltin(np))
+ {
+ int jmpval;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ Shbltin_t *bp = &shp->bltindata;
+ sh_pushcontext(shp,buffp,SH_JMPCMD);
+ jmpval = sigsetjmp(buffp->buff,1);
+ if(jmpval == 0)
+ {
+ bp->bnode = np;
+ bp->ptr = nv_context(np);
+ errorpush(&buffp->err,0);
+ error_info.id = argv[0];
+ opt_info.index = opt_info.offset = 0;
+ opt_info.disc = 0;
+ shp->exitval = 0;
+ shp->exitval = ((Shbltin_f)funptr(np))(n,argv,bp);
+ }
+ sh_popcontext(shp,buffp);
+ if(jmpval>SH_JMPCMD)
+ siglongjmp(*shp->jmplist,jmpval);
+ }
+ else
+ sh_funct(shp,np,n,argv,(struct argnod*)0,sh_isstate(SH_ERREXIT));
+ if(nq)
+ unset_instance(nq, &node, &nr, mode);
+ fcrestore(&save);
+ if(offset>0)
+ stakset(base,offset);
+ shp->prefix = prefix;
+ return(shp->exitval);
+}
+
+/*
+ * This dummy routine is called by built-ins that do recursion
+ * on the file system (chmod, chgrp, chown). It causes
+ * the shell to invoke the non-builtin version in this case
+ */
+int cmdrecurse(int argc, char* argv[], int ac, char* av[])
+{
+ NOT_USED(argc);
+ NOT_USED(argv[0]);
+ NOT_USED(ac);
+ NOT_USED(av[0]);
+ return(SH_RUNPROG);
+}
+
+/*
+ * set up pipe for cooperating process
+ */
+static void coproc_init(Shell_t *shp, int pipes[])
+{
+ int outfd;
+ if(shp->coutpipe>=0 && shp->cpid)
+ errormsg(SH_DICT,ERROR_exit(1),e_pexists);
+ shp->cpid = 0;
+ if(shp->cpipe[0]<=0 || shp->cpipe[1]<=0)
+ {
+ /* first co-process */
+ sh_pclose(shp->cpipe);
+ sh_pipe(shp->cpipe);
+ if((outfd=shp->cpipe[1]) < 10)
+ {
+ int fd=fcntl(shp->cpipe[1],F_DUPFD,10);
+ if(fd>=10)
+ {
+ shp->fdstatus[fd] = (shp->fdstatus[outfd]&~IOCLEX);
+ close(outfd);
+ shp->fdstatus[outfd] = IOCLOSE;
+ shp->cpipe[1] = fd;
+ }
+ }
+ if(fcntl(*shp->cpipe,F_SETFD,FD_CLOEXEC)>=0)
+ shp->fdstatus[shp->cpipe[0]] |= IOCLEX;
+ shp->fdptrs[shp->cpipe[0]] = shp->cpipe;
+
+ if(fcntl(shp->cpipe[1],F_SETFD,FD_CLOEXEC) >=0)
+ shp->fdstatus[shp->cpipe[1]] |= IOCLEX;
+ }
+ shp->outpipe = shp->cpipe;
+ sh_pipe(shp->inpipe=pipes);
+ shp->coutpipe = shp->inpipe[1];
+ shp->fdptrs[shp->coutpipe] = &shp->coutpipe;
+ if(fcntl(shp->outpipe[0],F_SETFD,FD_CLOEXEC)>=0)
+ shp->fdstatus[shp->outpipe[0]] |= IOCLEX;
+}
+
+#if SHOPT_SPAWN
+
+
+#if SHOPT_AMP || !defined(_lib_fork)
+
+/*
+ * create a shell script consisting of t->fork.forktre and execute it
+ */
+static int run_subshell(Shell_t *shp,const Shnode_t *t,pid_t grp)
+{
+ static const char prolog[] = "(print $(typeset +A);set; typeset -p; print .sh.dollar=$$;set +o)";
+ register int i, fd, trace = sh_isoption(SH_XTRACE);
+ int pin,pout;
+ pid_t pid;
+ char *arglist[3], *envlist[2], devfd[12], *cp;
+ Sfio_t *sp = sftmp(0);
+ envlist[0] = "_=" SH_ID;
+ envlist[1] = 0;
+ arglist[0] = error_info.id?error_info.id:shp->shname;
+ if(*arglist[0]=='-')
+ arglist[0]++;
+ arglist[1] = devfd;
+ strncpy(devfd,e_devfdNN,sizeof(devfd));
+ arglist[2] = 0;
+ sfstack(sfstdout,sp);
+ if(trace)
+ sh_offoption(SH_XTRACE);
+ sfwrite(sfstdout,"typeset -A -- ",14);
+ sh_trap(prolog,0);
+ nv_scan(shp->fun_tree, print_fun, (void*)0,0, 0);
+ if(shp->st.dolc>0)
+ {
+ /* pass the positional parameters */
+ char **argv = shp->st.dolv+1;
+ sfwrite(sfstdout,"set --",6);
+ while(*argv)
+ sfprintf(sfstdout," %s",sh_fmtq(*argv++));
+ sfputc(sfstdout,'\n');
+ }
+ pin = (shp->inpipe?shp->inpipe[1]:0);
+ pout = (shp->outpipe?shp->outpipe[0]:0);
+ for(i=3; i < 10; i++)
+ {
+ if(shp->fdstatus[i]&IOCLEX && i!=pin && i!=pout)
+ {
+ sfprintf(sfstdout,"exec %d<&%d\n",i,i);
+ fcntl(i,F_SETFD,0);
+ }
+ }
+ sfprintf(sfstdout,"LINENO=%d\n",t->fork.forkline);
+ if(trace)
+ {
+ sfwrite(sfstdout,"set -x\n",7);
+ sh_onoption(SH_XTRACE);
+ }
+ sfstack(sfstdout,NIL(Sfio_t*));
+ sh_deparse(sp,t->fork.forktre,0);
+ sfseek(sp,(Sfoff_t)0,SEEK_SET);
+ fd = sh_dup(sffileno(sp));
+ cp = devfd+8;
+ if(fd>9)
+ *cp++ = '0' + (fd/10);
+ *cp++ = '0' + fd%10;
+ *cp = 0;
+ sfclose(sp);
+ sfsync(NIL(Sfio_t*));
+ if(!shp->gd->shpath)
+ shp->gd->shpath = pathshell();
+ pid = spawnveg(shp->shpath,arglist,envlist,grp);
+ close(fd);
+ for(i=3; i < 10; i++)
+ {
+ if(shp->fdstatus[i]&IOCLEX && i!=pin && i!=pout)
+ fcntl(i,F_SETFD,FD_CLOEXEC);
+ }
+ if(pid <=0)
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arglist[0]);
+ return(pid);
+}
+#endif /* !_lib_fork */
+
+static void sigreset(Shell_t *shp,int mode)
+{
+ register char *trap;
+ register int sig=shp->st.trapmax;
+ while(sig-- > 0)
+ {
+ if(sig==SIGCHLD)
+ continue;
+ if((trap=shp->st.trapcom[sig]) && *trap==0)
+ signal(sig,mode?sh_fault:SIG_IGN);
+ }
+}
+
+/*
+ * A combined fork/exec for systems with slow or non-existent fork()
+ */
+static pid_t sh_ntfork(Shell_t *shp,const Shnode_t *t,char *argv[],int *jobid,int flag)
+{
+ static pid_t spawnpid;
+ static int savetype;
+ static int savejobid;
+ struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));
+ int otype=0, jmpval,jobfork=0;
+ volatile int jobwasset=0, scope=0, sigwasset=0;
+ char **arge, *path;
+ volatile pid_t grp = 0;
+ Pathcomp_t *pp;
+ if(flag)
+ {
+ otype = savetype;
+ savetype=0;
+ }
+# if SHOPT_AMP || !defined(_lib_fork)
+ if(!argv)
+ {
+ register Shnode_t *tchild = t->fork.forktre;
+ int optimize=0;
+ otype = t->tre.tretyp;
+ savetype = otype;
+ spawnpid = 0;
+# ifndef _lib_fork
+ if((tchild->tre.tretyp&COMMSK)==TCOM)
+ {
+ Namval_t *np = (Namval_t*)(tchild->com.comnamp);
+ if(np)
+ {
+ path = nv_name(np);
+ if(!nv_isattr(np,BLT_ENV))
+ np=0;
+ else if(strcmp(path,"echo")==0 || memcmp(path,"print",5)==0)
+ np=0;
+ }
+ else if(!tchild->com.comarg)
+ optimize=1;
+ else if(tchild->com.comtyp&COMSCAN)
+ {
+ if(tchild->com.comarg->argflag&ARG_RAW)
+ path = tchild->com.comarg->argval;
+ else
+ path = 0;
+ }
+ else
+ path = ((struct dolnod*)tchild->com.comarg)->dolval[ARG_SPARE];
+ if(!np && path && !nv_search(path,shp->fun_tree,0))
+ optimize=1;
+ }
+# endif
+ sh_pushcontext(shp,buffp,SH_JMPIO);
+ jmpval = sigsetjmp(buffp->buff,0);
+ {
+ if((otype&FINT) && !sh_isstate(SH_MONITOR))
+ {
+ signal(SIGQUIT,SIG_IGN);
+ signal(SIGINT,SIG_IGN);
+ if(!shp->st.ioset)
+ {
+ sh_iosave(shp,0,buffp->topfd,(char*)0);
+ sh_iorenumber(shp,sh_chkopen(e_devnull),0);
+ }
+ }
+ if(otype&FPIN)
+ {
+ int fd = shp->inpipe[1];
+ sh_iosave(shp,0,buffp->topfd,(char*)0);
+ sh_iorenumber(shp,shp->inpipe[0],0);
+ if(fd>=0 && (!(otype&FPOU) || (otype&FCOOP)) && fcntl(fd,F_SETFD,FD_CLOEXEC)>=0)
+ shp->fdstatus[fd] |= IOCLEX;
+ }
+ if(otype&FPOU)
+ {
+#if SHOPT_COSHELL
+ if(shp->outpipe[2] > 20000)
+ sh_coaccept(shp,shp->outpipe,1);
+#endif /* SHOPT_COSHELL */
+ sh_iosave(shp,1,buffp->topfd,(char*)0);
+ sh_iorenumber(shp,sh_dup(shp->outpipe[1]),1);
+ if(fcntl(shp->outpipe[0],F_SETFD,FD_CLOEXEC)>=0)
+ shp->fdstatus[shp->outpipe[0]] |= IOCLEX;
+ }
+
+ if(t->fork.forkio)
+ sh_redirect(shp,t->fork.forkio,0);
+ if(optimize==0)
+ {
+#ifdef SIGTSTP
+ if(job.jobcontrol)
+ {
+ signal(SIGTTIN,SIG_DFL);
+ signal(SIGTTOU,SIG_DFL);
+ }
+#endif /* SIGTSTP */
+#ifdef JOBS
+ if(sh_isstate(SH_MONITOR) && (job.jobcontrol || (otype&FAMP)))
+ {
+ if((otype&FAMP) || job.curpgid==0)
+ grp = 1;
+ else
+ grp = job.curpgid;
+ }
+#endif /* JOBS */
+ spawnpid = run_subshell(shp,t,grp);
+ }
+ else
+ {
+ sh_exec(tchild,SH_NTFORK);
+ if(jobid)
+ *jobid = savejobid;
+ }
+ }
+ sh_popcontext(shp,buffp);
+ if((otype&FINT) && !sh_isstate(SH_MONITOR))
+ {
+ signal(SIGQUIT,sh_fault);
+ signal(SIGINT,sh_fault);
+ }
+ if((otype&FPIN) && (!(otype&FPOU) || (otype&FCOOP)) && fcntl(shp->inpipe[1],F_SETFD,FD_CLOEXEC)>=0)
+ shp->fdstatus[shp->inpipe[1]] &= ~IOCLEX;
+ if(t->fork.forkio || otype)
+ sh_iorestore(shp,buffp->topfd,jmpval);
+ if(optimize==0)
+ {
+#ifdef SIGTSTP
+ if(job.jobcontrol)
+ {
+ signal(SIGTTIN,SIG_IGN);
+ signal(SIGTTOU,SIG_IGN);
+ }
+#endif /* SIGTSTP */
+ if(spawnpid>0)
+ _sh_fork(shp,spawnpid,otype,jobid);
+ if(grp>0 && !(otype&FAMP))
+ {
+ while(tcsetpgrp(job.fd,job.curpgid)<0 && job.curpgid!=spawnpid)
+ job.curpgid = spawnpid;
+ }
+ }
+ savetype=0;
+ if(jmpval>SH_JMPIO)
+ siglongjmp(*shp->jmplist,jmpval);
+ if(spawnpid<0 && (otype&FCOOP))
+ {
+ sh_close(shp->coutpipe);
+ sh_close(shp->cpipe[1]);
+ shp->cpipe[1] = -1;
+ shp->coutpipe = -1;
+ }
+ shp->exitval = 0;
+ return(spawnpid);
+ }
+# endif /* !_lib_fork */
+ sh_pushcontext(shp,buffp,SH_JMPCMD);
+ errorpush(&buffp->err,ERROR_SILENT);
+ jmpval = sigsetjmp(buffp->buff,0);
+ if(jmpval == 0)
+ {
+ if((otype&FINT) && !sh_isstate(SH_MONITOR))
+ {
+ signal(SIGQUIT,SIG_IGN);
+ signal(SIGINT,SIG_IGN);
+ }
+ spawnpid = -1;
+ if(t->com.comio)
+ sh_redirect(shp,t->com.comio,0);
+ error_info.id = *argv;
+ if(t->com.comset)
+ {
+ scope++;
+ sh_scope(shp,t->com.comset,0);
+ }
+ if(!strchr(path=argv[0],'/'))
+ {
+ Namval_t *np;
+ if((np=nv_search(path,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && np->nvalue.cp)
+ path = nv_getval(np);
+ else if(path_absolute(shp,path,NIL(Pathcomp_t*)))
+ {
+ path = stkptr(shp->stk,PATH_OFFSET);
+ stkfreeze(shp->stk,0);
+ }
+ else
+ {
+ pp=path_get(shp,path);
+ while(pp)
+ {
+ if(pp->len==1 && *pp->name=='.')
+ break;
+ pp = pp->next;
+ }
+ if(!pp)
+ path = 0;
+ }
+ }
+ else if(sh_isoption(SH_RESTRICTED))
+ errormsg(SH_DICT,ERROR_exit(1),e_restricted,path);
+ if(!path)
+ {
+ spawnpid = -1;
+ goto fail;
+ }
+ arge = sh_envgen();
+ shp->exitval = 0;
+#ifdef SIGTSTP
+ if(job.jobcontrol)
+ {
+ signal(SIGTTIN,SIG_DFL);
+ signal(SIGTTOU,SIG_DFL);
+ jobwasset++;
+ }
+#endif /* SIGTSTP */
+#ifdef JOBS
+ if(sh_isstate(SH_MONITOR) && (job.jobcontrol || (otype&FAMP)))
+ {
+ if((otype&FAMP) || job.curpgid==0)
+ grp = 1;
+ else
+ grp = job.curpgid;
+ }
+#endif /* JOBS */
+
+ sfsync(NIL(Sfio_t*));
+ sigreset(shp,0); /* set signals to ignore */
+ sigwasset++;
+ /* find first path that has a library component */
+ for(pp=path_get(shp,argv[0]); pp && !pp->lib ; pp=pp->next);
+ job_fork(-1);
+ jobfork = 1;
+ spawnpid = path_spawn(shp,path,argv,arge,pp,(grp<<1)|1);
+ if(spawnpid < 0 && errno==ENOEXEC)
+ {
+ char *devfd;
+ int fd = open(path,O_RDONLY);
+ argv[-1] = argv[0];
+ argv[0] = path;
+ if(fd>=0)
+ {
+ struct stat statb;
+ sfprintf(shp->strbuf,"/dev/fd/%d",fd);
+ if(stat(devfd=sfstruse(shp->strbuf),&statb)>=0)
+ argv[0] = devfd;
+ }
+ if(!shp->gd->shpath)
+ shp->gd->shpath = pathshell();
+ spawnpid = path_spawn(shp,shp->gd->shpath,&argv[-1],arge,pp,(grp<<1)|1);
+ if(fd>=0)
+ close(fd);
+ argv[0] = argv[-1];
+ }
+ fail:
+ if(jobfork && spawnpid<0)
+ job_fork(0);
+ if(spawnpid < 0) switch(errno=shp->path_err)
+ {
+ case ENOENT:
+ errormsg(SH_DICT,ERROR_system(ERROR_NOENT),e_found+4);
+ default:
+ errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec+4);
+ }
+ }
+ else
+ exitset();
+ sh_popcontext(shp,buffp);
+ if(buffp->olist)
+ free_list(buffp->olist);
+#ifdef SIGTSTP
+ if(jobwasset)
+ {
+ signal(SIGTTIN,SIG_IGN);
+ signal(SIGTTOU,SIG_IGN);
+ }
+#endif /* SIGTSTP */
+ if(sigwasset)
+ sigreset(shp,1); /* restore ignored signals */
+ if(scope)
+ {
+ sh_unscope(shp);
+ if(jmpval==SH_JMPSCRIPT)
+ nv_setlist(t->com.comset,NV_EXPORT|NV_IDENT|NV_ASSIGN,0);
+ }
+ if(t->com.comio && (jmpval || spawnpid<=0))
+ sh_iorestore(shp,buffp->topfd,jmpval);
+ if(jmpval>SH_JMPCMD)
+ siglongjmp(*shp->jmplist,jmpval);
+ if(spawnpid>0)
+ {
+ _sh_fork(shp,spawnpid,otype,jobid);
+ job_fork(spawnpid);
+#ifdef JOBS
+ if(grp==1)
+ job.curpgid = spawnpid;
+# ifdef SIGTSTP
+ if(grp>0 && !(otype&FAMP))
+ {
+ while(tcsetpgrp(job.fd,job.curpgid)<0 && job.curpgid!=spawnpid)
+ job.curpgid = spawnpid;
+ }
+# endif /* SIGTSTP */
+#endif /* JOBS */
+ savejobid = *jobid;
+ if(otype)
+ return(0);
+ }
+ return(spawnpid);
+}
+
+# ifdef _was_lib_fork
+# define _lib_fork 1
+# endif
+# ifndef _lib_fork
+ pid_t fork(void)
+ {
+ errormsg(SH_DICT,ERROR_exit(3),e_notimp,"fork");
+ return(-1);
+ }
+# endif /* _lib_fork */
+#endif /* SHOPT_SPAWN */