Index: check.t =================================================================== RCS file: /cvs/src/bin/mksh/check.t,v retrieving revision 1.661 retrieving revision 1.667 diff -u -p -r1.661 -r1.667 --- check.t 7 Oct 2014 15:22:14 -0000 1.661 +++ check.t 19 Oct 2014 22:26:13 -0000 1.667 @@ -1,4 +1,4 @@ -# $MirOS: src/bin/mksh/check.t,v 1.661 2014/10/07 15:22:14 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.667 2014/10/19 22:26:13 tg Exp $ # OpenBSD src/regress/bin/ksh updated: 2013/12/02 20:39:44 #- # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -24,10 +24,10 @@ # http://www.research.att.com/~gsf/public/ifs.sh # # More testsuites at: -# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD +# http://svnweb.freebsd.org/base/head/bin/test/tests/legacy_test.sh?view=co&content-type=text%2Fplain expected-stdout: - @(#)MIRBSD KSH R50 2014/10/07 + @(#)MIRBSD KSH R50 2014/10/19 description: Check version of shell. stdin: @@ -36,7 +36,7 @@ name: KSH_VERSION category: shell:legacy-no --- expected-stdout: - @(#)LEGACY KSH R50 2014/10/07 + @(#)LEGACY KSH R50 2014/10/19 description: Check version of legacy shell. stdin: @@ -2389,6 +2389,14 @@ stdin: vf=<<<$'=f $x \x40=' # now check print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |" + # check append + v=<<-EOF + vapp1 + EOF + v+=<<-EOF + vapp2 + EOF + print -r -- "| ${v//$'\n'/^} |" expected-stdout: function foo { vc=<<-EOF @@ -2404,6 +2412,7 @@ expected-stdout: } ve={=e $x \x40= } vf={=f $x @= } | + | vapp1^vapp2^ | --- name: heredoc-11 description: @@ -2433,6 +2442,14 @@ stdin: eval "$fnd" foo print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |" + # check append + v=<<- + vapp1 + << + v+=<<-'' + vapp2 + + print -r -- "| ${v//$'\n'/^} |" expected-stdout: function foo { vc=<<- @@ -2450,6 +2467,7 @@ expected-stdout: } vc={=c u \x40= } vd={=d $x \x40= } | + | vapp1^vapp2^ | --- name: heredoc-comsub-1 description: @@ -3733,7 +3751,7 @@ stdin: expected-stdout: <1> <1> <2> --- -name: IFS-subst-3 +name: IFS-subst-3-arr description: Check leading IFS non-whitespace after trim does make a field but leading IFS whitespace does not, nor empty replacements @@ -3749,6 +3767,32 @@ expected-stdout: <1> <> <2> --- +name: IFS-subst-3-ass +description: + Check non-field semantics +stdin: + showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs 0 x=${-+} + IFS=: + showargs 1 x=${-+:foo:bar} + IFS=' ' + showargs 2 x=${-+ foo bar} +expected-stdout: + <0> + <1> + <2> +--- +name: IFS-subst-3-lcl +description: + Check non-field semantics, smaller corner case (LP#1381965) +stdin: + set -x + local regex=${2:-} + exit 1 +expected-exit: e != 0 +expected-stderr-pattern: + /regex=/ +--- name: IFS-subst-4-1 description: reported by mikeserv @@ -4133,6 +4177,30 @@ expected-stdout: =b8iqa --- +name: IFS-subst-6 +description: + Regression wrt. vector expansion in trim +stdin: + showargs() { for x in "$@"; do echo -n "<$x> "; done; echo .; } + IFS= + x=abc + set -- a b + showargs ${x#$*} +expected-stdout: + . +--- +name: IFS-subst-7 +description: + ksh93 bug wrt. vector expansion in trim +stdin: + showargs() { for x in "$@"; do echo -n "<$x> "; done; echo .; } + IFS="*" + a=abcd + set -- '' c + showargs "$*" ${a##"$*"} +expected-stdout: + <*c> . +--- name: IFS-arith-1 description: http://austingroupbugs.net/view.php?id=832 @@ -8013,6 +8081,66 @@ expected-stdout: .fnr:f: .f2r:f: --- +name: unset-fnc-local-ksh +description: + Check that “unset” removes a previous “local” + (ksh93 syntax compatible version); apparently, + there are shells which fail this? +stdin: + function f { + echo f0: $x + typeset x + echo f1: $x + x=fa + echo f2: $x + unset x + echo f3: $x + x=fb + echo f4: $x + } + x=o + echo before: $x + f + echo after: $x +expected-stdout: + before: o + f0: o + f1: + f2: fa + f3: o + f4: fb + after: fb +--- +name: unset-fnc-local-sh +description: + Check that “unset” removes a previous “local” + (Debian Policy §10.4 sh version); apparently, + there are shells which fail this? +stdin: + f() { + echo f0: $x + local x + echo f1: $x + x=fa + echo f2: $x + unset x + echo f3: $x + x=fb + echo f4: $x + } + x=o + echo before: $x + f + echo after: $x +expected-stdout: + before: o + f0: o + f1: + f2: fa + f3: o + f4: fb + after: fb +--- name: varexpand-substr-1 description: Check if bash-style substring expansion works Index: eval.c =================================================================== RCS file: /cvs/src/bin/mksh/eval.c,v retrieving revision 1.153 retrieving revision 1.158 diff -u -p -r1.153 -r1.158 --- eval.c 7 Oct 2014 15:22:16 -0000 1.153 +++ eval.c 19 Oct 2014 21:53:07 -0000 1.158 @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.153 2014/10/07 15:22:16 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.158 2014/10/19 21:53:07 tg Exp $"); /* * string expansion @@ -246,7 +246,7 @@ expand( /* for alias, readonly, set, typeset commands */ if ((f & DOVACHECK) && is_wdvarassign(ccp)) { f &= ~(DOVACHECK | DOBLANK | DOGLOB | DOTILDE); - f |= DOASNTILDE | DOASNFIELD; + f |= DOASNTILDE | DOSCALAR; } if (Flag(FNOGLOB)) f &= ~DOGLOB; @@ -622,7 +622,7 @@ expand( case '%': /* ! DOBLANK,DOBRACE,DOTILDE */ f = (f & DONTRUNCOMMAND) | - DOPAT | DOTEMP; + DOPAT | DOTEMP | DOSCALAR; st->quotew = quote = 0; /* * Prepend open pattern (so | @@ -631,7 +631,7 @@ expand( */ if (!Flag(FSH)) { *dp++ = MAGIC; - *dp++ = '@' | 0x80; + *dp++ = 0x80 | '@'; } break; case '=': @@ -664,7 +664,11 @@ expand( f |= DOTEMP; /* FALLTHROUGH */ default: - word = quote ? IFS_WORD : IFS_IWS; + /* '-' '+' '?' */ + if (quote) + word = IFS_WORD; + else if (dp == Xstring(ds, dp)) + word = IFS_IWS; /* Enable tilde expansion */ tilde_ok = 1; f |= DOTILDE; @@ -704,7 +708,7 @@ expand( x.str = trimsub(str_val(st->var), dp, st->stype); if (x.str[0] != '\0') { - word = IFS_WS; + word = IFS_IWS; type = XSUB; } else type = quote ? XSUB : XNULLSUB; @@ -743,7 +747,7 @@ expand( if (f & DOBLANK) doblank++; st = st->prev; - word = quote || (!*x.str && (f & DOASNFIELD)) ? IFS_WORD : IFS_WS; + word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS; continue; case '?': { char *s = Xrestpos(ds, dp, st->base); @@ -759,11 +763,12 @@ expand( case 0x100 | 'Q': dp = Xrestpos(ds, dp, st->base); type = XSUB; - word = quote || (!*x.str && (f & DOASNFIELD)) ? IFS_WORD : IFS_WS; + word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS; if (f & DOBLANK) doblank++; st = st->prev; continue; + /* default: '-' '+' */ } st = st->prev; type = XBASE; @@ -800,7 +805,7 @@ expand( if (f & DOBLANK) { doblank--; if (dp == Xstring(ds, dp)) - word = IFS_WS; + word = IFS_IWS; } continue; @@ -834,8 +839,8 @@ expand( continue; } c = ifs0; - if ((f & DOASNFIELD)) { - /* assignment, do not field-split */ + if ((f & DOSCALAR)) { + /* do not field-split */ if (x.split) { c = ' '; break; Index: exec.c =================================================================== RCS file: /cvs/src/bin/mksh/exec.c,v retrieving revision 1.133 retrieving revision 1.137 diff -u -p -r1.133 -r1.137 --- exec.c 3 Oct 2014 17:32:11 -0000 1.133 +++ exec.c 19 Oct 2014 21:53:07 -0000 1.137 @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.133 2014/10/03 17:32:11 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.137 2014/10/19 21:53:07 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL "/bin/sh" @@ -32,7 +32,7 @@ __RCSID("$MirOS: src/bin/mksh/exec.c,v 1 static int comexec(struct op *, struct tbl * volatile, const char **, int volatile, volatile int *); static void scriptexec(struct op *, const char **) MKSH_A_NORETURN; -static int call_builtin(struct tbl *, const char **, const char *); +static int call_builtin(struct tbl *, const char **, const char *, bool); static int iosetup(struct ioword *, struct tbl *); static int herein(struct ioword *, char **); static const char *do_selectargs(const char **, bool); @@ -81,6 +81,8 @@ execute(struct op * volatile t, /* we want to run an executable, do some variance checks */ if (t->type == TCOM) { /* check if this is 'var=<args[0] == NULL && @@ -94,20 +96,23 @@ execute(struct op * volatile t, /* the variable assignment begins with a valid varname */ (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && /* and has no right-hand side (i.e. "varname=") */ - ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS && + ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) || + /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR && + ccp[3] == '=' && ccp[4] == EOS)) && /* plus we can have a here document content */ herein(t->ioact[0], &cp) == 0 && cp && *cp) { char *sp = cp, *dp; - size_t n = ccp - t->vars[0] + 2, z; + size_t n = ccp - t->vars[0] + (ccp[1] == '+' ? 4 : 2); + size_t z; /* drop redirection (will be garbage collected) */ t->ioact = NULL; /* set variable to its expanded value */ - z = strlen(cp) + 1; - if (notoktomul(z, 2) || notoktoadd(z * 2, n)) + z = strlen(cp); + if (notoktomul(z, 2) || notoktoadd(z * 2, n + 1)) internal_errorf(Toomem, (size_t)-1); - dp = alloc(z * 2 + n, ATEMP); + dp = alloc(z * 2 + n + 1, APERM); memcpy(dp, t->vars[0], n); t->vars[0] = dp; dp += n; @@ -500,7 +505,7 @@ comexec(struct op *t, struct tbl * volat /* Must be static (XXX but why?) */ static struct op texec; int type_flags; - bool keepasn_ok; + bool resetspec; int fcflags = FC_BI|FC_FUNC|FC_PATH; bool bourne_function_call = false; struct block *l_expand, *l_assign; @@ -532,7 +537,7 @@ comexec(struct op *t, struct tbl * volat * FOO=bar command FOO is neither kept nor exported * PATH=... foobar use new PATH in foobar search */ - keepasn_ok = true; + resetspec = false; while (tp && tp->type == CSHELL) { /* undo effects of command */ fcflags = FC_BI|FC_FUNC|FC_PATH; @@ -579,7 +584,7 @@ comexec(struct op *t, struct tbl * volat * POSIX says special builtins lose their status * if accessed using command. */ - keepasn_ok = false; + resetspec = true; if (!ap[0]) { /* ensure command with no args exits with 0 */ subst_exstat = 0; @@ -614,13 +619,13 @@ comexec(struct op *t, struct tbl * volat if (t->u.evalflags & DOTCOMEXEC) flags |= XEXEC; l_expand = e->loc; - if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) + if (!resetspec && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); /* ksh functions don't keep assignments, POSIX functions do. */ - if (keepasn_ok && tp && tp->type == CFUNC && + if (!resetspec && tp && tp->type == CFUNC && !(tp->flag & FKSH)) { bourne_function_call = true; type_flags = EXPORT; @@ -635,7 +640,7 @@ comexec(struct op *t, struct tbl * volat for (i = 0; t->vars[i]; i++) { /* do NOT lookup in the new var/fn block just created */ e->loc = l_expand; - cp = evalstr(t->vars[i], DOASNTILDE | DOASNFIELD); + cp = evalstr(t->vars[i], DOASNTILDE | DOSCALAR); e->loc = l_assign; if (Flag(FXTRACE)) { const char *ccp; @@ -684,8 +689,8 @@ comexec(struct op *t, struct tbl * volat /* shell built-in */ case CSHELL: - rv = call_builtin(tp, (const char **)ap, null); - if (!keepasn_ok && tp->val.f == c_shift) { + rv = call_builtin(tp, (const char **)ap, null, resetspec); + if (resetspec && tp->val.f == c_shift) { l_expand->argc = l_assign->argc; l_expand->argv = l_assign->argv; } @@ -952,7 +957,7 @@ shcomexec(const char **wp) struct tbl *tp; tp = ktsearch(&builtins, *wp, hash(*wp)); - return (call_builtin(tp, wp, "shcomexec")); + return (call_builtin(tp, wp, "shcomexec", false)); } /* @@ -1265,22 +1270,22 @@ search_path(const char *name, const char } static int -call_builtin(struct tbl *tp, const char **wp, const char *where) +call_builtin(struct tbl *tp, const char **wp, const char *where, bool resetspec) { int rv; if (!tp) internal_errorf("%s: %s", where, wp[0]); builtin_argv0 = wp[0]; - builtin_flag = tp->flag; + builtin_spec = tobool(!resetspec && (tp->flag & SPEC_BI)); shf_reopen(1, SHF_WR, shl_stdout); shl_stdout_ok = true; ksh_getopt_reset(&builtin_opt, GF_ERROR); rv = (*tp->val.f)(wp); shf_flush(shl_stdout); shl_stdout_ok = false; - builtin_flag = 0; builtin_argv0 = NULL; + builtin_spec = false; return (rv); } @@ -1559,7 +1564,8 @@ do_selectargs(const char **ap, bool prin if (print_menu || !*str_val(global("REPLY"))) pr_menu(ap); shellf("%s", str_val(global("PS3"))); - if (call_builtin(findcom("read", FC_BI), read_args, Tselect)) + if (call_builtin(findcom("read", FC_BI), read_args, Tselect, + false)) return (NULL); s = str_val(global("REPLY")); if (*s && getn(s, &i)) Index: funcs.c =================================================================== RCS file: /cvs/src/bin/mksh/funcs.c,v retrieving revision 1.258 retrieving revision 1.259 diff -u -p -r1.258 -r1.259 --- funcs.c 3 Sep 2014 19:55:51 -0000 1.258 +++ funcs.c 12 Oct 2014 21:58:51 -0000 1.259 @@ -38,7 +38,7 @@ #endif #endif -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.258 2014/09/03 19:55:51 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.259 2014/10/12 21:58:51 tg Exp $"); #if HAVE_KILLPG /* @@ -1587,12 +1587,16 @@ c_shift(const char **wp) return (1); arg = wp[builtin_opt.optind]; - if (arg) { - evaluate(arg, &val, KSH_UNWIND_ERROR, false); - n = val; - } else + if (!arg) n = 1; - if (n < 0) { + else if (!evaluate(arg, &val, KSH_RETURN_ERROR, false)) { + /* error already printed */ + bi_errorfz(); + return (1); + } else if (!(n = val)) { + /* nothing to do */ + return (0); + } else if (n < 0) { bi_errorf("%s: %s", arg, "bad number"); return (1); } @@ -2417,9 +2421,8 @@ c_set(const char **wp) return (c_typeset(args)); } - argi = parse_args(wp, OF_SET, &setargs); - if (argi < 0) - return (1); + if ((argi = parse_args(wp, OF_SET, &setargs)) < 0) + return (2); /* set $# and $* */ if (setargs) { wp += argi - 1; Index: main.c =================================================================== RCS file: /cvs/src/bin/mksh/main.c,v retrieving revision 1.284 retrieving revision 1.285 diff -u -p -r1.284 -r1.285 --- main.c 3 Oct 2014 17:19:27 -0000 1.284 +++ main.c 12 Oct 2014 21:58:52 -0000 1.285 @@ -34,7 +34,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/main.c,v 1.284 2014/10/03 17:19:27 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/main.c,v 1.285 2014/10/12 21:58:52 tg Exp $"); extern char **environ; @@ -1244,7 +1244,7 @@ bi_errorf(const char *fmt, ...) * non-interactive shells to exit. * XXX odd use of KEEPASN; also may not want LERROR here */ - if (builtin_flag & SPEC_BI) { + if (builtin_spec) { builtin_argv0 = NULL; unwind(LERROR); } Index: sh.h =================================================================== RCS file: /cvs/src/bin/mksh/sh.h,v retrieving revision 1.697 retrieving revision 1.701 diff -u -p -r1.697 -r1.701 --- sh.h 7 Oct 2014 15:22:17 -0000 1.697 +++ sh.h 19 Oct 2014 21:53:08 -0000 1.701 @@ -169,9 +169,9 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.697 2014/10/07 15:22:17 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.701 2014/10/19 21:53:08 tg Exp $"); #endif -#define MKSH_VERSION "R50 2014/10/07" +#define MKSH_VERSION "R50 2014/10/19" /* arithmetic types: C implementation */ #if !HAVE_CAN_INTTYPES @@ -992,8 +992,8 @@ EXTERN sigset_t sm_default, sm_sigchld; /* name of called builtin function (used by error functions) */ EXTERN const char *builtin_argv0; -/* flags of called builtin (SPEC_BI, etc.) */ -EXTERN uint32_t builtin_flag; +/* is called builtin SPEC_BI? */ +EXTERN bool builtin_spec; /* current working directory */ EXTERN char *current_wd; @@ -1396,7 +1396,7 @@ struct ioword { #define DOVACHECK BIT(9) /* var assign check (for typeset, set, etc) */ #define DOMARKDIRS BIT(10) /* force markdirs behaviour */ #define DOTCOMEXEC BIT(11) /* not an eval flag, used by sh -c hack */ -#define DOASNFIELD BIT(12) /* is assignment, change field handling */ +#define DOSCALAR BIT(12) /* change field handling to non-list context */ #define X_EXTRA 20 /* this many extra bytes in X string */