684 lines
13 KiB
C
684 lines
13 KiB
C
/* 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<<c;
|
|
if(opt&(1|INTFLG|RSHFLG))
|
|
sh_fail(*argv,gettxt(_SGI_DMMX_e_option,e_option),ERROR);
|
|
}
|
|
}
|
|
if(minus)
|
|
{
|
|
#if ESH || VSH
|
|
if(opt&(EDITVI|EMACS|GMACS))
|
|
newflags &= ~ (EDITVI|EMACS|GMACS);
|
|
#endif
|
|
newflags |= opt;
|
|
}
|
|
else
|
|
{
|
|
if(opt==EXECPR)
|
|
trace = 0;
|
|
newflags &= ~opt;
|
|
}
|
|
}
|
|
}
|
|
/* cannot set -n for interactive shells since there is no way out */
|
|
if(is_option(INTFLG))
|
|
newflags &= ~NOEXEC;
|
|
#ifdef RAWONLY
|
|
if(is_option(EDITVI))
|
|
newflags |= VIRAW;
|
|
#endif /* RAWONLY */
|
|
if(!builtin_flag)
|
|
goto skip;
|
|
if(sort)
|
|
{
|
|
if(argc>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<<value;
|
|
p_str(syscan->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;
|
|
}
|
|
}
|
|
|