Better handling of word splitting. Don't record the same region

multiple times when performing nested variable expansion, and
preserve some quoting information in order to avoid removing
apparently empty expansion result.
This commit is contained in:
Tor Egge 1998-09-06 21:13:09 +00:00
parent 37f0a6708e
commit 6f47734fd7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=38887
7 changed files with 231 additions and 49 deletions

View File

@ -39,7 +39,7 @@
static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
#endif
static const char rcsid[] =
"$Id$";
"$Id: expand.c,v 1.22 1998/05/18 06:43:40 charnier Exp $";
#endif /* not lint */
#include <sys/types.h>
@ -51,6 +51,7 @@ static const char rcsid[] =
#include <pwd.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
/*
* Routines to expand arguments to commands. We have to deal with
@ -102,6 +103,7 @@ STATIC char *evalvar __P((char *, int));
STATIC int varisset __P((char *, int));
STATIC void varvalue __P((char *, int, int));
STATIC void recordregion __P((int, int, int));
STATIC void removerecordregions __P((int));
STATIC void ifsbreakup __P((char *, struct arglist *));
STATIC void expandmeta __P((struct strlist *, int));
STATIC void expmeta __P((char *, char *));
@ -140,7 +142,8 @@ expandhere(arg, fd)
{
herefd = fd;
expandarg(arg, (struct arglist *)NULL, 0);
xwrite(fd, stackblock(), expdest - stackblock());
xwrite(fd, stackblock(),
rmquotes(stackblock(), expdest - stackblock()));
}
@ -182,6 +185,8 @@ expandarg(arg, arglist, flag)
} else {
if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
rmescapes(p);
else
rmquotes0(p);
sp = (struct strlist *)stalloc(sizeof (struct strlist));
sp->text = p;
*exparg.lastp = sp;
@ -226,6 +231,12 @@ argstr(p, flag)
case '\0':
case CTLENDVAR: /* ??? */
goto breakloop;
case CTLQUOTEMARK:
/* "$@" syntax adherence hack */
if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
break;
STPUTC(c, expdest);
break;
case CTLESC:
if (quotes)
STPUTC(c, expdest);
@ -315,6 +326,46 @@ exptilde(p, flag)
}
STATIC void
removerecordregions(endoff)
int endoff;
{
if (ifslastp == NULL)
return;
if (ifsfirst.endoff > endoff) {
while (ifsfirst.next != NULL) {
struct ifsregion *ifsp;
INTOFF;
ifsp = ifsfirst.next->next;
ckfree(ifsfirst.next);
ifsfirst.next = ifsp;
INTON;
}
if (ifsfirst.begoff > endoff)
ifslastp = NULL;
else {
ifslastp = &ifsfirst;
ifsfirst.endoff = endoff;
}
return;
}
ifslastp = &ifsfirst;
while (ifslastp->next && ifslastp->next->begoff < endoff)
ifslastp=ifslastp->next;
while (ifslastp->next != NULL) {
struct ifsregion *ifsp;
INTOFF;
ifsp = ifslastp->next->next;
ckfree(ifslastp->next);
ifslastp->next = ifsp;
INTON;
}
if (ifslastp->endoff > endoff)
ifslastp->endoff = endoff;
}
/*
* Expand arithmetic expression. Backup to start of expression,
* evaluate, place result in (backed up) result, adjust string position.
@ -325,17 +376,10 @@ expari(flag)
{
char *p, *start;
int result;
int begoff;
int quotes = flag & (EXP_FULL | EXP_CASE);
int quoted;
while (ifsfirst.next != NULL) {
struct ifsregion *ifsp;
INTOFF;
ifsp = ifsfirst.next->next;
ckfree(ifsfirst.next);
ifsfirst.next = ifsp;
INTON;
}
ifslastp = NULL;
/*
* This routine is slightly over-compilcated for
@ -362,12 +406,21 @@ expari(flag)
for (p = start; *p != CTLARI; p++)
if (*p == CTLESC)
p++;
if (p[1] == '"')
quoted=1;
else
quoted=0;
begoff = p - start;
removerecordregions(begoff);
if (quotes)
rmescapes(p+1);
result = arith(p+1);
rmescapes(p+2);
result = arith(p+2);
fmtstr(p, 12, "%d", result);
while (*p++)
;
if (quoted == 0)
recordregion(begoff, p - 1 - start, 0);
result = expdest - p + 1;
STADJUST(-result, expdest);
}
@ -670,19 +723,24 @@ evalvar(p, flag)
STPUTC('\0', expdest);
pat = expdest;
if (subevalvar(p, NULL, expdest - stackblock(), subtype,
startloc, varflags))
goto record;
else {
startloc, varflags) == 0) {
int amount = (expdest - pat) + 1;
STADJUST(-amount, expdest);
}
break;
/* Remove any recorded regions beyond start of variable */
removerecordregions(startloc);
goto record;
case VSASSIGN:
case VSQUESTION:
if (!set) {
if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
varflags &= ~VSNUL;
/*
* Remove any recorded regions beyond
* start of variable
*/
removerecordregions(startloc);
goto again;
}
break;
@ -816,17 +874,23 @@ varvalue(name, quoted, allow_split)
}
break;
case '@':
if (allow_split) {
sep = '\0';
goto allargs;
if (allow_split && quoted) {
for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
STRTODEST(p);
if (*ap)
STPUTC('\0', expdest);
}
break;
}
/* fall through */
case '*':
sep = ' ';
allargs:
if (ifsset() != 0)
sep = ifsval()[0];
else
sep = ' ';
for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
STRTODEST(p);
if (*ap)
if (*ap && sep)
STPUTC(sep, expdest);
}
break;
@ -893,45 +957,69 @@ ifsbreakup(string, arglist)
char *q;
char *ifs;
int ifsspc;
int nulonly;
start = string;
ifsspc = 0;
nulonly = 0;
if (ifslastp != NULL) {
ifsp = &ifsfirst;
do {
p = string + ifsp->begoff;
ifs = ifsp->nulonly? nullstr : ifsval();
ifsspc = strchr(ifs, ' ') != NULL;
nulonly = ifsp->nulonly;
ifs = nulonly ? nullstr :
( ifsset() ? ifsval() : " \t\n" );
ifsspc = 0;
while (p < string + ifsp->endoff) {
q = p;
if (*p == CTLESC)
p++;
if (strchr(ifs, *p++)) {
if (q > start || !ifsspc) {
*q = '\0';
sp = (struct strlist *)stalloc(sizeof *sp);
sp->text = start;
*arglist->lastp = sp;
arglist->lastp = &sp->next;
if (strchr(ifs, *p)) {
if (!nulonly)
ifsspc = (strchr(" \t\n", *p) != NULL);
/* Ignore IFS whitespace at start */
if (q == start && ifsspc) {
p++;
start = p;
continue;
}
if (ifsspc) {
*q = '\0';
sp = (struct strlist *)stalloc(sizeof *sp);
sp->text = start;
*arglist->lastp = sp;
arglist->lastp = &sp->next;
p++;
if (!nulonly) {
for (;;) {
if (p >= string + ifsp->endoff)
if (p >= string + ifsp->endoff) {
break;
}
q = p;
if (*p == CTLESC)
p++;
if (strchr(ifs, *p++) == NULL) {
if (strchr(ifs, *p) == NULL ) {
p = q;
break;
}
} else if (strchr(" \t\n",*p) == NULL) {
if (ifsspc) {
p++;
ifsspc = 0;
} else {
p = q;
break;
}
} else
p++;
}
}
start = p;
}
} else
p++;
}
} while ((ifsp = ifsp->next) != NULL);
if (*start || (!ifsspc && start > string)) {
if (*start || (!ifsspc && start > string &&
(nulonly || 1))) {
sp = (struct strlist *)stalloc(sizeof *sp);
sp->text = start;
*arglist->lastp = sp;
@ -1037,6 +1125,8 @@ expmeta(enddir, name)
if (*q == '!' || *q == '^')
q++;
for (;;) {
while (*q == CTLQUOTEMARK)
q++;
if (*q == CTLESC)
q++;
if (*q == '/' || *q == '\0')
@ -1050,6 +1140,8 @@ expmeta(enddir, name)
metaflag = 1;
} else if (*p == '\0')
break;
else if (*p == CTLQUOTEMARK)
continue;
else if (*p == CTLESC)
p++;
if (*p == '/') {
@ -1062,6 +1154,8 @@ expmeta(enddir, name)
if (enddir != expdir)
metaflag++;
for (p = name ; ; p++) {
if (*p == CTLQUOTEMARK)
continue;
if (*p == CTLESC)
p++;
*enddir++ = *p;
@ -1076,6 +1170,8 @@ expmeta(enddir, name)
if (start != name) {
p = name;
while (p < start) {
while (*p == CTLQUOTEMARK)
p++;
if (*p == CTLESC)
p++;
*enddir++ = *p++;
@ -1100,7 +1196,12 @@ expmeta(enddir, name)
*endname++ = '\0';
}
matchdot = 0;
if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.'))
p = start;
while (*p == CTLQUOTEMARK)
p++;
if (*p == CTLESC)
p++;
if (*p == '.')
matchdot++;
while (! int_pending() && (dp = readdir(dirp)) != NULL) {
if (dp->d_name[0] == '.' && ! matchdot)
@ -1245,13 +1346,18 @@ pmatch(pattern, string)
if (*q++ != *p++)
return 0;
break;
case CTLQUOTEMARK:
continue;
case '?':
if (*q++ == '\0')
return 0;
break;
case '*':
c = *p;
if (c != CTLESC && c != '?' && c != '*' && c != '[') {
while (c == CTLQUOTEMARK || c == '*')
c = *++p;
if (c != CTLESC && c != CTLQUOTEMARK &&
c != '?' && c != '*' && c != '[') {
while (*q != c) {
if (*q == '\0')
return 0;
@ -1272,6 +1378,8 @@ pmatch(pattern, string)
if (*endp == '!' || *endp == '^')
endp++;
for (;;) {
while (*endp == CTLQUOTEMARK)
endp++;
if (*endp == '\0')
goto dft; /* no matching ] */
if (*endp == CTLESC)
@ -1290,10 +1398,14 @@ pmatch(pattern, string)
return 0;
c = *p++;
do {
if (c == CTLQUOTEMARK)
continue;
if (c == CTLESC)
c = *p++;
if (*p == '-' && p[1] != ']') {
p++;
while (*p == CTLQUOTEMARK)
p++;
if (*p == CTLESC)
p++;
if ( collate_range_cmp(chr, c) >= 0
@ -1331,16 +1443,20 @@ dft: default:
void
rmescapes(str)
char *str;
{
{
char *p, *q;
p = str;
while (*p != CTLESC) {
while (*p != CTLESC && *p != CTLQUOTEMARK) {
if (*p++ == '\0')
return;
}
q = p;
while (*p) {
if (*p == CTLQUOTEMARK) {
p++;
continue;
}
if (*p == CTLESC)
p++;
*q++ = *p++;
@ -1348,6 +1464,58 @@ rmescapes(str)
*q = '\0';
}
void rmquotes0(str)
char *str;
{
char *p, *q;
p = str;
while (*p != CTLQUOTEMARK) {
if (*p == CTLESC) {
p++;
p++;
continue;
}
if (*p++ == '\0')
return;
}
q = p;
while (*p) {
if (*p == CTLQUOTEMARK) {
p++;
continue;
}
if (*p == CTLESC)
*q++ = *p++;
*q++ = *p++;
}
*q = '\0';
}
int
rmquotes(str, len)
char *str;
int len;
{
char *p, *q, *pe;
p = str;
pe = str + len;
while (*p != CTLQUOTEMARK) {
if (++p == pe)
return len;
}
q = p;
while (p < pe) {
if (*p == CTLQUOTEMARK) {
p++;
continue;
}
*q++ = *p++;
}
return q - str;
}
/*

View File

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* @(#)expand.h 8.2 (Berkeley) 5/4/95
* $Id$
* $Id: expand.h,v 1.5 1997/02/22 13:58:26 peter Exp $
*/
struct strlist {
@ -64,4 +64,6 @@ void expandarg __P((union node *, struct arglist *, int));
void expari __P((int));
int patmatch __P((char *, char *));
void rmescapes __P((char *));
void rmquotes0 __P((char *));
int rmquotes __P((char *, int));
int casematch __P((union node *, char *));

View File

@ -39,7 +39,7 @@
static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95";
#endif
static const char rcsid[] =
"$Id$";
"$Id: memalloc.c,v 1.9 1998/05/18 06:43:57 charnier Exp $";
#endif /* not lint */
#include "shell.h"
@ -268,7 +268,7 @@ char *
growstackstr() {
int len = stackblocksize();
if (herefd >= 0 && len >= 1024) {
xwrite(herefd, stackblock(), len);
xwrite(herefd, stackblock(), rmquotes(stackblock(), len));
sstrnleft = len - 1;
return stackblock();
}

View File

@ -45,7 +45,7 @@ static char const copyright[] =
static char sccsid[] = "@(#)mksyntax.c 8.2 (Berkeley) 5/4/95";
#endif
static const char rcsid[] =
"$Id$";
"$Id: mksyntax.c,v 1.11 1998/05/18 06:44:03 charnier Exp $";
#endif /* not lint */
/*
@ -293,6 +293,7 @@ init()
syntax[base + CTLBACKQ + CTLQUOTE] = "CCTL";
syntax[base + CTLARI] = "CCTL";
syntax[base + CTLENDARI] = "CCTL";
syntax[base + CTLQUOTEMARK] = "CCTL";
}

View File

@ -39,7 +39,7 @@
static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
#endif
static const char rcsid[] =
"$Id$";
"$Id: parser.c,v 1.22 1998/05/18 06:44:12 charnier Exp $";
#endif /* not lint */
#include <stdlib.h>
@ -619,6 +619,7 @@ parsefname() {
if (quoteflag == 0)
n->type = NXHERE;
TRACE(("Here document %d\n", n->type));
rmquotes0(wordtext);
if (here->striptabs) {
while (*wordtext == '\t')
wordtext++;
@ -942,14 +943,18 @@ readtoken1(firstc, syntax, eofmark, striptabs)
USTPUTC('\\', out);
if (SQSYNTAX[c] == CCTL)
USTPUTC(CTLESC, out);
else
USTPUTC(CTLQUOTEMARK, out);
USTPUTC(c, out);
quotef++;
}
break;
case CSQUOTE:
USTPUTC(CTLQUOTEMARK, out);
syntax = SQSYNTAX;
break;
case CDQUOTE:
USTPUTC(CTLQUOTEMARK, out);
syntax = DQSYNTAX;
dblquote = 1;
break;
@ -1401,6 +1406,10 @@ parsearith: {
prevsyntax = syntax;
syntax = ARISYNTAX;
USTPUTC(CTLARI, out);
if (dblquote)
USTPUTC('"',out);
else
USTPUTC(' ',out);
} else {
/*
* we collapse embedded arithmetic expansion to

View File

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* @(#)parser.h 8.3 (Berkeley) 5/4/95
* $Id$
* $Id: parser.h,v 1.6 1997/02/22 13:58:42 peter Exp $
*/
/* control characters in argument strings */
@ -46,6 +46,7 @@
/* CTLBACKQ | CTLQUOTE == '\205' */
#define CTLARI '\206'
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
/* variable substitution byte (follows CTLVAR) */
#define VSTYPE 0x0f /* type of variable substitution */

View File

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* @(#)var.h 8.2 (Berkeley) 5/4/95
* $Id$
* $Id: var.h,v 1.6 1997/02/22 13:58:48 peter Exp $
*/
/*
@ -94,6 +94,7 @@ extern struct var vhistsize;
*/
#define ifsval() (vifs.text + 4)
#define ifsset() ((vifs.flags & VUNSET) == 0)
#define mailval() (vmail.text + 5)
#define mpathval() (vmpath.text + 9)
#define pathval() (vpath.text + 5)