summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorkre <kre@NetBSD.org>2019-04-10 08:13:11 +0000
committerkre <kre@NetBSD.org>2019-04-10 08:13:11 +0000
commita4dae6a6d999df4f7c653b6d06b089b2de624ff0 (patch)
tree83f9a2c96590eb777388aa8fc42da3eb603ce500 /bin
parent7c047061c7f31962ca5af051e9b38ca7a4c02a90 (diff)
PR bin/54112
Fix handling of "$@" (that is, double quoted dollar at), when it appears in a string which will be subject to field splitting. Eg: ${0+"$@" } More common usages, like the simple "$@" or ${0+"$@"} end up being entirely quoted, so no field splitting happens, and the problem was avoided. See the PR for more details. This ends up making a bunch of old hack code (and some that was relatively new) vanish - for now it is just #if 0'd or commented out. Cleanups of that stuff will happen later. That some of the worst $@ hacks are now gone does not mean that processing of "$@" does not retain a very special place in every hackers heart. RIP extreme ugliness - long live the merely ordinary ugly. Added a new bin/sh ATF test case to verify that all this remains fixed.
Diffstat (limited to 'bin')
-rw-r--r--bin/sh/expand.c43
1 files changed, 38 insertions, 5 deletions
diff --git a/bin/sh/expand.c b/bin/sh/expand.c
index cfcbb73733e..c9f91c6a655 100644
--- a/bin/sh/expand.c
+++ b/bin/sh/expand.c
@@ -1,4 +1,4 @@
-/* $NetBSD: expand.c,v 1.131 2019/02/27 04:10:56 kre Exp $ */
+/* $NetBSD: expand.c,v 1.132 2019/04/10 08:13:11 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.131 2019/02/27 04:10:56 kre Exp $");
+__RCSID("$NetBSD: expand.c,v 1.132 2019/04/10 08:13:11 kre Exp $");
#endif
#endif /* not lint */
@@ -97,6 +97,8 @@ struct ifsregion ifsfirst; /* first struct in list of ifs regions */
struct ifsregion *ifslastp; /* last struct in list */
struct arglist exparg; /* holds expanded arg list */
+static int empty_dollar_at; /* have expanded "$@" to nothing */
+
STATIC const char *argstr(const char *, int);
STATIC const char *exptilde(const char *, int);
STATIC void expbackq(union node *, int, int);
@@ -180,6 +182,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
if (fflag) /* no filename expandsion */
flag &= ~EXP_GLOB;
+ empty_dollar_at = 0;
argbackq = arg->narg.backquote;
STARTSTACKSTR(expdest);
ifsfirst.next = NULL;
@@ -243,6 +246,8 @@ argstr(const char *p, int flag)
char c;
const int quotes = flag & EXP_QNEEDED; /* do CTLESC */
int firsteq = 1;
+ int had_dol_at = 0;
+ int startoff;
const char *ifs = NULL;
int ifs_split = EXP_IFS_SPLIT;
@@ -251,6 +256,7 @@ argstr(const char *p, int flag)
CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes));
+ startoff = expdest - stackblock();
if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
p = exptilde(p, flag);
for (;;) {
@@ -262,6 +268,8 @@ argstr(const char *p, int flag)
return p - 1;
case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
case CTLENDARI: /* end of a $(( )) string */
+ if (had_dol_at && (*p&0xFF) == CTLQUOTEEND)
+ p++;
NULLTERM_4_TRACE(expdest);
VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..."
" after %2.2X; added \"%s\" to expdest\n",
@@ -270,8 +278,12 @@ argstr(const char *p, int flag)
case CTLQUOTEMARK:
/* "$@" syntax adherence hack */
if (p[0] == CTLVAR && p[1] & VSQUOTE &&
- p[2] == '@' && p[3] == '=')
+ p[2] == '@' && p[3] == '=') {
+ had_dol_at = 1;
break;
+ }
+ had_dol_at = 0;
+ empty_dollar_at = 0;
if ((flag & EXP_SPLIT) != 0)
STPUTC(c, expdest);
ifs_split = 0;
@@ -285,9 +297,14 @@ argstr(const char *p, int flag)
STPUTC('\n', expdest); /* no line_number++ */
break;
case CTLQUOTEEND:
- if ((flag & EXP_SPLIT) != 0)
+ if (empty_dollar_at &&
+ expdest - stackblock() > startoff &&
+ expdest[-1] == CTLQUOTEMARK)
+ expdest--;
+ else if (!had_dol_at && (flag & EXP_SPLIT) != 0)
STPUTC(c, expdest);
ifs_split = EXP_IFS_SPLIT;
+ had_dol_at = 0;
break;
case CTLESC:
if (quotes || ISCTL(*p))
@@ -890,6 +907,8 @@ evalvar(const char *p, int flag)
} else if (special) {
set = varisset(var, varflags & VSNUL);
val = NULL;
+ if (!set && *var == '@')
+ empty_dollar_at = 1;
} else {
val = lookupvar(var);
if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
@@ -916,9 +935,11 @@ evalvar(const char *p, int flag)
}
}
+#if 0 /* no longer need this $@ evil ... */
if (!set && subtype != VSPLUS && special && *var == '@')
if (startloc > 0 && expdest[-1] == CTLQUOTEMARK)
expdest--, startloc--;
+#endif
if (set && subtype != VSPLUS) {
/* insert the value of the variable */
@@ -1202,13 +1223,23 @@ varvalue(const char *name, int quoted, int subtype, int flag)
if (flag & EXP_SPLIT && quoted) {
VTRACE(DBG_EXPAND, (": $@ split (%d)\n",
shellparam.nparam));
+#if 0
/* GROSS HACK */
if (shellparam.nparam == 0 &&
expdest[-1] == CTLQUOTEMARK)
expdest--;
/* KCAH SSORG */
+#endif
+ if (shellparam.nparam == 0)
+ empty_dollar_at = 1;
+
for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
- STRTODEST(p);
+ if (*p == '\0') {
+ /* retain an explicit null string */
+ STPUTC(CTLQUOTEMARK, expdest);
+ STPUTC(CTLQUOTEEND, expdest);
+ } else
+ STRTODEST(p);
if (*ap)
/* A NUL separates args inside "" */
STPUTC('\0', expdest);
@@ -1396,8 +1427,10 @@ ifsbreakup(char *string, struct arglist *arglist)
}
}
+/*
while (*start == CTLQUOTEEND)
start++;
+*/
/*
* Save anything left as an argument.