diff --git a/check.t b/check.t index d5abd2c..aa1d9c2 100644 --- a/check.t +++ b/check.t @@ -1,4 +1,4 @@ -# $MirOS: src/bin/mksh/check.t,v 1.552 2012/07/30 19:58:03 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.553 2012/07/30 21:37:08 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 R40 2012/07/21 + @(#)MIRBSD KSH R40 2012/07/30 description: Check version of shell. stdin: @@ -38,7 +38,7 @@ name: KSH_VERSION category: shell:legacy-no --- expected-stdout: - @(#)LEGACY KSH R40 2012/07/21 + @(#)LEGACY KSH R40 2012/07/30 description: Check version of legacy shell. stdin: @@ -6194,21 +6194,6 @@ expected-stdout: expected-stderr-pattern: /(Unrecognized character .... ignored at \..t4 line 1)*/ --- -name: utf8bom-3 -description: - Reading the UTF-8 BOM should enable the utf8-mode flag - (temporarily for COMSUBs) -stdin: - "$__progname" -c ':; if [[ $- = *U* ]]; then echo 1 on; else echo 1 off; fi' - "$__progname" -c ':; if [[ $- = *U* ]]; then echo 2 on; else echo 2 off; fi' - "$__progname" -c 'if [[ $- = *U* ]]; then echo 3 on; else echo 3 off; fi; x=$(:; if [[ $- = *U* ]]; then echo 4 on; else echo 4 off; fi); echo $x; if [[ $- = *U* ]]; then echo 5 on; else echo 5 off; fi' -expected-stdout: - 1 off - 2 on - 3 off - 4 on - 5 off ---- name: utf8opt-1a description: Check that the utf8-mode flag is not set at non-interactive startup @@ -9520,6 +9505,26 @@ expected-stdout: x=$(( echo $(true >&3 ) $((1+ 2)) ) | tr u x ) } --- +name: funsub-1 +description: + Check that non-subenvironment command substitution works +stdin: + set -e + foo=bar + echo "ob $foo ." + echo "${ + echo "ib $foo :" + foo=baz + echo "ia $foo :" + false + }" . + echo "oa $foo ." +expected-stdout: + ob bar . + ib bar : + ia baz : . + oa baz . +--- name: test-stnze-1 description: Check that the short form [ $x ] works @@ -9613,7 +9618,6 @@ expected-stdout: name: event-subst-3 description: Check that '!' substitution in noninteractive mode is ignored -category: !smksh file-setup: file 755 "falsetto" #! /bin/sh echo molto bene diff --git a/dot.mkshrc b/dot.mkshrc index b96e2d2..d850e99 100644 --- a/dot.mkshrc +++ b/dot.mkshrc @@ -1,7 +1,8 @@ # $Id$ -# $MirOS: src/bin/mksh/dot.mkshrc,v 1.69 2011/12/31 01:07:19 tg Exp $ +# $MirOS: src/bin/mksh/dot.mkshrc,v 1.70 2012/07/30 21:37:10 tg Exp $ #- -# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 +# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, +# 2011, 2012 # Thorsten Glaser # # Provided that these terms and disclaimer and all copyright notices @@ -29,7 +30,7 @@ function precmd { (( e )) && print -n "$e|" } -PS1=$'\001\r''$(precmd)${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \? +PS1=$'\001\r''${ precmd;}${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \? )}@${HOSTNAME%%.*}:$(local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || \ d=${d/#$p/~}; local m=${%d} n p=...; (( m > 0 )) || m=${#d} (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || \ diff --git a/eval.c b/eval.c index 9341973..34ace2f 100644 --- a/eval.c +++ b/eval.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.122 2012/07/30 17:28:21 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.123 2012/07/30 21:37:11 tg Exp $"); /* * string expansion @@ -58,7 +58,7 @@ typedef struct Expand { #define IFS_NWS 2 /* have seen IFS non-white-space */ static int varsub(Expand *, const char *, const char *, int *, int *); -static int comsub(Expand *, const char *); +static int comsub(Expand *, const char *, int); static char *trimsub(char *, char *, int); static void glob(char *, XPtrV *, int); static void globit(XString *, char **, char *, XPtrV *, int); @@ -278,17 +278,27 @@ expand(const char *cp, /* input word */ quote = st->quotew; continue; case COMSUB: + case FUNSUB: tilde_ok = 0; if (f & DONTRUNCOMMAND) { word = IFS_WORD; - *dp++ = '$'; *dp++ = '('; + *dp++ = '$'; + if (c == FUNSUB) { + *dp++ = '{'; + *dp++ = ' '; + } else + *dp++ = '('; while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } - *dp++ = ')'; + if (c == FUNSUB) { + *dp++ = ';'; + *dp++ = '}'; + } else + *dp++ = ')'; } else { - type = comsub(&x, sp); + type = comsub(&x, sp, c); if (type == XCOM && (f&DOBLANK)) doblank++; sp = strnul(sp) + 1; @@ -611,8 +621,8 @@ expand(const char *cp, /* input word */ case '#': case '%': /* ! DOBLANK,DOBRACE,DOTILDE */ - f = DOPAT | (f&DONTRUNCOMMAND) | - DOTEMP; + f = (f & DONTRUNCOMMAND) | + DOPAT | DOTEMP; st->quotew = quote = 0; /* * Prepend open pattern (so | @@ -1272,7 +1282,7 @@ varsub(Expand *xp, const char *sp, const char *word, * Run the command in $(...) and read its output. */ static int -comsub(Expand *xp, const char *cp) +comsub(Expand *xp, const char *cp, int fn) { Source *s, *sold; struct op *t; @@ -1286,10 +1296,15 @@ comsub(Expand *xp, const char *cp) afree(s, ATEMP); source = sold; + UTFMODE = old_utfmode; + if (t == NULL) return (XBASE); - if (t != NULL && t->type == TCOM && + /* no waitlast() unless specifically enabled later */ + xp->split = false; + + if (t->type == TCOM && *t->args == NULL && *t->vars == NULL && t->ioact != NULL) { /* $(ioact; @@ -1302,10 +1317,9 @@ comsub(Expand *xp, const char *cp) SHF_MAPHI|SHF_CLEXEC); if (shf == NULL) errorf("%s: %s %s", name, "can't open", "$() input"); - /* no waitlast() */ - xp->split = false; } else { int ofd1, pv[2]; + openpipe(pv); shf = shf_fdopen(pv[0], SHF_RD, NULL); ofd1 = savefd(1); @@ -1313,14 +1327,16 @@ comsub(Expand *xp, const char *cp) ksh_dup2(pv[1], 1, false); close(pv[1]); } - execute(t, XFORK|XXCOM|XPIPEO, NULL); + execute(t, XXCOM | XPIPEO | + (fn == FUNSUB ? XERROK : XFORK), NULL); restfd(1, ofd1); - startlast(); - /* waitlast() */ - xp->split = true; + if (fn != FUNSUB) { + startlast(); + /* waitlast() */ + xp->split = true; + } } - UTFMODE = old_utfmode; xp->u.shf = shf; return (XCOM); } @@ -1328,7 +1344,6 @@ comsub(Expand *xp, const char *cp) /* * perform #pattern and %pattern substitution in ${} */ - static char * trimsub(char *str, char *pat, int how) { diff --git a/lex.c b/lex.c index 8682f1a..b3802a4 100644 --- a/lex.c +++ b/lex.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.165 2012/07/01 15:54:55 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.166 2012/07/30 21:37:12 tg Exp $"); /* * states while lexing word @@ -109,7 +109,7 @@ void yyskiputf8bom(void); static int backslash_skip; static int ignore_backslash_newline; static struct sretrace_info *retrace_info; -short subshell_nesting_level = 0; +int subshell_nesting_type = 0; /* optimised getsc_bn() */ #define o_getsc() (*source->str != '\0' && *source->str != '\\' && \ @@ -262,6 +262,11 @@ yylex(int cf) while (!((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM || state == SHERESTRING) && ctype(c, C_LEX1)))) { + if (state == SBASE && + subshell_nesting_type == /*{*/ '}' && + c == /*{*/ '}') + /* possibly end ${ :;} */ + break; accept_nonword: Xcheck(ws, wp); switch (state) { @@ -395,14 +400,27 @@ yylex(int cf) } else { ungetsc(c); subst_command: - sp = yyrecursive(); + c = COMSUB; + subst_command2: + sp = yyrecursive(c); cz = strlen(sp) + 1; XcheckN(ws, wp, cz); - *wp++ = COMSUB; + *wp++ = c; memcpy(wp, sp, cz); wp += cz; } } else if (c == '{') /*}*/ { + c = getsc(); + if (ctype(c, C_IFSWS)) { + /* + * non-subenvironment + * "command" substitution + */ + c = FUNSUB; + goto subst_command2; + } + ungetsc(c); + *wp++ = OSUBST; *wp++ = '{'; /*}*/ wp = get_brace_var(&ws, wp); @@ -1171,7 +1189,7 @@ readhere(struct ioword *iop) /* end of here document marker, what to do? */ switch (c) { case /*(*/ ')': - if (!subshell_nesting_level) + if (!subshell_nesting_type) /*- * not allowed outside $(...) or (...) * => mismatch diff --git a/misc.c b/misc.c index 22364ee..c2a9c87 100644 --- a/misc.c +++ b/misc.c @@ -30,7 +30,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.196 2012/07/20 21:18:45 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.197 2012/07/30 21:37:13 tg Exp $"); #define KSH_CHVT_FLAG #ifdef MKSH_SMALL @@ -101,9 +101,9 @@ initctypes(void) chtypes['_'] |= C_ALPHA; setctypes("0123456789", C_DIGIT); /* \0 added automatically */ - setctypes(" \t\n|&;<>()", C_LEX1); + setctypes(TC_LEX1, C_LEX1); setctypes("*@#!$-?", C_VAR1); - setctypes(" \t\n", C_IFSWS); + setctypes(TC_IFSWS, C_IFSWS); setctypes("=-+?", C_SUBOP1); setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE); } diff --git a/mksh.1 b/mksh.1 index 00fa50a..43811c0 100644 --- a/mksh.1 +++ b/mksh.1 @@ -1,4 +1,4 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.290 2012/07/25 19:40:40 tg Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.291 2012/07/30 21:37:13 tg Exp $ .\" $OpenBSD: ksh.1,v 1.144 2012/07/08 08:13:20 guenther Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, @@ -74,7 +74,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: July 25 2012 $ +.Dd $Mdocdate: July 30 2012 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -1145,9 +1145,15 @@ or command substitutions take the form .Pf $( Ns Ar command Ns \&) or (deprecated) -.Pf \` Ns Ar command Ns \` ; +.Pf \` Ns Ar command Ns \` +or (executed in the current environment) +.Pf ${\ \& Ar command Ns \&;} +and strip trailing newlines; and arithmetic substitutions take the form .Pf $(( Ns Ar expression Ns )) . +Parsing the current-environment command substitution requires a space, +tab or newline after the opening brace and that the closing brace be +recognised as a keyword (i.e. is preceded by a newline or semicolon). .Pp If a substitution appears outside of double quotes, the results of the substitution are generally subject to word or field splitting according to @@ -1225,6 +1231,8 @@ A command substitution is replaced by the output generated by the specified command which is run in a subshell. For .Pf $( Ns Ar command Ns \&) +and +.Pf ${\ \& Ar command Ns \&;} substitutions, normal quoting rules are used when .Ar command is parsed; however, for the deprecated @@ -1963,7 +1971,7 @@ x=$(print \e\e001) PS1="$x$(print \e\er)$x$(tput smso)$x\e$PWD$x$(tput rmso)$x\*(Gt " .Ed .Pp -Due to pressure from David G. Korn, +Due to a strong suggestion from David G. Korn, .Nm now also supports the following form: .Bd -literal -offset indent diff --git a/sh.h b/sh.h index 3e8ab21..52f32cb 100644 --- a/sh.h +++ b/sh.h @@ -157,9 +157,9 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.575 2012/07/30 17:04:30 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.576 2012/07/30 21:37:15 tg Exp $"); #endif -#define MKSH_VERSION "R40 2012/07/21" +#define MKSH_VERSION "R40 2012/07/30" /* arithmetic types: C implementation */ #if !HAVE_CAN_INTTYPES @@ -765,6 +765,8 @@ EXTERN const char Tgbuiltin[] E_INIT("=builtin"); #define Tbuiltin (Tgbuiltin + 1) /* "builtin" */ EXTERN const char T_function[] E_INIT(" function"); #define Tfunction (T_function + 1) /* "function" */ +EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n"); +#define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */ typedef uint8_t Temp_type; /* expanded heredoc */ @@ -1281,6 +1283,7 @@ struct op { #define SPAT 10 /* separate pattern: | */ #define CPAT 11 /* close pattern: ) */ #define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */ +#define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */ /* * IO redirection @@ -1903,7 +1906,7 @@ ssize_t shf_vfprintf(struct shf *, const char *, va_list) void initkeywords(void); struct op *compile(Source *, bool); bool parse_usec(const char *, struct timeval *); -char *yyrecursive(void); +char *yyrecursive(int); /* tree.c */ void fptreef(struct shf *, int, const char *, ...); char *snptreef(char *, ssize_t, const char *, ...); diff --git a/syn.c b/syn.c index 12a9867..2a377d0 100644 --- a/syn.c +++ b/syn.c @@ -23,9 +23,9 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.78 2012/07/30 17:04:31 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.79 2012/07/30 21:37:16 tg Exp $"); -extern short subshell_nesting_level; +extern int subshell_nesting_type; extern void yyskiputf8bom(void); struct nesting_state { @@ -363,12 +363,15 @@ get_command(int cf) Leave: break; - case '(': + case '(': /*)*/ { + int subshell_nesting_type_saved; Subshell: - ++subshell_nesting_level; + subshell_nesting_type_saved = subshell_nesting_type; + subshell_nesting_type = ')'; t = nested(TPAREN, '(', ')'); - --subshell_nesting_level; + subshell_nesting_type = subshell_nesting_type_saved; break; + } case '{': /*}*/ t = nested(TBRACE, '{', '}'); @@ -1115,16 +1118,29 @@ parse_usec(const char *s, struct timeval *tv) * a COMSUB recursively using the main shell parser and lexer */ char * -yyrecursive(void) +yyrecursive(int subtype) { struct op *t; char *cp; bool old_reject; - int old_symbol, old_salias; + int old_symbol, old_salias, old_nesting_type; struct ioword **old_herep; + int stok, etok; + + switch (subtype) { + case FUNSUB: + stok = '{'; + etok = '}'; + break; + case COMSUB: + default: + stok = '('; + etok = ')'; + } /* tell the lexer to accept a closing parenthesis as EOD */ - ++subshell_nesting_level; + old_nesting_type = subshell_nesting_type; + subshell_nesting_type = etok; /* push reject state, parse recursively, pop reject state */ old_reject = reject; @@ -1134,7 +1150,7 @@ yyrecursive(void) old_salias = sALIAS; sALIAS = 0; /* we use TPAREN as a helper container here */ - t = nested(TPAREN, '(', ')'); + t = nested(TPAREN, stok, etok); sALIAS = old_salias; herep = old_herep; reject = old_reject; @@ -1144,6 +1160,6 @@ yyrecursive(void) cp = snptreef(NULL, 0, "%T", t->left); tfree(t, ATEMP); - --subshell_nesting_level; + subshell_nesting_type = old_nesting_type; return (cp); } diff --git a/tree.c b/tree.c index f10899a..d2e20b3 100644 --- a/tree.c +++ b/tree.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.62 2012/07/30 19:58:05 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.63 2012/07/30 21:37:17 tg Exp $"); #define INDENT 8 @@ -292,6 +292,7 @@ static const char * wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) { int c; + const char *cs; /*- * problems: @@ -335,16 +336,20 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) } case COMSUB: shf_puts("$(", shf); + cs = ")"; + pSUB: while ((c = *wp++) != 0) shf_putc(c, shf); - shf_putc(')', shf); + shf_puts(cs, shf); break; + case FUNSUB: + shf_puts("${ ", shf); + cs = ";}"; + goto pSUB; case EXPRSUB: shf_puts("$((", shf); - while ((c = *wp++) != 0) - shf_putc(c, shf); - shf_puts("))", shf); - break; + cs = "))"; + goto pSUB; case OQUOTE: if (opmode & WDS_TPUTS) { quotelevel++; @@ -580,6 +585,7 @@ wdscan(const char *wp, int c) wp++; break; case COMSUB: + case FUNSUB: case EXPRSUB: while (*wp++ != 0) ; @@ -819,6 +825,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) closeandout: shf_putc('>', shf); break; + case FUNSUB: + shf_puts("FUNSUB<", shf); + goto dumpsub; case EXPRSUB: shf_puts("EXPRSUB<", shf); goto dumpsub; diff --git a/var.c b/var.c index f8c91b4..c06d213 100644 --- a/var.c +++ b/var.c @@ -27,7 +27,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/var.c,v 1.152 2012/07/01 15:38:09 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/var.c,v 1.153 2012/07/30 21:37:17 tg Exp $"); /*- * Variables @@ -1254,7 +1254,7 @@ unsetspec(struct tbl *vp) flushcom(true); break; case V_IFS: - setctypes(" \t\n", C_IFS); + setctypes(TC_IFSWS, C_IFS); ifs0 = ' '; break; case V_TMPDIR: