implement VALSUBs

This commit is contained in:
tg
2013-05-02 21:59:54 +00:00
parent 54a698d564
commit 1d0409d932
10 changed files with 130 additions and 53 deletions

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.629 2013/05/02 20:21:36 tg Exp $' srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.630 2013/05/02 21:59:44 tg Exp $'
#- #-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013 # 2011, 2012, 2013
@ -1531,7 +1531,7 @@ else
#define EXTERN #define EXTERN
#define MKSH_INCLUDES_ONLY #define MKSH_INCLUDES_ONLY
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.629 2013/05/02 20:21:36 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.630 2013/05/02 21:59:44 tg Exp $");
int main(void) { printf("Hello, World!\n"); return (0); } int main(void) { printf("Hello, World!\n"); return (0); }
EOF EOF
case $cm in case $cm in
@ -2046,7 +2046,7 @@ addsrcs USE_PRINTF_BUILTIN printf.c
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose" test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
test -n "$LDSTATIC" && add_cppflags -DMKSH_OPTSTATIC test -n "$LDSTATIC" && add_cppflags -DMKSH_OPTSTATIC
add_cppflags -DMKSH_BUILD_R=459 add_cppflags -DMKSH_BUILD_R=461
$e $bi$me: Finished configuration testing, now producing output.$ao $e $bi$me: Finished configuration testing, now producing output.$ao

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/Makefile,v 1.120 2013/04/27 18:50:21 tg Exp $ # $MirOS: src/bin/mksh/Makefile,v 1.121 2013/05/02 21:59:45 tg Exp $
#- #-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013 # 2011, 2012, 2013
@ -54,7 +54,7 @@ CPPFLAGS+= -DMKSH_ASSUME_UTF8 -DMKSH_DISABLE_DEPRECATED \
-DHAVE_SETGROUPS=1 -DHAVE_STRERROR=0 -DHAVE_STRSIGNAL=0 \ -DHAVE_SETGROUPS=1 -DHAVE_STRERROR=0 -DHAVE_STRSIGNAL=0 \
-DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \ -DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \
-DHAVE_SYS_ERRLIST_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \ -DHAVE_SYS_ERRLIST_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \
-DHAVE_PERSISTENT_HISTORY=1 -DMKSH_BUILD_R=459 -DHAVE_PERSISTENT_HISTORY=1 -DMKSH_BUILD_R=461
CPPFLAGS+= -D${${PROG:L}_tf:C/(Mir${MAN:E}{0,1}){2}/4/:S/x/mksh_BUILD/:U} CPPFLAGS+= -D${${PROG:L}_tf:C/(Mir${MAN:E}{0,1}){2}/4/:S/x/mksh_BUILD/:U}
COPTS+= -std=c99 -Wall COPTS+= -std=c99 -Wall
.endif .endif

63
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.611 2013/05/02 20:28:10 tg Exp $ # $MirOS: src/bin/mksh/check.t,v 1.612 2013/05/02 21:59:45 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas 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: 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 $ # $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 # http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
expected-stdout: expected-stdout:
@(#)MIRBSD KSH R45 2013/04/27 @(#)MIRBSD KSH R46 2013/05/02
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -38,7 +38,7 @@ name: KSH_VERSION
category: shell:legacy-no category: shell:legacy-no
--- ---
expected-stdout: expected-stdout:
@(#)LEGACY KSH R45 2013/04/27 @(#)LEGACY KSH R46 2013/05/02
description: description:
Check version of legacy shell. Check version of legacy shell.
stdin: stdin:
@ -8922,8 +8922,8 @@ stdin:
EOFI EOFI
#IORDWR_IODUP #IORDWR_IODUP
sh 1<>/dev/console 0<&1 2>&1 sh 1<>/dev/console 0<&1 2>&1
#COMSUB_EXPRSUB #COMSUB_EXPRSUB_FUNSUB_VALSUB
echo $(true) $((1+ 2)) echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
#QCHAR_OQUOTE_CQUOTE #QCHAR_OQUOTE_CQUOTE
echo fo\ob\"a\`r\'b\$az echo fo\ob\"a\`r\'b\$az
echo "fo\ob\"a\`r\'b\$az" echo "fo\ob\"a\`r\'b\$az"
@ -9305,23 +9305,23 @@ expected-stdout:
function reread_IORDWR_IODUP { function reread_IORDWR_IODUP {
x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x ) x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x )
} }
inline_COMSUB_EXPRSUB() { inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
echo $(true) $((1+ 2)) echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
} }
inline_COMSUB_EXPRSUB() { inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
echo $(true ) $((1+ 2)) echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;}
} }
function comsub_COMSUB_EXPRSUB { x=$( function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$(
echo $(true) $((1+ 2)) echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
); } ); }
function comsub_COMSUB_EXPRSUB { function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB {
x=$(echo $(true ) $((1+ 2)) ) x=$(echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} )
} }
function reread_COMSUB_EXPRSUB { x=$(( function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$((
echo $(true) $((1+ 2)) echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
)|tr u x); } )|tr u x); }
function reread_COMSUB_EXPRSUB { function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB {
x=$(( echo $(true ) $((1+ 2)) ) | tr u x ) x=$(( echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | tr u x )
} }
inline_QCHAR_OQUOTE_CQUOTE() { inline_QCHAR_OQUOTE_CQUOTE() {
echo fo\ob\"a\`r\'b\$az echo fo\ob\"a\`r\'b\$az
@ -9907,6 +9907,35 @@ expected-stdout:
2:ya x2,1,0. 2:ya x2,1,0.
3:ya,1,3. 3:ya,1,3.
--- ---
name: valsub-1
description:
Check that "value substitutions" work as advertised
stdin:
x=1
y=2
z=3
REPLY=4
echo "before: x<$x> y<$y> z<$z> R<$REPLY>"
x=${|
local y
echo "begin: x<$x> y<$y> z<$z> R<$REPLY>"
x=5
y=6
z=7
REPLY=8
echo "end: x<$x> y<$y> z<$z> R<$REPLY>"
}
echo "after: x<$x> y<$y> z<$z> R<$REPLY>"
# ensure trailing newlines are kept
t=${|REPLY=$'foo\n\n';}
typeset -p t
expected-stdout:
before: x<1> y<2> z<3> R<4>
begin: x<1> y<> z<3> R<>
end: x<5> y<6> z<7> R<8>
after: x<8> y<2> z<7> R<4>
typeset t=$'foo\n\n'
---
name: test-stnze-1 name: test-stnze-1
description: description:
Check that the short form [ $x ] works Check that the short form [ $x ] works

View File

@ -1,5 +1,5 @@
# $Id$ # $Id$
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.81 2013/05/02 21:46:29 tg Exp $ # $MirOS: src/bin/mksh/dot.mkshrc,v 1.82 2013/05/02 21:59:48 tg Exp $
#- #-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013 # 2011, 2012, 2013
@ -31,19 +31,20 @@ esac
PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \ PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \
2>/dev/null)} = *([ ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \ 2>/dev/null)} = *([ ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \
2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100} 2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100}
function precmd { : ${MKSH:=$(whence -p mksh)}; PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
local e=$? local e=$?
(( e )) && print -n "$e|" (( e )) && REPLY+="$e|"
# precmd is required to retain the errorlevel when ${ …;} is used REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)}
REPLY+=@${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)} || p=
REPLY+=$p$d
return $e return $e
} } '"$PS1 "; export EDITOR HOSTNAME MKSH TERM USER
PS1=$'\001\r''${ precmd;}${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?
)}@${HOSTNAME%%.*}:${ local e=$? 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)} || \
p=; print -nr -- "$p$d"; return $e;} '"$PS1 "; PS4='[$EPOCHREALTIME] '
: ${MKSH:=$(whence -p mksh)}; export EDITOR HOSTNAME MKSH TERM USER
alias ls=ls alias ls=ls
unalias ls unalias ls
alias l='ls -F' alias l='ls -F'

36
eval.c
View File

@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.138 2013/03/29 17:33:06 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.139 2013/05/02 21:59:49 tg Exp $");
/* /*
* string expansion * string expansion
@ -66,6 +66,7 @@ typedef struct {
static int varsub(Expand *, const char *, const char *, int *, int *); static int varsub(Expand *, const char *, const char *, int *, int *);
static int comsub(Expand *, const char *, int); static int comsub(Expand *, const char *, int);
static void funsub(struct op *); static void funsub(struct op *);
static char *valsub(struct op *, Area *);
static char *trimsub(char *, char *, int); static char *trimsub(char *, char *, int);
static void glob(char *, XPtrV *, bool); static void glob(char *, XPtrV *, bool);
static void globit(XString *, char **, char *, XPtrV *, int); static void globit(XString *, char **, char *, XPtrV *, int);
@ -298,27 +299,26 @@ expand(
continue; continue;
case COMSUB: case COMSUB:
case FUNSUB: case FUNSUB:
case VALSUB:
tilde_ok = 0; tilde_ok = 0;
if (f & DONTRUNCOMMAND) { if (f & DONTRUNCOMMAND) {
word = IFS_WORD; word = IFS_WORD;
*dp++ = '$'; *dp++ = '$';
if (c == FUNSUB) { *dp++ = c == COMSUB ? '(' : '{';
*dp++ = '{'; if (c != COMSUB)
*dp++ = ' '; *dp++ = c == FUNSUB ? ' ' : '|';
} else
*dp++ = '(';
while (*sp != '\0') { while (*sp != '\0') {
Xcheck(ds, dp); Xcheck(ds, dp);
*dp++ = *sp++; *dp++ = *sp++;
} }
if (c == FUNSUB) { if (c != COMSUB) {
*dp++ = ';'; *dp++ = ';';
*dp++ = '}'; *dp++ = '}';
} else } else
*dp++ = ')'; *dp++ = ')';
} else { } else {
type = comsub(&x, sp, c); type = comsub(&x, sp, c);
if (type == XCOM && (f&DOBLANK)) if (type != XBASE && (f & DOBLANK))
doblank++; doblank++;
sp = strnul(sp) + 1; sp = strnul(sp) + 1;
newlines = 0; newlines = 0;
@ -1366,6 +1366,10 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
/* rewind the tempfile and restore regular stdout */ /* rewind the tempfile and restore regular stdout */
lseek(shf_fileno(shf), (off_t)0, SEEK_SET); lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
restfd(1, ofd1); restfd(1, ofd1);
} else if (fn == VALSUB) {
xp->str = valsub(t, ATEMP);
subst_exstat = exstat & 0xFF;
return (XSUB);
} else { } else {
int ofd1, pv[2]; int ofd1, pv[2];
@ -1832,3 +1836,19 @@ funsub(struct op *t)
execute(t, XXCOM | XERROK, NULL); execute(t, XXCOM | XERROK, NULL);
popblock(); popblock();
} }
static char *
valsub(struct op *t, Area *ap)
{
char *cp;
struct tbl *vp;
newblock();
vp = local("REPLY", false);
e->type = E_FUNC;
if (!kshsetjmp(e->jbuf))
execute(t, XXCOM | XERROK, NULL);
strdupx(cp, str_val(vp), ap);
popblock();
return (cp);
}

12
lex.c
View File

@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.184 2013/03/29 17:33:55 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/lex.c,v 1.185 2013/05/02 21:59:49 tg Exp $");
/* /*
* states while lexing word * states while lexing word
@ -421,8 +421,14 @@ yylex(int cf)
wp += cz; wp += cz;
} }
} else if (c == '{') /*}*/ { } else if (c == '{') /*}*/ {
c = getsc(); if ((c = getsc()) == '|') {
if (ctype(c, C_IFSWS)) { /*
* non-subenvironment
* value substitution
*/
c = VALSUB;
goto subst_command2;
} else if (ctype(c, C_IFSWS)) {
/* /*
* non-subenvironment * non-subenvironment
* "command" substitution * "command" substitution

11
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.314 2013/05/02 20:28:13 tg Exp $ .\" $MirOS: src/bin/mksh/mksh.1,v 1.315 2013/05/02 21:59:50 tg Exp $
.\" $OpenBSD: ksh.1,v 1.146 2013/03/18 11:10:52 mpi Exp $ .\" $OpenBSD: ksh.1,v 1.146 2013/03/18 11:10:52 mpi Exp $
.\"- .\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -1182,6 +1182,15 @@ work, and in that
.Ic exit .Ic exit
terminates the parent shell. terminates the parent shell.
.Pp .Pp
Another variant of substitution are the valsubs (value substitutions)
.Pf ${\*(Ba\& Ns Ar command Ns \&;}
which are also executed in the current environment, like funsubs, but
share their I/O with the parent; instead, they evaluate to whatever
the, initially empty, expression-local variable
.Ev REPLY
is set to within the
.Ar command Ns No s .
.Pp
If a substitution appears outside of double quotes, the results of the If a substitution appears outside of double quotes, the results of the
substitution are generally subject to word or field splitting according to substitution are generally subject to word or field splitting according to
the current value of the the current value of the

7
sh.h
View File

@ -164,9 +164,9 @@
#endif #endif
#ifdef EXTERN #ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.653 2013/04/27 19:16:27 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.654 2013/05/02 21:59:52 tg Exp $");
#endif #endif
#define MKSH_VERSION "R45 2013/04/27" #define MKSH_VERSION "R46 2013/05/02"
/* arithmetic types: C implementation */ /* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES #if !HAVE_CAN_INTTYPES
@ -520,7 +520,7 @@ char *ucstrstr(char *, const char *);
#define mkssert(e) do { } while (/* CONSTCOND */ 0) #define mkssert(e) do { } while (/* CONSTCOND */ 0)
#endif #endif
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 459) #if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 461)
#error Must run Build.sh to compile this. #error Must run Build.sh to compile this.
int int
im_sorry_dave(void) im_sorry_dave(void)
@ -1327,6 +1327,7 @@ struct op {
#define CPAT 11 /* close pattern: ) */ #define CPAT 11 /* close pattern: ) */
#define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */ #define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */
#define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */ #define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */
#define VALSUB 15 /* ${|foo;} substitution (NUL terminated) */
/* /*
* IO redirection * IO redirection

4
syn.c
View File

@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.90 2013/04/26 21:22:50 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/syn.c,v 1.91 2013/05/02 21:59:53 tg Exp $");
struct nesting_state { struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */ int start_token; /* token than began nesting (eg, FOR) */
@ -1129,7 +1129,7 @@ yyrecursive(int subtype MKSH_A_UNUSED)
struct yyrecursive_state *ys; struct yyrecursive_state *ys;
int stok, etok; int stok, etok;
if (subtype == FUNSUB) { if (subtype != COMSUB) {
stok = '{'; stok = '{';
etok = '}'; etok = '}';
} else { } else {

17
tree.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012 * 2011, 2012, 2013
* Thorsten Glaser <tg@mirbsd.org> * Thorsten Glaser <tg@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.68 2013/03/24 00:56:27 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.69 2013/05/02 21:59:54 tg Exp $");
#define INDENT 8 #define INDENT 8
@ -349,7 +349,14 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
shf_puts(cs, shf); shf_puts(cs, shf);
break; break;
case FUNSUB: case FUNSUB:
shf_puts("${ ", shf); c = ' ';
if (0)
/* FALLTHROUGH */
case VALSUB:
c = '|';
shf_putc('$', shf);
shf_putc('{', shf);
shf_putc(c, shf);
cs = ";}"; cs = ";}";
goto pSUB; goto pSUB;
case EXPRSUB: case EXPRSUB:
@ -592,6 +599,7 @@ wdscan(const char *wp, int c)
break; break;
case COMSUB: case COMSUB:
case FUNSUB: case FUNSUB:
case VALSUB:
case EXPRSUB: case EXPRSUB:
while (*wp++ != 0) while (*wp++ != 0)
; ;
@ -834,6 +842,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
case FUNSUB: case FUNSUB:
shf_puts("FUNSUB<", shf); shf_puts("FUNSUB<", shf);
goto dumpsub; goto dumpsub;
case VALSUB:
shf_puts("VALSUB<", shf);
goto dumpsub;
case EXPRSUB: case EXPRSUB:
shf_puts("EXPRSUB<", shf); shf_puts("EXPRSUB<", shf);
goto dumpsub; goto dumpsub;