freebsd-skq/contrib/tcsh/sh.dol.c

1095 lines
23 KiB
C
Raw Normal View History

2007-03-11 22:33:41 +00:00
/* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.70 2006/09/14 18:30:16 christos Exp $ */
/*
* sh.dol.c: Variable substitutions
*/
/*-
* Copyright (c) 1980, 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
2002-07-24 16:23:10 +00:00
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "sh.h"
2007-03-11 22:33:41 +00:00
RCSID("$tcsh: sh.dol.c,v 3.70 2006/09/14 18:30:16 christos Exp $")
/*
* C shell
*/
/*
* These routines perform variable substitution and quoting via ' and ".
* To this point these constructs have been preserved in the divided
* input words. Here we expand variables and turn quoting via ' and " into
* QUOTE bits on characters (which prevent further interpretation).
* If the `:q' modifier was applied during history expansion, then
* some QUOTEing may have occurred already, so we dont "trim()" here.
*/
2005-04-24 19:41:08 +00:00
static Char Dpeekc; /* Peek for DgetC */
static eChar Dpeekrd; /* Peek for Dreadc */
2007-03-11 22:33:41 +00:00
static Char *Dcp, *const *Dvp; /* Input vector for Dreadc */
2005-04-24 19:41:08 +00:00
#define DEOF CHAR_ERR
#define unDgetC(c) Dpeekc = c
#define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */
/*
* The following variables give the information about the current
* $ expansion, recording the current word position, the remaining
* words within this expansion, the count of remaining words, and the
* information about any : modifier which is being applied.
*/
static Char *dolp; /* Remaining chars from this word */
static Char **dolnxt; /* Further words */
static int dolcnt; /* Count of further words */
2007-03-11 22:33:41 +00:00
static struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */
static int dolmcnt; /* :gx -> INT_MAX, else 1 */
static int dol_flag_a; /* :ax -> 1, else 0 */
static Char **Dfix2 (Char *const *);
static int Dpack (struct Strbuf *);
static int Dword (struct blk_buf *);
static void dolerror (Char *);
static eChar DgetC (int);
static void Dgetdol (void);
static void fixDolMod (void);
static void setDolp (Char *);
static void unDredc (eChar);
static eChar Dredc (void);
static void Dtestq (Char);
/*
* Fix up the $ expansions and quotations in the
* argument list to command t.
*/
void
2007-03-11 22:33:41 +00:00
Dfix(struct command *t)
{
2005-04-24 19:41:08 +00:00
Char **pp;
Char *p;
if (noexec)
return;
/* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
for (pp = t->t_dcom; (p = *pp++) != NULL;) {
for (; *p; p++) {
if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */
2007-03-11 22:33:41 +00:00
Char **expanded;
expanded = Dfix2(t->t_dcom); /* found one */
blkfree(t->t_dcom);
2007-03-11 22:33:41 +00:00
t->t_dcom = expanded;
return;
}
}
}
}
/*
* $ substitute one word, for i/o redirection
*/
Char *
2007-03-11 22:33:41 +00:00
Dfix1(Char *cp)
{
2007-03-11 22:33:41 +00:00
Char *Dv[2], **expanded;
if (noexec)
return (0);
Dv[0] = cp;
Dv[1] = NULL;
2007-03-11 22:33:41 +00:00
expanded = Dfix2(Dv);
if (expanded[0] == NULL || expanded[1] != NULL) {
blkfree(expanded);
setname(short2str(cp));
stderror(ERR_NAME | ERR_AMBIG);
}
2007-03-11 22:33:41 +00:00
cp = Strsave(expanded[0]);
blkfree(expanded);
return (cp);
}
/*
* Subroutine to do actual fixing after state initialization.
*/
2007-03-11 22:33:41 +00:00
static Char **
Dfix2(Char *const *v)
{
2007-03-11 22:33:41 +00:00
struct blk_buf bb = BLK_BUF_INIT;
Dvp = v;
Dcp = STRNULL; /* Setup input vector for Dreadc */
unDgetC(0);
unDredc(0); /* Clear out any old peeks (at error) */
dolp = 0;
dolcnt = 0; /* Clear out residual $ expands (...) */
2007-03-11 22:33:41 +00:00
cleanup_push(&bb, bb_cleanup);
while (Dword(&bb))
continue;
2007-03-11 22:33:41 +00:00
cleanup_ignore(&bb);
cleanup_until(&bb);
return bb_finish(&bb);
}
/*
* Pack up more characters in this word
*/
2007-03-11 22:33:41 +00:00
static int
Dpack(struct Strbuf *wbuf)
{
2005-04-24 19:41:08 +00:00
eChar c;
for (;;) {
c = DgetC(DODOL);
if (c == '\\') {
c = DgetC(0);
if (c == DEOF) {
unDredc(c);
2007-03-11 22:33:41 +00:00
return 1;
}
if (c == '\n')
c = ' ';
else
c |= QUOTE;
}
if (c == DEOF) {
unDredc(c);
2007-03-11 22:33:41 +00:00
return 1;
}
if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */
unDgetC(c);
if (cmap(c, QUOTES))
2007-03-11 22:33:41 +00:00
return 0;
return 1;
}
2007-03-11 22:33:41 +00:00
Strbuf_append1(wbuf, (Char) c);
}
}
/*
* Get a word. This routine is analogous to the routine
* word() in sh.lex.c for the main lexical input. One difference
* here is that we don't get a newline to terminate our expansion.
* Rather, DgetC will return a DEOF when we hit the end-of-input.
*/
static int
2007-03-11 22:33:41 +00:00
Dword(struct blk_buf *bb)
{
2005-04-24 19:41:08 +00:00
eChar c, c1;
2007-03-11 22:33:41 +00:00
struct Strbuf wbuf = Strbuf_INIT;
2005-04-24 19:41:08 +00:00
int dolflg;
2007-03-11 22:33:41 +00:00
int sofar = 0;
2007-03-11 22:33:41 +00:00
cleanup_push(&wbuf, Strbuf_cleanup);
for (;;) {
c = DgetC(DODOL);
switch (c) {
case DEOF:
2007-03-11 22:33:41 +00:00
if (sofar == 0) {
cleanup_until(&wbuf);
return (0);
2007-03-11 22:33:41 +00:00
}
/* finish this word and catch the code above the next time */
unDredc(c);
/*FALLTHROUGH*/
case '\n':
2007-03-11 22:33:41 +00:00
goto end;
case ' ':
case '\t':
2007-03-11 22:33:41 +00:00
continue;
case '`':
/* We preserve ` quotations which are done yet later */
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, (Char) c);
/*FALLTHROUGH*/
case '\'':
case '"':
/*
* Note that DgetC never returns a QUOTES character from an
* expansion, so only true input quotes will get us here or out.
*/
c1 = c;
dolflg = c1 == '"' ? DODOL : 0;
for (;;) {
c = DgetC(dolflg);
if (c == c1)
break;
if (c == '\n' || c == DEOF)
2005-04-24 19:41:08 +00:00
stderror(ERR_UNMATCHED, (int)c1);
if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
2007-03-11 22:33:41 +00:00
if (wbuf.len != 0 && (wbuf.s[wbuf.len - 1] & TRIM) == '\\')
wbuf.len--;
}
switch (c1) {
case '"':
/*
* Leave any `s alone for later. Other chars are all
* quoted, thus `...` can tell it was within "...".
*/
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, c == '`' ? '`' : c | QUOTE);
break;
case '\'':
/* Prevent all further interpretation */
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, c | QUOTE);
break;
case '`':
/* Leave all text alone for later */
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, (Char) c);
break;
default:
break;
}
}
if (c1 == '`')
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, '`');
sofar = 1;
2007-03-11 22:33:41 +00:00
if (Dpack(&wbuf) != 0)
goto end;
continue;
case '\\':
c = DgetC(0); /* No $ subst! */
2007-03-11 22:33:41 +00:00
if (c == '\n' || c == DEOF)
continue;
c |= QUOTE;
break;
default:
break;
}
2007-03-11 22:33:41 +00:00
unDgetC(c);
sofar = 1;
if (Dpack(&wbuf) != 0)
goto end;
}
2007-03-11 22:33:41 +00:00
end:
cleanup_ignore(&wbuf);
cleanup_until(&wbuf);
bb_append(bb, Strbuf_finish(&wbuf));
return 1;
}
/*
* Get a character, performing $ substitution unless flag is 0.
* Any QUOTES character which is returned from a $ expansion is
* QUOTEd so that it will not be recognized above.
*/
2005-04-24 19:41:08 +00:00
static eChar
2007-03-11 22:33:41 +00:00
DgetC(int flag)
{
2005-04-24 19:41:08 +00:00
Char c;
top:
if ((c = Dpeekc) != 0) {
Dpeekc = 0;
return (c);
}
2007-03-11 22:33:41 +00:00
if (lap < labuf.len) {
c = labuf.s[lap++] & (QUOTE | TRIM);
quotspec:
if (cmap(c, QUOTES))
return (c | QUOTE);
return (c);
}
if (dolp) {
if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
goto quotspec;
if (dolcnt > 0) {
setDolp(*dolnxt++);
--dolcnt;
return (' ');
}
dolp = 0;
}
if (dolcnt > 0) {
setDolp(*dolnxt++);
--dolcnt;
goto top;
}
c = Dredc();
if (c == '$' && flag) {
Dgetdol();
goto top;
}
return (c);
}
static Char *nulvec[] = { NULL };
static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE,
{ NULL, NULL, NULL }, 0 };
static void
2007-03-11 22:33:41 +00:00
dolerror(Char *s)
{
setname(short2str(s));
stderror(ERR_NAME | ERR_RANGE);
}
/*
* Handle the multitudinous $ expansion forms.
* Ugh.
*/
static void
2007-03-11 22:33:41 +00:00
Dgetdol(void)
{
2005-04-24 19:41:08 +00:00
Char *np;
struct varent *vp = NULL;
2007-03-11 22:33:41 +00:00
struct Strbuf name = Strbuf_INIT;
2005-04-24 19:41:08 +00:00
eChar c, sc;
int subscr = 0, lwb = 1, upb = 0;
2005-04-24 19:41:08 +00:00
int dimen = 0, bitset = 0, length = 0;
static Char *dolbang = NULL;
2007-03-11 22:33:41 +00:00
cleanup_push(&name, Strbuf_cleanup);
dolmod.len = dolmcnt = dol_flag_a = 0;
c = sc = DgetC(0);
2007-03-11 22:33:41 +00:00
if (c == DEOF) {
stderror(ERR_SYNTAX);
return;
}
if (c == '{')
c = DgetC(0); /* sc is { to take } later */
if ((c & TRIM) == '#')
dimen++, c = DgetC(0); /* $# takes dimension */
else if (c == '?')
bitset++, c = DgetC(0); /* $? tests existence */
else if (c == '%')
length++, c = DgetC(0); /* $% returns length in chars */
switch (c) {
case '!':
if (dimen || bitset || length)
stderror(ERR_SYNTAX);
if (backpid != 0) {
2007-03-11 22:33:41 +00:00
xfree(dolbang);
setDolp(dolbang = putn(backpid));
}
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
goto eatbrac;
case '$':
if (dimen || bitset || length)
stderror(ERR_SYNTAX);
setDolp(doldol);
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
goto eatbrac;
2007-03-11 22:33:41 +00:00
case '<'|QUOTE: {
static struct Strbuf wbuf; /* = Strbuf_INIT; */
if (bitset)
stderror(ERR_NOTALLOWED, "$?<");
if (dimen)
stderror(ERR_NOTALLOWED, "$#<");
if (length)
stderror(ERR_NOTALLOWED, "$%<");
2007-03-11 22:33:41 +00:00
wbuf.len = 0;
{
2005-04-24 19:41:08 +00:00
char cbuf[MB_LEN_MAX];
size_t cbp = 0;
2007-03-11 22:33:41 +00:00
int old_pintr_disabled;
2005-04-24 19:41:08 +00:00
2007-03-11 22:33:41 +00:00
for (;;) {
2005-04-24 19:41:08 +00:00
int len;
2007-03-11 22:33:41 +00:00
ssize_t res;
Char wc;
2005-04-24 19:41:08 +00:00
2007-03-11 22:33:41 +00:00
pintr_push_enable(&old_pintr_disabled);
res = force_read(OLDSTD, cbuf + cbp, 1);
cleanup_until(&old_pintr_disabled);
if (res != 1)
break;
cbp++;
len = normal_mbtowc(&wc, cbuf, cbp);
2005-04-24 19:41:08 +00:00
if (len == -1) {
reset_mbtowc();
if (cbp < MB_LEN_MAX)
continue; /* Maybe a partial character */
2007-03-11 22:33:41 +00:00
wc = (unsigned char)*cbuf | INVALID_BYTE;
2005-04-24 19:41:08 +00:00
}
if (len <= 0)
len = 1;
if (cbp != (size_t)len)
memmove(cbuf, cbuf + len, cbp - len);
cbp -= len;
2007-03-11 22:33:41 +00:00
if (wc == '\n')
break;
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, wc);
2005-04-24 19:41:08 +00:00
}
while (cbp != 0) {
2007-03-11 22:33:41 +00:00
int len;
Char wc;
len = normal_mbtowc(&wc, cbuf, cbp);
if (len == -1) {
reset_mbtowc();
wc = (unsigned char)*cbuf | INVALID_BYTE;
}
if (len <= 0)
len = 1;
if (cbp != (size_t)len)
memmove(cbuf, cbuf + len, cbp - len);
cbp -= len;
if (wc == '\n')
2005-04-24 19:41:08 +00:00
break;
2007-03-11 22:33:41 +00:00
Strbuf_append1(&wbuf, wc);
}
2007-03-11 22:33:41 +00:00
Strbuf_terminate(&wbuf);
}
fixDolMod();
2007-03-11 22:33:41 +00:00
setDolp(wbuf.s); /* Kept allocated until next $< expansion */
cleanup_until(&name);
goto eatbrac;
2007-03-11 22:33:41 +00:00
}
case '*':
2007-03-11 22:33:41 +00:00
Strbuf_append(&name, STRargv);
Strbuf_terminate(&name);
vp = adrof(STRargv);
subscr = -1; /* Prevent eating [...] */
break;
case DEOF:
case '\n':
np = dimen ? STRargv : (bitset ? STRstatus : NULL);
if (np) {
bitset = 0;
2007-03-11 22:33:41 +00:00
Strbuf_append(&name, np);
Strbuf_terminate(&name);
vp = adrof(np);
subscr = -1; /* Prevent eating [...] */
unDredc(c);
break;
}
else
stderror(ERR_SYNTAX);
/*NOTREACHED*/
default:
if (Isdigit(c)) {
if (dimen)
stderror(ERR_NOTALLOWED, "$#<num>");
subscr = 0;
do {
subscr = subscr * 10 + c - '0';
c = DgetC(0);
2007-03-11 22:33:41 +00:00
} while (c != DEOF && Isdigit(c));
unDredc(c);
2002-07-24 16:23:10 +00:00
if (subscr < 0)
stderror(ERR_RANGE);
if (subscr == 0) {
if (bitset) {
dolp = dolzero ? STR1 : STR0;
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
goto eatbrac;
}
if (ffile == 0)
stderror(ERR_DOLZERO);
if (length) {
2007-03-11 22:33:41 +00:00
length = Strlen(ffile);
addla(putn(length));
}
else {
fixDolMod();
setDolp(ffile);
}
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
goto eatbrac;
}
#if 0
if (bitset)
stderror(ERR_NOTALLOWED, "$?<num>");
if (length)
stderror(ERR_NOTALLOWED, "$%<num>");
#endif
vp = adrof(STRargv);
if (vp == 0) {
vp = &nulargv;
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
goto eatmod;
}
break;
}
2007-03-11 22:33:41 +00:00
if (c == DEOF || !alnum(c)) {
np = dimen ? STRargv : (bitset ? STRstatus : NULL);
if (np) {
bitset = 0;
2007-03-11 22:33:41 +00:00
Strbuf_append(&name, np);
Strbuf_terminate(&name);
vp = adrof(np);
subscr = -1; /* Prevent eating [...] */
unDredc(c);
break;
}
else
stderror(ERR_VARALNUM);
}
for (;;) {
2007-03-11 22:33:41 +00:00
Strbuf_append1(&name, (Char) c);
c = DgetC(0);
2007-03-11 22:33:41 +00:00
if (c == DEOF || !alnum(c))
break;
}
2007-03-11 22:33:41 +00:00
Strbuf_terminate(&name);
unDredc(c);
2007-03-11 22:33:41 +00:00
vp = adrof(name.s);
}
if (bitset) {
2007-03-11 22:33:41 +00:00
dolp = (vp || getenv(short2str(name.s))) ? STR1 : STR0;
cleanup_until(&name);
goto eatbrac;
}
2002-07-24 16:23:10 +00:00
if (vp == NULL || vp->vec == NULL) {
2007-03-11 22:33:41 +00:00
np = str2short(getenv(short2str(name.s)));
if (np) {
2007-03-11 22:33:41 +00:00
static Char *env_val; /* = NULL; */
cleanup_until(&name);
fixDolMod();
2007-03-11 22:33:41 +00:00
xfree(env_val);
env_val = Strsave(np);
setDolp(env_val);
goto eatbrac;
}
2007-03-11 22:33:41 +00:00
udvar(name.s);
/* NOTREACHED */
}
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
c = DgetC(0);
upb = blklen(vp->vec);
if (dimen == 0 && subscr == 0 && c == '[') {
2007-03-11 22:33:41 +00:00
name = Strbuf_init;
cleanup_push(&name, Strbuf_cleanup);
np = name.s;
for (;;) {
c = DgetC(DODOL); /* Allow $ expand within [ ] */
if (c == ']')
break;
if (c == '\n' || c == DEOF)
stderror(ERR_INCBR);
2007-03-11 22:33:41 +00:00
Strbuf_append1(&name, (Char) c);
}
2007-03-11 22:33:41 +00:00
Strbuf_terminate(&name);
np = name.s;
if (dolp || dolcnt) /* $ exp must end before ] */
stderror(ERR_EXPORD);
if (!*np)
stderror(ERR_SYNTAX);
if (Isdigit(*np)) {
int i;
for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
continue;
if ((i < 0 || i > upb) && !any("-*", *np)) {
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
dolerror(vp->v_name);
return;
}
lwb = i;
if (!*np)
upb = lwb, np = STRstar;
}
if (*np == '*')
np++;
else if (*np != '-')
stderror(ERR_MISSING, '-');
else {
2005-04-24 19:41:08 +00:00
int i = upb;
np++;
if (Isdigit(*np)) {
i = 0;
while (Isdigit(*np))
i = i * 10 + *np++ - '0';
if (i < 0 || i > upb) {
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
dolerror(vp->v_name);
return;
}
}
if (i < lwb)
upb = lwb - 1;
else
upb = i;
}
if (lwb == 0) {
if (upb != 0) {
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
dolerror(vp->v_name);
return;
}
upb = -1;
}
if (*np)
stderror(ERR_SYNTAX);
2007-03-11 22:33:41 +00:00
cleanup_until(&name);
}
else {
if (subscr > 0) {
if (subscr > upb)
lwb = 1, upb = 0;
else
lwb = upb = subscr;
}
unDredc(c);
}
if (dimen) {
/* this is a kludge. It prevents Dgetdol() from */
/* pushing erroneous ${#<error> values into the labuf. */
if (sc == '{') {
c = Dredc();
if (c != '}')
stderror(ERR_MISSING, '}');
unDredc(c);
}
2007-03-11 22:33:41 +00:00
addla(putn(upb - lwb + 1));
}
else if (length) {
int i;
2007-03-11 22:33:41 +00:00
for (i = lwb - 1, length = 0; i < upb; i++)
2007-03-11 22:33:41 +00:00
length += Strlen(vp->vec[i]);
#ifdef notdef
/* We don't want that, since we can always compute it by adding $#xxx */
length += i - 1; /* Add the number of spaces in */
#endif
2007-03-11 22:33:41 +00:00
addla(putn(length));
}
else {
eatmod:
fixDolMod();
dolnxt = &vp->vec[lwb - 1];
dolcnt = upb - lwb + 1;
}
eatbrac:
if (sc == '{') {
c = Dredc();
if (c != '}')
stderror(ERR_MISSING, '}');
}
}
static void
2007-03-11 22:33:41 +00:00
fixDolMod(void)
{
2005-04-24 19:41:08 +00:00
eChar c;
c = DgetC(0);
if (c == ':') {
do {
2007-03-11 22:33:41 +00:00
c = DgetC(0), dolmcnt = 1, dol_flag_a = 0;
if (c == 'g' || c == 'a') {
if (c == 'g')
2007-03-11 22:33:41 +00:00
dolmcnt = INT_MAX;
else
2007-03-11 22:33:41 +00:00
dol_flag_a = 1;
c = DgetC(0);
}
2007-03-11 22:33:41 +00:00
if ((c == 'g' && dolmcnt != INT_MAX) ||
(c == 'a' && dol_flag_a == 0)) {
if (c == 'g')
2007-03-11 22:33:41 +00:00
dolmcnt = INT_MAX;
else
2007-03-11 22:33:41 +00:00
dol_flag_a = 1;
c = DgetC(0);
}
if (c == 's') { /* [eichin:19910926.0755EST] */
int delimcnt = 2;
2005-04-24 19:41:08 +00:00
eChar delim = DgetC(0);
2007-03-11 22:33:41 +00:00
Strbuf_append1(&dolmod, (Char) c);
Strbuf_append1(&dolmod, (Char) delim);
if (delim == DEOF || !delim || letter(delim)
|| Isdigit(delim) || any(" \t\n", delim)) {
seterror(ERR_BADSUBST);
break;
}
2005-04-24 19:41:08 +00:00
while ((c = DgetC(0)) != DEOF) {
2007-03-11 22:33:41 +00:00
Strbuf_append1(&dolmod, (Char) c);
if(c == delim) delimcnt--;
if(!delimcnt) break;
}
if(delimcnt) {
seterror(ERR_BADSUBST);
break;
}
continue;
}
if (!any("luhtrqxes", c))
2005-04-24 19:41:08 +00:00
stderror(ERR_BADMOD, (int)c);
2007-03-11 22:33:41 +00:00
Strbuf_append1(&dolmod, (Char) c);
if (c == 'q')
2007-03-11 22:33:41 +00:00
dolmcnt = INT_MAX;
}
while ((c = DgetC(0)) == ':');
unDredc(c);
}
else
unDredc(c);
}
static void
2007-03-11 22:33:41 +00:00
setDolp(Char *cp)
{
2005-04-24 19:41:08 +00:00
Char *dp;
2007-03-11 22:33:41 +00:00
size_t i;
if (dolmod.len == 0 || dolmcnt == 0) {
dolp = cp;
return;
}
2007-03-11 22:33:41 +00:00
cp = Strsave(cp);
for (i = 0; i < dolmod.len; i++) {
int didmod = 0;
/* handle s// [eichin:19910926.0510EST] */
2007-03-11 22:33:41 +00:00
if(dolmod.s[i] == 's') {
2005-04-24 19:41:08 +00:00
Char delim;
Char *lhsub, *rhsub, *np;
size_t lhlen = 0, rhlen = 0;
2007-03-11 22:33:41 +00:00
delim = dolmod.s[++i];
if (!delim || letter(delim)
|| Isdigit(delim) || any(" \t\n", delim)) {
seterror(ERR_BADSUBST);
break;
}
2007-03-11 22:33:41 +00:00
lhsub = &dolmod.s[++i];
while(dolmod.s[i] != delim && dolmod.s[++i]) {
lhlen++;
}
2007-03-11 22:33:41 +00:00
dolmod.s[i] = 0;
rhsub = &dolmod.s[++i];
while(dolmod.s[i] != delim && dolmod.s[++i]) {
rhlen++;
}
2007-03-11 22:33:41 +00:00
dolmod.s[i] = 0;
2007-03-11 22:33:41 +00:00
strip(lhsub);
strip(cp);
dp = cp;
do {
2007-03-11 22:33:41 +00:00
dp = Strstr(dp, lhsub);
if (dp) {
2007-03-11 22:33:41 +00:00
ptrdiff_t diff = dp - cp;
np = xmalloc((Strlen(cp) + 1 - lhlen + rhlen) *
sizeof(Char));
(void) Strncpy(np, cp, diff);
(void) Strcpy(np + diff, rhsub);
(void) Strcpy(np + diff + rhlen, dp + lhlen);
dp = np + diff + 1;
xfree(cp);
cp = np;
didmod = 1;
} else {
/* should this do a seterror? */
break;
}
}
2007-03-11 22:33:41 +00:00
while (dol_flag_a != 0);
/*
* restore dolmod for additional words
*/
2007-03-11 22:33:41 +00:00
dolmod.s[i] = rhsub[-1] = (Char) delim;
} else {
do {
2007-03-11 22:33:41 +00:00
if ((dp = domod(cp, dolmod.s[i])) != NULL) {
didmod = 1;
if (Strcmp(cp, dp) == 0) {
2007-03-11 22:33:41 +00:00
xfree(cp);
cp = dp;
break;
}
else {
2007-03-11 22:33:41 +00:00
xfree(cp);
cp = dp;
}
}
else
break;
}
2007-03-11 22:33:41 +00:00
while (dol_flag_a != 0);
}
if (didmod && dolmcnt != INT_MAX)
dolmcnt--;
#ifdef notdef
2007-03-11 22:33:41 +00:00
else
break;
#endif
}
2007-03-11 22:33:41 +00:00
addla(cp);
dolp = STRNULL;
if (seterr)
stderror(ERR_OLD);
}
static void
2007-03-11 22:33:41 +00:00
unDredc(eChar c)
{
Dpeekrd = c;
}
2005-04-24 19:41:08 +00:00
static eChar
2007-03-11 22:33:41 +00:00
Dredc(void)
{
2005-04-24 19:41:08 +00:00
Char c;
if ((c = Dpeekrd) != 0) {
Dpeekrd = 0;
return (c);
}
if (Dcp && (c = *Dcp++))
return (c & (QUOTE | TRIM));
if (*Dvp == 0) {
Dcp = 0;
return (DEOF);
}
Dcp = *Dvp++;
return (' ');
}
2007-03-11 22:33:41 +00:00
static int gflag;
static void
2007-03-11 22:33:41 +00:00
Dtestq(Char c)
{
if (cmap(c, QUOTES))
gflag = 1;
}
2007-03-11 22:33:41 +00:00
static void
inheredoc_cleanup(void *dummy)
{
USE(dummy);
inheredoc = 0;
}
/*
* Form a shell temporary file (in unit 0) from the words
* of the shell input up to EOF or a line the same as "term".
* Unit 0 should have been closed before this call.
*/
void
2007-03-11 22:33:41 +00:00
heredoc(Char *term)
{
2005-04-24 19:41:08 +00:00
eChar c;
Char *Dv[2];
2007-03-11 22:33:41 +00:00
struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
Char obuf[BUFSIZE + 1];
#define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
2005-04-24 19:41:08 +00:00
Char *lbp, *obp, *mbp;
Char **vp;
2005-04-24 19:41:08 +00:00
int quoted;
char *tmp;
#ifndef WINNT_NATIVE
struct timeval tv;
again:
#endif /* WINNT_NATIVE */
tmp = short2str(shtemp);
#ifndef O_CREAT
# define O_CREAT 0
2007-03-11 22:33:41 +00:00
if (xcreat(tmp, 0600) < 0)
stderror(ERR_SYSTEM, tmp, strerror(errno));
#endif
2007-03-11 22:33:41 +00:00
xclose(0);
#ifndef O_TEMPORARY
# define O_TEMPORARY 0
#endif
#ifndef O_EXCL
# define O_EXCL 0
#endif
2007-03-11 22:33:41 +00:00
if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
-1) {
int oerrno = errno;
#ifndef WINNT_NATIVE
if (errno == EEXIST) {
if (unlink(tmp) == -1) {
(void) gettimeofday(&tv, NULL);
2007-03-11 22:33:41 +00:00
xfree(shtemp);
mbp = putn((((int)tv.tv_sec) ^
((int)tv.tv_usec) ^ ((int)getpid())) & 0x00ffffff);
shtemp = Strspl(STRtmpsh, mbp);
xfree(mbp);
}
goto again;
}
#endif /* WINNT_NATIVE */
(void) unlink(tmp);
errno = oerrno;
stderror(ERR_SYSTEM, tmp, strerror(errno));
}
(void) unlink(tmp); /* 0 0 inode! */
Dv[0] = term;
Dv[1] = NULL;
gflag = 0;
trim(Dv);
rscan(Dv, Dtestq);
quoted = gflag;
obp = obuf;
2005-04-24 19:41:08 +00:00
obuf[BUFSIZE] = 0;
inheredoc = 1;
2007-03-11 22:33:41 +00:00
cleanup_push(&inheredoc, inheredoc_cleanup);
#ifdef WINNT_NATIVE
__dup_stdin = 1;
#endif /* WINNT_NATIVE */
2004-07-11 02:17:56 +00:00
#ifdef O_TEXT
setmode(1, O_TEXT);
#endif
#ifdef O_BINARY
setmode(0, O_BINARY);
#endif
2007-03-11 22:33:41 +00:00
cleanup_push(&lbuf, Strbuf_cleanup);
cleanup_push(&mbuf, Strbuf_cleanup);
for (;;) {
2007-03-11 22:33:41 +00:00
Char **words;
/*
* Read up a line
*/
2007-03-11 22:33:41 +00:00
lbuf.len = 0;
for (;;) {
c = readc(1); /* 1 -> Want EOF returns */
2005-04-24 19:41:08 +00:00
if (c == CHAR_ERR || c == '\n')
break;
2007-03-11 22:33:41 +00:00
if ((c &= TRIM) != 0)
Strbuf_append1(&lbuf, (Char) c);
}
2007-03-11 22:33:41 +00:00
Strbuf_terminate(&lbuf);
/*
* Check for EOF or compare to terminator -- before expansion
*/
2007-03-11 22:33:41 +00:00
if (c == CHAR_ERR || eq(lbuf.s, term))
break;
/*
* If term was quoted or -n just pass it on
*/
if (quoted || noexec) {
2007-03-11 22:33:41 +00:00
Strbuf_append1(&lbuf, '\n');
Strbuf_terminate(&lbuf);
for (lbp = lbuf.s; (c = *lbp++) != 0;) {
*obp++ = (Char) c;
2007-03-11 22:33:41 +00:00
if (obp == OBUF_END) {
2005-04-24 19:41:08 +00:00
tmp = short2str(obuf);
2007-03-11 22:33:41 +00:00
(void) xwrite(0, tmp, strlen (tmp));
obp = obuf;
}
}
continue;
}
/*
* Term wasn't quoted so variable and then command expand the input
* line
*/
2007-03-11 22:33:41 +00:00
Dcp = lbuf.s;
Dvp = Dv + 1;
2007-03-11 22:33:41 +00:00
mbuf.len = 0;
for (;;) {
c = DgetC(DODOL);
if (c == DEOF)
break;
if ((c &= TRIM) == 0)
continue;
/* \ quotes \ $ ` here */
if (c == '\\') {
c = DgetC(0);
if (!any("$\\`", c))
unDgetC(c | QUOTE), c = '\\';
else
c |= QUOTE;
}
2007-03-11 22:33:41 +00:00
Strbuf_append1(&mbuf, (Char) c);
}
2007-03-11 22:33:41 +00:00
Strbuf_terminate(&mbuf);
/*
* If any ` in line do command substitution
*/
2007-03-11 22:33:41 +00:00
mbp = mbuf.s;
if (Strchr(mbp, '`') != NULL) {
/*
* 1 arg to dobackp causes substitution to be literal. Words are
* broken only at newlines so that all blanks and tabs are
* preserved. Blank lines (null words) are not discarded.
*/
2007-03-11 22:33:41 +00:00
words = dobackp(mbp, 1);
}
else
/* Setup trivial vector similar to return of dobackp */
2007-03-11 22:33:41 +00:00
Dv[0] = mbp, Dv[1] = NULL, words = Dv;
/*
* Resurrect the words from the command substitution each separated by
* a newline. Note that the last newline of a command substitution
* will have been discarded, but we put a newline after the last word
* because this represents the newline after the last input line!
*/
2007-03-11 22:33:41 +00:00
for (vp= words; *vp; vp++) {
for (mbp = *vp; *mbp; mbp++) {
*obp++ = *mbp & TRIM;
2007-03-11 22:33:41 +00:00
if (obp == OBUF_END) {
2005-04-24 19:41:08 +00:00
tmp = short2str(obuf);
2007-03-11 22:33:41 +00:00
(void) xwrite(0, tmp, strlen (tmp));
obp = obuf;
}
}
*obp++ = '\n';
2007-03-11 22:33:41 +00:00
if (obp == OBUF_END) {
2005-04-24 19:41:08 +00:00
tmp = short2str(obuf);
2007-03-11 22:33:41 +00:00
(void) xwrite(0, tmp, strlen (tmp));
obp = obuf;
}
}
2007-03-11 22:33:41 +00:00
if (words != Dv)
blkfree(words);
}
2007-03-11 22:33:41 +00:00
*obp = 0;
tmp = short2str(obuf);
(void) xwrite(0, tmp, strlen (tmp));
(void) lseek(0, (off_t) 0, L_SET);
cleanup_until(&inheredoc);
}