summaryrefslogtreecommitdiffstats
path: root/bin/sh/expand.c
diff options
context:
space:
mode:
authortegge <tegge@FreeBSD.org>1998-09-06 21:13:09 +0000
committertegge <tegge@FreeBSD.org>1998-09-06 21:13:09 +0000
commitac59a0c5cd4a2de80842186c8dd22ff48fe29da8 (patch)
treedda84e744f853179b31c4cd9bfa1676a63fa9c1e /bin/sh/expand.c
parent59e6a57bc195e6145db20a1292466fa6df067e24 (diff)
downloadFreeBSD-src-ac59a0c5cd4a2de80842186c8dd22ff48fe29da8.zip
FreeBSD-src-ac59a0c5cd4a2de80842186c8dd22ff48fe29da8.tar.gz
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.
Diffstat (limited to 'bin/sh/expand.c')
-rw-r--r--bin/sh/expand.c252
1 files changed, 210 insertions, 42 deletions
diff --git a/bin/sh/expand.c b/bin/sh/expand.c
index 38a24b0..25cdc1e 100644
--- a/bin/sh/expand.c
+++ b/bin/sh/expand.c
@@ -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 @@ lose:
}
+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 @@ record:
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 @@ numvar:
}
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 @@ breakloop:
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;
+}
+
/*
OpenPOWER on IntegriCloud