diff -Nru mksh-50d/Build.sh mksh-50e/Build.sh --- mksh-50d/Build.sh 2014-10-07 17:23:02.000000000 +0200 +++ mksh-50e/Build.sh 2015-03-01 16:43:15.000000000 +0100 @@ -1,5 +1,5 @@ #!/bin/sh -srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.669 2014/10/07 15:22:12 tg Exp $' +srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.669.2.2 2015/03/01 15:42:50 tg Exp $' #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013, 2014 @@ -1700,22 +1700,22 @@ ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF' #include #include - int main(int ac, char **av) { return ((uint32_t)(ptrdiff_t)*av + (int32_t)ac); } + int main(int ac, char **av) { return ((uint32_t)(size_t)*av + (int32_t)ac); } EOF ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF' #include #include - int main(int ac, char **av) { return ((u_int32_t)(ptrdiff_t)*av + (int32_t)ac); } + int main(int ac, char **av) { return ((u_int32_t)(size_t)*av + (int32_t)ac); } EOF ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF' #include #include - int main(int ac, char **av) { return ((uint8_t)(ptrdiff_t)av[ac]); } + int main(int ac, char **av) { return ((uint8_t)(size_t)av[ac]); } EOF ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF' #include #include - int main(int ac, char **av) { return ((u_int8_t)(ptrdiff_t)av[ac]); } + int main(int ac, char **av) { return ((u_int8_t)(size_t)av[ac]); } EOF ac_test rlim_t <<-'EOF' @@ -1784,7 +1784,7 @@ #define EXTERN #define MKSH_INCLUDES_ONLY #include "sh.h" - __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.669 2014/10/07 15:22:12 tg Exp $"); + __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.669.2.2 2015/03/01 15:42:50 tg Exp $"); int main(void) { printf("Hello, World!\n"); return (isatty(0)); } EOF case $cm in @@ -1969,13 +1969,13 @@ ac_test setlocale_ctype '' 'setlocale(LC_CTYPE, "")' <<-'EOF' #include #include - int main(void) { return ((int)(ptrdiff_t)(void *)setlocale(LC_CTYPE, "")); } + int main(void) { return ((int)(size_t)(void *)setlocale(LC_CTYPE, "")); } EOF ac_test langinfo_codeset setlocale_ctype 0 'nl_langinfo(CODESET)' <<-'EOF' #include #include - int main(void) { return ((int)(ptrdiff_t)(void *)nl_langinfo(CODESET)); } + int main(void) { return ((int)(size_t)(void *)nl_langinfo(CODESET)); } EOF ac_test select <<-'EOF' @@ -2150,9 +2150,9 @@ cta(uari_wrap_32_bit, (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) > (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4)); -#define NUM 22 +#define NUM 21 #else -#define NUM 16 +#define NUM 15 #endif /* these are always required */ cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0); @@ -2161,11 +2161,10 @@ cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t)); cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t)); -cta(ptrdifft_sizet_same_size, sizeof(ptrdiff_t) == sizeof(size_t)); -cta(ptrdifft_voidptr_same_size, sizeof(ptrdiff_t) == sizeof(void *)); -cta(ptrdifft_funcptr_same_size, sizeof(ptrdiff_t) == sizeof(void (*)(void))); +cta(sizet_voidptr_same_size, sizeof(size_t) == sizeof(void *)); +cta(sizet_funcptr_same_size, sizeof(size_t) == sizeof(void (*)(void))); /* our formatting routines assume this */ -cta(ptr_fits_in_long, sizeof(ptrdiff_t) <= sizeof(long)); +cta(ptr_fits_in_long, sizeof(size_t) <= sizeof(long)); /* for struct alignment people */ char padding[64 - NUM]; }; @@ -2264,6 +2263,11 @@ ;' >conftest.c # GNU sed 2.03 segfaults when optimising this to sed -n NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \ + grep -v '^#' | \ + sed '/mksh_cfg.*= *$/{ + N + s/\n/ / + }' | \ grep '^ *mksh_cfg *=' | \ sed 's/^ *mksh_cfg *=[ ]*\([()0-9x+-][()0-9x+ -]*\).*$/\1/'` case $NSIG in @@ -2295,6 +2299,11 @@ echo ';' >>conftest.c # GNU sed 2.03 croaks on optimising this, too vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \ + grep -v '^#' | \ + sed '/mksh_cfg.*= *$/{ + N + s/\n/ / + }' | \ grep '^ *mksh_cfg *=' | \ sed 's/^ *mksh_cfg *=[ ]*\([0-9][0-9x]*\).*$/:\1 '$name/ done | sed -n '/^:[^ ]/s/^://p' | while read nr name; do @@ -2316,7 +2325,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" -add_cppflags -DMKSH_BUILD_R=504 +add_cppflags -DMKSH_BUILD_R=505 $e $bi$me: Finished configuration testing, now producing output.$ao @@ -2431,9 +2440,12 @@ ;; esac echo ": # work around NeXTstep bug" >Rebuild.sh -for file in "$srcdir"/*.opt; do +cd "$srcdir" +optfiles=`echo *.opt` +cd "$curdir" +for file in $optfiles; do echo "echo + Running genopt on '$file'..." - echo "(srcfile='$file'; BUILDSH_RUN_GENOPT=1; . '$srcdir/Build.sh')" + echo "(srcfile='$srcdir/$file'; BUILDSH_RUN_GENOPT=1; . '$srcdir/Build.sh')" done >>Rebuild.sh echo set -x >>Rebuild.sh for file in $SRCS; do @@ -2467,11 +2479,11 @@ extras='emacsfn.h rlimits.opt sh.h sh_flags.opt var_spec.h' test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc" gens= genq= - for file in "$srcdir"/*.opt; do + for file in $optfiles; do genf=`basename "$file" | sed 's/.opt$/.gen/'` gens="$gens $genf" - genq="$genq$nl$genf: $srcdir/Build.sh $file - srcfile=$file; BUILDSH_RUN_GENOPT=1; . $srcdir/Build.sh" + genq="$genq$nl$genf: $srcdir/Build.sh $srcdir/$file + srcfile=$srcdir/$file; BUILDSH_RUN_GENOPT=1; . $srcdir/Build.sh" done cat >Makefrag.inc < # # Provided that these terms and disclaimer and all copyright notices @@ -24,10 +24,13 @@ # 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 +# +# Integrated testsuites from: +# (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date expected-stdout: - @(#)MIRBSD KSH R50 2014/10/07 + @(#)MIRBSD KSH R50 2015/03/01 description: Check version of shell. stdin: @@ -36,7 +39,7 @@ category: shell:legacy-no --- expected-stdout: - @(#)LEGACY KSH R50 2014/10/07 + @(#)LEGACY KSH R50 2015/03/01 description: Check version of legacy shell. stdin: @@ -2359,6 +2362,14 @@ expected-stdout: baz --- +name: heredoc-9f +description: + Check long here strings +stdin: + cat <<< "$( : )aa" +expected-stdout: + aa +--- name: heredoc-10 description: Check direct here document assignment @@ -2389,6 +2400,14 @@ 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 +2423,7 @@ } ve={=e $x \x40= } vf={=f $x @= } | + | vapp1^vapp2^ | --- name: heredoc-11 description: @@ -2433,6 +2453,14 @@ 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 +2478,52 @@ } vc={=c u \x40= } vd={=d $x \x40= } | + | vapp1^vapp2^ | +--- +name: heredoc-12 +description: + Check here documents can use $* and $@; note shells vary: + • pdksh 5.2.14 acts the same + • dash has 1 and 2 the same but 3 lacks the space + • ksh93, bash4 differ in 2 by using space ipv colon +stdin: + set -- a b + nl=' + ' + IFS=" $nl"; n=1 + cat <"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } set -- A B C showargs 1 $* showargs 2 "$*" showargs 3 $@ showargs 4 "$@" expected-stdout: - <1> - <2> - <3> - <4> + <1> . + <2> . + <3> . + <4> . --- name: IFS-colon-1 description: Simple test, IFS=: stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS=: set -- A B C showargs 1 $* @@ -3609,16 +3683,16 @@ showargs 3 $@ showargs 4 "$@" expected-stdout: - <1> - <2> - <3> - <4> + <1> . + <2> . + <3> . + <4> . --- name: IFS-null-1 description: Simple test, IFS="" stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS="" set -- A B C showargs 1 $* @@ -3626,16 +3700,16 @@ showargs 3 $@ showargs 4 "$@" expected-stdout: - <1> - <2> - <3> - <4> + <1> . + <2> . + <3> . + <4> . --- name: IFS-space-colon-1 description: Simple test, IFS=: stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS="$IFS:" set -- showargs 1 $* @@ -3644,52 +3718,52 @@ showargs 4 "$@" showargs 5 : "$@" expected-stdout: - <1> - <2> <> - <3> - <4> - <5> <:> + <1> . + <2> <> . + <3> . + <4> . + <5> <:> . --- name: IFS-space-colon-2 description: Simple test, IFS=: AT&T ksh fails this, POSIX says the test is correct. stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS="$IFS:" set -- showargs :"$@" expected-stdout: - <:> + <:> . --- name: IFS-space-colon-4 description: Simple test, IFS=: stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS="$IFS:" set -- showargs "$@$@" expected-stdout: - + . --- name: IFS-space-colon-5 description: Simple test, IFS=: Don't know what POSIX thinks of this. AT&T ksh does not do this. stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS="$IFS:" set -- showargs "${@:-}" expected-stdout: - <> + <> . --- name: IFS-subst-1 description: Simple test, IFS=: stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS="$IFS:" x=":b: :" echo -n '1:'; for i in $x ; do echo -n " [$i]" ; done ; echo @@ -3711,53 +3785,80 @@ expected-stdout: 1: [] [b] [] 2: [:b::] - <3> <> <> - <4> <:b::> + <3> <> <> . + <4> <:b::> . 5: [a] [b] - <6> + <6> . 7: [a] [] [c] - <8> <> + <8> <> . 9: [h] [ith] [ere] - <10> - <11> + <10> . + <11> . 12: [A] [B] [] [D] - <13> <> + <13> <> . --- name: IFS-subst-2 description: Check leading whitespace after trim does not make a field stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } x="X 1 2" showargs 1 shift ${x#X} expected-stdout: - <1> <1> <2> + <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 stdin: - showargs() { for i; do echo -n " <$i>"; done; echo; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } showargs 0 ${-+} IFS=: showargs 1 ${-+:foo:bar} IFS=' ' showargs 2 ${-+ foo bar} expected-stdout: - <0> - <1> <> - <2> + <0> . + <1> <> . + <2> . +--- +name: IFS-subst-3-ass +description: + Check non-field semantics +stdin: + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; 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 stdin: + pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; } a='space divded argument here' IFS=\ ; set -- $a IFS= ; q="$*" ; nq=$* - printf '<%s>\n' "$*" $* "$q" "$nq" + pfn "$*" $* "$q" "$nq" [ "$q" = "$nq" ] && echo =true || echo =false expected-stdout: "; done; } a='space divded argument here' IFS=\ ; set -- $a IFS= ; q="$@" ; nq=$@ - printf '<%s>\n' "$*" $* "$q" "$nq" + pfn "$*" $* "$q" "$nq" [ "$q" = "$nq" ] && echo =true || echo =false expected-stdout: "; done; } a='space divded argument here' IFS=\ ; set -- $a; IFS= @@ -3806,14 +3909,14 @@ nqs=$* qk="$@" nqk=$@ - printf '= qs '; printf '<%s>\n' "$qs" - printf '=nqs '; printf '<%s>\n' "$nqs" - printf '= qk '; printf '<%s>\n' "$qk" - printf '=nqk '; printf '<%s>\n' "$nqk" - printf '~ qs '; printf '<%s>\n' "$*" - printf '~nqs '; printf '<%s>\n' $* - printf '~ qk '; printf '<%s>\n' "$@" - printf '~nqk '; printf '<%s>\n' $@ + print -nr -- '= qs '; pfn "$qs" + print -nr -- '=nqs '; pfn "$nqs" + print -nr -- '= qk '; pfn "$qk" + print -nr -- '=nqk '; pfn "$nqk" + print -nr -- '~ qs '; pfn "$*" + print -nr -- '~nqs '; pfn $* + print -nr -- '~ qk '; pfn "$@" + print -nr -- '~nqk '; pfn $@ expected-stdout: = qs @@ -3842,21 +3945,22 @@ description: extended testsuite based on problem by mikeserv stdin: + pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; } a='space divded argument here' IFS=\ ; set -- $a; IFS= qs="$*" - printf '= qs '; printf '<%s>\n' "$qs" - printf '~ qs '; printf '<%s>\n' "$*" + print -nr -- '= qs '; pfn "$qs" + print -nr -- '~ qs '; pfn "$*" nqs=$* - printf '=nqs '; printf '<%s>\n' "$nqs" - printf '~nqs '; printf '<%s>\n' $* + print -nr -- '=nqs '; pfn "$nqs" + print -nr -- '~nqs '; pfn $* qk="$@" - printf '= qk '; printf '<%s>\n' "$qk" - printf '~ qk '; printf '<%s>\n' "$@" + print -nr -- '= qk '; pfn "$qk" + print -nr -- '~ qk '; pfn "$@" nqk=$@ - printf '=nqk '; printf '<%s>\n' "$nqk" - printf '~nqk '; printf '<%s>\n' $@ + print -nr -- '=nqk '; pfn "$nqk" + print -nr -- '~nqk '; pfn $@ expected-stdout: = qs @@ -3885,22 +3989,23 @@ description: extended testsuite based on problem by mikeserv stdin: + pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; } a='space divded argument here' IFS=\ ; set -- $a; IFS= unset v qs=${v:-"$*"} - printf '= qs '; printf '<%s>\n' "$qs" - printf '~ qs '; printf '<%s>\n' ${v:-"$*"} + print -nr -- '= qs '; pfn "$qs" + print -nr -- '~ qs '; pfn ${v:-"$*"} nqs=${v:-$*} - printf '=nqs '; printf '<%s>\n' "$nqs" - printf '~nqs '; printf '<%s>\n' ${v:-$*} + print -nr -- '=nqs '; pfn "$nqs" + print -nr -- '~nqs '; pfn ${v:-$*} qk=${v:-"$@"} - printf '= qk '; printf '<%s>\n' "$qk" - printf '~ qk '; printf '<%s>\n' ${v:-"$@"} + print -nr -- '= qk '; pfn "$qk" + print -nr -- '~ qk '; pfn ${v:-"$@"} nqk=${v:-$@} - printf '=nqk '; printf '<%s>\n' "$nqk" - printf '~nqk '; printf '<%s>\n' ${v:-$@} + print -nr -- '=nqk '; pfn "$nqk" + print -nr -- '~nqk '; pfn ${v:-$@} expected-stdout: = qs @@ -3929,21 +4034,22 @@ description: extended testsuite based on problem by mikeserv stdin: + pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; } a='space divded argument here' IFS=\ ; set -- $a; IFS=, qs="$*" - printf '= qs '; printf '<%s>\n' "$qs" - printf '~ qs '; printf '<%s>\n' "$*" + print -nr -- '= qs '; pfn "$qs" + print -nr -- '~ qs '; pfn "$*" nqs=$* - printf '=nqs '; printf '<%s>\n' "$nqs" - printf '~nqs '; printf '<%s>\n' $* + print -nr -- '=nqs '; pfn "$nqs" + print -nr -- '~nqs '; pfn $* qk="$@" - printf '= qk '; printf '<%s>\n' "$qk" - printf '~ qk '; printf '<%s>\n' "$@" + print -nr -- '= qk '; pfn "$qk" + print -nr -- '~ qk '; pfn "$@" nqk=$@ - printf '=nqk '; printf '<%s>\n' "$nqk" - printf '~nqk '; printf '<%s>\n' $@ + print -nr -- '=nqk '; pfn "$nqk" + print -nr -- '~nqk '; pfn $@ expected-stdout: = qs @@ -3972,22 +4078,23 @@ description: extended testsuite based on problem by mikeserv stdin: + pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; } a='space divded argument here' IFS=\ ; set -- $a; IFS=, unset v qs=${v:-"$*"} - printf '= qs '; printf '<%s>\n' "$qs" - printf '~ qs '; printf '<%s>\n' ${v:-"$*"} + print -nr -- '= qs '; pfn "$qs" + print -nr -- '~ qs '; pfn ${v:-"$*"} nqs=${v:-$*} - printf '=nqs '; printf '<%s>\n' "$nqs" - printf '~nqs '; printf '<%s>\n' ${v:-$*} + print -nr -- '=nqs '; pfn "$nqs" + print -nr -- '~nqs '; pfn ${v:-$*} qk=${v:-"$@"} - printf '= qk '; printf '<%s>\n' "$qk" - printf '~ qk '; printf '<%s>\n' ${v:-"$@"} + print -nr -- '= qk '; pfn "$qk" + print -nr -- '~ qk '; pfn ${v:-"$@"} nqk=${v:-$@} - printf '=nqk '; printf '<%s>\n' "$nqk" - printf '~nqk '; printf '<%s>\n' ${v:-$@} + print -nr -- '=nqk '; pfn "$nqk" + print -nr -- '~nqk '; pfn ${v:-$@} expected-stdout: = qs @@ -4026,38 +4133,55 @@ 'emulate sh' zsh has extra fields in - a5ins (IFS_NWS unquoted $*) - b5ins, matching mksh’s + !!WARNING!! more to come: http://austingroupbugs.net/view.php?id=888 stdin: - "$__progname" -c 'IFS=; set -- "" 2 ""; printf "[%s]\n" $*; x=$*; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- "" 2 ""; pfb $*; x=$*; pfn "$x"' echo '=a1zns' - "$__progname" -c 'IFS=; set -- "" 2 ""; printf "[%s]\n" "$*"; x="$*"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- "" 2 ""; pfb "$*"; x="$*"; pfn "$x"' echo '=a2zqs' - "$__progname" -c 'IFS=; set -- "" 2 ""; printf "[%s]\n" $@; x=$@; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- "" 2 ""; pfb $@; x=$@; pfn "$x"' echo '=a3zna' - "$__progname" -c 'IFS=; set -- "" 2 ""; printf "[%s]\n" "$@"; x="$@"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- "" 2 ""; pfb "$@"; x="$@"; pfn "$x"' echo '=a4zqa' - "$__progname" -c 'IFS=,; set -- "" 2 ""; printf "[%s]\n" $*; x=$*; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- "" 2 ""; pfb $*; x=$*; pfn "$x"' echo '=a5ins' - "$__progname" -c 'IFS=,; set -- "" 2 ""; printf "[%s]\n" "$*"; x="$*"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- "" 2 ""; pfb "$*"; x="$*"; pfn "$x"' echo '=a6iqs' - "$__progname" -c 'IFS=,; set -- "" 2 ""; printf "[%s]\n" $@; x=$@; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- "" 2 ""; pfb $@; x=$@; pfn "$x"' echo '=a7ina' - "$__progname" -c 'IFS=,; set -- "" 2 ""; printf "[%s]\n" "$@"; x="$@"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- "" 2 ""; pfb "$@"; x="$@"; pfn "$x"' echo '=a8iqa' - "$__progname" -c 'IFS=; set -- A B "" "" C; printf "[%s]\n" $*; x=$*; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- A B "" "" C; pfb $*; x=$*; pfn "$x"' echo '=b1zns' - "$__progname" -c 'IFS=; set -- A B "" "" C; printf "[%s]\n" "$*"; x="$*"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- A B "" "" C; pfb "$*"; x="$*"; pfn "$x"' echo '=b2zqs' - "$__progname" -c 'IFS=; set -- A B "" "" C; printf "[%s]\n" $@; x=$@; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- A B "" "" C; pfb $@; x=$@; pfn "$x"' echo '=b3zna' - "$__progname" -c 'IFS=; set -- A B "" "" C; printf "[%s]\n" "$@"; x="$@"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=; set -- A B "" "" C; pfb "$@"; x="$@"; pfn "$x"' echo '=b4zqa' - "$__progname" -c 'IFS=,; set -- A B "" "" C; printf "[%s]\n" $*; x=$*; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- A B "" "" C; pfb $*; x=$*; pfn "$x"' echo '=b5ins' - "$__progname" -c 'IFS=,; set -- A B "" "" C; printf "[%s]\n" "$*"; x="$*"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- A B "" "" C; pfb "$*"; x="$*"; pfn "$x"' echo '=b6iqs' - "$__progname" -c 'IFS=,; set -- A B "" "" C; printf "[%s]\n" $@; x=$@; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- A B "" "" C; pfb $@; x=$@; pfn "$x"' echo '=b7ina' - "$__progname" -c 'IFS=,; set -- A B "" "" C; printf "[%s]\n" "$@"; x="$@"; printf "<%s>\n" "$x"' + "$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; }; + IFS=,; set -- A B "" "" C; pfb "$@"; x="$@"; pfn "$x"' echo '=b8iqa' expected-stdout: [2] @@ -4133,13 +4257,69 @@ =b8iqa --- +name: IFS-subst-6 +description: + Regression wrt. vector expansion in trim +stdin: + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; 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 s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } + IFS="*" + a=abcd + set -- '' c + showargs "$*" ${a##"$*"} +expected-stdout: + <*c> . +--- +name: IFS-subst-8 +description: + http://austingroupbugs.net/view.php?id=221 +stdin: + n() { echo "$#"; }; n "${foo-$@}" +expected-stdout: + 1 +--- +name: IFS-subst-9 +description: + Scalar context for $*/$@ in [[ and case +stdin: + "$__progname" -c 'IFS=; set a b; [[ $* = "$1$2" ]]; echo 1 $?' sh a b + "$__progname" -c 'IFS=; [[ $* = ab ]]; echo 2 "$?"' sh a b + "$__progname" -c 'IFS=; [[ "$*" = ab ]]; echo 3 "$?"' sh a b + "$__progname" -c 'IFS=; [[ $* = a ]]; echo 4 "$?"' sh a b + "$__progname" -c 'IFS=; [[ "$*" = a ]]; echo 5 "$?"' sh a b + "$__progname" -c 'IFS=; [[ "$@" = a ]]; echo 6 "$?"' sh a b + "$__progname" -c 'IFS=; case "$@" in a) echo 7 a;; ab) echo 7 b;; a\ b) echo 7 ok;; esac' sh a b + "$__progname" -c 'IFS=; case $* in a) echo 8 a;; ab) echo 8 ok;; esac' sh a b + "$__progname" -c 'pfsp() { for s_arg in "$@"; do print -nr -- "<$s_arg> "; done; print .; }; IFS=; star=$* at="$@"; pfsp 9 "$star" "$at"' sh a b +expected-stdout: + 1 0 + 2 0 + 3 0 + 4 1 + 5 1 + 6 1 + 7 ok + 8 ok + <9> . +--- name: IFS-arith-1 description: http://austingroupbugs.net/view.php?id=832 stdin: ${ZSH_VERSION+false} || emulate sh ${BASH_VERSION+set -o posix} - showargs() { for x in "$@"; do echo -n "<$x> "; done; echo .; } + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } IFS=0 showargs $((1230456)) expected-stdout: @@ -7063,6 +7243,7 @@ XXX if the OS can already execute them, we lose note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text + XXX fails when LD_PRELOAD is set with -e and Perl chokes it (ASan) need-pass: no category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh env-setup: !FOO=BAR! @@ -8013,6 +8194,66 @@ .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 @@ -8172,13 +8413,21 @@ description: Ensure empty strings expand emptily stdin: - print x ${a} ${b} y - print z ${a#?} ${b%?} w - print v ${a=} ${b/c/d} u -expected-stdout: - x y - z w - v u + print s ${a} . ${b} S + print t ${a#?} . ${b%?} T + print r ${a=} . ${b/c/d} R + print q + print s "${a}" . "${b}" S + print t "${a#?}" . "${b%?}" T + print r "${a=}" . "${b/c/d}" R +expected-stdout: + s . S + t . T + r . R + q + s . S + t . T + r . R --- name: varexpand-null-2 description: @@ -8194,13 +8443,41 @@ name: varexpand-null-3 description: Ensure concatenating behaviour matches other shells - although the line 2<> is probably wrong? XNULLSUB case. stdin: - x=; printf "1<%s>\n" "$x$@" - set A; printf "2<%s>\n" "${@:+}" + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } + x=; showargs 1 "$x"$@ + set A; showargs 2 "${@:+}" + n() { echo "$#"; } + unset e + set -- a b + n """$@" + n "$@" + n "$@""" + n "$e""$@" + n "$@" + n "$@""$e" + set -- + n """$@" + n "$@" + n "$@""" + n "$e""$@" + n "$@" + n "$@""$e" expected-stdout: - 1<> - 2<> + <1> <> . + <2> <> . + 2 + 2 + 2 + 2 + 2 + 2 + 1 + 0 + 1 + 1 + 0 + 1 --- name: print-funny-chars description: @@ -10921,6 +11198,7 @@ (mypid=$$; try mypid) echo =15 ) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//' + exit ${PIPESTATUS[0]} expected-stdout: y =1 @@ -11655,3 +11933,26 @@ expected-stderr-pattern: /.*/ --- +name: xtrace-2 +description: + Check that "set -x" is off during PS4 expansion +stdin: + f() { + print -n "(f1:$-)" + set -x + print -n "(f2:$-)" + } + PS4='[(p:$-)$(f)] ' + print "(o0:$-)" + set -x -o inherit-xtrace + print "(o1:$-)" + set +x + print "(o2:$-)" +expected-stdout: + (o0:sh) + (o1:shx) + (o2:sh) +expected-stderr: + [(p:sh)(f1:sh)(f2:sh)] print '(o1:shx)' + [(p:sh)(f1:sh)(f2:sh)] set +x +--- diff -Nru mksh-50d/debian/README.Debian mksh-50e/debian/README.Debian --- mksh-50d/debian/README.Debian 2014-10-07 20:40:16.000000000 +0200 +++ mksh-50e/debian/README.Debian 2015-03-08 00:39:05.000000000 +0100 @@ -38,7 +38,8 @@ installation however is not automatic due to Debian #540512 (a bug in dash). It is strongly recommended to use its “legacy” flavour for this, as it uses the host “long” C type for integer - arithmetics, following POSIX. Use these commands: + arithmetics, following POSIX, and contains some workarounds for + broken maintainer scripts in Debian packages. Use these commands: $ sudo ln -sf lksh /bin/sh $ sudo ln -sf mksh.1.gz /usr/share/man/man1/sh.1.gz This will install the mksh manpage for sh(1) since it explains @@ -47,10 +48,9 @@ Please note that lksh, as opposed to mksh, is not suited for interactive use (but no regression from dash which also isn’t). -* The /bin/lksh binary is used to run legacy ksh88 and pdksh scripts - only. It was not suitable as /bin/sh before Debian mksh 46-2, nor - is it intended for interactive use. It only has a printf(1) builtin - starting with mksh 46-2 in Debian. It however is used to provide +* The /bin/lksh binary is used to run legacy ksh88 and pdksh scripts. + It is not intended for interactive use, but has a printf(1) builtin. + It can be used as /bin/sh (see above). It however is used to provide pdksh as transitional package. It will not be registered with the menu system or set up as login shell. Also, do not use the pdksh compatibility symlink as login shell. @@ -65,7 +65,7 @@ (position independent code), as this is a static executable - glibc, if cross-built or running the testsuite is disabled; otherwise klibc if available and working, otherwise dietlibc - if available and working, otherwise glibc + if available and working, otherwise glibc (statically linked) - note that the stack protector is disabled on dietlibc, klibc due to limitations in those operating environments - calling as sh or -sh invokes "set -o posix -o sh" @@ -75,7 +75,7 @@ * /bin/lksh is built with the following options: - extra hardening flags: +all (no PIE if dietlibc or klibc is used) - dietlibc or klibc (under similar conditions as /bin/mksh-static) - or, otherwise, glibc (dynamically linked) + (statically linked) or, otherwise, glibc (dynamically linked) - the printf builtin - calling as sh or -sh invokes "set -o posix -o sh" - no locale support; uses getenv on LANG/LC_CTYPE/LC_ALL diff -Nru mksh-50d/debian/changelog mksh-50e/debian/changelog --- mksh-50d/debian/changelog 2014-10-29 13:28:42.000000000 +0100 +++ mksh-50e/debian/changelog 2015-03-08 00:42:41.000000000 +0100 @@ -1,3 +1,76 @@ +mksh (50e-2ubuntu1) vivid; urgency=high + + * Merge from Debian (LP: #1429469), remaining changes: + - Omit dietlibc builds on Ubuntu, where it is not in main + - Maintainer change for Ubuntu + + -- Thorsten Glaser Sat, 07 Mar 2015 23:42:38 +0000 + +mksh (50e-2) experimental; urgency=medium + + * QA upload. + * Backport upstream fix: + - [tg] SECURITY: make unset HISTFILE actually work + * Adjust shell version accordingly + + -- Thorsten Glaser Sat, 07 Mar 2015 23:30:36 +0000 + +mksh (50e-1) experimental; urgency=high + + * QA upload. + * Remove timestamps from README.Debian; should make builds reproducible + * Filter out some more junk from README.Debian + * Update to the next release of the R50-stable branch: + - [tg] Fix LP#1381965 and LP#1381993 (more field splitting) + - [jilles] Update location of FreeBSD testsuite for test(1) + - [Martin Natano] Remove dead NULL elements from Emacs keybindings + - [tg, Stéphane Chazelas, Geoff Clare] Change several testcases for $*/$@ + expansion with/without quotes to expected-fail, with even more to come ☹ + - [tg] Fix miscalculating required memory for encoding the double-quoted + parts of a here document or here string delimiter, leading to a buffer + overflow; discovered by zacts from IRC + - [RT] Rename a function conflicting with a MacRelix system header + - [tg] Use size_t (and ssize_t) consistently, stop using ptrdiff_t; fixes + some arithmetics and S/390 bugs + - [tg] Remove old workarounds for Clang 3.2 scan-build + - [tg] Remove all Clang/Coverity assertions, making room for new checks + - [tg] Fix NSIG generation on Debian sid gcc-snapshot + - [tg] Make a testcase not fail in a corner case + - [tg] Fix issues detected by GCC’s new sanitisers: data type of a value + to be shifted constantly must be unsigned (what not, in C…); shebang + check array accesses are always unsigned char + - [tg] Be even more explicit wrt. POSIX in the manpage + - [tg] Fix shebang / file magic decoding + - [tg] More int → bool conversion + - [tg] Let Build.sh be run by GNU bash 1.12.1 (Slackware 1.01) + - [Stéphane Chazelas, tg] Fix here string parsing issue + - [tg] Point out more future changes in the manpage + - [tg] Call setgid(2), setegid(2), setuid(2) before seteuid(2) + - [tg] Fix spurious empty line after ENOENT “whence -v”, found by Ypnose + - [tg] Optimise dot.mkshrc and modernise it a bit + - [tg] Use MAXPATHLEN from for PATH_MAX fallback + - [tg] Some code cleanup and warnings fixes + - [tg] Add options -a argv0 and -c to exec + - [jsg] Prevent use-after-free when hitting multiple errors unwinding + - [tg] Fix use of $* and $@ in scalar context: within [[ … ]] and after + case (spotted by Stéphane Chazelas) and in here documents (spotted by + tg@); fix here document expansion + - [tg] Unbreak when $@ shares double quotes with others + - [tg] Fix set -x in PS4 expansion infinite loop + * Update README, Description, copyright and build scripts + * Upload to experimental due to the jessie pre-release freeze + + -- Thorsten Glaser Sun, 01 Mar 2015 16:38:11 +0000 + +mksh (50d-4) unstable; urgency=medium + + * QA upload. + * Backport upstream fix: + - [tg] SECURITY: make unset HISTFILE actually work + * Adjust shell version accordingly + + -- Thorsten Glaser Sat, 07 Mar 2015 22:16:53 +0100 + mksh (50d-3ubuntu1) vivid; urgency=high * Merge from Debian (LP: #1377295), remaining changes: diff -Nru mksh-50d/debian/control mksh-50e/debian/control --- mksh-50d/debian/control 2014-10-29 13:28:09.000000000 +0100 +++ mksh-50e/debian/control 2015-03-08 00:39:05.000000000 +0100 @@ -38,20 +38,28 @@ standards compliance added, and several enhancements (for extended compatibility to other modern shells, as well as a couple of its own) are available. - This shell is Debian Policy 10.4 compliant and may be used as /bin/sh - on Debian systems (the /bin/lksh binary should be used for this), and - as rescue and initrd shell (especially the /bin/mksh-static binary). + This shell is Debian Policy 10.4 compliant and works as /bin/sh on + Debian systems (use the /bin/lksh executable) and is a good rescue + and initrd shell (consider the /bin/mksh-static executable). + . + The mksh binary is a complete, full-featured shell. If called as sh, + it enables a compatibility mode and some more controversial features + from POSIX, but still provides the “consistent across all platforms” + guarantee, using 32-bit integers for arithmetics. . The mksh-static binary is a version of mksh, linked against klibc or dietlibc (if they exist for that Debian architecture and are usable) and optimised for small code size, for example for use on initrd or initramfs images, installation or rescue systems, or /bin/sh on slow - architectures. + architectures. Otherwise, it is similar to the mksh binary. It omits some leaf features to be even smaller. . The lksh binary is a script shell based on mksh intended to run old ksh88 and pdksh scripts, but not for interactive use. When used as - /bin/sh it follows POSIX most closely. + /bin/sh it follows POSIX most closely, including use of the host’s + “long” C data type for arithmetics. It also contains kludges so it + can run as /bin/sh on Debian, where many packages ship buggy/broken + maintainer scripts, such as including a rudimentary printf(1) builtin. . A sample ~/.mkshrc is included in /usr/share/doc/mksh/examples and provided as /etc/mkshrc conffile, which is sourced by another file diff -Nru mksh-50d/debian/copyright mksh-50e/debian/copyright --- mksh-50d/debian/copyright 2014-10-07 18:28:41.000000000 +0200 +++ mksh-50e/debian/copyright 2015-03-08 00:39:05.000000000 +0100 @@ -2,7 +2,7 @@ Sat, 28 May 2005 22:02:17 +0000. It was downloaded from: -https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh-R50c.tgz +https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh-R50e.tgz Licence: @@ -24,7 +24,7 @@ The MirBSD Korn Shell (mksh) is Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012, 2013, 2014 + 2011, 2012, 2013, 2014, 2015 Thorsten “mirabilos” Glaser All rights reserved. diff -Nru mksh-50d/debian/meat mksh-50e/debian/meat --- mksh-50d/debian/meat 2014-10-07 22:49:03.000000000 +0200 +++ mksh-50e/debian/meat 2015-03-08 00:39:05.000000000 +0100 @@ -57,7 +57,7 @@ { echo "Build information for $where mksh" fgrep 'set -A check_categories' builddir/$where/test.sh - echo "From: $startdate" + #echo "From: $startdate" $CC --version 2>&1 | grep ^gcc echo "Result: $buildinfo (broken&1 | sed 's/^/| /' - echo "Date: $(date -u)" + #echo "Date: $(date -u)" } >builddir/buildinfo.$where } trybuild() { @@ -189,6 +189,7 @@ fi buildinfo=regressed resultest=$(grep -a '^[FPT]' utest.log 2>&1 | fgrep -a -v \ + -e 'Trying Perl interpreter' \ -e 'Testing mksh for conformance' \ -e 'This shell is actually') fi @@ -535,7 +536,7 @@ pkgvsn=$(dpkg-parsechangelog -n1 | sed -n '/^Version: */s///p') { cat debian/README.Debian - echo Build information for mksh R${pkgvsn%-*}: + echo Build information for mksh R${pkgvsn%%@(-|wtf)*([!-])}: for v in DEB_BUILD_GNU_TYPE DEB_HOST_GNU_TYPE DEB_BUILD_OPTIONS; do eval x=\$$v echo "| $v='$x'" @@ -568,7 +569,7 @@ cat builddir/buildinfo.legacy echo ---- echo Version: $pkgvsn - date -R + #date -R } | gzip -n9 >builddir/README.Debian.gz echo All builds complete. exit 0 diff -Nru mksh-50d/debian/patches/debian-changes mksh-50e/debian/patches/debian-changes --- mksh-50d/debian/patches/debian-changes 2014-10-23 11:24:49.000000000 +0200 +++ mksh-50e/debian/patches/debian-changes 2015-03-08 00:44:26.000000000 +0100 @@ -1,587 +1,81 @@ Please review changes against upstream code using SCM, see the Vcs-* tags in debian/control for its location. ---- mksh-50d.orig/check.t -+++ mksh-50d/check.t -@@ -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 +--- mksh-50e.orig/check.t ++++ mksh-50e/check.t +@@ -30,7 +30,7 @@ + # (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date expected-stdout: -- @(#)MIRBSD KSH R50 2014/10/07 -+ @(#)MIRBSD KSH R50 2014/10/19 +- @(#)MIRBSD KSH R50 2015/03/01 ++ @(#)MIRBSD KSH R50 2015/03/01 Debian-2 description: Check version of shell. stdin: -@@ -36,7 +36,7 @@ name: KSH_VERSION +@@ -39,7 +39,7 @@ name: KSH_VERSION category: shell:legacy-no --- expected-stdout: -- @(#)LEGACY KSH R50 2014/10/07 -+ @(#)LEGACY KSH R50 2014/10/19 +- @(#)LEGACY KSH R50 2015/03/01 ++ @(#)LEGACY KSH R50 2015/03/01 Debian-2 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 ---- mksh-50d.orig/eval.c -+++ mksh-50d/eval.c -@@ -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; +--- mksh-50e.orig/histrap.c ++++ mksh-50e/histrap.c +@@ -563,7 +563,7 @@ sethistfile(const char *name) + return; + + /* if the name is the same as the name we have */ +- if (hname && strcmp(hname, name) == 0) ++ if (hname && name && !strcmp(hname, name)) + return; + + /* +@@ -581,7 +581,8 @@ sethistfile(const char *name) + hist_source->line = 0; } - 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; ---- mksh-50d.orig/exec.c -+++ mksh-50d/exec.c -@@ -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); +- hist_init(hist_source); ++ if (name) ++ hist_init(hist_source); } - -@@ -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)) ---- mksh-50d.orig/funcs.c -+++ mksh-50d/funcs.c -@@ -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)); - } +@@ -712,8 +713,10 @@ hist_init(Source *s) + hist_source = s; -- 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; ---- mksh-50d.orig/main.c -+++ mksh-50d/main.c -@@ -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); - } ---- mksh-50d.orig/sh.h -+++ mksh-50d/sh.h -@@ -169,9 +169,9 @@ - #endif + #if HAVE_PERSISTENT_HISTORY +- if ((hname = str_val(global("HISTFILE"))) == NULL) ++ if (((hname = str_val(global("HISTFILE"))) == NULL) || !*hname) { ++ hname = NULL; + return; ++ } + strdupx(hname, hname, APERM); + hs = hist_init_first; +--- mksh-50e.orig/sh.h ++++ mksh-50e/sh.h +@@ -171,7 +171,7 @@ #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 $"); + __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.701.2.4 2015/03/01 15:43:05 tg Exp $"); #endif --#define MKSH_VERSION "R50 2014/10/07" -+#define MKSH_VERSION "R50 2014/10/19" +-#define MKSH_VERSION "R50 2015/03/01" ++#define MKSH_VERSION "R50 2015/03/01 Debian-2" /* 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 */ +--- mksh-50e.orig/var.c ++++ mksh-50e/var.c +@@ -1350,6 +1350,11 @@ unsetspec(struct tbl *vp) + */ + switch (special(vp->name)) { ++#if HAVE_PERSISTENT_HISTORY ++ case V_HISTFILE: ++ sethistfile(NULL); ++ return; ++#endif + case V_IFS: + setctypes(TC_IFSWS, C_IFS); + ifs0 = ' '; diff -Nru mksh-50d/dot.mkshrc mksh-50e/dot.mkshrc --- mksh-50d/dot.mkshrc 2014-07-28 23:46:34.000000000 +0200 +++ mksh-50e/dot.mkshrc 2015-01-11 23:40:09.000000000 +0100 @@ -1,8 +1,8 @@ # $Id$ -# $MirOS: src/bin/mksh/dot.mkshrc,v 1.89 2014/07/28 21:45:44 tg Exp $ +# $MirOS: src/bin/mksh/dot.mkshrc,v 1.89.2.1 2015/01/11 22:39:44 tg Exp $ #- # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013, 2014 +# 2011, 2012, 2013, 2014, 2015 # Thorsten Glaser # # Provided that these terms and disclaimer and all copyright notices @@ -67,7 +67,7 @@ local dasc line i cat "$@" | { set +U; if read -arN -1 line; then - typeset -i1 line + typeset -i1 'line[*]' i=0 while (( i < ${#line[*]} )); do hv=${line[i++]} @@ -263,9 +263,8 @@ # base64 encoder and decoder, RFC compliant, NUL safe function Lb64decode { - [[ -o utf8-mode ]]; local u=$? + [[ -o utf8-mode ]]; local u=$? c s="$*" t set +U - local c s="$*" t= [[ -n $s ]] || { s=$(cat; print x); s=${s%x}; } local -i i=0 j=0 n=${#s} p=0 v x local -i16 o @@ -302,9 +301,8 @@ set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + / function Lb64encode { - [[ -o utf8-mode ]]; local u=$? + [[ -o utf8-mode ]]; local u=$? c s t set +U - local c s t if (( $# )); then read -raN-1 s <<<"$*" unset s[${#s[*]}-1] @@ -342,9 +340,8 @@ Lbafh_v=0 } function Lbafh_add { - [[ -o utf8-mode ]]; local u=$? + [[ -o utf8-mode ]]; local u=$? s set +U - local s if (( $# )); then read -raN-1 s <<<"$*" unset s[${#s[*]}-1] diff -Nru mksh-50d/edit.c mksh-50e/edit.c --- mksh-50d/edit.c 2014-07-13 13:35:18.000000000 +0200 +++ mksh-50e/edit.c 2015-03-01 16:43:21.000000000 +0100 @@ -1,6 +1,6 @@ /* $OpenBSD: edit.c,v 1.39 2013/12/17 16:37:05 deraadt Exp $ */ /* $OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $ */ -/* $OpenBSD: emacs.c,v 1.48 2013/12/17 16:37:05 deraadt Exp $ */ +/* $OpenBSD: emacs.c,v 1.49 2015/02/16 01:44:41 tedu Exp $ */ /* $OpenBSD: vi.c,v 1.28 2013/12/18 16:45:46 deraadt Exp $ */ /*- @@ -28,7 +28,7 @@ #ifndef MKSH_NO_CMDLINE_EDITING -__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.276 2014/07/13 11:34:28 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.276.2.3 2015/03/01 15:42:56 tg Exp $"); /* * in later versions we might use libtermcap for this, but since external @@ -327,7 +327,7 @@ /* ok, so split into "~foo"/"bar" or "~"/"baz" */ *cp++ = 0; /* try to expand the tilde */ - if (!(dp = tilde(s + 1))) { + if (!(dp = do_tilde(s + 1))) { /* nope, revert damage */ *--cp = '/'; } else { @@ -592,8 +592,6 @@ char **words = NULL; bool is_command; - mkssert(buf != NULL); - len = x_locate_word(buf, buflen, pos, startp, &is_command); if (!((*flagsp) & XCF_COMMAND)) is_command = false; @@ -1009,7 +1007,6 @@ static const struct x_ftab x_ftab[] = { #define EMACSFN_ITEMS #include "emacsfn.h" - { 0, NULL, 0 } }; static struct x_defbindings const x_defbindings[] = { @@ -2240,7 +2237,6 @@ { char *cp; - mkssert(xcp != NULL); strndupx(cp, xcp, nchars, AEDIT); if (killstack[killsp]) afree(killstack[killsp], AEDIT); @@ -2455,8 +2451,7 @@ /* List function names */ if (list) { for (f = 0; f < NELEM(x_ftab); f++) - if (x_ftab[f].xf_name && - !(x_ftab[f].xf_flags & XF_NOBIND)) + if (!(x_ftab[f].xf_flags & XF_NOBIND)) shprintf("%s\n", x_ftab[f].xf_name); return (0); } @@ -2517,8 +2512,7 @@ #endif } else { for (f = 0; f < NELEM(x_ftab); f++) - if (x_ftab[f].xf_name && - strcmp(x_ftab[f].xf_name, a2) == 0) + if (!strcmp(x_ftab[f].xf_name, a2)) break; if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) { bi_errorf("%s: %s %s", a2, "no such", Tfunction); diff -Nru mksh-50d/eval.c mksh-50e/eval.c --- mksh-50d/eval.c 2014-10-07 17:23:06.000000000 +0200 +++ mksh-50e/eval.c 2015-03-01 16:43:23.000000000 +0100 @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 + * 2011, 2012, 2013, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -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.2.4 2015/03/01 15:42:58 tg Exp $"); /* * string expansion @@ -59,10 +59,11 @@ #define XSUBMID 6 /* middle of expanding ${} */ /* States used for field splitting */ -#define IFS_WORD 0 /* word has chars (or quotes) */ +#define IFS_WORD 0 /* word has chars (or quotes except "$@") */ #define IFS_WS 1 /* have seen IFS white-space */ #define IFS_NWS 2 /* have seen IFS non-white-space */ #define IFS_IWS 3 /* begin of word, ignore IFS WS */ +#define IFS_QUOTE 4 /* beg.w/quote, become IFS_WORD unless "$@" */ static int varsub(Expand *, const char *, const char *, int *, int *); static int comsub(Expand *, const char *, int); @@ -70,7 +71,7 @@ static char *trimsub(char *, char *, int); static void glob(char *, XPtrV *, bool); static void globit(XString *, char **, char *, XPtrV *, int); -static const char *maybe_expand_tilde(const char *, XString *, char **, int); +static const char *maybe_expand_tilde(const char *, XString *, char **, bool); #ifndef MKSH_NOPWNAM static char *homedir(char *); #endif @@ -237,7 +238,7 @@ /* record number of trailing newlines in COMSUB */ int newlines = 0; bool saw_eq, make_magic; - int tilde_ok; + unsigned int tilde_ok; size_t len; char *cp; @@ -246,7 +247,7 @@ /* 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; @@ -290,7 +291,17 @@ c = *sp++; break; case OQUOTE: - word = IFS_WORD; + switch (word) { + case IFS_QUOTE: + /* """something */ + word = IFS_WORD; + break; + case IFS_WORD: + break; + default: + word = IFS_QUOTE; + break; + } tilde_ok = 0; quote = 1; continue; @@ -383,6 +394,8 @@ if (f & DOBLANK) doblank++; tilde_ok = 0; + if (word == IFS_QUOTE && type != XNULLSUB) + word = IFS_WORD; if (type == XBASE) { /* expand? */ if (!st->next) { @@ -516,7 +529,6 @@ /* check for special cases */ d = str_val(st->var); - mkssert(d != NULL); switch (*pat) { case '#': /* anchor at begin */ @@ -622,7 +634,7 @@ case '%': /* ! DOBLANK,DOBRACE,DOTILDE */ f = (f & DONTRUNCOMMAND) | - DOPAT | DOTEMP; + DOPAT | DOTEMP | DOSCALAR; st->quotew = quote = 0; /* * Prepend open pattern (so | @@ -631,7 +643,7 @@ */ if (!Flag(FSH)) { *dp++ = MAGIC; - *dp++ = '@' | 0x80; + *dp++ = 0x80 | '@'; } break; case '=': @@ -664,7 +676,11 @@ 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,10 +720,16 @@ 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; + } else if (quote) { + word = IFS_WORD; + type = XSUB; + } else { + if (dp == Xstring(ds, dp)) + word = IFS_IWS; + type = XNULLSUB; + } if (f & DOBLANK) doblank++; st = st->prev; @@ -743,7 +765,7 @@ 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 +781,12 @@ 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; @@ -799,8 +822,8 @@ type = XBASE; if (f & DOBLANK) { doblank--; - if (dp == Xstring(ds, dp)) - word = IFS_WS; + if (dp == Xstring(ds, dp) && word != IFS_WORD) + word = IFS_IWS; } continue; @@ -834,15 +857,20 @@ continue; } c = ifs0; - if ((f & DOASNFIELD)) { - /* assignment, do not field-split */ + if ((f & DOHEREDOC)) { + /* pseudo-field-split reliably */ + if (c == 0) + c = ' '; + break; + } + if ((f & DOSCALAR)) { + /* do not field-split */ if (x.split) { c = ' '; break; } - if (c == 0) { + if (c == 0) continue; - } } if (c == 0) { if (quote && !x.split) @@ -867,7 +895,7 @@ /* $(<...) failed */ subst_exstat = 1; /* fake EOF */ - c = EOF; + c = -1; } else if (newlines) { /* spit out saved NLs */ c = '\n'; @@ -877,13 +905,13 @@ if (c == '\n') /* save newlines */ newlines++; - if (newlines && c != EOF) { + if (newlines && c != -1) { shf_ungetc(c, x.u.shf); c = '\n'; --newlines; } } - if (c == EOF) { + if (c == -1) { newlines = 0; if (x.u.shf) shf_close(x.u.shf); @@ -911,7 +939,7 @@ * IFS_IWS -/WS w/NWS - * (w means generate a word) */ - if ((word == IFS_WORD) || (c && + if ((word == IFS_WORD) || (word == IFS_QUOTE) || (c && (word == IFS_IWS || word == IFS_NWS) && !ctype(c, C_IFSWS))) { emit_word: @@ -1023,7 +1051,7 @@ tcp = maybe_expand_tilde(sp, &ds, &tdp, - f & DOASNTILDE); + tobool(f & DOASNTILDE)); if (tcp) { if (dp != tdp) word = IFS_WORD; @@ -1659,7 +1687,7 @@ * past the name, otherwise returns 0. */ static const char * -maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign) +maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign) { XString ts; char *dp = *dpp; @@ -1676,7 +1704,7 @@ } *tp = '\0'; r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? - tilde(Xstring(ts, tp)) : NULL; + do_tilde(Xstring(ts, tp)) : NULL; Xfree(ts, tp); if (r) { while (*r) { @@ -1698,7 +1726,7 @@ */ char * -tilde(char *cp) +do_tilde(char *cp) { char *dp = null; @@ -1745,28 +1773,30 @@ static void alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo) { - int count = 0; + unsigned int count = 0; char *brace_start, *brace_end, *comma = NULL; char *field_start; - char *p; + char *p = exp_start; /* search for open brace */ - for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != '{' /*}*/; p += 2) - ; + while ((p = strchr(p, MAGIC)) && p[1] != '{' /*}*/) + p += 2; brace_start = p; /* find matching close brace, if any */ if (p) { comma = NULL; count = 1; - for (p += 2; *p && count; p++) { - if (ISMAGIC(*p)) { - if (*++p == '{' /*}*/) - count++; + p += 2; + while (*p && count) { + if (ISMAGIC(*p++)) { + if (*p == '{' /*}*/) + ++count; else if (*p == /*{*/ '}') --count; else if (*p == ',' && count == 1) comma = p; + ++p; } } } @@ -1795,7 +1825,7 @@ for (p = brace_start + 2; p != brace_end; p++) { if (ISMAGIC(*p)) { if (*++p == '{' /*}*/) - count++; + ++count; else if ((*p == /*{*/ '}' && --count == 0) || (*p == ',' && count == 1)) { char *news; diff -Nru mksh-50d/exec.c mksh-50e/exec.c --- mksh-50d/exec.c 2014-10-03 19:33:01.000000000 +0200 +++ mksh-50e/exec.c 2015-03-01 16:43:24.000000000 +0100 @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 + * 2011, 2012, 2013, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -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.2.2 2015/03/01 15:42:59 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL "/bin/sh" @@ -32,7 +32,7 @@ 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 @@ /* 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 @@ /* 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; @@ -398,7 +403,7 @@ case TCASE: i = 0; - ccp = evalstr(t->str, DOTILDE); + ccp = evalstr(t->str, DOTILDE | DOSCALAR); for (t = t->left; t != NULL && t->type == TPAT; t = t->right) { for (ap = (const char **)t->vars; *ap; ap++) { if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) && @@ -441,7 +446,6 @@ case TEXEC: /* an eval'd TCOM */ - s = t->args[0]; up = makenv(); restoresigs(); cleanup_proc_env(); @@ -455,7 +459,7 @@ if (rv == ENOEXEC) scriptexec(t, (const char **)up); else - errorf("%s: %s", s, cstrerror(rv)); + errorf("%s: %s", t->str, cstrerror(rv)); } Break: exstat = rv & 0xFF; @@ -500,18 +504,21 @@ /* 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; + int optc; + const char *exec_argv0 = NULL; + bool exec_clrenv = false; - /* - * snag the last argument for $_ XXX not the same as AT&T ksh, - * which only seems to set $_ after a newline (but not in - * functions/dot scripts, but in interactive and script) - - * perhaps save last arg here and set it in shell()?. - */ + /* snag the last argument for $_ */ if (Flag(FTALKING) && *(lastp = ap)) { + /* + * XXX not the same as AT&T ksh, which only seems to set $_ + * after a newline (but not in functions/dot scripts, but in + * interactive and script) - perhaps save last arg here and + * set it in shell()?. + */ while (*++lastp) ; /* setstr() can't fail here */ @@ -532,7 +539,7 @@ * 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; @@ -548,10 +555,25 @@ } else if (tp->val.f == c_exec) { if (ap[1] == NULL) break; - ap++; + ksh_getopt_reset(&builtin_opt, GF_ERROR); + while ((optc = ksh_getopt(ap, &builtin_opt, "a:c")) != -1) + switch (optc) { + case 'a': + exec_argv0 = builtin_opt.optarg; + break; + case 'c': + exec_clrenv = true; + /* ensure we can actually do this */ + resetspec = true; + break; + default: + rv = 2; + goto Leave; + } + ap += builtin_opt.optind; flags |= XEXEC; } else if (tp->val.f == c_command) { - int optc, saw_p = 0; + bool saw_p = false; /* * Ugly dealing with options in two places (here @@ -559,8 +581,8 @@ */ ksh_getopt_reset(&builtin_opt, 0); while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') - saw_p = 1; - if (optc != EOF) + saw_p = true; + if (optc != -1) /* command -vV or something */ break; /* don't look for functions */ @@ -579,7 +601,7 @@ * 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,20 +636,21 @@ 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 && - !(tp->flag & FKSH)) { - bourne_function_call = true; + if (!resetspec && tp && tp->type == CFUNC && + !(tp->flag & FKSH)) type_flags = EXPORT; - } else + else type_flags = LOCAL|LOCAL_COPY|EXPORT; } l_assign = e->loc; + if (exec_clrenv) + l_assign->flags |= BF_STOPENV; if (Flag(FEXPORT)) type_flags |= EXPORT; if (Flag(FXTRACE)) @@ -635,7 +658,7 @@ 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; @@ -651,8 +674,6 @@ } /* but assign in there as usual */ typeset(cp, type_flags, 0, 0, 0); - if (bourne_function_call && !(type_flags & EXPORT)) - typeset(cp, LOCAL | LOCAL_COPY | EXPORT, 0, 0, 0); } if (Flag(FXTRACE)) { @@ -684,8 +705,8 @@ /* 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; } @@ -810,14 +831,24 @@ break; } - /* set $_ to programme's full path */ + /* set $_ to program's full path */ /* setstr() can't fail here */ setstr(typeset("_", LOCAL | EXPORT, 0, INTEGER, 0), tp->val.s, KSH_RETURN_ERROR); - if (flags&XEXEC) { + /* to fork, we set up a TEXEC node and call execute */ + texec.type = TEXEC; + /* for vistree/dumptree */ + texec.left = t; + texec.str = tp->val.s; + texec.args = ap; + + /* in this case we do not fork, of course */ + if (flags & XEXEC) { + if (exec_argv0) + texec.args[0] = exec_argv0; j_exit(); - if (!(flags&XBGND) + if (!(flags & XBGND) #ifndef MKSH_UNEMPLOYED || Flag(FMONITOR) #endif @@ -827,12 +858,6 @@ } } - /* to fork we set up a TEXEC node and call execute */ - texec.type = TEXEC; - /* for tprint */ - texec.left = t; - texec.str = tp->val.s; - texec.args = ap; rv = exchild(&texec, flags, xerrok, -1); break; } @@ -849,10 +874,8 @@ { const char *sh; #ifndef MKSH_SMALL - unsigned char *cp; - /* 64 == MAXINTERP in MirBSD */ - char buf[64]; int fd; + unsigned char buf[68]; #endif union mksh_ccphack args, cap; @@ -866,33 +889,35 @@ #ifndef MKSH_SMALL if ((fd = open(tp->str, O_RDONLY | O_BINARY)) >= 0) { - /* read first MAXINTERP octets from file */ - if (read(fd, buf, sizeof(buf)) <= 0) - /* read error -> no good */ - buf[0] = '\0'; + unsigned char *cp; + unsigned short m; + ssize_t n; + + /* read first couple of octets from file */ + n = read(fd, buf, sizeof(buf) - 1); close(fd); + /* read error or short read? */ + if (n < 5) + goto nomagic; + /* terminate buffer */ + buf[n] = '\0'; /* skip UTF-8 Byte Order Mark, if present */ - cp = (unsigned char *)buf; - if ((cp[0] == 0xEF) && (cp[1] == 0xBB) && (cp[2] == 0xBF)) - cp += 3; - /* save begin of shebang for later */ - fd = (char *)cp - buf; /* either 0 or (if BOM) 3 */ - - /* scan for newline (or CR) or NUL _before_ end of buffer */ - while ((size_t)((char *)cp - buf) < sizeof(buf)) - if (*cp == '\0' || *cp == '\n' || *cp == '\r') { - *cp = '\0'; - break; - } else - ++cp; + cp = buf + (n = ((buf[0] == 0xEF) && (buf[1] == 0xBB) && + (buf[2] == 0xBF)) ? 3 : 0); + + /* scan for newline or NUL (end of buffer) */ + while (*cp && *cp != '\n') + ++cp; /* if the shebang line is longer than MAXINTERP, bail out */ - if ((size_t)((char *)cp - buf) >= sizeof(buf)) + if (!*cp) goto noshebang; + /* replace newline by NUL */ + *cp = '\0'; /* restore begin of shebang position (buf+0 or buf+3) */ - cp = (unsigned char *)(buf + fd); - /* bail out if read error (above) or no shebang */ + cp = buf + n; + /* bail out if no shebang magic found */ if ((cp[0] != '#') || (cp[1] != '!')) goto noshebang; @@ -918,22 +943,24 @@ if (*cp) *tp->args-- = (char *)cp; } + goto nomagic; noshebang: - if (buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && - buf[3] == 'F') + m = buf[0] << 8 | buf[1]; + if (m == 0x7F45 && buf[2] == 'L' && buf[3] == 'F') errorf("%s: not executable: %d-bit ELF file", tp->str, - 32 * ((uint8_t)buf[4])); - fd = buf[0] << 8 | buf[1]; - if ((fd == /* OMAGIC */ 0407) || - (fd == /* NMAGIC */ 0410) || - (fd == /* ZMAGIC */ 0413) || - (fd == /* QMAGIC */ 0314) || - (fd == /* ECOFF_I386 */ 0x4C01) || - (fd == /* ECOFF_M68K */ 0x0150 || fd == 0x5001) || - (fd == /* ECOFF_SH */ 0x0500 || fd == 0x0005) || - (fd == /* "MZ" */ 0x4D5A) || - (fd == /* gzip */ 0x1F8B)) - errorf("%s: not executable: magic %04X", tp->str, fd); + 32 * buf[4]); + if ((m == /* OMAGIC */ 0407) || + (m == /* NMAGIC */ 0410) || + (m == /* ZMAGIC */ 0413) || + (m == /* QMAGIC */ 0314) || + (m == /* ECOFF_I386 */ 0x4C01) || + (m == /* ECOFF_M68K */ 0x0150 || m == 0x5001) || + (m == /* ECOFF_SH */ 0x0500 || m == 0x0005) || + (m == /* "MZ" */ 0x4D5A) || + (m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D)) + errorf("%s: not executable: magic %04X", tp->str, m); + nomagic: + ; } #endif args.ro = tp->args; @@ -952,7 +979,7 @@ struct tbl *tp; tp = ktsearch(&builtins, *wp, hash(*wp)); - return (call_builtin(tp, wp, "shcomexec")); + return (call_builtin(tp, wp, "shcomexec", false)); } /* @@ -1001,8 +1028,6 @@ while (/* CONSTCOND */ 1) { tp = findfunc(name, nhash, true); - /* because findfunc:create=true */ - mkssert(tp != NULL); if (tp->flag & ISSET) was_set = true; @@ -1265,22 +1290,22 @@ } 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); } @@ -1467,7 +1492,7 @@ if (yylex(sub) != LWORD) internal_errorf("%s: %s", "herein", "yylex"); source = osource; - ccp = evalstr(yylval.cp, 0); + ccp = evalstr(yylval.cp, DOSCALAR | DOHEREDOC); } if (resbuf == NULL) @@ -1487,7 +1512,7 @@ struct temp *h; int i; - /* ksh -c 'cat << EOF' can cause this... */ + /* ksh -c 'cat <heredoc == NULL) { warningf(true, "%s missing", "here document"); /* special to iosetup(): don't print error */ @@ -1522,7 +1547,7 @@ return (-2); } - if (shf_close(shf) == EOF) { + if (shf_close(shf) == -1) { i = errno; close(fd); warningf(true, "can't %s temporary file %s: %s", @@ -1559,7 +1584,8 @@ 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)) @@ -1709,6 +1735,7 @@ dbteste_getopnd(Test_env *te, Test_op op, bool do_eval) { const char *s = *te->pos.wp; + int flags = DOTILDE | DOSCALAR; if (!s) return (NULL); @@ -1719,11 +1746,9 @@ return (null); if (op == TO_STEQL || op == TO_STNEQ) - s = evalstr(s, DOTILDE | DOPAT); - else - s = evalstr(s, DOTILDE); + flags |= DOPAT; - return (s); + return (evalstr(s, flags)); } static void diff -Nru mksh-50d/expr.c mksh-50e/expr.c --- mksh-50d/expr.c 2014-06-24 21:54:09.000000000 +0200 +++ mksh-50e/expr.c 2015-01-25 16:44:30.000000000 +0100 @@ -1,4 +1,4 @@ -/* $OpenBSD: expr.c,v 1.23 2013/12/17 16:37:06 deraadt Exp $ */ +/* $OpenBSD: expr.c,v 1.24 2014/12/08 14:26:31 otto Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.76 2014/06/24 19:53:19 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.76.2.1 2015/01/25 15:44:05 tg Exp $"); /* the order of these enums is constrained by the order of opinfo[] */ enum token { diff -Nru mksh-50d/funcs.c mksh-50e/funcs.c --- mksh-50d/funcs.c 2014-09-03 21:56:41.000000000 +0200 +++ mksh-50e/funcs.c 2015-01-25 16:36:09.000000000 +0100 @@ -5,7 +5,7 @@ /*- * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - * 2010, 2011, 2012, 2013, 2014 + * 2010, 2011, 2012, 2013, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -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.2.2 2015/01/25 15:35:44 tg Exp $"); #if HAVE_KILLPG /* @@ -550,7 +550,7 @@ if (vflag || (tp->type != CALIAS && tp->type != CEXEC && tp->type != CTALIAS)) shf_puts(id, shl_stdout); - if (vflag) + if (vflag) { switch (tp->type) { case CKEYWD: case CALIAS: @@ -559,11 +559,20 @@ shf_puts(" is a", shl_stdout); break; } + switch (tp->type) { + case CKEYWD: + case CSHELL: + case CTALIAS: + case CEXEC: + shf_putc(' ', shl_stdout); + break; + } + } switch (tp->type) { case CKEYWD: if (vflag) - shf_puts(" reserved word", shl_stdout); + shf_puts("reserved word", shl_stdout); break; case CALIAS: if (vflag) @@ -590,16 +599,17 @@ } break; case CSHELL: - if (vflag) - shprintf("%s %s %s", - (tp->flag & SPEC_BI) ? " special" : null, - "shell", Tbuiltin); + if (vflag) { + if (tp->flag & SPEC_BI) + shf_puts("special ", shl_stdout); + shprintf("%s %s", "shell", Tbuiltin); + } break; case CTALIAS: case CEXEC: if (tp->flag & ISSET) { if (vflag) { - shf_puts(" is ", shl_stdout); + shf_puts("is ", shl_stdout); if (tp->type == CTALIAS) shprintf("a tracked %s%s for ", (tp->flag & EXPORT) ? @@ -609,12 +619,12 @@ shf_puts(tp->val.s, shl_stdout); } else { if (vflag) - shprintf(" %s\n", "not found"); + shf_puts("not found", shl_stdout); rv = 1; } break; default: - shprintf("%s is *GOK*", id); + shf_puts(" is *GOK*", shl_stdout); break; } if (vflag || !rv) @@ -1587,12 +1597,16 @@ 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); } @@ -1813,7 +1827,7 @@ char *cp, *allocd = NULL, *xp; const char *ccp; XString xs; - ptrdiff_t xsave = 0; + size_t xsave = 0; mksh_ttyst tios; bool restore_tios = false; #if HAVE_SELECT @@ -2417,9 +2431,8 @@ 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; @@ -2794,8 +2807,6 @@ for (argc = 0; wp[argc]; argc++) ; - mkssert(argc > 0); - mkssert(wp[0] != NULL); if (strcmp(wp[0], "[") == 0) { if (strcmp(wp[--argc], "]") != 0) { diff -Nru mksh-50d/histrap.c mksh-50e/histrap.c --- mksh-50d/histrap.c 2014-06-09 15:26:43.000000000 +0200 +++ mksh-50e/histrap.c 2015-03-01 16:43:25.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $ */ +/* $OpenBSD: history.c,v 1.40 2014/11/20 15:22:39 tedu Exp $ */ /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2014 + * 2011, 2012, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -27,7 +27,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.134 2014/06/09 13:25:53 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.134.2.3 2015/03/01 15:43:00 tg Exp $"); Trap sigtraps[NSIG + 1]; static struct sigaction Sigact_ign; @@ -303,7 +303,7 @@ for (hp = rflag ? hlast : hfirst; hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) shf_fprintf(shf, "%s\n", *hp); - if (shf_close(shf) == EOF) { + if (shf_close(shf) == -1) { bi_errorf("can't %s temporary file %s: %s", "write", tf->tffn, cstrerror(errno)); return (1); @@ -423,14 +423,14 @@ if (getn(str, &n)) { hp = histptr + (n < 0 ? n : (n - hist_source->line)); - if ((ptrdiff_t)hp < (ptrdiff_t)history) { + if ((size_t)hp < (size_t)history) { if (approx) hp = hist_get_oldest(); else { bi_errorf("%s: %s", str, Tnot_in_history); hp = NULL; } - } else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) { + } else if ((size_t)hp > (size_t)histptr) { if (approx) hp = hist_get_newest(allow_cur); else { @@ -634,7 +634,6 @@ char **hp; char *c, *cp; - mkssert(cmd != NULL); strdupx(c, cmd, APERM); if ((cp = strchr(c, '\n')) != NULL) *cp = '\0'; diff -Nru mksh-50d/jobs.c mksh-50e/jobs.c --- mksh-50d/jobs.c 2014-10-03 14:33:38.000000000 +0200 +++ mksh-50e/jobs.c 2015-01-25 16:44:31.000000000 +0100 @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.105 2014/10/03 12:32:48 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.105.2.1 2015/01/25 15:44:06 tg Exp $"); #if HAVE_KILLPG #define mksh_killpg killpg @@ -1047,7 +1047,7 @@ static void j_set_async(Job *j) { - Job *jl, *oldest; + Job *jl, *oldest; if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE) remove_job(async_job, "async"); @@ -1084,7 +1084,7 @@ static void j_startjob(Job *j) { - Proc *p; + Proc *p; j->flags |= JF_STARTED; for (p = j->proc_list; p->next; p = p->next) @@ -1421,8 +1421,8 @@ static void check_job(Job *j) { - int jstate; - Proc *p; + int jstate; + Proc *p; /* XXX debugging (nasty - interrupt routine using shl_out) */ if (!(j->flags & JF_STARTED)) { @@ -1524,14 +1524,14 @@ static void j_print(Job *j, int how, struct shf *shf) { - Proc *p; - int state; - int status; - int coredumped; - char jobchar = ' '; - char buf[64]; + Proc *p; + int state; + int status; + int coredumped; + char jobchar = ' '; + char buf[64]; const char *filler; - int output = 0; + int output = 0; if (how == JP_PGRP) { /* @@ -1737,8 +1737,8 @@ static Job * new_job(void) { - int i; - Job *newj, *j; + int i; + Job *newj, *j; if (free_jobs != NULL) { newj = free_jobs; @@ -1766,7 +1766,7 @@ static Proc * new_proc(void) { - Proc *p; + Proc *p; if (free_procs != NULL) { p = free_procs; @@ -1786,10 +1786,9 @@ static void remove_job(Job *j, const char *where) { - Proc *p, *tmp; - Job **prev, *curr; + Proc *p, *tmp; + Job **prev, *curr; - mkssert(j != NULL); prev = &job_list; curr = job_list; while (curr && curr != j) { @@ -1830,9 +1829,8 @@ static void put_job(Job *j, int where) { - Job **prev, *curr; + Job **prev, *curr; - mkssert(j != NULL); /* Remove job from list (if there) */ prev = &job_list; curr = job_list; @@ -1869,8 +1867,8 @@ static int kill_job(Job *j, int sig) { - Proc *p; - int rval = 0; + Proc *p; + int rval = 0; for (p = j->proc_list; p != NULL; p = p->next) if (p->pid != 0) diff -Nru mksh-50d/lalloc.c mksh-50e/lalloc.c --- mksh-50d/lalloc.c 2013-06-04 00:29:23.000000000 +0200 +++ mksh-50e/lalloc.c 2015-01-25 16:36:12.000000000 +0100 @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009, 2010, 2011, 2013 + * Copyright (c) 2009, 2010, 2011, 2013, 2014 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -20,7 +20,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.20 2013/06/03 22:28:33 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.20.2.1 2015/01/25 15:35:47 tg Exp $"); /* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */ #if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0) @@ -29,7 +29,7 @@ #define remalloc(p,n) realloc_osi((p), (n)) #endif -#define ALLOC_ISUNALIGNED(p) (((ptrdiff_t)(p)) % ALLOC_SIZE) +#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % ALLOC_SIZE) static ALLOC_ITEM *findptr(ALLOC_ITEM **, char *, Area *); diff -Nru mksh-50d/lex.c mksh-50e/lex.c --- mksh-50d/lex.c 2014-06-29 13:29:18.000000000 +0200 +++ mksh-50e/lex.c 2015-01-11 23:40:15.000000000 +0100 @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193 2014/06/29 11:28:28 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193.2.1 2015/01/11 22:39:50 tg Exp $"); /* * states while lexing word @@ -854,7 +854,7 @@ *dp = '\0'; /* store the quoted string */ *wp++ = OQUOTE; - XcheckN(ws, wp, (dp - sp)); + XcheckN(ws, wp, (dp - sp) * 2); dp = sp; while ((c = *dp++)) { if (c == '\\') { @@ -1027,17 +1027,24 @@ /* copy word to unprefixed string ident */ sp = yylval.cp; dp = ident; - if ((cf & HEREDELIM) && (sp[1] == '<')) - while ((dp - ident) < IDENT) { - if ((c = *sp++) == CHAR) - *dp++ = *sp++; - else if ((c != OQUOTE) && (c != CQUOTE)) - break; + if ((cf & HEREDELIM) && (sp[1] == '<')) { + herestringloop: + switch ((c = *sp++)) { + case CHAR: + ++sp; + /* FALLTHROUGH */ + case OQUOTE: + case CQUOTE: + goto herestringloop; + default: + break; } - else + /* dummy value */ + *dp++ = 'x'; + } else while ((dp - ident) < IDENT && (c = *sp++) == CHAR) *dp++ = *sp++; - /* Make sure the ident array stays '\0' padded */ + /* make sure the ident array stays NUL padded */ memset(dp, 0, (ident + IDENT) - dp + 1); if (c != EOS) /* word is not unquoted */ diff -Nru mksh-50d/main.c mksh-50e/main.c --- mksh-50d/main.c 2014-10-03 19:20:17.000000000 +0200 +++ mksh-50e/main.c 2015-03-01 16:43:26.000000000 +0100 @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.54 2013/11/28 10:33:37 sobrado Exp $ */ +/* $OpenBSD: main.c,v 1.55 2015/02/09 09:09:30 jsg Exp $ */ /* $OpenBSD: tty.c,v 1.10 2014/08/10 02:44:26 guenther Exp $ */ /* $OpenBSD: io.c,v 1.25 2014/08/11 20:28:47 guenther Exp $ */ /* $OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $ */ @@ -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.2.2 2015/03/01 15:43:01 tg Exp $"); extern char **environ; @@ -892,6 +892,13 @@ /* FALLTHROUGH */ default: quitenv(NULL); + /* + * quitenv() may have reclaimed the memory + * used by source which will end badly when + * we jump to a function that expects it to + * be valid + */ + source = NULL; } } } @@ -1014,8 +1021,6 @@ struct env *ep; int fd; - mkssert(e != NULL); - /* * Don't clean up temporary files - parent will probably need them. * Also, can't easily reclaim memory since variables, etc. could be @@ -1244,7 +1249,7 @@ * 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); } diff -Nru mksh-50d/misc.c mksh-50e/misc.c --- mksh-50d/misc.c 2014-01-05 22:58:17.000000000 +0100 +++ mksh-50e/misc.c 2015-03-01 16:43:27.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: misc.c,v 1.38 2013/11/28 10:33:37 sobrado Exp $ */ +/* $OpenBSD: misc.c,v 1.39 2015/01/16 06:39:32 deraadt Exp $ */ /* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 + * 2011, 2012, 2013, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -30,7 +30,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.219 2014/01/05 21:57:27 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.219.2.2 2015/03/01 15:43:02 tg Exp $"); #define KSH_CHVT_FLAG #ifdef MKSH_SMALL @@ -281,15 +281,15 @@ #endif DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid)); #else /* !HAVE_SETRESUGID */ - /* seteuid, setegid, setgid don't EAGAIN on Linux */ + /* setgid, setegid, seteuid don't EAGAIN on Linux */ + setgid(kshegid); #ifndef MKSH__NO_SETEUGID - seteuid(ksheuid); + setegid(kshegid); #endif DO_SETUID(setuid, (ksheuid)); #ifndef MKSH__NO_SETEUGID - setegid(kshegid); + seteuid(ksheuid); #endif - setgid(kshegid); #endif /* !HAVE_SETRESUGID */ } else if ((f == FPOSIX || f == FSH) && newval) { /* Turning on -o posix or -o sh? */ @@ -304,6 +304,11 @@ void change_xtrace(unsigned char newval, bool dosnapshot) { + static bool in_xtrace; + + if (in_xtrace) + return; + if (!dosnapshot && newval == Flag(FXTRACE)) return; @@ -328,8 +333,13 @@ shl_xtrace->fd = 2; changed_xtrace: - if ((Flag(FXTRACE) = newval) == 2) + if ((Flag(FXTRACE) = newval) == 2) { + in_xtrace = true; + Flag(FXTRACE) = 0; shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace); + Flag(FXTRACE) = 2; + in_xtrace = false; + } } /* @@ -485,7 +495,6 @@ if (arrayset) { const char *ccp = NULL; - mkssert(array != NULL); if (*array) ccp = skip_varname(array, false); if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) { @@ -1384,8 +1393,7 @@ { char *xp, *ip, *tp, *ipath, *ldest = NULL; XString xs; - ptrdiff_t pos; - size_t len; + size_t pos, len; int llen; struct stat sb; #ifdef MKSH__NO_PATH_MAX diff -Nru mksh-50d/mksh.1 mksh-50e/mksh.1 --- mksh-50d/mksh.1 2014-10-07 17:31:02.000000000 +0200 +++ mksh-50e/mksh.1 2015-03-01 16:43:28.000000000 +0100 @@ -1,9 +1,9 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.344 2014/10/07 15:30:12 tg Exp $ -.\" $OpenBSD: ksh.1,v 1.153 2014/08/17 07:15:41 jmc Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.344.2.3 2015/03/01 15:43:03 tg Exp $ +.\" $OpenBSD: ksh.1,v 1.156 2015/01/16 15:32:32 schwarze Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, -.\" 2010, 2011, 2012, 2013, 2014 -.\" Thorsten Glaser +.\" 2010, 2011, 2012, 2013, 2014, 2015 +.\" Thorsten “mirabilos” Glaser .\" .\" Provided that these terms and disclaimer and all copyright notices .\" are retained or reproduced in an accompanying document, permission @@ -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: October 7 2014 $ +.Dd $Mdocdate: March 1 2015 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -673,11 +673,11 @@ .It Xo case Ar word No in .Oo Op \&( .Ar pattern -.Op \*(Ba Ar pat +.Op \*(Ba Ar pattern .No ... Ns ) .Ar list -.Op ;; \*(Ba ;&\& \*(Ba ;\*(Ba\ \& -.Oc ... esac +.Ic terminator +.Oc No ... esac .Xc The .Ic case @@ -707,9 +707,11 @@ and .Ic esac e.g.\& -.Ic case $foo { *) echo bar;; } . +.Ic case $foo { *) echo bar ;; } . .Pp -The list terminators are: +The list +.Ic terminator Ns s +are: .Bl -tag -width 4n .It Ql ;; Terminate after the list. @@ -2923,6 +2925,10 @@ .It The EXIT trap, if set in a function, will be executed after the function returns. +.It +Shell options +.Pq Ic set Fl o +have local scope, i.e. changes inside a function are reset upon its exit. .El .Ss Command execution After evaluation of command-line arguments, redirections, and parameter @@ -3370,9 +3376,25 @@ .Pp .It Xo .Ic exec +.Op Fl a Ar argv0 +.Op Fl c .Op Ar command Op Ar arg ... .Xc The command is executed without forking, replacing the shell process. +This is currently absolute, i.e.\& +.Ic exec +never returns, even if the +.Ar command +is not found. +The +.Fl a +option permits setting a different +.Li argv[0] +value, and +.Fl c +clears the environment before executing the child process, except for the +.Ev _ +variable and direct assignments. .Pp If no command is given except for I/O redirection, the I/O redirection is permanent and the shell is @@ -6457,6 +6479,17 @@ instead, but be aware that, in POSIX, it's legal for the OS to make .Li print $((2147483647 + 1)) delete all files on your system, as it's Undefined Behaviour. +.Pp +.Nm mksh +provides a consistent, clear interface normally. +This may deviate from POSIX in optional or opinionated places, such +as whether leading-digit-zero numbers should be interpreted as octal. +.Ic set \-o posix +will cause the shell (either +.Nm mksh +or +.Nm lksh ) +to behave more like the standard expects. .Sh BUGS Suspending (using \*(haZ) pipelines like the one below will only suspend the currently running part of the pipeline; in this example, @@ -6468,16 +6501,19 @@ .Ed .Pp This document attempts to describe -.Nm mksh\ R50 +.Nm mksh\ R50e and up, compiled without any options impacting functionality, such as .Dv MKSH_SMALL , when not called as .Pa /bin/sh which, on some systems only, enables +.Ic set \-o posix +or .Ic set \-o sh automatically (whose behaviour differs across targets), for an operating environment supporting all of its advanced needs. +.Pp Please report bugs in .Nm to the diff -Nru mksh-50d/sh.h mksh-50e/sh.h --- mksh-50d/sh.h 2014-10-07 17:23:07.000000000 +0200 +++ mksh-50e/sh.h 2015-03-01 16:43:30.000000000 +0100 @@ -10,7 +10,7 @@ /*- * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 + * 2011, 2012, 2013, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -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.2.4 2015/03/01 15:43:05 tg Exp $"); #endif -#define MKSH_VERSION "R50 2014/10/07" +#define MKSH_VERSION "R50 2015/03/01" /* arithmetic types: C implementation */ #if !HAVE_CAN_INTTYPES @@ -312,9 +312,13 @@ #undef PATH_MAX #else #ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else #define PATH_MAX 1024 #endif #endif +#endif #ifndef SIZE_MAX #ifdef SIZE_T_MAX #define SIZE_MAX SIZE_T_MAX @@ -533,7 +537,7 @@ #define mkssert(e) do { } while (/* CONSTCOND */ 0) #endif -#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 504) +#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 505) #error Must run Build.sh to compile this. extern void thiswillneverbedefinedIhope(void); int @@ -992,8 +996,8 @@ /* 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; @@ -1233,6 +1237,7 @@ /* Values for struct block.flags */ #define BF_DOGETOPTS BIT(0) /* save/restore getopts state */ +#define BF_STOPENV BIT(1) /* do not export further */ /* * Used by ktwalk() and ktnext() routines. @@ -1396,7 +1401,8 @@ #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 DOHEREDOC BIT(13) /* change scalar handling to heredoc body */ #define X_EXTRA 20 /* this many extra bytes in X string */ @@ -1645,7 +1651,7 @@ char *debunk(char *, const char *, size_t); void expand(const char *, XPtrV *, int); int glob_str(char *, XPtrV *, bool); -char *tilde(char *); +char *do_tilde(char *); /* exec.c */ int execute(struct op * volatile, volatile int, volatile int * volatile); int shcomexec(const char **); diff -Nru mksh-50d/shf.c mksh-50e/shf.c --- mksh-50d/shf.c 2013-10-09 14:00:20.000000000 +0200 +++ mksh-50e/shf.c 2015-03-01 16:43:32.000000000 +0100 @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, - * 2012, 2013 + * 2012, 2013, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -25,7 +25,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.62 2013/10/09 11:59:30 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.62.2.2 2015/03/01 15:43:07 tg Exp $"); /* flags to shf_emptybuf() */ #define EB_READSW 0x01 /* about to switch to reading */ @@ -232,7 +232,7 @@ if (shf->fd >= 0) { ret = shf_flush(shf); if (close(shf->fd) < 0) - ret = EOF; + ret = -1; } if (shf->flags & SHF_ALLOCS) afree(shf, shf->areap); @@ -251,7 +251,7 @@ if (shf->fd >= 0) { ret = shf_flush(shf); if (close(shf->fd) < 0) - ret = EOF; + ret = -1; shf->rnleft = 0; shf->rp = shf->buf; shf->wnleft = 0; @@ -283,20 +283,20 @@ /* * Un-read what has been read but not examined, or write what has been - * buffered. Returns 0 for success, EOF for (write) error. + * buffered. Returns 0 for success, -1 for (write) error. */ int shf_flush(struct shf *shf) { if (shf->flags & SHF_STRING) - return ((shf->flags & SHF_WR) ? EOF : 0); + return ((shf->flags & SHF_WR) ? -1 : 0); if (shf->fd < 0) internal_errorf("%s: %s", "shf_flush", "no fd"); if (shf->flags & SHF_ERROR) { errno = shf->errnosv; - return (EOF); + return (-1); } if (shf->flags & SHF_READING) { @@ -315,7 +315,7 @@ /* * Write out any buffered data. If currently reading, flushes the read - * buffer. Returns 0 for success, EOF for (write) error. + * buffer. Returns 0 for success, -1 for (write) error. */ static int shf_emptybuf(struct shf *shf, int flags) @@ -327,7 +327,7 @@ if (shf->flags & SHF_ERROR) { errno = shf->errnosv; - return (EOF); + return (-1); } if (shf->flags & SHF_READING) { @@ -347,7 +347,7 @@ */ if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) || !(shf->flags & SHF_ALLOCB)) - return (EOF); + return (-1); /* allocate more space for buffer */ nbuf = aresize2(shf->buf, 2, shf->wbsize, shf->areap); shf->rp = nbuf + (shf->rp - shf->buf); @@ -379,7 +379,7 @@ ntowrite); shf->wp = shf->buf + ntowrite; } - return (EOF); + return (-1); } buf += n; ntowrite -= n; @@ -399,7 +399,7 @@ return (ret); } -/* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */ +/* Fill up a read buffer. Returns -1 for a read error, 0 otherwise. */ static int shf_fillbuf(struct shf *shf) { @@ -414,11 +414,11 @@ if (shf->flags & (SHF_EOF | SHF_ERROR)) { if (shf->flags & SHF_ERROR) errno = shf->errnosv; - return (EOF); + return (-1); } - if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF) - return (EOF); + if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == -1) + return (-1); shf->flags |= SHF_READING; @@ -434,7 +434,7 @@ shf->errnosv = errno; shf->rnleft = 0; shf->rp = shf->buf; - return (EOF); + return (-1); } if ((shf->rnleft = n) == 0) shf->flags |= SHF_EOF; @@ -443,7 +443,7 @@ /* * Read a buffer from shf. Returns the number of bytes read into buf, if - * no bytes were read, returns 0 if end of file was seen, EOF if a read + * no bytes were read, returns 0 if end of file was seen, -1 if a read * error occurred. */ ssize_t @@ -459,7 +459,7 @@ while (bsize > 0) { if (shf->rnleft == 0 && - (shf_fillbuf(shf) == EOF || shf->rnleft == 0)) + (shf_fillbuf(shf) == -1 || shf->rnleft == 0)) break; ncopy = shf->rnleft; if (ncopy > bsize) @@ -471,12 +471,12 @@ shf->rnleft -= ncopy; } /* Note: fread(3S) returns 0 for errors - this doesn't */ - return (orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) : + return (orig_bsize == bsize ? (shf_error(shf) ? -1 : 0) : orig_bsize - bsize); } /* - * Read up to a newline or EOF. The newline is put in buf; buf is always + * Read up to a newline or -1. The newline is put in buf; buf is always * NUL terminated. Returns NULL on read error or if nothing was read * before end of file, returns a pointer to the NUL byte in buf * otherwise. @@ -498,7 +498,7 @@ --bsize; do { if (shf->rnleft == 0) { - if (shf_fillbuf(shf) == EOF) + if (shf_fillbuf(shf) == -1) return (NULL); if (shf->rnleft == 0) { *buf = '\0'; @@ -520,22 +520,22 @@ return (buf); } -/* Returns the char read. Returns EOF for error and end of file. */ +/* Returns the char read. Returns -1 for error and end of file. */ int shf_getchar(struct shf *shf) { if (!(shf->flags & SHF_RD)) internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags); - if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0)) - return (EOF); + if (shf->rnleft == 0 && (shf_fillbuf(shf) == -1 || shf->rnleft == 0)) + return (-1); --shf->rnleft; return (*shf->rp++); } /* * Put a character back in the input stream. Returns the character if - * successful, EOF if there is no room. + * successful, -1 if there is no room. */ int shf_ungetc(int c, struct shf *shf) @@ -543,12 +543,12 @@ if (!(shf->flags & SHF_RD)) internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags); - if ((shf->flags & SHF_ERROR) || c == EOF || + if ((shf->flags & SHF_ERROR) || c == -1 || (shf->rp == shf->buf && shf->rnleft)) - return (EOF); + return (-1); - if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF) - return (EOF); + if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == -1) + return (-1); if (shf->rp == shf->buf) shf->rp = shf->buf + shf->rbsize; @@ -558,7 +558,7 @@ * we don't want to modify a string. */ if ((int)(shf->rp[-1]) != c) - return (EOF); + return (-1); shf->flags &= ~SHF_EOF; shf->rp--; shf->rnleft++; @@ -571,7 +571,7 @@ } /* - * Write a character. Returns the character if successful, EOF if the + * Write a character. Returns the character if successful, -1 if the * char could not be written. */ int @@ -580,8 +580,8 @@ if (!(shf->flags & SHF_WR)) internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags); - if (c == EOF) - return (EOF); + if (c == -1) + return (-1); if (shf->flags & SHF_UNBUF) { unsigned char cc = (unsigned char)c; @@ -591,7 +591,7 @@ internal_errorf("%s: %s", "shf_putchar", "no fd"); if (shf->flags & SHF_ERROR) { errno = shf->errnosv; - return (EOF); + return (-1); } while ((n = write(shf->fd, &cc, 1)) != 1) if (n < 0) { @@ -600,12 +600,12 @@ continue; shf->flags |= SHF_ERROR; shf->errnosv = errno; - return (EOF); + return (-1); } } else { /* Flush deals with strings and sticky errors */ - if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF) - return (EOF); + if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == -1) + return (-1); shf->wnleft--; *shf->wp++ = c; } @@ -614,19 +614,19 @@ } /* - * Write a string. Returns the length of the string if successful, EOF + * Write a string. Returns the length of the string if successful, -1 * if the string could not be written. */ ssize_t shf_puts(const char *s, struct shf *shf) { if (!s) - return (EOF); + return (-1); return (shf_write(s, strlen(s), shf)); } -/* Write a buffer. Returns nbytes if successful, EOF if there is an error. */ +/* Write a buffer. Returns nbytes if successful, -1 if there is an error. */ ssize_t shf_write(const char *buf, ssize_t nbytes, struct shf *shf) { @@ -653,13 +653,13 @@ if (shf->flags & SHF_STRING) { /* resize buffer until there's enough space left */ while (nbytes > shf->wnleft) - if (shf_emptybuf(shf, EB_GROW) == EOF) - return (EOF); + if (shf_emptybuf(shf, EB_GROW) == -1) + return (-1); /* then write everything into the buffer */ } else { /* flush deals with sticky errors */ - if (shf_emptybuf(shf, EB_GROW) == EOF) - return (EOF); + if (shf_emptybuf(shf, EB_GROW) == -1) + return (-1); /* write chunks larger than window size directly */ if (nbytes > shf->wbsize) { ncopy = nbytes; @@ -679,7 +679,7 @@ * Note: fwrite(3) returns 0 * for errors - this doesn't */ - return (EOF); + return (-1); } buf += n; ncopy -= n; @@ -767,12 +767,7 @@ ssize_t field, precision, len; unsigned long lnum; /* %#o produces the longest output */ - char numbuf[(8 * sizeof(long) + 2) / 3 + 1 -#ifdef DEBUG - /* a NUL for LLVM/Clang scan-build */ - + 1 -#endif - ]; + char numbuf[(8 * sizeof(long) + 2) / 3 + 1]; /* this stuff for dealing with the buffer */ ssize_t nwritten = 0; @@ -910,16 +905,6 @@ integral: flags |= FL_NUMBER; cp = numbuf + sizeof(numbuf); -#ifdef DEBUG - /* - * this is necessary so Clang 3.2 realises - * utf_skipcols/shf_putc in the output loop - * terminate; these values are always ASCII - * so an out-of-bounds access cannot happen - * but Clang doesn't know that - */ - *--cp = '\0'; -#endif switch (c) { case 'd': @@ -971,10 +956,6 @@ } } len = numbuf + sizeof(numbuf) - (s = cp); -#ifdef DEBUG - /* see above comment for Clang 3.2 */ - --len; -#endif if (flags & FL_DOT) { if (precision > len) { field = precision; @@ -1068,7 +1049,7 @@ } } - return (shf_error(shf) ? EOF : nwritten); + return (shf_error(shf) ? -1 : nwritten); } #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST) diff -Nru mksh-50d/syn.c mksh-50e/syn.c --- mksh-50d/syn.c 2014-01-05 22:58:19.000000000 +0100 +++ mksh-50e/syn.c 2015-01-25 16:36:19.000000000 +0100 @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.94 2014/01/05 21:57:29 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.94.2.1 2015/01/25 15:35:54 tg Exp $"); struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ @@ -196,10 +196,11 @@ musthave(LWORD, ishere ? HEREDELIM : 0); if (ishere) { iop->delim = yylval.cp; - if (*ident != 0) + if (*ident != 0) { /* unquoted */ gotnulldelim: iop->flag |= IOEVAL; + } if (herep > &heres[HERES - 1]) yyerror("too many %ss\n", "<<"); *herep++ = iop; diff -Nru mksh-50d/var.c mksh-50e/var.c --- mksh-50d/var.c 2014-10-04 13:48:09.000000000 +0200 +++ mksh-50e/var.c 2015-03-01 16:43:32.000000000 +0100 @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 + * 2011, 2012, 2013, 2014, 2015 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -28,7 +28,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/var.c,v 1.183 2014/10/04 11:47:19 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/var.c,v 1.183.2.2 2015/03/01 15:43:07 tg Exp $"); /*- * Variables @@ -308,7 +308,6 @@ * dereference namerefs; must come first */ n = array_index_calc(n, &array, &val); - mkssert(n != NULL); h = hash(n); if (!ksh_isalphx(*n)) { vp = &vtemp; @@ -679,8 +678,6 @@ char *op = (vp->flag&ALLOC) ? vp->val.s : NULL; size_t namelen, vallen; - mkssert(val != NULL); - namelen = strlen(vp->name); vallen = strlen(val) + 1; @@ -1114,6 +1111,8 @@ } XPput(denv, vp->val.s); } + if (l->flags & BF_STOPENV) + break; } XPput(denv, NULL); return ((char **)XPclose(denv));