/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */ /* UNIX System Laboratories, Inc. */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ #ident "@(#)ksh:sh/args.c 1.4.4.1" /* * UNIX shell * * S. R. Bourne * Rewritten by David Korn * AT&T Bell Laboratories * */ #include "defs.h" #ifdef DEVFD # include "jobs.h" #endif /* DEVFD */ #include "terminal.h" #undef ESCAPE #include "sym.h" #include "builtins.h" #ifdef DEVFD void close_pipes(); #endif /* DEVFD */ extern void gsort(); extern int strcmp(); static int arg_expand(); static struct dolnod* copyargs(); static void print_opts(); static int split(); static char *null; static struct dolnod *argfor; /* linked list of blocks to be cleaned up */ static struct dolnod *dolh; static char flagadr[12]; static const char flagchar[] = { 'i', 'n', 'v', 't', 's', 'x', 'e', 'r', 'k', 'u', 'f', 'a', 'm', 'h', 'p', 'c', 'C', 'b', 'w', 'P',0 }; static const optflag flagval[] = { INTFLG, NOEXEC, READPR, ONEFLG, STDFLG, EXECPR, ERRFLG, RSHFLG, KEYFLG, NOSET, NOGLOB, ALLEXP, MONITOR, HASHALL, PRIVM, CFLAG, NOCLOB, NOTIFY, WORDEXP,NOCMDST,0 }; /* ======== option handling ======== */ /* * This routine turns options on and off * The options "sicr" 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 arg_opts(argc,com,builtin_flag) char **com; int argc; unsigned int builtin_flag; { register char *cp; register int c; register char *flagc; register char **argv = com; register optflag newflags=opt_flags; register optflag opt; int trace = is_option(EXECPR); char minus; struct namnod *np = (struct namnod*)0; char sort = 0; char minmin = 0; while((cp= *++argv) && ((c= *cp) == '-' || c=='+')) { minus = (c == '-'); argc--; if((c= *++cp)==0) { newflags &= ~(EXECPR|READPR); trace = 0; argv++; break; } else if(c == '-') { minmin = 1; argv++; break; } while(c= *cp++) { if(builtin_flag) { if(c=='s') { sort = 1; continue; } else if(c=='A') { if(argv[1]==0) sh_fail(*argv, gettxt(_SGI_DMMX_e_argexp,e_argexp),ERROR); np = env_namset(*++argv,sh.var_tree,P_FLAG|V_FLAG); argc--; if(minus) nam_free(np); continue; } else if(strchr("icrwP",c)) sh_fail(*argv, gettxt(_SGI_DMMX_e_option,e_option),ERROR); } if(c=='c' && minus && argc>=2 && sh.comdiv==0) { sh.comdiv= *++argv; argc--; newflags |= CFLAG; continue; } #ifdef apollo /* * New option(-D) allowing the user to define * envirnoment variables on the command line. */ if (c == 'D') /* define env variable */ { char *newenv; if (minus) { if (cp && *cp) { if (strchr(cp, '=')) newenv = cp; else { newenv = malloc(strlen(cp) + 2); strcpy(newenv, cp); strcat(newenv, "="); } env_namset(newenv,sh.var_tree,N_EXPORT|N_FREE); } } else sh_fail(*argv, gettxt(_SGI_DMMX_e_option,e_option),ERROR); argc--; break; } #endif /* apollo */ if(flagc=strchr(flagchar,c)) opt = flagval[flagc-flagchar]; else if(c != 'o') sh_fail(*argv,gettxt(_SGI_DMMX_e_option,e_option),ERROR); else { argv++; if(*argv==NIL) { if(trace) sh_trace(com,1); trace = 0; print_opts(newflags); argv--; continue; } else { argc--; c=sh_lookup(*argv,tab_options); opt = 1L<1) gsort(argv,argc-1,strcmp); else gsort(st.dolv+1,st.dolc,strcmp); } if((newflags&PRIVM) && !is_option(PRIVM)) { if((sh.userid!=sh.euserid && setuid(sh.euserid)<0) || (sh.groupid!=sh.egroupid && setgid(sh.egroupid)<0) || (sh.userid==sh.euserid && sh.groupid==sh.egroupid)) newflags &= ~PRIVM; } else if(!(newflags&PRIVM) && is_option(PRIVM)) { setuid(sh.userid); setgid(sh.groupid); if(sh.euserid==0) { sh.euserid = sh.userid; sh.egroupid = sh.groupid; } } skip: if(trace) sh_trace(com,1); opt_flags = newflags; if(builtin_flag) { argv--; if(np) env_arrayset(np,argc,argv); else if(argc>1 || minmin) arg_set(argv); } return(argc); } /* * returns the value of $- */ char *arg_dolminus() { register const char *flagc=flagchar; register char *flagp=flagadr; while(*flagc) { if(opt_flags&flagval[flagc-flagchar]) *flagp++ = *flagc; flagc++; } *flagp = 0; return(flagadr); } /* * set up positional parameters */ void arg_set(argi) char *argi[]; { register char **argp=argi; register int size = 0; /* count number of bytes needed for strings */ register char *cp; register int argn; /* count args and number of bytes of arglist */ while((cp=(char*)*argp++) != ENDARGS) { size += strlen(cp); } /* free old ones unless on for loop chain */ argn = argp - argi; arg_free(dolh,0); dolh=copyargs(argi, --argn, size); st.dolc=argn-1; } /* * 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 *arg_free(blk,flag) struct dolnod * blk; { register struct dolnod* argr=blk; register struct dolnod* argblk; if(argblk=argr) { if((--argblk->doluse)==0) { argr = argblk->dolnxt; if(flag && argblk==dolh) dolh->doluse = 1; else { /* delete from chain */ if(argfor == argblk) argfor = argblk->dolnxt; else { for(argr=argfor;argr;argr=argr->dolnxt) if(argr->dolnxt==argblk) break; if(argr==0) { return(NULL); } argr->dolnxt = argblk->dolnxt; argr = argblk->dolnxt; } free((char*)argblk); } } } return(argr); } /* * grab space for arglist and link argblock for cleanup * The strings are copied after the argment vector */ static struct dolnod *copyargs(from, n, size) char *from[]; { register struct dolnod *dp=new_of(struct dolnod,n*sizeof(char*)+size+n); register char **pp; register char *sp; dp->doluse=1; /* use count */ /* link into chain */ dp->dolnxt = argfor; argfor = dp; pp= dp->dolarg; st.dolv=pp; sp = (char*)dp + sizeof(struct dolnod) + n*sizeof(char*); while(n--) { *pp++ = sp; sp = sh_copy(*from++,sp) + 1; } *pp = ENDARGS; return(dp); } /* * used to set new argument chain for functions */ struct dolnod *arg_new(argi,savargfor) char *argi[]; struct dolnod **savargfor; { register struct dolnod *olddolh = dolh; *savargfor = argfor; dolh = NULL; argfor = NULL; arg_set(argi); return(olddolh); } /* * reset arguments as they were before function */ void arg_reset(blk,afor) struct dolnod *blk; struct dolnod *afor; { while(argfor=arg_free(argfor,0)); dolh = blk; argfor = afor; } void arg_clear() { /* force `for' $* lists to go away */ while(argfor=arg_free(argfor,1)); argfor = dolh; #ifdef DEVFD close_pipes(); #endif /* DEVFD */ } /* * increase the use count so that an arg_set will not make it go away */ struct dolnod *arg_use() { register struct dolnod *dh; if(dh=dolh) dh->doluse++; return(dh); } /* * Print option settings on standard output */ static void print_opts(oflags) #ifndef pdp11 register #endif /* pdp11 */ optflag oflags; { register const struct sysnod *syscan = tab_options; #ifndef pdp11 register #endif /* pdp11 */ optflag value; p_setout(st.standout); p_str(gettxt(_SGI_DMMX_e_heading,e_heading),NL); while(value=syscan->sysval) { value = 1<sysnam,SP); p_nchr(SP,16-strlen(syscan->sysnam)); if(oflags&value) p_str(gettxt(_SGI_DMMX_e_on,e_on),NL); else p_str(gettxt(_SGI_DMMX_e_off,e_off),NL); syscan++; } } #ifdef DEVFD static int to_close[15]; static int indx; void close_pipes() { register int *fd = to_close; while(*fd) { close(*fd); *fd++ = -1; } indx = 0; } #endif /* DEVFD */ #ifdef VPIX # define EXTRA 2 #else # define EXTRA 1 #endif /* VPIX */ /* * build an argument list */ char **arg_build(nargs,comptr) int *nargs; struct comnod *comptr; { register struct argnod *argp; { register struct comnod *ac = comptr; register struct argnod *schain; /* see if the arguments have already been expanded */ if(ac->comarg==NULL) { *nargs = 0; return(&null); } else if((ac->comtyp&COMSCAN)==0) { *nargs = ((struct dolnod*)ac->comarg)->doluse; return(((struct dolnod*)ac->comarg)->dolarg+EXTRA); } schain = st.gchain; st.gchain = NULL; #ifdef DEVFD close_pipes(); #endif /* DEVFD */ *nargs = 0; if(ac) { argp = ac->comarg; while(argp) { *nargs += arg_expand(argp); argp = argp->argnxt.ap; } } argp = st.gchain; st.gchain = schain; } { register char **comargn; register int argn; register char **comargm; argn = *nargs; argn += EXTRA; /* allow room to prepend args */ comargn=(char**)stakalloc((unsigned)(argn+1)*sizeof(char*)); comargm = comargn += argn; *comargn = ENDARGS; if(argp==0) { /* reserve an extra null pointer */ *--comargn = 0; return(comargn); } while(argp) { struct argnod *nextarg = argp->argchn; argp->argchn = 0; *--comargn = argp->argval; if((argp->argflag&A_RAW)==0) sh_trim(*comargn); if((argp=nextarg)==0 || (argp->argflag&A_MAKE)) { if((argn=comargm-comargn)>1) gsort(comargn,argn,strcmp); comargm = comargn; } } return(comargn); } } /* Argument expansion */ static int arg_expand(argp) register struct argnod *argp; { register int count = 0; argp->argflag &= ~A_MAKE; #ifdef DEVFD if(*argp->argval==0 && (argp->argflag&A_EXP)) { /* argument of the form (cmd) */ register struct argnod *ap; int pv[2]; int fd; ap = (struct argnod*)stakseek(ARGVAL); ap->argflag |= A_MAKE; ap->argflag &= ~A_RAW; ap->argchn= st.gchain; st.gchain = ap; count++; stakputs(e_devfd); io_popen(pv); fd = argp->argflag&A_RAW; stakputs(sh_itos(pv[fd])); ap = (struct argnod*)stakfreeze(1); sh.inpipe = sh.outpipe = 0; if(fd) { sh.inpipe = pv; sh_exec((union anynode*)argp->argchn,(int)(st.states&ERRFLG)); } else { sh.outpipe = pv; sh_exec((union anynode*)argp->argchn,(int)(st.states&ERRFLG)); } #ifdef JOBS job.pipeflag++; #endif /* JOBS */ close(pv[1-fd]); to_close[indx++] = pv[fd]; } else #endif /* DEVFD */ if((argp->argflag&A_RAW)==0) { register char *ap = argp->argval; if(argp->argflag&A_MAC) ap = mac_expand(ap); count = split(ap,argp->argflag&(A_SPLIT|A_EXP)); } else { argp->argchn= st.gchain; st.gchain = argp; argp->argflag |= A_MAKE; count++; } return(count); } static int split(s,macflg) /* blank interpretation routine */ char *s; { register int c,lastsep,bol; register struct argnod *ap; int count=0; int expflag = (!is_option(NOGLOB) && (macflg&A_EXP)); const char *seps; if(macflg &= A_SPLIT) seps = nam_fstrval(IFSNOD); else seps = e_nullstr; if(seps==NULL) seps = e_sptbnl; lastsep=0; bol=1; /* Beginning of line */ while(1) { if(sh.trapnote&SIGSET) sh_exit(SIGFAIL); ap = (struct argnod*)stakseek(ARGVAL); while(c= *s++) { if(c == ESCAPE) { c = *s++; if(c!='/') stakputc(ESCAPE); } else if(strchr(seps,c)) /* sep */ { if(macflg==0) continue; if(strchr(e_sptbnl,c)) /* Current is IFS white */ { if(bol || /* IFS white at bol or */ *s == 0 || /* next is eol or */ strchr(seps,*s) || /* next is sep or */ strchr(e_sptbnl,*s) || /* next is IFS white or */ lastsep) /* last was sep */ continue; } else { /* Seperator and not IFS white */ if(bol-- && /* beginning of line and */ !strchr(seps,*s) ) /* next is not seperator */ continue; } lastsep = c; bol=0; break; } stakputc(c); lastsep=0; bol=0; } /* This allows contiguous visible delimiters to count as delimiters */ if(staktell()==ARGVAL) { if(c==0) return(count); } else if(c==0) { s--; } /* file name generation */ ap = (struct argnod*)stakfreeze(1); ap->argflag &= ~(A_RAW|A_MAKE); #ifdef BRACEPAT if(expflag) count += expbrace(ap); #else if(expflag && (c=path_expand(ap->argval))) count += c; #endif /* BRACEPAT */ else { count++; ap->argchn= st.gchain; st.gchain = ap; } st.gchain->argflag |= A_MAKE; } }