both mksh(1) and POSIX say: "$@" should always generate multiple words

issue in pdksh reported in IRC by engla, thanks!
This commit is contained in:
tg 2013-02-23 20:03:31 +00:00
parent 74e2ef8b0b
commit 817aeb8fdb
5 changed files with 99 additions and 80 deletions

View File

@ -1,5 +1,5 @@
#!/bin/sh
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.622 2013/02/19 18:45:15 tg Exp $'
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.623 2013/02/23 20:03:25 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013
@ -1532,7 +1532,7 @@ else
#define EXTERN
#define MKSH_INCLUDES_ONLY
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.622 2013/02/19 18:45:15 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.623 2013/02/23 20:03:25 tg Exp $");
int main(void) { printf("Hello, World!\n"); return (0); }
EOF
case $cm in
@ -2120,7 +2120,7 @@ addsrcs USE_PRINTF_BUILTIN printf.c
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
test -n "$LDSTATIC" && add_cppflags -DMKSH_OPTSTATIC
add_cppflags -DMKSH_BUILD_R=431
add_cppflags -DMKSH_BUILD_R=440
$e $bi$me: Finished configuration testing, now producing output.$ao

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/Makefile,v 1.112 2013/02/18 22:55:37 tg Exp $
# $MirOS: src/bin/mksh/Makefile,v 1.113 2013/02/23 20:03:27 tg Exp $
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013
@ -55,7 +55,7 @@ CPPFLAGS+= -DMKSH_ASSUME_UTF8 -DMKSH_DISABLE_DEPRECATED \
-DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \
-DHAVE_SYS_ERRLIST_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \
-DHAVE_PERSISTENT_HISTORY=1 -DHAVE_SILENT_IDIVWRAPV=0 \
-DMKSH_BUILD_R=431
-DMKSH_BUILD_R=440
CPPFLAGS+= -D${${PROG:L}_tf:C/(Mir${MAN:E}{0,1}){2}/4/:S/x/mksh_BUILD/:U}
COPTS+= -std=c99 -Wall
.endif

12
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.597 2013/02/19 18:45:17 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.598 2013/02/23 20:03:27 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -29,7 +29,7 @@
# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
expected-stdout:
@(#)MIRBSD KSH R43 2013/02/19
@(#)MIRBSD KSH R44 2013/02/23
description:
Check version of shell.
stdin:
@ -38,7 +38,7 @@ name: KSH_VERSION
category: shell:legacy-no
---
expected-stdout:
@(#)LEGACY KSH R43 2013/02/19
@(#)LEGACY KSH R44 2013/02/23
description:
Check version of legacy shell.
stdin:
@ -3466,10 +3466,10 @@ stdin:
showargs 3 $@
showargs 4 "$@"
expected-stdout:
<1> <A B C>
<1> <A> <B> <C>
<2> <ABC>
<3> <A B C>
<4> <A B C>
<3> <A> <B> <C>
<4> <A> <B> <C>
---
name: IFS-space-colon-1
description:

151
eval.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.136 2013/02/10 23:43:59 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.137 2013/02/23 20:03:30 tg Exp $");
/*
* string expansion
@ -33,15 +33,21 @@ __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.136 2013/02/10 23:43:59 tg Exp $");
*/
/* expansion generator state */
typedef struct Expand {
/* int type; */ /* see expand() */
const char *str; /* string */
typedef struct {
/* not including an "int type;" member, see expand() */
/* string */
const char *str;
/* source */
union {
const char **strv; /* string[] */
struct shf *shf; /* file */
} u; /* source */
struct tbl *var; /* variable in ${var..} */
bool split; /* split "$@" / call waitlast $() */
/* string[] */
const char **strv;
/* file */
struct shf *shf;
} u;
/* variable in ${var...} */
struct tbl *var;
/* split "$@" / call waitlast in $() */
bool split;
} Expand;
#define XBASE 0 /* scanning original */
@ -198,33 +204,46 @@ typedef struct SubType {
} SubType;
void
expand(const char *cp, /* input word */
XPtrV *wp, /* output words */
int f) /* DO* flags */
expand(
/* input word */
const char *ccp,
/* output words */
XPtrV *wp,
/* DO* flags */
int f)
{
int c = 0;
int type; /* expansion type */
int quote = 0; /* quoted */
XString ds; /* destination string */
char *dp; /* destination */
const char *sp; /* source */
int fdo, word; /* second pass flags; have word */
int doblank; /* field splitting of parameter/command subst */
/* expansion type */
int type;
/* quoted */
int quote = 0;
/* destination string and live pointer */
XString ds;
char *dp;
/* source */
const char *sp;
/* second pass flags */
int fdo;
/* have word */
int word;
/* field splitting of parameter/command substitution */
int doblank;
/* expansion variables */
Expand x = {
/* expansion variables */
NULL, { NULL }, NULL, 0
};
SubType st_head, *st;
/* For trailing newlines in COMSUB */
/* record number of trailing newlines in COMSUB */
int newlines = 0;
bool saw_eq, make_magic;
int tilde_ok;
size_t len;
char *cp;
if (cp == NULL)
if (ccp == NULL)
internal_errorf("expand(NULL)");
/* for alias, readonly, set, typeset commands */
if ((f & DOVACHECK) && is_wdvarassign(cp)) {
if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
f |= DOASNTILDE;
}
@ -238,7 +257,7 @@ expand(const char *cp, /* input word */
/* init destination string */
Xinit(ds, dp, 128, ATEMP);
type = XBASE;
sp = cp;
sp = ccp;
fdo = 0;
saw_eq = false;
/* must be 1/0 */
@ -317,7 +336,6 @@ expand(const char *cp, /* input word */
*dp++ = ')'; *dp++ = ')';
} else {
struct tbl v;
char *p;
v.flag = DEFINED|ISSET|INTEGER;
/* not default */
@ -326,9 +344,10 @@ expand(const char *cp, /* input word */
v_evaluate(&v, substitute(sp, 0),
KSH_UNWIND_ERROR, true);
sp = strnul(sp) + 1;
for (p = str_val(&v); *p; ) {
cp = str_val(&v);
while (*cp) {
Xcheck(ds, dp);
*dp++ = *p++;
*dp++ = *cp++;
}
}
continue;
@ -394,8 +413,7 @@ expand(const char *cp, /* input word */
if (stype)
sp += slen;
switch (stype & 0x17F) {
case 0x100 | '#':
{
case 0x100 | '#': {
char *beg, *end;
mksh_ari_t seed;
register uint32_t h;
@ -416,8 +434,7 @@ expand(const char *cp, /* input word */
(unsigned int)h);
break;
}
case 0x100 | 'Q':
{
case 0x100 | 'Q': {
struct shf shf;
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
@ -839,7 +856,10 @@ expand(const char *cp, /* input word */
if (c == 0) {
if (quote && !x.split)
continue;
/* this is so we don't terminate */
c = ' ';
/* now force-emit a word */
goto emit_word;
}
if (quote && x.split) {
/* terminate word for "$@" */
@ -851,13 +871,13 @@ expand(const char *cp, /* input word */
case XCOM:
if (newlines) {
/* Spit out saved NLs */
/* spit out saved NLs */
c = '\n';
--newlines;
} else {
while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
if (c == '\n')
/* Save newlines */
/* save newlines */
newlines++;
if (newlines && c != EOF) {
shf_ungetc(c, x.u.shf);
@ -895,21 +915,21 @@ expand(const char *cp, /* input word */
*/
if (word == IFS_WORD ||
(!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
char *p;
emit_word:
*dp++ = '\0';
p = Xclose(ds, dp);
cp = Xclose(ds, dp);
if (fdo & DOBRACE)
/* also does globbing */
alt_expand(wp, p, p,
p + Xlength(ds, (dp - 1)),
alt_expand(wp, cp, cp,
cp + Xlength(ds, (dp - 1)),
fdo | (f & DOMARKDIRS));
else if (fdo & DOGLOB)
glob(p, wp, tobool(f & DOMARKDIRS));
glob(cp, wp, tobool(f & DOMARKDIRS));
else if ((f & DOPAT) || !(fdo & DOMAGIC))
XPput(*wp, p);
XPput(*wp, cp);
else
XPput(*wp, debunk(p, p, strlen(p) + 1));
XPput(*wp, debunk(cp, cp,
strlen(cp) + 1));
fdo = 0;
saw_eq = false;
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
@ -920,10 +940,8 @@ expand(const char *cp, /* input word */
return;
} else if (type == XSUB && ctype(c, C_IFS) &&
!ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
char *p;
*(p = alloc(1, ATEMP)) = '\0';
XPput(*wp, p);
*(cp = alloc(1, ATEMP)) = '\0';
XPput(*wp, cp);
type = XSUBMID;
}
if (word != IFS_NWS)
@ -932,10 +950,8 @@ expand(const char *cp, /* input word */
if (type == XSUB) {
if (word == IFS_NWS &&
Xlength(ds, dp) == 0) {
char *p;
*(p = alloc(1, ATEMP)) = '\0';
XPput(*wp, p);
*(cp = alloc(1, ATEMP)) = '\0';
XPput(*wp, cp);
}
type = XSUBMID;
}
@ -1002,18 +1018,17 @@ expand(const char *cp, /* input word */
if (type == XBASE &&
(f & (DOTILDE|DOASNTILDE)) &&
(tilde_ok & 2)) {
const char *p;
char *dp_x;
const char *tcp;
char *tdp = dp;
dp_x = dp;
p = maybe_expand_tilde(sp,
&ds, &dp_x,
tcp = maybe_expand_tilde(sp,
&ds, &tdp,
f & DOASNTILDE);
if (p) {
if (dp != dp_x)
if (tcp) {
if (dp != tdp)
word = IFS_WORD;
dp = dp_x;
sp = p;
dp = tdp;
sp = tcp;
continue;
}
}
@ -1176,8 +1191,10 @@ varsub(Expand *xp, const char *sp, const char *word,
c = sp[0];
if (c == '*' || c == '@') {
switch (stype & 0x17F) {
case '=': /* can't assign to a vector */
case '%': /* can't trim a vector (yet) */
/* can't assign to a vector */
case '=':
/* can't trim a vector (yet) */
case '%':
case '#':
case '0':
case '/':
@ -1204,8 +1221,10 @@ varsub(Expand *xp, const char *sp, const char *word,
XPtrV wv;
switch (stype & 0x17F) {
case '=': /* can't assign to a vector */
case '%': /* can't trim a vector (yet) */
/* can't assign to a vector */
case '=':
/* can't trim a vector (yet) */
case '%':
case '#':
case '?':
case '0':
@ -1495,11 +1514,11 @@ globit(XString *xs, /* dest string */
*/
if ((check & GF_EXCHECK) ||
((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
#define stat_check() (stat_done ? stat_done : \
(stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
? -1 : 1))
#define stat_check() (stat_done ? stat_done : (stat_done = \
stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1))
struct stat lstatb, statb;
int stat_done = 0; /* -1: failed, 1 ok */
/* -1: failed, 1 ok, 0 not yet done */
int stat_done = 0;
if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
return;

6
sh.h
View File

@ -164,9 +164,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.639 2013/02/19 18:45:22 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.640 2013/02/23 20:03:31 tg Exp $");
#endif
#define MKSH_VERSION "R43 2013/02/19"
#define MKSH_VERSION "R44 2013/02/23"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@ -511,7 +511,7 @@ char *ucstrstr(char *, const char *);
#define mkssert(e) do { } while (/* CONSTCOND */ 0)
#endif
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 431)
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 440)
#error Must run Build.sh to compile this.
int
im_sorry_dave(void)