Files
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

1028 lines
17 KiB
C
Executable File

#ident "@(#)macro.c 1.10 95/03/21 SMI" /* From AT&T Toolchest */
/*
* UNIX shell
*
* S. R. Bourne
* AT&T Bell Laboratories
* Rewritten by David Korn
*
*/
#include "defs.h"
#include "sym.h"
#include "builtins.h"
#ifdef MULTIBYTE
# include "national.h"
#endif /* MULTIBYTE */
void mac_check();
/* These external routines are referenced by this module */
extern char *ltos();
extern void match_paren();
extern char *submatch();
#ifdef MULTIBYTE
static int charlen();
#endif /* MULTIBYTE */
static void copyto();
static int substring();
static void skipto();
static int getch();
static int comsubst();
static void mac_error();
static void mac_copy();
#ifdef POSIX
static void tilde_expand();
#endif /* POSIX */
static char quote; /* used locally */
static char quoted; /* used locally */
static char mflag; /* 0 for $x, 1 for here docs */
static const char *ifs;
static int w_fd = -1;
static int mactry;
static char *mac_current;
static jmp_buf mac_buf;
static char idb[2];
#ifdef FLOAT
extern char *etos(),*ftos();
static double numb;
#else
static long numb;
#endif /* FLOAT */
static void copyto(endch,newquote)
register char endch;
{
register int c;
register int count = 1;
int saveq = quote;
#ifdef POSIX
register int tilde = -1;
#endif /* POSIX */
quote = newquote;
#ifdef POSIX
/* check for tilde expansion */
c = io_readc();
if(c=='~' && !mflag && !quote)
tilde = staktell();
io_unreadc(c);
#endif /* POSIX */
while(c=getch(endch))
{
if((c==endch) && (saveq || !quote) && --count<=0)
break;
if(quote || c==ESCAPE)
{
if(c==ESCAPE)
{
c = io_readc();
if(quote && !escchar(c) && c!= '"')
{
stakputc(ESCAPE);
stakputc(ESCAPE);
}
}
if(!mflag || !escchar(c))
stakputc(ESCAPE);
}
stakputc(c);
if(c=='[' && endch==']')
count++;
#ifdef POSIX
else if(c=='/' && tilde>=0)
{
tilde_expand(tilde,c);
tilde = -1;
}
#endif /* POSIX */
}
#ifdef POSIX
if(tilde>=0)
tilde_expand(tilde,0);
#endif /* POSIX */
quote = saveq;
if(c!=endch)
mac_error();
}
#ifdef POSIX
/*
* <offset> is byte offset for beginning of tilde string
* if <c> is non-zero, append <c> to expansion
*/
static void tilde_expand(offset,c)
register offset;
{
extern char *sh_tilde();
register char *cp;
int curoff = staktell();
stakputc(0);
if(cp = sh_tilde(stakptr(offset)))
{
register struct namnod *n = (struct namnod *)NULL;
register char *v;
/* XPG4: ${P:-W} */
if ((*cp == '$')) {
n = nam_search(cp+1, sh.var_tree, 0);
v = nam_strval(n);
if (v == (char *)NULL)
v = cp;
} else
v = cp;
stakseek(offset);
mac_copy(v, -1);
if(c)
stakputc(c);
}
else
stakseek(curoff);
}
#endif /* POSIX */
/* skip chars up to } */
static void skipto(endch)
register char endch;
{
register char c;
while((c=io_readc()) && c!=endch)
{
switch(c)
{
case ESCAPE:
io_readc();
break;
case SQUOTE: case DQUOTE:
skipto(c);
break;
case DOLLAR:
if((c=io_readc()) == LBRACE)
skipto(RBRACE);
else if(!dolchar(c))
io_unreadc(c);
}
}
if(c!=endch)
mac_error();
}
static int getch(endch)
int endch;
{
register int c;
register int bra; /* {...} bra =1, {#...} bra=2 */
int atflag=0; /* set if $@ or ${array[@]} within double quotes */
retry:
c = io_readc();
if(c==DOLLAR)
{
register char *v;
register char *argp;
register struct namnod *n=(struct namnod*)NULL;
int dolg=0;
int dolmax = st.dolc+1;
int nulflg;
char *id=idb;
int offset;
int vsize = -1;
bra = 0;
*id = 0;
retry1:
c = io_readc();
switch(c)
{
case DOLLAR:
v=sh_itos(sh.pid);
break;
case '!':
if(sh.bckpid)
{
v=sh_itos(sh.bckpid);
}
else
v = "";
break;
case LBRACE:
if(bra++ ==0)
goto retry1;
case LPAREN:
if(bra==0 && mactry==0)
{
if(comsubst(1))
goto retry;
#ifdef FLOAT
if((long)numb==numb)
v = ltos((long)numb,10);
else
{
double abnumb = numb;
char *cp;
if(abnumb < 0)
abnumb = -abnumb;
if(abnumb <1e10 && abnumb>1e-10)
{
v = ftos(numb,12);
cp = v + strlen(v);
/* eliminate trailing zeros */
while(cp>v && *--cp=='0')
*cp = 0;
}
else
v = etos(numb,12);
}
#else
v = ltos(numb,10);
#endif /* FLOAT */
}
else
goto nosub;
break;
case RBRACE:
if(bra!=2)
goto nosub;
bra = 0;
case '#':
if(bra ==1)
{
bra++;
goto retry1;
}
v=sh_itos(st.dolc);
break;
case '?':
v=sh_itos(sh.savexit&EXITMASK);
break;
case '-':
v=arg_dolminus();
break;
default:
if(isalpha(c))
{
offset = staktell();
while(isalnum(c))
{
stakputc(c);
c = io_readc();
}
while(c=='[' && bra)
{
stakputc('[');
copyto(']',0);
*id = *stakptr(staktell()-1);
stakputc(']');
c = io_readc();
}
io_unreadc(c);
stakputc(0);
n=env_namset(stakptr(offset),sh.var_tree,P_FLAG);
stakseek(offset);
v = nam_strval(n);
c = (bra==2 && ((c= *id), astchar(c)));
if(nam_istype(n,N_ARRAY))
{
if(c || (array_next(n) && v))
dolg = -1;
else
dolg = 0;
}
else
{
if(c)
dolmax = 0;
id = n->namid;
}
goto cont1;
}
*id = c;
if(astchar(c))
{
dolg=1;
c=1;
}
else if(isdigit(c))
{
c -= '0';
if(bra)
{
int d;
while((d=io_readc(),isdigit(d)))
c = 10*c + (d-'0');
io_unreadc(d);
}
}
else
goto nosub;
if(c==0)
{
if((st.states&PROFILE) && !(st.states&FUNCTION))
v = sh.shname;
else
v = st.cmdadr;
}
else if(c <= st.dolc)
v = st.dolv[c];
else
dolg = 0, v = 0;
}
cont1:
c = io_readc();
if(bra==2)
{
if(c!=RBRACE)
mac_error();
if(dolg==0 && dolmax)
#ifdef MULTIBYTE
c = (v?charlen(v):0);
#else
c = (v?strlen(v):0);
#endif /* MULTIBYTE */
else if(dolg>0)
c = st.dolc;
else if(dolg<0)
c = array_elem(n);
else
c = (v!=0);
v = sh_itos(c);
dolg = 0;
c = RBRACE;
}
/* check for quotes @ */
if(idb[0]=='@' && quote && !atflag)
{
quoted--;
atflag = 1;
}
if(c==':' && bra) /* null and unset fix */
{
nulflg=1;
c=io_readc();
}
else
nulflg=0;
if(!defchar(c) && bra)
mac_error();
argp = 0;
if(bra)
{
if(c!=RBRACE)
{
offset = staktell();
if(((v==0 || (nulflg && *v==0)) ^ (setchar(c)!=0))
|| is_option(NOEXEC))
{
int newquote = quote;
if(c=='#' || c == '%')
newquote = 0;
copyto(RBRACE,newquote);
/* add null byte */
stakputc(0);
stakseek(staktell()-1);
}
else
skipto(RBRACE);
argp=stakptr(offset);
}
}
else
{
io_unreadc(c);
c=0;
}
/* check for substring operations */
if(c == '#' || c == '%')
{
if(dolg != 0)
mac_error();
if(v && *v)
{
#ifdef POSIX
char *pat;
int savec;
do
{
bra = 0;
if(*argp==c)
{
bra++;
argp++;
}
pat = argp;
while(1)
{
switch(*argp)
{
case '#': case '%':
case 0:
goto endloop;
case ESCAPE:
argp++;
default:
argp++;
}
}
endloop:
savec = *argp;
*argp++ = 0;
if(c=='#')
{
char *savev = v;
v = submatch(v,pat,bra);
if(v==0)
v = savev;
}
else
vsize = substring(v,pat,bra);
c = savec;
}
while(c);
#else
bra = 0;
if(*argp==c)
{
bra++;
argp++;
}
if(c=='#')
{
char *savev = v;
v = submatch(v,argp,bra);
if(v==0)
v = savev;
}
else
vsize = substring(v,argp,bra);
#endif /* POSIX */
}
if(v)
stakseek(offset);
}
retry2:
if(v && (!nulflg || *v ) && c!='+')
{
int type = *id;
#define sep bra
if(*ifs)
sep = *ifs;
else
sep = SP;
while(1)
{
/* quoted null strings have to be marked */
if(*v==0 && quote)
{
stakputc(ESCAPE);
stakputc(0);
}
mac_copy(v,vsize);
if(dolg==0)
break;
if(dolg>0)
{
if(++dolg >= dolmax)
break;
v = st.dolv[dolg];
}
else
{
if(type == 0)
break;
v = nam_strval(n);
type = array_next(n);
}
if(quote && *id=='*')
{
if(*ifs==0)
continue;
stakputc(ESCAPE);
}
stakputc(sep);
#undef sep
}
}
else if(argp)
{
if(c=='?' && !is_option(NOEXEC))
{
sh_trim(argp);
sh_fail(id,*argp?argp:e_nullset);
}
else if(c=='=')
{
if(n)
{
sh_trim(argp);
nam_putval(n,argp);
v = nam_strval(n);
nulflg = 0;
stakseek(offset);
goto retry2;
}
else
mac_error();
}
}
else if(is_option(NOSET))
#ifdef WEXP
{
if (is_option(WEXP_E))
cmd_shfail(id, e_wrde_badval, WEXP_BADVAL);
else
sh_fail(id,e_notset);
}
#else
sh_fail(id,e_notset);
#endif /* WEXP */
goto retry;
}
else if(c==endch)
return(c);
else if(c==SQUOTE && mactry==0)
{
comsubst(0);
goto retry;
}
else if(c==DQUOTE && !mflag)
{
if(quote ==0)
{
atflag = 0;
quoted++;
}
quote ^= 1;
goto retry;
}
return(c);
nosub:
if(bra)
mac_error();
io_unreadc(c);
return(DOLLAR);
}
/* Strip "" and do $ substitution
* Leaves result on top of stack
*/
char *mac_expand(as)
char *as;
{
register int savqu =quoted;
register int savq = quote;
register int savpeekn = st.peekn;
struct fileblk cb;
mac_current = as;
st.peekn = 0;
io_push(&cb);
io_sopen(as);
stakseek(0);
mflag = 0;
quote=0;
quoted=0;
if(!(ifs = nam_fstrval(IFSNOD)))
ifs = e_sptbnl;
copyto(0,0);
io_pop(1);
st.peekn = savpeekn;
if(quoted && staktell()==0)
{
stakputc(ESCAPE);
stakputc(0);
}
/* above is the fix for *'.c' bug */
quote=savq;
quoted=savqu;
return(stakfreeze(1));
}
/*
* command substitution
* type==0 for ``
* type==1 for $()
*/
static int comsubst(type)
int type;
{
struct fileblk cb;
register int fd;
#define d fd
register union anynode *t;
register char *argc;
struct ionod *saviotemp = st.iotemp;
struct slnod *saveslp = st.staklist;
int savem = mflag;
int savtop = staktell();
char *savptr = stakfreeze(0);
char inbuff[IOBSIZE+1];
int saveflags = (st.states&FIXFLG);
int stacksize = 0;
register int waitflag = 0;
#ifdef WEXP
if (is_option(WEXP_N))
cmd_shcfail(e_wrde_cmdsub, WEXP_CMDSUB);
#endif /* WEXP */
if(type)
{
type = ((d=io_readc())==LPAREN);
if(type || d==RPAREN)
stakputc(d);
else
io_unreadc(d);
if(d != RPAREN)
match_paren(LPAREN,RPAREN);
if(type && (d=io_readc())==RPAREN)
{
stakseek(staktell()-1);
argc = stakfreeze(1);
numb = sh_arith(mac_trim(argc+1,0));
stakset(savptr,savtop);
mflag = savem;
return(0);
}
else if(type)
{
/* nested command substitution, keep reading */
stakputc(d);
match_paren(LPAREN,RPAREN);
}
stakseek(staktell()-1);
}
else
{
while((d=io_readc())!=SQUOTE && d)
{
if(d==ESCAPE)
{
d = io_readc();
/*
* This is wrong but it preserves compatibility with
* the SVR2 shell
*/
if(!(escchar(d) || (d=='"' && quote)))
stakputc(ESCAPE);
}
stakputc(d);
}
}
argc=stakfreeze(1);
st.states &= ~FIXFLG; /* do not save command subs in history file */
if(w_fd>=0)
{
p_setout(w_fd);
p_flush(); /* flush before executing command */
}
io_push(&cb);
io_sopen(argc);
sh.nested_sub = 0;
st.exec_flag++;
t = sh_parse(EOFSYM,MTFLG|NLFLG);
st.exec_flag--;
if(!t || is_option(NOEXEC))
goto readit;
if(!sh.nested_sub && !t->tre.treio && is_rbuiltin(t))
{
/* nested command subs not handled specially */
/* handle command substitution of most builtins separately */
/* exec, login, cd, ., eval and shift not handled this way */
/* put output into tmpfile */
int save1_out = st.standout;
if((st.states&IS_TMP)==0)
{
char tmp_fname[TMPSIZ];
/* create and keep open a /tmp file for command subs */
fd = io_mktmp(tmp_fname);
fd = io_renumber(fd,TMPIO);
st.states |= IS_TMP;
/* root cannot unlink because fsck could give bad ref count */
if(sh.userid || !is_option(INTFLG))
unlink(tmp_fname);
else
st.states |= RM_TMP;
}
else
fd = TMPIO;
st.standout = fd;
/* this will only flush the buffer if output is fd already */
p_setout(fd);
/* 1192528: Clear the flag to flush output buffer */
(io_ftable[fd])->flag &= ~IOFLUS;
st.subflag++;
sh_funct(t,(char**)0,(int)(st.states&ERRFLG),(struct argnod*)0);
st.subflag = 0;
p_setout(fd);
p_char(0);
if(((io_ftable[fd])->flag & IOFLUS) || is_option(EXECPR))
{
/* file is larger than buffer, read from it */
p_flush();
io_seek(fd,(off_t)0,SEEK_SET);
io_init(input=fd,st.standin,inbuff);
waitflag = -1;
}
else
{
/* The file is all in the buffer */
strcpy(inbuff,_sobuf);
io_sopen(inbuff);
io_ftable[fd]->ptr = io_ftable[fd]->base;
}
st.standout = save1_out;
goto readit;
}
else if(t->tre.tretyp==0 && t->com.comarg==0)
{
jmp_buf retbuf;
jmp_buf *savreturn = sh.freturn;
unsigned save_states = st.states;
struct fileblk *savio = sh.savio;
int jmpval;
sh.freturn = (jmp_buf*)retbuf;
st.states |= BUILTIN;
sh.savio = st.standin;
jmpval = SETJMP(retbuf);
if(t->tre.treio && !(((t->tre.treio)->iofile)&IOUFD) && jmpval==0)
{
struct stat statb;
fd = io_redirect(t->tre.treio,3);
if(fstat(fd,&statb)>=0)
stacksize = 4*statb.st_size;
}
else
fd = io_fopen((char*)e_devnull);
st.states = save_states;
sh.freturn = savreturn;
sh.savio = savio;
}
else
{
int pv[2];
int forkflag = FPOU|FCOMSUB;
waitflag++;
if(st.iotemp!=saviotemp)
forkflag |= FTMP;
t = sh_mkfork(forkflag,t);
/* this is done like this so that the pipe
* is open only when needed
*/
io_popen(pv);
sh.inpipe = 0;
sh.outpipe = pv;
sh_exec(t, (int)(st.states&ERRFLG));
fd = pv[INPIPE];
io_fclose(pv[OTPIPE]);
}
io_init(input=fd,st.standin,inbuff);
readit:
sh_freeup();
st.iotemp = saviotemp;
st.staklist = saveslp;
mflag = savem;
stakset(savptr,savtop);
if(stacksize)
{
/* cause preallocation of stack frame */
stakseek(savtop+stacksize);
stakseek(savtop);
}
mac_copy((char*)0,-1);
if(waitflag>0)
job_wait(sh.subpid);
d = staktell();
while(d>0)
{
if(*stakptr(--d) != NL)
{
d++;
break;
}
else if(quote)
d--;
}
stakseek(d);
io_pop(waitflag>=0?0:1);
st.states |= saveflags;
if(w_fd >=0)
p_setout(w_fd);
return(1);
#undef d
}
/*
* Copy and expand a here-document
*/
int mac_here(iop)
register struct ionod *iop;
{
register char c;
register int in;
register int ot;
struct fileblk fb;
char inbuff[IOBSIZE+1];
quote = 0;
ifs = e_nullstr;
mflag = 1;
ot = io_mktmp(inbuff);
unlink(inbuff);
w_fd = ot;
io_push(&fb);
if(iop->iofile&IOSTRG)
{
io_sopen(iop->ioname);
in = F_STRING;
}
else
{
in = io_fopen(iop->ioname);
io_init(in,&fb,inbuff);
}
p_setout(ot);
stakseek(0);
if(is_option(EXECPR))
sh.heretrace=1;
while(1)
{
c=getch(0);
if(c==ESCAPE)
{
c = io_readc();
if(!escchar(c))
stakputc(ESCAPE);
}
if(staktell())
{
*stakptr(staktell()) = 0;
p_str(stakptr(0),c);
stakseek(0);
}
else if(c)
p_char(c);
if(c==0)
break;
}
p_flush();
sh.heretrace=0;
mflag = 0;
io_pop(0);
w_fd = -1;
io_ftable[ot] = 0;
lseek(ot,(off_t)0,SEEK_SET);
return(ot);
}
/*
* copy value of string or file onto the stack inserting backslashes
* as needed to prevent word splitting and file expansion
*/
static void mac_copy(str,size)
register char *str;
register int size;
{
register int c;
while(size!=0 && (c = (str?*str++:io_readc())))
{
/*@ assert (!mflag&&quote)==0; @*/
if(quote || (!mflag&&addescape(c)&&(c==ESCAPE||!strchr(ifs,c))))
stakputc(ESCAPE);
stakputc(c);
if(size>0)
size--;
}
}
/*
* Deletes the right substring of STRING using the expression PAT
* the longest substring is deleted when FLAG is set.
*/
static int substring(string,pat,flag)
register char *string;
char *pat;
int flag;
{
register char *sp = string;
register int size;
sp += strlen(sp);
size = sp-string;
while(sp>=string)
{
if(strmatch(sp,pat))
{
size = sp-string;
if(flag==0)
break;
}
sp--;
#ifdef MULTIBYTE
if(*sp&HIGHBIT)
{
if(*(sp-in_csize(3))==ESS3)
sp -= in_csize(3);
else if(*(sp-in_csize(2))==ESS2)
sp -= in_csize(2);
else
sp -= (in_csize(1)-1);
}
#endif /* MULTIBYTE */
}
return(size);
}
/*
* do parameter and command substitution and strip of quotes
* attempt file name expansion if <type> not zero
*/
char *mac_trim(s,type)
char *s;
{
register char *t=mac_expand(s);
struct argnod *schain = st.gchain;
if(type && f_complete(t,e_nullstr)==1)
t = st.gchain->argval;
st.gchain = schain;
sh_trim(t);
return(t);
}
/*
* perform only parameter substitution and catch failures
*/
char *mac_try(s)
register char *s;
{
if(s)
{
register int savec = st.peekn;
mactry++;
if(SETJMP(mac_buf)==0)
s = mac_trim(s,0);
else
{
io_pop(1);
st.peekn = savec;
}
mactry = 0;
}
if(s==0)
return(NULLSTR);
return(s);
}
static void mac_error()
{
sh_fail(mac_current,e_subst);
}
/*
* check to see if the error occured while expanding prompt
*/
void mac_check()
{
if(mactry)
LONGJMP(mac_buf,1);
}
#ifdef MULTIBYTE
static int charlen(str)
register char *str;
{
register int n = 0;
register int c;
while(*str)
{
c = echarset(*str); /* find character set */
str += (in_csize(c)+(c>=2)); /* move to next char */
n += out_csize(c); /* add character size */
}
return(n);
}
#endif /* MULTIBYTE */