521 lines
10 KiB
C
521 lines
10 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 "@(#)sh:macro.c 1.21.8.1"
|
|
/*
|
|
* UNIX shell
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "sym.h"
|
|
#include <wait.h>
|
|
|
|
static unsigned char quote; /* used locally */
|
|
static unsigned char quoted; /* used locally */
|
|
static int getch();
|
|
static void skipto();
|
|
static void comsubst();
|
|
static void flush();
|
|
|
|
static void
|
|
copyto(endch, trimflag)
|
|
int trimflag; /* flag to check if argument will be trimmed */
|
|
register unsigned char endch;
|
|
{
|
|
register unsigned char c;
|
|
register unsigned char d;
|
|
register unsigned char *pc;
|
|
|
|
while ((c = getch(endch, trimflag)) != endch && c)
|
|
if (quote) {
|
|
if(c == '\\') { /* don't interpret next character */
|
|
pushstak(c);
|
|
d = readc();
|
|
if(!escchar(d)) { /* both \ and following
|
|
character are quoted if next
|
|
character is not $, `, ", or \*/
|
|
pushstak('\\');
|
|
pushstak('\\');
|
|
pc = readw(d);
|
|
/* push entire multibyte char */
|
|
while(d = *pc++)
|
|
pushstak(d);
|
|
} else
|
|
pushstak(d);
|
|
} else { /* push escapes onto stack to quote characters */
|
|
pc = readw(c);
|
|
pushstak('\\');
|
|
while(c = *pc++)
|
|
pushstak(c);
|
|
}
|
|
} else if(c == '\\') {
|
|
c = readc(); /* get character to be escaped */
|
|
pushstak('\\');
|
|
pushstak(c);
|
|
} else
|
|
pushstak(c);
|
|
zerostak();
|
|
if (c != endch)
|
|
error(0, badsub, badsubid);
|
|
}
|
|
|
|
static void
|
|
skipto(endch)
|
|
register unsigned char endch;
|
|
{
|
|
/*
|
|
* skip chars up to }
|
|
*/
|
|
register unsigned char c;
|
|
|
|
while ((c = readc()) && c != endch)
|
|
{
|
|
switch (c)
|
|
{
|
|
case SQUOTE:
|
|
skipto(SQUOTE);
|
|
break;
|
|
|
|
case DQUOTE:
|
|
skipto(DQUOTE);
|
|
break;
|
|
|
|
case DOLLAR:
|
|
if (readc() == BRACE)
|
|
skipto('}');
|
|
}
|
|
}
|
|
if (c != endch)
|
|
error(0, badsub, badsubid);
|
|
}
|
|
|
|
static int
|
|
getch(endch, trimflag)
|
|
unsigned char endch;
|
|
int trimflag; /* flag to check if an argument is going to be trimmed, here document
|
|
output is never trimmed
|
|
*/
|
|
{
|
|
register unsigned char d;
|
|
int atflag=0;/*flag to check if $@ has already been seen within double
|
|
quotes */
|
|
retry:
|
|
d = readc();
|
|
if (!subchar(d))
|
|
return(d);
|
|
|
|
if (d == DOLLAR)
|
|
{
|
|
unsigned char c;
|
|
|
|
if ((c = readc(), dolchar(c)))
|
|
{
|
|
struct namnod *n = (struct namnod *)0;
|
|
int dolg = 0;
|
|
BOOL bra;
|
|
BOOL nulflg;
|
|
register unsigned char *argp, *v;
|
|
unsigned char idb[2];
|
|
unsigned char *id = idb;
|
|
|
|
if (bra = (c == BRACE))
|
|
c = readc();
|
|
if (letter(c))
|
|
{
|
|
argp = (unsigned char *)relstak();
|
|
while (alphanum(c))
|
|
{
|
|
pushstak(c);
|
|
c = readc();
|
|
}
|
|
zerostak();
|
|
n = lookup(absstak(argp));
|
|
setstak(argp);
|
|
if (n->namflg & N_FUNCTN)
|
|
error(0, badsub, badsubid);
|
|
v = n->namval;
|
|
id = (unsigned char *)n->namid;
|
|
peekc = c | MARK;
|
|
}
|
|
else if (digchar(c))
|
|
{
|
|
*id = c;
|
|
idb[1] = 0;
|
|
if (astchar(c))
|
|
{
|
|
if(c == '@' && !atflag && quote) {
|
|
quoted--;
|
|
atflag = 1;
|
|
}
|
|
dolg = 1;
|
|
c = '1';
|
|
}
|
|
c -= '0';
|
|
v = ((c == 0) ? cmdadr : ((int)c <= dolc) ? dolv[c] : (unsigned char *)(dolg = 0));
|
|
}
|
|
else if (c == '$')
|
|
v = pidadr;
|
|
else if (c == '!')
|
|
v = pcsadr;
|
|
else if (c == '#')
|
|
{
|
|
itos(dolc);
|
|
v = numbuf;
|
|
}
|
|
else if (c == '?')
|
|
{
|
|
itos(retval);
|
|
v = numbuf;
|
|
}
|
|
else if (c == '-')
|
|
v = flagadr;
|
|
else if (bra)
|
|
error(0, badsub, badsubid);
|
|
else
|
|
goto retry;
|
|
c = readc();
|
|
if (c == ':' && bra) /* null and unset fix */
|
|
{
|
|
nulflg = 1;
|
|
c = readc();
|
|
}
|
|
else
|
|
nulflg = 0;
|
|
if (!defchar(c) && c != '#' && bra) /* Also allow pat remove (#) */
|
|
error(0, badsub, badsubid);
|
|
argp = 0;
|
|
if (bra)
|
|
{
|
|
if (c != '}')
|
|
{
|
|
argp = (unsigned char *)relstak();
|
|
if(c == '#') /* Check for pattern remove start */
|
|
{
|
|
copyto('}', trimflag);
|
|
setstak(argp);
|
|
}
|
|
else if ((v == 0 || (nulflg && *v == 0)) ^ (setchar(c)))
|
|
copyto('}', trimflag);
|
|
else
|
|
skipto('}');
|
|
argp = absstak(argp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
peekc = c | MARK;
|
|
c = 0;
|
|
}
|
|
if(c == '#' && v && *v && argp)
|
|
{
|
|
/*
|
|
* Simulate largest pattern remove from Korn without regexec()
|
|
* Specifically for POSIX builtin executables script
|
|
* builtin_exec in /sbin to emulate basename $0.
|
|
*/
|
|
unsigned char *last_slash = v;
|
|
unsigned char *vs = v;
|
|
trim(argp);
|
|
/* Look for large pattern remove ##pat like Korn shell */
|
|
if(*argp != '#' && *(argp+1) != '*' && *(argp+2) != '/')
|
|
error(0, badsub, badsubid);
|
|
/* search for last slash */
|
|
while(*vs)
|
|
{
|
|
if(*vs == '/')
|
|
last_slash = vs;
|
|
++vs;
|
|
}
|
|
vs = (last_slash==v?v:++last_slash);
|
|
while(*vs)
|
|
pushstak(*vs++);
|
|
}
|
|
else if (v && (!nulflg || *v))
|
|
{
|
|
|
|
if (c != '+')
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*v == 0 && quote) {
|
|
pushstak('\\');
|
|
pushstak('\0');
|
|
}
|
|
else
|
|
while (c = *v++) {
|
|
if(quote || (c == '\\' && trimflag)) {
|
|
register int length;
|
|
wchar_t l;
|
|
pushstak('\\');
|
|
pushstak(c);
|
|
length = mbtowc(&l, (char *)v - 1, MULTI_BYTE_MAX);
|
|
while(--length > 0)
|
|
pushstak(*v++);
|
|
}
|
|
else
|
|
pushstak(c);
|
|
|
|
}
|
|
|
|
if (dolg == 0 || (++dolg > dolc))
|
|
break;
|
|
else /* $* and $@ expansion */
|
|
{
|
|
v = dolv[dolg];
|
|
if(*id == '*' && quote)
|
|
/* push quoted space so that " $* " will not be broken into separate arguments */
|
|
pushstak('\\');
|
|
pushstak(' ');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (argp)
|
|
{
|
|
if (c == '?') {
|
|
if(trimflag)
|
|
trim(argp);
|
|
|
|
prs(id);
|
|
prs(gettxt(colonid, colon));
|
|
|
|
if (*argp) {
|
|
prs_cntl((char *)argp);
|
|
}
|
|
else {
|
|
prs_cntl(gettxt(badparamid, badparam));
|
|
}
|
|
newline();
|
|
exitsh(ERROR);
|
|
}
|
|
else if (c == '=')
|
|
{
|
|
if (n)
|
|
{
|
|
int strlngth = staktop - stakbot;
|
|
unsigned char *savptr = fixstak();
|
|
unsigned char *newargp;
|
|
/*
|
|
* copy word onto stack, trim it, and then
|
|
* do assignment
|
|
*/
|
|
usestak();
|
|
while(c = *argp++) {
|
|
if(c == '\\' && trimflag) {
|
|
c = *argp++;
|
|
if(!c)
|
|
continue;
|
|
}
|
|
pushstak(c);
|
|
}
|
|
newargp = fixstak();
|
|
assign(n, newargp);
|
|
tdystak(savptr);
|
|
memcpy(stakbot, savptr, strlngth);
|
|
staktop = stakbot + strlngth;
|
|
}
|
|
else
|
|
error(0, badsub, badsubid);
|
|
}
|
|
}
|
|
else if (flags & setflg)
|
|
failed(0, id, unset, unsetid);
|
|
goto retry;
|
|
}
|
|
else
|
|
peekc = c | MARK;
|
|
}
|
|
else if (d == endch)
|
|
return(d);
|
|
else if (d == SQUOTE)
|
|
{
|
|
comsubst(trimflag);
|
|
goto retry;
|
|
}
|
|
else if (d == DQUOTE && trimflag)
|
|
{
|
|
if(!quote) {
|
|
atflag = 0;
|
|
quoted++;
|
|
}
|
|
quote ^= QUOTE;
|
|
goto retry;
|
|
}
|
|
return(d);
|
|
}
|
|
|
|
unsigned char *
|
|
macro(as)
|
|
unsigned char *as;
|
|
{
|
|
/*
|
|
* Strip "" and do $ substitution
|
|
* Leaves result on top of stack
|
|
*/
|
|
register BOOL savqu = quoted;
|
|
register unsigned char savq = quote;
|
|
struct filehdr fb;
|
|
|
|
push(&fb);
|
|
estabf(as);
|
|
usestak();
|
|
quote = 0;
|
|
quoted = 0;
|
|
copyto(0, 1);
|
|
pop();
|
|
if (quoted && (stakbot == staktop)) {
|
|
pushstak('\\');
|
|
pushstak('\0');
|
|
/*
|
|
* above is the fix for *'.c' bug
|
|
*/
|
|
}
|
|
quote = savq;
|
|
quoted = savqu;
|
|
return(fixstak());
|
|
}
|
|
/* Save file descriptor for command substitution */
|
|
int savpipe = -1;
|
|
|
|
static void
|
|
comsubst(trimflag)
|
|
int trimflag; /* used to determine if argument will later be trimmed */
|
|
{
|
|
/*
|
|
* command substn
|
|
*/
|
|
struct fileblk cb;
|
|
register unsigned char d;
|
|
int strlngth = staktop - stakbot;
|
|
register unsigned char *oldstaktop;
|
|
unsigned char *savptr = fixstak();
|
|
|
|
usestak();
|
|
while ((d = readc()) != SQUOTE && d) {
|
|
if(d == '\\') {
|
|
d = readc();
|
|
if(!escchar(d) || (d == '"' && !quote))
|
|
/* trim quotes for `, \, or " if command substitution is within
|
|
double quotes */
|
|
pushstak('\\');
|
|
}
|
|
pushstak(d);
|
|
}
|
|
{
|
|
register unsigned char *argc;
|
|
|
|
argc = fixstak();
|
|
push(&cb);
|
|
estabf(argc); /* read from string */
|
|
}
|
|
{
|
|
register struct trenod *t;
|
|
int pv[2];
|
|
|
|
/*
|
|
* this is done like this so that the pipe
|
|
* is open only when needed
|
|
*/
|
|
t = makefork(FPOU, cmd(EOFSYM, MTFLG | NLFLG ));
|
|
chkpipe(pv);
|
|
savpipe = pv[OTPIPE];
|
|
initf(pv[INPIPE]); /* read from pipe */
|
|
execute(t, XEC_NOSTOP, (int)(flags & errflg), 0, pv);
|
|
(void)close(pv[OTPIPE]);
|
|
savpipe = -1;
|
|
}
|
|
tdystak(savptr);
|
|
memcpy(stakbot, savptr, strlngth);
|
|
oldstaktop = staktop = stakbot + strlngth;
|
|
while (d = readc()) {
|
|
if(quote || (d == '\\' && trimflag)) {
|
|
register unsigned char *rest;
|
|
/* quote output from command subst. if within double
|
|
quotes or backslash part of output */
|
|
rest = readw(d);
|
|
pushstak('\\');
|
|
while(d = *rest++)
|
|
/* Pick up all of multibyte character */
|
|
pushstak(d);
|
|
}
|
|
else
|
|
pushstak(d);
|
|
}
|
|
{
|
|
extern pid_t parent;
|
|
int stat;
|
|
register rc;
|
|
while (waitpid(parent,&stat,0) != parent)
|
|
continue;
|
|
if (WIFEXITED(stat))
|
|
rc = WEXITSTATUS(stat);
|
|
else
|
|
rc = (WTERMSIG(stat) | SIGFLG);
|
|
if (rc && (flags & errflg))
|
|
exitsh(rc);
|
|
exitval = rc;
|
|
flags |= eflag;
|
|
exitset();
|
|
}
|
|
while (oldstaktop != staktop)
|
|
{ /* strip off trailing newlines from command substitution only */
|
|
if ((*--staktop) != NL)
|
|
{
|
|
++staktop;
|
|
break;
|
|
} else if(quote)
|
|
staktop--; /* skip past backslashes if quoting */
|
|
}
|
|
pop();
|
|
}
|
|
|
|
#define CPYSIZ 512
|
|
|
|
void
|
|
subst(in, ot)
|
|
int in, ot;
|
|
{
|
|
register unsigned char c;
|
|
struct fileblk fb;
|
|
register int count = CPYSIZ;
|
|
|
|
push(&fb);
|
|
initf(in);
|
|
/*
|
|
* DQUOTE used to stop it from quoting
|
|
*/
|
|
while (c = (getch(DQUOTE, 0))) /* read characters from here document
|
|
and interpret them */
|
|
{
|
|
if(c == '\\') {
|
|
c = readc(); /* check if character in here document is
|
|
escaped */
|
|
if(!escchar(c) || c == '"')
|
|
pushstak('\\');
|
|
}
|
|
pushstak(c);
|
|
if (--count == 0)
|
|
{
|
|
flush(ot);
|
|
count = CPYSIZ;
|
|
}
|
|
}
|
|
flush(ot);
|
|
pop();
|
|
}
|
|
|
|
static void
|
|
flush(ot)
|
|
{
|
|
(void)write(ot, stakbot, staktop - stakbot);
|
|
if (flags & execpr)
|
|
(void)write(output, stakbot, staktop - stakbot);
|
|
staktop = stakbot;
|
|
}
|