Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error on same line) is fixed. (An old bug.) echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )" consistently prints 1002 lines (x, 1000 empty ones, then y) as it should (And you don't want to know what it did before, or why.) (Another old one.) (Recently added) Problems with ~ expansion fixed (mem management related). Proper fix for the cwrappers configure problem (which includes the quick fix that was done earlier, but extends upon that to be correct). (This was another newly added problem.) And the really devious (and rare) old bug - if STACKSTRNUL() needs to allocate a new buffer in which to store the \0, calculate the size of the string space remaining correctly, unlike when SPUTC() grows the buffer, there is no actual data being stored in the STACKSTRNUL() case - the string space remaining was calculated as one byte too few. That would be harmless, unless the next buffer also filled, in which case it was assumed that it was really full, not one byte less, meaning one junk char (a nul, or anything) was being copied into the next (even bigger buffer) corrupting the data. Consistent use of stalloc() to allocate a new block of (stack) memory, and grabstackstr() to claim a block of (stack) memory that had already been occupied but not claimed as in use. Since grabstackstr is implemented as just a call to stalloc() this is a no-op change in practice, but makes it much easier to comprehend what is really happening. Previous code sometimes used stalloc() when the use case was really for grabstackstr(). Change grabstackstr() to actually use the arg passed to it, instead of (not much better than) guessing how much space to claim, More care when using unstalloc()/ungrabstackstr() to return space, and in particular when the stack must be returned to its previous state, rather than just returning no-longer needed space, neither of those work. They also don't work properly if there have been (really, even might have been) any stack mem allocations since the last stalloc()/grabstackstr(). (If we know there cannot have been then the alloc/release sequence is kind of pointless.) To work correctly in general we must use setstackmark()/popstackmark() so do that when needed. Have those also save/restore the top of stack string space remaining. [Aside: for those reading this, the "stack" mentioned is not in any way related to the thing used for maintaining the C function call state, ie: the "stack segment" of the program, but the shell's internal memory management strategy.] More comments to better explain what is happening in some cases. Also cleaned up some hopelessly broken DEBUG mode data that were recently added (no effect on anyone but the poor semi-human attempting to make sense of it...). User visible changes: Proper counting of line numbers when a here document is delimited by a multi-line end-delimiter, as in cat << 'REALLY END' here doc line 1 here doc line 2 REALLY END (which is an obscure case, but nothing says should not work.) The \n in the end-delimiter of the here doc (the last one) was not incrementing the line number, which from that point on in the script would be 1 too low (or more, for end-delimiters with more than one \n in them.) With tilde expansion: unset HOME; echo ~ changed to return getpwuid(getuid())->pw_home instead of failing (returning ~) POSIX says this is unspecified, which makes it difficult for a script to compensate for being run without HOME set (as in env -i sh script), so while not able to be used portably, this seems like a useful extension (and is implemented the same way by some other shells). Further, with HOME=; printf %s ~ we now write nothing (which is required by POSIX - which requires ~ to expand to the value of $HOME if it is set) previously if $HOME (in this case) or a user's directory in the passwd file (for ~user) were a null STRING, We failed the ~ expansion and left behind '~' or '~user'.
This commit is contained in:
parent
9038bdca03
commit
ee3b307fc5
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: exec.c,v 1.49 2017/06/07 05:08:32 kre Exp $ */
|
||||
/* $NetBSD: exec.c,v 1.50 2017/06/17 07:22:12 kre Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
|
@ -37,7 +37,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
|
||||
#else
|
||||
__RCSID("$NetBSD: exec.c,v 1.49 2017/06/07 05:08:32 kre Exp $");
|
||||
__RCSID("$NetBSD: exec.c,v 1.50 2017/06/17 07:22:12 kre Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -334,11 +334,10 @@ padvance(const char **path, const char *name, int magic_percent)
|
|||
*path = p + 1;
|
||||
else
|
||||
*path = NULL;
|
||||
return stalloc(len);
|
||||
return grabstackstr(q + strlen(name) + 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** Command hashing code ***/
|
||||
|
||||
|
||||
|
@ -617,14 +616,17 @@ loop:
|
|||
if (!S_ISREG(statb.st_mode))
|
||||
goto loop;
|
||||
if (pathopt) { /* this is a %func directory */
|
||||
char *endname;
|
||||
|
||||
if (act & DO_NOFUNC)
|
||||
goto loop;
|
||||
stalloc(strlen(fullname) + 1);
|
||||
endname = fullname + strlen(fullname) + 1;
|
||||
grabstackstr(endname);
|
||||
readcmdfile(fullname);
|
||||
if ((cmdp = cmdlookup(name, 0)) == NULL ||
|
||||
cmdp->cmdtype != CMDFUNCTION)
|
||||
error("%s not defined in %s", name, fullname);
|
||||
stunalloc(fullname);
|
||||
ungrabstackstr(fullname, endname);
|
||||
goto success;
|
||||
}
|
||||
#ifdef notdef
|
||||
|
@ -644,7 +646,11 @@ loop:
|
|||
TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
|
||||
INTOFF;
|
||||
if (act & DO_ALTPATH) {
|
||||
/*
|
||||
* this should be a grabstackstr() but is not needed:
|
||||
* fullname is no longer needed for anything
|
||||
stalloc(strlen(fullname) + 1);
|
||||
*/
|
||||
cmdp = &loc_cmd;
|
||||
} else
|
||||
cmdp = cmdlookup(name, 1);
|
||||
|
|
291
bin/sh/expand.c
291
bin/sh/expand.c
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: expand.c,v 1.115 2017/06/07 09:31:30 kre Exp $ */
|
||||
/* $NetBSD: expand.c,v 1.116 2017/06/17 07:22:12 kre Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
|
@ -37,7 +37,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
|
||||
#else
|
||||
__RCSID("$NetBSD: expand.c,v 1.115 2017/06/07 09:31:30 kre Exp $");
|
||||
__RCSID("$NetBSD: expand.c,v 1.116 2017/06/17 07:22:12 kre Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -225,9 +225,6 @@ argstr(const char *p, int flag)
|
|||
int firsteq = 1;
|
||||
const char *ifs = NULL;
|
||||
int ifs_split = EXP_IFS_SPLIT;
|
||||
#ifdef DEBUG
|
||||
const char *ed = expdest;
|
||||
#endif
|
||||
|
||||
if (flag & EXP_IFS_SPLIT)
|
||||
ifs = ifsval();
|
||||
|
@ -239,16 +236,16 @@ argstr(const char *p, int flag)
|
|||
for (;;) {
|
||||
switch (c = *p++) {
|
||||
case '\0':
|
||||
STACKSTRNUL(expdest);
|
||||
VTRACE(DBG_EXPAND, ("argstr returning at \"\" "
|
||||
"added \"%.*s\" to expdest (followed by: %2.2x)\n",
|
||||
expdest-ed, ed, *expdest));
|
||||
"added \"%s\" to expdest\n", stackblock()));
|
||||
return p - 1;
|
||||
case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
|
||||
case CTLENDARI: /* end of a $(( )) string */
|
||||
STACKSTRNUL(expdest);
|
||||
VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..."
|
||||
" after %2.2x; "
|
||||
"added \"%.*s\" to expdest (followed by: %2.2x)\n",
|
||||
p, p[-1]&0xff, expdest-ed, ed, *expdest));
|
||||
" after %2.2X; added \"%s\" to expdest\n",
|
||||
p, (c&0xff), stackblock()));
|
||||
return p;
|
||||
case CTLQUOTEMARK:
|
||||
/* "$@" syntax adherence hack */
|
||||
|
@ -272,27 +269,43 @@ argstr(const char *p, int flag)
|
|||
STPUTC(c, expdest);
|
||||
c = *p++;
|
||||
STPUTC(c, expdest);
|
||||
if (c == '\n') /* should not happen, but ... */
|
||||
line_number++;
|
||||
break;
|
||||
case CTLVAR:
|
||||
case CTLVAR: {
|
||||
#ifdef DEBUG
|
||||
unsigned int pos = expdest - stackblock();
|
||||
#endif
|
||||
p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
|
||||
STACKSTRNUL(expdest);
|
||||
VTRACE(DBG_EXPAND, ("argstr evalvar "
|
||||
"added \"%.*s\" to expdest (followed by: %2.2x)\n",
|
||||
expdest-ed, ed, *expdest));
|
||||
"added \"%s\" to expdest\n",
|
||||
stackblock() + pos));
|
||||
break;
|
||||
}
|
||||
case CTLBACKQ:
|
||||
case CTLBACKQ|CTLQUOTE:
|
||||
case CTLBACKQ|CTLQUOTE: {
|
||||
#ifdef DEBUG
|
||||
unsigned int pos = expdest - stackblock();
|
||||
#endif
|
||||
expbackq(argbackq->n, c & CTLQUOTE, flag);
|
||||
argbackq = argbackq->next;
|
||||
VTRACE(DBG_EXPAND, ("argstr expbackq "
|
||||
"added \"%.*s\" to expdest (followed by: %2.2x)\n",
|
||||
expdest-ed, ed, *expdest));
|
||||
STACKSTRNUL(expdest);
|
||||
VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" "
|
||||
"to expdest\n", stackblock() + pos));
|
||||
break;
|
||||
case CTLARI:
|
||||
}
|
||||
case CTLARI: {
|
||||
#ifdef DEBUG
|
||||
unsigned int pos = expdest - stackblock();
|
||||
#endif
|
||||
p = expari(p);
|
||||
STACKSTRNUL(expdest);
|
||||
VTRACE(DBG_EXPAND, ("argstr expari "
|
||||
"+ \"%.*s\" to dest (fwd by: %2.2x) p=\"%.5s...\"\n",
|
||||
expdest-ed, ed, *expdest, p));
|
||||
"+ \"%s\" to expdest p=\"%.5s...\"\n",
|
||||
stackblock() + pos, p));
|
||||
break;
|
||||
}
|
||||
case ':':
|
||||
case '=':
|
||||
/*
|
||||
|
@ -333,20 +346,29 @@ exptilde(const char *p, int flag)
|
|||
const char *home;
|
||||
int quotes = flag & (EXP_GLOB | EXP_CASE);
|
||||
char *user;
|
||||
struct stackmark smark;
|
||||
#ifdef DEBUG
|
||||
unsigned int offs = expdest - stackblock();
|
||||
#endif
|
||||
|
||||
setstackmark(&smark);
|
||||
user = stackblock(); /* we will just borrow top of stack */
|
||||
|
||||
user = expdest; /* we will just borrow top of stack */
|
||||
while ((c = *++p) != '\0') {
|
||||
switch(c) {
|
||||
case CTLESC:
|
||||
case CTLVAR:
|
||||
case CTLBACKQ:
|
||||
case CTLESC: /* any of these occurring */
|
||||
case CTLVAR: /* means ~ expansion */
|
||||
case CTLBACKQ: /* does not happen at all */
|
||||
case CTLBACKQ | CTLQUOTE:
|
||||
case CTLARI:
|
||||
case CTLARI: /* just leave original unchanged */
|
||||
case CTLENDARI:
|
||||
case CTLQUOTEMARK:
|
||||
case CTLENDVAR:
|
||||
case '\n':
|
||||
popstackmark(&smark);
|
||||
return (startp);
|
||||
case CTLNONL:
|
||||
break;
|
||||
continue;
|
||||
case ':':
|
||||
if (flag & EXP_VARTILDE)
|
||||
goto done;
|
||||
|
@ -358,24 +380,46 @@ exptilde(const char *p, int flag)
|
|||
}
|
||||
done:
|
||||
STACKSTRNUL(user);
|
||||
user = stackblock(); /* to start of collected username */
|
||||
|
||||
CTRACE(DBG_EXPAND, ("exptilde, found \"%s\" :", expdest));
|
||||
if (*expdest == '\0')
|
||||
CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user));
|
||||
if (*user == '\0') {
|
||||
home = lookupvar("HOME");
|
||||
else if ((pw = getpwnam(expdest)) == NULL)
|
||||
/*
|
||||
* if HOME is unset, results are unspecified...
|
||||
* we used to just leave the ~ unchanged, but
|
||||
* (some) other shells do ... and this seems more useful.
|
||||
*/
|
||||
if (home == NULL && (pw = getpwuid(getuid())) != NULL)
|
||||
home = pw->pw_dir;
|
||||
} else if ((pw = getpwnam(user)) == NULL) {
|
||||
/*
|
||||
* If user does not exist, results are undefined.
|
||||
* so we can abort() here if we want, but let's not!
|
||||
*/
|
||||
home = NULL;
|
||||
else
|
||||
} else
|
||||
home = pw->pw_dir;
|
||||
STUNSTR(user - expdest); /* return borrowed string space */
|
||||
|
||||
if (home == NULL || *home == '\0')
|
||||
VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>"));
|
||||
popstackmark(&smark); /* now expdest is valid again */
|
||||
|
||||
/*
|
||||
* Posix XCU 2.6.1: The value of $HOME (for ~) or the initial
|
||||
* working directory from getpwnam() for ~user
|
||||
* Nothing there about "except if a null string". So do what it wants.
|
||||
*/
|
||||
if (home == NULL /* || *home == '\0' */) {
|
||||
CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp));
|
||||
return startp;
|
||||
while ((c = *home++) != '\0') {
|
||||
} while ((c = *home++) != '\0') {
|
||||
if (quotes && SQSYNTAX[(int)c] == CCTL)
|
||||
STPUTC(CTLESC, expdest);
|
||||
STPUTC(c, expdest);
|
||||
}
|
||||
CTRACE(DBG_EXPAND, ("returning \"%s\"\n", p));
|
||||
CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n",
|
||||
expdest - stackblock() - offs, expdest - stackblock() - offs,
|
||||
stackblock() + offs, p));
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
@ -431,8 +475,13 @@ removerecordregions(int endoff)
|
|||
|
||||
|
||||
/*
|
||||
* Expand arithmetic expression. Backup to start of expression,
|
||||
* evaluate, place result in (backed up) result, adjust string position.
|
||||
* Expand arithmetic expression.
|
||||
*
|
||||
* In this incarnation, we start at the beginning (yes, "Let's start at the
|
||||
* very beginning. A very good place to start.") and collect the expression
|
||||
* until the end - which means expanding anything contained within.
|
||||
*
|
||||
* Fortunately, argstr() just happens to do that for us...
|
||||
*/
|
||||
STATIC const char *
|
||||
expari(const char *p)
|
||||
|
@ -442,23 +491,10 @@ expari(const char *p)
|
|||
int adjustment;
|
||||
int begoff;
|
||||
int quoted;
|
||||
#ifdef DEBUG
|
||||
const char *ed = expdest;
|
||||
#endif
|
||||
struct stackmark smark;
|
||||
|
||||
/* ifsfree(); */
|
||||
|
||||
/*
|
||||
* This routine is slightly over-complicated for
|
||||
* efficiency. First we make sure there is
|
||||
* enough space for the result, which may be bigger
|
||||
* than the expression if we add exponentation. Next we
|
||||
* scan backwards looking for the start of arithmetic. If the
|
||||
* next previous character is a CTLESC character, then we
|
||||
* have to rescan starting from the beginning since CTLESC
|
||||
* characters have to be processed left to right.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPACE_NEEDED is enough for all possible digits (rounded up)
|
||||
* plus possible "-", and the terminating '\0', hence, plus 2
|
||||
|
@ -469,57 +505,47 @@ expari(const char *p)
|
|||
* so this is safe, if occasionally slightly wasteful.
|
||||
*/
|
||||
#define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2))
|
||||
/*
|
||||
* Only need check for SPACE_NEEDED-2 as we have already
|
||||
* consumed (but will overwrite) 2 chars, the CTLARI, and the
|
||||
* flags (quoting) byte (if those had not already been seen,
|
||||
* we would not be here.)
|
||||
*/
|
||||
|
||||
quoted = *p++ == '"';
|
||||
begoff = expdest - stackblock();
|
||||
VTRACE(DBG_EXPAND, ("expari: '%c' \"%s\" begoff %d\n", p[-1],p,begoff));
|
||||
VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n",
|
||||
quoted ? "(quoted)" : "", p, begoff));
|
||||
|
||||
p = argstr(p, EXP_NL); /* expand $(( )) string */
|
||||
STPUTC('\0', expdest);
|
||||
start = stackblock() + begoff;
|
||||
VTRACE(DBG_EXPAND, ("expari: argstr added: '%s' start: \"%.8s\"\n",
|
||||
ed, start));
|
||||
removerecordregions(begoff);
|
||||
rmescapes_nl(start); /* convert CRTNONL back into \n's */
|
||||
q = grabstackstr(expdest);
|
||||
|
||||
removerecordregions(begoff); /* nothing there is kept */
|
||||
rmescapes_nl(start); /* convert CRTNONL back into \n's */
|
||||
|
||||
setstackmark(&smark);
|
||||
q = grabstackstr(expdest); /* keep the expression while eval'ing */
|
||||
result = arith(start, line_number);
|
||||
VTRACE(DBG_EXPAND, ("expari: after arith: result=%jd '%s' q@'%.3s'\n",
|
||||
result, ed, q));
|
||||
ungrabstackstr(q, expdest);
|
||||
popstackmark(&smark); /* return the stack to before grab */
|
||||
|
||||
start = stackblock() + begoff;
|
||||
start = stackblock() + begoff; /* block may have moved */
|
||||
adjustment = expdest - start;
|
||||
VTRACE(DBG_EXPAND, ("expari: removing %d ", adjustment));
|
||||
STADJUST(-adjustment, expdest); /* remove the argstr() */
|
||||
VTRACE(DBG_EXPAND, ("ed \"%.*s\", expdest \"%s\"\n",
|
||||
expdest-ed, ed, expdest));
|
||||
STADJUST(-adjustment, expdest); /* remove the argstr() result */
|
||||
|
||||
CHECKSTRSPACE(SPACE_NEEDED, expdest);
|
||||
CHECKSTRSPACE(SPACE_NEEDED, expdest); /* nb: stack block might move */
|
||||
fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result);
|
||||
VTRACE(DBG_EXPAND, ("expari: after fmtstr: ed \"%s\" expdest \"%s\"\n",
|
||||
ed, expdest));
|
||||
|
||||
for (q = expdest; *q++ != '\0'; )
|
||||
for (q = expdest; *q++ != '\0'; ) /* find end of what we added */
|
||||
;
|
||||
|
||||
if (quoted == 0) /* allow weird splitting */
|
||||
recordregion(begoff, begoff + q - 1 - expdest, 0);
|
||||
adjustment = q - expdest - 1;
|
||||
STADJUST(adjustment, expdest);
|
||||
VTRACE(DBG_EXPAND, ("expari: adding %d ed \"%.*s\", "
|
||||
"expdest \"%s\" returning \"%.5s...\"\n", adjustment, expdest - ed,
|
||||
ed, expdest, p));
|
||||
STADJUST(adjustment, expdest); /* move expdest to end */
|
||||
VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", produced \"%s\" "
|
||||
"returning \"%.5s...\"\n", adjustment, stackblock() + begoff, p));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Expand stuff in backwards quotes.
|
||||
* Expand stuff in backwards quotes (these days, any command substitution).
|
||||
*/
|
||||
|
||||
STATIC void
|
||||
|
@ -529,7 +555,7 @@ expbackq(union node *cmd, int quoted, int flag)
|
|||
int i;
|
||||
char buf[128];
|
||||
char *p;
|
||||
char *dest = expdest;
|
||||
char *dest = expdest; /* expdest may be reused by eval, use an alt */
|
||||
struct ifsregion saveifs, *savelastp;
|
||||
struct nodelist *saveargbackq;
|
||||
char lastc;
|
||||
|
@ -538,25 +564,29 @@ expbackq(union node *cmd, int quoted, int flag)
|
|||
int saveherefd;
|
||||
int quotes = flag & (EXP_GLOB | EXP_CASE);
|
||||
int nnl;
|
||||
struct stackmark smark;
|
||||
|
||||
VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x)\n", quoted, flag));
|
||||
VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n",
|
||||
quoted, flag, startloc));
|
||||
INTOFF;
|
||||
saveifs = ifsfirst;
|
||||
savelastp = ifslastp;
|
||||
saveargbackq = argbackq;
|
||||
saveherefd = herefd;
|
||||
herefd = -1;
|
||||
p = grabstackstr(dest);
|
||||
evalbackcmd(cmd, &in);
|
||||
ungrabstackstr(p, dest);
|
||||
|
||||
setstackmark(&smark); /* preserve the stack */
|
||||
p = grabstackstr(dest); /* save what we have there currently */
|
||||
evalbackcmd(cmd, &in); /* evaluate the $( ) tree (using stack) */
|
||||
popstackmark(&smark); /* and return stack to when we entered */
|
||||
|
||||
ifsfirst = saveifs;
|
||||
ifslastp = savelastp;
|
||||
argbackq = saveargbackq;
|
||||
herefd = saveherefd;
|
||||
|
||||
p = in.buf;
|
||||
lastc = '\0';
|
||||
nnl = 0;
|
||||
p = in.buf; /* now extract the results */
|
||||
nnl = 0; /* dropping trailing \n's */
|
||||
for (;;) {
|
||||
if (--in.nleft < 0) {
|
||||
if (in.fd < 0)
|
||||
|
@ -571,13 +601,38 @@ expbackq(union node *cmd, int quoted, int flag)
|
|||
}
|
||||
lastc = *p++;
|
||||
if (lastc != '\0') {
|
||||
if (lastc == '\n')
|
||||
nnl++;
|
||||
if (lastc == '\n') /* don't save \n yet */
|
||||
nnl++; /* it might be trailing */
|
||||
else {
|
||||
CHECKSTRSPACE(nnl + 2, dest);
|
||||
while (nnl > 0) {
|
||||
nnl--;
|
||||
USTPUTC('\n', dest);
|
||||
/*
|
||||
* We have something other than \n
|
||||
*
|
||||
* Before saving it, we need to insert
|
||||
* any \n's that we have just skipped.
|
||||
*/
|
||||
|
||||
/* XXX
|
||||
* this hack is just because our
|
||||
* CHECKSTRSPACE() is lazy, and only
|
||||
* ever grows the stack once, even
|
||||
* if that does not allocate the space
|
||||
* we requested. ie: safe for small
|
||||
* requests, but not large ones.
|
||||
* FIXME someday...
|
||||
*/
|
||||
if (nnl < 20) {
|
||||
CHECKSTRSPACE(nnl + 2, dest);
|
||||
while (nnl > 0) {
|
||||
nnl--;
|
||||
USTPUTC('\n', dest);
|
||||
}
|
||||
} else {
|
||||
/* The slower, safer, way */
|
||||
while (nnl > 0) {
|
||||
nnl--;
|
||||
STPUTC('\n', dest);
|
||||
}
|
||||
CHECKSTRSPACE(2, dest);
|
||||
}
|
||||
if (quotes && syntax[(int)lastc] == CCTL)
|
||||
USTPUTC(CTLESC, dest);
|
||||
|
@ -598,7 +653,8 @@ expbackq(union node *cmd, int quoted, int flag)
|
|||
(int)((dest - stackblock()) - startloc),
|
||||
(int)((dest - stackblock()) - startloc),
|
||||
stackblock() + startloc));
|
||||
expdest = dest;
|
||||
|
||||
expdest = dest; /* all done, expdest is all ours again */
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
@ -613,6 +669,8 @@ subevalvar(const char *p, const char *str, int subtype, int startloc,
|
|||
int amount;
|
||||
|
||||
herefd = -1;
|
||||
VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n",
|
||||
subtype, p, p-str, str, startloc, varflags));
|
||||
argstr(p, EXP_TILDE);
|
||||
STACKSTRNUL(expdest);
|
||||
herefd = saveherefd;
|
||||
|
@ -622,10 +680,10 @@ subevalvar(const char *p, const char *str, int subtype, int startloc,
|
|||
switch (subtype) {
|
||||
case VSASSIGN:
|
||||
setvar(str, startp, 0);
|
||||
amount = startp - expdest;
|
||||
amount = startp - expdest; /* remove what argstr added */
|
||||
STADJUST(amount, expdest);
|
||||
varflags &= ~VSNUL;
|
||||
return 1;
|
||||
varflags &= ~VSNUL; /*XXX Huh? What's that achieve? */
|
||||
return 1; /* go back and eval var again */
|
||||
|
||||
case VSQUESTION:
|
||||
if (*p != CTLENDVAR) {
|
||||
|
@ -833,7 +891,7 @@ evalvar(const char *p, int flag)
|
|||
if (special) {
|
||||
if (varflags & VSLINENO) {
|
||||
/*
|
||||
* The LINENO hack
|
||||
* The LINENO hack (expansion part)
|
||||
*/
|
||||
while (--special > 0) {
|
||||
/* not needed, it is a number...
|
||||
|
@ -946,15 +1004,12 @@ evalvar(const char *p, int flag)
|
|||
removerecordregions(startloc);
|
||||
goto again;
|
||||
}
|
||||
apply_ifs = 0;
|
||||
apply_ifs = 0; /* never executed */
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
#if 0
|
||||
p[-1] = '='; /* recover overwritten '=' */
|
||||
#endif
|
||||
|
||||
if (apply_ifs)
|
||||
recordregion(startloc, expdest - stackblock(),
|
||||
|
@ -985,7 +1040,7 @@ evalvar(const char *p, int flag)
|
|||
|
||||
|
||||
/*
|
||||
* Test whether a specialized variable is set.
|
||||
* Test whether a special parameter is set.
|
||||
*/
|
||||
|
||||
STATIC int
|
||||
|
@ -1007,7 +1062,15 @@ varisset(const char *name, int nulok)
|
|||
}
|
||||
} else if (is_digit(*name)) {
|
||||
char *ap;
|
||||
int num = atoi(name);
|
||||
long num;
|
||||
|
||||
/*
|
||||
* handle overflow sensibly (the *ap tests should never fail)
|
||||
*/
|
||||
errno = 0;
|
||||
num = strtol(name, &ap, 10);
|
||||
if (errno != 0 || (*ap != '\0' && *ap != '='))
|
||||
return 0;
|
||||
|
||||
if (num == 0)
|
||||
ap = arg0;
|
||||
|
@ -1043,16 +1106,16 @@ varvalue(const char *name, int quoted, int subtype, int flag)
|
|||
|
||||
#define STRTODEST(p) \
|
||||
do {\
|
||||
if (flag & (EXP_GLOB | EXP_CASE) && subtype != VSLENGTH) { \
|
||||
syntax = quoted? DQSYNTAX : BASESYNTAX; \
|
||||
while (*p) { \
|
||||
if (syntax[(int)*p] == CCTL) \
|
||||
STPUTC(CTLESC, expdest); \
|
||||
STPUTC(*p++, expdest); \
|
||||
} \
|
||||
} else \
|
||||
while (*p) \
|
||||
STPUTC(*p++, expdest); \
|
||||
if (flag & (EXP_GLOB | EXP_CASE) && subtype != VSLENGTH) { \
|
||||
syntax = quoted? DQSYNTAX : BASESYNTAX; \
|
||||
while (*p) { \
|
||||
if (syntax[(int)*p] == CCTL) \
|
||||
STPUTC(CTLESC, expdest); \
|
||||
STPUTC(*p++, expdest); \
|
||||
} \
|
||||
} else \
|
||||
while (*p) \
|
||||
STPUTC(*p++, expdest); \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: memalloc.c,v 1.29 2008/02/15 17:26:06 matt Exp $ */
|
||||
/* $NetBSD: memalloc.c,v 1.30 2017/06/17 07:22:12 kre Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
|
@ -37,7 +37,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95";
|
||||
#else
|
||||
__RCSID("$NetBSD: memalloc.c,v 1.29 2008/02/15 17:26:06 matt Exp $");
|
||||
__RCSID("$NetBSD: memalloc.c,v 1.30 2017/06/17 07:22:12 kre Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -167,6 +167,7 @@ setstackmark(struct stackmark *mark)
|
|||
mark->stackp = stackp;
|
||||
mark->stacknxt = stacknxt;
|
||||
mark->stacknleft = stacknleft;
|
||||
mark->sstrnleft = sstrnleft;
|
||||
mark->marknext = markp;
|
||||
markp = mark;
|
||||
}
|
||||
|
@ -186,6 +187,7 @@ popstackmark(struct stackmark *mark)
|
|||
}
|
||||
stacknxt = mark->stacknxt;
|
||||
stacknleft = mark->stacknleft;
|
||||
sstrnleft = mark->sstrnleft;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
@ -297,11 +299,26 @@ makestrspace(void)
|
|||
return stackblock() + len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that this only works to release stack space for reuse
|
||||
* if nothing else has allocated space on the stack since the grabstackstr()
|
||||
*
|
||||
* "s" is the start of the area to be released, and "p" represents the end
|
||||
* of the string we have stored beyond there and are now releasing.
|
||||
* (ie: "p" should be the same as in the call to grabstackstr()).
|
||||
*
|
||||
* stunalloc(s) and ungrabstackstr(s, p) are almost interchangable after
|
||||
* a grabstackstr(), however the latter also returns string space so we
|
||||
* can just continue with STPUTC() etc without needing a new STARTSTACKSTR(s)
|
||||
*/
|
||||
void
|
||||
ungrabstackstr(char *s, char *p)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (s < stacknxt || stacknxt + stacknleft < s)
|
||||
abort();
|
||||
#endif
|
||||
stacknleft += stacknxt - s;
|
||||
stacknxt = s;
|
||||
sstrnleft = stacknleft - (p - s);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: memalloc.h,v 1.16 2017/06/03 10:31:16 kre Exp $ */
|
||||
/* $NetBSD: memalloc.h,v 1.17 2017/06/17 07:22:12 kre Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
|
@ -38,6 +38,7 @@ struct stackmark {
|
|||
struct stack_block *stackp;
|
||||
char *stacknxt;
|
||||
int stacknleft;
|
||||
int sstrnleft;
|
||||
struct stackmark *marknext;
|
||||
};
|
||||
|
||||
|
@ -68,11 +69,10 @@ void ungrabstackstr(char *, char *);
|
|||
#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
|
||||
#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(); }
|
||||
#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
|
||||
#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
|
||||
#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), sstrnleft++, *p = '\0') : (*p = '\0'))
|
||||
#define STUNPUTC(p) (++sstrnleft, --p)
|
||||
#define STTOPC(p) p[-1]
|
||||
#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
|
||||
#define STUNSTR(amount) (sstrnleft -= (amount))
|
||||
#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)
|
||||
#define grabstackstr(p) stalloc((p) - stackblock())
|
||||
|
||||
#define ckfree(p) free((pointer)(p))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: parser.c,v 1.137 2017/06/08 22:10:39 kre Exp $ */
|
||||
/* $NetBSD: parser.c,v 1.138 2017/06/17 07:22:12 kre Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
|
@ -37,7 +37,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
|
||||
#else
|
||||
__RCSID("$NetBSD: parser.c,v 1.137 2017/06/08 22:10:39 kre Exp $");
|
||||
__RCSID("$NetBSD: parser.c,v 1.138 2017/06/17 07:22:12 kre Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -825,7 +825,10 @@ checkend(int c, char * const eofmark, const int striptabs)
|
|||
char *q;
|
||||
|
||||
for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++)
|
||||
;
|
||||
if (c2 == '\n') {
|
||||
plinno++;
|
||||
needprompt = doprompt;
|
||||
}
|
||||
if ((c2 == PEOF || c2 == '\n') && *q == '\0') {
|
||||
c = PEOF;
|
||||
if (c2 == '\n') {
|
||||
|
@ -893,13 +896,13 @@ slurp_heredoc(char *const eofmark, const int striptabs, const int sq)
|
|||
* not suppress the newline (the \ quotes itself)
|
||||
*/
|
||||
if (c == '\\') { /* A backslash */
|
||||
STPUTC(c, out);
|
||||
c = pgetc(); /* followed by */
|
||||
if (c == '\n') { /* a newline? */
|
||||
/*XXX CTLNONL ?? XXX*/
|
||||
STPUTC(c, out);
|
||||
plinno++;
|
||||
continue; /* :drop both */
|
||||
continue; /* don't break */
|
||||
}
|
||||
STPUTC('\\', out); /* else keep \ */
|
||||
}
|
||||
STPUTC(c, out); /* keep the char */
|
||||
if (c == '\n') { /* at end of line */
|
||||
|
@ -980,6 +983,8 @@ readheredocs(void)
|
|||
* Now "parse" here docs that have unquoted eofmarkers.
|
||||
*/
|
||||
setinputstring(wordtext, 1, line);
|
||||
VTRACE(DBG_PARSE, ("Reprocessing %d line here doc from %d\n",
|
||||
l, line));
|
||||
readtoken1(pgetc(), DQSYNTAX, 1);
|
||||
n->narg.text = wordtext;
|
||||
n->narg.backquote = backquotelist;
|
||||
|
@ -1341,7 +1346,7 @@ cleanup_state_stack(VSS *stack)
|
|||
*/
|
||||
static char *
|
||||
parsebackq(VSS *const stack, char * const in,
|
||||
struct nodelist **const pbqlist, const int oldstyle)
|
||||
struct nodelist **const pbqlist, const int oldstyle, const int magicq)
|
||||
{
|
||||
struct nodelist **nlpp;
|
||||
const int savepbq = parsebackquote;
|
||||
|
@ -1353,6 +1358,7 @@ parsebackq(VSS *const stack, char * const in,
|
|||
struct jmploc *const savehandler = handler;
|
||||
const int savelen = in - stackblock();
|
||||
int saveprompt;
|
||||
int lno;
|
||||
|
||||
if (setjmp(jmploc.loc)) {
|
||||
if (sstr)
|
||||
|
@ -1381,23 +1387,35 @@ parsebackq(VSS *const stack, char * const in,
|
|||
char *pstr;
|
||||
int line1 = plinno;
|
||||
|
||||
VTRACE(DBG_PARSE, ("parsebackq: repackaging `` as $( )"));
|
||||
/*
|
||||
* Because the entire `...` is read here, we don't
|
||||
* need to bother the state stack. That will be used
|
||||
* (as appropriate) when the processed string is re-read.
|
||||
*/
|
||||
STARTSTACKSTR(out);
|
||||
#ifdef DEBUG
|
||||
for (psavelen = 0;;psavelen++) {
|
||||
#else
|
||||
for (;;) {
|
||||
#endif
|
||||
if (needprompt) {
|
||||
setprompt(2);
|
||||
needprompt = 0;
|
||||
}
|
||||
pc = pgetc_linecont();
|
||||
pc = pgetc();
|
||||
if (pc == '`')
|
||||
break;
|
||||
switch (pc) {
|
||||
case '\\':
|
||||
pc = pgetc(); /* cannot be '\n' */
|
||||
pc = pgetc();
|
||||
#ifdef DEBUG
|
||||
psavelen++;
|
||||
#endif
|
||||
if (pc == '\n') { /* keep \ \n for later */
|
||||
plinno++;
|
||||
needprompt = doprompt;
|
||||
}
|
||||
if (pc != '\\' && pc != '`' && pc != '$'
|
||||
&& (!ISDBLQUOTE() || pc != '"'))
|
||||
STPUTC('\\', out);
|
||||
|
@ -1405,14 +1423,11 @@ parsebackq(VSS *const stack, char * const in,
|
|||
|
||||
case '\n':
|
||||
plinno++;
|
||||
/*
|
||||
out = insert_elided_nl(out);
|
||||
*/
|
||||
needprompt = doprompt;
|
||||
break;
|
||||
|
||||
case PEOF:
|
||||
startlinno = plinno;
|
||||
startlinno = line1;
|
||||
synerror("EOF in backquote substitution");
|
||||
break;
|
||||
|
||||
|
@ -1422,7 +1437,9 @@ parsebackq(VSS *const stack, char * const in,
|
|||
STPUTC(pc, out);
|
||||
}
|
||||
STPUTC('\0', out);
|
||||
VTRACE(DBG_PARSE, (" read %d", psavelen));
|
||||
psavelen = out - stackblock();
|
||||
VTRACE(DBG_PARSE, (" produced %d\n", psavelen));
|
||||
if (psavelen > 0) {
|
||||
pstr = grabstackstr(out);
|
||||
setinputstring(pstr, 1, line1);
|
||||
|
@ -1441,7 +1458,9 @@ parsebackq(VSS *const stack, char * const in,
|
|||
} else
|
||||
saveprompt = 0;
|
||||
|
||||
lno = -plinno;
|
||||
n = list(0, oldstyle);
|
||||
lno += plinno;
|
||||
|
||||
if (oldstyle)
|
||||
doprompt = saveprompt;
|
||||
|
@ -1475,9 +1494,11 @@ parsebackq(VSS *const stack, char * const in,
|
|||
}
|
||||
parsebackquote = savepbq;
|
||||
handler = savehandler;
|
||||
if (arinest || ISDBLQUOTE())
|
||||
if (arinest || ISDBLQUOTE()) {
|
||||
USTPUTC(CTLBACKQ | CTLQUOTE, out);
|
||||
else
|
||||
while (--lno >= 0)
|
||||
USTPUTC(CTLNONL, out);
|
||||
} else
|
||||
USTPUTC(CTLBACKQ, out);
|
||||
|
||||
return out;
|
||||
|
@ -1729,25 +1750,25 @@ readtoken1(int firstc, char const *syn, int magicq)
|
|||
USTPUTC(c, out);
|
||||
--parenlevel;
|
||||
} else {
|
||||
if (pgetc_linecont() == ')') {
|
||||
if (pgetc_linecont() == /*(*/ ')') {
|
||||
out = insert_elided_nl(out);
|
||||
if (--arinest == 0) {
|
||||
TS_POP();
|
||||
USTPUTC(CTLENDARI, out);
|
||||
} else
|
||||
USTPUTC(')', out);
|
||||
USTPUTC(/*(*/ ')', out);
|
||||
} else {
|
||||
/*
|
||||
* unbalanced parens
|
||||
* (don't 2nd guess - no error)
|
||||
*/
|
||||
pungetc();
|
||||
USTPUTC(')', out);
|
||||
USTPUTC(/*(*/ ')', out);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case CBQUOTE: /* '`' */
|
||||
out = parsebackq(stack, out, &bqlist, 1);
|
||||
out = parsebackq(stack, out, &bqlist, 1, magicq);
|
||||
continue;
|
||||
case CEOF: /* --> c == PEOF */
|
||||
break; /* will exit loop */
|
||||
|
@ -1762,7 +1783,7 @@ readtoken1(int firstc, char const *syn, int magicq)
|
|||
|
||||
if (syntax == ARISYNTAX) {
|
||||
cleanup_state_stack(stack);
|
||||
synerror("Missing '))'");
|
||||
synerror(/*((*/ "Missing '))'");
|
||||
}
|
||||
if (syntax != BASESYNTAX && /* ! parsebackquote && */ !magicq) {
|
||||
cleanup_state_stack(stack);
|
||||
|
@ -1775,7 +1796,7 @@ readtoken1(int firstc, char const *syn, int magicq)
|
|||
synerror("Missing '}'");
|
||||
}
|
||||
|
||||
USTPUTC('\0', out);
|
||||
STPUTC('\0', out);
|
||||
len = out - stackblock();
|
||||
out = stackblock();
|
||||
|
||||
|
@ -1790,6 +1811,11 @@ readtoken1(int firstc, char const *syn, int magicq)
|
|||
}
|
||||
}
|
||||
|
||||
VTRACE(DBG_PARSE,
|
||||
("readtoken1 %sword \"%s\", completed%s (%d) left %d enl\n",
|
||||
(quotef ? "quoted " : ""), out, (bqlist ? " with cmdsubs" : ""),
|
||||
len, elided_nl));
|
||||
|
||||
quoteflag = quotef;
|
||||
backquotelist = bqlist;
|
||||
grabstackblock(len);
|
||||
|
@ -1822,7 +1848,7 @@ parsesub: {
|
|||
} else {
|
||||
out = insert_elided_nl(out);
|
||||
pungetc();
|
||||
out = parsebackq(stack, out, &bqlist, 0);
|
||||
out = parsebackq(stack, out, &bqlist, 0, magicq);
|
||||
}
|
||||
} else {
|
||||
USTPUTC(CTLVAR, out);
|
||||
|
@ -1978,8 +2004,8 @@ parsearith: {
|
|||
*
|
||||
* XXX It isn't, must fix, soonish...
|
||||
*/
|
||||
USTPUTC('(', out);
|
||||
USTPUTC('(', out);
|
||||
USTPUTC('(' /*)*/, out);
|
||||
USTPUTC('(' /*)*/, out);
|
||||
/*
|
||||
* Need 2 of them because there will (should be)
|
||||
* two closing ))'s to follow later.
|
||||
|
@ -2009,8 +2035,12 @@ parsearith: {
|
|||
|
||||
#ifdef mkinit
|
||||
RESET {
|
||||
struct heredoc;
|
||||
extern struct heredoc *heredoclist;
|
||||
|
||||
tokpushback = 0;
|
||||
checkkwd = 0;
|
||||
heredoclist = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue