diff options
Diffstat (limited to 'src/cmd/ksh93/sh')
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*)∓ + 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 = ¬ify_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 */ |