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