From a3c3f96fb829bbed2d01b359890bf8b729fa5c54 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 12 Apr 2017 16:52:30 -0700 Subject: [PATCH] Upgrade to mksh R55. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R55 is mostly a feature release with summary bugfixes: [komh] Fix OS/2 search_access() and UNC path logic [tg] Undocument printf(1) to avoid user confusion [Jean Delvare, tg] Fix printf builtin -R option [tg] Make ${var@x}, unknown x, fail (thanks izabera) [tg] ${var=x} must evaluate x in scalar context (10x Martijn Dekker) [tg] Fixup relation between lksh and mksh, reduce delta [tg] Improve manpage display; add OS/2 $PATH FAQ [Jean Delvare] Fix bugs in manpage [tg] Review tilde expansion, removing “odd use of KEEPASN” and introduce POSIX “declaration utility” concept; wait isn’t one [tg] Add \builtin utility, declaration utility forwarder [tg] Make $'\xz' expand to xz, not \0 [tg] Use fixed string pooling (requires the above change in host mksh) [tg] POSIX declaration commands can have varassign and redirections [Martijn Dekker] Add typeset -g, replacing homegrown “global” [Harvey-OS] Disable NOPROSPECTOFWORK, APEX is reportedly fixed now [tg] Display ulimit -a output with flags; improve Haiku [tg] Drop old let] hack, use \builtin internally [tg] Fix padding in Lb64encode in dot.mkshrc [tg] Move FAQ content to a separate, new FAQ section in the manpage [tg] Add new standard variable PATHSEP (‘:’, ‘;’ on OS/2) [Martijn Dekker] Fix LINENO in eval and alias [komh] Fix “\builtin” on OS/2 [tg] Improve (internal) character classes code for speed [tg] Fix: the underscore is no drive letter [tg] No longer hard-disable persistent history support in lksh [tg] Introduce build flag -T for enabling “textmode” on OS/2 (supporting CR+LF line endings, but incompatible with mksh proper) [tg] Merge mksh-os2 [tg] Permit changing $OS2_SHELL during a running shell [tg] Fix multibyte handling in ^R (Emacs search-history) [tg] Allow “typeset -p arrname[2]” to work [tg] Make some error messages more consistent [tg, komh] Disable UTF-8 detection code for OS/2 as unrealistic [tg, sdaoden] Limit alias name chars to POSIX plus non-leading ‘-’ [tg, Martijn Dekker] Expand aliases at COMSUB parse time [tg] Make “typeset -f” output alias-resistent [tg, Martijn Dekker] Permit “eval break” and “eval continue” [tg] Make -masm=intel safe on i386 [tg] Disambiguate $((…)) vs. $((…)…) in “typeset -f” output [Jean Delvare] Clarify the effect of exit and return in a subshell [tg] Simplify compile-time asserts and make them actually compile-time [tg] Fix ^O in Emacs mode if the line was modified (LP#1675842) [tg] Address Coverity Scan… stuff… now that it builds again [Martijn Dekker, tg] Add test -v [tg] Document set -o posix/sh completely Bug: N/A Test: manual Change-Id: Ifce1d879933a5773e98b4f34f4a9bb86a6bdff3b --- Android.mk | 2 +- src/Build.sh | 114 +++---- src/check.t | 834 +++++++++++++++++++++++++++++++---------------- src/dot.mkshrc | 443 ++++++++++++------------- src/edit.c | 588 +++++++++++++++++---------------- src/eval.c | 103 ++++-- src/exec.c | 90 ++++- src/expr.c | 15 +- src/funcs.c | 551 ++++++------------------------- src/histrap.c | 8 +- src/lex.c | 30 +- src/lksh.1 | 88 ++--- src/main.c | 138 +++++--- src/misc.c | 42 ++- src/mksh.1 | 591 ++++++++++++++++++++------------- src/os2.c | 557 +++++++++++++++++++++++++++++++ src/sh.h | 236 +++++++++----- src/sh_flags.gen | 16 +- src/sh_flags.opt | 16 +- src/shf.c | 48 ++- src/syn.c | 190 +++++------ src/tree.c | 33 +- src/var.c | 415 ++++++++++++++++++++++- 23 files changed, 3184 insertions(+), 1964 deletions(-) mode change 100755 => 100644 src/Build.sh create mode 100644 src/os2.c diff --git a/Android.mk b/Android.mk index 427aea9..5e7c633 100644 --- a/Android.mk +++ b/Android.mk @@ -83,6 +83,6 @@ LOCAL_CFLAGS += \ -DHAVE_SETGROUPS=1 -DHAVE_STRERROR=1 -DHAVE_STRSIGNAL=0 \ -DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \ -DHAVE_SYS_ERRLIST_DECL=0 -DHAVE_SYS_SIGLIST_DECL=1 \ - -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=541 + -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=551 include $(BUILD_EXECUTABLE) diff --git a/src/Build.sh b/src/Build.sh old mode 100755 new mode 100644 index d0ff130..ca88a06 --- a/src/Build.sh +++ b/src/Build.sh @@ -1,8 +1,8 @@ #!/bin/sh -srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.707 2016/11/11 23:31:29 tg Exp $' +srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.716 2017/04/12 18:33:22 tg Exp $' #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013, 2014, 2015, 2016 +# 2011, 2012, 2013, 2014, 2015, 2016, 2017 # mirabilos # # Provided that these terms and disclaimer and all copyright notices @@ -495,6 +495,7 @@ check_categories= last= tfn= legacy=0 +textmode=0 for i do @@ -551,6 +552,12 @@ do :-r) r=1 ;; + :-T) + textmode=1 + ;; + :+T) + textmode=0 + ;; :-t) last=t ;; @@ -586,16 +593,21 @@ fi rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \ *.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out -SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c" +SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c" SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c" if test $legacy = 0; then - SRCS="$SRCS edit.c" check_categories="$check_categories shell:legacy-no int:32" else check_categories="$check_categories shell:legacy-yes" add_cppflags -DMKSH_LEGACY_MODE - HAVE_PERSISTENT_HISTORY=0 +fi + +if test $textmode = 0; then + check_categories="$check_categories shell:textmode-no shell:binmode-yes" +else + check_categories="$check_categories shell:textmode-yes shell:binmode-no" + add_cppflags -DMKSH_WITH_TEXTMODE fi if test x"$srcdir" = x"."; then @@ -766,7 +778,6 @@ Harvey) add_cppflags -DMKSH__NO_SETEUGID oswarn=' and will currently not work' add_cppflags -DMKSH_UNEMPLOYED - add_cppflags -DMKSH_NOPROSPECTOFWORK # these taken from Harvey-OS github and need re-checking add_cppflags -D_setjmp=setjmp -D_longjmp=longjmp : "${HAVE_CAN_NO_EH_FRAME=0}" @@ -849,16 +860,39 @@ OpenBSD) : "${HAVE_SETLOCALE_CTYPE=0}" ;; OS/2) + add_cppflags -DMKSH_ASSUME_UTF8=0; HAVE_ISSET_MKSH_ASSUME_UTF8=1 HAVE_TERMIOS_H=0 HAVE_MKNOD=0 # setmode() incompatible - oswarn="; it is currently being ported, get it from" - oswarn="$oswarn${nl}https://github.com/komh/mksh-os2 in the meanwhile" + oswarn="; it is being ported" check_categories="$check_categories nosymlink" : "${CC=gcc}" : "${SIZE=: size}" + SRCS="$SRCS os2.c" add_cppflags -DMKSH_UNEMPLOYED add_cppflags -DMKSH_NOPROSPECTOFWORK add_cppflags -DMKSH_NO_LIMITS + add_cppflags -DMKSH_DOSPATH + if test $textmode = 0; then + x='dis' + y='standard OS/2 tools' + else + x='en' + y='standard Unix mksh and other tools' + fi + echo >&2 " +OS/2 Note: mksh can be built with or without 'textmode'. +Without 'textmode' it will behave like a standard Unix utility, +compatible to mksh on all other platforms, using only ASCII LF +(0x0A) as line ending character. This is supported by the mksh +upstream developer. +With 'textmode', mksh will be modified to behave more like other +OS/2 utilities, supporting ASCII CR+LF (0x0D 0x0A) as line ending +at the cost of deviation from standard mksh. This is supported by +the mksh-os2 porter. + +] You are currently compiling with textmode ${x}abled, introducing +] incompatibilities with $y. +" ;; OSF1) HAVE_SIG_T=0 # incompatible @@ -2152,68 +2186,6 @@ test 1 = $fv || check_categories="$check_categories no-histfile" ac_testdone ac_cppflags -save_CFLAGS=$CFLAGS -ac_testn compile_time_asserts_$$ '' 'whether compile-time assertions pass' <<-'EOF' - #define MKSH_INCLUDES_ONLY - #include "sh.h" - #ifndef CHAR_BIT - #define CHAR_BIT 8 /* defuse this test on really legacy systems */ - #endif - struct ctasserts { - #define cta(name, assertion) char name[(assertion) ? 1 : -1] -/* this one should be defined by the standard */ -cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) && - (sizeof(unsigned char) == 1)); -cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) && - ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF)); -/* the next assertion is probably not really needed */ -cta(short_is_2_char, sizeof(short) == 2); -cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short)); -/* the next assertion is probably not really needed */ -cta(int_is_4_char, sizeof(int) == 4); -cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int)); - -cta(long_ge_int, sizeof(long) >= sizeof(int)); -cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long)); - -#ifndef MKSH_LEGACY_MODE -/* the next assertion is probably not really needed */ -cta(ari_is_4_char, sizeof(mksh_ari_t) == 4); -/* but this is */ -cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1)); -/* the next assertion is probably not really needed */ -cta(uari_is_4_char, sizeof(mksh_uari_t) == 4); -/* but the next three are; we REQUIRE unsigned integer wraparound */ -cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1)); -cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3)); -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 -#else -#define NUM 16 -#endif -/* these are always required */ -cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0); -cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0); -/* we require these to have the precisely same size and assume 2s complement */ -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(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(size_t) <= sizeof(long)); -cta(ari_fits_in_long, sizeof(mksh_ari_t) <= sizeof(long)); -/* for struct alignment people */ - char padding[64 - NUM]; - }; -char ctasserts_dblcheck[sizeof(struct ctasserts) == 64 ? 1 : -1]; - int main(void) { return (sizeof(ctasserts_dblcheck) + isatty(0)); } -EOF -CFLAGS=$save_CFLAGS -eval test 1 = \$HAVE_COMPILE_TIME_ASSERTS_$$ || exit 1 - # # extra checks for legacy mksh # @@ -2367,7 +2339,7 @@ addsrcs '!' HAVE_STRLCPY strlcpy.c 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=541 +add_cppflags -DMKSH_BUILD_R=551 $e $bi$me: Finished configuration testing, now producing output.$ao diff --git a/src/check.t b/src/check.t index 1b03af6..93c614f 100644 --- a/src/check.t +++ b/src/check.t @@ -1,8 +1,8 @@ -# $MirOS: src/bin/mksh/check.t,v 1.756 2016/11/11 23:31:31 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.775 2017/04/12 17:38:41 tg Exp $ # -*- mode: sh -*- #- # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013, 2014, 2015, 2016 +# 2011, 2012, 2013, 2014, 2015, 2016, 2017 # mirabilos # # Provided that these terms and disclaimer and all copyright notices @@ -30,22 +30,40 @@ # (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date expected-stdout: - @(#)MIRBSD KSH R54 2016/11/11 + @(#)MIRBSD KSH R55 2017/04/12 description: Check version of shell. stdin: echo $KSH_VERSION name: KSH_VERSION -category: shell:legacy-no +category: !shell:legacy-yes,!shell:textmode-yes --- expected-stdout: - @(#)LEGACY KSH R54 2016/11/11 + @(#)LEGACY KSH R55 2017/04/12 description: Check version of legacy shell. stdin: echo $KSH_VERSION name: KSH_VERSION-legacy -category: shell:legacy-yes +category: !shell:legacy-no,!shell:textmode-yes +--- +expected-stdout: + @(#)MIRBSD KSH R55 2017/04/12 +TEXTMODE +description: + Check version of shell. +stdin: + echo $KSH_VERSION +name: KSH_VERSION-textmode +category: !shell:legacy-yes,!shell:textmode-no +--- +expected-stdout: + @(#)LEGACY KSH R55 2017/04/12 +TEXTMODE +description: + Check version of legacy shell. +stdin: + echo $KSH_VERSION +name: KSH_VERSION-legacy-textmode +category: !shell:legacy-no,!shell:textmode-no --- name: selftest-1 description: @@ -92,23 +110,6 @@ category: disabled stdin: set --- -name: selftest-legacy -description: - Check some things in the LEGACY KSH -category: shell:legacy-yes -stdin: - set +o emacs - set +o vi - [[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 1=emacs - [[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 1=vi - set -o emacs - set -o vi - [[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 2=emacs - [[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 2=vi -expected-stdout: - 2=emacs - 2=vi ---- name: selftest-direct-builtin-call description: Check that direct builtin calls work @@ -119,6 +120,26 @@ stdin: expected-stdout: -c echo foo --- +name: selftest-pathsep-unix +description: + Check that $PATHSEP is set correctly. +category: !os:os2 +stdin: + PATHSEP=.; export PATHSEP + "$__progname" -c 'print -r -- $PATHSEP' +expected-stdout: + : +--- +name: selftest-pathsep-dospath +description: + Check that $PATHSEP is set correctly. +category: os:os2 +stdin: + PATHSEP=.; export PATHSEP + "$__progname" -c 'print -r -- $PATHSEP' +expected-stdout: + ; +--- name: alias-1 description: Check that recursion is detected/avoided in aliases. @@ -261,14 +282,42 @@ name: alias-11 description: Check that special argument handling still applies with escaped aliases stdin: - alias local='\typeset' - function foo { - local x=$1 y=z + alias local1='\typeset' + alias local2='\\builtin typeset' + function fooa { + local1 x=$1 y=z print -r -- "$x,$y" } - foo 'bar - baz' + function foob { + local2 x=$1 y=z + print -r -- "$x,$y" + } + x=1 y=2; fooa 'bar - baz' + x=1 y=2; foob 'bar - baz' expected-stdout: bar - baz,z + bar - baz,z +--- +name: alias-12 +description: + Something weird from Martijn Dekker +stdin: + alias echo=print + x() { echo a; (echo b); x=$(echo c); } + typeset -f x + alias OPEN='{' CLOSE='};' + { OPEN echo hi1; CLOSE } + var=`{ OPEN echo hi2; CLOSE }` && echo "$var" + var=$({ OPEN echo hi3; CLOSE }) && echo "$var" +expected-stdout: + x() { + \print a + ( \print b ) + x=$(\print c ) + } + hi1 + hi2 + hi3 --- name: arith-compound description: @@ -952,6 +1001,8 @@ stdin: echo end-$i done echo end-3 + for i in a b c; do echo $i; eval break; echo bad-$i; done + echo end-4 expected-stdout: a end-1 @@ -964,6 +1015,8 @@ expected-stdout: c:x end-c end-3 + a + end-4 --- name: break-2 description: @@ -1033,6 +1086,8 @@ stdin: echo end-$i done echo end-3 + for i in a b c; do echo $i; eval continue; echo bad-$i ; done + echo end-4 expected-stdout: a b @@ -1055,6 +1110,10 @@ expected-stdout: c:z end-c end-3 + a + b + c + end-4 --- name: continue-2 description: @@ -2832,7 +2891,7 @@ stdin: expected-stdout: 0 bar() { - foo 4<<-a <<-b 5<<-c + \foo 4<<-a <<-b 5<<-c four a zero @@ -4711,6 +4770,23 @@ expected-stdout: 8 ok <9> . --- +name: IFS-subst-10 +description: + Scalar context in ${var=$subst} +stdin: + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } + set -- one "two three" four + unset -v var + save_IFS=$IFS + IFS= + set -- ${var=$*} + IFS=$save_IFS + echo "var=$var" + showargs "$@" +expected-stdout: + var=onetwo threefour + . +--- name: IFS-arith-1 description: http://austingroupbugs.net/view.php?id=832 @@ -5205,6 +5281,24 @@ expected-stdout: line <6> expected-exit: 1 --- +name: lineno-eval-alias +description: + Check if LINENO is trapped in eval and aliases +stdin: + ${ZSH_VERSION+false} || emulate sh; echo $LINENO + echo $LINENO + eval ' echo $LINENO + echo $LINENO + echo $LINENO' + echo $LINENO +expected-stdout: + 1 + 2 + 3 + 3 + 3 + 6 +--- name: unknown-trap description: Ensure unknown traps are not a syntax error @@ -6326,7 +6420,7 @@ name: regression-62 description: Check if test -nt/-ot succeeds if second(first) file is missing. stdin: - touch a + :>a test a -nt b && echo nt OK || echo nt BAD test b -ot a && echo ot OK || echo ot BAD expected-stdout: @@ -7058,6 +7152,11 @@ description: Check tilde expansion works env-setup: !HOME=/sweet! stdin: + :>'c=a' + typeset c=[ab] + :>'d=a' + x=typeset; $x d=[ab] + echo "<$c>" "<$d>" wd=$PWD cd / plus=$(print -r -- ~+) @@ -7067,10 +7166,106 @@ stdin: [[ $minus = "$wd" ]]; echo two $? . [[ $nix = /sweet ]]; echo nix $? . expected-stdout: + <[ab]> one 0 . two 0 . nix 0 . --- +name: tilde-expand-3 +description: + Check mostly Austin 351 stuff +stdin: + showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } + set "1 b=2" "3 d=4" + export a=$1 \c=$2 + showargs 1 "$a" "$b" "$c" "$d" + unset a b c d + HOME=/tmp + export \a=~ b=~ + command export c=~ + builtin export d=~ + \\builtin export e=~ + showargs 2 "$a" "$b" "$c" "$d" "$e" ksh + unset a b c d e + set -o posix + export \a=~ b=~ + command export c=~ + builtin export d=~ + \\builtin export e=~ + showargs 3 "$a" "$b" "$c" "$d" "$e" posix + unset a b c d e + set +o posix + export a=$1 + showargs 4 "$a" "$b" ksh + unset a b + showargs 5 a=$1 ksh + export \a=$1 + showargs 6 "$a" "$b" ksh + unset a b + set -o posix + export a=$1 + showargs 7 "$a" "$b" posix + unset a b + showargs 8 a=$1 posix + export \a=$1 + showargs 9 "$a" "$b" posix + unset a b + set +o posix + command echo 10 ksh a=~ + command command export a=~ + showargs 11 "$a" + unset a + set -o posix + command echo 12 posix a=~ + command command export a=~ + showargs 13 "$a" + unset a + # unspecified whether /tmp or ~ + var=export; command $var a=~ + showargs 14 "$a" + echo 'echo "<$foo>"' >bar + "$__progname" bar + var=foo + export $var=1 + "$__progname" bar + export $var=~ + "$__progname" bar + # unspecified + command -- export a=~ + showargs 18 "$a" + set -A bla + typeset bla[1]=~:~ + global gbl=~ g2=$1 + local lcl=~ l2=$1 + readonly ro=~ r2=$1 + showargs 19 "${bla[1]}" a=~ "$gbl" "$lcl" "$ro" "$g2" "$l2" "$r2" + set +o posix + echo "20 some arbitrary stuff "=~ + set -o posix + echo "21 some arbitrary stuff "=~ +expected-stdout: + <1> <1 b=2> <> <3> <4> . + <2> . + <3> <~> <~> . + <4> <1 b=2> <> . + <5> . + <6> <1> <2> . + <7> <1 b=2> <> . + <8> . + <9> <1> <2> . + 10 ksh a=/tmp + <11> . + 12 posix a=~ + <13> . + <14> <~> . + <> + <1> + <~> + <18> <~> . + <19> <1 b=2> <1 b=2> <1 b=2> . + 20 some arbitrary stuff =/tmp + 21 some arbitrary stuff =~ +--- name: exit-err-1 description: Check some "exit on error" conditions @@ -7443,7 +7638,7 @@ expected-stdout: After error 2 Exit trap expected-stderr-pattern: - /syntax error: 'newline' unexpected/ + /syntax error: unexpected 'newline'/ --- name: test-stlt-1 description: @@ -7553,6 +7748,58 @@ expected-stdout: 2- 1 1 1 = 3- 0 0 0 = --- +name: test-varset-1 +description: + Test the test -v operator +stdin: + [[ -v a ]] + rv=$?; echo $((++i)) $rv + a= + [[ -v a ]] + rv=$?; echo $((++i)) $rv + unset a + [[ -v a ]] + rv=$?; echo $((++i)) $rv + a=x + [[ -v a ]] + rv=$?; echo $((++i)) $rv + nameref b=a + [[ -v b ]] + rv=$?; echo $((++i)) $rv + unset a + [[ -v b ]] + rv=$?; echo $((++i)) $rv + x[1]=y + [[ -v x ]] + rv=$?; echo $((++i)) $rv + [[ -v x[0] ]] + rv=$?; echo $((++i)) $rv + [[ -v x[1] ]] + rv=$?; echo $((++i)) $rv + [[ -v x[2] ]] + rv=$?; echo $((++i)) $rv +expected-stdout: + 1 1 + 2 0 + 3 1 + 4 0 + 5 0 + 6 1 + 7 1 + 8 1 + 9 0 + 10 1 +--- +name: test-varset-2 +description: + test -v works only on scalars +stdin: + [[ -v x[*] ]] + echo ok +expected-exit: e != 0 +expected-stderr-pattern: + /unexpected '\*'/ +--- name: test-stnze-1 description: Check that the short form [ $x ] works @@ -7903,11 +8150,11 @@ expected-stderr-pattern: --- name: typeset-1 description: - Check that global does what typeset is supposed to do + Check that typeset -g works correctly stdin: set -A arrfoo 65 foo() { - global -Uui16 arrfoo[*] + typeset -g -Uui16 arrfoo[*] } echo before ${arrfoo[0]} . foo @@ -7917,7 +8164,7 @@ stdin: echo inside before ${arrbar[0]} . arrbar[0]=97 echo inside changed ${arrbar[0]} . - global -Uui16 arrbar[*] + typeset -g -Uui16 arrbar[*] echo inside typeset ${arrbar[0]} . arrbar[0]=48 echo inside changed ${arrbar[0]} . @@ -7935,6 +8182,24 @@ expected-stdout: inside changed 16#30 . after 16#30 . --- +name: typeset-2 +description: + Check that typeset -p on arrays works correctly +stdin: + set -A x -- a b c + echo = + typeset -p x + echo = + typeset -p x[1] +expected-stdout: + = + set -A x + typeset x[0]=a + typeset x[1]=b + typeset x[2]=c + = + typeset x[1]=b +--- name: typeset-padding-1 description: Check if left/right justification works as per TFM @@ -8081,7 +8346,7 @@ description: -UMKSH_ASSUME_UTF8 => not expected, but if your OS is old, try passing HAVE_SETLOCALE_CTYPE=0 to Build.sh need-pass: no -category: !os:hpux,!os:msys +category: !os:hpux,!os:msys,!os:os2 need-ctty: yes arguments: !-i! env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8! @@ -8173,17 +8438,17 @@ stdin: alias typeset -f expected-stdout: - autoload='\typeset -fu' - functions='\typeset -f' - hash='\builtin alias -t' - history='\builtin fc -l' - integer='\typeset -i' - local='\typeset' - login='\exec login' - nameref='\typeset -n' + autoload='\\builtin typeset -fu' + functions='\\builtin typeset -f' + hash='\\builtin alias -t' + history='\\builtin fc -l' + integer='\\builtin typeset -i' + local='\\builtin typeset' + login='\\builtin exec login' + nameref='\\builtin typeset -n' nohup='nohup ' - r='\builtin fc -e -' - type='\builtin whence -v' + r='\\builtin fc -e -' + type='\\builtin whence -v' --- name: aliases-2b description: @@ -8193,17 +8458,17 @@ stdin: alias typeset -f expected-stdout: - autoload='\typeset -fu' - functions='\typeset -f' - hash='\builtin alias -t' - history='\builtin fc -l' - integer='\typeset -i' - local='\typeset' - login='\exec login' - nameref='\typeset -n' + autoload='\\builtin typeset -fu' + functions='\\builtin typeset -f' + hash='\\builtin alias -t' + history='\\builtin fc -l' + integer='\\builtin typeset -i' + local='\\builtin typeset' + login='\\builtin exec login' + nameref='\\builtin typeset -n' nohup='nohup ' - r='\builtin fc -e -' - type='\builtin whence -v' + r='\\builtin fc -e -' + type='\\builtin whence -v' --- name: aliases-3b description: @@ -8213,17 +8478,17 @@ stdin: ./sh -c 'alias; typeset -f' rm -f sh expected-stdout: - autoload='\typeset -fu' - functions='\typeset -f' - hash='\builtin alias -t' - history='\builtin fc -l' - integer='\typeset -i' - local='\typeset' - login='\exec login' - nameref='\typeset -n' + autoload='\\builtin typeset -fu' + functions='\\builtin typeset -f' + hash='\\builtin alias -t' + history='\\builtin fc -l' + integer='\\builtin typeset -i' + local='\\builtin typeset' + login='\\builtin exec login' + nameref='\\builtin typeset -n' nohup='nohup ' - r='\builtin fc -e -' - type='\builtin whence -v' + r='\\builtin fc -e -' + type='\\builtin whence -v' --- name: aliases-cmdline description: @@ -8280,8 +8545,8 @@ stdin: :|| local() { :; } alias local expected-stdout: - local='\typeset' - local='\typeset' + local='\\builtin typeset' + local='\\builtin typeset' --- name: arrays-1 description: @@ -8691,21 +8956,21 @@ expected-stdout: name: arrassign-fnc-global description: Check locality of array access inside a function - with the mksh-specific global keyword + with the bash4/mksh/yash/zsh typeset -g keyword stdin: function fn { - global x + typeset -g x x+=(f) echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:" } function rfn { set -A y - global y + typeset -g y y+=(f) echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:" } function fnr { - global z + typeset -g z set -A z z+=(f) echo ".fnr:${z[0]}.${z[1]}.${z[2]}.${z[3]}:" @@ -8843,21 +9108,21 @@ expected-stdout: name: strassign-fnc-global description: Check locality of string access inside a function - with the mksh-specific global keyword + with the bash4/mksh/yash/zsh typeset -g keyword stdin: function fn { - global x + typeset -g x x+=f echo ".fn:$x:" } function rfn { y= - global y + typeset -g y y+=f echo ".rfn:$y:" } function fnr { - global z + typeset -g z z= z+=f echo ".fnr:$z:" @@ -9221,7 +9486,7 @@ stdin: while (( i < ${#line[*]} )); do hv=${line[i++]} if (( (pos & 15) == 0 )); then - (( pos )) && print "$dasc|" + (( pos )) && print -r -- "$dasc|" print -n "${pos#16#} " dasc=' |' fi @@ -9238,7 +9503,7 @@ stdin: print -n ' ' (( (pos++ & 15) == 7 )) && print -n -- '- ' done - (( hv == 2147483647 )) || print "$dasc|" + (( hv == 2147483647 )) || print -r -- "$dasc|" } expected-stdout: 00000000 3C 64 E4 DB C3 9B E2 82 - AC C3 9B 40 3E 0A 3C 00 |.<.| @@ -9277,6 +9542,7 @@ expected-stdout: name: print-crlf description: Check that CR+LF is shown and read as-is +category: shell:textmode-no stdin: cat >foo <<-'EOF' x='bar @@ -9299,6 +9565,32 @@ expected-stdout: {.5} {foo <<-'EOF' + x='bar + ' # + echo .${#x} # + if test x"$KSH_VERSION" = x""; then # + printf '<%s>' "$x" # + else # + print -nr -- "<$x>" # + fi # + EOF + echo "[$("$__progname" foo)]" + "$__progname" foo | while IFS= read -r line; do + print -r -- "{$line}" + done +expected-stdout: + [.4 + ] + {.4} + {\?\| 00000040 40 5C 41 5C 42 5C 43 5C - 44 1B 5C 46 5C 47 5C 48 |@\A\B\C\D.\F\G\H| 00000050 5C 49 5C 4A 5C 4B 5C 4C - 5C 4D 5C 4E 5C 4F 5C 50 |\I\J\K\L\M\N\O\P| - 00000060 5C 51 5C 52 5C 53 5C 54 - 20 5C 56 5C 57 5C 58 5C |\Q\R\S\T \V\W\X\| - 00000070 59 5C 5A 5C 5B 5C 5C 5D - 5C 5E 5C 5F 5C 60 07 08 |Y\Z\[\]\^\_\`..| - 00000080 20 20 5C 64 1B 0C 5C 67 - 5C 68 5C 69 5C 6A 5C 6B | \d..\g\h\i\j\k| - 00000090 5C 6C 5C 6D 0A 5C 6F 5C - 70 20 5C 71 0D 5C 73 09 |\l\m.\o\p \q.\s.| - 000000A0 0B 5C 77 5C 79 5C 7A 5C - 7B 5C 7C 5C 7D 5C 7E 20 |.\w\y\z\{\|\}\~ | - 000000B0 E2 82 AC 64 20 EF BF BD - 20 12 33 20 78 20 53 20 |...d ... .3 x S | - 000000C0 53 34 0A - |S4.| + 00000060 5C 51 5C 52 5C 53 5C 54 - 20 5C 55 5C 56 5C 57 5C |\Q\R\S\T \U\V\W\| + 00000070 58 5C 59 5C 5A 5C 5B 5C - 5C 5D 5C 5E 5C 5F 5C 60 |X\Y\Z\[\\]\^\_\`| + 00000080 07 08 20 20 5C 64 1B 0C - 5C 67 5C 68 5C 69 5C 6A |.. \d..\g\h\i\j| + 00000090 5C 6B 5C 6C 5C 6D 0A 5C - 6F 5C 70 20 5C 71 0D 5C |\k\l\m.\o\p \q.\| + 000000A0 73 09 5C 75 0B 5C 77 5C - 78 5C 79 5C 7A 5C 7B 5C |s.\u.\w\x\y\z\{\| + 000000B0 7C 5C 7D 5C 7E 20 E2 82 - AC 64 20 EF BF BD 20 12 ||\}\~ ...d ... .| + 000000C0 33 20 78 20 53 20 53 34 - 0A |3 x S S4.| --- name: dollar-doublequoted-strings description: @@ -9439,7 +9731,7 @@ stdin: while [[ -n $line ]]; do hv=1#${line::1} if (( (pos & 15) == 0 )); then - (( pos )) && print "$dasc|" + (( pos )) && print -r -- "$dasc|" print -n "${pos#16#} " dasc=' |' fi @@ -9457,7 +9749,7 @@ stdin: print -n ' ' (( (pos++ & 15) == 7 )) && print -n -- '- ' done - (( hv == 2147483647 )) || print "$dasc|" + (( hv == 2147483647 )) || print -r -- "$dasc|" } expected-stdout: 00000000 20 21 22 23 24 25 26 27 - 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./| @@ -9767,7 +10059,7 @@ stdin: while [[ -n $line ]]; do hv=1#${line::1} if (( (pos & 15) == 0 )); then - (( pos )) && print "$dasc|" + (( pos )) && print -r -- "$dasc|" print -n "${pos#16#} " dasc=' |' fi @@ -9785,7 +10077,7 @@ stdin: print -n ' ' (( (pos++ & 15) == 7 )) && print -n -- '- ' done - (( hv == 2147483647 )) || print "$dasc|" + (( hv == 2147483647 )) || print -r -- "$dasc|" } expected-stdout: 00000000 48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3 |Hello, World!\..| @@ -9855,7 +10147,7 @@ stdin: dasc=$dasc$dch dch= elif (( (pos & 7) == 0 )); then - (( pos )) && print "$dasc|" + (( pos )) && print -r -- "$dasc|" print -n "${pos#16#} " dasc=' |' fi @@ -9870,7 +10162,7 @@ stdin: print -n ' ' (( (pos++ & 7) == 3 )) && print -n -- '- ' done - (( hv == 2147483647 )) || print "$dasc|" + (( hv == 2147483647 )) || print -r -- "$dasc|" } expected-stdout: 00000000 0048 0065 006C 006C - 006F 002C 0020 0057 |Hello, W| @@ -9936,7 +10228,7 @@ stdin: while (( i < ${#line[*]} )); do hv=${line[i++]} if (( (pos & 15) == 0 )); then - (( pos )) && print "$dasc|" + (( pos )) && print -r -- "$dasc|" print -n "${pos#16#} " dasc=' |' fi @@ -9953,7 +10245,7 @@ stdin: print -n ' ' (( (pos++ & 15) == 7 )) && print -n -- '- ' done - (( hv == 2147483647 )) || print "$dasc|" + (( hv == 2147483647 )) || print -r -- "$dasc|" } expected-stdout: 00000000 48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3 |Hello, World!\..| @@ -10019,7 +10311,7 @@ stdin: dasc=$dasc$dch dch= elif (( (pos & 7) == 0 )); then - (( pos )) && print "$dasc|" + (( pos )) && print -r -- "$dasc|" print -n "${pos#16#} " dasc=' |' fi @@ -10033,7 +10325,7 @@ stdin: print -n ' ' (( (pos++ & 7) == 3 )) && print -n -- '- ' done - (( hv == 2147483647 )) || print "$dasc|" + (( hv == 2147483647 )) || print -r -- "$dasc|" } expected-stdout: 00000000 0048 0065 006C 006C - 006F 002C 0020 0057 |Hello, W| @@ -10123,9 +10415,16 @@ expected-stdout: 16#61 16#0 16#62 16#20AC 16#63 . --- name: ulimit-1 +description: + Check that ulimit as used in dot.mksh works or is stubbed +stdin: + ulimit -c 0 +--- +name: ulimit-2 description: Check if we can use a specific syntax idiom for ulimit -category: !os:syllable + XXX Haiku works, but only for -n and -V +category: !os:haiku,!os:syllable stdin: if ! x=$(ulimit -d) || [[ $x = unknown ]]; then #echo expected to fail on this OS @@ -10170,7 +10469,6 @@ name: bashiop-1 description: Check if GNU bash-like I/O redirection works Part 1: this is also supported by GNU bash -category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -10191,7 +10489,6 @@ name: bashiop-2a description: Check if GNU bash-like I/O redirection works Part 2: this is *not* supported by GNU bash -category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -10212,7 +10509,6 @@ name: bashiop-2b description: Check if GNU bash-like I/O redirection works Part 2: this is *not* supported by GNU bash -category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -10233,7 +10529,6 @@ name: bashiop-2c description: Check if GNU bash-like I/O redirection works Part 2: this is supported by GNU bash 4 only -category: shell:legacy-no stdin: echo mir >foo set -o noclobber @@ -10257,7 +10552,6 @@ name: bashiop-3a description: Check if GNU bash-like I/O redirection fails correctly Part 1: this is also supported by GNU bash -category: shell:legacy-no stdin: echo mir >foo set -o noclobber @@ -10279,7 +10573,6 @@ name: bashiop-3b description: Check if GNU bash-like I/O redirection fails correctly Part 2: this is *not* supported by GNU bash -category: shell:legacy-no stdin: echo mir >foo set -o noclobber @@ -10303,7 +10596,6 @@ description: Check if GNU bash-like I/O redirection works Part 4: this is also supported by GNU bash, but failed in some mksh versions -category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -10325,11 +10617,10 @@ expected-stdout: ras dwa --- -name: bashiop-5-normal +name: bashiop-5 description: Check if GNU bash-like I/O redirection is only supported in !POSIX !sh mode as it breaks existing scripts' syntax -category: shell:legacy-no stdin: :>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(cld <<-EOF #!$__perlname @@ -10622,22 +10898,6 @@ stdin: expected-stdout: Fowl --- -name: fd-cloexec-3 -description: - Verify that file descriptors > 2 are not private for LEGACY KSH -category: shell:legacy-yes -stdin: - cat >cld <<-EOF - #!$__perlname - open(my \$fh, ">&", 9) or die "E: open \$!"; - syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!"; - EOF - chmod +x cld - exec 9>&1 - ./cld -expected-stdout: - Fowl ---- name: comsub-1a description: COMSUB are now parsed recursively, so this works @@ -10726,10 +10986,10 @@ expected-stdout: x() { case $1 in (u) - echo x + \echo x ;| (*) - echo $1 + \echo $1 ;; esac } @@ -10737,20 +10997,36 @@ expected-stdout: name: comsub-5 description: Check COMSUB works with aliases (does not expand them twice) + and reentrancy safety stdin: print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn chmod +x pfn alias echo='echo a' foo() { + echo moo ./pfn "$(echo foo)" } ./pfn "$(echo b)" + typeset -f foo >x + cat x + foo + . ./x typeset -f foo + foo expected-stdout: a b foo() { - ./pfn "$(echo foo )" + \echo a moo + ./pfn "$(\echo a foo )" } + a moo + a foo + foo() { + \echo a moo + ./pfn "$(\echo a foo )" + } + a moo + a foo --- name: comsub-torture description: @@ -10861,56 +11137,56 @@ expected-stdout: vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" } inline_TCOM() { - vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" + vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" } function comsub_TCOM { x=$( vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" ); } function comsub_TCOM { - x=$(vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" ) + x=$(vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" ) } function reread_TCOM { x=$(( vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" )|tr u x); } function reread_TCOM { - x=$(( vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" ) | tr u x ) + x=$( ( vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" ) | \tr u x ) } inline_TPAREN_TPIPE_TLIST() { (echo $foo | tr -dc 0-9; echo) } inline_TPAREN_TPIPE_TLIST() { - ( echo $foo | tr -dc 0-9 - echo ) + ( \echo $foo | \tr -dc 0-9 + \echo ) } function comsub_TPAREN_TPIPE_TLIST { x=$( (echo $foo | tr -dc 0-9; echo) ); } function comsub_TPAREN_TPIPE_TLIST { - x=$(( echo $foo | tr -dc 0-9 ; echo ) ) + x=$( ( \echo $foo | \tr -dc 0-9 ; \echo ) ) } function reread_TPAREN_TPIPE_TLIST { x=$(( (echo $foo | tr -dc 0-9; echo) )|tr u x); } function reread_TPAREN_TPIPE_TLIST { - x=$(( ( echo $foo | tr -dc 0-9 ; echo ) ) | tr u x ) + x=$( ( ( \echo $foo | \tr -dc 0-9 ; \echo ) ) | \tr u x ) } inline_TAND_TOR() { cmd && echo ja || echo nein } inline_TAND_TOR() { - cmd && echo ja || echo nein + \cmd && \echo ja || \echo nein } function comsub_TAND_TOR { x=$( cmd && echo ja || echo nein ); } function comsub_TAND_TOR { - x=$(cmd && echo ja || echo nein ) + x=$(\cmd && \echo ja || \echo nein ) } function reread_TAND_TOR { x=$(( cmd && echo ja || echo nein )|tr u x); } function reread_TAND_TOR { - x=$(( cmd && echo ja || echo nein ) | tr u x ) + x=$( ( \cmd && \echo ja || \echo nein ) | \tr u x ) } inline_TSELECT() { select file in *; do echo "<$file>" ; break ; done @@ -10918,21 +11194,21 @@ expected-stdout: inline_TSELECT() { select file in * do - echo "<$file>" - break + \echo "<$file>" + \break done } function comsub_TSELECT { x=$( select file in *; do echo "<$file>" ; break ; done ); } function comsub_TSELECT { - x=$(select file in * ; do echo "<$file>" ; break ; done ) + x=$(select file in * ; do \echo "<$file>" ; \break ; done ) } function reread_TSELECT { x=$(( select file in *; do echo "<$file>" ; break ; done )|tr u x); } function reread_TSELECT { - x=$(( select file in * ; do echo "<$file>" ; break ; done ) | tr u x ) + x=$( ( select file in * ; do \echo "<$file>" ; \break ; done ) | \tr u x ) } inline_TFOR_TTIME() { time for i in {1,2,3} ; do echo $i ; done @@ -10940,20 +11216,20 @@ expected-stdout: inline_TFOR_TTIME() { time for i in {1,2,3} do - echo $i + \echo $i done } function comsub_TFOR_TTIME { x=$( time for i in {1,2,3} ; do echo $i ; done ); } function comsub_TFOR_TTIME { - x=$(time for i in {1,2,3} ; do echo $i ; done ) + x=$(time for i in {1,2,3} ; do \echo $i ; done ) } function reread_TFOR_TTIME { x=$(( time for i in {1,2,3} ; do echo $i ; done )|tr u x); } function reread_TFOR_TTIME { - x=$(( time for i in {1,2,3} ; do echo $i ; done ) | tr u x ) + x=$( ( time for i in {1,2,3} ; do \echo $i ; done ) | \tr u x ) } inline_TCASE() { case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac @@ -10961,13 +11237,13 @@ expected-stdout: inline_TCASE() { case $foo in (1) - echo eins + \echo eins ;& (2) - echo zwei + \echo zwei ;| (*) - echo kann net bis drei zählen + \echo kann net bis drei zählen ;; esac } @@ -10975,13 +11251,13 @@ expected-stdout: case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac ); } function comsub_TCASE { - x=$(case $foo in (1) echo eins ;& (2) echo zwei ;| (*) echo kann net bis drei zählen ;; esac ) + x=$(case $foo in (1) \echo eins ;& (2) \echo zwei ;| (*) \echo kann net bis drei zählen ;; esac ) } function reread_TCASE { x=$(( case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac )|tr u x); } function reread_TCASE { - x=$(( case $foo in (1) echo eins ;& (2) echo zwei ;| (*) echo kann net bis drei zählen ;; esac ) | tr u x ) + x=$( ( case $foo in (1) \echo eins ;& (2) \echo zwei ;| (*) \echo kann net bis drei zählen ;; esac ) | \tr u x ) } inline_TIF_TBANG_TDBRACKET_TELIF() { if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi @@ -10989,25 +11265,25 @@ expected-stdout: inline_TIF_TBANG_TDBRACKET_TELIF() { if ! [[ 1 = 1 ]] then - echo eins + \echo eins elif [[ 1 = 2 ]] then - echo zwei + \echo zwei else - echo drei + \echo drei fi } function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$( if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi ); } function comsub_TIF_TBANG_TDBRACKET_TELIF { - x=$(if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) + x=$(if ! [[ 1 = 1 ]] ; then \echo eins ; elif [[ 1 = 2 ]] ; then \echo zwei ; else \echo drei ; fi ) } function reread_TIF_TBANG_TDBRACKET_TELIF { x=$(( if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi )|tr u x); } function reread_TIF_TBANG_TDBRACKET_TELIF { - x=$(( if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) | tr u x ) + x=$( ( if ! [[ 1 = 1 ]] ; then \echo eins ; elif [[ 1 = 2 ]] ; then \echo zwei ; else \echo drei ; fi ) | \tr u x ) } inline_TWHILE() { i=1; while (( i < 10 )); do echo $i; let ++i; done @@ -11015,24 +11291,24 @@ expected-stdout: inline_TWHILE() { i=1 while { - \let] " i < 10 " + \\builtin let " i < 10 " } do - echo $i - let ++i + \echo $i + \let ++i done } function comsub_TWHILE { x=$( i=1; while (( i < 10 )); do echo $i; let ++i; done ); } function comsub_TWHILE { - x=$(i=1 ; while { \let] " i < 10 " ; } ; do echo $i ; let ++i ; done ) + x=$(i=1 ; while { \\builtin let " i < 10 " ; } ; do \echo $i ; \let ++i ; done ) } function reread_TWHILE { x=$(( i=1; while (( i < 10 )); do echo $i; let ++i; done )|tr u x); } function reread_TWHILE { - x=$(( i=1 ; while { \let] " i < 10 " ; } ; do echo $i ; let ++i ; done ) | tr u x ) + x=$( ( i=1 ; while { \\builtin let " i < 10 " ; } ; do \echo $i ; \let ++i ; done ) | \tr u x ) } inline_TUNTIL() { i=10; until (( !--i )) ; do echo $i; done @@ -11040,42 +11316,42 @@ expected-stdout: inline_TUNTIL() { i=10 until { - \let] " !--i " + \\builtin let " !--i " } do - echo $i + \echo $i done } function comsub_TUNTIL { x=$( i=10; until (( !--i )) ; do echo $i; done ); } function comsub_TUNTIL { - x=$(i=10 ; until { \let] " !--i " ; } ; do echo $i ; done ) + x=$(i=10 ; until { \\builtin let " !--i " ; } ; do \echo $i ; done ) } function reread_TUNTIL { x=$(( i=10; until (( !--i )) ; do echo $i; done )|tr u x); } function reread_TUNTIL { - x=$(( i=10 ; until { \let] " !--i " ; } ; do echo $i ; done ) | tr u x ) + x=$( ( i=10 ; until { \\builtin let " !--i " ; } ; do \echo $i ; done ) | \tr u x ) } inline_TCOPROC() { cat * |& ls } inline_TCOPROC() { - cat * |& - ls + \cat * |& + \ls } function comsub_TCOPROC { x=$( cat * |& ls ); } function comsub_TCOPROC { - x=$(cat * |& ls ) + x=$(\cat * |& \ls ) } function reread_TCOPROC { x=$(( cat * |& ls )|tr u x); } function reread_TCOPROC { - x=$(( cat * |& ls ) | tr u x ) + x=$( ( \cat * |& \ls ) | \tr u x ) } inline_TFUNCT_TBRACE_TASYNC() { function korn { echo eins; echo zwei ; } @@ -11083,11 +11359,11 @@ expected-stdout: } inline_TFUNCT_TBRACE_TASYNC() { function korn { - echo eins - echo zwei + \echo eins + \echo zwei } bourne() { - logger * & + \logger * & } } function comsub_TFUNCT_TBRACE_TASYNC { x=$( @@ -11095,32 +11371,32 @@ expected-stdout: bourne () { logger * & } ); } function comsub_TFUNCT_TBRACE_TASYNC { - x=$(function korn { echo eins ; echo zwei ; } ; bourne() { logger * & } ) + x=$(function korn { \echo eins ; \echo zwei ; } ; bourne() { \logger * & } ) } function reread_TFUNCT_TBRACE_TASYNC { x=$(( function korn { echo eins; echo zwei ; } bourne () { logger * & } )|tr u x); } function reread_TFUNCT_TBRACE_TASYNC { - x=$(( function korn { echo eins ; echo zwei ; } ; bourne() { logger * & } ) | tr u x ) + x=$( ( function korn { \echo eins ; \echo zwei ; } ; bourne() { \logger * & } ) | \tr u x ) } inline_IOREAD_IOCAT() { tr x u 0>bar } inline_IOREAD_IOCAT() { - tr x u >bar + \tr x u >bar } function comsub_IOREAD_IOCAT { x=$( tr x u 0>bar ); } function comsub_IOREAD_IOCAT { - x=$(tr x u >bar ) + x=$(\tr x u >bar ) } function reread_IOREAD_IOCAT { x=$(( tr x u 0>bar )|tr u x); } function reread_IOREAD_IOCAT { - x=$(( tr x u >bar ) | tr u x ) + x=$( ( \tr x u >bar ) | \tr u x ) } inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() { cat >|bar <<'EOFN' @@ -11128,7 +11404,7 @@ expected-stdout: EOFN } inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() { - cat >|bar <<"EOFN" + \cat >|bar <<"EOFN" foo EOFN @@ -11139,7 +11415,7 @@ expected-stdout: EOFN ); } function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP { - x=$(cat >|bar <<"EOFN" + x=$(\cat >|bar <<"EOFN" foo EOFN ) @@ -11150,10 +11426,10 @@ expected-stdout: EOFN )|tr u x); } function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP { - x=$(( cat >|bar <<"EOFN" + x=$( ( \cat >|bar <<"EOFN" foo EOFN - ) | tr u x ) + ) | \tr u x ) } inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() { cat 1>bar <<-EOFI @@ -11161,7 +11437,7 @@ expected-stdout: EOFI } inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() { - cat >bar <<-EOFI + \cat >bar <<-EOFI foo EOFI @@ -11172,7 +11448,7 @@ expected-stdout: EOFI ); } function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP { - x=$(cat >bar <<-EOFI + x=$(\cat >bar <<-EOFI foo EOFI ) @@ -11183,46 +11459,46 @@ expected-stdout: EOFI )|tr u x); } function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP { - x=$(( cat >bar <<-EOFI + x=$( ( \cat >bar <<-EOFI foo EOFI - ) | tr u x ) + ) | \tr u x ) } inline_IORDWR_IODUP() { sh 1<>/dev/console 0<&1 2>&1 } inline_IORDWR_IODUP() { - sh 1<>/dev/console <&1 2>&1 + \sh 1<>/dev/console <&1 2>&1 } function comsub_IORDWR_IODUP { x=$( sh 1<>/dev/console 0<&1 2>&1 ); } function comsub_IORDWR_IODUP { - x=$(sh 1<>/dev/console <&1 2>&1 ) + x=$(\sh 1<>/dev/console <&1 2>&1 ) } function reread_IORDWR_IODUP { x=$(( sh 1<>/dev/console 0<&1 2>&1 )|tr u x); } function reread_IORDWR_IODUP { - x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x ) + x=$( ( \sh 1<>/dev/console <&1 2>&1 ) | \tr u x ) } inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() { echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} } inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() { - echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} + \echo $(\true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} } function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$( echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} ); } function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { - x=$(echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) + x=$(\echo $(\true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) } function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$(( echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} )|tr u x); } function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { - x=$(( echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | tr u x ) + x=$( ( \echo $(\true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | \tr u x ) } inline_QCHAR_OQUOTE_CQUOTE() { echo fo\ob\"a\`r\'b\$az @@ -11230,9 +11506,9 @@ expected-stdout: echo 'fo\ob\"a\`r'\''b\$az' } inline_QCHAR_OQUOTE_CQUOTE() { - echo fo\ob\"a\`r\'b\$az - echo "fo\ob\"a\`r\'b\$az" - echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" + \echo fo\ob\"a\`r\'b\$az + \echo "fo\ob\"a\`r\'b\$az" + \echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" } function comsub_QCHAR_OQUOTE_CQUOTE { x=$( echo fo\ob\"a\`r\'b\$az @@ -11240,7 +11516,7 @@ expected-stdout: echo 'fo\ob\"a\`r'\''b\$az' ); } function comsub_QCHAR_OQUOTE_CQUOTE { - x=$(echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) + x=$(\echo fo\ob\"a\`r\'b\$az ; \echo "fo\ob\"a\`r\'b\$az" ; \echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) } function reread_QCHAR_OQUOTE_CQUOTE { x=$(( echo fo\ob\"a\`r\'b\$az @@ -11248,7 +11524,7 @@ expected-stdout: echo 'fo\ob\"a\`r'\''b\$az' )|tr u x); } function reread_QCHAR_OQUOTE_CQUOTE { - x=$(( echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) | tr u x ) + x=$( ( \echo fo\ob\"a\`r\'b\$az ; \echo "fo\ob\"a\`r\'b\$az" ; \echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) | \tr u x ) } inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() { [[ ${foo#bl\(u\)b} = @(bar|baz) ]] @@ -11266,7 +11542,7 @@ expected-stdout: [[ ${foo#bl\(u\)b} = @(bar|baz) ]] )|tr u x); } function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT { - x=$(( [[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) | tr u x ) + x=$( ( [[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) | \tr u x ) } inline_heredoc_closed() { x=$(cat <&1 <<-EOF + x=$(\sysctl -n kern.version | \sed 1q ) + [[ -s /etc/motd && "$([[ "$(\head -1 /etc/motd )" != $x ]] && \ed -s /etc/motd 2>&1 <<-EOF 1,/^\$/d 0a $x @@ -11362,11 +11638,11 @@ expected-stdout: . wq EOF - )" = @(?) ]] && rm -f /etc/motd + )" = @(?) ]] && \rm -f /etc/motd if [[ ! -s /etc/motd ]] then - install -c -o root -g wheel -m 664 /dev/null /etc/motd - print -- "$x\n" >/etc/motd + \install -c -o root -g wheel -m 664 /dev/null /etc/motd + \print -- "$x\n" >/etc/motd fi } function comsub_patch_motd { x=$( @@ -11386,7 +11662,7 @@ expected-stdout: fi ); } function comsub_patch_motd { - x=$(x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF + x=$(x=$(\sysctl -n kern.version | \sed 1q ) ; [[ -s /etc/motd && "$([[ "$(\head -1 /etc/motd )" != $x ]] && \ed -s /etc/motd 2>&1 <<-EOF 1,/^\$/d 0a $x @@ -11394,7 +11670,7 @@ expected-stdout: . wq EOF - )" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) + )" = @(?) ]] && \rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then \install -c -o root -g wheel -m 664 /dev/null /etc/motd ; \print -- "$x\n" >/etc/motd ; fi ) } function reread_patch_motd { x=$(( x=$(sysctl -n kern.version | sed 1q) @@ -11413,7 +11689,7 @@ expected-stdout: fi )|tr u x); } function reread_patch_motd { - x=$(( x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF + x=$( ( x=$(\sysctl -n kern.version | \sed 1q ) ; [[ -s /etc/motd && "$([[ "$(\head -1 /etc/motd )" != $x ]] && \ed -s /etc/motd 2>&1 <<-EOF 1,/^\$/d 0a $x @@ -11421,7 +11697,7 @@ expected-stdout: . wq EOF - )" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) | tr u x ) + )" = @(?) ]] && \rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then \install -c -o root -g wheel -m 664 /dev/null /etc/motd ; \print -- "$x\n" >/etc/motd ; fi ) | \tr u x ) } inline_wdarrassign() { case x in @@ -11432,7 +11708,7 @@ expected-stdout: case x in (x) a+=b - \set -A c+ -- d e + \\builtin set -A c+ -- d e ;; esac } @@ -11442,7 +11718,7 @@ expected-stdout: esac ); } function comsub_wdarrassign { - x=$(case x in (x) a+=b ; \set -A c+ -- d e ;; esac ) + x=$(case x in (x) a+=b ; \\builtin set -A c+ -- d e ;; esac ) } function reread_wdarrassign { x=$(( case x in @@ -11450,7 +11726,7 @@ expected-stdout: esac )|tr u x); } function reread_wdarrassign { - x=$(( case x in (x) a+=b ; \set -A c+ -- d e ;; esac ) | tr u x ) + x=$( ( case x in (x) a+=b ; \\builtin set -A c+ -- d e ;; esac ) | \tr u x ) } --- name: comsub-torture-io @@ -11517,56 +11793,56 @@ expected-stdout: vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" >&3 } inline_TCOM() { - vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" >&3 + vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" >&3 } function comsub_TCOM { x=$( vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" >&3 ); } function comsub_TCOM { - x=$(vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" >&3 ) + x=$(vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" >&3 ) } function reread_TCOM { x=$(( vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" >&3 )|tr u x); } function reread_TCOM { - x=$(( vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" >&3 ) | tr u x ) + x=$( ( vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" >&3 ) | \tr u x ) } inline_TPAREN_TPIPE_TLIST() { (echo $foo | tr -dc 0-9 >&3; echo >&3) >&3 } inline_TPAREN_TPIPE_TLIST() { - ( echo $foo | tr -dc 0-9 >&3 - echo >&3 ) >&3 + ( \echo $foo | \tr -dc 0-9 >&3 + \echo >&3 ) >&3 } function comsub_TPAREN_TPIPE_TLIST { x=$( (echo $foo | tr -dc 0-9 >&3; echo >&3) >&3 ); } function comsub_TPAREN_TPIPE_TLIST { - x=$(( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) + x=$( ( \echo $foo | \tr -dc 0-9 >&3 ; \echo >&3 ) >&3 ) } function reread_TPAREN_TPIPE_TLIST { x=$(( (echo $foo | tr -dc 0-9 >&3; echo >&3) >&3 )|tr u x); } function reread_TPAREN_TPIPE_TLIST { - x=$(( ( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) | tr u x ) + x=$( ( ( \echo $foo | \tr -dc 0-9 >&3 ; \echo >&3 ) >&3 ) | \tr u x ) } inline_TAND_TOR() { cmd >&3 && >&3 echo ja || echo >&3 nein } inline_TAND_TOR() { - cmd >&3 && echo ja >&3 || echo nein >&3 + \cmd >&3 && \echo ja >&3 || \echo nein >&3 } function comsub_TAND_TOR { x=$( cmd >&3 && >&3 echo ja || echo >&3 nein ); } function comsub_TAND_TOR { - x=$(cmd >&3 && echo ja >&3 || echo nein >&3 ) + x=$(\cmd >&3 && \echo ja >&3 || \echo nein >&3 ) } function reread_TAND_TOR { x=$(( cmd >&3 && >&3 echo ja || echo >&3 nein )|tr u x); } function reread_TAND_TOR { - x=$(( cmd >&3 && echo ja >&3 || echo nein >&3 ) | tr u x ) + x=$( ( \cmd >&3 && \echo ja >&3 || \echo nein >&3 ) | \tr u x ) } inline_TSELECT() { select file in *; do echo "<$file>" ; break >&3 ; done >&3 @@ -11574,21 +11850,21 @@ expected-stdout: inline_TSELECT() { select file in * do - echo "<$file>" - break >&3 + \echo "<$file>" + \break >&3 done >&3 } function comsub_TSELECT { x=$( select file in *; do echo "<$file>" ; break >&3 ; done >&3 ); } function comsub_TSELECT { - x=$(select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) + x=$(select file in * ; do \echo "<$file>" ; \break >&3 ; done >&3 ) } function reread_TSELECT { x=$(( select file in *; do echo "<$file>" ; break >&3 ; done >&3 )|tr u x); } function reread_TSELECT { - x=$(( select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) | tr u x ) + x=$( ( select file in * ; do \echo "<$file>" ; \break >&3 ; done >&3 ) | \tr u x ) } inline_TFOR_TTIME() { for i in {1,2,3} ; do time >&3 echo $i ; done >&3 @@ -11596,20 +11872,20 @@ expected-stdout: inline_TFOR_TTIME() { for i in {1,2,3} do - time echo $i >&3 + time \echo $i >&3 done >&3 } function comsub_TFOR_TTIME { x=$( for i in {1,2,3} ; do time >&3 echo $i ; done >&3 ); } function comsub_TFOR_TTIME { - x=$(for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) + x=$(for i in {1,2,3} ; do time \echo $i >&3 ; done >&3 ) } function reread_TFOR_TTIME { x=$(( for i in {1,2,3} ; do time >&3 echo $i ; done >&3 )|tr u x); } function reread_TFOR_TTIME { - x=$(( for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) | tr u x ) + x=$( ( for i in {1,2,3} ; do time \echo $i >&3 ; done >&3 ) | \tr u x ) } inline_TCASE() { case $foo in 1) echo eins >&3;& 2) echo zwei >&3 ;| *) echo kann net bis drei zählen >&3;; esac >&3 @@ -11617,13 +11893,13 @@ expected-stdout: inline_TCASE() { case $foo in (1) - echo eins >&3 + \echo eins >&3 ;& (2) - echo zwei >&3 + \echo zwei >&3 ;| (*) - echo kann net bis drei zählen >&3 + \echo kann net bis drei zählen >&3 ;; esac >&3 } @@ -11631,13 +11907,13 @@ expected-stdout: case $foo in 1) echo eins >&3;& 2) echo zwei >&3 ;| *) echo kann net bis drei zählen >&3;; esac >&3 ); } function comsub_TCASE { - x=$(case $foo in (1) echo eins >&3 ;& (2) echo zwei >&3 ;| (*) echo kann net bis drei zählen >&3 ;; esac >&3 ) + x=$(case $foo in (1) \echo eins >&3 ;& (2) \echo zwei >&3 ;| (*) \echo kann net bis drei zählen >&3 ;; esac >&3 ) } function reread_TCASE { x=$(( case $foo in 1) echo eins >&3;& 2) echo zwei >&3 ;| *) echo kann net bis drei zählen >&3;; esac >&3 )|tr u x); } function reread_TCASE { - x=$(( case $foo in (1) echo eins >&3 ;& (2) echo zwei >&3 ;| (*) echo kann net bis drei zählen >&3 ;; esac >&3 ) | tr u x ) + x=$( ( case $foo in (1) \echo eins >&3 ;& (2) \echo zwei >&3 ;| (*) \echo kann net bis drei zählen >&3 ;; esac >&3 ) | \tr u x ) } inline_TIF_TBANG_TDBRACKET_TELIF() { if ! [[ 1 = 1 ]] >&3 ; then echo eins; elif [[ 1 = 2 ]] >&3; then echo zwei ;else echo drei; fi >&3 @@ -11645,25 +11921,25 @@ expected-stdout: inline_TIF_TBANG_TDBRACKET_TELIF() { if ! [[ 1 = 1 ]] >&3 then - echo eins + \echo eins elif [[ 1 = 2 ]] >&3 then - echo zwei + \echo zwei else - echo drei + \echo drei fi >&3 } function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$( if ! [[ 1 = 1 ]] >&3 ; then echo eins; elif [[ 1 = 2 ]] >&3; then echo zwei ;else echo drei; fi >&3 ); } function comsub_TIF_TBANG_TDBRACKET_TELIF { - x=$(if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) + x=$(if ! [[ 1 = 1 ]] >&3 ; then \echo eins ; elif [[ 1 = 2 ]] >&3 ; then \echo zwei ; else \echo drei ; fi >&3 ) } function reread_TIF_TBANG_TDBRACKET_TELIF { x=$(( if ! [[ 1 = 1 ]] >&3 ; then echo eins; elif [[ 1 = 2 ]] >&3; then echo zwei ;else echo drei; fi >&3 )|tr u x); } function reread_TIF_TBANG_TDBRACKET_TELIF { - x=$(( if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) | tr u x ) + x=$( ( if ! [[ 1 = 1 ]] >&3 ; then \echo eins ; elif [[ 1 = 2 ]] >&3 ; then \echo zwei ; else \echo drei ; fi >&3 ) | \tr u x ) } inline_TWHILE() { i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3 @@ -11671,24 +11947,24 @@ expected-stdout: inline_TWHILE() { i=1 while { - \let] " i < 10 " + \\builtin let " i < 10 " } >&3 do - echo $i - let ++i + \echo $i + \let ++i done >&3 } function comsub_TWHILE { x=$( i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3 ); } function comsub_TWHILE { - x=$(i=1 ; while { \let] " i < 10 " ; } >&3 ; do echo $i ; let ++i ; done >&3 ) + x=$(i=1 ; while { \\builtin let " i < 10 " ; } >&3 ; do \echo $i ; \let ++i ; done >&3 ) } function reread_TWHILE { x=$(( i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3 )|tr u x); } function reread_TWHILE { - x=$(( i=1 ; while { \let] " i < 10 " ; } >&3 ; do echo $i ; let ++i ; done >&3 ) | tr u x ) + x=$( ( i=1 ; while { \\builtin let " i < 10 " ; } >&3 ; do \echo $i ; \let ++i ; done >&3 ) | \tr u x ) } inline_TUNTIL() { i=10; until (( !--i )) >&3 ; do echo $i; done >&3 @@ -11696,42 +11972,42 @@ expected-stdout: inline_TUNTIL() { i=10 until { - \let] " !--i " + \\builtin let " !--i " } >&3 do - echo $i + \echo $i done >&3 } function comsub_TUNTIL { x=$( i=10; until (( !--i )) >&3 ; do echo $i; done >&3 ); } function comsub_TUNTIL { - x=$(i=10 ; until { \let] " !--i " ; } >&3 ; do echo $i ; done >&3 ) + x=$(i=10 ; until { \\builtin let " !--i " ; } >&3 ; do \echo $i ; done >&3 ) } function reread_TUNTIL { x=$(( i=10; until (( !--i )) >&3 ; do echo $i; done >&3 )|tr u x); } function reread_TUNTIL { - x=$(( i=10 ; until { \let] " !--i " ; } >&3 ; do echo $i ; done >&3 ) | tr u x ) + x=$( ( i=10 ; until { \\builtin let " !--i " ; } >&3 ; do \echo $i ; done >&3 ) | \tr u x ) } inline_TCOPROC() { cat * >&3 |& >&3 ls } inline_TCOPROC() { - cat * >&3 |& - ls >&3 + \cat * >&3 |& + \ls >&3 } function comsub_TCOPROC { x=$( cat * >&3 |& >&3 ls ); } function comsub_TCOPROC { - x=$(cat * >&3 |& ls >&3 ) + x=$(\cat * >&3 |& \ls >&3 ) } function reread_TCOPROC { x=$(( cat * >&3 |& >&3 ls )|tr u x); } function reread_TCOPROC { - x=$(( cat * >&3 |& ls >&3 ) | tr u x ) + x=$( ( \cat * >&3 |& \ls >&3 ) | \tr u x ) } inline_TFUNCT_TBRACE_TASYNC() { function korn { echo eins; echo >&3 zwei ; } @@ -11739,11 +12015,11 @@ expected-stdout: } inline_TFUNCT_TBRACE_TASYNC() { function korn { - echo eins - echo zwei >&3 + \echo eins + \echo zwei >&3 } bourne() { - logger * >&3 & + \logger * >&3 & } } function comsub_TFUNCT_TBRACE_TASYNC { x=$( @@ -11751,32 +12027,32 @@ expected-stdout: bourne () { logger * >&3 & } ); } function comsub_TFUNCT_TBRACE_TASYNC { - x=$(function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 & } ) + x=$(function korn { \echo eins ; \echo zwei >&3 ; } ; bourne() { \logger * >&3 & } ) } function reread_TFUNCT_TBRACE_TASYNC { x=$(( function korn { echo eins; echo >&3 zwei ; } bourne () { logger * >&3 & } )|tr u x); } function reread_TFUNCT_TBRACE_TASYNC { - x=$(( function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 & } ) | tr u x ) + x=$( ( function korn { \echo eins ; \echo zwei >&3 ; } ; bourne() { \logger * >&3 & } ) | \tr u x ) } inline_COMSUB_EXPRSUB() { echo $(true >&3) $((1+ 2)) } inline_COMSUB_EXPRSUB() { - echo $(true >&3 ) $((1+ 2)) + \echo $(\true >&3 ) $((1+ 2)) } function comsub_COMSUB_EXPRSUB { x=$( echo $(true >&3) $((1+ 2)) ); } function comsub_COMSUB_EXPRSUB { - x=$(echo $(true >&3 ) $((1+ 2)) ) + x=$(\echo $(\true >&3 ) $((1+ 2)) ) } function reread_COMSUB_EXPRSUB { x=$(( echo $(true >&3) $((1+ 2)) )|tr u x); } function reread_COMSUB_EXPRSUB { - x=$(( echo $(true >&3 ) $((1+ 2)) ) | tr u x ) + x=$( ( \echo $(\true >&3 ) $((1+ 2)) ) | \tr u x ) } --- name: funsub-1 diff --git a/src/dot.mkshrc b/src/dot.mkshrc index 13a1f54..af55d7d 100644 --- a/src/dot.mkshrc +++ b/src/dot.mkshrc @@ -1,8 +1,8 @@ # $Id$ -# $MirOS: src/bin/mksh/dot.mkshrc,v 1.108 2016/07/26 22:03:41 tg Exp $ +# $MirOS: src/bin/mksh/dot.mkshrc,v 1.114 2017/03/19 22:31:26 tg Exp $ #- # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013, 2014, 2015, 2016 +# 2011, 2012, 2013, 2014, 2015, 2016, 2017 # mirabilos # # Provided that these terms and disclaimer and all copyright notices @@ -22,65 +22,100 @@ #- # ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells -# catch non-mksh (including lksh) trying to run this file +# catch non-mksh, non-lksh, trying to run this file case ${KSH_VERSION:-} in -*MIRBSD\ KSH*) ;; -*) return 0 ;; +*LEGACY\ KSH*|*MIRBSD\ KSH*) ;; +*) \return 0 ;; esac -PS1='#'; (( USER_ID )) && PS1='$'; \: "${TERM:=vt100}${HOSTNAME:=$(\ulimit -c \ - 0; hostname 2>/dev/null)}${EDITOR:=/bin/ed}${USER:=$(\ulimit -c 0; id -un \ - 2>/dev/null || \echo \?)}${MKSH:=$(\builtin whence -p mksh)}" -HOSTNAME=${HOSTNAME%%*([ ]).*}; HOSTNAME=${HOSTNAME##*([ ])} -[[ $HOSTNAME = ?(ip6-)localhost?(6) ]] && HOSTNAME= -\: "${HOSTNAME:=nil}${MKSH:=/bin/mksh}"; \export EDITOR HOSTNAME MKSH TERM USER -PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${| - \typeset e=$? +# give MidnightBSD's laffer1 a bit of csh feeling +function setenv { + if (( $# )); then + \\builtin eval '\\builtin export "$1"="${2:-}"' + else + \\builtin typeset -x + fi +} + +# pager (not control character safe) +smores() ( + \\builtin set +m + \\builtin cat "$@" |& + \\builtin trap "rv=\$?; \\\\builtin kill $! >/dev/null 2>&1; \\\\builtin exit \$rv" EXIT + while IFS= \\builtin read -pr line; do + llen=${%line} + (( llen == -1 )) && llen=${#line} + (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 )) + if (( (curlin += llen) >= LINES )); then + \\builtin print -nr -- $'\e[7m--more--\e[0m' + \\builtin read -u1 || \\builtin exit $? + [[ $REPLY = [Qq]* ]] && \\builtin exit 0 + curlin=$llen + fi + \\builtin print -r -- "$line" + done +) + +\\builtin alias ls=ls l='ls -F' la='l -a' ll='l -l' lo='l -alo' +\: "${HOSTNAME:=$(\\builtin ulimit -c 0; \\builtin print -r -- $(hostname \ + 2>/dev/null))}${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit \ + -c 0; id -un 2>/dev/null)}${USER:=?}" +[[ $HOSTNAME = ?(?(ip6-)localhost?(6)) ]] && HOSTNAME=nil; \\builtin unalias ls +\\builtin export EDITOR HOSTNAME TERM USER + +# minimal support for lksh users +if [[ $KSH_VERSION = *LEGACY\ KSH* ]]; then + PS1='$USER@${HOSTNAME%%.*}:$PWD>' + \\builtin return 0 +fi + +# mksh-specific from here +\: "${MKSH:=$(\\builtin whence -p mksh)}${MKSH:=/bin/mksh}" +\\builtin export MKSH + +PS4='[$EPOCHREALTIME] '; PS1='#'; (( USER_ID )) && PS1='$'; PS1=$'\001\r''${| + \\builtin typeset e=$? (( e )) && REPLY+="$e|" REPLY+=${USER}@${HOSTNAME%%.*}: - \typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/} - d=${d%/}; \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d} + \\builtin typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/} + d=${d%/}; \\builtin typeset m=${%d} n p=...; (( m > 0 )) || m=${#d} (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p= REPLY+=$p$d - \return $e + \\builtin return $e } '"$PS1 " -\alias ls=ls -\unalias ls -\alias l='ls -F' -\alias la='l -a' -\alias ll='l -l' -\alias lo='l -alo' -\alias doch='sudo mksh -c "$(\builtin fc -ln -1)"' -\command -v rot13 >/dev/null || \alias rot13='tr \ +\\builtin alias doch='sudo mksh -c "$(\\builtin fc -ln -1)"' +\\builtin command -v rot13 >/dev/null || \\builtin alias rot13='tr \ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM' -if \command -v hd >/dev/null; then \:; elif \command -v hexdump >/dev/null; then +if \\builtin command -v hd >/dev/null; then + \: +elif \\builtin command -v hexdump >/dev/null; then function hd { hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \ -e '" |" "%_p"' -e '"|\n"' "$@" } else function hd { - \typeset -Uui16 -Z11 pos=0 - \typeset -Uui16 -Z5 hv=2147483647 - \typeset dasc line i - \set +U + \\builtin typeset -Uui16 -Z11 pos=0 + \\builtin typeset -Uui16 -Z5 hv=2147483647 + \\builtin typeset dasc line i + \\builtin set +U - \cat "$@" | if \read -arN -1 line; then - \typeset -i1 'line[*]' + \\builtin cat "$@" | if \\builtin read -arN -1 line; then + \\builtin typeset -i1 'line[*]' i=0 while (( i < ${#line[*]} )); do hv=${line[i++]} if (( (pos & 15) == 0 )); then (( pos )) && \ - \builtin print -r -- "$dasc|" - \builtin print -n "${pos#16#} " + \\builtin print -r -- "$dasc|" + \\builtin print -nr "${pos#16#} " dasc=' |' fi - \builtin print -n "${hv#16#} " + \\builtin print -nr "${hv#16#} " #XXX EBCDIC, but we need [[:print:]] to fix this if (( (hv < 32) || (hv > 126) )); then dasc+=. @@ -88,69 +123,69 @@ else dasc+=${line[i-1]#1#} fi (( (pos++ & 15) == 7 )) && \ - \builtin print -n -- '- ' + \\builtin print -nr -- '- ' done while (( pos & 15 )); do - \builtin print -n ' ' + \\builtin print -nr ' ' (( (pos++ & 15) == 7 )) && \ - \builtin print -n -- '- ' + \\builtin print -nr -- '- ' done - (( hv == 2147483647 )) || \builtin print -r -- "$dasc|" + (( hv == 2147483647 )) || \\builtin print -r -- "$dasc|" fi } fi # Berkeley C shell compatible dirs, popd, and pushd functions # Z shell compatible chpwd() hook, used to update DIRSTACK[0] -DIRSTACKBASE=$(\builtin realpath ~/. 2>/dev/null || \ - \builtin print -nr -- "${HOME:-/}") -set -A DIRSTACK +DIRSTACKBASE=$(\\builtin realpath ~/. 2>/dev/null || \ + \\builtin print -nr -- "${HOME:-/}") +\\builtin set -A DIRSTACK function chpwd { - DIRSTACK[0]=$(\builtin realpath . 2>/dev/null || \ - \builtin print -r -- "$PWD") + DIRSTACK[0]=$(\\builtin realpath . 2>/dev/null || \ + \\builtin print -nr -- "$PWD") [[ $DIRSTACKBASE = ?(*/) ]] || \ DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/\~} \: } \chpwd . cd() { - \builtin cd "$@" || \return $? + \\builtin cd "$@" || \\builtin return $? \chpwd "$@" } function cd_csh { - \typeset d t=${1/#\~/$DIRSTACKBASE} + \\builtin typeset d t=${1/#\~/$DIRSTACKBASE} - if ! d=$(\builtin cd "$t" 2>&1); then - \builtin print -u2 "${1}: ${d##*cd: $t: }." - \return 1 + if ! d=$(\\builtin cd "$t" 2>&1); then + \\builtin print -ru2 "${1}: ${d##*cd: $t: }." + \\builtin return 1 fi \cd "$t" } function dirs { - \typeset d dwidth - \typeset -i fl=0 fv=0 fn=0 cpos=0 + \\builtin typeset d dwidth + \\builtin typeset -i fl=0 fv=0 fn=0 cpos=0 - while \getopts ":lvn" d; do + while \\builtin getopts ":lvn" d; do case $d { (l) fl=1 ;; (v) fv=1 ;; (n) fn=1 ;; - (*) \builtin print -u2 'Usage: dirs [-lvn].' - \return 1 ;; + (*) \\builtin print -ru2 'Usage: dirs [-lvn].' + \\builtin return 1 ;; } done - \shift $((OPTIND - 1)) + \\builtin shift $((OPTIND - 1)) if (( $# > 0 )); then - \builtin print -u2 'Usage: dirs [-lvn].' - \return 1 + \\builtin print -ru2 'Usage: dirs [-lvn].' + \\builtin return 1 fi if (( fv )); then fv=0 while (( fv < ${#DIRSTACK[*]} )); do d=${DIRSTACK[fv]} (( fl )) && d=${d/#\~/$DIRSTACKBASE} - \builtin print -r -- "$fv $d" - \builtin let fv++ + \\builtin print -r -- "$fv $d" + (( ++fv )) done else fv=0 @@ -160,136 +195,117 @@ function dirs { (( dwidth = (${%d} > 0 ? ${%d} : ${#d}) )) if (( fn && (cpos += dwidth + 1) >= 79 && \ dwidth < 80 )); then - \builtin print + \\builtin print (( cpos = dwidth + 1 )) fi - \builtin print -nr -- "$d " - \builtin let fv++ + \\builtin print -nr -- "$d " + (( ++fv )) done - \builtin print + \\builtin print fi - \return 0 + \\builtin return 0 } function popd { - \typeset d fa - \typeset -i n=1 + \\builtin typeset d fa + \\builtin typeset -i n=1 - while \getopts ":0123456789lvn" d; do + while \\builtin getopts ":0123456789lvn" d; do case $d { (l|v|n) fa+=" -$d" ;; (+*) n=2 - \break ;; - (*) \builtin print -u2 'Usage: popd [-lvn] [+].' - \return 1 ;; + \\builtin break ;; + (*) \\builtin print -ru2 'Usage: popd [-lvn] [+].' + \\builtin return 1 ;; } done - \shift $((OPTIND - n)) + \\builtin shift $((OPTIND - n)) n=0 if (( $# > 1 )); then - \builtin print -u2 popd: Too many arguments. - \return 1 + \\builtin print -ru2 popd: Too many arguments. + \\builtin return 1 elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then - \builtin print -u2 popd: Directory stack not that deep. - \return 1 + \\builtin print -ru2 popd: Directory stack not that deep. + \\builtin return 1 fi elif [[ -n $1 ]]; then - \builtin print -u2 popd: Bad directory. - \return 1 + \\builtin print -ru2 popd: Bad directory. + \\builtin return 1 fi if (( ${#DIRSTACK[*]} < 2 )); then - \builtin print -u2 popd: Directory stack empty. - \return 1 + \\builtin print -ru2 popd: Directory stack empty. + \\builtin return 1 fi - \unset DIRSTACK[n] - \set -A DIRSTACK -- "${DIRSTACK[@]}" - \cd_csh "${DIRSTACK[0]}" || \return 1 + \\builtin unset DIRSTACK[n] + \\builtin set -A DIRSTACK -- "${DIRSTACK[@]}" + \cd_csh "${DIRSTACK[0]}" || \\builtin return 1 \dirs $fa } function pushd { - \typeset d fa - \typeset -i n=1 + \\builtin typeset d fa + \\builtin typeset -i n=1 - while \getopts ":0123456789lvn" d; do + while \\builtin getopts ":0123456789lvn" d; do case $d { (l|v|n) fa+=" -$d" ;; (+*) n=2 - \break ;; - (*) \builtin print -u2 'Usage: pushd [-lvn] [|+].' - \return 1 ;; + \\builtin break ;; + (*) \\builtin print -ru2 'Usage: pushd [-lvn] [|+].' + \\builtin return 1 ;; } done - \shift $((OPTIND - n)) + \\builtin shift $((OPTIND - n)) if (( $# == 0 )); then if (( ${#DIRSTACK[*]} < 2 )); then - \builtin print -u2 pushd: No other directory. - \return 1 + \\builtin print -ru2 pushd: No other directory. + \\builtin return 1 fi d=${DIRSTACK[1]} DIRSTACK[1]=${DIRSTACK[0]} - \cd_csh "$d" || \return 1 + \cd_csh "$d" || \\builtin return 1 elif (( $# > 1 )); then - \builtin print -u2 pushd: Too many arguments. - \return 1 + \\builtin print -ru2 pushd: Too many arguments. + \\builtin return 1 elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then - \builtin print -u2 pushd: Directory stack not that deep. - \return 1 + \\builtin print -ru2 pushd: Directory stack not that deep. + \\builtin return 1 fi while (( n-- )); do d=${DIRSTACK[0]} - \unset DIRSTACK[0] - \set -A DIRSTACK -- "${DIRSTACK[@]}" "$d" + \\builtin unset DIRSTACK[0] + \\builtin set -A DIRSTACK -- "${DIRSTACK[@]}" "$d" done - \cd_csh "${DIRSTACK[0]}" || \return 1 + \cd_csh "${DIRSTACK[0]}" || \\builtin return 1 else - \set -A DIRSTACK -- placeholder "${DIRSTACK[@]}" - \cd_csh "$1" || \return 1 + \\builtin set -A DIRSTACK -- placeholder "${DIRSTACK[@]}" + \cd_csh "$1" || \\builtin return 1 fi \dirs $fa } -# pager (not control character safe) -smores() ( - \set +m - \cat "$@" |& - \trap "rv=\$?; 'kill' $! >/dev/null 2>&1; 'exit' \$rv" EXIT - while IFS= \read -pr line; do - llen=${%line} - (( llen == -1 )) && llen=${#line} - (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 )) - if (( (curlin += llen) >= LINES )); then - \builtin print -n -- '\e[7m--more--\e[0m' - \read -u1 || \exit $? - [[ $REPLY = [Qq]* ]] && \exit 0 - curlin=$llen - fi - \builtin print -r -- "$line" - done -) - # base64 encoder and decoder, RFC compliant, NUL safe, not EBCDIC safe function Lb64decode { - \set +U - \typeset c s="$*" t - [[ -n $s ]] || { s=$(\cat; \builtin print x); s=${s%x}; } - \typeset -i i=0 j=0 n=${#s} p=0 v x - \typeset -i16 o + \\builtin set +U + \\builtin typeset c s="$*" t + [[ -n $s ]] || { s=$(\\builtin cat; \\builtin print x); s=${s%x}; } + \\builtin typeset -i i=0 j=0 n=${#s} p=0 v x + \\builtin typeset -i16 o while (( i < n )); do c=${s:(i++):1} case $c { - (=) \break ;; + (=) \\builtin break ;; ([A-Z]) (( v = 1#$c - 65 )) ;; ([a-z]) (( v = 1#$c - 71 )) ;; ([0-9]) (( v = 1#$c + 4 )) ;; (+) v=62 ;; (/) v=63 ;; - (*) \continue ;; + (*) \\builtin continue ;; } (( x = (x << 6) | v )) case $((p++)) { - (0) \continue ;; + (0) \\builtin continue ;; (1) (( o = (x >> 4) & 255 )) ;; (2) (( o = (x >> 2) & 255 )) ;; (3) (( o = x & 255 )) @@ -297,63 +313,60 @@ function Lb64decode { ;; } t+=\\x${o#16#} - (( ++j & 4095 )) && \continue - \builtin print -n $t + (( ++j & 4095 )) && \\builtin continue + \\builtin print -n $t t= done - \builtin print -n $t + \\builtin print -n $t } - -\set -A Lb64encode_tbl -- 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 { - \set +U - \typeset c s t + \\builtin set +U + \\builtin typeset c s t table + \\builtin set -A table -- 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 + / if (( $# )); then - \read -raN-1 s <<<"$*" - \unset s[${#s[*]}-1] + \\builtin read -raN-1 s <<<"$*" + \\builtin unset s[${#s[*]}-1] else - \read -raN-1 s + \\builtin read -raN-1 s fi - \typeset -i i=0 n=${#s[*]} j v + \\builtin typeset -i i=0 n=${#s[*]} v while (( i < n )); do (( v = s[i++] << 16 )) - (( j = i < n ? s[i++] : 0 )) - (( v |= j << 8 )) - (( j = i < n ? s[i++] : 0 )) - (( v |= j )) - t+=${Lb64encode_tbl[v >> 18]}${Lb64encode_tbl[v >> 12 & 63]} - c=${Lb64encode_tbl[v >> 6 & 63]} + (( v |= s[i++] << 8 )) + (( v |= s[i++] )) + t+=${table[v >> 18]}${table[v >> 12 & 63]} + c=${table[v >> 6 & 63]} if (( i <= n )); then - t+=$c${Lb64encode_tbl[v & 63]} + t+=$c${table[v & 63]} elif (( i == n + 1 )); then t+=$c= else t+=== fi if (( ${#t} == 76 || i >= n )); then - \builtin print $t + \\builtin print -r $t t= fi done } # Better Avalanche for the Jenkins Hash -\typeset -Z11 -Uui16 Lbafh_v +\\builtin typeset -Z11 -Uui16 Lbafh_v function Lbafh_init { Lbafh_v=0 } function Lbafh_add { - \set +U - \typeset s + \\builtin set +U + \\builtin typeset s if (( $# )); then - \read -raN-1 s <<<"$*" - \unset s[${#s[*]}-1] + \\builtin read -raN-1 s <<<"$*" + \\builtin unset s[${#s[*]}-1] else - \read -raN-1 s + \\builtin read -raN-1 s fi - \typeset -i i=0 n=${#s[*]} + \\builtin typeset -i i=0 n=${#s[*]} while (( i < n )); do ((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 )) @@ -361,7 +374,7 @@ function Lbafh_add { done } function Lbafh_finish { - \typeset -Ui t + \\builtin typeset -Ui t ((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \ ((Lbafh_v << 1) & 0xFEFEFEFE) )) @@ -373,42 +386,33 @@ function Lbafh_finish { # strip comments (and leading/trailing whitespace if IFS is set) from # any file(s) given as argument, or stdin if none, and spew to stdout function Lstripcom { - \set -o noglob - \cat "$@" | while \read _line; do + \\builtin set -o noglob + \\builtin cat "$@" | while \\builtin read _line; do _line=${_line%%#*} - [[ -n $_line ]] && \builtin print -r -- $_line + [[ -n $_line ]] && \\builtin print -r -- $_line done } -# give MidnightBSD's laffer1 a bit of csh feeling -function setenv { - if (( $# )); then - \eval '\export "$1"="${2:-}"' - else - \typeset -x - fi -} - # toggle built-in aliases and utilities, and aliases and functions from mkshrc function enable { - \typeset doprnt=0 mode=1 x y z rv=0 - \typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all - \set -A b_alias - \set -A i_alias - \set -A i_func + \\builtin typeset doprnt=0 mode=1 x y z rv=0 + \\builtin typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all + \\builtin set -A b_alias + \\builtin set -A i_alias + \\builtin set -A i_func # accumulate mksh built-in aliases, in ASCIIbetical order - i_alias[nalias]=autoload; b_alias[nalias++]='\typeset -fu' - i_alias[nalias]=functions; b_alias[nalias++]='\typeset -f' - i_alias[nalias]=hash; b_alias[nalias++]='\builtin alias -t' - i_alias[nalias]=history; b_alias[nalias++]='\builtin fc -l' - i_alias[nalias]=integer; b_alias[nalias++]='\typeset -i' - i_alias[nalias]=local; b_alias[nalias++]='\typeset' - i_alias[nalias]=login; b_alias[nalias++]='\exec login' - i_alias[nalias]=nameref; b_alias[nalias++]='\typeset -n' + i_alias[nalias]=autoload; b_alias[nalias++]='\\builtin typeset -fu' + i_alias[nalias]=functions; b_alias[nalias++]='\\builtin typeset -f' + i_alias[nalias]=hash; b_alias[nalias++]='\\builtin alias -t' + i_alias[nalias]=history; b_alias[nalias++]='\\builtin fc -l' + i_alias[nalias]=integer; b_alias[nalias++]='\\builtin typeset -i' + i_alias[nalias]=local; b_alias[nalias++]='\\builtin typeset' + i_alias[nalias]=login; b_alias[nalias++]='\\builtin exec login' + i_alias[nalias]=nameref; b_alias[nalias++]='\\builtin typeset -n' i_alias[nalias]=nohup; b_alias[nalias++]='nohup ' - i_alias[nalias]=r; b_alias[nalias++]='\builtin fc -e -' - i_alias[nalias]=type; b_alias[nalias++]='\builtin whence -v' + i_alias[nalias]=r; b_alias[nalias++]='\\builtin fc -e -' + i_alias[nalias]=type; b_alias[nalias++]='\\builtin whence -v' # accumulate mksh built-in utilities, in definition order, even ifndef i_func[nfunc++]=. @@ -416,6 +420,7 @@ function enable { i_func[nfunc++]='[' i_func[nfunc++]=alias i_func[nfunc++]=break + # \\builtin cannot, by design, be overridden i_func[nfunc++]=builtin i_func[nfunc++]=cat i_func[nfunc++]=cd @@ -434,7 +439,6 @@ function enable { i_func[nfunc++]=jobs i_func[nfunc++]=kill i_func[nfunc++]=let - i_func[nfunc++]='let]' i_func[nfunc++]=print i_func[nfunc++]=pwd i_func[nfunc++]=read @@ -471,11 +475,13 @@ function enable { i_alias[nalias]=la; b_alias[nalias++]='l -a' i_alias[nalias]=ll; b_alias[nalias++]='l -l' i_alias[nalias]=lo; b_alias[nalias++]='l -alo' - i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\builtin fc -ln -1)"' + i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\\builtin fc -ln -1)"' i_alias[nalias]=rot13; b_alias[nalias++]='tr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM' - i_alias[nalias]=cls; b_alias[nalias++]='\builtin print -n \\ec' + i_alias[nalias]=cls; b_alias[nalias++]='\\builtin print -n \\ec' # accumulate functions from dot.mkshrc, in definition order + i_func[nfunc++]=setenv + i_func[nfunc++]=smores i_func[nfunc++]=hd i_func[nfunc++]=chpwd i_func[nfunc++]=cd @@ -483,21 +489,19 @@ function enable { i_func[nfunc++]=dirs i_func[nfunc++]=popd i_func[nfunc++]=pushd - i_func[nfunc++]=smores i_func[nfunc++]=Lb64decode i_func[nfunc++]=Lb64encode i_func[nfunc++]=Lbafh_init i_func[nfunc++]=Lbafh_add i_func[nfunc++]=Lbafh_finish i_func[nfunc++]=Lstripcom - i_func[nfunc++]=setenv i_func[nfunc++]=enable # collect all identifiers, sorted ASCIIbetically - \set -sA i_all -- "${i_alias[@]}" "${i_func[@]}" + \\builtin set -sA i_all -- "${i_alias[@]}" "${i_func[@]}" # handle options, we don't do dynamic loading - while \getopts "adf:nps" x; do + while \\builtin getopts "adf:nps" x; do case $x { (a) mode=-1 @@ -506,8 +510,8 @@ function enable { # deliberately causing an error, like bash-static ;| (f) - \builtin print -u2 enable: dynamic loading not available - \return 2 + \\builtin print -ru2 enable: dynamic loading not available + \\builtin return 2 ;; (n) mode=0 @@ -516,88 +520,89 @@ function enable { doprnt=1 ;; (s) - \set -sA i_all -- . : break continue eval exec exit \ - export readonly return set shift times trap unset + \\builtin set -sA i_all -- . : break continue eval \ + exec exit export readonly return set shift times \ + trap unset ;; (*) - \builtin print -u2 enable: usage: \ + \\builtin print -ru2 enable: usage: \ "enable [-adnps] [-f filename] [name ...]" return 2 ;; } done - \shift $((OPTIND - 1)) + \\builtin shift $((OPTIND - 1)) # display builtins enabled/disabled/all/special? if (( doprnt || ($# == 0) )); then for x in "${i_all[@]}"; do - y=$(\alias "$x") || y= - [[ $y = "$x='\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)'" ]]; z=$? + y=$(\\builtin alias "$x") || y= + [[ $y = "$x='\\\\builtin whence -p $x >/dev/null || (\\\\builtin print -r mksh: $x: not found; \\\\builtin exit 127) && \$(\\\\builtin whence -p $x)'" ]]; z=$? case $mode:$z { (-1:0|0:0) - \print -r -- "enable -n $x" + \\builtin print -r -- "enable -n $x" ;; (-1:1|1:1) - \print -r -- "enable $x" + \\builtin print -r -- "enable $x" ;; } done - \return 0 + \\builtin return 0 fi for x in "$@"; do z=0 for y in "${i_alias[@]}" "${i_func[@]}"; do - [[ $x = "$y" ]] || \continue + [[ $x = "$y" ]] || \\builtin continue z=1 - \break + \\builtin break done if (( !z )); then - \builtin print -ru2 enable: "$x": not a shell builtin + \\builtin print -ru2 enable: "$x": not a shell builtin rv=1 - \continue + \\builtin continue fi if (( !mode )); then # disable this - \alias "$x=\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)" + \\builtin alias "$x=\\\\builtin whence -p $x >/dev/null || (\\\\builtin print -r mksh: $x: not found; \\\\builtin exit 127) && \$(\\\\builtin whence -p $x)" else # find out if this is an alias or not, first z=0 y=-1 while (( ++y < nalias )); do - [[ $x = "${i_alias[y]}" ]] || \continue + [[ $x = "${i_alias[y]}" ]] || \\builtin continue z=1 - \break + \\builtin break done if (( z )); then # re-enable the original alias body - \alias "$x=${b_alias[y]}" + \\builtin alias "$x=${b_alias[y]}" else # re-enable the original utility/function - \unalias "$x" + \\builtin unalias "$x" fi fi done - \return $rv + \\builtin return $rv } \: place customisations below this line for p in ~/.etc/bin ~/bin; do - [[ -d $p/. ]] || \continue - #XXX OS/2 - [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH + [[ -d $p/. ]] || \\builtin continue + [[ $PATHSEP$PATH$PATHSEP = *"$PATHSEP$p$PATHSEP"* ]] || \ + PATH=$p$PATHSEP$PATH done -\export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=- -\alias cls='\builtin print -n \\ec' +\\builtin export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=- +\\builtin alias cls='\\builtin print -n \\ec' -#\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \ +#\\builtin unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \ # LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME #p=en_GB.UTF-8 -#\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p -#\set -U +#\\builtin export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p +#\\builtin set -U -\unset p +\\builtin unset p \: place customisations above this line diff --git a/src/edit.c b/src/edit.c index 4ed5ca3..58eaf7f 100644 --- a/src/edit.c +++ b/src/edit.c @@ -5,7 +5,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -28,7 +28,7 @@ #ifndef MKSH_NO_CMDLINE_EDITING -__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.312 2016/11/11 23:48:28 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.321 2017/04/12 16:46:20 tg Exp $"); /* * in later versions we might use libtermcap for this, but since external @@ -145,6 +145,9 @@ x_read(char *buf) static int x_getc(void) { +#ifdef __OS2__ + return (_read_kbd(0, 1, 0)); +#else char c; ssize_t n; @@ -166,6 +169,7 @@ x_getc(void) x_mode(true); } return ((n == 1) ? (int)(unsigned char)c : -1); +#endif } static void @@ -339,7 +343,7 @@ x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag) * and if so, discern "~foo/bar" and "~/baz" from "~blah"; * if we have a directory part (the former), try to expand */ - if (*s == '~' && (cp = mksh_sdirsep(s)) != NULL) { + if (*s == '~' && (cp = /* not sdirsep */ strchr(s, '/')) != NULL) { /* ok, so split into "~foo"/"bar" or "~"/"baz" */ *cp++ = 0; /* try to expand the tilde */ @@ -658,7 +662,7 @@ x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp, } } - if (*toglob == '~' && !mksh_vdirsep(toglob)) { + if (*toglob == '~' && /* not vdirsep */ !vstrchr(toglob, '/')) { /* neither for '~foo' (but '~foo/bar') */ *flagsp |= XCF_IS_NOSPACE; goto dont_add_glob; @@ -949,6 +953,7 @@ static bool xlp_valid; /* lastvis pointer was recalculated */ static char **x_histp; /* history position */ static int x_nextcmd; /* for newline-and-next */ static char **x_histncp; /* saved x_histp for " */ +static char **x_histmcp; /* saved x_histp for " */ static char *xmp; /* mark pointer */ static unsigned char x_last_command; static unsigned char (*x_tab)[X_TABSZ]; /* key definition */ @@ -1163,6 +1168,7 @@ static void x_modified(void) { if (!modified) { + x_histmcp = x_histp; x_histp = histptr + 1; modified = 1; } @@ -1239,7 +1245,7 @@ x_emacs(char *buf) xlp_valid = true; xmp = NULL; x_curprefix = 0; - x_histp = histptr + 1; + x_histmcp = x_histp = histptr + 1; x_last_command = XFUNC_error; x_init_prompt(true); @@ -1780,12 +1786,11 @@ x_newline(int c MKSH_A_UNUSED) static int x_end_of_text(int c MKSH_A_UNUSED) { - unsigned char tmp; - char *cp = (void *)&tmp; + unsigned char tmp[1], *cp = tmp; - tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof : + *tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof : (unsigned char)CTRL('D'); - x_zotc3(&cp); + x_zotc3((char **)&cp); x_putc('\r'); x_putc('\n'); x_flush(); @@ -1862,9 +1867,11 @@ x_load_hist(char **hp) static int x_nl_next_com(int c MKSH_A_UNUSED) { - if (!x_histncp || (x_histp != x_histncp && x_histp != histptr + 1)) + if (!modified) + x_histmcp = x_histp; + if (!x_histncp || (x_histmcp != x_histncp && x_histmcp != histptr + 1)) /* fresh start of ^O */ - x_histncp = x_histp; + x_histncp = x_histmcp; x_nextcmd = source->line - (histptr - x_histncp) + 1; return (x_newline('\n')); } @@ -1922,8 +1929,10 @@ x_search_hist(int c) offset = -1; break; } - if (p > pat) - *--p = '\0'; + if (p > pat) { + p = x_bs0(p - 1, pat); + *p = '\0'; + } if (p == pat) offset = -1; else @@ -2933,8 +2942,12 @@ x_e_putc3(const char **cp) char *cp2; width = utf_widthadj(*cp, (const char **)&cp2); - while (*cp < cp2) - x_putcf(*(*cp)++); + if (cp2 == *cp + 1) { + (*cp)++; + shf_puts("\xEF\xBF\xBD", shl_out); + } else + while (*cp < cp2) + x_putcf(*(*cp)++); } else { (*cp)++; x_putc(c); @@ -3162,6 +3175,8 @@ x_prev_histword(int c MKSH_A_UNUSED) x_ins(cp); *rcp = ch; } + if (!modified) + x_histmcp = x_histp; modified = m + 1; return (KSTD); } @@ -3466,7 +3481,7 @@ static void free_edstate(struct edstate *old); static struct edstate ebuf; static struct edstate undobuf; -static struct edstate *es; /* current editor state */ +static struct edstate *vs; /* current Vi editing mode state */ static struct edstate *undo; static char *ibuf; /* input buffer */ @@ -3530,7 +3545,7 @@ x_vi(char *buf) undobuf.linelen = ebuf.linelen = 0; undobuf.cursor = ebuf.cursor = 0; undobuf.winleft = ebuf.winleft = 0; - es = &ebuf; + vs = &ebuf; undo = &undobuf; x_init_prompt(true); @@ -3581,7 +3596,7 @@ x_vi(char *buf) unwind(LSHELL); } else if (isched(c, edchars.eof) && state != VVERSION) { - if (es->linelen == 0) { + if (vs->linelen == 0) { x_vi_zotc(c); c = -1; break; @@ -3598,15 +3613,15 @@ x_vi(char *buf) x_putc('\n'); x_flush(); - if (c == -1 || (ssize_t)LINE <= es->linelen) + if (c == -1 || (ssize_t)LINE <= vs->linelen) return (-1); - if (es->cbuf != buf) - memcpy(buf, es->cbuf, es->linelen); + if (vs->cbuf != buf) + memcpy(buf, vs->cbuf, vs->linelen); - buf[es->linelen++] = '\n'; + buf[vs->linelen++] = '\n'; - return (es->linelen); + return (vs->linelen); } static int @@ -3643,7 +3658,7 @@ vi_hook(int ch) break; case 0: if (state == VLIT) { - es->cursor--; + vs->cursor--; refresh(0); } else refresh(insert != 0); @@ -3665,8 +3680,8 @@ vi_hook(int ch) state = nextstate(ch); if (state == VSEARCH) { save_cbuf(); - es->cursor = 0; - es->linelen = 0; + vs->cursor = 0; + vs->linelen = 0; if (putbuf(ch == '/' ? "/" : "?", 1, false) != 0) return (-1); @@ -3674,8 +3689,8 @@ vi_hook(int ch) } if (state == VVERSION) { save_cbuf(); - es->cursor = 0; - es->linelen = 0; + vs->cursor = 0; + vs->linelen = 0; putbuf(KSH_VERSION, strlen(KSH_VERSION), false); refresh(0); @@ -3686,10 +3701,10 @@ vi_hook(int ch) case VLIT: if (is_bad(ch)) { - del_range(es->cursor, es->cursor + 1); + del_range(vs->cursor, vs->cursor + 1); vi_error(); } else - es->cbuf[es->cursor++] = ch; + vs->cbuf[vs->cursor++] = ch; refresh(1); state = VNORMAL; break; @@ -3772,8 +3787,8 @@ vi_hook(int ch) } else if (isched(ch, edchars.erase) || ch == CTRL('h')) { if (srchlen != 0) { srchlen--; - es->linelen -= char_len(locpat[srchlen]); - es->cursor = es->linelen; + vs->linelen -= char_len(locpat[srchlen]); + vs->cursor = vs->linelen; refresh(0); return (0); } @@ -3782,8 +3797,8 @@ vi_hook(int ch) refresh(0); } else if (isched(ch, edchars.kill)) { srchlen = 0; - es->linelen = 1; - es->cursor = 1; + vs->linelen = 1; + vs->cursor = 1; refresh(0); return (0); } else if (isched(ch, edchars.werase)) { @@ -3793,16 +3808,16 @@ vi_hook(int ch) new_es.cursor = srchlen; new_es.cbuf = locpat; - save_es = es; - es = &new_es; + save_es = vs; + vs = &new_es; n = backword(1); - es = save_es; + vs = save_es; i = (unsigned)srchlen; while (--i >= n) - es->linelen -= char_len(locpat[i]); + vs->linelen -= char_len(locpat[i]); srchlen = (int)n; - es->cursor = es->linelen; + vs->cursor = vs->linelen; refresh(0); return (0); } else { @@ -3811,17 +3826,17 @@ vi_hook(int ch) else { locpat[srchlen++] = ch; if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) { - if ((size_t)es->linelen + 2 > - (size_t)es->cbufsize) + if ((size_t)vs->linelen + 2 > + (size_t)vs->cbufsize) vi_error(); - es->cbuf[es->linelen++] = '^'; - es->cbuf[es->linelen++] = UNCTRL(ch); + vs->cbuf[vs->linelen++] = '^'; + vs->cbuf[vs->linelen++] = UNCTRL(ch); } else { - if (es->linelen >= es->cbufsize) + if (vs->linelen >= vs->cbufsize) vi_error(); - es->cbuf[es->linelen++] = ch; + vs->cbuf[vs->linelen++] = ch; } - es->cursor = es->linelen; + vs->cursor = vs->linelen; refresh(0); } return (0); @@ -3834,18 +3849,18 @@ vi_hook(int ch) switch (ch) { case 'A': /* the cursor may not be at the BOL */ - if (!es->cursor) + if (!vs->cursor) break; /* nor further in the line than we can search for */ - if ((size_t)es->cursor >= sizeof(srchpat) - 1) - es->cursor = sizeof(srchpat) - 2; + if ((size_t)vs->cursor >= sizeof(srchpat) - 1) + vs->cursor = sizeof(srchpat) - 2; /* anchor the search pattern */ srchpat[0] = '^'; /* take the current line up to the cursor */ - memmove(srchpat + 1, es->cbuf, es->cursor); - srchpat[es->cursor + 1] = '\0'; + memmove(srchpat + 1, vs->cbuf, vs->cursor); + srchpat[vs->cursor + 1] = '\0'; /* set a magic flag */ - argc1 = 2 + (int)es->cursor; + argc1 = 2 + (int)vs->cursor; /* and emulate a backwards history search */ lastsearch = '/'; *curcmd = 'n'; @@ -3942,52 +3957,52 @@ vi_insert(int ch) if (isched(ch, edchars.erase) || ch == CTRL('h')) { if (insert == REPLACE) { - if (es->cursor == undo->cursor) { + if (vs->cursor == undo->cursor) { vi_error(); return (0); } if (inslen > 0) inslen--; - es->cursor--; - if (es->cursor >= undo->linelen) - es->linelen--; + vs->cursor--; + if (vs->cursor >= undo->linelen) + vs->linelen--; else - es->cbuf[es->cursor] = undo->cbuf[es->cursor]; + vs->cbuf[vs->cursor] = undo->cbuf[vs->cursor]; } else { - if (es->cursor == 0) + if (vs->cursor == 0) return (0); if (inslen > 0) inslen--; - es->cursor--; - es->linelen--; - memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1], - es->linelen - es->cursor + 1); + vs->cursor--; + vs->linelen--; + memmove(&vs->cbuf[vs->cursor], &vs->cbuf[vs->cursor + 1], + vs->linelen - vs->cursor + 1); } expanded = NONE; return (0); } if (isched(ch, edchars.kill)) { - if (es->cursor != 0) { + if (vs->cursor != 0) { inslen = 0; - memmove(es->cbuf, &es->cbuf[es->cursor], - es->linelen - es->cursor); - es->linelen -= es->cursor; - es->cursor = 0; + memmove(vs->cbuf, &vs->cbuf[vs->cursor], + vs->linelen - vs->cursor); + vs->linelen -= vs->cursor; + vs->cursor = 0; } expanded = NONE; return (0); } if (isched(ch, edchars.werase)) { - if (es->cursor != 0) { + if (vs->cursor != 0) { tcursor = backword(1); - memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor], - es->linelen - es->cursor); - es->linelen -= es->cursor - tcursor; - if (inslen < es->cursor - tcursor) + memmove(&vs->cbuf[tcursor], &vs->cbuf[vs->cursor], + vs->linelen - vs->cursor); + vs->linelen -= vs->cursor - tcursor; + if (inslen < vs->cursor - tcursor) inslen = 0; else - inslen -= es->cursor - tcursor; - es->cursor = tcursor; + inslen -= vs->cursor - tcursor; + vs->cursor = tcursor; } expanded = NONE; return (0); @@ -4034,7 +4049,7 @@ vi_insert(int ch) break; case CTRL('e'): - print_expansions(es, 0); + print_expansions(vs, 0); break; case CTRL('i'): @@ -4046,17 +4061,17 @@ vi_insert(int ch) /* end nonstandard vi commands } */ default: - if (es->linelen >= es->cbufsize - 1) + if (vs->linelen >= vs->cbufsize - 1) return (-1); ibuf[inslen++] = ch; if (insert == INSERT) { - memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor], - es->linelen - es->cursor); - es->linelen++; + memmove(&vs->cbuf[vs->cursor + 1], &vs->cbuf[vs->cursor], + vs->linelen - vs->cursor); + vs->linelen++; } - es->cbuf[es->cursor++] = ch; - if (insert == REPLACE && es->cursor > es->linelen) - es->linelen++; + vs->cbuf[vs->cursor++] = ch; + if (insert == REPLACE && vs->cursor > vs->linelen) + vs->linelen++; expanded = NONE; } return (0); @@ -4075,18 +4090,18 @@ vi_cmd(int argcnt, const char *cmd) if (is_move(*cmd)) { if ((cur = domove(argcnt, cmd, 0)) >= 0) { - if (cur == es->linelen && cur != 0) + if (cur == vs->linelen && cur != 0) cur--; - es->cursor = cur; + vs->cursor = cur; } else return (-1); } else { /* Don't save state in middle of macro.. */ if (is_undoable(*cmd) && !macro.p) { - undo->winleft = es->winleft; - memmove(undo->cbuf, es->cbuf, es->linelen); - undo->linelen = es->linelen; - undo->cursor = es->cursor; + undo->winleft = vs->winleft; + memmove(undo->cbuf, vs->cbuf, vs->linelen); + undo->linelen = vs->linelen; + undo->cursor = vs->cursor; lastac = argcnt; memmove(lastcmd, cmd, MAXVICMD); } @@ -4141,8 +4156,8 @@ vi_cmd(int argcnt, const char *cmd) case 'a': modified = 1; hnum = hlast; - if (es->linelen != 0) - es->cursor++; + if (vs->linelen != 0) + vs->cursor++; insert = INSERT; break; @@ -4150,13 +4165,13 @@ vi_cmd(int argcnt, const char *cmd) modified = 1; hnum = hlast; del_range(0, 0); - es->cursor = es->linelen; + vs->cursor = vs->linelen; insert = INSERT; break; case 'S': - es->cursor = domove(1, "^", 1); - del_range(es->cursor, es->linelen); + vs->cursor = domove(1, "^", 1); + del_range(vs->cursor, vs->linelen); modified = 1; hnum = hlast; insert = INSERT; @@ -4172,7 +4187,7 @@ vi_cmd(int argcnt, const char *cmd) case 'y': if (*cmd == cmd[1]) { c1 = *cmd == 'c' ? domove(1, "^", 1) : 0; - c2 = es->linelen; + c2 = vs->linelen; } else if (!is_move(cmd[1])) return (-1); else { @@ -4180,18 +4195,18 @@ vi_cmd(int argcnt, const char *cmd) return (-1); if (*cmd == 'c' && (cmd[1] == 'w' || cmd[1] == 'W') && - !ksh_isspace(es->cbuf[es->cursor])) { + !ksh_isspace(vs->cbuf[vs->cursor])) { do { --ncursor; - } while (ksh_isspace(es->cbuf[ncursor])); + } while (ksh_isspace(vs->cbuf[ncursor])); ncursor++; } - if (ncursor > es->cursor) { - c1 = es->cursor; + if (ncursor > vs->cursor) { + c1 = vs->cursor; c2 = ncursor; } else { c1 = ncursor; - c2 = es->cursor; + c2 = vs->cursor; if (cmd[1] == '%') c2++; } @@ -4200,7 +4215,7 @@ vi_cmd(int argcnt, const char *cmd) yank_range(c1, c2); if (*cmd != 'y') { del_range(c1, c2); - es->cursor = c1; + vs->cursor = c1; } if (*cmd == 'c') { modified = 1; @@ -4212,13 +4227,13 @@ vi_cmd(int argcnt, const char *cmd) case 'p': modified = 1; hnum = hlast; - if (es->linelen != 0) - es->cursor++; + if (vs->linelen != 0) + vs->cursor++; while (putbuf(ybuf, yanklen, false) == 0 && --argcnt > 0) ; - if (es->cursor != 0) - es->cursor--; + if (vs->cursor != 0) + vs->cursor--; if (argcnt != 0) return (-1); break; @@ -4230,8 +4245,8 @@ vi_cmd(int argcnt, const char *cmd) while (putbuf(ybuf, yanklen, false) == 0 && --argcnt > 0) any = 1; - if (any && es->cursor != 0) - es->cursor--; + if (any && vs->cursor != 0) + vs->cursor--; if (argcnt != 0) return (-1); break; @@ -4239,15 +4254,15 @@ vi_cmd(int argcnt, const char *cmd) case 'C': modified = 1; hnum = hlast; - del_range(es->cursor, es->linelen); + del_range(vs->cursor, vs->linelen); insert = INSERT; break; case 'D': - yank_range(es->cursor, es->linelen); - del_range(es->cursor, es->linelen); - if (es->cursor != 0) - es->cursor--; + yank_range(vs->cursor, vs->linelen); + del_range(vs->cursor, vs->linelen); + if (vs->cursor != 0) + vs->cursor--; break; case 'g': @@ -4276,7 +4291,7 @@ vi_cmd(int argcnt, const char *cmd) case 'I': modified = 1; hnum = hlast; - es->cursor = domove(1, "^", 1); + vs->cursor = domove(1, "^", 1); insert = INSERT; break; @@ -4303,7 +4318,7 @@ vi_cmd(int argcnt, const char *cmd) break; case 'r': - if (es->linelen == 0) + if (vs->linelen == 0) return (-1); modified = 1; hnum = hlast; @@ -4312,11 +4327,11 @@ vi_cmd(int argcnt, const char *cmd) else { int n; - if (es->cursor + argcnt > es->linelen) + if (vs->cursor + argcnt > vs->linelen) return (-1); for (n = 0; n < argcnt; ++n) - es->cbuf[es->cursor + n] = cmd[1]; - es->cursor += n - 1; + vs->cbuf[vs->cursor + n] = cmd[1]; + vs->cursor += n - 1; } break; @@ -4327,66 +4342,66 @@ vi_cmd(int argcnt, const char *cmd) break; case 's': - if (es->linelen == 0) + if (vs->linelen == 0) return (-1); modified = 1; hnum = hlast; - if (es->cursor + argcnt > es->linelen) - argcnt = es->linelen - es->cursor; - del_range(es->cursor, es->cursor + argcnt); + if (vs->cursor + argcnt > vs->linelen) + argcnt = vs->linelen - vs->cursor; + del_range(vs->cursor, vs->cursor + argcnt); insert = INSERT; break; case 'v': if (!argcnt) { - if (es->linelen == 0) + if (vs->linelen == 0) return (-1); if (modified) { - es->cbuf[es->linelen] = '\0'; - histsave(&source->line, es->cbuf, + vs->cbuf[vs->linelen] = '\0'; + histsave(&source->line, vs->cbuf, HIST_STORE, true); } else argcnt = source->line + 1 - (hlast - hnum); } if (argcnt) - shf_snprintf(es->cbuf, es->cbufsize, Tf_sd, + shf_snprintf(vs->cbuf, vs->cbufsize, Tf_sd, "fc -e ${VISUAL:-${EDITOR:-vi}} --", argcnt); else - strlcpy(es->cbuf, + strlcpy(vs->cbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", - es->cbufsize); - es->linelen = strlen(es->cbuf); + vs->cbufsize); + vs->linelen = strlen(vs->cbuf); return (2); case 'x': - if (es->linelen == 0) + if (vs->linelen == 0) return (-1); modified = 1; hnum = hlast; - if (es->cursor + argcnt > es->linelen) - argcnt = es->linelen - es->cursor; - yank_range(es->cursor, es->cursor + argcnt); - del_range(es->cursor, es->cursor + argcnt); + if (vs->cursor + argcnt > vs->linelen) + argcnt = vs->linelen - vs->cursor; + yank_range(vs->cursor, vs->cursor + argcnt); + del_range(vs->cursor, vs->cursor + argcnt); break; case 'X': - if (es->cursor > 0) { + if (vs->cursor > 0) { modified = 1; hnum = hlast; - if (es->cursor < argcnt) - argcnt = es->cursor; - yank_range(es->cursor - argcnt, es->cursor); - del_range(es->cursor - argcnt, es->cursor); - es->cursor -= argcnt; + if (vs->cursor < argcnt) + argcnt = vs->cursor; + yank_range(vs->cursor - argcnt, vs->cursor); + del_range(vs->cursor - argcnt, vs->cursor); + vs->cursor -= argcnt; } else return (-1); break; case 'u': - t = es; - es = undo; + t = vs; + vs = undo; undo = t; break; @@ -4434,7 +4449,7 @@ vi_cmd(int argcnt, const char *cmd) } if (argcnt >= 2) { /* flag from cursor-up command */ - es->cursor = argcnt - 2; + vs->cursor = argcnt - 2; return (0); } break; @@ -4475,16 +4490,16 @@ vi_cmd(int argcnt, const char *cmd) } modified = 1; hnum = hlast; - if (es->cursor != es->linelen) - es->cursor++; + if (vs->cursor != vs->linelen) + vs->cursor++; while (*p && !issp(*p)) { argcnt++; p++; } if (putbuf(T1space, 1, false) != 0 || putbuf(sp, argcnt, false) != 0) { - if (es->cursor != 0) - es->cursor--; + if (vs->cursor != 0) + vs->cursor--; return (-1); } insert = INSERT; @@ -4496,10 +4511,10 @@ vi_cmd(int argcnt, const char *cmd) char *p; int i; - if (es->linelen == 0) + if (vs->linelen == 0) return (-1); for (i = 0; i < argcnt; i++) { - p = &es->cbuf[es->cursor]; + p = &vs->cbuf[vs->cursor]; if (ksh_islower(*p)) { modified = 1; hnum = hlast; @@ -4509,18 +4524,18 @@ vi_cmd(int argcnt, const char *cmd) hnum = hlast; *p = ksh_tolower(*p); } - if (es->cursor < es->linelen - 1) - es->cursor++; + if (vs->cursor < vs->linelen - 1) + vs->cursor++; } break; } case '#': { - int ret = x_do_comment(es->cbuf, es->cbufsize, - &es->linelen); + int ret = x_do_comment(vs->cbuf, vs->cbufsize, + &vs->linelen); if (ret >= 0) - es->cursor = 0; + vs->cursor = 0; return (ret); } @@ -4528,7 +4543,7 @@ vi_cmd(int argcnt, const char *cmd) case '=': /* Nonstandard vi/ksh */ case CTRL('e'): - print_expansions(es, 1); + print_expansions(vs, 1); break; @@ -4564,13 +4579,13 @@ vi_cmd(int argcnt, const char *cmd) case '[': case 'O': state = VPREFIX2; - if (es->linelen != 0) - es->cursor++; + if (vs->linelen != 0) + vs->cursor++; insert = INSERT; return (0); } - if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen) - es->cursor--; + if (insert == 0 && vs->cursor != 0 && vs->cursor >= vs->linelen) + vs->cursor--; } return (0); } @@ -4583,30 +4598,30 @@ domove(int argcnt, const char *cmd, int sub) switch (*cmd) { case 'b': - if (!sub && es->cursor == 0) + if (!sub && vs->cursor == 0) return (-1); ncursor = backword(argcnt); break; case 'B': - if (!sub && es->cursor == 0) + if (!sub && vs->cursor == 0) return (-1); ncursor = Backword(argcnt); break; case 'e': - if (!sub && es->cursor + 1 >= es->linelen) + if (!sub && vs->cursor + 1 >= vs->linelen) return (-1); ncursor = endword(argcnt); - if (sub && ncursor < es->linelen) + if (sub && ncursor < vs->linelen) ncursor++; break; case 'E': - if (!sub && es->cursor + 1 >= es->linelen) + if (!sub && vs->cursor + 1 >= vs->linelen) return (-1); ncursor = Endword(argcnt); - if (sub && ncursor < es->linelen) + if (sub && ncursor < vs->linelen) ncursor++; break; @@ -4634,32 +4649,32 @@ domove(int argcnt, const char *cmd, int sub) case 'h': case CTRL('h'): - if (!sub && es->cursor == 0) + if (!sub && vs->cursor == 0) return (-1); - ncursor = es->cursor - argcnt; + ncursor = vs->cursor - argcnt; if (ncursor < 0) ncursor = 0; break; case ' ': case 'l': - if (!sub && es->cursor + 1 >= es->linelen) + if (!sub && vs->cursor + 1 >= vs->linelen) return (-1); - if (es->linelen != 0) { - ncursor = es->cursor + argcnt; - if (ncursor > es->linelen) - ncursor = es->linelen; + if (vs->linelen != 0) { + ncursor = vs->cursor + argcnt; + if (ncursor > vs->linelen) + ncursor = vs->linelen; } break; case 'w': - if (!sub && es->cursor + 1 >= es->linelen) + if (!sub && vs->cursor + 1 >= vs->linelen) return (-1); ncursor = forwword(argcnt); break; case 'W': - if (!sub && es->cursor + 1 >= es->linelen) + if (!sub && vs->cursor + 1 >= vs->linelen) return (-1); ncursor = Forwword(argcnt); break; @@ -4670,43 +4685,43 @@ domove(int argcnt, const char *cmd, int sub) case '^': ncursor = 0; - while (ncursor < es->linelen - 1 && - ksh_isspace(es->cbuf[ncursor])) + while (ncursor < vs->linelen - 1 && + ksh_isspace(vs->cbuf[ncursor])) ncursor++; break; case '|': ncursor = argcnt; - if (ncursor > es->linelen) - ncursor = es->linelen; + if (ncursor > vs->linelen) + ncursor = vs->linelen; if (ncursor) ncursor--; break; case '$': - if (es->linelen != 0) - ncursor = es->linelen; + if (vs->linelen != 0) + ncursor = vs->linelen; else ncursor = 0; break; case '%': - ncursor = es->cursor; - while (ncursor < es->linelen && - (i = bracktype(es->cbuf[ncursor])) == 0) + ncursor = vs->cursor; + while (ncursor < vs->linelen && + (i = bracktype(vs->cbuf[ncursor])) == 0) ncursor++; - if (ncursor == es->linelen) + if (ncursor == vs->linelen) return (-1); bcount = 1; do { if (i > 0) { - if (++ncursor >= es->linelen) + if (++ncursor >= vs->linelen) return (-1); } else { if (--ncursor < 0) return (-1); } - t = bracktype(es->cbuf[ncursor]); + t = bracktype(vs->cbuf[ncursor]); if (t == i) bcount++; else if (t == -i) @@ -4728,8 +4743,8 @@ redo_insert(int count) while (count-- > 0) if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0) return (-1); - if (es->cursor > 0) - es->cursor--; + if (vs->cursor > 0) + vs->cursor--; insert = 0; return (0); } @@ -4739,7 +4754,7 @@ yank_range(int a, int b) { yanklen = b - a; if (yanklen != 0) - memmove(ybuf, &es->cbuf[a], yanklen); + memmove(ybuf, &vs->cbuf[a], yanklen); } static int @@ -4777,17 +4792,17 @@ bracktype(int ch) static void save_cbuf(void) { - memmove(holdbufp, es->cbuf, es->linelen); - holdlen = es->linelen; + memmove(holdbufp, vs->cbuf, vs->linelen); + holdlen = vs->linelen; holdbufp[holdlen] = '\0'; } static void restore_cbuf(void) { - es->cursor = 0; - es->linelen = holdlen; - memmove(es->cbuf, holdbufp, holdlen); + vs->cursor = 0; + vs->linelen = holdlen; + memmove(vs->cbuf, holdbufp, holdlen); } /* return a new edstate */ @@ -4838,28 +4853,28 @@ putbuf(const char *buf, ssize_t len, bool repl) if (len == 0) return (0); if (repl) { - if (es->cursor + len >= es->cbufsize) + if (vs->cursor + len >= vs->cbufsize) return (-1); - if (es->cursor + len > es->linelen) - es->linelen = es->cursor + len; + if (vs->cursor + len > vs->linelen) + vs->linelen = vs->cursor + len; } else { - if (es->linelen + len >= es->cbufsize) + if (vs->linelen + len >= vs->cbufsize) return (-1); - memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor], - es->linelen - es->cursor); - es->linelen += len; + memmove(&vs->cbuf[vs->cursor + len], &vs->cbuf[vs->cursor], + vs->linelen - vs->cursor); + vs->linelen += len; } - memmove(&es->cbuf[es->cursor], buf, len); - es->cursor += len; + memmove(&vs->cbuf[vs->cursor], buf, len); + vs->cursor += len; return (0); } static void del_range(int a, int b) { - if (es->linelen != b) - memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b); - es->linelen -= b - a; + if (vs->linelen != b) + memmove(&vs->cbuf[a], &vs->cbuf[b], vs->linelen - b); + vs->linelen -= b - a; } static int @@ -4867,19 +4882,19 @@ findch(int ch, int cnt, bool forw, bool incl) { int ncursor; - if (es->linelen == 0) + if (vs->linelen == 0) return (-1); - ncursor = es->cursor; + ncursor = vs->cursor; while (cnt--) { do { if (forw) { - if (++ncursor == es->linelen) + if (++ncursor == vs->linelen) return (-1); } else { if (--ncursor < 0) return (-1); } - } while (es->cbuf[ncursor] != ch); + } while (vs->cbuf[ncursor] != ch); } if (!incl) { if (forw) @@ -4895,19 +4910,19 @@ forwword(int argcnt) { int ncursor; - ncursor = es->cursor; - while (ncursor < es->linelen && argcnt--) { - if (ksh_isalnux(es->cbuf[ncursor])) - while (ncursor < es->linelen && - ksh_isalnux(es->cbuf[ncursor])) + ncursor = vs->cursor; + while (ncursor < vs->linelen && argcnt--) { + if (ksh_isalnux(vs->cbuf[ncursor])) + while (ncursor < vs->linelen && + ksh_isalnux(vs->cbuf[ncursor])) ncursor++; - else if (!ksh_isspace(es->cbuf[ncursor])) - while (ncursor < es->linelen && - !ksh_isalnux(es->cbuf[ncursor]) && - !ksh_isspace(es->cbuf[ncursor])) + else if (!ksh_isspace(vs->cbuf[ncursor])) + while (ncursor < vs->linelen && + !ksh_isalnux(vs->cbuf[ncursor]) && + !ksh_isspace(vs->cbuf[ncursor])) ncursor++; - while (ncursor < es->linelen && - ksh_isspace(es->cbuf[ncursor])) + while (ncursor < vs->linelen && + ksh_isspace(vs->cbuf[ncursor])) ncursor++; } return (ncursor); @@ -4918,19 +4933,19 @@ backword(int argcnt) { int ncursor; - ncursor = es->cursor; + ncursor = vs->cursor; while (ncursor > 0 && argcnt--) { - while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor])) + while (--ncursor > 0 && ksh_isspace(vs->cbuf[ncursor])) ; if (ncursor > 0) { - if (ksh_isalnux(es->cbuf[ncursor])) + if (ksh_isalnux(vs->cbuf[ncursor])) while (--ncursor >= 0 && - ksh_isalnux(es->cbuf[ncursor])) + ksh_isalnux(vs->cbuf[ncursor])) ; else while (--ncursor >= 0 && - !ksh_isalnux(es->cbuf[ncursor]) && - !ksh_isspace(es->cbuf[ncursor])) + !ksh_isalnux(vs->cbuf[ncursor]) && + !ksh_isspace(vs->cbuf[ncursor])) ; ncursor++; } @@ -4943,20 +4958,20 @@ endword(int argcnt) { int ncursor; - ncursor = es->cursor; - while (ncursor < es->linelen && argcnt--) { - while (++ncursor < es->linelen - 1 && - ksh_isspace(es->cbuf[ncursor])) + ncursor = vs->cursor; + while (ncursor < vs->linelen && argcnt--) { + while (++ncursor < vs->linelen - 1 && + ksh_isspace(vs->cbuf[ncursor])) ; - if (ncursor < es->linelen - 1) { - if (ksh_isalnux(es->cbuf[ncursor])) - while (++ncursor < es->linelen && - ksh_isalnux(es->cbuf[ncursor])) + if (ncursor < vs->linelen - 1) { + if (ksh_isalnux(vs->cbuf[ncursor])) + while (++ncursor < vs->linelen && + ksh_isalnux(vs->cbuf[ncursor])) ; else - while (++ncursor < es->linelen && - !ksh_isalnux(es->cbuf[ncursor]) && - !ksh_isspace(es->cbuf[ncursor])) + while (++ncursor < vs->linelen && + !ksh_isalnux(vs->cbuf[ncursor]) && + !ksh_isspace(vs->cbuf[ncursor])) ; ncursor--; } @@ -4969,13 +4984,13 @@ Forwword(int argcnt) { int ncursor; - ncursor = es->cursor; - while (ncursor < es->linelen && argcnt--) { - while (ncursor < es->linelen && - !ksh_isspace(es->cbuf[ncursor])) + ncursor = vs->cursor; + while (ncursor < vs->linelen && argcnt--) { + while (ncursor < vs->linelen && + !ksh_isspace(vs->cbuf[ncursor])) ncursor++; - while (ncursor < es->linelen && - ksh_isspace(es->cbuf[ncursor])) + while (ncursor < vs->linelen && + ksh_isspace(vs->cbuf[ncursor])) ncursor++; } return (ncursor); @@ -4986,11 +5001,11 @@ Backword(int argcnt) { int ncursor; - ncursor = es->cursor; + ncursor = vs->cursor; while (ncursor > 0 && argcnt--) { - while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor])) + while (--ncursor >= 0 && ksh_isspace(vs->cbuf[ncursor])) ; - while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor])) + while (ncursor >= 0 && !ksh_isspace(vs->cbuf[ncursor])) ncursor--; ncursor++; } @@ -5002,14 +5017,14 @@ Endword(int argcnt) { int ncursor; - ncursor = es->cursor; - while (ncursor < es->linelen - 1 && argcnt--) { - while (++ncursor < es->linelen - 1 && - ksh_isspace(es->cbuf[ncursor])) + ncursor = vs->cursor; + while (ncursor < vs->linelen - 1 && argcnt--) { + while (++ncursor < vs->linelen - 1 && + ksh_isspace(vs->cbuf[ncursor])) ; - if (ncursor < es->linelen - 1) { - while (++ncursor < es->linelen && - !ksh_isspace(es->cbuf[ncursor])) + if (ncursor < vs->linelen - 1) { + while (++ncursor < vs->linelen && + !ksh_isspace(vs->cbuf[ncursor])) ; ncursor--; } @@ -5036,10 +5051,10 @@ grabhist(int save, int n) } if (save) save_cbuf(); - if ((es->linelen = strlen(hptr)) >= es->cbufsize) - es->linelen = es->cbufsize - 1; - memmove(es->cbuf, hptr, es->linelen); - es->cursor = 0; + if ((vs->linelen = strlen(hptr)) >= vs->cbufsize) + vs->linelen = vs->cbufsize - 1; + memmove(vs->cbuf, hptr, vs->linelen); + vs->cursor = 0; ohnum = n; return (0); } @@ -5070,10 +5085,10 @@ grabsearch(int save, int start, int fwd, const char *pat) save_cbuf(); histnum(hist); hptr = *histpos(); - if ((es->linelen = strlen(hptr)) >= es->cbufsize) - es->linelen = es->cbufsize - 1; - memmove(es->cbuf, hptr, es->linelen); - es->cursor = 0; + if ((vs->linelen = strlen(hptr)) >= vs->cbufsize) + vs->linelen = vs->cbufsize - 1; + memmove(vs->cbuf, hptr, vs->linelen); + vs->cursor = 0; return (hist); } @@ -5108,12 +5123,12 @@ outofwin(void) { int cur, col; - if (es->cursor < es->winleft) + if (vs->cursor < vs->winleft) return (1); col = 0; - cur = es->winleft; - while (cur < es->cursor) - col = newcol((unsigned char)es->cbuf[cur++], col); + cur = vs->winleft; + while (cur < vs->cursor) + col = newcol((unsigned char)vs->cbuf[cur++], col); if (col >= winwidth) return (1); return (0); @@ -5128,19 +5143,19 @@ rewindow(void) holdcur1 = holdcur2 = tcur = 0; holdcol1 = holdcol2 = tcol = 0; - while (tcur < es->cursor) { + while (tcur < vs->cursor) { if (tcol - holdcol2 > winwidth / 2) { holdcur1 = holdcur2; holdcol1 = holdcol2; holdcur2 = tcur; holdcol2 = tcol; } - tcol = newcol((unsigned char)es->cbuf[tcur++], tcol); + tcol = newcol((unsigned char)vs->cbuf[tcur++], tcol); } while (tcol - holdcol1 > winwidth / 2) - holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++], + holdcol1 = newcol((unsigned char)vs->cbuf[holdcur1++], holdcol1); - es->winleft = holdcur1; + vs->winleft = holdcur1; } static int @@ -5161,13 +5176,13 @@ display(char *wb1, char *wb2, int leftside) int moreright; col = 0; - cur = es->winleft; + cur = vs->winleft; moreright = 0; twb1 = wb1; - while (col < winwidth && cur < es->linelen) { - if (cur == es->cursor && leftside) + while (col < winwidth && cur < vs->linelen) { + if (cur == vs->cursor && leftside) ncol = col + pwidth; - if ((ch = es->cbuf[cur]) == '\t') + if ((ch = vs->cbuf[cur]) == '\t') do { *twb1++ = ' '; } while (++col < winwidth && (col & 7) != 0); @@ -5183,11 +5198,11 @@ display(char *wb1, char *wb2, int leftside) col++; } } - if (cur == es->cursor && !leftside) + if (cur == vs->cursor && !leftside) ncol = col + pwidth - 1; cur++; } - if (cur == es->cursor) + if (cur == vs->cursor) ncol = col + pwidth; if (col < winwidth) { while (col < winwidth) { @@ -5213,13 +5228,13 @@ display(char *wb1, char *wb2, int leftside) twb2++; col++; } - if (es->winleft > 0 && moreright) + if (vs->winleft > 0 && moreright) /* * POSIX says to use * for this but that is a globbing * character and may confuse people; + is more innocuous */ mc = '+'; - else if (es->winleft > 0) + else if (vs->winleft > 0) mc = '<'; else if (moreright) mc = '>'; @@ -5267,7 +5282,7 @@ expand_word(int cmd) /* Undo previous expansion */ if (cmd == 0 && expanded == EXPAND && buf) { - restore_edstate(es, buf); + restore_edstate(vs, buf); buf = 0; expanded = NONE; return (0); @@ -5278,17 +5293,17 @@ expand_word(int cmd) } i = XCF_COMMAND_FILE | XCF_FULLPATH; - nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor, + nwords = x_cf_glob(&i, vs->cbuf, vs->linelen, vs->cursor, &start, &end, &words); if (nwords == 0) { vi_error(); return (-1); } - buf = save_edstate(es); + buf = save_edstate(vs); expanded = EXPAND; del_range(start, end); - es->cursor = start; + vs->cursor = start; i = 0; while (i < nwords) { if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) { @@ -5302,7 +5317,7 @@ expand_word(int cmd) } i = buf->cursor - end; if (rval == 0 && i > 0) - es->cursor += i; + vs->cursor += i; modified = 1; hnum = hlast; insert = INSERT; @@ -5328,7 +5343,7 @@ complete_word(int cmd, int count) return (0); } if (cmd == 0 && expanded == PRINT && buf) { - restore_edstate(es, buf); + restore_edstate(vs, buf); buf = 0; expanded = NONE; return (0); @@ -5345,7 +5360,7 @@ complete_word(int cmd, int count) flags = XCF_COMMAND_FILE; if (count) flags |= XCF_FULLPATH; - nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor, + nwords = x_cf_glob(&flags, vs->cbuf, vs->linelen, vs->cursor, &start, &end, &words); if (nwords == 0) { vi_error(); @@ -5390,9 +5405,9 @@ complete_word(int cmd, int count) is_unique = nwords == 1; } - buf = save_edstate(es); + buf = save_edstate(vs); del_range(start, end); - es->cursor = start; + vs->cursor = start; /* * escape all shell-sensitive characters and put the result into @@ -5544,12 +5559,13 @@ x_eval_region_helper(const char *cmd, size_t len) if (!kshsetjmp(e->jbuf)) { char *wds = alloc(len + 3, ATEMP); - wds[0] = FUNSUB; + wds[0] = FUNASUB; memcpy(wds + 1, cmd, len); wds[len + 1] = '\0'; wds[len + 2] = EOS; cp = evalstr(wds, DOSCALAR); + afree(wds, ATEMP); strdupx(cp, cp, AEDIT); } else cp = NULL; diff --git a/src/eval.c b/src/eval.c index a9980c7..23894d6 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * 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.194 2016/11/11 23:31:34 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.201 2017/04/06 01:59:54 tg Exp $"); /* * string expansion @@ -301,25 +301,36 @@ expand( word = IFS_WORD; quote = st->quotew; continue; + case COMASUB: case COMSUB: + case FUNASUB: case FUNSUB: case VALSUB: tilde_ok = 0; if (f & DONTRUNCOMMAND) { word = IFS_WORD; *dp++ = '$'; - *dp++ = c == COMSUB ? '(' : '{'; - if (c != COMSUB) - *dp++ = c == FUNSUB ? ' ' : '|'; + switch (c) { + case COMASUB: + case COMSUB: + *dp++ = '('; + c = ')'; + break; + case FUNASUB: + case FUNSUB: + case VALSUB: + *dp++ = '{'; + *dp++ = c == VALSUB ? '|' : ' '; + c = '}'; + break; + } while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } - if (c != COMSUB) { + if (c == '}') *dp++ = ';'; - *dp++ = '}'; - } else - *dp++ = ')'; + *dp++ = c; } else { type = comsub(&x, sp, c); if (type != XBASE && (f & DOBLANK)) @@ -625,13 +636,12 @@ expand( break; case '=': /* - * Enabling tilde expansion - * after :s here is - * non-standard ksh, but is - * consistent with rules for - * other assignments. Not - * sure what POSIX thinks of - * this. + * Tilde expansion for string + * variables in POSIX mode is + * governed by Austinbug 351. + * In non-POSIX mode historic + * ksh behaviour (enable it!) + * us followed. * Not doing tilde expansion * for integer variables is a * non-POSIX thing - makes @@ -640,7 +650,7 @@ expand( */ if (!(x.var->flag & INTEGER)) f |= DOASNTILDE | DOTILDE; - f |= DOTEMP; + f |= DOTEMP | DOSCALAR; /* * These will be done after the * value has been assigned. @@ -880,10 +890,30 @@ expand( c = '\n'; --newlines; } else { - while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') + while ((c = shf_getc(x.u.shf)) == 0 || +#ifdef MKSH_WITH_TEXTMODE + c == '\r' || +#endif + c == '\n') { +#ifdef MKSH_WITH_TEXTMODE + if (c == '\r') { + c = shf_getc(x.u.shf); + switch (c) { + case '\n': + break; + default: + shf_ungetc(c, x.u.shf); + /* FALLTHROUGH */ + case -1: + c = '\r'; + break; + } + } +#endif if (c == '\n') /* save newlines */ newlines++; + } if (newlines && c != -1) { shf_ungetc(c, x.u.shf); c = '\n'; @@ -1197,7 +1227,7 @@ varsub(Expand *xp, const char *sp, const char *word, } else if (ctype(c, C_SUBOP1)) { slen += 2; stype |= c; - } else if (ctype(c, C_SUBOP2)) { + } else if (ksh_issubop2(c)) { /* Note: ksh88 allows :%, :%%, etc */ slen += 2; stype = c; @@ -1207,12 +1237,16 @@ varsub(Expand *xp, const char *sp, const char *word, } } else if (c == '@') { /* @x where x is command char */ - slen += 2; - stype |= 0x100; - if (word[slen] == CHAR) { - stype |= word[slen + 1]; - slen += 2; + switch (c = word[slen + 2] == CHAR ? word[slen + 3] : 0) { + case '#': + case '/': + case 'Q': + break; + default: + return (-1); } + stype |= 0x100 | c; + slen += 4; } else if (stype) /* : is not ok */ return (-1); @@ -1301,7 +1335,7 @@ varsub(Expand *xp, const char *sp, const char *word, c = stype & 0x7F; /* test the compiler's code generator */ - if (((stype < 0x100) && (ctype(c, C_SUBOP2) || + if (((stype < 0x100) && (ksh_issubop2(c) || (((stype & 0x80) ? *xp->str == '\0' : xp->str == null) && (state != XARG || (ifs0 || xp->split ? (xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ? @@ -1311,7 +1345,7 @@ varsub(Expand *xp, const char *sp, const char *word, /* expand word instead of variable value */ state = XBASE; if (Flag(FNOUNSET) && xp->str == null && !zero_ok && - (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) + (ksh_issubop2(c) || (state != XBASE && c != '+'))) errorf(Tf_parm, sp); *stypep = stype; *slenp = slen; @@ -1322,17 +1356,28 @@ varsub(Expand *xp, const char *sp, const char *word, * Run the command in $(...) and read its output. */ static int -comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED) +comsub(Expand *xp, const char *cp, int fn) { Source *s, *sold; struct op *t; struct shf *shf; + bool doalias = false; uint8_t old_utfmode = UTFMODE; + switch (fn) { + case COMASUB: + fn = COMSUB; + if (0) + /* FALLTHROUGH */ + case FUNASUB: + fn = FUNSUB; + doalias = true; + } + s = pushs(SSTRING, ATEMP); s->start = s->str = cp; sold = source; - t = compile(s, true); + t = compile(s, true, doalias); afree(s, ATEMP); source = sold; @@ -1713,7 +1758,7 @@ maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign) Xinit(ts, tp, 16, ATEMP); /* : only for DOASNTILDE form */ - while (p[0] == CHAR && !mksh_cdirsep(p[1]) && + while (p[0] == CHAR && /* not cdirsep */ p[1] != '/' && (!isassign || p[1] != ':')) { Xcheck(ts, tp); *tp++ = p[1]; diff --git a/src/exec.c b/src/exec.c index 882b628..6307bce 100644 --- a/src/exec.c +++ b/src/exec.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * 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.186 2016/11/11 23:31:34 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.196 2017/04/12 16:46:21 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh" @@ -376,9 +376,8 @@ execute(struct op * volatile t, if (t->right == NULL) /* should be error */ break; - rv = execute(t->left, XERROK, NULL) == 0 ? - execute(t->right->left, flags & XERROK, xerrok) : - execute(t->right->right, flags & XERROK, xerrok); + rv = execute(execute(t->left, XERROK, NULL) == 0 ? + t->right->left : t->right->right, flags & XERROK, xerrok); break; case TCASE: @@ -806,7 +805,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap, /* NOTREACHED */ default: quitenv(NULL); - internal_errorf(Tf_sd, "CFUNC", i); + internal_errorf(Tunexpected_type, Tunwind, Tfunction, i); } break; } @@ -889,6 +888,9 @@ scriptexec(struct op *tp, const char **ap) unsigned short m; ssize_t n; +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + setmode(fd, O_TEXT); +#endif /* read first couple of octets from file */ n = read(fd, buf, sizeof(buf) - 1); close(fd); @@ -944,6 +946,17 @@ scriptexec(struct op *tp, const char **ap) if (*cp) *tp->args-- = (char *)cp; } +#ifdef __OS2__ + /* + * Search shell/interpreter name without directory in PATH + * if specified path does not exist + */ + if (mksh_vdirsep(sh) && !search_path(sh, path, X_OK, NULL)) { + cp = search_path(_getname(sh), path, X_OK, NULL); + if (cp) + sh = cp; + } +#endif goto nomagic; noshebang: m = buf[0] << 8 | buf[1]; @@ -964,6 +977,19 @@ scriptexec(struct op *tp, const char **ap) buf[4] == 'Z') || (m == /* 7zip */ 0x377A) || (m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D)) errorf("%s: not executable: magic %04X", tp->str, m); +#ifdef __OS2__ + cp = _getext(tp->str); + if (cp && (!stricmp(cp, ".cmd") || !stricmp(cp, ".bat"))) { + /* execute .cmd and .bat with OS2_SHELL, usually CMD.EXE */ + sh = str_val(global("OS2_SHELL")); + *tp->args-- = "/c"; + /* convert slahes to backslashes */ + for (cp = tp->str; *cp; cp++) { + if (*cp == '/') + *cp = '\\'; + } + } +#endif nomagic: ; } @@ -978,13 +1004,17 @@ scriptexec(struct op *tp, const char **ap) errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno)); } +/* actual 'builtin' built-in utility call is handled in comexec() */ int -shcomexec(const char **wp) +c_builtin(const char **wp) { - struct tbl *tp; + return (call_builtin(get_builtin(*wp), wp, Tbuiltin, false)); +} - tp = ktsearch(&builtins, *wp, hash(*wp)); - return (call_builtin(tp, wp, "shcomexec", false)); +struct tbl * +get_builtin(const char *s) +{ + return (s && *s ? ktsearch(&builtins, s, hash(s)) : NULL); } /* @@ -1090,6 +1120,14 @@ builtin(const char *name, int (*func) (const char **)) /* external utility overrides built-in utility, with flags */ flag |= LOW_BI; break; + case '-': + /* is declaration utility if argv[1] is one (POSIX: command) */ + flag |= DECL_FWDR; + break; + case '^': + /* is declaration utility (POSIX: export, readonly) */ + flag |= DECL_UTIL; + break; default: goto flags_seen; } @@ -1123,7 +1161,11 @@ findcom(const char *name, int flags) char *fpath; union mksh_cchack npath; - if (mksh_vdirsep(name)) { + if (mksh_vdirsep(name) +#ifdef MKSH_DOSPATH + && (strcmp(name, T_builtin) != 0) +#endif + ) { insert = 0; /* prevent FPATH search below */ flags &= ~FC_FUNC; @@ -1242,7 +1284,7 @@ search_access(const char *fn, int mode) } #ifdef __OS2__ /* treat all files as executable on OS/2 */ - sb.st_mode &= S_IXUSR | S_IXGRP | S_IXOTH; + sb.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; #endif if (mode == X_OK && (!S_ISREG(sb.st_mode) || !(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) @@ -1251,6 +1293,13 @@ search_access(const char *fn, int mode) return (0); } +#ifdef __OS2__ +/* check if path is something we want to find, adding executable extensions */ +#define search_access(fn, mode) access_ex((search_access), (fn), (mode)) +#else +#define search_access(fn, mode) (search_access)((fn), (mode)) +#endif + /* * search for command with PATH */ @@ -1272,7 +1321,11 @@ search_path(const char *name, const char *lpath, search_path_ok: if (errnop) *errnop = 0; +#ifndef __OS2__ return (name); +#else + return (real_exec_name(name)); +#endif } goto search_path_err; } @@ -1289,6 +1342,10 @@ search_path(const char *name, const char *lpath, XcheckN(xs, xp, p - sp); memcpy(xp, sp, p - sp); xp += p - sp; +#ifdef __OS2__ + if (xp > Xstring(xs, xp) && mksh_cdirsep(xp[-1])) + xp--; +#endif *xp++ = '/'; } sp = p; @@ -1319,9 +1376,7 @@ call_builtin(struct tbl *tp, const char **wp, const char *where, bool resetspec) if (!tp) internal_errorf(Tf_sD_s, where, wp[0]); builtin_argv0 = wp[0]; - builtin_spec = tobool(!resetspec && - /*XXX odd use of KEEPASN */ - ((tp->flag & SPEC_BI) || (Flag(FPOSIX) && (tp->flag & KEEPASN)))); + 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); @@ -1450,8 +1505,11 @@ iosetup(struct ioword *iop, struct tbl *tp) /* herein() may already have printed message */ if (u == -1) { u = errno; - warningf(true, Tf_cant, + warningf(true, Tf_cant_ss_s, +#if 0 + /* can't happen */ iotype == IODUP ? "dup" : +#endif (iotype == IOREAD || iotype == IOHERE) ? Topen : Tcreate, cp, cstrerror(u)); } diff --git a/src/expr.c b/src/expr.c index f259b0c..124dc17 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2016 + * 2011, 2012, 2013, 2014, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.90 2016/11/07 16:58:48 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.93 2017/04/02 16:47:41 tg Exp $"); #define EXPRTOK_DEFNS #include "exprtok.h" @@ -203,7 +203,7 @@ evalerr(Expr_state *es, enum error_type type, const char *str) case ET_BADLIT: warningf(true, Tf_sD_s_qs, es->expression, - "bad number", str); + Tbadnum, str); break; case ET_RECURSIVE: @@ -572,8 +572,9 @@ exprtoken(Expr_state *es) if (c == '\0') es->tok = END; else if (ksh_isalphx(c)) { - for (; ksh_isalnux(c); c = *cp) - cp++; + do { + c = *++cp; + } while (ksh_isalnux(c)); if (c == '[') { size_t len; @@ -856,6 +857,9 @@ utf_wctomb(char *dst, unsigned int wc) int ksh_access(const char *fn, int mode) { +#ifdef __OS2__ + return (access_ex(access, fn, mode)); +#else int rv; struct stat sb; @@ -865,6 +869,7 @@ ksh_access(const char *fn, int mode) rv = -1; return (rv); +#endif } #ifndef MIRBSD_BOOTFLOPPY diff --git a/src/funcs.c b/src/funcs.c index b76fbfc..930462d 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -5,7 +5,7 @@ /*- * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - * 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * 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.319 2016/11/11 23:48:29 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.340 2017/04/12 17:46:29 tg Exp $"); #if HAVE_KILLPG /* @@ -73,7 +73,7 @@ bi_getn(const char *as, int *ai) int rv; if (!(rv = getn(as, ai))) - bi_errorf(Tf_sD_s, as, "bad number"); + bi_errorf(Tf_sD_s, Tbadnum, as); return (rv); } @@ -92,6 +92,7 @@ c_false(const char **wp MKSH_A_UNUSED) /* * A leading = means assignments before command are kept. * A leading * means a POSIX special builtin. + * A leading ^ means declaration utility, - forwarder. */ const struct builtin mkshbuiltins[] = { {Tsgdot, c_dot}, @@ -99,33 +100,34 @@ const struct builtin mkshbuiltins[] = { {Tbracket, c_test}, /* no =: AT&T manual wrong */ {Talias, c_alias}, - {"*=break", c_brkcont}, - {Tgbuiltin, c_builtin}, + {Tsgbreak, c_brkcont}, + {T__builtin, c_builtin}, + {Tbuiltin, c_builtin}, #if !defined(__ANDROID__) {Tbcat, c_cat}, #endif {Tcd, c_cd}, /* dash compatibility hack */ {"chdir", c_cd}, - {Tcommand, c_command}, - {"*=continue", c_brkcont}, + {T_command, c_command}, + {Tsgcontinue, c_brkcont}, {"echo", c_print}, {"*=eval", c_eval}, {"*=exec", c_exec}, {"*=exit", c_exitreturn}, - {Tsgexport, c_typeset}, + {Tdsgexport, c_typeset}, {Tfalse, c_false}, {"fc", c_fc}, {Tgetopts, c_getopts}, - {"=global", c_typeset}, + /* deprecated, replaced by typeset -g */ + {"^=global", c_typeset}, {Tjobs, c_jobs}, {"kill", c_kill}, {"let", c_let}, - {"let]", c_let}, {"print", c_print}, {"pwd", c_pwd}, {Tread, c_read}, - {Tsgreadonly, c_typeset}, + {Tdsgreadonly, c_typeset}, #if !defined(__ANDROID__) {"!realpath", c_realpath}, #endif @@ -133,7 +135,7 @@ const struct builtin mkshbuiltins[] = { {"*=return", c_exitreturn}, {Tsgset, c_set}, {"*=shift", c_shift}, - {"=source", c_dot}, + {Tgsource, c_dot}, #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID {Tsuspend, c_suspend}, #endif @@ -141,12 +143,12 @@ const struct builtin mkshbuiltins[] = { {"*=times", c_times}, {"*=trap", c_trap}, {Ttrue, c_true}, - {Tgtypeset, c_typeset}, + {Tdgtypeset, c_typeset}, {"ulimit", c_ulimit}, {"umask", c_umask}, {Tunalias, c_unalias}, {"*=unset", c_unset}, - {"=wait", c_wait}, + {"wait", c_wait}, {"whence", c_whence}, #ifndef MKSH_UNEMPLOYED {Tbg, c_fgbg}, @@ -193,8 +195,8 @@ static const struct t_op { {"-f", TO_FILREG }, {"-G", TO_FILGID }, {"-g", TO_FILSETG }, - {"-h", TO_FILSYM }, {"-H", TO_FILCDF }, + {"-h", TO_FILSYM }, {"-k", TO_FILSTCK }, {"-L", TO_FILSYM }, {"-n", TO_STNZE }, @@ -202,10 +204,11 @@ static const struct t_op { {"-o", TO_OPTION }, {"-p", TO_FILFIFO }, {"-r", TO_FILRD }, - {"-s", TO_FILGZ }, {"-S", TO_FILSOCK }, + {"-s", TO_FILGZ }, {"-t", TO_FILTT }, {"-u", TO_FILSETU }, + {"-v", TO_ISSET }, {"-w", TO_FILWR }, {"-x", TO_FILEX }, {"-z", TO_STZER }, @@ -313,8 +316,6 @@ c_print(const char **wp) bool hist; /* print words as wide characters? */ bool chars; - /* print a "--" argument? */ - bool pminusminus; /* writing to a coprocess (SIGPIPE blocked)? */ bool coproc; bool copipe; @@ -325,47 +326,39 @@ c_print(const char **wp) po.ws = ' '; po.ls = '\n'; po.nl = true; - po.exp = true; if (wp[0][0] == 'e') { /* "echo" builtin */ - ++wp; -#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT - if (Flag(FSH)) { - /* - * MidnightBSD /bin/sh needs a BSD echo, that is, - * one that supports -e but does not enable it by - * default - */ - po.exp = false; - } -#endif if (Flag(FPOSIX) || #ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT Flag(FSH) || #endif Flag(FAS_BUILTIN)) { - /* Debian Policy 10.4 compliant "echo" builtin */ + /* BSD "echo" cmd, Debian Policy 10.4 compliant */ + ++wp; + bsd_echo: if (*wp && !strcmp(*wp, "-n")) { - /* recognise "-n" only as the first arg */ po.nl = false; ++wp; } - /* print everything as-is */ po.exp = false; } else { - bool new_exp = po.exp, new_nl = po.nl; + bool new_exp, new_nl = true; - /** - * a compromise between sysV and BSD echo commands: - * escape sequences are enabled by default, and -n, - * -e and -E are recognised if they appear in argu- - * ments with no illegal options (ie, echo -nq will - * print -nq). - * Different from sysV echo since options are reco- - * gnised, different from BSD echo since escape se- - * quences are enabled by default. + /*- + * compromise between various historic echos: only + * recognise -Een if they appear in arguments with + * no illegal options; e.g. echo -nq outputs '-nq' */ +#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT + /* MidnightBSD /bin/sh needs -e supported but off */ + if (Flag(FSH)) + new_exp = false; + else +#endif + /* otherwise compromise on -e enabled by default */ + new_exp = true; + goto print_tradparse_beg; print_tradparse_arg: if ((s = *wp) && *s++ == '-' && *s) { @@ -381,6 +374,7 @@ c_print(const char **wp) new_nl = false; goto print_tradparse_ch; case '\0': + print_tradparse_beg: po.exp = new_exp; po.nl = new_nl; ++wp; @@ -390,10 +384,10 @@ c_print(const char **wp) } } else { /* "print" builtin */ - const char *opts = "AclNnpRrsu,"; + const char *opts = "AcelNnpRrsu,"; const char *emsg; - po.pminusminus = false; + po.exp = true; while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1) switch (c) { @@ -423,11 +417,9 @@ c_print(const char **wp) } break; case 'R': - /* fake BSD echo command */ - po.pminusminus = true; - po.exp = false; - opts = "en"; - break; + /* fake BSD echo but don't reset other flags */ + wp += builtin_opt.optind; + goto bsd_echo; case 'r': po.exp = false; break; @@ -451,8 +443,7 @@ c_print(const char **wp) if (wp[builtin_opt.optind] && ksh_isdash(wp[builtin_opt.optind])) builtin_opt.optind++; - } else if (po.pminusminus) - builtin_opt.optind--; + } wp += builtin_opt.optind; } @@ -747,7 +738,7 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand) break; #ifndef MKSH_SMALL default: - bi_errorf("%s is of unknown type %d", id, tp->type); + bi_errorf(Tunexpected_type, id, Tcommand, tp->type); return (1); #endif } @@ -757,380 +748,15 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand) return (rv); } -/* typeset, global, export, and readonly */ -static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool); -static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool, - bool); -int -c_typeset(const char **wp) +bool +valid_alias_name(const char *cp) { - struct tbl *vp, **p; - uint32_t fset = 0, fclr = 0, flag; - int thing = 0, field = 0, base = 0, i; - struct block *l; - const char *opts; - const char *fieldstr = NULL, *basestr = NULL; - bool localv = false, func = false, pflag = false, istset = true; - enum namerefflag new_refflag = SRF_NOP; - - switch (**wp) { - - /* export */ - case 'e': - fset |= EXPORT; - istset = false; - break; - - /* readonly */ - case 'r': - fset |= RDONLY; - istset = false; - break; - - /* set */ - case 's': - /* called with 'typeset -' */ - break; - - /* typeset */ - case 't': - localv = true; - break; - } - - /* see comment below regarding possible opions */ - opts = istset ? "L#R#UZ#afi#lnprtux" : "p"; - - builtin_opt.flags |= GF_PLUSOPT; - /* - * AT&T ksh seems to have 0-9 as options which are multiplied - * to get a number that is used with -L, -R, -Z or -i (eg, -1R2 - * sets right justify in a field of 12). This allows options - * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and - * does not allow the number to be specified as a separate argument - * Here, the number must follow the RLZi option, but is optional - * (see the # kludge in ksh_getopt()). - */ - while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) { - flag = 0; - switch (i) { - case 'L': - flag = LJUST; - fieldstr = builtin_opt.optarg; - break; - case 'R': - flag = RJUST; - fieldstr = builtin_opt.optarg; - break; - case 'U': - /* - * AT&T ksh uses u, but this conflicts with - * upper/lower case. If this option is changed, - * need to change the -U below as well - */ - flag = INT_U; - break; - case 'Z': - flag = ZEROFIL; - fieldstr = builtin_opt.optarg; - break; - case 'a': - /* - * this is supposed to set (-a) or unset (+a) the - * indexed array attribute; it does nothing on an - * existing regular string or indexed array though - */ - break; - case 'f': - func = true; - break; - case 'i': - flag = INTEGER; - basestr = builtin_opt.optarg; - break; - case 'l': - flag = LCASEV; - break; - case 'n': - new_refflag = (builtin_opt.info & GI_PLUS) ? - SRF_DISABLE : SRF_ENABLE; - break; - /* export, readonly: POSIX -p flag */ - case 'p': - /* typeset: show values as well */ - pflag = true; - if (istset) - continue; - break; - case 'r': - flag = RDONLY; - break; - case 't': - flag = TRACE; - break; - case 'u': - /* upper case / autoload */ - flag = UCASEV_AL; - break; - case 'x': - flag = EXPORT; - break; - case '?': - return (1); - } - if (builtin_opt.info & GI_PLUS) { - fclr |= flag; - fset &= ~flag; - thing = '+'; - } else { - fset |= flag; - fclr &= ~flag; - thing = '-'; - } - } - - if (fieldstr && !bi_getn(fieldstr, &field)) - return (1); - if (basestr) { - if (!getn(basestr, &base)) { - bi_errorf(Tf_sD_s, "bad integer base", basestr); - return (1); - } - if (base < 1 || base > 36) - base = 10; - } - - if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] && - (wp[builtin_opt.optind][0] == '-' || - wp[builtin_opt.optind][0] == '+') && - wp[builtin_opt.optind][1] == '\0') { - thing = wp[builtin_opt.optind][0]; - builtin_opt.optind++; - } - - if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) || - new_refflag != SRF_NOP)) { - bi_errorf("only -t, -u and -x options may be used with -f"); - return (1); - } - if (wp[builtin_opt.optind]) { - /* - * Take care of exclusions. - * At this point, flags in fset are cleared in fclr and vice - * versa. This property should be preserved. - */ - if (fset & LCASEV) - /* LCASEV has priority over UCASEV_AL */ - fset &= ~UCASEV_AL; - if (fset & LJUST) - /* LJUST has priority over RJUST */ - fset &= ~RJUST; - if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { - /* -Z implies -ZR */ - fset |= RJUST; - fclr &= ~RJUST; - } - /* - * Setting these attributes clears the others, unless they - * are also set in this command - */ - if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV | - INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP) - fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | - LCASEV | INTEGER | INT_U | INT_L); - } - if (new_refflag != SRF_NOP) { - fclr &= ~(ARRAY | ASSOC); - fset &= ~(ARRAY | ASSOC); - fclr |= EXPORT; - fset |= ASSOC; - if (new_refflag == SRF_DISABLE) - fclr |= ASSOC; - } - - /* set variables and attributes */ - if (wp[builtin_opt.optind] && - /* not "typeset -p varname" */ - !(!func && pflag && !(fset | fclr))) { - int rv = 0; - struct tbl *f; - - if (localv && !func) - fset |= LOCAL; - for (i = builtin_opt.optind; wp[i]; i++) { - if (func) { - f = findfunc(wp[i], hash(wp[i]), - tobool(fset & UCASEV_AL)); - if (!f) { - /* AT&T ksh does ++rv: bogus */ - rv = 1; - continue; - } - if (fset | fclr) { - f->flag |= fset; - f->flag &= ~fclr; - } else { - fpFUNCTf(shl_stdout, 0, - tobool(f->flag & FKSH), - wp[i], f->val.t); - shf_putc('\n', shl_stdout); - } - } else if (!typeset(wp[i], fset, fclr, field, base)) { - bi_errorf(Tf_sD_s, wp[i], Tnot_ident); - return (1); - } - } - return (rv); - } - - /* list variables and attributes */ - - /* no difference at this point.. */ - flag = fset | fclr; - if (func) { - for (l = e->loc; l; l = l->next) { - for (p = ktsort(&l->funs); (vp = *p++); ) { - if (flag && (vp->flag & flag) == 0) - continue; - if (thing == '-') - fpFUNCTf(shl_stdout, 0, - tobool(vp->flag & FKSH), - vp->name, vp->val.t); - else - shf_puts(vp->name, shl_stdout); - shf_putc('\n', shl_stdout); - } - } - } else if (wp[builtin_opt.optind]) { - for (i = builtin_opt.optind; wp[i]; i++) { - varsearch(e->loc, &vp, wp[i], hash(wp[i])); - c_typeset_vardump(vp, flag, thing, pflag, istset); - } - } else - c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset); - return (0); -} - -static void -c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing, - bool pflag, bool istset) -{ - struct tbl **blockvars, *vp; - - if (l->next) - c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset); - blockvars = ktsort(&l->vars); - while ((vp = *blockvars++)) - c_typeset_vardump(vp, flag, thing, pflag, istset); - /*XXX doesn’t this leak? */ -} - -static void -c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag, - bool istset) -{ - struct tbl *tvp; - int any_set = 0; - char *s; - - if (!vp) - return; - - /* - * See if the parameter is set (for arrays, if any - * element is set). - */ - for (tvp = vp; tvp; tvp = tvp->u.array) - if (tvp->flag & ISSET) { - any_set = 1; - break; - } - - /* - * Check attributes - note that all array elements - * have (should have?) the same attributes, so checking - * the first is sufficient. - * - * Report an unset param only if the user has - * explicitly given it some attribute (like export); - * otherwise, after "echo $FOO", we would report FOO... - */ - if (!any_set && !(vp->flag & USERATTRIB)) - return; - if (flag && (vp->flag & flag) == 0) - return; - if (!(vp->flag & ARRAY)) - /* optimise later conditionals */ - any_set = 0; - do { - /* - * Ignore array elements that aren't set unless there - * are no set elements, in which case the first is - * reported on - */ - if (any_set && !(vp->flag & ISSET)) - continue; - /* no arguments */ - if (!thing && !flag) { - if (any_set == 1) { - shprintf(Tf_s_s_sN, Tset, "-A", vp->name); - any_set = 2; - } - /* - * AT&T ksh prints things like export, integer, - * leftadj, zerofill, etc., but POSIX says must - * be suitable for re-entry... - */ - shprintf(Tf_s_s, Ttypeset, ""); - if (((vp->flag & (ARRAY | ASSOC)) == ASSOC)) - shprintf(Tf__c_, 'n'); - if ((vp->flag & INTEGER)) - shprintf(Tf__c_, 'i'); - if ((vp->flag & EXPORT)) - shprintf(Tf__c_, 'x'); - if ((vp->flag & RDONLY)) - shprintf(Tf__c_, 'r'); - if ((vp->flag & TRACE)) - shprintf(Tf__c_, 't'); - if ((vp->flag & LJUST)) - shprintf("-L%d ", vp->u2.field); - if ((vp->flag & RJUST)) - shprintf("-R%d ", vp->u2.field); - if ((vp->flag & ZEROFIL)) - shprintf(Tf__c_, 'Z'); - if ((vp->flag & LCASEV)) - shprintf(Tf__c_, 'l'); - if ((vp->flag & UCASEV_AL)) - shprintf(Tf__c_, 'u'); - if ((vp->flag & INT_U)) - shprintf(Tf__c_, 'U'); - } else if (pflag) { - shprintf(Tf_s_s, istset ? Ttypeset : - (flag & EXPORT) ? Texport : Treadonly, ""); - } - if (any_set) - shprintf("%s[%lu]", vp->name, arrayindex(vp)); + while (*cp) + if (!ksh_isalias(*cp)) + return (false); else - shf_puts(vp->name, shl_stdout); - if ((!thing && !flag && pflag) || - (thing == '-' && (vp->flag & ISSET))) { - s = str_val(vp); - shf_putc('=', shl_stdout); - /* AT&T ksh can't have justified integers... */ - if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER) - shf_puts(s, shl_stdout); - else - print_value_quoted(shl_stdout, s); - } - shf_putc('\n', shl_stdout); - - /* - * Only report first 'element' of an array with - * no set elements. - */ - if (!any_set) - return; - } while ((vp = vp->u.array)); + ++cp; + return (true); } int @@ -1231,6 +857,11 @@ c_alias(const char **wp) strndupx(xalias, alias, val++ - alias, ATEMP); alias = xalias; } + if (!valid_alias_name(alias) || *alias == '-') { + bi_errorf(Tinvname, alias, Talias); + afree(xalias, ATEMP); + return (1); + } h = hash(alias); if (val == NULL && !tflag && !xflag) { ap = ktsearch(t, alias, h); @@ -1639,7 +1270,7 @@ c_getopts(const char **wp) if (user_opt.optarg == NULL) unset(voptarg, 1); else - /* This can't fail (have cleared readonly/integer) */ + /* this can't fail (haing cleared readonly/integer) */ setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR); rv = 0; @@ -1737,7 +1368,7 @@ c_shift(const char **wp) /* nothing to do */ return (0); } else if (n < 0) { - bi_errorf(Tf_sD_s, arg, "bad number"); + bi_errorf(Tf_sD_s, Tbadnum, arg); return (1); } if (l->argc < n) { @@ -1798,7 +1429,7 @@ c_umask(const char **wp) ++cp; } if (*cp) { - bi_errorf("bad number"); + bi_errorf(Tbadnum); return (1); } } else { @@ -1981,6 +1612,10 @@ c_read(const char **wp) #define c_read_opts "Aad:N:n:prst:u," #else #define c_read_opts "Aad:N:n:prsu," +#endif +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + int saved_mode; + int saved_errno; #endif while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1) @@ -2110,7 +1745,15 @@ c_read(const char **wp) } #endif +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + saved_mode = setmode(fd, O_TEXT); +#endif if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) { +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + saved_errno = errno; + setmode(fd, saved_mode); + errno = saved_errno; +#endif if (errno == EINTR) { /* check whether the signal would normally kill */ if (!fatal_trap_check()) { @@ -2125,6 +1768,9 @@ c_read(const char **wp) rv = 2; goto c_read_out; } +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + setmode(fd, saved_mode); +#endif switch (readmode) { case READALL: @@ -2386,6 +2032,7 @@ c_eval(const char **wp) return (1); s = pushs(SWORDS, ATEMP); s->u.strv = wp + builtin_opt.optind; + s->line = current_lineno; /*- * The following code handles the case where the command is @@ -2423,7 +2070,7 @@ c_eval(const char **wp) savef = Flag(FERREXIT); Flag(FERREXIT) |= 0x80; - rv = shell(s, false); + rv = shell(s, 2); Flag(FERREXIT) = savef; source = saves; afree(s, ATEMP); @@ -2559,7 +2206,7 @@ c_brkcont(const char **wp) * scripts, but don't generate an error (ie, keep going). */ if ((unsigned int)n == quit) { - warningf(true, "%s: can't %s", wp[0], wp[0]); + warningf(true, Tf_cant_s, wp[0], wp[0]); return (0); } /* @@ -2827,7 +2474,6 @@ c_exec(const char **wp MKSH_A_UNUSED) for (i = 0; i < NUFILE; i++) { if (e->savefd[i] > 0) close(e->savefd[i]); -#ifndef MKSH_LEGACY_MODE /* * keep all file descriptors > 2 private for ksh, * but not for POSIX or legacy/kludge sh @@ -2835,14 +2481,13 @@ c_exec(const char **wp MKSH_A_UNUSED) if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 && e->savefd[i]) fcntl(i, F_SETFD, FD_CLOEXEC); -#endif } e->savefd = NULL; } return (0); } -#if HAVE_MKNOD +#if HAVE_MKNOD && !defined(__OS2__) int c_mknod(const char **wp) { @@ -2939,14 +2584,13 @@ c_mknod(const char **wp) | "(" oexpr ")" ; - unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"| - "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"| - "-L"|"-h"|"-S"|"-H"; + unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"| + "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"| + "-u"|"-v"|"-w"|"-x"|"-z"; + + binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"| + "-lt"|"-le"|"-ef"|"-nt"|"-ot"; - binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| - "-nt"|"-ot"|"-ef"| - "<"|">" # rules used for [[ ... ]] expressions - ; operand ::= */ @@ -3099,6 +2743,14 @@ test_isop(Test_meta meta, const char *s) return (TO_NONOP); } +#ifdef __OS2__ +#define test_access(name, mode) access_ex(access, (name), (mode)) +#define test_stat(name, buffer) stat_ex((name), (buffer)) +#else +#define test_access(name, mode) access((name), (mode)) +#define test_stat(name, buffer) stat((name), (buffer)) +#endif + int test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, bool do_eval) @@ -3107,6 +2759,7 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, size_t k; struct stat b1, b2; mksh_ari_t v1, v2; + struct tbl *vp; if (!do_eval) return (0); @@ -3153,6 +2806,10 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, case TO_STZER: return (*opnd1 == '\0'); + /* -v */ + case TO_ISSET: + return ((vp = isglobal(opnd1, false)) && (vp->flag & ISSET)); + /* -o */ case TO_OPTION: if ((i = *opnd1) == '!' || i == '?') @@ -3164,12 +2821,12 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, /* -r */ case TO_FILRD: /* LINTED use of access */ - return (access(opnd1, R_OK) == 0); + return (test_access(opnd1, R_OK) == 0); /* -w */ case TO_FILWR: /* LINTED use of access */ - return (access(opnd1, W_OK) == 0); + return (test_access(opnd1, W_OK) == 0); /* -x */ case TO_FILEX: @@ -3179,11 +2836,11 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, case TO_FILAXST: /* -e */ case TO_FILEXST: - return (stat(opnd1, &b1) == 0); + return (test_stat(opnd1, &b1) == 0); - /* -r */ + /* -f */ case TO_FILREG: - return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode)); + return (test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode)); /* -d */ case TO_FILID: @@ -3282,7 +2939,7 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, * Binary Operators */ - /* = */ + /* =, == */ case TO_STEQL: if (te->flags & TEF_DBRACKET) { if ((i = gmatchx(opnd1, opnd2, false))) @@ -3668,7 +3325,7 @@ c_ulimit(const char **wp) if (!all) print_ulimit(rlimits[i], how); else for (i = 0; i < NELEM(rlimits); ++i) { - shprintf("%-20s ", rlimits[i]->name); + shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name); print_ulimit(rlimits[i], how); } return (0); diff --git a/src/histrap.c b/src/histrap.c index 56723ff..26dd521 100644 --- a/src/histrap.c +++ b/src/histrap.c @@ -27,7 +27,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.159 2016/11/11 18:44:32 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.160 2017/04/08 01:07:16 tg Exp $"); Trap sigtraps[ksh_NSIG + 1]; static struct sigaction Sigact_ign; @@ -829,7 +829,7 @@ hist_init(Source *s) goto retry; } if (hs != hist_init_retry) - bi_errorf(Tf_cant, + bi_errorf(Tf_cant_ss_s, "unlink HISTFILE", hname, cstrerror(errno)); histfsize = 0; return; @@ -1033,8 +1033,8 @@ inittraps(void) if (!strcmp(sigtraps[i].name, "EXIT") || !strcmp(sigtraps[i].name, "ERR")) { #ifndef MKSH_SMALL - internal_warningf("ignoring invalid signal name %s", - sigtraps[i].name); + internal_warningf(Tinvname, sigtraps[i].name, + "signal"); #endif sigtraps[i].name = null; } diff --git a/src/lex.c b/src/lex.c index 741bb93..78c2ee7 100644 --- a/src/lex.c +++ b/src/lex.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.228 2016/08/01 21:38:03 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.234 2017/04/06 01:59:55 tg Exp $"); /* * states while lexing word @@ -489,7 +489,7 @@ yylex(int cf) * If this is a trim operation, * treat (,|,) specially in STBRACE. */ - if (ctype(c, C_SUBOP2)) { + if (ksh_issubop2(c)) { ungetsc(c); if (Flag(FSH)) PUSH_STATE(STBRACEBOURNE); @@ -532,7 +532,7 @@ yylex(int cf) case '`': subst_gravis: PUSH_STATE(SBQUOTE); - *wp++ = COMSUB; + *wp++ = COMASUB; /* * We need to know whether we are within double * quotes in order to translate \" to " within @@ -885,7 +885,7 @@ yylex(int cf) Xcheck(ws, wp); if (statep != &states[1]) /* XXX figure out what is missing */ - yyerror("no closing quote\n"); + yyerror("no closing quote"); /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) @@ -893,9 +893,7 @@ yylex(int cf) dp = Xstring(ws, wp); if (state == SBASE && ( -#ifndef MKSH_LEGACY_MODE (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) || -#endif c == '<' || c == '>') && ((c2 = Xlength(ws, wp)) == 0 || (c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) { struct ioword *iop = alloc(sizeof(struct ioword), ATEMP); @@ -1016,15 +1014,12 @@ yylex(int cf) while ((dp - ident) < IDENT && (c = *sp++) == CHAR) *dp++ = *sp++; if (c != EOS) - /* word is not unquoted */ + /* word is not unquoted, or space ran out */ dp = ident; /* make sure the ident array stays NUL padded */ memset(dp, 0, (ident + IDENT) - dp + 1); - if (!(cf & (KEYWORD | ALIAS))) - return (LWORD); - - if (*ident != '\0') { + if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) { struct tbl *p; uint32_t h = hash(ident); @@ -1068,6 +1063,7 @@ yylex(int cf) s->start = s->str = p->val.s; s->u.tblp = p; s->flags |= SF_HASALIAS; + s->line = source->line; s->next = source; if (source->type == SEOF) { /* prevent infinite recursion at EOS */ @@ -1079,9 +1075,12 @@ yylex(int cf) goto Again; } } - } else if (cf & ALIAS) { + } else if (*ident == '\0') { /* retain typeset et al. even when quoted */ - if (assign_command((dp = wdstrip(yylval.cp, 0)), true)) + struct tbl *tt = get_builtin((dp = wdstrip(yylval.cp, 0))); + uint32_t flag = tt ? tt->flag : 0; + + if (flag & (DECL_UTIL | DECL_FWDR)) strlcpy(ident, dp, sizeof(ident)); afree(dp, ATEMP); } @@ -1204,6 +1203,7 @@ yyerror(const char *fmt, ...) error_prefix(true); va_start(va, fmt); shf_vfprintf(shl_out, fmt, va); + shf_putc('\n', shl_out); va_end(va); errorfz(); } @@ -1625,7 +1625,7 @@ get_brace_var(XString *wsp, char *wp) char *tmp, *p; if (!arraysub(&tmp)) - yyerror("missing ]\n"); + yyerror("missing ]"); *wp++ = c; for (p = tmp; *p; ) { Xcheck(*wsp, wp); diff --git a/src/lksh.1 b/src/lksh.1 index a9f8be7..c3a82cb 100644 --- a/src/lksh.1 +++ b/src/lksh.1 @@ -1,6 +1,6 @@ -.\" $MirOS: src/bin/mksh/lksh.1,v 1.20 2016/11/11 23:31:35 tg Exp $ +.\" $MirOS: src/bin/mksh/lksh.1,v 1.23 2017/04/02 13:38:02 tg Exp $ .\"- -.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016 +.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017 .\" mirabilos .\" .\" Provided that these terms and disclaimer and all copyright notices @@ -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: November 11 2016 $ +.Dd $Mdocdate: April 2 2017 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -173,37 +173,34 @@ It is built on refer to its manual page for details on the scripting language. It is recommended to port scripts to .Nm mksh -instead of relying on legacy or idiotic POSIX-mandated behaviour, +instead of relying on legacy or objectionable POSIX-mandated behaviour, since the MirBSD Korn Shell scripting language is much more consistent. .Pp +Do not use +.Nm +as an interactive or login shell; use +.Nm mksh +instead. +.Pp Note that it's strongly recommended to invoke .Nm -with at least the +with .Fl o Ic posix -option, if not both that -.Em and Fl o Ic sh , to fully enjoy better compatibility to the .Tn POSIX standard (which is probably why you use .Nm over .Nm mksh -in the first place) or legacy scripts, respectively. +in the first place); +.Fl o Ic sh +(possibly additionally to the above) may be needed for some legacy scripts. .Sh LEGACY MODE .Nm currently has the following differences from .Nm mksh : .Bl -bullet .It -.\"XXX TODO: remove (some systems may wish to have lksh as ksh) -There is no explicit support for interactive use, -nor any command line editing or history code. -Hence, -.Nm -is not suitable as a user's login shell, either; use -.Nm mksh -instead. -.It The .Ev KSH_VERSION string identifies @@ -241,25 +238,11 @@ Division of the largest negative number by \-1 is Undefined Behaviour. The compiler is permitted to delete all data and crash the system if Undefined Behaviour occurs (see above for an example). .It -.\"XXX TODO: move this to FPOSIX The rotation arithmetic operators are not available. .It The shift arithmetic operators take all bits of the second operand into account; if they exceed permitted precision, the result is unspecified. .It -.\"XXX TODO: move this to FPOSIX -The -.Tn GNU -.Nm bash -extension &\*(Gt to redirect stdout and stderr in one go is not parsed. -.It -.\"XXX TODO: drop along with allowing interactivity -The -.Nm mksh -command line option -.Fl T -is not available. -.It Unless .Ic set -o posix is active, @@ -275,19 +258,6 @@ passes through the errorlevel from the .Xr getopt 1 command. .It -.\"XXX TODO: move to FPOSIX/FSH -Unlike -.At -.Nm ksh , -.Nm mksh -in -.Fl o Ic posix -or -.Fl o Ic sh -mode and -.Nm lksh -do not keep file descriptors \*(Gt 2 private from sub-processes. -.It Functions defined with the .Ic function reserved word share the shell options @@ -297,16 +267,10 @@ instead of locally scoping them. .Sh SEE ALSO .Xr mksh 1 .Pp -.Pa https://www.mirbsd.org/mksh.htm +.Pa http://www.mirbsd.org/mksh.htm .Pp -.Pa https://www.mirbsd.org/ksh\-chan.htm +.Pa http://www.mirbsd.org/ksh\-chan.htm .Sh CAVEATS -The distinction between the shell variants -.Pq Nm lksh / Nm mksh -and shell flags -.Pq Fl o Ic posix / Ic sh -will be reworked for an upcoming release. -.Pp To use .Nm as @@ -315,16 +279,22 @@ compilation to enable .Ic set -o posix by default if called as .Nm sh +.Pq adding Dv \-DMKSH_BINSHPOSIX to Dv CPPFLAGS is highly recommended for better standards compliance. +.Pp For better compatibility with legacy scripts, such as many .Tn Debian maintainer scripts, Upstart and SYSV init scripts, and other -unfixed scripts, using the compile-time options for enabling +unfixed scripts, also adding the +.Dv \-DMKSH_BINSHREDUCED +compile-time option to enable .Em both .Ic set -o posix -o sh when the shell is run as -.Nm sh -is recommended. +.Nm sh , +as well as integrating the optional disrecommended +.Xr printf 1 +builtin, might be necessary. .Pp .Nm tries to make a cross between a legacy bourne/posix compatibl-ish @@ -332,14 +302,6 @@ shell and a legacy pdksh-alike but .Dq legacy is not exactly specified. .Pp -The -.Ic set -built-in command does not currently have all options one would expect -from a full-blown -.Nm mksh -or -.Nm pdksh . -.Pp Talk to the .Mx development team using the mailing list at diff --git a/src/main.c b/src/main.c index ebbadd9..a556d5d 100644 --- a/src/main.c +++ b/src/main.c @@ -5,7 +5,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -34,7 +34,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/main.c,v 1.322 2016/11/11 23:48:30 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/main.c,v 1.332 2017/04/12 16:01:45 tg Exp $"); extern char **environ; @@ -56,8 +56,6 @@ static mksh_uari_t rndsetup(void); static void x_sigwinch(int); #endif -static const char initifs[] = "IFS= \t\n"; - static const char initsubs[] = "${PS2=> }" "${PS3=#? }" @@ -71,18 +69,18 @@ static const char *initcoms[] = { Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL, Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL, Talias, - "integer=\\typeset -i", - "local=\\typeset", + "integer=\\\\builtin typeset -i", + "local=\\\\builtin typeset", /* not "alias -t --": hash -r needs to work */ - "hash=\\builtin alias -t", - "type=\\builtin whence -v", - "autoload=\\typeset -fu", - "functions=\\typeset -f", - "history=\\builtin fc -l", - "nameref=\\typeset -n", + "hash=\\\\builtin alias -t", + "type=\\\\builtin whence -v", + "autoload=\\\\builtin typeset -fu", + "functions=\\\\builtin typeset -f", + "history=\\\\builtin fc -l", + "nameref=\\\\builtin typeset -n", "nohup=nohup ", - "r=\\builtin fc -e -", - "login=\\exec login", + "r=\\\\builtin fc -e -", + "login=\\\\builtin exec login", NULL, /* this is what AT&T ksh seems to track, with the addition of emacs */ Talias, "-tU", @@ -101,6 +99,51 @@ static bool initio_done; static struct env env; struct env *e = &env; +/* compile-time assertions */ +#define cta(name, expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; } + +/* this one should be defined by the standard */ +cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) && + (sizeof(unsigned char) == 1)); +cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) && + ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF)); +/* the next assertion is probably not really needed */ +cta(short_is_2_char, sizeof(short) == 2); +cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short)); +/* the next assertion is probably not really needed */ +cta(int_is_4_char, sizeof(int) == 4); +cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int)); + +cta(long_ge_int, sizeof(long) >= sizeof(int)); +cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long)); + +#ifndef MKSH_LEGACY_MODE +/* the next assertion is probably not really needed */ +cta(ari_is_4_char, sizeof(mksh_ari_t) == 4); +/* but this is */ +cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1)); +/* the next assertion is probably not really needed */ +cta(uari_is_4_char, sizeof(mksh_uari_t) == 4); +/* but the next three are; we REQUIRE unsigned integer wraparound */ +cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1)); +cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3)); +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)); +#endif +/* these are always required */ +cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0); +cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0); +/* we require these to have the precisely same size and assume 2s complement */ +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(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(size_t) <= sizeof(long)); +cta(ari_fits_in_long, sizeof(mksh_ari_t) <= sizeof(long)); + static mksh_uari_t rndsetup(void) { @@ -197,6 +240,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) for (i = 0; i < 3; ++i) if (!isatty(i)) setmode(i, O_BINARY); + + os2_init(&argc, &argv); #endif /* do things like getpgrp() et al. */ @@ -363,9 +408,10 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) } /* for security */ - typeset(initifs, 0, 0, 0, 0); + typeset("IFS= \t\n", 0, 0, 0, 0); /* assign default shell variable values */ + typeset("PATHSEP=" MKSH_PATHSEPS, 0, 0, 0, 0); substitute(initsubs, 0); /* Figure out the current working directory and set $PWD */ @@ -382,7 +428,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) setstr(vp, current_wd, KSH_RETURN_ERROR); for (wp = initcoms; *wp != NULL; wp++) { - shcomexec(wp); + c_builtin(wp); while (*wp != NULL) wp++; } @@ -468,7 +514,9 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) * to search for it. This changes the behaviour of a * simple "mksh foo", but can't be helped. */ - s->file = search_path(argv[argi++], path, X_OK, NULL); + s->file = argv[argi++]; + if (search_access(s->file, X_OK) != 0) + s->file = search_path(s->file, path, X_OK, NULL); if (!s->file || !*s->file) s->file = argv[argi - 1]; #else @@ -627,7 +675,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) } if (restricted_shell) { - shcomexec(restr_com); + c_builtin(restr_com); /* After typeset command... */ Flag(FRESTRICTED) = 1; } @@ -657,9 +705,9 @@ main(int argc, const char *argv[]) if ((rv = main_init(argc, argv, &s, &l)) == 0) { if (Flag(FAS_BUILTIN)) { - rv = shcomexec(l->argv); + rv = c_builtin(l->argv); } else { - shell(s, true); + shell(s, 0); /* NOTREACHED */ } } @@ -712,7 +760,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok) unwind(i); /* NOTREACHED */ default: - internal_errorf("include %d", i); + internal_errorf(Tunexpected_type, Tunwind, Tsource, i); /* NOTREACHED */ } } @@ -723,7 +771,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok) s = pushs(SFILE, ATEMP); s->u.shf = shf; strdupx(s->file, name, ATEMP); - i = shell(s, false); + i = shell(s, 1); quitenv(s->u.shf); if (old_argv) { e->loc->argv = old_argv; @@ -743,7 +791,7 @@ command(const char *comm, int line) s = pushs(SSTRING, ATEMP); s->start = s->str = comm; s->line = line; - rv = shell(s, false); + rv = shell(s, 1); source = sold; return (rv); } @@ -752,22 +800,33 @@ command(const char *comm, int line) * run the commands from the input source, returning status. */ int -shell(Source * volatile s, volatile bool toplevel) +shell(Source * volatile s, volatile int level) { struct op *t; volatile bool wastty = tobool(s->flags & SF_TTY); volatile uint8_t attempts = 13; - volatile bool interactive = Flag(FTALKING) && toplevel; + volatile bool interactive = (level == 0) && Flag(FTALKING); volatile bool sfirst = true; Source *volatile old_source = source; int i; - newenv(E_PARSE); + newenv(level == 2 ? E_EVAL : E_PARSE); if (interactive) really_exit = false; switch ((i = kshsetjmp(e->jbuf))) { case 0: break; + case LBREAK: + case LCONTIN: + if (level != 2) { + source = old_source; + quitenv(NULL); + internal_errorf(Tf_cant_s, Tshell, + i == LBREAK ? Tbreak : Tcontinue); + /* NOTREACHED */ + } + /* assert: interactive == false */ + /* FALLTHROUGH */ case LINTR: /* we get here if SIGINT not caught or ignored */ case LERROR: @@ -807,7 +866,7 @@ shell(Source * volatile s, volatile bool toplevel) default: source = old_source; quitenv(NULL); - internal_errorf("shell %d", i); + internal_errorf(Tunexpected_type, Tunwind, Tshell, i); /* NOTREACHED */ } while (/* CONSTCOND */ 1) { @@ -824,7 +883,7 @@ shell(Source * volatile s, volatile bool toplevel) j_notify(); set_prompt(PS1, s); } - t = compile(s, sfirst); + t = compile(s, sfirst, true); if (interactive) histsave(&s->line, NULL, HIST_FLUSH, true); sfirst = false; @@ -845,7 +904,7 @@ shell(Source * volatile s, volatile bool toplevel) * immediately after the last command * executed. */ - if (toplevel) + if (level == 0) unwind(LEXIT); break; } @@ -908,6 +967,7 @@ unwind(int i) case E_INCL: case E_LOOP: case E_ERRH: + case E_EVAL: kshlongjmp(e->jbuf, i); /* NOTREACHED */ case E_NONE: @@ -1271,12 +1331,10 @@ bi_errorf(const char *fmt, ...) VWARNINGF_BUILTIN, fmt, va); va_end(va); - /* - * POSIX special builtins and ksh special builtins cause - * non-interactive shells to exit. XXX may not want LERROR here - */ + /* POSIX special builtins cause non-interactive shells to exit */ if (builtin_spec) { builtin_argv0 = NULL; + /* may not want to use LERROR here */ unwind(LERROR); } } @@ -1379,19 +1437,19 @@ initio(void) #ifdef DF if ((lfp = getenv("SDMKSH_PATH")) == NULL) { if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp)) - errorf("cannot get home directory"); + errorf("can't get home directory"); lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt"); } if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0) - errorf("cannot open debug output file %s", lfp); + errorf("can't open debug output file %s", lfp); if (shl_dbg_fd < FDBASE) { int nfd; nfd = fcntl(shl_dbg_fd, F_DUPFD, FDBASE); close(shl_dbg_fd); if ((shl_dbg_fd = nfd) == -1) - errorf("cannot dup debug output file"); + errorf("can't dup debug output file"); } fcntl(shl_dbg_fd, F_SETFD, FD_CLOEXEC); shf_fdopen(shl_dbg_fd, SHF_WR, shl_dbg); @@ -1407,7 +1465,7 @@ ksh_dup2(int ofd, int nfd, bool errok) int rv; if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF)) - errorf("too many files open in shell"); + errorf(Ttoo_many_files); #ifdef __ultrix /*XXX imake style */ @@ -1431,7 +1489,7 @@ savefd(int fd) (errno == EBADF || errno == EPERM)) return (-1); if (nfd < 0 || nfd > SHRT_MAX) - errorf("too many files open in shell"); + errorf(Ttoo_many_files); fcntl(nfd, F_SETFD, FD_CLOEXEC); return ((short)nfd); } @@ -1464,6 +1522,10 @@ openpipe(int *pv) pv[1] = savefd(lpv[1]); if (pv[1] != lpv[1]) close(lpv[1]); +#ifdef __OS2__ + setmode(pv[0], O_BINARY); + setmode(pv[1], O_BINARY); +#endif } void diff --git a/src/misc.c b/src/misc.c index 647d1d1..6957c22 100644 --- a/src/misc.c +++ b/src/misc.c @@ -3,7 +3,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * 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.249 2016/11/11 23:31:35 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.255 2017/04/12 16:46:22 tg Exp $"); #define KSH_CHVT_FLAG #ifdef MKSH_SMALL @@ -40,10 +40,6 @@ __RCSID("$MirOS: src/bin/mksh/misc.c,v 1.249 2016/11/11 23:31:35 tg Exp $"); #define KSH_CHVT_CODE #define KSH_CHVT_FLAG #endif -#ifdef MKSH_LEGACY_MODE -#undef KSH_CHVT_CODE -#undef KSH_CHVT_FLAG -#endif /* type bits for unsigned char */ unsigned char chtypes[UCHAR_MAX + 1]; @@ -93,11 +89,10 @@ setctypes(const char *s, int t) void initctypes(void) { - setctypes(letters_uc, C_ALPHA); - setctypes(letters_lc, C_ALPHA); - chtypes['_'] |= C_ALPHA; + setctypes(letters_uc, C_ALPHX); + setctypes(letters_lc, C_ALPHX); + chtypes['_'] |= C_ALPHX; setctypes("0123456789", C_DIGIT); - /* \0 added automatically */ setctypes(TC_LEX1, C_LEX1); setctypes("*@#!$-?", C_VAR1); setctypes(TC_IFSWS, C_IFSWS); @@ -506,7 +501,7 @@ parse_args(const char **argv, if (arrayset) { const char *ccp = NULL; - if (*array) + if (array && *array) ccp = skip_varname(array, false); if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) { bi_errorf(Tf_sD_s, array, Tnot_ident); @@ -1541,12 +1536,11 @@ do_realpath(const char *upath) /* symlink target is an absolute path */ xp = Xstring(xs, xp); beginning_of_a_pathname: - /* assert: (ip == ipath)[0] == '/' */ + /* assert: mksh_cdirsep((ip == ipath)[0]) */ /* assert: xp == xs.beg => start of path */ /* exactly two leading slashes? (SUSv4 3.266) */ - /* @komh do NOT use mksh_cdirsep() here */ - if (ip[1] == '/' && ip[2] != '/') { + if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) { /* keep them, e.g. for UNC pathnames */ Xput(xs, xp, '/'); } @@ -1711,15 +1705,13 @@ simplify_path(char *p) case 0: return; case '/': - /* exactly two leading slashes? (SUSv4 3.266) */ - /* @komh no mksh_cdirsep() here! */ - if (p[1] == '/' && p[2] != '/') - /* keep them, e.g. for UNC pathnames */ - ++p; -#ifdef __OS2__ - /* FALLTHROUGH */ +#ifdef MKSH_DOSPATH case '\\': #endif + /* exactly two leading slashes? (SUSv4 3.266) */ + if (p[1] == p[0] && !mksh_cdirsep(p[2])) + /* keep them, e.g. for UNC pathnames */ + ++p; needslash = true; break; default: @@ -2157,7 +2149,7 @@ getrusage(int what, struct rusage *ru) int unbksl(bool cstyle, int (*fg)(void), void (*fp)(int)) { - int wc, i, c, fc; + int wc, i, c, fc, n; fc = (*fg)(); switch (fc) { @@ -2245,7 +2237,8 @@ unbksl(bool cstyle, int (*fg)(void), void (*fp)(int)) * four (U: eight) digits; convert to Unicode */ wc = 0; - while (i--) { + n = 0; + while (n < i || i == -1) { wc <<= 4; if ((c = (*fg)()) >= ord('0') && c <= ord('9')) wc += ksh_numdig(c); @@ -2258,7 +2251,10 @@ unbksl(bool cstyle, int (*fg)(void), void (*fp)(int)) (*fp)(c); break; } + ++n; } + if (!n) + goto unknown_escape; if ((cstyle && wc > 0xFF) || fc != 'x') /* Unicode marker */ wc += 0x100; diff --git a/src/mksh.1 b/src/mksh.1 index 0de2b8f..6a2609a 100644 --- a/src/mksh.1 +++ b/src/mksh.1 @@ -1,8 +1,8 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.420 2016/11/11 23:31:36 tg Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.442 2017/04/12 18:30:58 tg Exp $ .\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, -.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016 +.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 .\" mirabilos .\" .\" Provided that these terms and disclaimer and all copyright notices @@ -76,7 +76,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: November 11 2016 $ +.Dd $Mdocdate: April 12 2017 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -186,23 +186,8 @@ sometimes does take portable shell scripting or various standards into account all information is first and foremost presented with .Nm in mind and should be taken as such. -.Ss I'm an Android user, so what's mksh? -.Nm mksh -is a -.Ux -shell / command interpreter, similar to -.Nm COMMAND.COM -or -.Nm CMD.EXE , -which has been included with -.Tn Android Open Source Project -for a while now. -Basically, it's a program that runs in a terminal (console window), -takes user input and runs commands or scripts, which it can also -be asked to do by other programs, even in the background. -Any privilege pop-ups you might be encountering are thus not -.Nm mksh -issues but questions by some other program utilising it. +.Ss I use Android, OS/2, etc. so what...? +Please see the FAQ at the end of this document. .Ss Invocation Most builtins can be called directly, for example if a link points from its name to the shell; not all make sense, have been tested or work at all though. @@ -1130,17 +1115,17 @@ also by newline) may be one same parse tree. .Pp The following command aliases are defined automatically by the shell: .Bd -literal -offset indent -autoload=\*(aq\etypeset \-fu\*(aq -functions=\*(aq\etypeset \-f\*(aq -hash=\*(aq\ebuiltin alias \-t\*(aq -history=\*(aq\ebuiltin fc \-l\*(aq -integer=\*(aq\etypeset \-i\*(aq -local=\*(aq\etypeset\*(aq -login=\*(aq\eexec login\*(aq -nameref=\*(aq\etypeset \-n\*(aq +autoload=\*(aq\e\ebuiltin typeset \-fu\*(aq +functions=\*(aq\e\ebuiltin typeset \-f\*(aq +hash=\*(aq\e\ebuiltin alias \-t\*(aq +history=\*(aq\e\ebuiltin fc \-l\*(aq +integer=\*(aq\e\ebuiltin typeset \-i\*(aq +local=\*(aq\e\ebuiltin typeset\*(aq +login=\*(aq\e\ebuiltin exec login\*(aq +nameref=\*(aq\e\ebuiltin typeset \-n\*(aq nohup=\*(aqnohup \*(aq -r=\*(aq\ebuiltin fc \-e \-\*(aq -type=\*(aq\ebuiltin whence \-v\*(aq +r=\*(aq\e\ebuiltin fc \-e \-\*(aq +type=\*(aq\e\ebuiltin whence \-v\*(aq .Ed .Pp Tracked aliases allow the shell to remember where it found a particular @@ -2035,9 +2020,11 @@ searched when looking for commands and files sourced using the .Dq Li \&. command (see below). An empty string resulting from a leading or trailing -colon, or two adjacent colons, is treated as a +(semi)colon, or two adjacent ones, is treated as a .Dq Li \&. (the current directory). +.It Ev PATHSEP +A colon (semicolon on OS/2), for the user's convenience. .It Ev PGRP The process ID of the shell's process group leader. .It Ev PIPESTATUS @@ -2127,7 +2114,7 @@ Due to a strong suggestion from David G. Korn, .Nm now also supports the following form: .Bd -literal -offset indent -PS1=$'\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt ' +PS1=$\*(aq\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt \*(aq .Ed .It Ev PS2 Secondary prompt string, by default @@ -2185,9 +2172,18 @@ files are created in The effective user id of the shell. .El .Ss Tilde expansion -Tilde expansion which is done in parallel with parameter substitution, is done -on words starting with an unquoted +Tilde expansion, which is done in parallel with parameter substitution, +is applied to words starting with an unquoted .Ql \*(TI . +In parameter assignments (such as those preceding a simple-command or those +occurring in the arguments of a declaration utility), tilde expansion is done +after any assignment (i.e. after the equals sign) or after an unquoted colon +.Pq Ql \&: ; +login names are also delimited by colons. +The Korn shell, except in POSIX mode, always expands tildes after unquoted +equals signs, not just in assignment context (see below), and enables tab +completion for tildes after all unquoted colons during command line editing. +.Pp The characters following the tilde, up to the first .Ql / , if any, are assumed to be a login name. @@ -2208,21 +2204,6 @@ If the login name is not found in the password file or if any quoting or parameter substitution occurs in the login name, no substitution is performed. .Pp -In parameter assignments -(such as those preceding a simple-command or those occurring -in the arguments of -.Ic alias , -.Ic export , -.Ic global , -.Ic readonly -and -.Ic typeset ) , -tilde expansion is done after any assignment -(i.e. after the equals sign) -or after an unquoted colon -.Pq Ql \&: ; -login names are also delimited by colons. -.Pp The home directory of previously expanded login names are cached and re-used. The .Ic alias Fl d @@ -2586,7 +2567,7 @@ Redirections are processed after pipelines are created and in the order they are given, so the following will print an error with a line number prepended to it: .Pp -.D1 $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t +.Dl $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t .Pp File descriptors created by I/O redirections are private to the shell. .Ss Arithmetic expressions @@ -2946,6 +2927,12 @@ function. A function can be made to finish immediately using the .Ic return command; this may also be used to explicitly specify the exit status. +Note that when called in a subshell, +.Ic return +will only exit that subshell and will not cause the original shell to exit +a running function (see the +.Ic while Ns Li \&... Ns Ic read +loop FAQ below). .Pp Functions defined with the .Ic function @@ -3022,18 +3009,18 @@ Additional .Nm commands keeping assignments: .Pp -.Ic builtin , global , source , typeset , -.Ic wait +.Ic global , source , typeset .Pp Builtins that are not special: .Pp .Ic [ , alias , bg , bind , -.Ic cat , cd , command , echo , -.Ic false , fc , fg , getopts , -.Ic jobs , kill , let , print , -.Ic pwd , read , realpath , rename , -.Ic sleep , suspend , test , true , -.Ic ulimit , umask , unalias , whence +.Ic builtin , cat , cd , command , +.Ic echo , false , fc , fg , +.Ic getopts , jobs , kill , let , +.Ic print , pwd , read , realpath , +.Ic rename , sleep , suspend , test , +.Ic true , ulimit , umask , unalias , +.Ic wait , whence .Pp Once the type of command has been determined, any command-line parameter assignments are performed and exported for the duration of the command. @@ -3082,6 +3069,8 @@ For any name without a value, the existing alias is listed. Any name with a value defines an alias (see .Sx Aliases above). +.Li \&[A\-Za\-z0\-9_!%,@\-] +are valid in names except they may not begin with a hyphen-minus. .Pp When listing aliases, one of two formats is used. Normally, aliases are listed as @@ -3217,6 +3206,18 @@ Execute the built-in command .Ar command . .Pp .It Xo +.Ic \ebuiltin +.Ar command Op Ar arg ... +.Xc +Same as +.Ic builtin . +Additionally acts as declaration utility forwarder, i.e. this is a +declaration utility (see +.Sx Tilde expansion ) +.No iff Ar command +is a declaration utility. +.Pp +.It Xo .Ic cat .Op Fl u .Op Ar @@ -3346,6 +3347,7 @@ cannot be a shell function; and secondly, special built-in commands lose their specialness (i.e. redirection and utility errors do not cause the shell to exit, and command assignments are not permanent). +The declaration utility property is not reset. .Pp If the .Fl p @@ -3421,8 +3423,10 @@ If the .Ic posix or .Ic sh -option is set or this is a direct builtin call, only the first argument -is treated as an option, and only if it is exactly +option is set or this is a direct builtin call or +.Ic print +.Fl R , +only the first argument is treated as an option, and only if it is exactly .Dq Li \-n . Backslash interpretation is disabled. .Pp @@ -3463,7 +3467,7 @@ Note that the Bourne shell differs here; it does pass these file descriptors on. .Pp .It Ic exit Op Ar status -The shell exits with the specified exit status. +The shell or subshell exits with the specified exit status. If .Ar status is not specified, the exit status is the current value of the @@ -3478,6 +3482,7 @@ parameter. Sets the export attribute of the named parameters. Exported parameters are passed in the environment to executed commands. If values are specified, the named parameters are also assigned. +This is a declaration utility. .Pp If no parameters are specified, all parameters with the export attribute set are printed one per line; either their names, or, if a @@ -3632,9 +3637,22 @@ resetting .Ev OPTIND may lead to unexpected results. .Pp -.It global Ar ... +.It Xo +.Ic global +.Op Ic +\-aglpnrtUux +.Oo Fl L Ns Op Ar n +.No \*(Ba Fl R Ns Op Ar n +.No \*(Ba Fl Z Ns Op Ar n Oc +.Op Fl i Ns Op Ar n +.Oo Ar name +.Op Ns = Ns Ar value +.Ar ... Oc +.Xc See -.Ic typeset . +.Ic typeset Fl g . +.No Deprecated , Em will +be removed from a future version of +.Nm . .Pp .It Xo .Ic hash @@ -3712,12 +3730,8 @@ If an error occurs during the parsing or evaluation of an expression, the exit status is greater than 1. Since expressions may need to be quoted, .No \&(( Ar expr No )) -is syntactic sugar for -.No "{ let '" Ns Ar expr Ns "'; }" . -.Pp -.It Ic let] -Internally used alias for -.Ic let . +is syntactic sugar for: +.Dl "{ \e\ebuiltin let \*(aq" Ns Ar expr Ns "\*(aq; }" .Pp .It Xo .Ic mknod @@ -3757,13 +3771,13 @@ however, distributors may have added this as builtin as a speed hack. .Pp .It Xo .Ic print -.Oo Fl AclNnprsu Ns Oo Ar n Oc \*(Ba -.Fl R Op Fl en Oc +.Oo Fl AcelNnprsu Ns Oo Ar n Oc \*(Ba +.Fl R Op Fl n Oc .Op Ar argument ... .Xc Print the specified argument(s) on the standard output, separated by spaces, terminated with a newline. -The C escapes mentioned in +The escapes mentioned in .Sx Backslash expansion above, as well as .Dq Li \ec , @@ -3789,6 +3803,9 @@ utility, tab completion, the built-in utility and the .Ic select statement do. +.It Fl e +Restore backslash expansion after a previous +.Fl r . .It Fl l Change the output word separator to newline. .It Fl N @@ -3803,7 +3820,7 @@ above). Inhibit backslash expansion. .It Fl s Print to the history file instead of standard output. -.It Fl u Op Ar n +.It Fl u Ns Op Ar n Print to the file descriptor .Ar n Pq defaults to 1 if omitted instead of standard output. @@ -3811,31 +3828,13 @@ instead of standard output. .Pp The .Fl R -option is used to emulate, to some degree, the +option mostly emulates the .Bx .Xr echo 1 -command which does not process -.Ql \e -sequences unless the -.Fl e -option is given. -As above, the -.Fl n -option suppresses the trailing newline. -.Pp -.It Ic printf Ar format Op Ar arguments ... -Formatted output. -Approximately the same as the -.Xr printf 1 , -utility, except it uses the same -.Sx Backslash expansion -and I/O code and does not handle floating point as the rest of -.Nm mksh . -An external utility is preferred over the builtin. -This is not normally part of -.Nm mksh ; -however, distributors may have added this as builtin as a speed hack. -Do not use in new code. +command which does not expand backslashes and interprets +its first argument as option only if it is exactly +.Dq Li \-n +.Pq to suppress the trailing newline . .Pp .It Ic pwd Op Fl LP Print the present working directory. @@ -3970,40 +3969,6 @@ If no input is read or a timeout occurred, .Ic read exits with a non-zero status. .Pp -Another handy set of tricks: -If -.Ic read -is run in a loop such as -.Ic while read foo; do ...; done -then leading whitespace will be removed (IFS) and backslashes processed. -You might want to use -.Ic while IFS= read \-r foo; do ...; done -for pristine I/O. -Similarly, when using the -.Fl a -option, use of the -.Fl r -option might be prudent; the same applies for: -.Bd -literal -offset indent -find . \-type f \-print0 \*(Ba& \e - while IFS= read \-d \*(aq\*(aq \-pr filename; do - print \-r \-\- "found \*(Lt${filename#./}\*(Gt" -done -.Ed -.Pp -The inner loop will be executed in a subshell and variable changes -cannot be propagated if executed in a pipeline: -.Bd -literal -offset indent -bar \*(Ba baz \*(Ba while read foo; do ...; done -.Ed -.Pp -Use co-processes instead: -.Bd -literal -offset indent -bar \*(Ba baz \*(Ba& -while read \-p foo; do ...; done -exec 3\*(Gt&p; exec 3\*(Gt&\- -.Ed -.Pp .It Xo .Ic readonly .Op Fl p @@ -4012,6 +3977,7 @@ exec 3\*(Gt&p; exec 3\*(Gt&\- .Ar ... Oc .Xc Sets the read-only attribute of the named parameters. +This is a declaration utility. If values are given, parameters are set to them before setting the attribute. Once a parameter is @@ -4266,7 +4232,6 @@ Background jobs are run with lower priority. .It Fl o Ic braceexpand Enable brace expansion (a.k.a. alternation). This is enabled by default. -If disabled, tilde expansion after an equals sign is disabled as a side effect. .It Fl o Ic emacs Enable BRL emacs-like command-line editing (interactive shells only); see .Sx Emacs editing mode . @@ -4507,34 +4472,6 @@ is a symbolic link. .It Fl O Ar file .Ar file Ns 's owner is the shell's effective user ID. -.It Fl o Ar option -Shell -.Ar option -is set (see the -.Ic set -command above for a list of options). -As a non-standard extension, if the option starts with a -.Ql \&! , -the test is negated; the test always fails if -.Ar option -doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option -.Ar foo -exists). -The same can be achieved with [ \-o ?foo ] like in -.At -.Nm ksh93 . -.Ar option -can also be the short flag led by either -.Ql \- -or -.Ql + -.Pq no logical negation , -for example -.Dq Li \-x -or -.Dq Li +x -instead of -.Dq Li xtrace . .It Fl p Ar file .Ar file is a named pipe @@ -4596,6 +4533,38 @@ is not empty. .It Fl z Ar string .Ar string is empty. +.It Fl v Ar name +The shell parameter +.Ar name +is set. +.It Fl o Ar option +Shell +.Ar option +is set (see the +.Ic set +command above for a list of options). +As a non-standard extension, if the option starts with a +.Ql \&! , +the test is negated; the test always fails if +.Ar option +doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option +.Ar foo +exists). +The same can be achieved with [ \-o ?foo ] like in +.At +.Nm ksh93 . +.Ar option +can also be the short flag led by either +.Ql \- +or +.Ql + +.Pq no logical negation , +for example +.Dq Li \-x +or +.Dq Li +x +instead of +.Dq Li xtrace . .It Ar string No = Ar string Strings are equal. .It Ar string No == Ar string @@ -4800,28 +4769,23 @@ traps in functions are not yet implemented. A command that exits with a zero value. .Pp .It Xo -.Ic global -.Oo Op Ic +\-alpnrtUux -.Op Fl L Ns Op Ar n -.Op Fl R Ns Op Ar n -.Op Fl Z Ns Op Ar n +.Ic typeset +.Op Ic +\-aglpnrtUux +.Oo Fl L Ns Op Ar n +.No \*(Ba Fl R Ns Op Ar n +.No \*(Ba Fl Z Ns Op Ar n Oc .Op Fl i Ns Op Ar n -.No \*(Ba Fl f Op Fl tux Oc .Oo Ar name .Op Ns = Ns Ar value .Ar ... Oc .Xc .It Xo .Ic typeset -.Oo Op Ic +\-alpnrtUux -.Op Fl LRZ Ns Op Ar n -.Op Fl i Ns Op Ar n -.No \*(Ba Fl f Op Fl tux Oc -.Oo Ar name -.Op Ns = Ns Ar value -.Ar ... Oc +.Fl f Op Fl tux +.Op Ar name ... .Xc Display or set parameter attributes. +This is a declaration utility. With no .Ar name arguments, parameter attributes are displayed; if no options are used, the @@ -4837,27 +4801,16 @@ parameter values are not printed. If .Ar name arguments are given, the attributes of the named parameters are set -.Pq Ic \- +.Pq Ic \&\- or cleared -.Pq Ic + . +.Pq Ic \&+ ; +inside a function, this will cause the parameters to be created +(with no value) in the local scope (but see +.Fl g ) . Values for parameters may optionally be specified. For .Ar name Ns \&[*] , -the change affects the entire array, and no value may be specified. -.Pp -If -.Ic typeset -is used inside a function, any parameters specified are localised. -This is not done by the otherwise identical -.Ic global . -.Em Note : -This means that -.Nm No 's Ic global -command is -.Em not -equivalent to other programming languages' as it does not allow a -function called from another function to access a parameter at truly -global scope, but only prevents putting an accessed one into local scope. +the change affects all elements of the array, and no value may be specified. .Pp When .Fl f @@ -4877,6 +4830,9 @@ Indexed array attribute. .It Fl f Function mode. Display or set functions and their attributes, instead of parameters. +.It Fl g +Do not cause named parameters to be created in +the local scope when called inside a function. .It Fl i Ns Op Ar n Integer attribute. .Ar n @@ -5028,7 +4984,7 @@ once they are set. Also note that the types of limits available are system dependent \*(en some systems have only the .Fl f -limit. +limit, or not even that, or can set only the soft limits .Bl -tag -width 5n .It Fl a Display all limits; unless @@ -5395,11 +5351,11 @@ Most other historic, or opinionated differences can be disabled by using this mode; these are: .Bl -bullet .It -The GNU +The incompatible GNU .Nm bash I/O redirection .Ic &\*(Gt Ns Ar file -is no longer supported. +is not supported. .It File descriptors created by I/O redirections are inherited by child processes. @@ -5409,20 +5365,34 @@ Numbers with a leading digit zero are interpreted as octal. The .Nm echo builtin does not interpret backslashes and only supports the exact option -.Dq Li \-n . +.Fl n . .It -\&... (list is incomplete and may change for R54) +Alias expansion with a trailing space only reruns on command words. +.It +Tilde expansion follows POSIX instead of Korn shell rules. +.It +The exit status of +.Ic fg +is always 0. +.It +.Ic kill +.Fl l +only lists signal names, all in one line. +.It +.Ic getopts +does not accept options with a leading +.Ql + . .El .Ss SH mode Compatibility mode; intended for use with legacy scripts that cannot easily be fixed; the changes are as follows: .Bl -bullet .It -The GNU +The incompatible GNU .Nm bash I/O redirection .Ic &\*(Gt Ns Ar file -is no longer supported. +is not supported. .It File descriptors created by I/O redirections are inherited by child processes. @@ -5430,7 +5400,9 @@ child processes. The .Nm echo builtin does not interpret backslashes and only supports the exact option -.Dq Li \-n . +.Fl n , +unless built with +.Ev \-DMKSH_MIDNIGHTBSD01ASH_COMPAT . .It The substitution operations .Sm off @@ -5460,7 +5432,16 @@ and .Xc wrongly do not require a parenthesis to be escaped and do not parse extglobs. .It -\&... (list is incomplete and may change for R54) +The getopt construct from +.Xr lksh 1 +passes through the errorlevel. +.It +.Nm sh +.Fl c +eats a leading +.Fl \- +if built with +.Ev \-DMKSH_MIDNIGHTBSD01ASH_COMPAT . .El .Ss Interactive input line editing The shell supports three modes of reading command lines from a @@ -5744,9 +5725,6 @@ Goes to history number .No KILL Pq \*(haU .Xc Deletes the entire input line. -If Ctrl-U should only delete the line up to the cursor, use: -.Pp -.D1 $ bind \-m \*(haU='\*(ha[0\*(haK' .It kill\-region: \*(haW Deletes the input between the cursor and the mark. .It Xo kill\-to\-eol: @@ -6514,7 +6492,7 @@ contains the system and suid profile. .Xr utf\-8 7 , .Xr mknod 8 .Pp -.Pa https://www.mirbsd.org/ksh\-chan.htm +.Pa http://www.mirbsd.org/ksh\-chan.htm .Rs .%A Morris Bolsky .%B "The KornShell Command and Programming Language" @@ -6608,9 +6586,17 @@ The effort of several projects, such as Debian and OpenBSD, and other contributors including our users, to improve the shell is appreciated. See the documentation, web site and CVS for details. .Pp +.Nm mksh\-os2 +is developed by +.An KO Myung-Hun Aq Mt komh@chollian.net . +.Pp +.Nm mksh\-w32 +is developed by +.An Michael Langguth Aq Mt lan@scalaris.com . +.Pp The BSD daemon is Copyright \(co Marshall Kirk McKusick. The complete legalese is at: -.Pa https://www.mirbsd.org/TaC\-mksh.txt +.Pa http://www.mirbsd.org/TaC\-mksh.txt .\" .\" This boils down to: feel free to use mksh.ico as application icon .\" or shortcut for mksh or mksh/Win32 or OS/2; distro patches are ok @@ -6625,24 +6611,6 @@ The complete legalese is at: .\" to protect it or lose it, which McKusick almost did. .\" .Sh CAVEATS -.Nm -has a different scope model from -.At -.Nm ksh , -which leads to subtle differences in semantics for identical builtins. -This can cause issues with a -.Ic nameref -to suddenly point to a local variable by accident; fixing this is hard. -.Pp -The parts of a pipeline, like below, are executed in subshells. -Thus, variable assignments inside them are not visible in the -surrounding execution environment. -Use co-processes instead. -.Bd -literal -offset indent -foo \*(Ba bar \*(Ba read baz # will not change $baz -foo \*(Ba bar \*(Ba& read \-p baz # will, however, do so -.Ed -.Pp .Nm mksh provides a consistent 32-bit integer arithmetic implementation, both signed and unsigned, with sign of the result of a remainder operation @@ -6687,6 +6655,8 @@ case ${KSH_VERSION:\-} in esac .Ed In near future, (Unicode) locale tracking will be implemented though. +.Pp +See also the FAQ below. .Sh BUGS Suspending (using \*(haZ) pipelines like the one below will only suspend the currently running part of the pipeline; in this example, @@ -6710,7 +6680,7 @@ for the in-memory portion of the history is slow, should use .Xr memmove 3 . .Pp This document attempts to describe -.Nm mksh\ R54 +.Nm mksh\ R55 and up, .\" with vendor patches from insert-your-name-here, compiled without any options impacting functionality, such as @@ -6727,9 +6697,8 @@ for an operating environment supporting all of its advanced needs. Please report bugs in .Nm to the -.Mx -mailing list at .Aq Mt miros\-mksh@mirbsd.org +mailing list or in the .Li \&#\&!/bin/mksh .Pq or Li \&#ksh @@ -6738,3 +6707,177 @@ IRC channel at .Pq Port 6697 SSL, 6667 unencrypted , or at: .Pa https://launchpad.net/mksh +.Sh FREQUENTLY ASKED QUESTIONS +This FAQ attempts to document some of the questions users of +.Nm +or readers of this manual page may encounter. +.Ss I'm an Android user, so what's mksh? +.Nm mksh +is a +.Ux +shell / command interpreter, similar to +.Nm COMMAND.COM +or +.Nm CMD.EXE , +which has been included with +.Tn Android Open Source Project +for a while now. +Basically, it's a program that runs in a terminal (console window), +takes user input and runs commands or scripts, which it can also +be asked to do by other programs, even in the background. +Any privilege pop-ups you might be encountering are thus not +.Nm mksh +issues but questions by some other program utilising it. +.Ss "I'm an OS/2 user, what do I need to know?" +Unlike the native command prompt, the current working directory is, +for security reasons common on Unix systems which the shell is designed for, +not in the search path at all; if you really need this, run the command +.Li PATH=.$PATHSEP$PATH +or add that to a suitable initialisation file. +.Pp +There are two different newline modes for mksh-os2: standard (Unix) mode, +in which only LF (0A hex) is supported as line separator, and "textmode", +which also accepts ASCII newlines (CR+LF), like most other tools on OS/2, +but creating an incompatibility with standard +.Nm . +If you compiled mksh from source, you will get the standard Unix mode unless +.Fl T +is added during compilation; you will most likely have gotten this shell +through komh's port on Hobbes, or from his OS/2 Factory on eComStation +Korea, which uses "textmode", though. +Most OS/2 users will want to use "textmode" unless they need absolute +compatibility with Unix +.Nm . +.Ss "How do I start mksh on a specific terminal?" +Normally: +.Dl mksh \-T/dev/tty2 +.Pp +However, if you want for it to return (e.g. for an embedded +system rescue shell), use this on your real console device instead: +.Dl mksh \-T!/dev/ttyACM0 +.Pp +.Nm +can also daemonise (send to the background): +.Dl mksh \-T\- \-c \*(aqexec cdio lock\*(aq +.Ss "POSIX says..." +Run the shell in POSIX mode (and possibly +.Nm lksh +instead of +.Nm mksh ) : +.Dl set \-o posix +.Ss "My prompt from does not work!" +Contact us on the mailing list or on IRC, we'll convert it for you. +.Ss "Something is going wrong with my while...read loop" +Most likely, you've encountered the problem in which the shell runs +all parts of a pipeline as subshell. +The inner loop will be executed in a subshell and variable changes +cannot be propagated if run in a pipeline: +.Bd -literal -offset indent +bar \*(Ba baz \*(Ba while read foo; do ...; done +.Ed +.Pp +Note that +.Ic exit +in the inner loop will only exit the subshell and not the original shell. +Likewise, if the code is inside a function, +.Ic return +in the inner loop will only exit the subshell and won't terminate the function. +.Pp +Use co-processes instead: +.Bd -literal -offset indent +bar \*(Ba baz \*(Ba& +while read \-p foo; do ...; done +exec 3\*(Gt&p; exec 3\*(Gt&\- +.Ed +.Pp +If +.Ic read +is run in a loop such as +.Ic while read foo; do ...; done +then leading whitespace will be removed (IFS) and backslashes processed. +You might want to use +.Ic while IFS= read \-r foo; do ...; done +for pristine I/O. +Similarly, when using the +.Fl a +option, use of the +.Fl r +option might be prudent +.Pq Dq Li read \-raN\-1 arr \*(Ltfile ; +the same applies for NUL-terminated lines: +.Bd -literal -offset indent +find . \-type f \-print0 \*(Ba& \e + while IFS= read \-d \*(aq\*(aq \-pr filename; do + print \-r \-\- "found \*(Lt${filename#./}\*(Gt" +done +.Ed +.Pp +.Ss "What differences in function-local scopes are there?" +.Nm +has a different scope model from +.At +.Nm ksh , +which leads to subtle differences in semantics for identical builtins. +This can cause issues with a +.Ic nameref +to suddenly point to a local variable by accident. +.Pp +.Tn GNU +.Nm bash +allows unsetting local variables; in +.Nm , +doing so in a function allows back access to the global variable +(actually the one in the next scope up) with the same name. +The following code, when run before the function definitions, changes +the behaviour of +.Ic unset +to behave like other shells (the alias can be removed after the definitions): +.Bd -literal -offset indent +case ${KSH_VERSION:\-} in +*MIRBSD\ KSH*\*(Ba*LEGACY\ KSH*) + function unset_compat { + \e\ebuiltin typeset unset_compat_x + + for unset_compat_x in "$@"; do + eval "\e\e\e\ebuiltin unset $unset_compat_x[*]" + done + } + \e\ebuiltin alias unset=unset_compat + ;; +esac +.Ed +.Pp +When a local variable is created (e.g. using +.Ic local , +.Ic typeset , +.Ic integer , +.Ic \e\ebuiltin typeset ) +it does not, like in other shells, inherit the value from the global +(next scope up) variable with the same name; it is rather created +without any value (unset but defined). +.Ss "I get an error in this regex comparison" +Use extglobs instead of regexes: +.Dl "[[ foo =~ (foo\*(Babar).*baz ]] # becomes" +.Dl "[[ foo = *@(foo\*(Babar)*baz* ]] # instead" +.Ss "Are there any extensions to avoid?" +.Tn GNU +.Nm bash +supports +.Dq Li &\*(Gt +.Pq and Dq Li \*(Ba& +to redirect both stdout and stderr in one go, but this breaks POSIX +and Korn Shell syntax; use POSIX redirections instead: +.Dl "foo \*(Ba& bar \*(Ba& baz &\*(Gtlog # GNU bash" +.Dl "foo 2\*(Gt&1 \*(Ba bar 2\*(Gt&1 \*(Ba baz \*(Gtlog 2\*(Gt&1 # POSIX" +.Ss "\*(haL (Ctrl-L) does not clear the screen" +Use \*(ha[\*(haL (Escape+Ctrl-L) or rebind it: +.Dl bind \*(aq\*(haL=clear-screen\*(aq +.Ss "\*(haU (Ctrl-U) clears the entire line" +If it should only delete the line up to the cursor, use: +.Dl bind \-m \*(haU=\*(aq\*(ha[0\*(haK\*(aq +.Ss "Cursor Up behaves differently from zsh" +Some shells make Cursor Up search in the history only for +commands starting with what was already entered. +.Nm +separates the shortcuts: Cursor Up goes up one command +and PgUp searches the history as described above. diff --git a/src/os2.c b/src/os2.c new file mode 100644 index 0000000..5d39630 --- /dev/null +++ b/src/os2.c @@ -0,0 +1,557 @@ +/*- + * Copyright (c) 2015 + * KO Myung-Hun + * + * Provided that these terms and disclaimer and all copyright notices + * are retained or reproduced in an accompanying document, permission + * is granted to deal in this work without restriction, including un- + * limited rights to use, publicly perform, distribute, sell, modify, + * merge, give away, or sublicence. + * + * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to + * the utmost extent permitted by applicable law, neither express nor + * implied; without malicious intent or gross negligence. In no event + * may a licensor, author or contributor be held liable for indirect, + * direct, other damage, loss, or other issues arising in any way out + * of dealing in the work, even if advised of the possibility of such + * damage or existence of a defect, except proven that it results out + * of said person's immediate fault when using the work as intended. + */ + +#define INCL_DOS +#include + +#include "sh.h" + +#include +#include +#include +#include + +__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.1 2017/04/02 15:00:44 tg Exp $"); + +static char *remove_trailing_dots(char *); +static int access_stat_ex(int (*)(), const char *, void *); +static int test_exec_exist(const char *, char *); +static void response(int *, const char ***); +static char *make_response_file(char * const *); +static void env_slashify(void); +static void add_temp(const char *); +static void cleanup_temps(void); +static void cleanup(void); + +#define RPUT(x) do { \ + if (new_argc >= new_alloc) { \ + new_alloc += 20; \ + if (!(new_argv = realloc(new_argv, \ + new_alloc * sizeof(char *)))) \ + goto exit_out_of_memory; \ + } \ + new_argv[new_argc++] = (x); \ +} while (/* CONSTCOND */ 0) + +#define KLIBC_ARG_RESPONSE_EXCLUDE \ + (__KLIBC_ARG_DQUOTE | __KLIBC_ARG_WILDCARD | __KLIBC_ARG_SHELL) + +static void +response(int *argcp, const char ***argvp) +{ + int i, old_argc, new_argc, new_alloc = 0; + const char **old_argv, **new_argv; + char *line, *l, *p; + FILE *f; + + old_argc = *argcp; + old_argv = *argvp; + for (i = 1; i < old_argc; ++i) + if (old_argv[i] && + !(old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) && + old_argv[i][0] == '@') + break; + + if (i >= old_argc) + /* do nothing */ + return; + + new_argv = NULL; + new_argc = 0; + for (i = 0; i < old_argc; ++i) { + if (i == 0 || !old_argv[i] || + (old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) || + old_argv[i][0] != '@' || + !(f = fopen(old_argv[i] + 1, "rt"))) + RPUT(old_argv[i]); + else { + long filesize; + + fseek(f, 0, SEEK_END); + filesize = ftell(f); + fseek(f, 0, SEEK_SET); + + line = malloc(filesize + /* type */ 1 + /* NUL */ 1); + if (!line) { + exit_out_of_memory: + fputs("Out of memory while reading response file\n", stderr); + exit(255); + } + + line[0] = __KLIBC_ARG_NONZERO | __KLIBC_ARG_RESPONSE; + l = line + 1; + while (fgets(l, (filesize + 1) - (l - (line + 1)), f)) { + p = strchr(l, '\n'); + if (p) { + /* + * if a line ends with a backslash, + * concatenate with the next line + */ + if (p > l && p[-1] == '\\') { + char *p1; + int count = 0; + + for (p1 = p - 1; p1 >= l && + *p1 == '\\'; p1--) + count++; + + if (count & 1) { + l = p + 1; + + continue; + } + } + + *p = 0; + } + p = strdup(line); + if (!p) + goto exit_out_of_memory; + + RPUT(p + 1); + + l = line + 1; + } + + free(line); + + if (ferror(f)) { + fputs("Cannot read response file\n", stderr); + exit(255); + } + + fclose(f); + } + } + + RPUT(NULL); + --new_argc; + + *argcp = new_argc; + *argvp = new_argv; +} + +static void +init_extlibpath(void) +{ + const char *vars[] = { + "BEGINLIBPATH", + "ENDLIBPATH", + "LIBPATHSTRICT", + NULL + }; + char val[512]; + int flag; + + for (flag = 0; vars[flag]; flag++) { + DosQueryExtLIBPATH(val, flag + 1); + if (val[0]) + setenv(vars[flag], val, 1); + } +} + +/* + * Convert backslashes of environmental variables to forward slahes. + * A backslash may be used as an escaped character when doing 'echo'. + * This leads to an unexpected behavior. + */ +static void +env_slashify(void) +{ + /* + * PATH and TMPDIR are used by OS/2 as well. That is, they may + * have backslashes as a directory separator. + * BEGINLIBPATH and ENDLIBPATH are special variables on OS/2. + */ + const char *var_list[] = { + "PATH", + "TMPDIR", + "BEGINLIBPATH", + "ENDLIBPATH", + NULL + }; + const char **var; + char *value; + + for (var = var_list; *var; var++) { + value = getenv(*var); + + if (value) + _fnslashify(value); + } +} + +void +os2_init(int *argcp, const char ***argvp) +{ + response(argcp, argvp); + + init_extlibpath(); + env_slashify(); + + if (!isatty(STDIN_FILENO)) + setmode(STDIN_FILENO, O_BINARY); + if (!isatty(STDOUT_FILENO)) + setmode(STDOUT_FILENO, O_BINARY); + if (!isatty(STDERR_FILENO)) + setmode(STDERR_FILENO, O_BINARY); + + atexit(cleanup); +} + +void +setextlibpath(const char *name, const char *val) +{ + int flag; + char *p, *cp; + + if (!strcmp(name, "BEGINLIBPATH")) + flag = BEGIN_LIBPATH; + else if (!strcmp(name, "ENDLIBPATH")) + flag = END_LIBPATH; + else if (!strcmp(name, "LIBPATHSTRICT")) + flag = LIBPATHSTRICT; + else + return; + + /* convert slashes to backslashes */ + strdupx(cp, val, ATEMP); + for (p = cp; *p; p++) { + if (*p == '/') + *p = '\\'; + } + + DosSetExtLIBPATH(cp, flag); + + afree(cp, ATEMP); +} + +/* remove trailing dots */ +static char * +remove_trailing_dots(char *name) +{ + char *p; + + for (p = name + strlen(name); --p > name && *p == '.'; ) + /* nothing */; + + if (*p != '.' && *p != '/' && *p != '\\' && *p != ':') + p[1] = '\0'; + + return (name); +} + +#define REMOVE_TRAILING_DOTS(name) \ + remove_trailing_dots(memcpy(alloca(strlen(name) + 1), name, strlen(name) + 1)) + +/* alias of stat() */ +extern int _std_stat(const char *, struct stat *); + +/* replacement for stat() of kLIBC which fails if there are trailing dots */ +int +stat(const char *name, struct stat *buffer) +{ + return (_std_stat(REMOVE_TRAILING_DOTS(name), buffer)); +} + +/* alias of access() */ +extern int _std_access(const char *, int); + +/* replacement for access() of kLIBC which fails if there are trailing dots */ +int +access(const char *name, int mode) +{ + /* + * On OS/2 kLIBC, X_OK is set only for executable files. + * This prevents scripts from being executed. + */ + if (mode & X_OK) + mode = (mode & ~X_OK) | R_OK; + + return (_std_access(REMOVE_TRAILING_DOTS(name), mode)); +} + +#define MAX_X_SUFFIX_LEN 4 + +static const char *x_suffix_list[] = + { "", ".ksh", ".exe", ".sh", ".cmd", ".com", ".bat", NULL }; + +/* call fn() by appending executable extensions */ +static int +access_stat_ex(int (*fn)(), const char *name, void *arg) +{ + char *x_name; + const char **x_suffix; + int rc = -1; + size_t x_namelen = strlen(name) + MAX_X_SUFFIX_LEN + 1; + + /* otherwise, try to append executable suffixes */ + x_name = alloc(x_namelen, ATEMP); + + for (x_suffix = x_suffix_list; rc && *x_suffix; x_suffix++) { + strlcpy(x_name, name, x_namelen); + strlcat(x_name, *x_suffix, x_namelen); + + rc = fn(x_name, arg); + } + + afree(x_name, ATEMP); + + return (rc); +} + +/* access()/search_access() version */ +int +access_ex(int (*fn)(const char *, int), const char *name, int mode) +{ + /*XXX this smells fishy --mirabilos */ + return (access_stat_ex(fn, name, (void *)mode)); +} + +/* stat() version */ +int +stat_ex(const char *name, struct stat *buffer) +{ + return (access_stat_ex(stat, name, buffer)); +} + +static int +test_exec_exist(const char *name, char *real_name) +{ + struct stat sb; + + if (stat(name, &sb) < 0 || !S_ISREG(sb.st_mode)) + return (-1); + + /* safe due to calculations in real_exec_name() */ + memcpy(real_name, name, strlen(name) + 1); + + return (0); +} + +const char * +real_exec_name(const char *name) +{ + char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1]; + const char *real_name = name; + + if (access_stat_ex(test_exec_exist, real_name, x_name) != -1) + /*XXX memory leak */ + strdupx(real_name, x_name, ATEMP); + + return (real_name); +} + +/* OS/2 can process a command line up to 32 KiB */ +#define MAX_CMD_LINE_LEN 32768 + +/* make a response file to pass a very long command line */ +static char * +make_response_file(char * const *argv) +{ + char rsp_name_arg[] = "@mksh-rsp-XXXXXX"; + char *rsp_name = &rsp_name_arg[1]; + int arg_len = 0; + int i; + + for (i = 0; argv[i]; i++) + arg_len += strlen(argv[i]) + 1; + + /* + * If a length of command line is longer than MAX_CMD_LINE_LEN, then + * use a response file. OS/2 cannot process a command line longer + * than 32K. Of course, a response file cannot be recognised by a + * normal OS/2 program, that is, neither non-EMX or non-kLIBC. But + * it cannot accept a command line longer than 32K in itself. So + * using a response file in this case, is an acceptable solution. + */ + if (arg_len > MAX_CMD_LINE_LEN) { + int fd; + char *result; + + if ((fd = mkstemp(rsp_name)) == -1) + return (NULL); + + /* write all the arguments except a 0th program name */ + for (i = 1; argv[i]; i++) { + write(fd, argv[i], strlen(argv[i])); + write(fd, "\n", 1); + } + + close(fd); + add_temp(rsp_name); + strdupx(result, rsp_name_arg, ATEMP); + return (result); + } + + return (NULL); +} + +/* alias of execve() */ +extern int _std_execve(const char *, char * const *, char * const *); + +/* replacement for execve() of kLIBC */ +int +execve(const char *name, char * const *argv, char * const *envp) +{ + const char *exec_name; + FILE *fp; + char sign[2]; + char *rsp_argv[3]; + char *rsp_name_arg; + int pid; + int status; + int fd; + int rc; + + /* + * #! /bin/sh : append .exe + * extproc sh : search sh.exe in PATH + */ + exec_name = search_path(name, path, X_OK, NULL); + if (!exec_name) { + errno = ENOENT; + return (-1); + } + + /*- + * kLIBC execve() has problems when executing scripts. + * 1. it fails to execute a script if a directory whose name + * is same as an interpreter exists in a current directory. + * 2. it fails to execute a script not starting with sharpbang. + * 3. it fails to execute a batch file if COMSPEC is set to a shell + * incompatible with cmd.exe, such as /bin/sh. + * And ksh process scripts more well, so let ksh process scripts. + */ + errno = 0; + if (!(fp = fopen(exec_name, "rb"))) + errno = ENOEXEC; + + if (!errno && fread(sign, 1, sizeof(sign), fp) != sizeof(sign)) + errno = ENOEXEC; + + if (fp && fclose(fp)) + errno = ENOEXEC; + + if (!errno && + !((sign[0] == 'M' && sign[1] == 'Z') || + (sign[0] == 'N' && sign[1] == 'E') || + (sign[0] == 'L' && sign[1] == 'X'))) + errno = ENOEXEC; + + if (errno == ENOEXEC) + return (-1); + + rsp_name_arg = make_response_file(argv); + + if (rsp_name_arg) { + rsp_argv[0] = argv[0]; + rsp_argv[1] = rsp_name_arg; + rsp_argv[2] = NULL; + + argv = rsp_argv; + } + + pid = spawnve(P_NOWAIT, exec_name, argv, envp); + + afree(rsp_name_arg, ATEMP); + + if (pid == -1) { + cleanup_temps(); + + return (-1); + } + + /* close all opened handles */ + for (fd = 0; fd < NUFILE; fd++) { + if (fcntl(fd, F_GETFD) == -1) + continue; + + close(fd); + } + + while ((rc = waitpid(pid, &status, 0)) < 0 && errno == EINTR) + /* nothing */; + + cleanup_temps(); + + /* Is this possible? And is this right? */ + if (rc == -1) + return (-1); + + if (WIFSIGNALED(status)) + _exit(ksh_sigmask(WTERMSIG(status))); + + _exit(WEXITSTATUS(status)); +} + +static struct temp *templist = NULL; + +static void +add_temp(const char *name) +{ + struct temp *tp; + + tp = alloc(offsetof(struct temp, tffn[0]) + strlen(name) + 1, APERM); + memcpy(tp->tffn, name, strlen(name) + 1); + tp->next = templist; + templist = tp; +} + +/* alias of unlink() */ +extern int _std_unlink(const char *); + +/* + * Replacement for unlink() of kLIBC not supporting to remove files used by + * another processes. + */ +int +unlink(const char *name) +{ + int rc; + + rc = _std_unlink(name); + if (rc == -1 && errno != ENOENT) + add_temp(name); + + return (rc); +} + +static void +cleanup_temps(void) +{ + struct temp *tp; + struct temp **tpnext; + + for (tpnext = &templist, tp = templist; tp; tp = *tpnext) { + if (_std_unlink(tp->tffn) == 0 || errno == ENOENT) { + *tpnext = tp->next; + afree(tp, APERM); + } else { + tpnext = &tp->next; + } + } +} + +static void +cleanup(void) +{ + cleanup_temps(); +} diff --git a/src/sh.h b/src/sh.h index c9b9078..5b36378 100644 --- a/src/sh.h +++ b/src/sh.h @@ -10,7 +10,7 @@ /*- * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -175,9 +175,9 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.791 2016/11/11 23:31:38 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.808 2017/04/12 17:38:46 tg Exp $"); #endif -#define MKSH_VERSION "R54 2016/11/11" +#define MKSH_VERSION "R55 2017/04/12" /* arithmetic types: C implementation */ #if !HAVE_CAN_INTTYPES @@ -392,13 +392,20 @@ struct rusage { #endif #ifdef __OS2__ +#define MKSH_UNIXROOT "/@unixroot" +#else +#define MKSH_UNIXROOT "" +#endif + +#ifdef MKSH_DOSPATH +#ifndef __GNUC__ +# error GCC extensions needed later on +#endif #define MKSH_PATHSEPS ";" #define MKSH_PATHSEPC ';' -#define MKSH_UNIXROOT "/@unixroot" #else #define MKSH_PATHSEPS ":" #define MKSH_PATHSEPC ':' -#define MKSH_UNIXROOT "" #endif #if !HAVE_FLOCK_DECL @@ -505,12 +512,20 @@ extern int __cdecl setegid(gid_t); EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */ #ifdef MKSH_LEGACY_MODE -#define KSH_VERSIONNAME "LEGACY" +#define KSH_VERSIONNAME_ISLEGACY "LEGACY" #else -#define KSH_VERSIONNAME "MIRBSD" +#define KSH_VERSIONNAME_ISLEGACY "MIRBSD" #endif -EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME \ - " KSH " MKSH_VERSION); +#ifdef MKSH_WITH_TEXTMODE +#define KSH_VERSIONNAME_TEXTMODE " +TEXTMODE" +#else +#define KSH_VERSIONNAME_TEXTMODE "" +#endif +#ifndef KSH_VERSIONNAME_VENDOR_EXT +#define KSH_VERSIONNAME_VENDOR_EXT "" +#endif +EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME_ISLEGACY \ + " KSH " MKSH_VERSION KSH_VERSIONNAME_TEXTMODE KSH_VERSIONNAME_VENDOR_EXT); #define KSH_VERSION (initvsn + /* "KSH_VERSION=@(#)" */ 16) EXTERN const char digits_uc[] E_INIT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); @@ -578,7 +593,7 @@ char *ucstrstr(char *, const char *); #define mkssert(e) do { } while (/* CONSTCOND */ 0) #endif -#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 541) +#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 551) #error Must run Build.sh to compile this. extern void thiswillneverbedefinedIhope(void); int @@ -630,14 +645,6 @@ im_sorry_dave(void) } while (/* CONSTCOND */ 0) #endif -#ifdef MKSH_LEGACY_MODE -#ifndef MKSH_NO_CMDLINE_EDITING -#define MKSH_NO_CMDLINE_EDITING /* defined */ -#endif -#undef MKSH_S_NOVI -#define MKSH_S_NOVI 1 -#endif - #ifdef MKSH_SMALL #ifndef MKSH_NOPWNAM #define MKSH_NOPWNAM /* defined */ @@ -772,6 +779,7 @@ extern struct env { #define E_LOOP 5 /* executing for/while # */ #define E_ERRH 6 /* general error handler # */ #define E_GONE 7 /* hidden in child */ +#define E_EVAL 8 /* running eval # */ /* # indicates env has valid jbuf (see unwind()) */ /* struct env.flag values */ @@ -855,8 +863,8 @@ EXTERN char null[] E_INIT(""); #ifndef HAVE_STRING_POOLING /* helpers for pooled strings */ EXTERN const char T4spaces[] E_INIT(" "); -#define T1space (T4spaces + 3) -EXTERN const char Tcolsp[] E_INIT(": "); +#define T1space (Treal_sp2 + 5) +#define Tcolsp (Tf_sD_ + 2) EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n"); #define TC_IFSWS (TC_LEX1 + 7) EXTERN const char TFCEDIT_dollaru[] E_INIT("${FCEDIT:-/bin/ed} $_"); @@ -865,15 +873,19 @@ EXTERN const char Tsgdot[] E_INIT("*=."); EXTERN const char Taugo[] E_INIT("augo"); EXTERN const char Tbracket[] E_INIT("["); #define Tdot (Tsgdot + 2) -EXTERN const char Talias[] E_INIT("alias"); -EXTERN const char Tbadsubst[] E_INIT("bad substitution"); +#define Talias (Tunalias + 2) +EXTERN const char Tbadnum[] E_INIT("bad number"); +#define Tbadsubst (Tfg_badsubst + 10) EXTERN const char Tbg[] E_INIT("bg"); EXTERN const char Tbad_bsize[] E_INIT("bad shf/buf/bsize"); #define Tbsize (Tbad_bsize + 12) EXTERN const char Tbad_sig_ss[] E_INIT("%s: bad signal '%s'"); #define Tbad_sig_s (Tbad_sig_ss + 4) -EXTERN const char Tgbuiltin[] E_INIT("=builtin"); -#define Tbuiltin (Tgbuiltin + 1) +EXTERN const char Tsgbreak[] E_INIT("*=break"); +#define Tbreak (Tsgbreak + 2) +EXTERN const char T__builtin[] E_INIT("-\\builtin"); +#define T_builtin (T__builtin + 1) +#define Tbuiltin (T__builtin + 2) EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes"); EXTERN const char Tcant_cd[] E_INIT("restricted shell - can't cd"); EXTERN const char Tcant_find[] E_INIT("can't find"); @@ -882,31 +894,35 @@ EXTERN const char Tcant_open[] E_INIT("can't open"); EXTERN const char Tbcat[] E_INIT("!cat"); #define Tcat (Tbcat + 1) #define Tcd (Tcant_cd + 25) -EXTERN const char Tcommand[] E_INIT("command"); +#define T_command (T_funny_command + 9) +#define Tcommand (T_funny_command + 10) +EXTERN const char Tsgcontinue[] E_INIT("*=continue"); +#define Tcontinue (Tsgcontinue + 2) EXTERN const char Tcreate[] E_INIT("create"); EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected"); EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL"); -EXTERN const char Tsgexport[] E_INIT("*=export"); -#define Texport (Tsgexport + 2) +EXTERN const char Tdsgexport[] E_INIT("^*=export"); +#define Texport (Tdsgexport + 3) #ifdef __OS2__ EXTERN const char Textproc[] E_INIT("extproc"); #endif EXTERN const char Tfalse[] E_INIT("false"); EXTERN const char Tfg[] E_INIT("fg"); EXTERN const char Tfg_badsubst[] E_INIT("fileglob: bad substitution"); -EXTERN const char Tfile[] E_INIT("file"); +#define Tfile (Tfile_fd + 20) EXTERN const char Tfile_fd[] E_INIT("function definition file"); EXTERN const char TFPATH[] E_INIT("FPATH"); EXTERN const char T_function[] E_INIT(" function"); #define Tfunction (T_function + 1) -EXTERN const char T_funny_command[] E_INIT("funny $() command"); +EXTERN const char T_funny_command[] E_INIT("funny $()-command"); EXTERN const char Tgetopts[] E_INIT("getopts"); -EXTERN const char Thistory[] E_INIT("history"); +#define Thistory (Tnot_in_history + 7) EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented"); +EXTERN const char Tinvname[] E_INIT("%s: invalid %s name"); EXTERN const char Tjobs[] E_INIT("jobs"); EXTERN const char Tjob_not_started[] E_INIT("job not started"); EXTERN const char Tmksh[] E_INIT("mksh"); -EXTERN const char Tname[] E_INIT("name"); +#define Tname (Tinvname + 15) EXTERN const char Tno_args[] E_INIT("missing argument"); EXTERN const char Tno_OLDPWD[] E_INIT("no OLDPWD"); EXTERN const char Tnot_ident[] E_INIT("is not an identifier"); @@ -917,77 +933,84 @@ EXTERN const char Tnot_found_s[] E_INIT("%s not found"); #define TOLDPWD (Tno_OLDPWD + 3) #define Topen (Tcant_open + 6) #define TPATH (TFPATH + 1) -EXTERN const char Tpv[] E_INIT("pv"); +#define Tpv (TpVv + 1) EXTERN const char TpVv[] E_INIT("Vpv"); #define TPWD (Tno_OLDPWD + 6) -EXTERN const char Tread[] E_INIT("read"); -EXTERN const char Tsgreadonly[] E_INIT("*=readonly"); -#define Treadonly (Tsgreadonly + 2) +#define Tread (Tshf_read + 4) +EXTERN const char Tdsgreadonly[] E_INIT("^*=readonly"); +#define Treadonly (Tdsgreadonly + 3) EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection"); #define Tredirection (Tredirection_dup + 19) -EXTERN const char Treal_sp1[] E_INIT("real "); +#define Treal_sp1 (Treal_sp2 + 1) EXTERN const char Treal_sp2[] E_INIT(" real "); EXTERN const char Treq_arg[] E_INIT("requires an argument"); EXTERN const char Tselect[] E_INIT("select"); EXTERN const char Tsgset[] E_INIT("*=set"); -#define Tset (Tsgset + 2) +#define Tset (Tf_parm + 18) #define Tsh (Tmksh + 2) #define TSHELL (TEXECSHELL + 4) +#define Tshell (Ttoo_many_files + 23) EXTERN const char Tshf_read[] E_INIT("shf_read"); EXTERN const char Tshf_write[] E_INIT("shf_write"); +EXTERN const char Tgsource[] E_INIT("=source"); +#define Tsource (Tgsource + 1) EXTERN const char Tj_suspend[] E_INIT("j_suspend"); #define Tsuspend (Tj_suspend + 2) EXTERN const char Tsynerr[] E_INIT("syntax error"); EXTERN const char Ttime[] E_INIT("time"); EXTERN const char Ttoo_many_args[] E_INIT("too many arguments"); +EXTERN const char Ttoo_many_files[] E_INIT("too many open files in shell"); EXTERN const char Ttrue[] E_INIT("true"); EXTERN const char Ttty_fd_dupof[] E_INIT("dup of tty fd"); #define Ttty_fd (Ttty_fd_dupof + 7) -EXTERN const char Tgtypeset[] E_INIT("=typeset"); -#define Ttypeset (Tgtypeset + 1) +EXTERN const char Tdgtypeset[] E_INIT("^=typeset"); +#define Ttypeset (Tdgtypeset + 2) #define Tugo (Taugo + 1) EXTERN const char Tunalias[] E_INIT("unalias"); #define Tunexpected (TELIF_unexpected + 6) +EXTERN const char Tunexpected_type[] E_INIT("%s: unexpected %s type %d"); EXTERN const char Tunknown_option[] E_INIT("unknown option"); -EXTERN const char Tuser_sp1[] E_INIT("user "); +EXTERN const char Tunwind[] E_INIT("unwind"); +#define Tuser_sp1 (Tuser_sp2 + 1) EXTERN const char Tuser_sp2[] E_INIT(" user "); #define Twrite (Tshf_write + 4) EXTERN const char Tf__S[] E_INIT(" %S"); -EXTERN const char Tf__d[] E_INIT(" %d"); +#define Tf__d (Tunexpected_type + 22) EXTERN const char Tf__ss[] E_INIT(" %s%s"); -EXTERN const char Tf__sN[] E_INIT(" %s\n"); +#define Tf__sN (Tf_s_s_sN + 5) EXTERN const char Tf_sSs[] E_INIT("%s/%s"); -EXTERN const char Tf_T[] E_INIT("%T"); +#define Tf_T (Tf_s_T + 3) EXTERN const char Tf_dN[] E_INIT("%d\n"); EXTERN const char Tf_s_[] E_INIT("%s "); EXTERN const char Tf_s_T[] E_INIT("%s %T"); EXTERN const char Tf_s_s_sN[] E_INIT("%s %s %s\n"); -EXTERN const char Tf_s_s[] E_INIT("%s %s"); -EXTERN const char Tf_s_sD_s[] E_INIT("%s %s: %s"); +#define Tf_s_s (Tf_sD_s_s + 4) +#define Tf_s_sD_s (Tf_cant_ss_s + 6) EXTERN const char Tf_optfoo[] E_INIT("%s%s-%c: %s"); EXTERN const char Tf_sD_[] E_INIT("%s: "); EXTERN const char Tf_szs[] E_INIT("%s: %zd %s"); EXTERN const char Tf_parm[] E_INIT("%s: parameter not set"); EXTERN const char Tf_coproc[] E_INIT("-p: %s"); -EXTERN const char Tf_cant[] E_INIT("can't %s %s: %s"); -EXTERN const char Tf_heredoc[] E_INIT("here document '%s' unclosed\n"); +EXTERN const char Tf_cant_s[] E_INIT("%s: can't %s"); +EXTERN const char Tf_cant_ss_s[] E_INIT("can't %s %s: %s"); +EXTERN const char Tf_heredoc[] E_INIT("here document '%s' unclosed"); #if HAVE_MKNOD EXTERN const char Tf_nonnum[] E_INIT("non-numeric %s %s '%s'"); #endif EXTERN const char Tf_S_[] E_INIT("%S "); #define Tf_S (Tf__S + 1) -EXTERN const char Tf_lu[] E_INIT("%lu"); +#define Tf_lu (Tf_toolarge + 17) EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu"); EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s"); -#define Tf_ss (Tf__ss + 1) +#define Tf_ss (Tf_sss + 2) EXTERN const char Tf_sss[] E_INIT("%s%s%s"); EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s"); -EXTERN const char Tf_toomany[] E_INIT("too many %ss\n"); +EXTERN const char Tf_toomany[] E_INIT("too many %ss"); EXTERN const char Tf_sd[] E_INIT("%s %d"); -#define Tf_s (Tf__ss + 3) +#define Tf_s (Tf_temp + 28) EXTERN const char Tft_end[] E_INIT("%;"); EXTERN const char Tft_R[] E_INIT("%R"); -#define Tf_d (Tf__d + 1) +#define Tf_d (Tunexpected_type + 23) EXTERN const char Tf_sD_s_qs[] E_INIT("%s: %s '%s'"); EXTERN const char Tf_ro[] E_INIT("read-only: %s"); EXTERN const char Tf_flags[] E_INIT("%s: flags 0x%X"); @@ -996,8 +1019,8 @@ EXTERN const char Tf_ssfaileds[] E_INIT("%s: %s failed: %s"); EXTERN const char Tf_sD_sD_s[] E_INIT("%s: %s: %s"); EXTERN const char Tf__c_[] E_INIT("-%c "); EXTERN const char Tf_sD_s_s[] E_INIT("%s: %s %s"); -#define Tf_sN (Tf__sN + 1) -#define Tf_sD_s (Tf_s_sD_s + 3) +#define Tf_sN (Tf_s_s_sN + 6) +#define Tf_sD_s (Tf_temp + 24) EXTERN const char T_devtty[] E_INIT("/dev/tty"); #else /* helpers for string pooling */ #define T4spaces " " @@ -1012,13 +1035,17 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define Tbracket "[" #define Tdot "." #define Talias "alias" +#define Tbadnum "bad number" #define Tbadsubst "bad substitution" #define Tbg "bg" #define Tbad_bsize "bad shf/buf/bsize" #define Tbsize "bsize" #define Tbad_sig_ss "%s: bad signal '%s'" #define Tbad_sig_s "bad signal '%s'" -#define Tgbuiltin "=builtin" +#define Tsgbreak "*=break" +#define Tbreak "break" +#define T__builtin "-\\builtin" +#define T_builtin "\\builtin" #define Tbuiltin "builtin" #define Toomem "can't allocate %zu data bytes" #define Tcant_cd "restricted shell - can't cd" @@ -1028,11 +1055,14 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define Tbcat "!cat" #define Tcat "cat" #define Tcd "cd" +#define T_command "-command" #define Tcommand "command" +#define Tsgcontinue "*=continue" +#define Tcontinue "continue" #define Tcreate "create" #define TELIF_unexpected "TELIF unexpected" #define TEXECSHELL "EXECSHELL" -#define Tsgexport "*=export" +#define Tdsgexport "^*=export" #define Texport "export" #ifdef __OS2__ #define Textproc "extproc" @@ -1045,10 +1075,11 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define TFPATH "FPATH" #define T_function " function" #define Tfunction "function" -#define T_funny_command "funny $() command" +#define T_funny_command "funny $()-command" #define Tgetopts "getopts" #define Thistory "history" #define Tintovfl "integer overflow %zu %c %zu prevented" +#define Tinvname "%s: invalid %s name" #define Tjobs "jobs" #define Tjob_not_started "job not started" #define Tmksh "mksh" @@ -1067,7 +1098,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define TpVv "Vpv" #define TPWD "PWD" #define Tread "read" -#define Tsgreadonly "*=readonly" +#define Tdsgreadonly "^*=readonly" #define Treadonly "readonly" #define Tredirection_dup "can't finish (dup) redirection" #define Tredirection "redirection" @@ -1079,22 +1110,28 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define Tset "set" #define Tsh "sh" #define TSHELL "SHELL" +#define Tshell "shell" #define Tshf_read "shf_read" #define Tshf_write "shf_write" +#define Tgsource "=source" +#define Tsource "source" #define Tj_suspend "j_suspend" #define Tsuspend "suspend" #define Tsynerr "syntax error" #define Ttime "time" #define Ttoo_many_args "too many arguments" +#define Ttoo_many_files "too many open files in shell" #define Ttrue "true" #define Ttty_fd_dupof "dup of tty fd" #define Ttty_fd "tty fd" -#define Tgtypeset "=typeset" +#define Tdgtypeset "^=typeset" #define Ttypeset "typeset" #define Tugo "ugo" #define Tunalias "unalias" #define Tunexpected "unexpected" +#define Tunexpected_type "%s: unexpected %s type %d" #define Tunknown_option "unknown option" +#define Tunwind "unwind" #define Tuser_sp1 "user " #define Tuser_sp2 " user " #define Twrite "write" @@ -1115,8 +1152,9 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define Tf_szs "%s: %zd %s" #define Tf_parm "%s: parameter not set" #define Tf_coproc "-p: %s" -#define Tf_cant "can't %s %s: %s" -#define Tf_heredoc "here document '%s' unclosed\n" +#define Tf_cant_s "%s: can't %s" +#define Tf_cant_ss_s "can't %s %s: %s" +#define Tf_heredoc "here document '%s' unclosed" #if HAVE_MKNOD #define Tf_nonnum "non-numeric %s %s '%s'" #endif @@ -1128,7 +1166,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty"); #define Tf_ss "%s%s" #define Tf_sss "%s%s%s" #define Tf_sD_s_sD_s "%s: %s %s: %s" -#define Tf_toomany "too many %ss\n" +#define Tf_toomany "too many %ss" #define Tf_sd "%s %d" #define Tf_s "%s" #define Tft_end "%;" @@ -1247,7 +1285,7 @@ EXTERN bool really_exit; /* * fast character classes */ -#define C_ALPHA BIT(0) /* a-z_A-Z */ +#define C_ALPHX BIT(0) /* A-Za-z_ */ #define C_DIGIT BIT(1) /* 0-9 */ #define C_LEX1 BIT(2) /* \t \n\0|&;<>() */ #define C_VAR1 BIT(3) /* *@#!$-? */ @@ -1255,17 +1293,19 @@ EXTERN bool really_exit; #define C_SUBOP1 BIT(5) /* "=-+?" */ #define C_QUOTE BIT(6) /* \t\n "#$&'()*;<=>?[\]`| (needing quoting) */ #define C_IFS BIT(7) /* $IFS */ -#define C_SUBOP2 BIT(8) /* "#%" (magic, see below) */ extern unsigned char chtypes[]; -#define ctype(c, t) tobool( ((t) == C_SUBOP2) ? \ - (((c) == '#' || (c) == '%') ? 1 : 0) : \ - (chtypes[(unsigned char)(c)] & (t)) ) +#define ctype(c, t) tobool(chtypes[(unsigned char)(c)] & (t)) #define ord(c) ((int)(unsigned char)(c)) -#define ksh_isalphx(c) ctype((c), C_ALPHA) -#define ksh_isalnux(c) ctype((c), C_ALPHA | C_DIGIT) -#define ksh_isdigit(c) (((c) >= '0') && ((c) <= '9')) +#define ksh_issubop2(c) tobool((c) == ord('#') || (c) == ord('%')) +#define ksh_isalias(c) (ctype((c), C_ALPHX | C_DIGIT) || (c) == ord('!') || \ + (c) == ord('%') || (c) == ord(',') || \ + (c) == ord('@') || (c) == ord('-')) +#define ksh_isalpha(c) (ctype((c), C_ALPHX) && (c) != ord('_')) +#define ksh_isalphx(c) ctype((c), C_ALPHX) +#define ksh_isalnux(c) ctype((c), C_ALPHX | C_DIGIT) +#define ksh_isdigit(c) ctype((c), C_DIGIT) #define ksh_islower(c) (((c) >= 'a') && ((c) <= 'z')) #define ksh_isupper(c) (((c) >= 'A') && ((c) <= 'Z')) #define ksh_tolower(c) (ksh_isupper(c) ? (c) - 'A' + 'a' : (c)) @@ -1330,7 +1370,7 @@ EXTERN sigset_t sm_default, sm_sigchld; /* name of called builtin function (used by error functions) */ EXTERN const char *builtin_argv0; -/* is called builtin SPEC_BI? (also KEEPASN, odd use though) */ +/* is called builtin a POSIX special builtin? (error functions only) */ EXTERN bool builtin_spec; /* current working directory */ @@ -1463,7 +1503,7 @@ struct tbl { }; EXTERN struct tbl *vtemp; -/* set by global() and local() */ +/* set by isglobal(), global() and local() */ EXTERN bool last_lookup_was_array; /* common flag bits */ @@ -1500,6 +1540,8 @@ EXTERN bool last_lookup_was_array; #define SPEC_BI BIT(12) /* a POSIX special builtin */ #define LOWER_BI BIT(13) /* (with LOW_BI) override even w/o flags */ #define LOW_BI BIT(14) /* external utility overrides built-in one */ +#define DECL_UTIL BIT(15) /* is declaration utility */ +#define DECL_FWDR BIT(16) /* is declaration utility forwarder */ /* * Attributes that can be set by the user (used to decide if an unset @@ -1672,6 +1714,8 @@ struct op { #define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */ #define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */ #define VALSUB 15 /* ${|foo;} substitution (NUL terminated) */ +#define COMASUB 16 /* `…` substitution (COMSUB but expand aliases) */ +#define FUNASUB 17 /* function substitution but expand aliases */ /* * IO redirection @@ -2019,7 +2063,8 @@ int glob_str(char *, XPtrV *, bool); char *do_tilde(char *); /* exec.c */ int execute(struct op * volatile, volatile int, volatile int * volatile); -int shcomexec(const char **); +int c_builtin(const char **); +struct tbl *get_builtin(const char *); struct tbl *findfunc(const char *, uint32_t, bool); int define(const char *, struct op *); const char *builtin(const char *, int (*)(const char **)); @@ -2057,6 +2102,7 @@ int c_printf(const char **); int c_whence(const char **); int c_command(const char **); int c_typeset(const char **); +bool valid_alias_name(const char *); int c_alias(const char **); int c_unalias(const char **); int c_let(const char **); @@ -2086,8 +2132,6 @@ int c_times(const char **); int timex(struct op *, int, volatile int *); void timex_hook(struct op *, char ** volatile *); int c_exec(const char **); -/* dummy function (just need pointer value), special case in comexec() */ -#define c_builtin shcomexec int c_test(const char **); #if HAVE_MKNOD int c_mknod(const char **); @@ -2170,7 +2214,7 @@ int pprompt(const char *, int); /* main.c */ int include(const char *, int, const char **, bool); int command(const char *, int); -int shell(Source * volatile, volatile bool); +int shell(Source * volatile, volatile int); /* argument MUST NOT be 0 */ void unwind(int) MKSH_A_NORETURN; void newenv(int); @@ -2262,6 +2306,14 @@ char *strdup_i(const char *, Area *); char *strndup_i(const char *, size_t, Area *); #endif int unbksl(bool, int (*)(void), void (*)(int)); +#ifdef __OS2__ +/* os2.c */ +void os2_init(int *, const char ***); +void setextlibpath(const char *, const char *); +int access_ex(int (*)(const char *, int), const char *, int); +int stat_ex(const char *, struct stat *); +const char *real_exec_name(const char *); +#endif /* shf.c */ struct shf *shf_open(const char *, int, int, int); struct shf *shf_fdopen(int, int, struct shf *); @@ -2295,9 +2347,8 @@ char *shf_smprintf(const char *, ...) ssize_t shf_vfprintf(struct shf *, const char *, va_list) MKSH_A_FORMAT(__printf__, 2, 0); /* syn.c */ -int assign_command(const char *, bool) MKSH_A_PURE; void initkeywords(void); -struct op *compile(Source *, bool); +struct op *compile(Source *, bool, bool); bool parse_usec(const char *, struct timeval *); char *yyrecursive(int); void yyrecursive_pop(bool); @@ -2323,6 +2374,7 @@ void popblock(void); void initvar(void); struct block *varsearch(struct block *, struct tbl **, const char *, uint32_t); struct tbl *global(const char *); +struct tbl *isglobal(const char *, bool); struct tbl *local(const char *, bool); char *str_val(struct tbl *); int setstr(struct tbl *, const char *, int); @@ -2352,7 +2404,7 @@ enum Test_op { /* non-operator */ TO_NONOP = 0, /* unary operators */ - TO_STNZE, TO_STZER, TO_OPTION, + TO_STNZE, TO_STZER, TO_ISSET, TO_OPTION, TO_FILAXST, TO_FILEXST, TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK, @@ -2410,9 +2462,6 @@ EXTERN bool tty_hasstate; /* true if tty_state is valid */ extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */ #ifdef __OS2__ -#ifndef __GNUC__ -# error oops? -#endif #define binopen2(path,flags) __extension__({ \ int binopen2_fd = open((path), (flags) | O_BINARY); \ if (binopen2_fd >= 0) \ @@ -2425,24 +2474,31 @@ extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */ setmode(binopen3_fd, O_BINARY); \ (binopen3_fd); \ }) +#else +#define binopen2(path,flags) open((path), (flags) | O_BINARY) +#define binopen3(path,flags,mode) open((path), (flags) | O_BINARY, (mode)) +#endif + +#ifdef MKSH_DOSPATH #define mksh_abspath(s) __extension__({ \ const char *mksh_abspath_s = (s); \ (mksh_cdirsep(mksh_abspath_s[0]) || \ - (ksh_isalphx(mksh_abspath_s[0]) && \ + (ksh_isalpha(mksh_abspath_s[0]) && \ mksh_abspath_s[1] == ':')); \ }) #define mksh_cdirsep(c) __extension__({ \ char mksh_cdirsep_c = (c); \ (mksh_cdirsep_c == '/' || mksh_cdirsep_c == '\\'); \ }) -/* - * I've seen mksh_sdirsep(s) and mksh_vdirsep(s) but need to think - * more about the OS/2 port (and, possibly, toy with it) before I - * can merge this upstream, but good job so far @komh, thanks! - */ +#define mksh_sdirsep(s) __extension__({ \ + const char *mksh_sdirsep_s = (s); \ + ((char *)((ksh_isalphx(mksh_sdirsep_s[0]) && \ + mksh_sdirsep_s[1] == ':' && \ + !mksh_cdirsep(mksh_sdirsep_s[2])) ? \ + (mksh_sdirsep_s + 1) : strpbrk(mksh_sdirsep_s, "/\\"))); \ +}) +#define mksh_vdirsep(s) (mksh_sdirsep((s)) != NULL) #else -#define binopen2(path,flags) open((path), (flags) | O_BINARY) -#define binopen3(path,flags,mode) open((path), (flags) | O_BINARY, (mode)) #define mksh_abspath(s) ((s)[0] == '/') #define mksh_cdirsep(c) ((c) == '/') #define mksh_sdirsep(s) strchr((s), '/') diff --git a/src/sh_flags.gen b/src/sh_flags.gen index bcbc729..24b3359 100644 --- a/src/sh_flags.gen +++ b/src/sh_flags.gen @@ -1,6 +1,6 @@ /* +++ GENERATED FILE +++ DO NOT EDIT +++ */ /*- - * Copyright (c) 2013, 2014, 2015 + * Copyright (c) 2013, 2014, 2015, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -21,7 +21,7 @@ #ifndef SHFLAGS_OPTCS #if defined(SHFLAGS_DEFNS) -__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.4 2015/12/12 21:08:44 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $"); #elif defined(SHFLAGS_ENUMS) #define FN(sname,cname,flags,ochar) cname, #define F0(sname,cname,flags,ochar) cname = 0, @@ -36,11 +36,11 @@ F0("allexport", FEXPORT, OF_ANY, 'a') FN("bgnice", FBGNICE, OF_ANY, 0) #endif FN("braceexpand", FBRACEEXPAND, OF_ANY, 0) -#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +#ifndef MKSH_NO_CMDLINE_EDITING FN("emacs", FEMACS, OF_ANY, 0) #endif FN("errexit", FERREXIT, OF_ANY, 'e') -#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +#ifndef MKSH_NO_CMDLINE_EDITING FN("gmacs", FGMACS, OF_ANY, 0) #endif FN("ignoreeof", FIGNOREEOF, OF_ANY, 0) @@ -79,16 +79,16 @@ FN("stdin", FSTDIN, OF_CMDLINE, 's') FN("trackall", FTRACKALL, OF_ANY, 'h') FN("utf8-mode", FUNICODE, OF_ANY, 'U') FN("verbose", FVERBOSE, OF_ANY, 'v') -#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +#ifndef MKSH_NO_CMDLINE_EDITING FN("vi", FVI, OF_ANY, 0) #endif -#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +#ifndef MKSH_NO_CMDLINE_EDITING FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY, 0) #endif -#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +#ifndef MKSH_NO_CMDLINE_EDITING FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY, 0) #endif -#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +#ifndef MKSH_NO_CMDLINE_EDITING FN("viraw", FVIRAW, OF_ANY, 0) #endif FN("xtrace", FXTRACE, OF_ANY, 'x') diff --git a/src/sh_flags.opt b/src/sh_flags.opt index 9f364d5..795d198 100644 --- a/src/sh_flags.opt +++ b/src/sh_flags.opt @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013, 2014, 2015 + * Copyright (c) 2013, 2014, 2015, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -19,7 +19,7 @@ */ @SHFLAGS_DEFNS -__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.4 2015/12/12 21:08:44 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $"); @SHFLAGS_ENUMS #define FN(sname,cname,flags,ochar) cname, #define F0(sname,cname,flags,ochar) cname = 0, @@ -52,7 +52,7 @@ FN("bgnice", FBGNICE, OF_ANY FN("braceexpand", FBRACEEXPAND, OF_ANY /* ./. Emacs command line editing mode */ ->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +>|!MKSH_NO_CMDLINE_EDITING FN("emacs", FEMACS, OF_ANY /* -e quit on error */ @@ -60,7 +60,7 @@ FN("emacs", FEMACS, OF_ANY FN("errexit", FERREXIT, OF_ANY /* ./. Emacs command line editing mode, gmacs variant */ ->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +>|!MKSH_NO_CMDLINE_EDITING FN("gmacs", FGMACS, OF_ANY /* ./. reading EOF does not exit */ @@ -160,19 +160,19 @@ FN("utf8-mode", FUNICODE, OF_ANY FN("verbose", FVERBOSE, OF_ANY /* ./. Vi command line editing mode */ ->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +>|!MKSH_NO_CMDLINE_EDITING FN("vi", FVI, OF_ANY /* ./. enable ESC as file name completion character (non-standard) */ ->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +>|!MKSH_NO_CMDLINE_EDITING FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY /* ./. enable Tab as file name completion character (non-standard) */ ->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +>|!MKSH_NO_CMDLINE_EDITING FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY /* ./. always read in raw mode (no effect) */ ->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE) +>|!MKSH_NO_CMDLINE_EDITING FN("viraw", FVIRAW, OF_ANY /* -x execution trace (display commands as they are run) */ diff --git a/src/shf.c b/src/shf.c index ace0ab6..09cc7c3 100644 --- a/src/shf.c +++ b/src/shf.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, - * 2012, 2013, 2015, 2016 + * 2012, 2013, 2015, 2016, 2017 * mirabilos * * 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.76 2016/07/25 00:04:47 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.79 2017/04/12 17:08:49 tg Exp $"); /* flags to shf_emptybuf() */ #define EB_READSW 0x01 /* about to switch to reading */ @@ -289,29 +289,31 @@ shf_sclose(struct shf *shf) int shf_flush(struct shf *shf) { + int rv = 0; + if (shf->flags & SHF_STRING) - return ((shf->flags & SHF_WR) ? -1 : 0); - - if (shf->fd < 0) + rv = (shf->flags & SHF_WR) ? -1 : 0; + else if (shf->fd < 0) internal_errorf(Tf_sD_s, "shf_flush", "no fd"); - - if (shf->flags & SHF_ERROR) { + else if (shf->flags & SHF_ERROR) { errno = shf->errnosv; - return (-1); - } - - if (shf->flags & SHF_READING) { + rv = -1; + } else if (shf->flags & SHF_READING) { shf->flags &= ~(SHF_EOF | SHF_READING); if (shf->rnleft > 0) { - lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR); + if (lseek(shf->fd, (off_t)-shf->rnleft, + SEEK_CUR) == -1) { + shf->flags |= SHF_ERROR; + shf->errnosv = errno; + rv = -1; + } shf->rnleft = 0; shf->rp = shf->buf; } - return (0); } else if (shf->flags & SHF_WRITING) - return (shf_emptybuf(shf, 0)); + rv = shf_emptybuf(shf, 0); - return (0); + return (rv); } /* @@ -518,7 +520,23 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf) shf->rnleft -= ncopy; buf += ncopy; bsize -= ncopy; +#ifdef MKSH_WITH_TEXTMODE + if (end && buf > orig_buf + 1 && buf[-2] == '\r') { + buf--; + bsize++; + buf[-1] = '\n'; + } +#endif } while (!end && bsize); +#ifdef MKSH_WITH_TEXTMODE + if (!bsize && buf[-1] == '\r') { + int c = shf_getc(shf); + if (c == '\n') + buf[-1] = '\n'; + else if (c != -1) + shf_ungetc(c, shf); + } +#endif *buf = '\0'; return (buf); } diff --git a/src/syn.c b/src/syn.c index 87b7c75..0454488 100644 --- a/src/syn.c +++ b/src/syn.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.115 2016/09/01 12:59:12 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.120 2017/04/06 01:59:57 tg Exp $"); struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ @@ -35,25 +35,24 @@ struct yyrecursive_state { struct yyrecursive_state *next; struct ioword **old_herep; int old_symbol; - int old_salias; int old_nesting_type; bool old_reject; }; -static void yyparse(void); -static struct op *pipeline(int); -static struct op *andor(void); -static struct op *c_list(bool); +static void yyparse(bool); +static struct op *pipeline(int, int); +static struct op *andor(int); +static struct op *c_list(int, bool); static struct ioword *synio(int); -static struct op *nested(int, int, int); -static struct op *get_command(int); -static struct op *dogroup(void); -static struct op *thenpart(void); -static struct op *elsepart(void); -static struct op *caselist(void); -static struct op *casepart(int); -static struct op *function_body(char *, bool); -static char **wordlist(void); +static struct op *nested(int, int, int, int); +static struct op *get_command(int, int); +static struct op *dogroup(int); +static struct op *thenpart(int); +static struct op *elsepart(int); +static struct op *caselist(int); +static struct op *casepart(int, int); +static struct op *function_body(char *, int, bool); +static char **wordlist(int); static struct op *block(int, struct op *, struct op *); static struct op *newtp(int); static void syntaxerr(const char *) MKSH_A_NORETURN; @@ -71,7 +70,6 @@ static struct nesting_state nesting; /* \n changed to ; */ static bool reject; /* token(cf) gets symbol again */ static int symbol; /* yylex value */ -static int sALIAS = ALIAS; /* 0 in yyrecursive */ #define REJECT (reject = true) #define ACCEPT (reject = false) @@ -83,13 +81,13 @@ static const char Tcbrace[] = "}"; static const char Tesac[] = "esac"; static void -yyparse(void) +yyparse(bool doalias) { int c; ACCEPT; - outtree = c_list(source->type == SSTRING); + outtree = c_list(doalias ? ALIAS : 0, source->type == SSTRING); c = tpeek(0); if (c == 0 && !outtree) outtree = newtp(TEOF); @@ -98,14 +96,14 @@ yyparse(void) } static struct op * -pipeline(int cf) +pipeline(int cf, int sALIAS) { struct op *t, *p, *tl = NULL; - t = get_command(cf); + t = get_command(cf, sALIAS); if (t != NULL) { while (token(0) == '|') { - if ((p = get_command(CONTIN)) == NULL) + if ((p = get_command(CONTIN, sALIAS)) == NULL) syntaxerr(NULL); if (tl == NULL) t = tl = block(TPIPE, t, p); @@ -118,15 +116,15 @@ pipeline(int cf) } static struct op * -andor(void) +andor(int sALIAS) { struct op *t, *p; int c; - t = pipeline(0); + t = pipeline(0, sALIAS); if (t != NULL) { while ((c = token(0)) == LOGAND || c == LOGOR) { - if ((p = pipeline(CONTIN)) == NULL) + if ((p = pipeline(CONTIN, sALIAS)) == NULL) syntaxerr(NULL); t = block(c == LOGAND? TAND: TOR, t, p); } @@ -136,14 +134,14 @@ andor(void) } static struct op * -c_list(bool multi) +c_list(int sALIAS, bool multi) { struct op *t = NULL, *p, *tl = NULL; int c; bool have_sep; while (/* CONSTCOND */ 1) { - p = andor(); + p = andor(sALIAS); /* * Token has always been read/rejected at this point, so * we don't worry about what flags to pass token() @@ -232,23 +230,27 @@ synio(int cf) } static struct op * -nested(int type, int smark, int emark) +nested(int type, int smark, int emark, int sALIAS) { struct op *t; struct nesting_state old_nesting; nesting_push(&old_nesting, smark); - t = c_list(true); + t = c_list(sALIAS, true); musthave(emark, KEYWORD|sALIAS); nesting_pop(&old_nesting); return (block(type, t, NULL)); } +static const char builtin_cmd[] = { + QCHAR, '\\', CHAR, 'b', CHAR, 'u', CHAR, 'i', + CHAR, 'l', CHAR, 't', CHAR, 'i', CHAR, 'n', EOS +}; static const char let_cmd[] = { - QCHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS + CHAR, 'l', CHAR, 'e', CHAR, 't', EOS }; static const char setA_cmd0[] = { - QCHAR, 's', CHAR, 'e', CHAR, 't', EOS + CHAR, 's', CHAR, 'e', CHAR, 't', EOS }; static const char setA_cmd1[] = { CHAR, '-', CHAR, 'A', EOS @@ -258,7 +260,7 @@ static const char setA_cmd2[] = { }; static struct op * -get_command(int cf) +get_command(int cf, int sALIAS) { struct op *t; int c, iopn = 0, syniocf, lno; @@ -289,11 +291,11 @@ get_command(int cf) t->lineno = source->line; goto get_command_start; while (/* CONSTCOND */ 1) { - bool check_assign_cmd; + bool check_decl_utility; if (XPsize(args) == 0) { get_command_start: - check_assign_cmd = true; + check_decl_utility = true; cf = sALIAS | CMDASN; } else if (t->u.evalflags) cf = CMDWORD | CMDASN; @@ -311,16 +313,15 @@ get_command(int cf) case LWORD: ACCEPT; - /* - * the iopn == 0 and XPsize(vars) == 0 are - * dubious but AT&T ksh acts this way - */ - if (iopn == 0 && XPsize(vars) == 0 && - check_assign_cmd) { - if (assign_command(ident, false)) + if (check_decl_utility) { + struct tbl *tt = get_builtin(ident); + uint32_t flag; + + flag = tt ? tt->flag : 0; + if (flag & DECL_UTIL) t->u.evalflags = DOVACHECK; - else if (strcmp(ident, Tcommand) != 0) - check_assign_cmd = false; + if (!(flag & DECL_FWDR)) + check_decl_utility = false; } if ((XPsize(args) == 0 || Flag(FKEYWORD)) && is_wdvarassign(yylval.cp)) @@ -343,6 +344,7 @@ get_command(int cf) tcp[wdscan(tcp, EOS) - tcp - 3] = EOS; /* construct new args strings */ + XPput(args, wdcopy(builtin_cmd, ATEMP)); XPput(args, wdcopy(setA_cmd0, ATEMP)); XPput(args, wdcopy(setA_cmd1, ATEMP)); XPput(args, tcp); @@ -372,7 +374,8 @@ get_command(int cf) syntaxerr(NULL); ACCEPT; musthave(/*(*/')', 0); - t = function_body(XPptrv(args)[0], false); + t = function_body(XPptrv(args)[0], + sALIAS, false); } goto Leave; @@ -388,13 +391,13 @@ get_command(int cf) Subshell: subshell_nesting_type_saved = subshell_nesting_type; subshell_nesting_type = ')'; - t = nested(TPAREN, '(', ')'); + t = nested(TPAREN, '(', ')', sALIAS); subshell_nesting_type = subshell_nesting_type_saved; break; } case '{': /*}*/ - t = nested(TBRACE, '{', '}'); + t = nested(TBRACE, '{', '}', sALIAS); break; case MDPAREN: @@ -412,6 +415,7 @@ get_command(int cf) } t = newtp(TCOM); t->lineno = lno; + XPput(args, wdcopy(builtin_cmd, ATEMP)); XPput(args, wdcopy(let_cmd, ATEMP)); XPput(args, yylval.cp); break; @@ -439,12 +443,12 @@ get_command(int cf) t = newtp((c == FOR) ? TFOR : TSELECT); musthave(LWORD, CMDASN); if (!is_wdvarname(yylval.cp, true)) - yyerror("%s: bad identifier\n", + yyerror("%s: bad identifier", c == FOR ? "for" : Tselect); strdupx(t->str, ident, ATEMP); nesting_push(&old_nesting, c); - t->vars = wordlist(); - t->left = dogroup(); + t->vars = wordlist(sALIAS); + t->left = dogroup(sALIAS); nesting_pop(&old_nesting); break; @@ -452,8 +456,8 @@ get_command(int cf) case UNTIL: nesting_push(&old_nesting, c); t = newtp((c == WHILE) ? TWHILE : TUNTIL); - t->left = c_list(true); - t->right = dogroup(); + t->left = c_list(sALIAS, true); + t->right = dogroup(sALIAS); nesting_pop(&old_nesting); break; @@ -462,22 +466,22 @@ get_command(int cf) musthave(LWORD, 0); t->str = yylval.cp; nesting_push(&old_nesting, c); - t->left = caselist(); + t->left = caselist(sALIAS); nesting_pop(&old_nesting); break; case IF: nesting_push(&old_nesting, c); t = newtp(TIF); - t->left = c_list(true); - t->right = thenpart(); + t->left = c_list(sALIAS, true); + t->right = thenpart(sALIAS); musthave(FI, KEYWORD|sALIAS); nesting_pop(&old_nesting); break; case BANG: syniocf &= ~(KEYWORD|sALIAS); - t = pipeline(0); + t = pipeline(0, sALIAS); if (t == NULL) syntaxerr(NULL); t = block(TBANG, NULL, t); @@ -485,7 +489,7 @@ get_command(int cf) case TIME: syniocf &= ~(KEYWORD|sALIAS); - t = pipeline(0); + t = pipeline(0, sALIAS); if (t && t->type == TCOM) { t->str = alloc(2, ATEMP); /* TF_* flags */ @@ -497,7 +501,7 @@ get_command(int cf) case FUNCTION: musthave(LWORD, 0); - t = function_body(yylval.cp, true); + t = function_body(yylval.cp, sALIAS, true); break; } @@ -536,7 +540,7 @@ get_command(int cf) } static struct op * -dogroup(void) +dogroup(int sALIAS) { int c; struct op *list; @@ -554,40 +558,40 @@ dogroup(void) c = '}'; else syntaxerr(NULL); - list = c_list(true); + list = c_list(sALIAS, true); musthave(c, KEYWORD|sALIAS); return (list); } static struct op * -thenpart(void) +thenpart(int sALIAS) { struct op *t; musthave(THEN, KEYWORD|sALIAS); t = newtp(0); - t->left = c_list(true); + t->left = c_list(sALIAS, true); if (t->left == NULL) syntaxerr(NULL); - t->right = elsepart(); + t->right = elsepart(sALIAS); return (t); } static struct op * -elsepart(void) +elsepart(int sALIAS) { struct op *t; switch (token(KEYWORD|sALIAS|CMDASN)) { case ELSE: - if ((t = c_list(true)) == NULL) + if ((t = c_list(sALIAS, true)) == NULL) syntaxerr(NULL); return (t); case ELIF: t = newtp(TELIF); - t->left = c_list(true); - t->right = thenpart(); + t->left = c_list(sALIAS, true); + t->right = thenpart(sALIAS); return (t); default: @@ -597,7 +601,7 @@ elsepart(void) } static struct op * -caselist(void) +caselist(int sALIAS) { struct op *t, *tl; int c; @@ -613,7 +617,7 @@ caselist(void) t = tl = NULL; /* no ALIAS here */ while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { - struct op *tc = casepart(c); + struct op *tc = casepart(c, sALIAS); if (tl == NULL) t = tl = tc, tl->right = NULL; else @@ -624,7 +628,7 @@ caselist(void) } static struct op * -casepart(int endtok) +casepart(int endtok, int sALIAS) { struct op *t; XPtrV ptns; @@ -656,7 +660,7 @@ casepart(int endtok) t->vars = (char **)XPclose(ptns); musthave(')', 0); - t->left = c_list(true); + t->left = c_list(sALIAS, true); /* initialise to default for ;; or omitted */ t->u.charflag = ';'; @@ -680,7 +684,7 @@ casepart(int endtok) } static struct op * -function_body(char *name, +function_body(char *name, int sALIAS, /* function foo { ... } vs foo() { .. } */ bool ksh_func) { @@ -697,7 +701,7 @@ function_body(char *name, */ for (p = sname; *p; p++) if (ctype(*p, C_QUOTE)) - yyerror("%s: invalid function name\n", sname); + yyerror(Tinvname, sname, Tfunction); /* * Note that POSIX allows only compound statements after foo(), @@ -722,7 +726,7 @@ function_body(char *name, t->u.ksh_func = tobool(ksh_func); t->lineno = source->line; - if ((t->left = get_command(CONTIN)) == NULL) { + if ((t->left = get_command(CONTIN, sALIAS)) == NULL) { char *tv; /* * Probably something like foo() followed by EOF or ';'. @@ -747,7 +751,7 @@ function_body(char *name, } static char ** -wordlist(void) +wordlist(int sALIAS) { int c; XPtrV args; @@ -864,7 +868,7 @@ syntaxerr(const char *what) goto Again; } /* don't quote the EOF */ - yyerror("%s: unexpected EOF\n", Tsynerr); + yyerror("%s: unexpected EOF", Tsynerr); /* NOTREACHED */ case LWORD: @@ -891,7 +895,7 @@ syntaxerr(const char *what) s = redir; } } - yyerror("%s: '%s' %s\n", Tsynerr, s, what); + yyerror(Tf_sD_s_qs, Tsynerr, what, s); } static void @@ -925,7 +929,7 @@ newtp(int type) } struct op * -compile(Source *s, bool skiputf8bom) +compile(Source *s, bool skiputf8bom, bool doalias) { nesting.start_token = 0; nesting.start_line = 0; @@ -933,33 +937,10 @@ compile(Source *s, bool skiputf8bom) source = s; if (skiputf8bom) yyskiputf8bom(); - yyparse(); + yyparse(doalias); return (outtree); } -/*- - * This kludge exists to take care of sh/AT&T ksh oddity in which - * the arguments of alias/export/readonly/typeset have no field - * splitting, file globbing, or (normal) tilde expansion done. - * AT&T ksh seems to do something similar to this since - * $ touch a=a; typeset a=[ab]; echo "$a" - * a=[ab] - * $ x=typeset; $x a=[ab]; echo "$a" - * a=a - * $ - */ -int -assign_command(const char *s, bool docommand) -{ - if (!*s) - return (0); - return ((strcmp(s, Talias) == 0) || - (strcmp(s, Texport) == 0) || - (strcmp(s, Treadonly) == 0) || - (docommand && (strcmp(s, Tcommand) == 0)) || - (strcmp(s, Ttypeset) == 0)); -} - /* Check if we are in the middle of reading an alias */ static int inalias(struct source *s) @@ -1144,7 +1125,7 @@ parse_usec(const char *s, struct timeval *tv) * a COMSUB recursively using the main shell parser and lexer */ char * -yyrecursive(int subtype MKSH_A_UNUSED) +yyrecursive(int subtype) { struct op *t; char *cp; @@ -1172,12 +1153,10 @@ yyrecursive(int subtype MKSH_A_UNUSED) memcpy(ys->old_heres, heres, sizeof(heres)); ys->old_herep = herep; herep = heres; - ys->old_salias = sALIAS; - sALIAS = 0; ys->next = e->yyrecursive_statep; e->yyrecursive_statep = ys; /* we use TPAREN as a helper container here */ - t = nested(TPAREN, stok, etok); + t = nested(TPAREN, stok, etok, ALIAS); yyrecursive_pop(false); /* t->left because nested(TPAREN, ...) hides our goodies there */ @@ -1197,7 +1176,6 @@ yyrecursive_pop(bool popall) return; e->yyrecursive_statep = ys->next; - sALIAS = ys->old_salias; memcpy(heres, ys->old_heres, sizeof(heres)); herep = ys->old_herep; reject = ys->old_reject; diff --git a/src/tree.c b/src/tree.c index ff15077..1fd8f2a 100644 --- a/src/tree.c +++ b/src/tree.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2015, 2016 + * 2011, 2012, 2013, 2015, 2016, 2017 * mirabilos * * Provided that these terms and disclaimer and all copyright notices @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.86 2016/07/25 00:04:48 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.89 2017/04/12 16:46:23 tg Exp $"); #define INDENT 8 @@ -58,7 +58,7 @@ ptree(struct op *t, int indent, struct shf *shf) case TCOM: prevent_semicolon = false; /* special-case 'var=<args && /* we have zero arguments, i.e. no program to run */ t->args[0] == NULL && /* we have exactly one variable assignment */ @@ -86,6 +86,15 @@ ptree(struct op *t, int indent, struct shf *shf) shf_puts("#no-vars# ", shf); if (t->args) { w = t->args; + if (*w && **w == CHAR) { + char *cp = wdstrip(*w++, WDS_TPUTS); + + if (valid_alias_name(cp)) + shf_putc('\\', shf); + shf_puts(cp, shf); + shf_putc(' ', shf); + afree(cp, ATEMP); + } while (*w) fptreef(shf, indent, Tf_S_, *w++); } else @@ -352,14 +361,18 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) } shf_putc(c, shf); break; + case COMASUB: case COMSUB: shf_puts("$(", shf); cs = ")"; + if (*wp == '(' /*)*/) + shf_putc(' ', shf); pSUB: while ((c = *wp++) != 0) shf_putc(c, shf); shf_puts(cs, shf); break; + case FUNASUB: case FUNSUB: c = ' '; if (0) @@ -409,8 +422,9 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) case SPAT: c = '|'; if (0) + /* FALLTHROUGH */ case CPAT: - c = /*(*/ ')'; + c = /*(*/ ')'; shf_putc(c, shf); break; } @@ -606,7 +620,9 @@ wdscan(const char *wp, int c) case QCHAR: wp++; break; + case COMASUB: case COMSUB: + case FUNASUB: case FUNSUB: case VALSUB: case EXPRSUB: @@ -832,8 +848,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) } shf_puts("ADELIM=", shf); if (0) + /* FALLTHROUGH */ case CHAR: - shf_puts("CHAR=", shf); + shf_puts("CHAR=", shf); dumpchar(shf, *wp++); break; case QCHAR: @@ -844,6 +861,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) shf_putc('\\', shf); dumpchar(shf, c); goto closeandout; + case COMASUB: + shf_puts("COMASUB<", shf); + goto dumpsub; case COMSUB: shf_puts("COMSUB<", shf); dumpsub: @@ -852,6 +872,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) closeandout: shf_putc('>', shf); break; + case FUNASUB: + shf_puts("FUNASUB<", shf); + goto dumpsub; case FUNSUB: shf_puts("FUNSUB<", shf); goto dumpsub; diff --git a/src/var.c b/src/var.c index 64de0f9..b83977f 100644 --- a/src/var.c +++ b/src/var.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015, 2016 + * 2011, 2012, 2013, 2014, 2015, 2016, 2017 * mirabilos * * 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.209 2016/11/11 23:31:39 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/var.c,v 1.214 2017/04/02 16:47:43 tg Exp $"); /*- * Variables @@ -45,6 +45,9 @@ static uint32_t lcg_state = 5381, qh_state = 4711; /* may only be set by typeset() just before call to array_index_calc() */ static enum namerefflag innermost_refflag = SRF_NOP; +static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool); +static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool, + bool); static char *formatstr(struct tbl *, const char *); static void exportprep(struct tbl *, const char *); static int special(const char *); @@ -224,6 +227,13 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp) */ struct tbl * global(const char *n) +{ + return (isglobal(n, true)); +} + +/* search for variable; if not found, return NULL or create globally */ +struct tbl * +isglobal(const char *n, bool docreate) { struct tbl *vp; union mksh_cchack vname; @@ -291,17 +301,19 @@ global(const char *n) goto out; } l = varsearch(e->loc, &vp, vn, h); + if (vp == NULL && docreate) + vp = ktenter(&l->vars, vn, h); + else + docreate = false; if (vp != NULL) { if (array) vp = arraysearch(vp, val); - goto out; + if (docreate) { + vp->flag |= DEFINED; + if (special(vn)) + vp->flag |= SPECIAL; + } } - vp = ktenter(&l->vars, vn, h); - if (array) - vp = arraysearch(vp, val); - vp->flag |= DEFINED; - if (special(vn)) - vp->flag |= SPECIAL; out: last_lookup_was_array = array; if (vn != n) @@ -1053,8 +1065,9 @@ skip_varname(const char *s, bool aok) size_t alen; if (s && ksh_isalphx(*s)) { - while (*++s && ksh_isalnux(*s)) - ; + do { + ++s; + } while (ksh_isalnux(*s)); if (aok && *s == '[' && (alen = array_ref_len(s))) s += alen; } @@ -1346,7 +1359,7 @@ setspec(struct tbl *vp) if (getint(vp, &num, false) == -1) { s = str_val(vp); if (st != V_RANDOM) - errorf(Tf_sD_sD_s, vp->name, "bad number", s); + errorf(Tf_sD_sD_s, vp->name, Tbadnum, s); num.u = hash(s); } vp->flag |= SPECIAL; @@ -1770,3 +1783,381 @@ record_match(const char *istr) vp->flag = DEFINED | RDONLY; setstr(vp, istr, 0x4); } + +/* typeset, global(deprecated), export, and readonly */ +int +c_typeset(const char **wp) +{ + struct tbl *vp, **p; + uint32_t fset = 0, fclr = 0, flag; + int thing = 0, field = 0, base = 0, i; + struct block *l; + const char *opts; + const char *fieldstr = NULL, *basestr = NULL; + bool localv = false, func = false, pflag = false, istset = true; + enum namerefflag new_refflag = SRF_NOP; + + switch (**wp) { + + /* export */ + case 'e': + fset |= EXPORT; + istset = false; + break; + + /* readonly */ + case 'r': + fset |= RDONLY; + istset = false; + break; + + /* set */ + case 's': + /* called with 'typeset -' */ + break; + + /* typeset */ + case 't': + localv = true; + break; + } + + /* see comment below regarding possible opions */ + opts = istset ? "L#R#UZ#afgi#lnprtux" : "p"; + + builtin_opt.flags |= GF_PLUSOPT; + /* + * AT&T ksh seems to have 0-9 as options which are multiplied + * to get a number that is used with -L, -R, -Z or -i (eg, -1R2 + * sets right justify in a field of 12). This allows options + * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and + * does not allow the number to be specified as a separate argument + * Here, the number must follow the RLZi option, but is optional + * (see the # kludge in ksh_getopt()). + */ + while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) { + flag = 0; + switch (i) { + case 'L': + flag = LJUST; + fieldstr = builtin_opt.optarg; + break; + case 'R': + flag = RJUST; + fieldstr = builtin_opt.optarg; + break; + case 'U': + /* + * AT&T ksh uses u, but this conflicts with + * upper/lower case. If this option is changed, + * need to change the -U below as well + */ + flag = INT_U; + break; + case 'Z': + flag = ZEROFIL; + fieldstr = builtin_opt.optarg; + break; + case 'a': + /* + * this is supposed to set (-a) or unset (+a) the + * indexed array attribute; it does nothing on an + * existing regular string or indexed array though + */ + break; + case 'f': + func = true; + break; + case 'g': + localv = (builtin_opt.info & GI_PLUS) ? true : false; + break; + case 'i': + flag = INTEGER; + basestr = builtin_opt.optarg; + break; + case 'l': + flag = LCASEV; + break; + case 'n': + new_refflag = (builtin_opt.info & GI_PLUS) ? + SRF_DISABLE : SRF_ENABLE; + break; + /* export, readonly: POSIX -p flag */ + case 'p': + /* typeset: show values as well */ + pflag = true; + if (istset) + continue; + break; + case 'r': + flag = RDONLY; + break; + case 't': + flag = TRACE; + break; + case 'u': + /* upper case / autoload */ + flag = UCASEV_AL; + break; + case 'x': + flag = EXPORT; + break; + case '?': + return (1); + } + if (builtin_opt.info & GI_PLUS) { + fclr |= flag; + fset &= ~flag; + thing = '+'; + } else { + fset |= flag; + fclr &= ~flag; + thing = '-'; + } + } + + if (fieldstr && !getn(fieldstr, &field)) { + bi_errorf(Tf_sD_s, Tbadnum, fieldstr); + return (1); + } + if (basestr) { + if (!getn(basestr, &base)) { + bi_errorf(Tf_sD_s, "bad integer base", basestr); + return (1); + } + if (base < 1 || base > 36) + base = 10; + } + + if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] && + (wp[builtin_opt.optind][0] == '-' || + wp[builtin_opt.optind][0] == '+') && + wp[builtin_opt.optind][1] == '\0') { + thing = wp[builtin_opt.optind][0]; + builtin_opt.optind++; + } + + if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) || + new_refflag != SRF_NOP)) { + bi_errorf("only -t, -u and -x options may be used with -f"); + return (1); + } + if (wp[builtin_opt.optind]) { + /* + * Take care of exclusions. + * At this point, flags in fset are cleared in fclr and vice + * versa. This property should be preserved. + */ + if (fset & LCASEV) + /* LCASEV has priority over UCASEV_AL */ + fset &= ~UCASEV_AL; + if (fset & LJUST) + /* LJUST has priority over RJUST */ + fset &= ~RJUST; + if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { + /* -Z implies -ZR */ + fset |= RJUST; + fclr &= ~RJUST; + } + /* + * Setting these attributes clears the others, unless they + * are also set in this command + */ + if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV | + INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP) + fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | + LCASEV | INTEGER | INT_U | INT_L); + } + if (new_refflag != SRF_NOP) { + fclr &= ~(ARRAY | ASSOC); + fset &= ~(ARRAY | ASSOC); + fclr |= EXPORT; + fset |= ASSOC; + if (new_refflag == SRF_DISABLE) + fclr |= ASSOC; + } + + /* set variables and attributes */ + if (wp[builtin_opt.optind] && + /* not "typeset -p varname" */ + !(!func && pflag && !(fset | fclr))) { + int rv = 0; + struct tbl *f; + + if (localv && !func) + fset |= LOCAL; + for (i = builtin_opt.optind; wp[i]; i++) { + if (func) { + f = findfunc(wp[i], hash(wp[i]), + tobool(fset & UCASEV_AL)); + if (!f) { + /* AT&T ksh does ++rv: bogus */ + rv = 1; + continue; + } + if (fset | fclr) { + f->flag |= fset; + f->flag &= ~fclr; + } else { + fpFUNCTf(shl_stdout, 0, + tobool(f->flag & FKSH), + wp[i], f->val.t); + shf_putc('\n', shl_stdout); + } + } else if (!typeset(wp[i], fset, fclr, field, base)) { + bi_errorf(Tf_sD_s, wp[i], Tnot_ident); + return (1); + } + } + return (rv); + } + + /* list variables and attributes */ + + /* no difference at this point.. */ + flag = fset | fclr; + if (func) { + for (l = e->loc; l; l = l->next) { + for (p = ktsort(&l->funs); (vp = *p++); ) { + if (flag && (vp->flag & flag) == 0) + continue; + if (thing == '-') + fpFUNCTf(shl_stdout, 0, + tobool(vp->flag & FKSH), + vp->name, vp->val.t); + else + shf_puts(vp->name, shl_stdout); + shf_putc('\n', shl_stdout); + } + } + } else if (wp[builtin_opt.optind]) { + for (i = builtin_opt.optind; wp[i]; i++) { + vp = isglobal(wp[i], false); + c_typeset_vardump(vp, flag, thing, + last_lookup_was_array ? 4 : 0, pflag, istset); + } + } else + c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset); + return (0); +} + +static void +c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing, + bool pflag, bool istset) +{ + struct tbl **blockvars, *vp; + + if (l->next) + c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset); + blockvars = ktsort(&l->vars); + while ((vp = *blockvars++)) + c_typeset_vardump(vp, flag, thing, 0, pflag, istset); + /*XXX doesn’t this leak? */ +} + +static void +c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set, + bool pflag, bool istset) +{ + struct tbl *tvp; + char *s; + + if (!vp) + return; + + /* + * See if the parameter is set (for arrays, if any + * element is set). + */ + for (tvp = vp; tvp; tvp = tvp->u.array) + if (tvp->flag & ISSET) { + any_set |= 1; + break; + } + + /* + * Check attributes - note that all array elements + * have (should have?) the same attributes, so checking + * the first is sufficient. + * + * Report an unset param only if the user has + * explicitly given it some attribute (like export); + * otherwise, after "echo $FOO", we would report FOO... + */ + if (!any_set && !(vp->flag & USERATTRIB)) + return; + if (flag && (vp->flag & flag) == 0) + return; + if (!(vp->flag & ARRAY)) + /* optimise later conditionals */ + any_set = 0; + do { + /* + * Ignore array elements that aren't set unless there + * are no set elements, in which case the first is + * reported on + */ + if (any_set && !(vp->flag & ISSET)) + continue; + /* no arguments */ + if (!thing && !flag) { + if (any_set == 1) { + shprintf(Tf_s_s_sN, Tset, "-A", vp->name); + any_set = 2; + } + /* + * AT&T ksh prints things like export, integer, + * leftadj, zerofill, etc., but POSIX says must + * be suitable for re-entry... + */ + shprintf(Tf_s_s, Ttypeset, ""); + if (((vp->flag & (ARRAY | ASSOC)) == ASSOC)) + shprintf(Tf__c_, 'n'); + if ((vp->flag & INTEGER)) + shprintf(Tf__c_, 'i'); + if ((vp->flag & EXPORT)) + shprintf(Tf__c_, 'x'); + if ((vp->flag & RDONLY)) + shprintf(Tf__c_, 'r'); + if ((vp->flag & TRACE)) + shprintf(Tf__c_, 't'); + if ((vp->flag & LJUST)) + shprintf("-L%d ", vp->u2.field); + if ((vp->flag & RJUST)) + shprintf("-R%d ", vp->u2.field); + if ((vp->flag & ZEROFIL)) + shprintf(Tf__c_, 'Z'); + if ((vp->flag & LCASEV)) + shprintf(Tf__c_, 'l'); + if ((vp->flag & UCASEV_AL)) + shprintf(Tf__c_, 'u'); + if ((vp->flag & INT_U)) + shprintf(Tf__c_, 'U'); + } else if (pflag) { + shprintf(Tf_s_s, istset ? Ttypeset : + (flag & EXPORT) ? Texport : Treadonly, ""); + } + if (any_set) + shprintf("%s[%lu]", vp->name, arrayindex(vp)); + else + shf_puts(vp->name, shl_stdout); + if ((!thing && !flag && pflag) || + (thing == '-' && (vp->flag & ISSET))) { + s = str_val(vp); + shf_putc('=', shl_stdout); + /* AT&T ksh can't have justified integers... */ + if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER) + shf_puts(s, shl_stdout); + else + print_value_quoted(shl_stdout, s); + } + shf_putc('\n', shl_stdout); + + /* + * Only report first 'element' of an array with + * no set elements. + */ + if (!any_set) + return; + } while (!(any_set & 4) && (vp = vp->u.array)); +}