OSDN Git Service

Upgrade to mksh R53a.
authorElliott Hughes <enh@google.com>
Fri, 12 Aug 2016 22:06:53 +0000 (15:06 -0700)
committerElliott Hughes <enh@google.com>
Fri, 12 Aug 2016 22:06:53 +0000 (15:06 -0700)
Note that we skipped R52c because it wouldn't build with clang (thanks to
the "Shave 200 bytes off .text by revisiting string pooling" change).

From the release notes:

R53a is a snapshot/feature release:

[lintian] Fix spelling
[tg] Unbreak multi-line command history broken by history flush
[tg] Fix redefining POSIX functions that were Korn functions before
[tg, TNF] Fix bounds checks in Vi editing mode
[tg] Handle combining characters at end of string or output correctly
[tg] Fix ${!#} ${!?} ${!-} (POSIX, prompted by izabera)
[tg] Fix shf.c-internal buffer overread on printing digits
[J�rg] Fix a typo in the testsuite
[arekm] Increase default edit line size (unless MKSH_SMALL)
[tg] Improve description of Emacs mode keybindings, especially ^U
[tg, arekm, jilles] Abort read builtin in case of read(2) errors
[tg, izabera, carstenh] Fix most of the ambiguous corner cases related to ${[pfx]var[op[word]]} (${@:-1} still unsupported)
[carstenh] Contribute some more testsuite coverage
[tg] WDS_TPUTS now emits QCHAR newline reentrant-safe
[tg] Fix var=<< implementation (LP#1380389)
[tg, FreeBSD] Make XSI test(1) extensions behave as if they were POSIX
[tg, izabera] Add $(<<<x) and $(<<EOF…) implementation
[tg] Lower minimum screen size accepted as “sane” from the OS to 4×2
[tg, Torsten Sillke] Simplify tilde-expanded parameters
[tg, Torsten Sillke] Fix default PS1 for substring matches
[tg] Apply defer-builtin-with-arguments logic to realpath builtin
[tg] Rework string pooling (own vs. compiler’s) (LP#1580348)
[tg] Feature: print -A, prints arguments as characters
[tg, izabera] Replace <<< and >>> as ROL and ROR operators with their new ^< and ^> spelling as per this proposal
[tg, slagtc] Clear-to-EOL under tmux to work around its anti-feature
[tg, p120ph37] Remove support for using file descriptors with more than a single digit, in preparation for named file descriptors
[tg] Correct, but simplify (at the potential cost of more tty I/O than strictly necessary, though never redundant and (probably) not more than before when it was miscalculated), line clearing and redrawing
[slagtc, tg] Implement new evaluate-region editing command Esc+Ctrl-E
[tg] Prefer external rename utility over the recovery builtin
[tg] Remove redundant full-line redraws
[tg, Natureshadow] Fix errorlevel of ‘.’ (“dot” special builtin) when the sourced script does not run any commands, for POSIX compliance
[tg] Refactor op tokens and edchars to shave off some more bytes
[tg] Fix some bugs in the manpage and some occasional/minor code bugs
[tg, Brian Callahan] Mark tests requiring new perl as !need-pass
[tg, slagtc] Add $KSH_MATCH and, to make it usable, ${foo@/bar/baz}
[tg, Score_Under] Fix bogus patch from OpenBSD: only NULL the global source in unwind when actually reclaiming its Area
[izabera] Mention in the manpage that integer bases go up to 36
[Natureshadow] Fix /= operator broken during refactoring

R52c is a bugfix-only release:

[tg] Shave 200 bytes off .text by revisiting string pooling
[tg, J�rg] Fix manpage for ditroff on Schillix
[tg, wbx] Use sed 1q instead of unportable head(1)
[tg] Implement underrun debugging tool for area-based memory allocator
[tg] Fix history underrun when first interactive command is entered
[tg, bef0rd] Do not misinterpret “${0/}” as “${0//”, fixes segfault
[tg, Stéphane Chazelas] Fix display problems with special parameters
[tg, Stéphane Chazelas] Catch attempt to trim $* and $@ with ?, fixes segfault (Todd Miller did this in 2004 for ${x[*]} already, so just sync)
[Martijn Dekker] Fix “command -p” with -Vv to behave as POSIX requires
[tg, jilles, Oleg Bulatov] Fix recusive parser with active heredocs
[tg] Flush even syntax-failing or interrupted commands to history
[tg, fmunozs] Fix invalid memory access for “'\0'” in arithmetics
[tg] Explicitly reserve SIGEXIT and SIGERR for ksh
[tg, izabera] Catch missing here documents at EOF even under “set -n”
[kre, tg] Document Austin#1015 handling (not considered a violation)
[tg, fmunozs] Fix buffer overread for empty nameref targets
[tg] Fix warnings pointed out by latest Debian gcc-snapshot
[tg, Martijn Dekker] Document upcoming set +o changes
[Martijn Dekker] Expand testsuite for command/whence

Change-Id: Ife475b25acae5a85277757cfe9f14c6575eac0cd

27 files changed:
Android.mk
mkmf.sh [deleted file]
src/Build.sh
src/check.t
src/dot.mkshrc
src/edit.c
src/emacsfn.h
src/eval.c
src/exec.c
src/expr.c
src/exprtok.h [new file with mode: 0644]
src/funcs.c
src/histrap.c
src/jobs.c
src/lalloc.c
src/lex.c
src/lksh.1
src/main.c
src/misc.c
src/mksh.1
src/sh.h
src/shf.c
src/signames.inc [deleted file]
src/syn.c
src/tree.c
src/var.c
src/var_spec.h

index 12f3c83..4cff508 100644 (file)
@@ -50,8 +50,9 @@ LOCAL_CFLAGS += \
 
 # ...and CPPFLAGS.
 LOCAL_CFLAGS += \
-    -DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 -DMKSH_CONSERVATIVE_FDS \
-    -DMKSH_DONT_EMIT_IDSTRING -DMKSH_NOPWNAM -DMKSH_BUILDSH \
+    -DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 \
+    -DMKSH_DONT_EMIT_IDSTRING \
+    -DMKSH_BUILDSH \
     -D_GNU_SOURCE -DSETUID_CAN_FAIL_WITH_EAGAIN \
     -DHAVE_ATTRIBUTE_BOUNDED=0 -DHAVE_ATTRIBUTE_FORMAT=1 \
     -DHAVE_ATTRIBUTE_NORETURN=1 \
@@ -67,7 +68,9 @@ LOCAL_CFLAGS += \
     -DHAVE_STRINGS_H=1 -DHAVE_TERMIOS_H=1 -DHAVE_ULIMIT_H=0 \
     -DHAVE_VALUES_H=0 -DHAVE_CAN_INTTYPES=1 -DHAVE_CAN_UCBINTS=1 \
     -DHAVE_CAN_INT8TYPE=1 -DHAVE_CAN_UCBINT8=1 -DHAVE_RLIM_T=1 \
-    -DHAVE_SIG_T=1 -DHAVE_SYS_ERRLIST=0 -DHAVE_SYS_SIGNAME=1 \
+    -DHAVE_SIG_T=1 \
+    -DHAVE_STRING_POOLING=1 \
+    -DHAVE_SYS_ERRLIST=0 -DHAVE_SYS_SIGNAME=1 \
     -DHAVE_SYS_SIGLIST=1 -DHAVE_FLOCK=1 -DHAVE_LOCK_FCNTL=1 \
     -DHAVE_GETRUSAGE=1 \
     -DHAVE_GETSID=1 \
@@ -80,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=521
+    -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=530
 
 include $(BUILD_EXECUTABLE)
diff --git a/mkmf.sh b/mkmf.sh
deleted file mode 100644 (file)
index fafa34b..0000000
--- a/mkmf.sh
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright © 2010, 2012, 2013
-#      Thorsten Glaser <tg@mirbsd.org>
-# This file is provided under the same terms as mksh.
-#-
-# Helper script to let src/Build.sh generate Makefrag.inc
-# which we in turn use in the manual creation of Android.mk
-#
-# This script is supposed to be run from/inside AOSP by the
-# porter of mksh to Android (and only manually).
-
-if test x"$1" = x"-t"; then
-       # test compilation
-       args=-r
-       mkmfmode=1
-else
-       # prepare for AOSP
-       args=-M
-       mkmfmode=0
-fi
-
-cd "$(dirname "$0")"
-srcdir=$(pwd)
-rm -rf tmp
-mkdir tmp
-cd ../..
-aospdir=$(pwd)
-cd $srcdir/tmp
-
-addvar() {
-       _vn=$1; shift
-
-       eval $_vn=\"\$$_vn '$*"'
-}
-
-CFLAGS=
-CPPFLAGS=
-LDFLAGS=
-LIBS=
-
-# The definitions below were generated by examining the
-# output of the following command:
-# make showcommands out/target/product/generic/system/bin/mksh 2>&1 | tee log
-#
-# They are only used to let Build.sh find the compiler, header
-# files, linker and libraries to generate Makefrag.inc (similar
-# to what GNU autotools’ configure scripts do), and never used
-# during the real build process. We need this to port mksh to
-# the Android platform and it is crucial these are as close as
-# possible to the values used later. (You also must example the
-# results gathered from Makefrag.inc to see they are the same
-# across all Android platforms, or add appropriate ifdefs.)
-# Since we no longer use the NDK, AOSP has to have been
-# built before using this script.
-
-CC=$ANDROID_TOOLCHAIN/*-gcc
-
-target_arch=$(cd $ANDROID_BUILD_TOP; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make --no-print-directory -f build/core/config.mk dumpvar-TARGET_ARCH)
-
-addvar CPPFLAGS \
-    -isystem $aospdir/bionic/libc/arch-$target_arch/include \
-    -isystem $aospdir/bionic/libc/include \
-    -isystem $aospdir/bionic/libc/kernel/uapi \
-    -isystem $aospdir/bionic/libc/kernel/uapi/asm-$target_arch \
-    -isystem $aospdir/bionic/libm/include \
-    -isystem $aospdir/bionic/libm/include/$target_arch \
-    -D_FORTIFY_SOURCE=2 \
-    -include $aospdir/build/core/combo/include/arch/linux-$target_arch/AndroidConfig.h \
-    -I$aospdir/build/core/combo/include/arch/linux-$target_arch/ \
-    -DANDROID -DNDEBUG -UDEBUG
-addvar CFLAGS \
-    -fno-exceptions \
-    -Wno-multichar \
-    -fpic \
-    -fPIE \
-    -ffunction-sections \
-    -fdata-sections \
-    -funwind-tables \
-    -fstack-protector \
-    -Wa,--noexecstack \
-    -Werror=format-security \
-    -fno-short-enums \
-    -Wno-unused-but-set-variable \
-    -fno-builtin-sin \
-    -fno-strict-volatile-bitfields \
-    -Wno-psabi \
-    -fmessage-length=0 \
-    -W \
-    -Wall \
-    -Wno-unused \
-    -Winit-self \
-    -Wpointer-arith \
-    -Werror=return-type \
-    -Werror=non-virtual-dtor \
-    -Werror=address \
-    -Werror=sequence-point \
-    -g \
-    -Wstrict-aliasing=2 \
-    -fgcse-after-reload \
-    -frerun-cse-after-loop \
-    -frename-registers \
-    -Os \
-    -fomit-frame-pointer \
-    -fno-strict-aliasing
-addvar LDFLAGS \
-    -nostdlib \
-    -Bdynamic \
-    -fPIE \
-    -pie \
-    -Wl,-dynamic-linker,/system/bin/linker \
-    -Wl,--gc-sections \
-    -Wl,-z,nocopyreloc \
-    -Wl,-z,noexecstack \
-    -Wl,-z,relro \
-    -Wl,-z,now \
-    -Wl,--warn-shared-textrel \
-    -Wl,--fatal-warnings \
-    -Wl,--no-undefined \
-    $ANDROID_PRODUCT_OUT/obj/lib/crtbegin_dynamic.o
-addvar LIBS \
-    -L$ANDROID_PRODUCT_OUT/obj/lib \
-    -Wl,-rpath-link=$ANDROID_PRODUCT_OUT/obj/lib \
-    -Wl,--no-whole-archive \
-    $ANDROID_PRODUCT_OUT/obj/STATIC_LIBRARIES/libcompiler_rt-extras_intermediates/libcompiler_rt-extras.a \
-    -lc \
-    $ANDROID_PRODUCT_OUT/obj/lib/crtend_android.o
-
-
-### Flags used by test builds
-if test $mkmfmode = 1; then
-       addvar CPPFLAGS '-DMKSHRC_PATH=\"/system/etc/mkshrc\"'
-       addvar CPPFLAGS '-DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\"'
-       addvar CPPFLAGS '-DMKSH_DEFAULT_TMPDIR=\"/data/local\"'
-fi
-
-### Override flags
-# Let the shell free all memory upon exiting
-addvar CPPFLAGS -DDEBUG_LEAKS
-# UTF-8 works nowadays
-addvar CPPFLAGS -DMKSH_ASSUME_UTF8
-# Reduce filedescriptor usage
-addvar CPPFLAGS -DMKSH_CONSERVATIVE_FDS
-# Leave out RCS ID strings from the binary
-addvar CPPFLAGS -DMKSH_DONT_EMIT_IDSTRING
-# No getpwnam() calls (affects "cd ~username/" only)
-addvar CPPFLAGS -DMKSH_NOPWNAM
-# Leave out the ulimit builtin
-#addvar CPPFLAGS -DMKSH_NO_LIMITS
-# Compile an extra small mksh (optional)
-#addvar CPPFLAGS -DMKSH_SMALL
-
-# Set target platform
-TARGET_OS=Android
-
-# Android-x86 does not have helper functions for ProPolice SSP
-# and AOSP adds the flags by itself (same for warning flags)
-HAVE_CAN_FNOSTRICTALIASING=0
-HAVE_CAN_FSTACKPROTECTORALL=0
-HAVE_CAN_WALL=0
-export HAVE_CAN_FNOSTRICTALIASING HAVE_CAN_FSTACKPROTECTORALL HAVE_CAN_WALL
-
-# even the idea of persistent history on a phone is funny
-HAVE_PERSISTENT_HISTORY=0; export HAVE_PERSISTENT_HISTORY
-
-# ... and run it!
-export CC CPPFLAGS CFLAGS LDFLAGS LIBS TARGET_OS
-sh ../src/Build.sh $args
-rv=$?
-test x"$args" = x"-r" && exit $rv
-test x0 = x"$rv" && mv -f Makefrag.inc ../
-cd ..
-rm -rf tmp
-exit $rv
index 0bd52d5..d31a7ca 100644 (file)
@@ -1,5 +1,5 @@
 #!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.695 2016/01/02 20:11:31 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.702 2016/08/10 18:20:16 tg Exp $'
 #-
 # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 #              2011, 2012, 2013, 2014, 2015, 2016
@@ -596,7 +596,6 @@ else
        check_categories="$check_categories shell:legacy-yes"
        add_cppflags -DMKSH_LEGACY_MODE
        HAVE_PERSISTENT_HISTORY=0
-       HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1      # from sh.h
 fi
 
 if test x"$srcdir" = x"."; then
@@ -691,7 +690,6 @@ case $TARGET_OS in
        : "${HAVE_CAN_OTWO=0}"
        add_cppflags -DMKSH_NO_SIGSETJMP
        add_cppflags -DMKSH_TYPEDEF_SIG_ATOMIC_T=int
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        ;;
 AIX)
        add_cppflags -D_ALL_SOURCE
@@ -721,7 +719,6 @@ Coherent)
        add_cppflags -DMKSH__NO_SYMLINK
        check_categories="$check_categories nosymlink"
        add_cppflags -DMKSH__NO_SETEUGID
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        add_cppflags -DMKSH_DISABLE_TTY_WARNING
        ;;
 CYGWIN*)
@@ -737,7 +734,6 @@ FreeBSD)
 FreeMiNT)
        oswarn="; it has minor issues"
        add_cppflags -D_GNU_SOURCE
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        : "${HAVE_SETLOCALE_CTYPE=0}"
        ;;
 GNU)
@@ -787,14 +783,12 @@ MidnightBSD)
 Minix-vmd)
        add_cppflags -DMKSH__NO_SETEUGID
        add_cppflags -DMKSH_UNEMPLOYED
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        add_cppflags -D_MINIX_SOURCE
        oldish_ed=no-stderr-ed          # no /bin/ed, maybe see below
        : "${HAVE_SETLOCALE_CTYPE=0}"
        ;;
 Minix3)
        add_cppflags -DMKSH_UNEMPLOYED
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        add_cppflags -DMKSH_NO_LIMITS
        add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX
        oldish_ed=no-stderr-ed          # /usr/bin/ed(!) is broken
@@ -825,12 +819,10 @@ NEXTSTEP)
                oswarn="; it needs libposix.a"
                ;;
        esac
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        ;;
 Ninix3)
        # similar to Minix3
        add_cppflags -DMKSH_UNEMPLOYED
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        add_cppflags -DMKSH_NO_LIMITS
        # but no idea what else could be needed
        oswarn="; it has unknown issues"
@@ -898,7 +890,6 @@ SCO_SV)
                oswarn="$oswarn$nl$TARGET_OS ${TARGET_OSREV}, please tell me what to do"
                ;;
        esac
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        : "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}"
        ;;
 skyos)
@@ -916,12 +907,10 @@ syllable)
 ULTRIX)
        : "${CC=cc -YPOSIX}"
        add_cppflags -DMKSH_TYPEDEF_SSIZE_T=int
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        : "${HAVE_SETLOCALE_CTYPE=0}"
        ;;
 UnixWare|UNIX_SV)
        # SCO UnixWare
-       add_cppflags -DMKSH_CONSERVATIVE_FDS
        : "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}"
        ;;
 UWIN*)
@@ -1114,6 +1103,7 @@ clang)
        :*)     ;;
        *:)     CCC_LD=$CCC_CC; export CCC_LD ;;
        esac
+       : "${HAVE_STRING_POOLING=i1}"
        ;;
 dec)
        vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
@@ -1130,6 +1120,7 @@ gcc)
        vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
            -dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
            $LIBS -dumpversion`'
+       : "${HAVE_STRING_POOLING=i2}"
        ;;
 hpcc)
        vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
@@ -1526,6 +1517,16 @@ if test 1 = $i; then
        ac_flags 1 fwrapv -fwrapv
 fi
 
+# “on demand” means: GCC version >= 4
+fd='if to rely on compiler for string pooling'
+ac_cache string_pooling || case $HAVE_STRING_POOLING in
+2) fx=' (on demand, cached)' ;;
+i1) fv=1 ;;
+i2) fv=2; fx=' (on demand)' ;;
+esac
+ac_testdone
+test x"$HAVE_STRING_POOLING" = x"0" || ac_cppflags
+
 phase=x
 # The following tests run with -Werror or similar (all compilers) if possible
 NOWARN=$DOWARN
@@ -1633,7 +1634,6 @@ if ac_ifcpp 'ifdef MKSH_SMALL' isset_MKSH_SMALL '' \
        : "${HAVE_NICE=0}"
        : "${HAVE_PERSISTENT_HISTORY=0}"
        check_categories="$check_categories smksh"
-       HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1      # from sh.h
 fi
 ac_ifcpp 'if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)' \
     isset_MKSH_BINSH '' 'if invoking as sh should be handled specially' && \
@@ -1646,9 +1646,6 @@ ac_ifcpp 'ifdef MKSH_NOPROSPECTOFWORK' isset_MKSH_NOPROSPECTOFWORK '' \
     check_categories="$check_categories arge nojsig"
 ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \
     'if the default UTF-8 mode is specified' && : "${HAVE_SETLOCALE_CTYPE=0}"
-ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
-    'if traditional/conservative fd use is requested' && \
-    check_categories="$check_categories convfds"
 #ac_ifcpp 'ifdef MKSH_DISABLE_DEPRECATED' isset_MKSH_DISABLE_DEPRECATED '' \
 #    "if deprecated features are to be omitted" && \
 #    check_categories="$check_categories nodeprecated"
@@ -2345,7 +2342,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=521
+add_cppflags -DMKSH_BUILD_R=530
 
 $e $bi$me: Finished configuration testing, now producing output.$ao
 
@@ -2501,7 +2498,7 @@ echo tcfn=$mkshexe >>Rebuild.sh
 echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
 echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh
 if test $cm = makefile; then
-       extras='emacsfn.h rlimits.opt sh.h sh_flags.opt var_spec.h'
+       extras='emacsfn.h exprtok.h rlimits.opt sh.h sh_flags.opt var_spec.h'
        test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
        gens= genq=
        for file in $optfiles; do
@@ -2651,9 +2648,7 @@ MKSH_A4PB                 force use of arc4random_pushb
 MKSH_ASSUME_UTF8               (0=disabled, 1=enabled; default: unset)
 MKSH_BINSHPOSIX                        if */sh or */-sh, enable set -o posix
 MKSH_BINSHREDUCED              if */sh or */-sh, enable set -o sh
-MKSH_CLRTOEOL_STRING           "\033[K"
 MKSH_CLS_STRING                        "\033[;H\033[J"
-MKSH_CONSERVATIVE_FDS          fd 0-9 for scripts, shell only up to 31
 MKSH_DEFAULT_EXECSHELL         "/bin/sh" (do not change)
 MKSH_DEFAULT_PROFILEDIR                "/etc" (do not change)
 MKSH_DEFAULT_TMPDIR            "/tmp" (do not change)
index 98d260d..f221442 100644 (file)
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.t,v 1.721 2016/01/20 21:34:09 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.751 2016/08/12 16:48:02 tg Exp $
 # -*- mode: sh -*-
 #-
 # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 # http://svnweb.freebsd.org/base/head/bin/test/tests/legacy_test.sh?view=co&content-type=text%2Fplain
 #
 # Integrated testsuites from:
-# (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
+# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
 
 expected-stdout:
-       @(#)MIRBSD KSH R52 2016/01/20
+       @(#)MIRBSD KSH R53 2016/08/12
 description:
        Check version of shell.
 stdin:
@@ -39,7 +39,7 @@ name: KSH_VERSION
 category: shell:legacy-no
 ---
 expected-stdout:
-       @(#)LEGACY KSH R52 2016/01/20
+       @(#)LEGACY KSH R53 2016/08/12
 description:
        Check version of legacy shell.
 stdin:
@@ -199,7 +199,7 @@ description:
 stdin:
        alias X='case '
        alias Y=Z
-       X Y in 'Y') echo is y ;; Z) echo is z ; esac
+       X Y in 'Y') echo is y ;; Z) echo is z ;; esac
 expected-stdout:
        is z
 ---
@@ -359,6 +359,18 @@ stdin:
 expected-stdout:
        20
 ---
+name: arith-prec-1
+description:
+       Prove arithmetic expressions with embedded parameter
+       substitutions cannot be parsed ahead of time
+stdin:
+       a='3 + 4'
+       print 1 $((2 * a)) .
+       print 2 $((2 * $a)) .
+expected-stdout:
+       1 14 .
+       2 10 .
+---
 name: arith-div-assoc-1
 description:
        Check associativity of division operator
@@ -1311,6 +1323,7 @@ stdin:
        (echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>/dev/null || echo failed in 38
        foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>/dev/null || echo failed in 39
        foo="a b c"; (echo -n '40 '; ./pfs "${foo#a}"; echo .) 2>/dev/null || echo failed in 40
+       (foo() { return 100; }; foo; echo 41 ${#+${#:+${#?}}\ \}\}\}}) 2>/dev/null || echo failed in 41
 expected-stdout:
        1 }z
        2 ''z}
@@ -1352,6 +1365,7 @@ expected-stdout:
        38 xay / x'a'y .
        39 x' / x' .
        40 < b c> .
+       41 3 }}}
 ---
 name: expand-unglob-dblq
 description:
@@ -1400,6 +1414,7 @@ stdin:
                (echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>/dev/null || \
                    echo "$1 QSTN brac -> error"
        }
+       : '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
        tl_norm 1 -
        tl_norm 2 ''
        tl_norm 3 x
@@ -1530,6 +1545,7 @@ stdin:
                (echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>/dev/null || \
                    echo "$1 QSTN brac -> error"
        }
+       : '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
        tl_norm 1 -
        tl_norm 2 ''
        tl_norm 3 x
@@ -1637,7 +1653,7 @@ expected-exit: 1
 ---
 name: expand-weird-1
 description:
-       Check corner case of trim expansion vs. $# vs. ${#var}
+       Check corner cases of trim expansion vs. $# vs. ${#var} vs. ${var?}
 stdin:
        set 1 2 3 4 5 6 7 8 9 10 11
        echo ${#}       # value of $#
@@ -1645,23 +1661,59 @@ stdin:
        echo ${##1}     # $# trimmed 1
        set 1 2 3 4 5 6 7 8 9 10 11 12
        echo ${##1}
+       (exit 0)
+       echo $? = ${#?} .
+       (exit 111)
+       echo $? = ${#?} .
 expected-stdout:
        11
        2
        1
        2
+       0 = 1 .
+       111 = 3 .
 ---
 name: expand-weird-2
 description:
-       Check corner case of ${var?} vs. ${#var}
-stdin:
-       (exit 0)
-       echo $? = ${#?} .
-       (exit 111)
-       echo $? = ${#?} .
-expected-stdout:
-       0 = 1 .
-       111 = 3 .
+       Check more substitution and extension corner cases
+stdin:
+       :& set -C; pid=$$; sub=$!; flg=$-; set -- i; exec 3>x.tmp
+       #echo "D: !=$! #=$# \$=$$ -=$- ?=$?"
+       echo >&3 3 = s^${!-word} , ${#-word} , p^${$-word} , f^${--word} , ${?-word} .
+       echo >&3 4 = ${!+word} , ${#+word} , ${$+word} , ${-+word} , ${?+word} .
+       echo >&3 5 = s^${!=word} , ${#=word} , p^${$=word} , f^${-=word} , ${?=word} .
+       echo >&3 6 = s^${!?word} , ${#?word} , p^${$?word} , f^${-?word} , ${??word} .
+       echo >&3 7 = sl^${#!} , ${##} , pl^${#$} , fl^${#-} , ${#?} .
+       echo >&3 8 = sw^${%!} , ${%#} , pw^${%$} , fw^${%-} , ${%?} .
+       echo >&3 9 = ${!!} , s^${!#} , ${!$} , s^${!-} , s^${!?} .
+       echo >&3 10 = s^${!#pattern} , ${##pattern} , p^${$#pattern} , f^${-#pattern} , ${?#pattern} .
+       echo >&3 11 = s^${!%pattern} , ${#%pattern} , p^${$%pattern} , f^${-%pattern} , ${?%pattern} .
+       echo >&3 12 = $# : ${##} , ${##1} .
+       set --
+       echo >&3 14 = $# : ${##} , ${##1} .
+       set -- 1 2 3 4 5
+       echo >&3 16 = $# : ${##} , ${##1} .
+       set -- 1 2 3 4 5 6 7 8 9 a b c d e
+       echo >&3 18 = $# : ${##} , ${##1} .
+       exec 3>&-
+       <x.tmp sed \
+           -e "s/ pl^${#pid} / PID /g" -e "s/ sl^${#sub} / SUB /g" -e "s/ fl^${#flg} / FLG /g" \
+           -e "s/ pw^${%pid} / PID /g" -e "s/ sw^${%sub} / SUB /g" -e "s/ fw^${%flg} / FLG /g" \
+           -e "s/ p^$pid / PID /g" -e "s/ s^$sub / SUB /g" -e "s/ f^$flg / FLG /g"
+expected-stdout:
+       3 = SUB , 1 , PID , FLG , 0 .
+       4 = word , word , word , word , word .
+       5 = SUB , 1 , PID , FLG , 0 .
+       6 = SUB , 1 , PID , FLG , 0 .
+       7 = SUB , 1 , PID , FLG , 1 .
+       8 = SUB , 1 , PID , FLG , 1 .
+       9 = ! , SUB , $ , SUB , SUB .
+       10 = SUB , 1 , PID , FLG , 0 .
+       11 = SUB , 1 , PID , FLG , 0 .
+       12 = 1 : 1 , .
+       14 = 0 : 1 , 0 .
+       16 = 5 : 1 , 5 .
+       18 = 14 : 2 , 4 .
 ---
 name: expand-weird-3
 description:
@@ -1687,6 +1739,50 @@ stdin:
 expected-stdout:
        <~/x> </x> <~> <\~> <~><~> <~/x> <~//etc> <~/~>
 ---
+name: expand-bang-1
+description:
+       Check corner case of ${!?} with ! being var vs. op
+stdin:
+       echo ${!?}
+expected-exit: 1
+expected-stderr-pattern: /not set/
+---
+name: expand-bang-2
+description:
+       Check corner case of ${!var} vs. ${var op} with var=!
+stdin:
+       echo 1 $! .
+       echo 2 ${!#} .
+       echo 3 ${!#[0-9]} .
+       echo 4 ${!-foo} .
+       # get an at least three-digit bg pid
+       while :; do
+               :&
+               x=$!
+               if [[ $x != +([0-9]) ]]; then
+                       echo >&2 "cannot test, pid '$x' not numeric"
+                       echo >&2 report this with as many details as possible
+                       exit 1
+               fi
+               [[ $x = [0-9][0-9][0-9]* ]] && break
+       done
+       y=${x#?}
+       t=$!; [[ $t = $x ]]; echo 5 $? .
+       t=${!#}; [[ $t = $x ]]; echo 6 $? .
+       t=${!#[0-9]}; [[ $t = $y ]]; echo 7 $? .
+       t=${!-foo}; [[ $t = $x ]]; echo 8 $? .
+       t=${!?bar}; [[ $t = $x ]]; echo 9 $? .
+expected-stdout:
+       1 .
+       2 .
+       3 .
+       4 foo .
+       5 0 .
+       6 0 .
+       7 0 .
+       8 0 .
+       9 0 .
+---
 name: expand-number-1
 description:
        Check that positional arguments do not overflow
@@ -1695,6 +1791,41 @@ stdin:
 expected-stdout:
        1  .
 ---
+name: expand-slashes-1
+description:
+       Check that side effects in substring replacement are handled correctly
+stdin:
+       foo=n1n1n1n2n3
+       i=2
+       n=1
+       echo 1 ${foo//n$((n++))/[$((++i))]} .
+       echo 2 $n , $i .
+expected-stdout:
+       1 [3][3][3]n2n3 .
+       2 2 , 3 .
+---
+name: expand-slashes-2
+description:
+       Check that side effects in substring replacement are handled correctly
+stdin:
+       foo=n1n1n1n2n3
+       i=2
+       n=1
+       echo 1 ${foo@/n$((n++))/[$((++i))]} .
+       echo 2 $n , $i .
+expected-stdout:
+       1 [3]n1n1[4][5] .
+       2 5 , 5 .
+---
+name: expand-slashes-3
+description:
+       Check that we can access the replaced string
+stdin:
+       foo=n1n1n1n2n3
+       echo 1 ${foo@/n[12]/[$KSH_MATCH]} .
+expected-stdout:
+       1 [n1][n1][n1][n2]n3 .
+---
 name: eglob-bad-1
 description:
        Check that globbing isn't done when glob has syntax error
@@ -1858,7 +1989,7 @@ stdin:
        [[ -n $BASH_VERSION ]] && shopt -s extglob
        x=1222321_ab/cde_b/c_1221
        y=xyz
-       echo 1: ${x/2}
+       echo 1: ${x/2} . ${x/}
        echo 2: ${x//2}
        echo 3: ${x/+(2)}
        echo 4: ${x//+(2)}
@@ -1890,7 +2021,7 @@ stdin:
        echo 30: ${x//\\a/9}
        echo 31: ${x/2/$y}
 expected-stdout:
-       1: 122321_ab/cde_b/c_1221
+       1: 122321_ab/cde_b/c_1221 . 1222321_ab/cde_b/c_1221
        2: 131_ab/cde_b/c_11
        3: 1321_ab/cde_b/c_1221
        4: 131_ab/cde_b/c_11
@@ -2249,9 +2380,20 @@ expected-stdout:
        hi
        there
 ---
-name: heredoc-4
+name: heredoc-4a
+description:
+       Check that an error occurs if the heredoc-delimiter is missing.
+stdin: !
+       cat << EOF
+       hi
+       there
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
+name: heredoc-4an
 description:
        Check that an error occurs if the heredoc-delimiter is missing.
+arguments: !-n!
 stdin: !
        cat << EOF
        hi
@@ -2259,6 +2401,23 @@ stdin: !
 expected-exit: e > 0
 expected-stderr-pattern: /.*/
 ---
+name: heredoc-4b
+description:
+       Check that an error occurs if the heredoc is missing.
+stdin: !
+       cat << EOF
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
+name: heredoc-4bn
+description:
+       Check that an error occurs if the heredoc is missing.
+arguments: !-n!
+stdin: !
+       cat << EOF
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
 name: heredoc-5
 description:
        Check that backslash quotes a $, ` and \ and kills a \newline
@@ -2501,6 +2660,10 @@ stdin:
        eval "$fnd"
        foo
        print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
+       x=y
+       foo
+       typeset -f foo
+       print -r -- "| vc={$vc} vd={$vd} |"
        # check append
        v=<<-
                vapp1
@@ -2526,6 +2689,19 @@ expected-stdout:
        } vc={=c u \x40=
        } vd={=d $x \x40=
        } |
+       function foo {
+               vc=<<- 
+       =c $x \x40=
+       <<
+       
+               vd=<<-"" 
+       =d $x \x40=
+       
+       
+       } 
+       | vc={=c y \x40=
+       } vd={=d $x \x40=
+       } |
        | vapp1^vapp2^ |
 ---
 name: heredoc-12
@@ -2657,6 +2833,54 @@ stdin:
 expected-stdout:
        = these parens \( ) are a problem =
 ---
+name: heredoc-comsub-5
+description:
+       Check heredoc and COMSUB mixture in input
+stdin:
+       prefix() { sed -e "s/^/$1:/"; }
+       XXX() { echo x-en; }
+       YYY() { echo y-es; }
+       
+       prefix A <<XXX && echo "$(prefix B <<XXX
+       echo line 1
+       XXX
+       echo line 2)" && prefix C <<YYY
+       echo line 3
+       XXX
+       echo line 4)"
+       echo line 5
+       YYY
+       XXX
+expected-stdout:
+       A:echo line 3
+       B:echo line 1
+       line 2
+       C:echo line 4)"
+       C:echo line 5
+       x-en
+---
+name: heredoc-comsub-6
+description:
+       Check here documents and here strings can be used
+       without a specific command, like $(<…) (extension)
+stdin:
+       foo=bar
+       x=$(<<<EO${foo}F)
+       echo "3<$x>"
+               y=$(<<-EOF
+                       hi!
+       
+                       $foo) is not a problem
+       
+       
+               EOF)
+       echo "7<$y>"
+expected-stdout:
+       3<EObarF>
+       7<hi!
+       
+       bar) is not a problem>
+---
 name: heredoc-subshell-1
 description:
        Tests for here documents in subshells, taken from Austin ML
@@ -3119,6 +3343,37 @@ expected-stdout:
 expected-stderr-pattern:
        /(.*can't unlink HISTFILE.*\n)?X*$/
 ---
+name: history-multiline
+description:
+       Check correct multiline history, Debian #783978
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!
+file-setup: file 644 "Env"
+       PS1=X
+       PS2=Y
+stdin:
+       for i in A B C
+       do
+          print $i
+          print $i
+       done
+       fc -l
+expected-stdout:
+       A
+       A
+       B
+       B
+       C
+       C
+       1       for i in A B C
+               do
+                  print $i
+                  print $i
+               done
+expected-stderr-pattern:
+       /^XYYYYXX$/
+---
 name: history-e-minus-1
 description:
        Check if more recent command is executed
@@ -6162,6 +6417,106 @@ expected-stdout:
        ac_space=' '
        ac_newline=$'\n'
 ---
+name: regression-67
+description:
+       Check that we can both break and use source on the same line
+stdin:
+       for s in s; do break; done; print -s s
+---
+name: regression-68
+description:
+       Check that all common arithmetic operators work as expected
+stdin:
+       echo 1 $(( a = 5 )) .
+       echo 2 $(( ++a )) , $(( a++ )) , $(( a )) .
+       echo 3 $(( --a )) , $(( a-- )) , $(( a )) .
+       echo 4 $(( a == 5 )) , $(( a == 6 )) .
+       echo 5 $(( a != 5 )) , $(( a != 6 )) .
+       echo 6 $(( a *= 3 )) .
+       echo 7 $(( a /= 5 )) .
+       echo 8 $(( a %= 2 )) .
+       echo 9 $(( a += 9 )) .
+       echo 10 $(( a -= 4 )) .
+       echo 11 $(( a <<= 1 )) .
+       echo 12 $(( a >>= 1 )) .
+       echo 13 $(( a &= 4 )) .
+       echo 14 $(( a ^= a )) .
+       echo 15 $(( a |= 5 )) .
+       echo 16 $(( 5 << 1 )) .
+       echo 17 $(( 5 >> 1 )) .
+       echo 18 $(( 5 <= 6 )) , $(( 5 <= 5 )) , $(( 5 <= 4 )) .
+       echo 19 $(( 5 >= 6 )) , $(( 5 >= 5 )) , $(( 5 >= 4 )) .
+       echo 20 $(( 5 < 6 )) , $(( 5 < 5 )) , $(( 5 < 4 )) .
+       echo 21 $(( 5 > 6 )) , $(( 5 > 5 )) , $(( 5 > 4 )) .
+       echo 22 $(( 0 && 0 )) , $(( 0 && 1 )) , $(( 1 && 0 )) , $(( 1 && 1 )) .
+       echo 23 $(( 0 || 0 )) , $(( 0 || 1 )) , $(( 1 || 0 )) , $(( 1 || 1 )) .
+       echo 24 $(( 5 * 3 )) .
+       echo 25 $(( 7 / 2 )) .
+       echo 26 $(( 5 % 5 )) , $(( 5 % 4 )) , $(( 5 % 1 )) , $(( 5 % -1 )) , $(( 5 % -2 )) .
+       echo 27 $(( 5 + 2 )) , $(( 5 + 0 )) , $(( 5 + -2 )) .
+       echo 28 $(( 5 - 2 )) , $(( 5 - 0 )) , $(( 5 - -2 )) .
+       echo 29 $(( 6 & 4 )) , $(( 6 & 8 )) .
+       echo 30 $(( 4 ^ 2 )) , $(( 4 ^ 4 )) .
+       echo 31 $(( 4 | 2 )) , $(( 4 | 4 )) , $(( 4 | 0 )) .
+       echo 32 $(( 0 ? 1 : 2 )) , $(( 3 ? 4 : 5 )) .
+       echo 33 $(( 5 , 2 , 3 )) .
+       echo 34 $(( ~0 )) , $(( ~1 )) , $(( ~~1 )) , $(( ~~2 )) .
+       echo 35 $(( !0 )) , $(( !1 )) , $(( !!1 )) , $(( !!2 )) .
+       echo 36 $(( (5) )) .
+expected-stdout:
+       1 5 .
+       2 6 , 6 , 7 .
+       3 6 , 6 , 5 .
+       4 1 , 0 .
+       5 0 , 1 .
+       6 15 .
+       7 3 .
+       8 1 .
+       9 10 .
+       10 6 .
+       11 12 .
+       12 6 .
+       13 4 .
+       14 0 .
+       15 5 .
+       16 10 .
+       17 2 .
+       18 1 , 1 , 0 .
+       19 0 , 1 , 1 .
+       20 1 , 0 , 0 .
+       21 0 , 0 , 1 .
+       22 0 , 0 , 0 , 1 .
+       23 0 , 1 , 1 , 1 .
+       24 15 .
+       25 3 .
+       26 0 , 1 , 0 , 0 , 1 .
+       27 7 , 5 , 3 .
+       28 3 , 5 , 7 .
+       29 4 , 0 .
+       30 6 , 0 .
+       31 6 , 4 , 4 .
+       32 2 , 4 .
+       33 3 .
+       34 -1 , -2 , 1 , 2 .
+       35 1 , 0 , 1 , 1 .
+       36 5 .
+---
+name: regression-69
+description:
+       Check that all non-lksh arithmetic operators work as expected
+category: shell:legacy-no
+stdin:
+       a=5 b=0x80000005
+       echo 1 $(( a ^<= 1 )) , $(( b ^<= 1 )) .
+       echo 2 $(( a ^>= 2 )) , $(( b ^>= 2 )) .
+       echo 3 $(( 5 ^< 1 )) .
+       echo 4 $(( 5 ^> 1 )) .
+expected-stdout:
+       1 10 , 11 .
+       2 -2147483646 , -1073741822 .
+       3 10 .
+       4 -2147483646 .
+---
 name: readonly-0
 description:
        Ensure readonly is honoured for assignments and unset
@@ -6578,6 +6933,37 @@ stdin:
 expected-exit: 1
 expected-stderr-pattern: !/not set/
 ---
+name: xxx-param-subst-qmark-namespec
+description:
+       Check special names are output correctly
+stdin:
+       doit() {
+               "$__progname" -c "$@" >o1 2>o2
+               rv=$?
+               echo RETVAL: $rv
+               sed -e "s\ 1^${__progname%.exe}\.*e*x*e*: \ 1PROG: \ 1" -e 's/^/STDOUT: /g' <o1
+               sed -e "s\ 1^${__progname%.exe}\.*e*x*e*: \ 1PROG: \ 1" -e 's/^/STDERR: /g' <o2
+       }
+       doit 'echo ${1x}'
+       doit 'echo "${1x}"'
+       doit 'echo ${1?}'
+       doit 'echo ${19?}'
+       doit 'echo ${!:?}'
+       doit -u 'echo ${*:?}' foo ""
+expected-stdout:
+       RETVAL: 1
+       STDERR: PROG: ${1x}: bad substitution
+       RETVAL: 1
+       STDERR: PROG: ${1x}: bad substitution
+       RETVAL: 1
+       STDERR: PROG: 1: parameter null or not set
+       RETVAL: 1
+       STDERR: PROG: 19: parameter null or not set
+       RETVAL: 1
+       STDERR: PROG: !: parameter null or not set
+       RETVAL: 1
+       STDERR: foo: ${*:?}: bad substitution
+---
 name: xxx-param-_-1
 # fails due to weirdness of execv stuff
 category: !os:uwin-nt
@@ -7690,6 +8076,27 @@ expected-stdout:
        2 off
        3 done
 ---
+name: utf8bug-1
+description:
+       Ensure trailing combining characters are not lost
+stdin:
+       set -U
+       a=a
+       b=$'\u0301'
+       x=$a$b
+       print -r -- "<e$x>"
+       x=$a
+       x+=$b
+       print -r -- "<e$x>"
+       b=$'\u0301'b
+       x=$a
+       x+=$b
+       print -r -- "<e$x>"
+expected-stdout:
+       <eá>
+       <eá>
+       <eáb>
+---
 name: aliases-1
 description:
        Check if built-in shell aliases are okay
@@ -8728,17 +9135,45 @@ name: print-funny-chars
 description:
        Check print builtin's capability to output designated characters
 stdin:
-       print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
-       print '<\x00>'
-       print '<\x01>'
-       print '<\u0000>'
-       print '<\u0001>'
+       {
+               print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
+               print '<\x00>'
+               print '<\x01>'
+               print '<\u0000>'
+               print '<\u0001>'
+       } | {
+               # integer-base-one-3Ar
+               typeset -Uui16 -Z11 pos=0
+               typeset -Uui16 -Z5 hv=2147483647
+               dasc=
+               if read -arN -1 line; then
+                       typeset -i1 line
+                       i=0
+                       while (( i < ${#line[*]} )); do
+                               hv=${line[i++]}
+                               if (( (pos & 15) == 0 )); then
+                                       (( pos )) && print "$dasc|"
+                                       print -n "${pos#16#}  "
+                                       dasc=' |'
+                               fi
+                               print -n "${hv#16#} "
+                               if (( (hv < 32) || (hv > 126) )); then
+                                       dasc=$dasc.
+                               else
+                                       dasc=$dasc${line[i-1]#1#}
+                               fi
+                               (( (pos++ & 15) == 7 )) && print -n -- '- '
+                       done
+               fi
+               while (( pos & 15 )); do
+                       print -n '   '
+                       (( (pos++ & 15) == 7 )) && print -n -- '- '
+               done
+               (( hv == 2147483647 )) || print "$dasc|"
+       }
 expected-stdout:
-       <däÛÃ\9bâ\82¬Ã\9b@>
-       <\0>
-       <\ 1>
-       <\0>
-       <\ 1>
+       00000000  3C 64 E4 DB C3 9B E2 82 - AC C3 9B 40 3E 0A 3C 00  |<d.........@>.<.|
+       00000010  3E 0A 3C 01 3E 0A 3C 00 - 3E 0A 3C 01 3E 0A        |>.<.>.<.>.<.>.|
 ---
 name: print-bksl-c
 description:
@@ -8830,6 +9265,16 @@ stdin:
 expected-stdout-pattern:
        /^4 3 2 <> <\0>$/
 ---
+name: print-array
+description:
+       Check that print -A works as expected
+stdin:
+       print -An 0x20AC 0xC3 0xBC 8#101
+       set -U
+       print -A 0x20AC 0xC3 0xBC 8#102
+expected-stdout:
+       ¬Ã¼Aâ\82¬Ã\83¼B
+---
 name: print-escapes
 description:
        Check backslash expansion by the print builtin
@@ -9016,6 +9461,17 @@ expected-exit: e != 0
 expected-stderr-pattern:
        /\.: missing argument.*\n.*source: missing argument/
 ---
+name: dot-errorlevel
+description:
+       Ensure dot resets $?
+stdin:
+       :>dotfile
+       (exit 42)
+       . ./dotfile
+       echo 1 $? .
+expected-stdout:
+       1 0 .
+---
 name: alias-function-no-conflict
 description:
        make aliases not conflict with function definitions
@@ -9828,29 +10284,6 @@ expected-stdout:
        2  = bar .
        3  = bar .
 ---
-name: mkshiop-1
-description:
-       Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
-       read -u10 foo 10<<< bar
-       echo x$foo
-expected-stdout:
-       xbar
----
-name: mkshiop-2
-description:
-       Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
-       exec 12>foo
-       print -u12 bar
-       echo baz >&12
-       cat foo
-expected-stdout:
-       bar
-       baz
----
 name: oksh-eval
 description:
        Check expansions.
@@ -10085,6 +10518,8 @@ name: fd-cloexec-1
 description:
        Verify that file descriptors > 2 are private for Korn shells
        AT&T ksh93 does this still, which means we must keep it as well
+       XXX fails on some old Perl installations
+need-pass: no
 category: shell:legacy-no
 stdin:
        cat >cld <<-EOF
@@ -10103,6 +10538,8 @@ name: fd-cloexec-2
 description:
        Verify that file descriptors > 2 are not private for POSIX shells
        See Debian Bug #154540, Closes: #499139
+       XXX fails on some old Perl installations
+need-pass: no
 stdin:
        cat >cld <<-EOF
                #!$__perlname
@@ -11792,6 +12229,17 @@ expected-stdout:
        done
 expected-stderr-pattern: /.*-x.*option/
 ---
+name: utilities-getopts-3
+description:
+       Check unsetting OPTARG
+stdin:
+       set -- -x arg -y
+       getopts x:y opt && echo "${OPTARG-unset}"
+       getopts x:y opt && echo "${OPTARG-unset}"
+expected-stdout:
+       arg
+       unset
+---
 name: wcswidth-1
 description:
        Check the new wcswidth feature
@@ -12111,6 +12559,77 @@ expected-stdout:
        after   0='swc' 1='二' 2=''
        = done
 ---
+name: command-pvV-posix-priorities
+description:
+       For POSIX compatibility, command -v should find aliases and reserved
+       words, and command -p[vV] should find aliases, reserved words, and
+       builtins over external commands.
+stdin:
+       PATH=/bin:/usr/bin
+       alias foo="bar baz"
+       bar() { :; }
+       for word in 'if' 'foo' 'bar' 'set' 'true'; do
+               command -v "$word"
+               command -pv "$word"
+               command -V "$word"
+               command -pV "$word"
+       done
+expected-stdout:
+       if
+       if
+       if is a reserved word
+       if is a reserved word
+       alias foo='bar baz'
+       alias foo='bar baz'
+       foo is an alias for 'bar baz'
+       foo is an alias for 'bar baz'
+       bar
+       bar
+       bar is a function
+       bar is a function
+       set
+       set
+       set is a special shell builtin
+       set is a special shell builtin
+       true
+       true
+       true is a shell builtin
+       true is a shell builtin
+---
+name: whence-preserve-tradition
+description:
+       This regression test is to ensure that the POSIX compatibility
+       changes for 'command' (see previous test) do not affect traditional
+       'whence' behaviour.
+category: os:mirbsd
+stdin:
+       PATH=/bin:/usr/bin
+       alias foo="bar baz"
+       bar() { :; }
+       for word in 'if' 'foo' 'bar' 'set' 'true'; do
+               whence "$word"
+               whence -p "$word"
+               whence -v "$word"
+               whence -pv "$word"
+       done
+expected-stdout:
+       if
+       if is a reserved word
+       if not found
+       'bar baz'
+       foo is an alias for 'bar baz'
+       foo not found
+       bar
+       bar is a function
+       bar not found
+       set
+       set is a special shell builtin
+       set not found
+       true
+       /bin/true
+       true is a shell builtin
+       true is a tracked alias for /bin/true
+---
 name: duffs-device
 description:
        Check that the compiler did not optimise-break them
@@ -12183,7 +12702,7 @@ stdin:
        Copyright (C) 2002 Free Software Foundation, Inc.'
        EOF
        chmod +x bash
-       "$__progname" -xc 'foo=$(./bash --version 2>&1 | head -1); echo "=$foo="'
+       "$__progname" -xc 'foo=$(./bash --version 2>&1 | sed q); echo "=$foo="'
 expected-stdout:
        =GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)=
 expected-stderr-pattern:
index ab13f9d..13a1f54 100644 (file)
@@ -1,8 +1,8 @@
 # $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.104 2015/12/31 21:00:12 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.108 2016/07/26 22:03:41 tg Exp $
 #-
 # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-#              2011, 2012, 2013, 2014, 2015
+#              2011, 2012, 2013, 2014, 2015, 2016
 #      mirabilos <m@mirbsd.org>
 #
 # Provided that these terms and disclaimer and all copyright notices
@@ -40,8 +40,8 @@ PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
        (( e )) && REPLY+="$e|"
        REPLY+=${USER}@${HOSTNAME%%.*}:
 
-       \typeset d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/\~}
-       \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
+       \typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
+       d=${d%/}; \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
        (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
        REPLY+=$p$d
 
@@ -250,25 +250,23 @@ function pushd {
 }
 
 # pager (not control character safe)
-function 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
-       )
-}
+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 {
@@ -367,8 +365,8 @@ function Lbafh_finish {
 
        ((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
            ((Lbafh_v << 1) & 0xFEFEFEFE) ))
-       ((# Lbafh_v = t ^ (t >>> 8) ^ (Lbafh_v >>> 8) ^ \
-           (Lbafh_v >>> 16) ^ (Lbafh_v >>> 24) ))
+       ((# Lbafh_v = t ^ (t ^> 8) ^ (Lbafh_v ^> 8) ^ \
+           (Lbafh_v ^> 16) ^ (Lbafh_v ^> 24) ))
        \:
 }
 
@@ -480,6 +478,7 @@ function enable {
        # accumulate functions from dot.mkshrc, in definition order
        i_func[nfunc++]=hd
        i_func[nfunc++]=chpwd
+       i_func[nfunc++]=cd
        i_func[nfunc++]=cd_csh
        i_func[nfunc++]=dirs
        i_func[nfunc++]=popd
index 5433e6a..d13df5e 100644 (file)
@@ -5,7 +5,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *              2011, 2012, 2013, 2014, 2015
+ *              2011, 2012, 2013, 2014, 2015, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * 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.292 2015/10/09 16:11:13 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.306 2016/08/01 18:42:40 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
@@ -39,21 +39,28 @@ __RCSID("$MirOS: src/bin/mksh/edit.c,v 1.292 2015/10/09 16:11:13 tg Exp $");
 #ifndef MKSH_CLS_STRING
 #define MKSH_CLS_STRING                "\033[;H\033[J"
 #endif
-#ifndef MKSH_CLRTOEOL_STRING
-#define MKSH_CLRTOEOL_STRING   "\033[K"
-#endif
 
 /* tty driver characters we are interested in */
-typedef struct {
-       int erase;
-       int kill;
-       int werase;
-       int intr;
-       int quit;
-       int eof;
-} X_chars;
-
-static X_chars edchars;
+#define EDCHAR_DISABLED        0xFFFFU
+#define EDCHAR_INITIAL 0xFFFEU
+static struct {
+       unsigned short erase;
+       unsigned short kill;
+       unsigned short werase;
+       unsigned short intr;
+       unsigned short quit;
+       unsigned short eof;
+} edchars;
+
+#define isched(x,e) ((unsigned short)(unsigned char)(x) == (e))
+#define isedchar(x) (!((x) & ~0xFF))
+#ifndef _POSIX_VDISABLE
+#define toedchar(x) ((unsigned short)(unsigned char)(x))
+#else
+#define toedchar(x) (((_POSIX_VDISABLE != -1) && ((x) == _POSIX_VDISABLE)) ? \
+                       ((unsigned short)EDCHAR_DISABLED) : \
+                       ((unsigned short)(unsigned char)(x)))
+#endif
 
 /* x_cf_glob() flags */
 #define XCF_COMMAND    BIT(0)  /* Do command completion */
@@ -68,6 +75,10 @@ static int xx_cols;                  /* for Emacs mode */
 static int modified;                   /* buffer has been "modified" */
 static char *holdbufp;                 /* place to hold last edit buffer */
 
+/* 0=dumb 1=tmux (for now) */
+static bool x_term_mode;
+
+static void x_adjust(void);
 static int x_getc(void);
 static void x_putcf(int);
 static void x_modified(void);
@@ -78,7 +89,7 @@ static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
 static size_t x_longest_prefix(int, char * const *);
 static void x_glob_hlp_add_qchar(char *);
 static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
-static int x_basename(const char *, const char *);
+static size_t x_basename(const char *, const char *);
 static void x_free_words(int, char **);
 static int x_escape(const char *, size_t, int (*)(const char *, size_t));
 static int x_emacs(char *);
@@ -102,7 +113,6 @@ static int x_command_glob(int, char *, char ***);
 static int x_locate_word(const char *, int, int, int *, bool *);
 
 static int x_e_getmbc(char *);
-static int x_e_rebuildline(const char *);
 
 /* +++ generic editing functions +++ */
 
@@ -149,7 +159,7 @@ x_getc(void)
                                        /* redraw line in Emacs mode */
                                        xx_cols = x_cols;
                                        x_init_prompt(false);
-                                       x_e_rebuildline(MKSH_CLRTOEOL_STRING);
+                                       x_adjust();
                                }
                        }
 #endif
@@ -226,7 +236,7 @@ static void
 x_print_expansions(int nwords, char * const *words, bool is_command)
 {
        bool use_copy = false;
-       int prefix_len;
+       size_t prefix_len;
        XPtrV l = { NULL, 0, 0 };
 
        /*
@@ -332,7 +342,7 @@ x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
                        *--cp = '/';
                } else {
                        /* ok, expand and replace */
-                       cp = shf_smprintf("%s/%s", dp, cp);
+                       cp = shf_smprintf(Tf_sSs, dp, cp);
                        if (magic_flag)
                                afree(s, ATEMP);
                        s = cp;
@@ -381,7 +391,7 @@ x_file_glob(int *flagsp, char *toglob, char ***wordsp)
        source = s;
        if (yylex(ONEWORD | LQCHAR) != LWORD) {
                source = sold;
-               internal_warningf("%s: %s", "fileglob", "bad substitution");
+               internal_warningf(Tfg_badsubst);
                return (0);
        }
        source = sold;
@@ -435,8 +445,8 @@ x_file_glob(int *flagsp, char *toglob, char ***wordsp)
 /* Data structure used in x_command_glob() */
 struct path_order_info {
        char *word;
-       int base;
-       int path_order;
+       size_t base;
+       size_t path_order;
 };
 
 /* Compare routine used in x_command_glob() */
@@ -447,8 +457,13 @@ path_order_cmp(const void *aa, const void *bb)
        const struct path_order_info *b = (const struct path_order_info *)bb;
        int t;
 
-       t = strcmp(a->word + a->base, b->word + b->base);
-       return (t ? t : a->path_order - b->path_order);
+       if ((t = strcmp(a->word + a->base, b->word + b->base)))
+               return (t);
+       if (a->path_order > b->path_order)
+               return (1);
+       if (a->path_order < b->path_order)
+               return (-1);
+       return (0);
 }
 
 static int
@@ -471,7 +486,7 @@ x_command_glob(int flags, char *toglob, char ***wordsp)
                glob_table(pat, &w, &l->funs);
 
        glob_path(flags, pat, &w, path);
-       if ((fpath = str_val(global("FPATH"))) != null)
+       if ((fpath = str_val(global(TFPATH))) != null)
                glob_path(flags, pat, &w, fpath);
 
        nwords = XPsize(w);
@@ -716,7 +731,7 @@ x_free_words(int nwords, char **words)
  *     ///             2
  *                     0
  */
-static int
+static size_t
 x_basename(const char *s, const char *se)
 {
        const char *p;
@@ -828,12 +843,11 @@ static int
 x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
 {
        size_t add = 0, wlen = len;
-       const char *ifs = str_val(local("IFS", 0));
        int rval = 0;
 
        while (wlen - add > 0)
                if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
-                   vstrchr(ifs, s[add])) {
+                   ctype(s[add], C_IFS)) {
                        if (putbuf_func(s, add) != 0) {
                                rval = -1;
                                break;
@@ -961,7 +975,6 @@ static size_t x_fword(bool);
 static void x_goto(char *);
 static char *x_bs0(char *, char *) MKSH_A_PURE;
 static void x_bs3(char **);
-static int x_size_str(char *);
 static int x_size2(char *, char **);
 static void x_zots(char *);
 static void x_zotc3(char **);
@@ -972,12 +985,11 @@ static int x_search_dir(int);
 #endif
 static int x_match(char *, char *);
 static void x_redraw(int);
-static void x_push(int);
+static void x_push(size_t);
 static char *x_mapin(const char *, Area *);
 static char *x_mapout(int);
 static void x_mapout2(int, char **);
 static void x_print(int, int);
-static void x_adjust(void);
 static void x_e_ungetc(int);
 static int x_e_getc(void);
 static void x_e_putc2(int);
@@ -987,6 +999,7 @@ static void x_e_puts(const char *);
 static int x_fold_case(int);
 #endif
 static char *x_lastcp(void);
+static void x_lastpos(void);
 static void do_complete(int, Comp_type);
 static size_t x_nb2nc(size_t) MKSH_A_PURE;
 
@@ -1122,6 +1135,7 @@ static struct x_defbindings const x_defbindings[] = {
 #endif
 #ifndef MKSH_SMALL
        /* more non-standard ones */
+       { XFUNC_eval_region,            1, CTRL('E')    },
        { XFUNC_edit_line,              2,      'e'     }
 #endif
 };
@@ -1183,6 +1197,12 @@ x_e_getmbc(char *sbuf)
        return (pos);
 }
 
+/*
+ * minimum required space to work with on a line - if the prompt
+ * leaves less space than this on a line, the prompt is truncated
+ */
+#define MIN_EDIT_SPACE 7
+
 static void
 x_init_prompt(bool doprint)
 {
@@ -1375,15 +1395,9 @@ x_ins(const char *s)
        x_lastcp();
        x_adj_ok = tobool(xcp >= xlp);
        x_zots(cp);
-       /* has x_adjust() been called? */
-       if (adj == x_adj_done) {
-               /* no */
-               cp = xlp;
-               while (cp > xcp)
-                       x_bs3(&cp);
-       }
-       if (xlp == xep - 1)
-               x_redraw(xx_cols);
+       if (adj == x_adj_done)
+               /* x_adjust() has not been called */
+               x_lastpos();
        x_adj_ok = true;
        return (0);
 }
@@ -1490,10 +1504,7 @@ x_delete(size_t nc, bool push)
        /*x_goto(xcp);*/
        x_adj_ok = true;
        xlp_valid = false;
-       cp = x_lastcp();
-       while (cp > xcp)
-               x_bs3(&cp);
-
+       x_lastpos();
        x_modified();
        return;
 }
@@ -1576,7 +1587,7 @@ static void
 x_goto(char *cp)
 {
        cp = cp >= xep ? xep : x_bs0(cp, xbuf);
-       if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
+       if (cp < xbp || cp >= utf_skipcols(xbp, x_displen, NULL)) {
                /* we are heading off screen */
                xcp = cp;
                x_adjust();
@@ -1613,15 +1624,6 @@ x_bs3(char **p)
 }
 
 static int
-x_size_str(char *cp)
-{
-       int size = 0;
-       while (*cp)
-               size += x_size2(cp, &cp);
-       return (size);
-}
-
-static int
 x_size2(char *cp, char **dcp)
 {
        uint8_t c = *(unsigned char *)cp;
@@ -1656,7 +1658,7 @@ x_zotc3(char **cp)
 
        if (c == '\t') {
                /* Kludge, tabs are always four spaces. */
-               x_e_puts("    ");
+               x_e_puts(T4spaces);
                (*cp)++;
        } else if (ISCTRL(c) && /* but not C1 */ c < 0x80) {
                x_e_putc2('^');
@@ -1770,9 +1772,11 @@ x_newline(int c MKSH_A_UNUSED)
 static int
 x_end_of_text(int c MKSH_A_UNUSED)
 {
-       char tmp = edchars.eof;
-       char *cp = &tmp;
+       unsigned char tmp;
+       char *cp = (void *)&tmp;
 
+       tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
+           (unsigned char)CTRL('D');
        x_zotc3(&cp);
        x_putc('\r');
        x_putc('\n');
@@ -1826,7 +1830,6 @@ x_goto_hist(int c MKSH_A_UNUSED)
 static void
 x_load_hist(char **hp)
 {
-       int oldsize;
        char *sp = NULL;
 
        if (hp == histptr + 1) {
@@ -1839,17 +1842,12 @@ x_load_hist(char **hp)
        if (sp == NULL)
                sp = *hp;
        x_histp = hp;
-       oldsize = x_size_str(xbuf);
        if (modified)
                strlcpy(holdbufp, xbuf, LINE);
        strlcpy(xbuf, sp, xend - xbuf);
        xbp = xbuf;
        xep = xcp = xbuf + strlen(xbuf);
-       xlp_valid = false;
-       if (xep <= x_lastcp()) {
-               x_redraw(oldsize);
-       }
-       x_goto(xep);
+       x_adjust();
        modified = 0;
 }
 
@@ -1952,7 +1950,7 @@ x_search_hist(int c)
                }
        }
        if (offset < 0)
-               x_redraw(-1);
+               x_redraw('\n');
        return (KSTD);
 }
 
@@ -2027,18 +2025,13 @@ x_match(char *str, char *pat)
 static int
 x_del_line(int c MKSH_A_UNUSED)
 {
-       int i, j;
-
        *xep = 0;
-       i = xep - xbuf;
-       j = x_size_str(xbuf);
-       xcp = xbuf;
-       x_push(i);
+       x_push(xep - (xcp = xbuf));
        xlp = xbp = xep = xbuf;
        xlp_valid = true;
        *xcp = 0;
        xmp = NULL;
-       x_redraw(j);
+       x_redraw('\r');
        x_modified();
        return (KSTD);
 }
@@ -2060,89 +2053,82 @@ x_mv_begin(int c MKSH_A_UNUSED)
 static int
 x_draw_line(int c MKSH_A_UNUSED)
 {
-       x_redraw(-1);
+       x_redraw('\n');
        return (KSTD);
 }
 
 static int
-x_e_rebuildline(const char *clrstr)
+x_cls(int c MKSH_A_UNUSED)
 {
-       shf_puts(clrstr, shl_out);
-       x_adjust();
+       shf_puts(MKSH_CLS_STRING, shl_out);
+       x_redraw(0);
        return (KSTD);
 }
 
-static int
-x_cls(int c MKSH_A_UNUSED)
+/*
+ * clear line from x_col (current cursor position) to xx_cols - 2,
+ * then output lastch, then go back to x_col; if lastch is space,
+ * clear with termcap instead of spaces, or not if line_was_cleared;
+ * lastch MUST be an ASCII character with wcwidth(lastch) == 1
+ */
+static void
+x_clrtoeol(int lastch, bool line_was_cleared)
 {
-       return (x_e_rebuildline(MKSH_CLS_STRING));
+       int col;
+
+       if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
+               shf_puts("\033[K", shl_out);
+               line_was_cleared = true;
+       }
+       if (lastch == ' ' && line_was_cleared)
+               return;
+
+       col = x_col;
+       while (col < (xx_cols - 2)) {
+               x_putc(' ');
+               ++col;
+       }
+       x_putc(lastch);
+       ++col;
+       while (col > x_col) {
+               x_putc('\b');
+               --col;
+       }
 }
 
-/*
- * Redraw (part of) the line. If limit is < 0, the everything is redrawn
- * on a NEW line, otherwise limit is the screen column up to which needs
- * redrawing.
- */
+/* output the prompt, assuming a line has just been started */
 static void
-x_redraw(int limit)
+x_pprompt(void)
 {
-       int i, j;
-       char *cp;
+       if (prompt_trunc != -1)
+               pprompt(prompt, prompt_trunc);
+       x_col = pwidth;
+}
+
+/* output CR, then redraw the line, clearing to EOL if needed (cr ≠ 0, LF) */
+static void
+x_redraw(int cr)
+{
+       int lch;
 
        x_adj_ok = false;
-       if (limit == -1)
-               x_e_putc2('\n');
-       else
-               x_e_putc2('\r');
+       /* clear the line */
+       x_e_putc2(cr ? cr : '\r');
        x_flush();
-       if (xbp == xbuf) {
-               if (prompt_trunc != -1)
-                       pprompt(prompt, prompt_trunc);
-               x_col = pwidth;
-       }
+       /* display the prompt */
+       if (xbp == xbuf)
+               x_pprompt();
        x_displen = xx_cols - 2 - x_col;
+       /* display the line content */
        xlp_valid = false;
        x_zots(xbp);
-       if (xbp != xbuf || xep > xlp)
-               limit = xx_cols;
-       if (limit >= 0) {
-               if (xep > xlp)
-                       /* we fill the line */
-                       i = 0;
-               else {
-                       char *cpl = xbp;
-
-                       i = limit;
-                       while (cpl < xlp)
-                               i -= x_size2(cpl, &cpl);
-               }
-
-               j = 0;
-               while ((j < i) || (x_col < (xx_cols - 2))) {
-                       if (!(x_col < (xx_cols - 2)))
-                               break;
-                       x_e_putc2(' ');
-                       j++;
-               }
-               i = ' ';
-               if (xep > xlp) {
-                       /* more off screen */
-                       if (xbp > xbuf)
-                               i = '*';
-                       else
-                               i = '>';
-               } else if (xbp > xbuf)
-                       i = '<';
-               x_e_putc2(i);
-               j++;
-               while (j--)
-                       x_e_putc2('\b');
-       }
-       cp = xlp;
-       while (cp > xcp)
-               x_bs3(&cp);
+       /* check whether there is more off-screen */
+       lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' ';
+       /* clear the rest of the line */
+       x_clrtoeol(lch, !cr || cr == '\n');
+       /* go back to actual cursor position */
+       x_lastpos();
        x_adj_ok = true;
-       return;
 }
 
 static int
@@ -2260,7 +2246,7 @@ x_kill(int c MKSH_A_UNUSED)
 }
 
 static void
-x_push(int nchars)
+x_push(size_t nchars)
 {
        afree(killstack[killsp], AEDIT);
        strndupx(killstack[killsp], xcp, nchars, AEDIT);
@@ -2277,7 +2263,7 @@ x_yank(int c MKSH_A_UNUSED)
        killtp--;
        if (killstack[killtp] == 0) {
                x_e_puts("\nnothing to yank");
-               x_redraw(-1);
+               x_redraw('\n');
                return (KSTD);
        }
        xmp = xcp;
@@ -2294,7 +2280,7 @@ x_meta_yank(int c MKSH_A_UNUSED)
            killstack[killtp] == 0) {
                killtp = killsp;
                x_e_puts("\nyank something first");
-               x_redraw(-1);
+               x_redraw('\n');
                return (KSTD);
        }
        len = strlen(killstack[killtp]);
@@ -2443,7 +2429,7 @@ x_print(int prefix, int key)
        shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
        if (XFUNC_VALUE(f) != XFUNC_ins_string)
 #endif
-               shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
+               shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
 #ifndef MKSH_SMALL
        else
                shprintf("'%s'\n", x_atab[prefix][key]);
@@ -2475,7 +2461,7 @@ x_bind(const char *a1, const char *a2,
        if (list) {
                for (f = 0; f < NELEM(x_ftab); f++)
                        if (!(x_ftab[f].xf_flags & XF_NOBIND))
-                               shprintf("%s\n", x_ftab[f].xf_name);
+                               shprintf(Tf_sN, x_ftab[f].xf_name);
                return (0);
        }
        if (a1 == NULL) {
@@ -2516,7 +2502,7 @@ x_bind(const char *a1, const char *a2,
                m1 = msg;
                while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
                        x_mapout2(*c++, &m1);
-               bi_errorf("%s: %s", "too long key sequence", msg);
+               bi_errorf("too long key sequence: %s", msg);
                return (1);
        }
 #ifndef MKSH_SMALL
@@ -2540,7 +2526,7 @@ x_bind(const char *a1, const char *a2,
                        if (!strcmp(x_ftab[f].xf_name, a2))
                                break;
                if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
-                       bi_errorf("%s: %s %s", a2, "no such", Tfunction);
+                       bi_errorf("%s: no such function", a2);
                        return (1);
                }
        }
@@ -2709,7 +2695,7 @@ x_expand(int c MKSH_A_UNUSED)
        i = 0;
        while (i < nwords) {
                if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
-                   (++i < nwords && x_ins(" ") < 0)) {
+                   (++i < nwords && x_ins(T1space) < 0)) {
                        x_e_putc2(7);
                        return (KSTD);
                }
@@ -2794,7 +2780,7 @@ do_complete(
         */
        if (nwords == 1 && words[0][nlen - 1] != '/' &&
            !(flags & XCF_IS_NOSPACE)) {
-               x_ins(" ");
+               x_ins(T1space);
        }
 
        x_free_words(nwords, words);
@@ -2854,7 +2840,7 @@ x_adjust(void)
 
  x_adjust_out:
        xlp_valid = false;
-       x_redraw(xx_cols);
+       x_redraw('\r');
        x_flush();
 }
 
@@ -3015,7 +3001,6 @@ x_set_arg(int c)
 static int
 x_comment(int c MKSH_A_UNUSED)
 {
-       int oldsize = x_size_str(xbuf);
        ssize_t len = xep - xbuf;
        int ret = x_do_comment(xbuf, xend - xbuf, &len);
 
@@ -3026,7 +3011,7 @@ x_comment(int c MKSH_A_UNUSED)
                xep = xbuf + len;
                *xep = '\0';
                xcp = xbp = xbuf;
-               x_redraw(oldsize);
+               x_redraw('\r');
                if (ret > 0)
                        return (x_newline('\n'));
        }
@@ -3038,15 +3023,13 @@ x_version(int c MKSH_A_UNUSED)
 {
        char *o_xbuf = xbuf, *o_xend = xend;
        char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
-       int lim = x_lastcp() - xbp;
-       size_t vlen;
        char *v;
 
        strdupx(v, KSH_VERSION, ATEMP);
 
        xbuf = xbp = xcp = v;
-       xend = xep = v + (vlen = strlen(v));
-       x_redraw(lim);
+       xend = xep = v + strlen(v);
+       x_redraw('\r');
        x_flush();
 
        c = x_e_getc();
@@ -3055,7 +3038,7 @@ x_version(int c MKSH_A_UNUSED)
        xbp = o_xbp;
        xep = o_xep;
        xcp = o_xcp;
-       x_redraw((int)vlen);
+       x_redraw('\r');
 
        if (c < 0)
                return (KSTD);
@@ -3084,7 +3067,7 @@ x_edit_line(int c MKSH_A_UNUSED)
                        x_arg = source->line - (histptr - x_histp);
        }
        if (x_arg)
-               shf_snprintf(xbuf, xend - xbuf, "%s %d",
+               shf_snprintf(xbuf, xend - xbuf, Tf_sd,
                    "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
        else
                strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
@@ -3259,22 +3242,10 @@ x_fold_case(int c)
  * NAME:
  *     x_lastcp - last visible char
  *
- * SYNOPSIS:
- *     x_lastcp()
- *
  * DESCRIPTION:
  *     This function returns a pointer to that char in the
  *     edit buffer that will be the last displayed on the
- *     screen. The sequence:
- *
- *     cp = x_lastcp();
- *     while (cp > xcp)
- *             x_bs3(&cp);
- *
- *     Will position the cursor correctly on the screen.
- *
- * RETURN VALUE:
- *     cp or NULL
+ *     screen.
  */
 static char *
 x_lastcp(void)
@@ -3296,6 +3267,16 @@ x_lastcp(void)
        return (xlp);
 }
 
+/* correctly position the cursor on the screen from end of visible area */
+static void
+x_lastpos(void)
+{
+       char *cp = x_lastcp();
+
+       while (cp > xcp)
+               x_bs3(&cp);
+}
+
 static void
 x_mode(bool onoff)
 {
@@ -3308,13 +3289,13 @@ x_mode(bool onoff)
        if (onoff) {
                x_mkraw(tty_fd, NULL, false);
 
-               edchars.erase = tty_state.c_cc[VERASE];
-               edchars.kill = tty_state.c_cc[VKILL];
-               edchars.intr = tty_state.c_cc[VINTR];
-               edchars.quit = tty_state.c_cc[VQUIT];
-               edchars.eof = tty_state.c_cc[VEOF];
+               edchars.erase = toedchar(tty_state.c_cc[VERASE]);
+               edchars.kill = toedchar(tty_state.c_cc[VKILL]);
+               edchars.intr = toedchar(tty_state.c_cc[VINTR]);
+               edchars.quit = toedchar(tty_state.c_cc[VQUIT]);
+               edchars.eof = toedchar(tty_state.c_cc[VEOF]);
 #ifdef VWERASE
-               edchars.werase = tty_state.c_cc[VWERASE];
+               edchars.werase = toedchar(tty_state.c_cc[VWERASE]);
 #else
                edchars.werase = 0;
 #endif
@@ -3332,33 +3313,17 @@ x_mode(bool onoff)
                if (!edchars.werase)
                        edchars.werase = CTRL('W');
 
-#ifdef _POSIX_VDISABLE
-               /* Convert unset values to internal 'unset' value */
-               if (edchars.erase == _POSIX_VDISABLE)
-                       edchars.erase = -1;
-               if (edchars.kill == _POSIX_VDISABLE)
-                       edchars.kill = -1;
-               if (edchars.intr == _POSIX_VDISABLE)
-                       edchars.intr = -1;
-               if (edchars.quit == _POSIX_VDISABLE)
-                       edchars.quit = -1;
-               if (edchars.eof == _POSIX_VDISABLE)
-                       edchars.eof = -1;
-               if (edchars.werase == _POSIX_VDISABLE)
-                       edchars.werase = -1;
-#endif
-
-               if (edchars.erase >= 0) {
+               if (isedchar(edchars.erase)) {
                        bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
                        bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
                }
-               if (edchars.kill >= 0)
+               if (isedchar(edchars.kill))
                        bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
-               if (edchars.werase >= 0)
+               if (isedchar(edchars.werase))
                        bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
-               if (edchars.intr >= 0)
+               if (isedchar(edchars.intr))
                        bind_if_not_bound(0, edchars.intr, XFUNC_abort);
-               if (edchars.quit >= 0)
+               if (isedchar(edchars.quit))
                        bind_if_not_bound(0, edchars.quit, XFUNC_noop);
        } else
                mksh_tcset(tty_fd, &tty_state);
@@ -3597,16 +3562,19 @@ x_vi(char *buf)
                if (c == -1)
                        break;
                if (state != VLIT) {
-                       if (c == edchars.intr || c == edchars.quit) {
+                       if (isched(c, edchars.intr) ||
+                           isched(c, edchars.quit)) {
                                /* pretend we got an interrupt */
                                x_vi_zotc(c);
                                x_flush();
-                               trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
+                               trapsig(isched(c, edchars.intr) ?
+                                   SIGINT : SIGQUIT);
                                x_mode(false);
                                unwind(LSHELL);
-                       } else if (c == edchars.eof && state != VVERSION) {
+                       } else if (isched(c, edchars.eof) &&
+                           state != VVERSION) {
                                if (es->linelen == 0) {
-                                       x_vi_zotc(edchars.eof);
+                                       x_vi_zotc(c);
                                        c = -1;
                                        break;
                                }
@@ -3793,7 +3761,7 @@ vi_hook(int ch)
                                memcpy(srchpat, locpat, srchlen + 1);
                        }
                        state = VCMD;
-               } else if (ch == edchars.erase || ch == CTRL('h')) {
+               } else if (isched(ch, edchars.erase) || ch == CTRL('h')) {
                        if (srchlen != 0) {
                                srchlen--;
                                es->linelen -= char_len(locpat[srchlen]);
@@ -3804,13 +3772,13 @@ vi_hook(int ch)
                        restore_cbuf();
                        state = VNORMAL;
                        refresh(0);
-               } else if (ch == edchars.kill) {
+               } else if (isched(ch, edchars.kill)) {
                        srchlen = 0;
                        es->linelen = 1;
                        es->cursor = 1;
                        refresh(0);
                        return (0);
-               } else if (ch == edchars.werase) {
+               } else if (isched(ch, edchars.werase)) {
                        unsigned int i, n;
                        struct edstate new_es, *save_es;
 
@@ -3964,7 +3932,7 @@ vi_insert(int ch)
 {
        int tcursor;
 
-       if (ch == edchars.erase || ch == CTRL('h')) {
+       if (isched(ch, edchars.erase) || ch == CTRL('h')) {
                if (insert == REPLACE) {
                        if (es->cursor == undo->cursor) {
                                vi_error();
@@ -3990,7 +3958,7 @@ vi_insert(int ch)
                expanded = NONE;
                return (0);
        }
-       if (ch == edchars.kill) {
+       if (isched(ch, edchars.kill)) {
                if (es->cursor != 0) {
                        inslen = 0;
                        memmove(es->cbuf, &es->cbuf[es->cursor],
@@ -4001,7 +3969,7 @@ vi_insert(int ch)
                expanded = NONE;
                return (0);
        }
-       if (ch == edchars.werase) {
+       if (isched(ch, edchars.werase)) {
                if (es->cursor != 0) {
                        tcursor = backword(1);
                        memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
@@ -4372,7 +4340,7 @@ vi_cmd(int argcnt, const char *cmd)
                                            (hlast - hnum);
                        }
                        if (argcnt)
-                               shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
+                               shf_snprintf(es->cbuf, es->cbufsize, Tf_sd,
                                    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
                                    argcnt);
                        else
@@ -4501,7 +4469,7 @@ vi_cmd(int argcnt, const char *cmd)
                                        argcnt++;
                                        p++;
                                }
-                               if (putbuf(" ", 1, false) != 0 ||
+                               if (putbuf(T1space, 1, false) != 0 ||
                                    putbuf(sp, argcnt, false) != 0) {
                                        if (es->cursor != 0)
                                                es->cursor--;
@@ -4597,8 +4565,8 @@ vi_cmd(int argcnt, const char *cmd)
 static int
 domove(int argcnt, const char *cmd, int sub)
 {
-       int bcount, i = 0, t;
-       int ncursor = 0;
+       int ncursor = 0, i = 0, t;
+       unsigned int bcount;
 
        switch (*cmd) {
        case 'b':
@@ -4918,16 +4886,16 @@ forwword(int argcnt)
        ncursor = es->cursor;
        while (ncursor < es->linelen && argcnt--) {
                if (ksh_isalnux(es->cbuf[ncursor]))
-                       while (ksh_isalnux(es->cbuf[ncursor]) &&
-                           ncursor < es->linelen)
+                       while (ncursor < es->linelen &&
+                           ksh_isalnux(es->cbuf[ncursor]))
                                ncursor++;
                else if (!ksh_isspace(es->cbuf[ncursor]))
-                       while (!ksh_isalnux(es->cbuf[ncursor]) &&
-                           !ksh_isspace(es->cbuf[ncursor]) &&
-                           ncursor < es->linelen)
+                       while (ncursor < es->linelen &&
+                           !ksh_isalnux(es->cbuf[ncursor]) &&
+                           !ksh_isspace(es->cbuf[ncursor]))
                                ncursor++;
-               while (ksh_isspace(es->cbuf[ncursor]) &&
-                   ncursor < es->linelen)
+               while (ncursor < es->linelen &&
+                   ksh_isspace(es->cbuf[ncursor]))
                        ncursor++;
        }
        return (ncursor);
@@ -4991,11 +4959,11 @@ Forwword(int argcnt)
 
        ncursor = es->cursor;
        while (ncursor < es->linelen && argcnt--) {
-               while (!ksh_isspace(es->cbuf[ncursor]) &&
-                   ncursor < es->linelen)
+               while (ncursor < es->linelen &&
+                   !ksh_isspace(es->cbuf[ncursor]))
                        ncursor++;
-               while (ksh_isspace(es->cbuf[ncursor]) &&
-                   ncursor < es->linelen)
+               while (ncursor < es->linelen &&
+                   ksh_isspace(es->cbuf[ncursor]))
                        ncursor++;
        }
        return (ncursor);
@@ -5051,7 +5019,7 @@ grabhist(int save, int n)
        }
        (void)histnum(n);
        if ((hptr = *histpos()) == NULL) {
-               internal_warningf("%s: %s", "grabhist", "bad history array");
+               internal_warningf("grabhist: bad history array");
                return (-1);
        }
        if (save)
@@ -5106,9 +5074,7 @@ redraw_line(bool newl)
                x_putc('\r');
                x_putc('\n');
        }
-       if (prompt_trunc != -1)
-               pprompt(prompt, prompt_trunc);
-       x_col = pwidth;
+       x_pprompt();
        morec = ' ';
 }
 
@@ -5263,9 +5229,7 @@ ed_mov_opt(int col, char *wb)
        if (col < x_col) {
                if (col + 1 < x_col - col) {
                        x_putc('\r');
-                       if (prompt_trunc != -1)
-                               pprompt(prompt, prompt_trunc);
-                       x_col = pwidth;
+                       x_pprompt();
                        while (x_col++ < col)
                                x_putcf(*wb++);
                } else {
@@ -5319,7 +5283,7 @@ expand_word(int cmd)
                        rval = -1;
                        break;
                }
-               if (++i < nwords && putbuf(" ", 1, false) != 0) {
+               if (++i < nwords && putbuf(T1space, 1, false) != 0) {
                        rval = -1;
                        break;
                }
@@ -5437,7 +5401,7 @@ complete_word(int cmd, int count)
                 */
                if (match_len > 0 && match[match_len - 1] != '/' &&
                    !(flags & XCF_IS_NOSPACE))
-                       rval = putbuf(" ", 1, false);
+                       rval = putbuf(T1space, 1, false);
        }
        x_free_words(nwords, words);
 
@@ -5507,12 +5471,11 @@ x_init(void)
        int i, j;
 
        /*
-        * Set edchars to -2 to force initial binding, except
-        * we need default values for some deficient systems…
+        * set edchars to force initial binding, except we need
+        * default values for ^W for some deficient systems…
         */
        edchars.erase = edchars.kill = edchars.intr = edchars.quit =
-           edchars.eof = -2;
-       /* ^W */
+           edchars.eof = EDCHAR_INITIAL;
        edchars.werase = 027;
 
        /* command line editing specific memory allocation */
@@ -5548,4 +5511,89 @@ x_done(void)
                afreeall(AEDIT);
 }
 #endif
+
+void
+x_initterm(const char *termtype)
+{
+       /* default must be 0 (bss) */
+       x_term_mode = 0;
+       /* this is what tmux uses, don't ask me about it */
+       if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7))
+               x_term_mode = 1;
+}
+
+#ifndef MKSH_SMALL
+static char *
+x_eval_region_helper(const char *cmd, size_t len)
+{
+       char * volatile cp;
+       newenv(E_ERRH);
+
+       if (!kshsetjmp(e->jbuf)) {
+               char *wds = alloc(len + 3, ATEMP);
+
+               wds[0] = FUNSUB;
+               memcpy(wds + 1, cmd, len);
+               wds[len + 1] = '\0';
+               wds[len + 2] = EOS;
+
+               cp = evalstr(wds, DOSCALAR);
+               strdupx(cp, cp, AEDIT);
+       } else
+               cp = NULL;
+       quitenv(NULL);
+       return (cp);
+}
+
+static int
+x_eval_region(int c MKSH_A_UNUSED)
+{
+       char *evbeg, *evend, *cp;
+       size_t newlen;
+       /* only for LINE overflow checking */
+       size_t restlen;
+
+       if (xmp == NULL) {
+               evbeg = xbuf;
+               evend = xep;
+       } else if (xmp < xcp) {
+               evbeg = xmp;
+               evend = xcp;
+       } else {
+               evbeg = xcp;
+               evend = xmp;
+       }
+
+       x_e_putc2('\r');
+       x_clrtoeol(' ', false);
+       x_flush();
+       x_mode(false);
+       cp = x_eval_region_helper(evbeg, evend - evbeg);
+       x_mode(true);
+
+       if (cp == NULL) {
+               /* command cannot be parsed */
+ x_eval_region_err:
+               x_e_putc2(7);
+               x_redraw('\r');
+               return (KSTD);
+       }
+
+       newlen = strlen(cp);
+       restlen = xep - evend;
+       /* check for LINE overflow, until this is dynamically allocated */
+       if (evbeg + newlen + restlen >= xend)
+               goto x_eval_region_err;
+
+       xmp = evbeg;
+       xcp = evbeg + newlen;
+       xep = xcp + restlen;
+       memmove(xcp, evend, restlen + /* NUL */ 1);
+       memcpy(xmp, cp, newlen);
+       afree(cp, AEDIT);
+       x_adjust();
+       x_modified();
+       return (KSTD);
+}
+#endif /* !MKSH_SMALL */
 #endif /* !MKSH_NO_CMDLINE_EDITING */
index ba14d09..008db94 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009, 2010, 2015
+ * Copyright (c) 2009, 2010, 2015, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -19,7 +19,7 @@
  */
 
 #if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.7 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.9 2016/07/26 21:50:30 tg Exp $");
 #define FN(cname,sname,flags)  static int x_##cname(int);
 #elif defined(EMACSFN_ENUMS)
 #define FN(cname,sname,flags)  XFUNC_##cname,
@@ -54,6 +54,9 @@ FN(end_of_text, "eot", 0)
 FN(enumerate, "list", 0)
 FN(eot_del, "eot-or-delete", XF_ARG)
 FN(error, "error", 0)
+#ifndef MKSH_SMALL
+FN(eval_region, "evaluate-region", 0)
+#endif
 FN(expand, "expand-file", 0)
 #ifndef MKSH_SMALL
 FN(fold_capitalise, "capitalize-word", XF_ARG)
index 3e670da..ab1cc88 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.180 2016/01/19 23:12:12 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.192 2016/08/01 21:38:01 tg Exp $");
 
 /*
  * string expansion
@@ -120,7 +120,7 @@ substitute(const char *cp, int f)
        s->start = s->str = cp;
        source = s;
        if (yylex(ONEWORD) != LWORD)
-               internal_errorf("bad substitution");
+               internal_errorf(Tbadsubst);
        source = sold;
        afree(s, ATEMP);
        return (evalstr(yylval.cp, f));
@@ -186,7 +186,7 @@ evalonestr(const char *cp, int f)
                rv = (char *) *XPptrv(w);
                break;
        default:
-               rv = evalstr(cp, f&~DOGLOB);
+               rv = evalstr(cp, f & ~DOGLOB);
                break;
        }
        XPfree(w);
@@ -375,14 +375,15 @@ expand(
  unwind_substsyn:
                                        /* restore sp */
                                        sp = varname - 2;
-                                       end = (beg = wdcopy(sp, ATEMP)) +
-                                           (wdscan(sp, CSUBST) - sp);
+                                       beg = wdcopy(sp, ATEMP);
+                                       end = (wdscan(cstrchr(sp, '\0') + 1,
+                                           CSUBST) - sp) + beg;
                                        /* ({) the } or x is already skipped */
                                        if (end < wdscan(beg, EOS))
                                                *end = EOS;
-                                       str = snptreef(NULL, 64, "%S", beg);
+                                       str = snptreef(NULL, 64, Tf_S, beg);
                                        afree(beg, ATEMP);
-                                       errorf("%s: %s", str, "bad substitution");
+                                       errorf(Tf_sD_s, str, Tbadsubst);
                                }
                                if (f & DOBLANK)
                                        doblank++;
@@ -403,8 +404,8 @@ expand(
                                        st->stype = stype;
                                        st->base = Xsavepos(ds, dp);
                                        st->f = f;
-                                       if (x.var == &vtemp) {
-                                               st->var = tempvar();
+                                       if (x.var == vtemp) {
+                                               st->var = tempvar(vtemp->name);
                                                st->var->flag &= ~INTEGER;
                                                /* can't fail here */
                                                setstr(st->var,
@@ -477,12 +478,14 @@ expand(
                                                strndupx(x.str, beg, num, ATEMP);
                                                goto do_CSUBST;
                                            }
+                                       case 0x100 | '/':
                                        case '/': {
                                                char *s, *p, *d, *sbeg, *end;
-                                               char *pat, *rrep;
+                                               char *pat = NULL, *rrep = null;
                                                char fpat = 0, *tpat1, *tpat2;
+                                               char *ws, *wpat, *wrep;
 
-                                               s = wdcopy(sp, ATEMP);
+                                               s = ws = wdcopy(sp, ATEMP);
                                                p = s + (wdscan(sp, ADELIM) - sp);
                                                d = s + (wdscan(sp, CSUBST) - sp);
                                                p[-2] = EOS;
@@ -491,16 +494,24 @@ expand(
                                                else
                                                        d[-2] = EOS;
                                                sp += (d ? d : p) - s - 1;
-                                               if (!(stype & 0x80) &&
+                                               if (!(stype & 0x180) &&
                                                    s[0] == CHAR &&
                                                    (s[1] == '#' || s[1] == '%'))
                                                        fpat = s[1];
-                                               pat = evalstr(s + (fpat ? 2 : 0),
-                                                   DOTILDE | DOSCALAR | DOPAT);
-                                               rrep = d ? evalstr(p,
-                                                   DOTILDE | DOSCALAR) : null;
-                                               afree(s, ATEMP);
+                                               wpat = s + (fpat ? 2 : 0);
+                                               wrep = d ? p : NULL;
+                                               if (!(stype & 0x100)) {
+                                                       rrep = wrep ? evalstr(wrep,
+                                                           DOTILDE | DOSCALAR) :
+                                                           null;
+                                               }
 
+                                               /* prepare string on which to work */
+                                               strdupx(s, str_val(st->var), ATEMP);
+                                               sbeg = s;
+ again_search:
+                                               pat = evalstr(wpat,
+                                                   DOTILDE | DOSCALAR | DOPAT);
                                                /* check for special cases */
                                                if (!*pat && !fpat) {
                                                        /*
@@ -509,19 +520,15 @@ expand(
                                                         */
                                                        goto no_repl;
                                                }
-                                               if ((stype & 0x80) &&
+                                               if ((stype & 0x180) &&
                                                    gmatchx(null, pat, false)) {
                                                        /*
                                                         * pattern matches empty
                                                         * string => don't loop
                                                         */
-                                                       stype &= ~0x80;
+                                                       stype &= ~0x180;
                                                }
 
-                                               /* prepare string on which to work */
-                                               strdupx(s, str_val(st->var), ATEMP);
-                                               sbeg = s;
-
                                                /* first see if we have any match at all */
                                                if (fpat == '#') {
                                                        /* anchor at the beginning */
@@ -566,13 +573,27 @@ expand(
                                                                        break;
                                                                p--;
                                                        }
+                                               strndupx(end, sbeg, p - sbeg, ATEMP);
+                                               record_match(end);
+                                               afree(end, ATEMP);
+                                               if (stype & 0x100) {
+                                                       if (rrep != null)
+                                                               afree(rrep, ATEMP);
+                                                       rrep = wrep ? evalstr(wrep,
+                                                           DOTILDE | DOSCALAR) :
+                                                           null;
+                                               }
                                                strndupx(end, s, sbeg - s, ATEMP);
-                                               d = shf_smprintf("%s%s%s", end, rrep, p);
+                                               d = shf_smprintf(Tf_sss, end, rrep, p);
                                                afree(end, ATEMP);
                                                sbeg = d + (sbeg - s) + strlen(rrep);
                                                afree(s, ATEMP);
                                                s = d;
-                                               if (stype & 0x80)
+                                               if (stype & 0x100) {
+                                                       afree(tpat1, ATEMP);
+                                                       afree(pat, ATEMP);
+                                                       goto again_search;
+                                               } else if (stype & 0x80)
                                                        goto again_repl;
  end_repl:
                                                afree(tpat1, ATEMP);
@@ -581,6 +602,7 @@ expand(
                                                afree(pat, ATEMP);
                                                if (rrep != null)
                                                        afree(rrep, ATEMP);
+                                               afree(ws, ATEMP);
                                                goto do_CSUBST;
                                            }
                                        case '#':
@@ -728,10 +750,11 @@ expand(
                                case '?':
                                        dp = Xrestpos(ds, dp, st->base);
 
-                                       errorf("%s: %s", st->var->name,
+                                       errorf(Tf_sD_s, st->var->name,
                                            debunk(dp, dp, strlen(dp) + 1));
                                        break;
                                case '0':
+                               case 0x100 | '/':
                                case '/':
                                case 0x100 | '#':
                                case 0x100 | 'Q':
@@ -1060,7 +1083,7 @@ varsub(Expand *xp, const char *sp, const char *word,
        int c;
        int state;      /* next state: XBASE, XARG, XSUB, XNULLSUB */
        int stype;      /* substitution type */
-       int slen;
+       int slen = 0;
        const char *p;
        struct tbl *vp;
        bool zero_ok = false;
@@ -1133,16 +1156,30 @@ varsub(Expand *xp, const char *sp, const char *word,
                        }
                }
                if (Flag(FNOUNSET) && c == 0 && !zero_ok)
-                       errorf("%s: %s", sp, "parameter not set");
+                       errorf(Tf_parm, sp);
                /* unqualified variable/string substitution */
                *stypep = 0;
-               xp->str = shf_smprintf("%d", c);
+               xp->str = shf_smprintf(Tf_d, c);
+               return (XSUB);
+       }
+       if (stype == '!' && c != '\0' && *word == CSUBST) {
+               sp++;
+               if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
+                   p[2] == ']') {
+                       c = '!';
+                       stype = 0;
+                       goto arraynames;
+               }
+               xp->var = global(sp);
+               xp->str = p ? shf_smprintf("%s[%lu]",
+                   xp->var->name, arrayindex(xp->var)) : xp->var->name;
+               *stypep = 0;
                return (XSUB);
        }
 
        /* Check for qualifiers in word part */
        stype = 0;
-       c = word[slen = 0] == CHAR ? word[1] : 0;
+       c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
        if (c == ':') {
                slen += 2;
                stype = 0x80;
@@ -1151,7 +1188,7 @@ varsub(Expand *xp, const char *sp, const char *word,
        if (!stype && c == '/') {
                slen += 2;
                stype = c;
-               if (word[slen] == ADELIM) {
+               if (word[slen] == ADELIM && word[slen + 1] == c) {
                        slen += 2;
                        stype |= 0x80;
                }
@@ -1181,8 +1218,6 @@ varsub(Expand *xp, const char *sp, const char *word,
                return (-1);
        if (!stype && *word != CSUBST)
                return (-1);
-       *stypep = stype;
-       *slenp = slen;
 
        c = sp[0];
        if (c == '*' || c == '@') {
@@ -1192,7 +1227,9 @@ varsub(Expand *xp, const char *sp, const char *word,
                /* can't trim a vector (yet) */
                case '%':
                case '#':
+               case '?':
                case '0':
+               case 0x100 | '/':
                case '/':
                case 0x100 | '#':
                case 0x100 | 'Q':
@@ -1211,83 +1248,73 @@ varsub(Expand *xp, const char *sp, const char *word,
                }
                /* POSIX 2009? */
                zero_ok = true;
-       } else {
-               if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
-                   p[2] == ']') {
-                       XPtrV wv;
-
-                       switch (stype & 0x17F) {
-                       /* can't assign to a vector */
-                       case '=':
-                       /* can't trim a vector (yet) */
-                       case '%':
-                       case '#':
-                       case '?':
-                       case '0':
-                       case '/':
-                       case 0x100 | '#':
-                       case 0x100 | 'Q':
-                               return (-1);
-                       }
-                       XPinit(wv, 32);
-                       if ((c = sp[0]) == '!')
-                               ++sp;
-                       vp = global(arrayname(sp));
-                       for (; vp; vp = vp->u.array) {
-                               if (!(vp->flag&ISSET))
-                                       continue;
-                               XPput(wv, c == '!' ? shf_smprintf("%lu",
-                                   arrayindex(vp)) :
-                                   str_val(vp));
-                       }
-                       if (XPsize(wv) == 0) {
-                               xp->str = null;
-                               state = p[1] == '@' ? XNULLSUB : XSUB;
-                               XPfree(wv);
-                       } else {
-                               XPput(wv, 0);
-                               xp->u.strv = (const char **)XPptrv(wv);
-                               xp->str = *xp->u.strv++;
-                               /* ${foo[@]} */
-                               xp->split = tobool(p[1] == '@');
-                               state = XARG;
-                       }
+       } else if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
+           p[2] == ']') {
+               XPtrV wv;
+
+               switch (stype & 0x17F) {
+               /* can't assign to a vector */
+               case '=':
+               /* can't trim a vector (yet) */
+               case '%':
+               case '#':
+               case '?':
+               case '0':
+               case 0x100 | '/':
+               case '/':
+               case 0x100 | '#':
+               case 0x100 | 'Q':
+                       return (-1);
+               }
+               c = 0;
+ arraynames:
+               XPinit(wv, 32);
+               vp = global(arrayname(sp));
+               for (; vp; vp = vp->u.array) {
+                       if (!(vp->flag&ISSET))
+                               continue;
+                       XPput(wv, c == '!' ? shf_smprintf(Tf_lu,
+                           arrayindex(vp)) :
+                           str_val(vp));
+               }
+               if (XPsize(wv) == 0) {
+                       xp->str = null;
+                       state = p[1] == '@' ? XNULLSUB : XSUB;
+                       XPfree(wv);
                } else {
-                       /* Can't assign things like $! or $1 */
-                       if ((stype & 0x17F) == '=' &&
-                           ctype(*sp, C_VAR1 | C_DIGIT))
-                               return (-1);
-                       if (*sp == '!' && sp[1]) {
-                               ++sp;
-                               xp->var = global(sp);
-                               if (vstrchr(sp, '['))
-                                       xp->str = shf_smprintf("%s[%lu]",
-                                           xp->var->name,
-                                           arrayindex(xp->var));
-                               else
-                                       xp->str = xp->var->name;
-                       } else {
-                               xp->var = global(sp);
-                               xp->str = str_val(xp->var);
-                       }
-                       state = XSUB;
+                       XPput(wv, 0);
+                       xp->u.strv = (const char **)XPptrv(wv);
+                       xp->str = *xp->u.strv++;
+                       /* ${foo[@]} */
+                       xp->split = tobool(p[1] == '@');
+                       state = XARG;
                }
+       } else {
+               xp->var = global(sp);
+               xp->str = str_val(xp->var);
+               /* can't assign things like $! or $1 */
+               if ((stype & 0x17F) == '=' && !*xp->str &&
+                   ctype(*sp, C_VAR1 | C_DIGIT))
+                       return (-1);
+               state = XSUB;
        }
 
        c = stype & 0x7F;
        /* test the compiler's code generator */
-       if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
+       if (((stype < 0x100) && (ctype(c, C_SUBOP2) ||
            (((stype & 0x80) ? *xp->str == '\0' : xp->str == null) &&
            (state != XARG || (ifs0 || xp->split ?
            (xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
            c == '=' || c == '-' || c == '?' : c == '+'))) ||
            stype == (0x80 | '0') || stype == (0x100 | '#') ||
-           stype == (0x100 | 'Q'))
+           stype == (0x100 | 'Q') || (stype & 0x7F) == '/')
                /* expand word instead of variable value */
                state = XBASE;
        if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
            (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
-               errorf("%s: %s", sp, "parameter not set");
+               errorf(Tf_parm, sp);
+       *stypep = stype;
+       *slenp = slen;
        return (state);
 }
 
@@ -1323,14 +1350,31 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
                struct ioword *io = *t->ioact;
                char *name;
 
-               if ((io->ioflag & IOTYPE) != IOREAD)
-                       errorf("%s: %s", T_funny_command,
-                           snptreef(NULL, 32, "%R", io));
-               shf = shf_open(name = evalstr(io->ioname, DOTILDE), O_RDONLY,
-                       0, SHF_MAPHI | SHF_CLEXEC);
-               if (shf == NULL)
-                       warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
-                           "can't open", "$(<...) input", cstrerror(errno));
+               switch (io->ioflag & IOTYPE) {
+               case IOREAD:
+                       shf = shf_open(name = evalstr(io->ioname, DOTILDE),
+                               O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
+                       if (shf == NULL)
+                               warningf(!Flag(FTALKING), Tf_sD_s_sD_s,
+                                   name, Tcant_open, "$(<...) input",
+                                   cstrerror(errno));
+                       break;
+               case IOHERE:
+                       if (!herein(io, &name)) {
+                               xp->str = name;
+                               /* as $(…) requires, trim trailing newlines */
+                               name += strlen(name);
+                               while (name > xp->str && name[-1] == '\n')
+                                       --name;
+                               *name = '\0';
+                               return (XSUB);
+                       }
+                       shf = NULL;
+                       break;
+               default:
+                       errorf(Tf_sD_s, T_funny_command,
+                           snptreef(NULL, 32, Tft_R, io));
+               }
        } else if (fn == FUNSUB) {
                int ofd1;
                struct temp *tf = NULL;
@@ -1341,8 +1385,8 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
                 */
                maketemp(ATEMP, TT_FUNSUB, &tf);
                if (!tf->shf) {
-                       errorf("can't %s temporary file %s: %s",
-                           "create", tf->tffn, cstrerror(errno));
+                       errorf(Tf_temp,
+                           Tcreate, tf->tffn, cstrerror(errno));
                }
                /* extract shf from temporary file, unlink and free it */
                shf = tf->shf;
@@ -1400,6 +1444,7 @@ trimsub(char *str, char *pat, int how)
                for (p = str; p <= end; p += utf_ptradj(p)) {
                        c = *p; *p = '\0';
                        if (gmatchx(str, pat, false)) {
+                               record_match(str);
                                *p = c;
                                return (p);
                        }
@@ -1411,6 +1456,7 @@ trimsub(char *str, char *pat, int how)
                for (p = end; p >= str; p--) {
                        c = *p; *p = '\0';
                        if (gmatchx(str, pat, false)) {
+                               record_match(str);
                                *p = c;
                                return (p);
                        }
@@ -1438,6 +1484,7 @@ trimsub(char *str, char *pat, int how)
                for (p = str; p <= end; p++)
                        if (gmatchx(p, pat, false)) {
  trimsub_match:
+                               record_match(p);
                                strndupx(end, str, p - str, ATEMP);
                                return (end);
                        }
@@ -1592,7 +1639,7 @@ globit(XString *xs,       /* dest string */
                /* xp = *xpp;   copy_non_glob() may have re-alloc'd xs */
                *xp = '\0';
                prefix_len = Xlength(*xs, xp);
-               dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
+               dirp = opendir(prefix_len ? Xstring(*xs, xp) : Tdot);
                if (dirp == NULL)
                        goto Nodir;
                while ((d = readdir(dirp)) != NULL) {
@@ -1694,24 +1741,40 @@ maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign)
  *
  * based on a version by Arnold Robbins
  */
-
 char *
 do_tilde(char *cp)
 {
        char *dp = null;
+#ifndef MKSH_NOPWNAM
+       bool do_simplify = true;
+#endif
 
        if (cp[0] == '\0')
                dp = str_val(global("HOME"));
        else if (cp[0] == '+' && cp[1] == '\0')
-               dp = str_val(global("PWD"));
+               dp = str_val(global(TPWD));
        else if (ksh_isdash(cp))
-               dp = str_val(global("OLDPWD"));
+               dp = str_val(global(TOLDPWD));
 #ifndef MKSH_NOPWNAM
-       else
+       else {
                dp = homedir(cp);
+               do_simplify = false;
+       }
 #endif
-       /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
-       return (dp == null ? NULL : dp);
+
+       /* if parameters aren't set, don't expand ~ */
+       if (dp == NULL || dp == null)
+               return (NULL);
+
+       /* simplify parameters as if cwd upon entry */
+#ifndef MKSH_NOPWNAM
+       if (do_simplify)
+#endif
+         {
+               strdupx(dp, dp, ATEMP);
+               simplify_path(dp);
+       }
+       return (dp);
 }
 
 #ifndef MKSH_NOPWNAM
index 9361c19..e3149cd 100644 (file)
@@ -2,7 +2,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *              2011, 2012, 2013, 2014, 2015
+ *              2011, 2012, 2013, 2014, 2015, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * 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.170 2015/12/31 21:03:47 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.179 2016/08/01 21:38:02 tg Exp $");
 
 #ifndef MKSH_DEFAULT_EXECSHELL
 #define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@@ -34,7 +34,6 @@ static int comexec(struct op *, struct tbl * volatile, const char **,
 static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
 static int call_builtin(struct tbl *, const char **, const char *, bool);
 static int iosetup(struct ioword *, struct tbl *);
-static int herein(struct ioword *, char **);
 static const char *do_selectargs(const char **, bool);
 static Test_op dbteste_isa(Test_env *, Test_meta);
 static const char *dbteste_getopnd(Test_env *, Test_op, bool);
@@ -60,7 +59,6 @@ execute(struct op * volatile t,
        const char *s, *ccp;
        struct ioword **iowp;
        struct tbl *tp = NULL;
-       char *cp;
 
        if (t == NULL)
                return (0);
@@ -79,11 +77,18 @@ execute(struct op * volatile t,
 
        /* we want to run an executable, do some variance checks */
        if (t->type == TCOM) {
+               /*
+                * Clear subst_exstat before argument expansion. Used by
+                * null commands (see comexec() and c_eval()) and by c_set().
+                */
+               subst_exstat = 0;
+
+               /* for $LINENO */
+               current_lineno = t->lineno;
+
                /* check if this is 'var=<<EOF' */
-               /*XXX this is broken, don’t use! */
-               /*XXX https://bugs.launchpad.net/mksh/+bug/1380389 */
                if (
-                   /* we have zero arguments, i.e. no programme to run */
+                   /* we have zero arguments, i.e. no program to run */
                    t->args[0] == NULL &&
                    /* we have exactly one variable assignment */
                    t->vars[0] != NULL && t->vars[1] == NULL &&
@@ -97,43 +102,21 @@ execute(struct op * volatile t,
                    /* and has no right-hand side (i.e. "varname=") */
                    ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
                    /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
-                   ccp[3] == '=' && ccp[4] == EOS)) &&
-                   /* plus we can have a here document content */
-                   herein(t->ioact[0], &cp) == 0 && cp && *cp) {
-                       char *sp = cp, *dp;
-                       size_t n = ccp - t->vars[0] + (ccp[1] == '+' ? 4 : 2);
-                       size_t z;
-
-                       /* drop redirection (will be garbage collected) */
-                       t->ioact = NULL;
-
-                       /* set variable to its expanded value */
-                       z = strlen(cp);
-                       if (notoktomul(z, 2) || notoktoadd(z * 2, n + 1))
-                               internal_errorf(Toomem, (size_t)-1);
-                       dp = alloc(z * 2 + n + 1, APERM);
-                       memcpy(dp, t->vars[0], n);
-                       t->vars[0] = dp;
-                       dp += n;
-                       while (*sp) {
-                               *dp++ = QCHAR;
-                               *dp++ = *sp++;
-                       }
-                       *dp = EOS;
+                   ccp[3] == '=' && ccp[4] == EOS))) {
+                       char *cp, *dp;
+
+                       if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
+                               cp = NULL;
+                       dp = shf_smprintf(Tf_ss, evalstr(t->vars[0],
+                           DOASNTILDE | DOSCALAR), rv ? null : cp);
+                       typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
                        /* free the expanded value */
                        afree(cp, APERM);
+                       afree(dp, ATEMP);
+                       goto Break;
                }
 
                /*
-                * Clear subst_exstat before argument expansion. Used by
-                * null commands (see comexec() and c_eval()) and by c_set().
-                */
-               subst_exstat = 0;
-
-               /* for $LINENO */
-               current_lineno = t->lineno;
-
-               /*
                 * POSIX says expand command words first, then redirections,
                 * and assignments last..
                 */
@@ -407,6 +390,7 @@ execute(struct op * volatile t,
                        for (ap = (const char **)t->vars; *ap; ap++) {
                                if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
                                    gmatchx(ccp, s, false))) {
+                                       record_match(ccp);
                                        rv = execute(t->left, flags & XERROK,
                                            xerrok);
                                        i = 0;
@@ -458,7 +442,7 @@ execute(struct op * volatile t,
                if (rv == ENOEXEC)
                        scriptexec(t, (const char **)up);
                else
-                       errorf("%s: %s", t->str, cstrerror(rv));
+                       errorf(Tf_sD_s, t->str, cstrerror(rv));
        }
  Break:
        exstat = rv & 0xFF;
@@ -549,12 +533,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                                break;
                        }
                        if ((tp = findcom(cp, FC_BI)) == NULL)
-                               errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin");
-                       if (tp->type == CSHELL && (tp->val.f == c_cat
-#ifdef MKSH_PRINTF_BUILTIN
-                           || tp->val.f == c_printf
-#endif
-                           ))
+                               errorf(Tf_sD_sD_s, Tbuiltin, cp, Tnot_found);
+                       if (tp->type == CSHELL && (tp->flag & LOW_BI))
                                break;
                        continue;
                } else if (tp->val.f == c_exec) {
@@ -594,7 +574,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                        fcflags = FC_BI|FC_PATH;
                        if (saw_p) {
                                if (Flag(FRESTRICTED)) {
-                                       warningf(true, "%s: %s",
+                                       warningf(true, Tf_sD_s,
                                            "command -p", "restricted");
                                        rv = 1;
                                        goto Leave;
@@ -612,29 +592,21 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                                subst_exstat = 0;
                                break;
                        }
-               } else if (tp->val.f == c_cat) {
+               } else if (tp->flag & LOW_BI) {
                        /* if we have any flags, do not use the builtin */
-                       if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
+                       if ((ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
                            /* argument, begins with -, is not - or -- */
-                           (ap[1][1] != '-' || ap[1][2] != '\0')) {
-                               struct tbl *ext_cat;
-
-                               ext_cat = findcom(Tcat, FC_PATH | FC_FUNC);
-                               if (ext_cat && (ext_cat->type != CTALIAS ||
-                                   (ext_cat->flag & ISSET)))
-                                       tp = ext_cat;
+                           (ap[1][1] != '-' || ap[1][2] != '\0')) ||
+                           /* always prefer the external utility */
+                           (tp->flag & LOWER_BI)) {
+                               struct tbl *ext_cmd;
+
+                               ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC);
+                               if (ext_cmd && (ext_cmd->type != CTALIAS ||
+                                   (ext_cmd->flag & ISSET)))
+                                       tp = ext_cmd;
                        }
                        break;
-#ifdef MKSH_PRINTF_BUILTIN
-               } else if (tp->val.f == c_printf) {
-                       struct tbl *ext_printf;
-
-                       ext_printf = findcom(Tprintf, FC_PATH | FC_FUNC);
-                       if (ext_printf && (ext_printf->type != CTALIAS ||
-                           (ext_printf->flag & ISSET)))
-                               tp = ext_printf;
-                       break;
-#endif
                } else if (tp->val.f == c_trap) {
                        t->u.evalflags &= ~DOTCOMEXEC;
                        break;
@@ -703,7 +675,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                goto Leave;
        } else if (!tp) {
                if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
-                       warningf(true, "%s: %s", cp, "restricted");
+                       warningf(true, Tf_sD_s, cp, "restricted");
                        rv = 1;
                        goto Leave;
                }
@@ -732,40 +704,30 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                        struct tbl *ftp;
 
                        if (!tp->u.fpath) {
+ fpath_error:
                                rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
-                               warningf(true, "%s: %s %s: %s", cp,
-                                   "can't find", "function definition file",
+                               warningf(true, Tf_sD_s_sD_s, cp,
+                                   Tcant_find, Tfile_fd,
                                    cstrerror(tp->u2.errnov));
                                break;
                        }
-                       if (include(tp->u.fpath, 0, NULL, false) < 0) {
-                               if (!strcmp(cp, Tcat)) {
- no_cat_in_FPATH:
-                                       tp = findcom(Tcat, FC_BI);
+                       errno = 0;
+                       if (include(tp->u.fpath, 0, NULL, false) < 0 ||
+                           !(ftp = findfunc(cp, hash(cp), false)) ||
+                           !(ftp->flag & ISSET)) {
+                               rv = errno;
+                               if ((ftp = findcom(cp, FC_BI)) &&
+                                   (ftp->type == CSHELL) &&
+                                   (ftp->flag & LOW_BI)) {
+                                       tp = ftp;
                                        goto do_call_builtin;
                                }
-#ifdef MKSH_PRINTF_BUILTIN
-                               if (!strcmp(cp, Tprintf)) {
- no_printf_in_FPATH:
-                                       tp = findcom(Tprintf, FC_BI);
-                                       goto do_call_builtin;
+                               if (rv) {
+                                       tp->u2.errnov = rv;
+                                       cp = tp->u.fpath;
+                                       goto fpath_error;
                                }
-#endif
-                               warningf(true, "%s: %s %s %s: %s", cp,
-                                   "can't open", "function definition file",
-                                   tp->u.fpath, cstrerror(errno));
-                               rv = 127;
-                               break;
-                       }
-                       if (!(ftp = findfunc(cp, hash(cp), false)) ||
-                           !(ftp->flag & ISSET)) {
-                               if (!strcmp(cp, Tcat))
-                                       goto no_cat_in_FPATH;
-#ifdef MKSH_PRINTF_BUILTIN
-                               if (!strcmp(cp, Tprintf))
-                                       goto no_printf_in_FPATH;
-#endif
-                               warningf(true, "%s: %s %s", cp,
+                               warningf(true, Tf_sD_s_s, cp,
                                    "function not defined by", tp->u.fpath);
                                rv = 127;
                                break;
@@ -846,7 +808,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                        /* NOTREACHED */
                default:
                        quitenv(NULL);
-                       internal_errorf("%s %d", "CFUNC", i);
+                       internal_errorf(Tf_sd, "CFUNC", i);
                }
                break;
        }
@@ -858,10 +820,10 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                if (!(tp->flag&ISSET)) {
                        if (tp->u2.errnov == ENOENT) {
                                rv = 127;
-                               warningf(true, "%s: %s", cp, "not found");
+                               warningf(true, Tf_sD_s, cp, Tnot_found);
                        } else {
                                rv = 126;
-                               warningf(true, "%s: %s: %s", cp, "can't execute",
+                               warningf(true, Tf_sD_sD_s, cp, "can't execute",
                                    cstrerror(tp->u2.errnov));
                        }
                        break;
@@ -915,7 +877,7 @@ scriptexec(struct op *tp, const char **ap)
 #endif
        union mksh_ccphack args, cap;
 
-       sh = str_val(global("EXECSHELL"));
+       sh = str_val(global(TEXECSHELL));
        if (sh && *sh)
                sh = search_path(sh, path, X_OK, NULL);
        if (!sh || !*sh)
@@ -1015,7 +977,7 @@ scriptexec(struct op *tp, const char **ap)
        execve(args.rw[0], args.rw, cap.rw);
 
        /* report both the programme that was run and the bogus interpreter */
-       errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
+       errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno));
 }
 
 int
@@ -1084,7 +1046,7 @@ define(const char *name, struct op *t)
        }
 
        if (tp->flag & ALLOC) {
-               tp->flag &= ~(ISSET|ALLOC);
+               tp->flag &= ~(ISSET|ALLOC|FKSH);
                tfree(tp->val.t, tp->areap);
        }
 
@@ -1112,23 +1074,38 @@ builtin(const char *name, int (*func) (const char **))
        uint32_t flag = DEFINED;
 
        /* see if any flags should be set for this builtin */
-       while (1) {
-               if (*name == '=')
-                       /* command does variable assignment */
-                       flag |= KEEPASN;
-               else if (*name == '*')
-                       /* POSIX special builtin */
-                       flag |= SPEC_BI;
-               else
-                       break;
-               name++;
+ flags_loop:
+       switch (*name) {
+       case '=':
+               /* command does variable assignment */
+               flag |= KEEPASN;
+               break;
+       case '*':
+               /* POSIX special builtin */
+               flag |= SPEC_BI;
+               break;
+       case '~':
+               /* external utility overrides built-in utility, always */
+               flag |= LOWER_BI;
+               /* FALLTHROUGH */
+       case '!':
+               /* external utility overrides built-in utility, with flags */
+               flag |= LOW_BI;
+               break;
+       default:
+               goto flags_seen;
        }
+       ++name;
+       goto flags_loop;
+ flags_seen:
 
+       /* enter into the builtins hash table */
        tp = ktenter(&builtins, name, hash(name));
        tp->flag = flag;
        tp->type = CSHELL;
        tp->val.f = func;
 
+       /* return name, for direct builtin call check in main.c */
        return (name);
 }
 
@@ -1164,7 +1141,7 @@ findcom(const char *name, int flags)
        if (!tp && (flags & FC_FUNC)) {
                tp = findfunc(name, h, false);
                if (tp && !(tp->flag & ISSET)) {
-                       if ((fpath = str_val(global("FPATH"))) == null) {
+                       if ((fpath = str_val(global(TFPATH))) == null) {
                                tp->u.fpath = NULL;
                                tp->u2.errnov = ENOENT;
                        } else
@@ -1209,7 +1186,7 @@ findcom(const char *name, int flags)
                                afree(npath.rw, ATEMP);
                        tp->flag |= ISSET|ALLOC;
                } else if ((flags & FC_FUNC) &&
-                   (fpath = str_val(global("FPATH"))) != null &&
+                   (fpath = str_val(global(TFPATH))) != null &&
                    (npath.ro = search_path(name, fpath, R_OK,
                    &tp->u2.errnov)) != NULL) {
                        /*
@@ -1338,7 +1315,7 @@ call_builtin(struct tbl *tp, const char **wp, const char *where, bool resetspec)
        int rv;
 
        if (!tp)
-               internal_errorf("%s: %s", where, wp[0]);
+               internal_errorf(Tf_sD_s, where, wp[0]);
        builtin_argv0 = wp[0];
        builtin_spec = tobool(!resetspec &&
            /*XXX odd use of KEEPASN */
@@ -1378,7 +1355,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
 
        if (Flag(FXTRACE)) {
                change_xtrace(2, false);
-               fptreef(shl_xtrace, 0, "%R", &iotmp);
+               fptreef(shl_xtrace, 0, Tft_R, &iotmp);
                change_xtrace(1, false);
        }
 
@@ -1439,8 +1416,8 @@ iosetup(struct ioword *iop, struct tbl *tp)
                    &emsg)) < 0) {
                        char *sp;
 
-                       warningf(true, "%s: %s",
-                           (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg);
+                       warningf(true, Tf_sD_s,
+                           (sp = snptreef(NULL, 32, Tft_R, &iotmp)), emsg);
                        afree(sp, ATEMP);
                        return (-1);
                }
@@ -1453,7 +1430,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
 
        if (do_open) {
                if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
-                       warningf(true, "%s: %s", cp, "restricted");
+                       warningf(true, Tf_sD_s, cp, "restricted");
                        return (-1);
                }
                u = binopen3(cp, flags, 0666);
@@ -1471,10 +1448,10 @@ iosetup(struct ioword *iop, struct tbl *tp)
                /* herein() may already have printed message */
                if (u == -1) {
                        u = errno;
-                       warningf(true, "can't %s %s: %s",
+                       warningf(true, Tf_cant,
                            iotype == IODUP ? "dup" :
                            (iotype == IOREAD || iotype == IOHERE) ?
-                           "open" : "create", cp, cstrerror(u));
+                           Topen : Tcreate, cp, cstrerror(u));
                }
                return (-1);
        }
@@ -1502,9 +1479,8 @@ iosetup(struct ioword *iop, struct tbl *tp)
                        char *sp;
 
                        eno = errno;
-                       warningf(true, "%s %s: %s",
-                           "can't finish (dup) redirection",
-                           (sp = snptreef(NULL, 32, "%R", &iotmp)),
+                       warningf(true, Tf_s_sD_s, Tredirection_dup,
+                           (sp = snptreef(NULL, 32, Tft_R, &iotmp)),
                            cstrerror(eno));
                        afree(sp, ATEMP);
                        if (iotype != IODUP)
@@ -1559,7 +1535,7 @@ hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
                s->start = s->str = ccp;
                source = s;
                if (yylex(sub) != LWORD)
-                       internal_errorf("%s: %s", "herein", "yylex");
+                       internal_errorf("herein: yylex");
                source = osource;
                ccp = evalstr(yylval.cp, DOSCALAR | DOHEREDOC);
        }
@@ -1573,7 +1549,7 @@ hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
        return (0);
 }
 
-static int
+int
 herein(struct ioword *iop, char **resbuf)
 {
        int fd = -1;
@@ -1581,13 +1557,6 @@ herein(struct ioword *iop, char **resbuf)
        struct temp *h;
        int i;
 
-       /* ksh -c 'cat <<EOF' can cause this... */
-       if (iop->heredoc == NULL && !(iop->ioflag & IOHERESTR)) {
-               warningf(true, "%s missing", "here document");
-               /* special to iosetup(): don't print error */
-               return (-2);
-       }
-
        /* lexer substitution flags */
        i = (iop->ioflag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
 
@@ -1602,8 +1571,8 @@ herein(struct ioword *iop, char **resbuf)
        h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
        if (!(shf = h->shf) || (fd = binopen3(h->tffn, O_RDONLY, 0)) < 0) {
                i = errno;
-               warningf(true, "can't %s temporary file %s: %s",
-                   !shf ? "create" : "open", h->tffn, cstrerror(i));
+               warningf(true, Tf_temp,
+                   !shf ? Tcreate : Topen, h->tffn, cstrerror(i));
                if (shf)
                        shf_close(shf);
                /* special to iosetup(): don't print error */
@@ -1619,8 +1588,8 @@ herein(struct ioword *iop, char **resbuf)
        if (shf_close(shf) == -1) {
                i = errno;
                close(fd);
-               warningf(true, "can't %s temporary file %s: %s",
-                   "write", h->tffn, cstrerror(i));
+               warningf(true, Tf_temp,
+                   Twrite, h->tffn, cstrerror(i));
                /* special to iosetup(): don't print error */
                return (-2);
        }
@@ -1636,7 +1605,7 @@ static const char *
 do_selectargs(const char **ap, bool print_menu)
 {
        static const char *read_args[] = {
-               "read", "-r", "REPLY", NULL
+               Tread, "-r", "REPLY", NULL
        };
        char *s;
        int i, argct;
@@ -1652,8 +1621,8 @@ do_selectargs(const char **ap, bool print_menu)
                 */
                if (print_menu || !*str_val(global("REPLY")))
                        pr_menu(ap);
-               shellf("%s", str_val(global("PS3")));
-               if (call_builtin(findcom("read", FC_BI), read_args, Tselect,
+               shellf(Tf_s, str_val(global("PS3")));
+               if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect,
                    false))
                        return (NULL);
                if (*(s = str_val(global("REPLY"))))
index 64b9481..e40108f 100644 (file)
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.81 2016/01/14 21:17:50 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.88 2016/07/27 00:55:27 tg Exp $");
 
-/* the order of these enums is constrained by the order of opinfo[] */
-enum token {
-       /* some (long) unary operators */
-       O_PLUSPLUS = 0, O_MINUSMINUS,
-       /* binary operators */
-       O_EQ, O_NE,
-       /* assignments are assumed to be in range O_ASN .. O_BORASN */
-       O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
-#ifndef MKSH_LEGACY_MODE
-       O_ROLASN, O_RORASN,
-#endif
-       O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
-       /* binary non-assignment operators */
-#ifndef MKSH_LEGACY_MODE
-       O_ROL, O_ROR,
-#endif
-       O_LSHIFT, O_RSHIFT,
-       O_LE, O_GE, O_LT, O_GT,
-       O_LAND,
-       O_LOR,
-       O_TIMES, O_DIV, O_MOD,
-       O_PLUS, O_MINUS,
-       O_BAND,
-       O_BXOR,
-       O_BOR,
-       O_TERN,
-       O_COMMA,
-       /* things after this aren't used as binary operators */
-       /* unary that are not also binaries */
-       O_BNOT, O_LNOT,
-       /* misc */
-       OPEN_PAREN, CLOSE_PAREN, CTERN,
-       /* things that don't appear in the opinfo[] table */
-       VAR, LIT, END, BAD
-};
-#define IS_ASSIGNOP(op)        ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+#define EXPRTOK_DEFNS
+#include "exprtok.h"
 
 /* precisions; used to be enum prec but we do arithmetics on it */
 #define P_PRIMARY      0       /* VAR, LIT, (), ! ~ ++ -- */
 #define P_MULT         1       /* * / % */
 #define P_ADD          2       /* + - */
-#define P_SHIFT                3       /* <<< >>> << >> */
+#define P_SHIFT                3       /* ^< ^> << >> */
 #define P_RELATION     4       /* < <= > >= */
 #define P_EQUALITY     5       /* == != */
 #define P_BAND         6       /* & */
@@ -75,72 +41,29 @@ enum token {
 #define P_LAND         9       /* && */
 #define P_LOR          10      /* || */
 #define P_TERN         11      /* ?: */
-       /* = += -= *= /= %= <<<= >>>= <<= >>= &= ^= |= */
+       /* = += -= *= /= %= ^<= ^>= <<= >>= &= ^= |= */
 #define P_ASSIGN       12
 #define P_COMMA                13      /* , */
 #define MAX_PREC       P_COMMA
 
-struct opinfo {
-       char name[5];
-       /* name length */
-       uint8_t len;
-       /* precedence: lower is higher */
-       uint8_t prec;
+enum token {
+#define EXPRTOK_ENUM
+#include "exprtok.h"
 };
 
-/*
- * Tokens in this table must be ordered so the longest are first
- * (eg, += before +). If you change something, change the order
- * of enum token too.
- */
-static const struct opinfo opinfo[] = {
-       { "++",   2, P_PRIMARY },       /* before + */
-       { "--",   2, P_PRIMARY },       /* before - */
-       { "==",   2, P_EQUALITY },      /* before = */
-       { "!=",   2, P_EQUALITY },      /* before ! */
-       { "=",    1, P_ASSIGN },        /* keep assigns in a block */
-       { "*=",   2, P_ASSIGN },
-       { "/=",   2, P_ASSIGN },
-       { "%=",   2, P_ASSIGN },
-       { "+=",   2, P_ASSIGN },
-       { "-=",   2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
-       { "<<<=", 4, P_ASSIGN },        /* before <<< */
-       { ">>>=", 4, P_ASSIGN },        /* before >>> */
-#endif
-       { "<<=",  3, P_ASSIGN },
-       { ">>=",  3, P_ASSIGN },
-       { "&=",   2, P_ASSIGN },
-       { "^=",   2, P_ASSIGN },
-       { "|=",   2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
-       { "<<<",  3, P_SHIFT },         /* before << */
-       { ">>>",  3, P_SHIFT },         /* before >> */
-#endif
-       { "<<",   2, P_SHIFT },
-       { ">>",   2, P_SHIFT },
-       { "<=",   2, P_RELATION },
-       { ">=",   2, P_RELATION },
-       { "<",    1, P_RELATION },
-       { ">",    1, P_RELATION },
-       { "&&",   2, P_LAND },
-       { "||",   2, P_LOR },
-       { "*",    1, P_MULT },
-       { "/",    1, P_MULT },
-       { "%",    1, P_MULT },
-       { "+",    1, P_ADD },
-       { "-",    1, P_ADD },
-       { "&",    1, P_BAND },
-       { "^",    1, P_BXOR },
-       { "|",    1, P_BOR },
-       { "?",    1, P_TERN },
-       { ",",    1, P_COMMA },
-       { "~",    1, P_PRIMARY },
-       { "!",    1, P_PRIMARY },
-       { "(",    1, P_PRIMARY },
-       { ")",    1, P_PRIMARY },
-       { ":",    1, P_PRIMARY },
-       { "",     0, P_PRIMARY }
+static const char opname[][4] = {
+#define EXPRTOK_NAME
+#include "exprtok.h"
+};
+
+static const uint8_t oplen[] = {
+#define EXPRTOK_LEN
+#include "exprtok.h"
+};
+
+static const uint8_t opprec[] = {
+#define EXPRTOK_PREC
+#include "exprtok.h"
 };
 
 typedef struct expr_state {
@@ -227,7 +150,7 @@ v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
        exprtoken(es);
        if (es->tok == END) {
                es->tok = LIT;
-               es->val = tempvar();
+               es->val = tempvar("");
        }
        v = intvar(es, evalexpr(es, MAX_PREC));
 
@@ -272,35 +195,35 @@ evalerr(Expr_state *es, enum error_type type, const char *str)
                        s = tbuf;
                        break;
                default:
-                       s = opinfo[(int)es->tok].name;
+                       s = opname[(int)es->tok];
                }
-               warningf(true, "%s: %s '%s'", es->expression,
-                   "unexpected", s);
+               warningf(true, Tf_sD_s_qs, es->expression,
+                   Tunexpected, s);
                break;
 
        case ET_BADLIT:
-               warningf(true, "%s: %s '%s'", es->expression,
+               warningf(true, Tf_sD_s_qs, es->expression,
                    "bad number", str);
                break;
 
        case ET_RECURSIVE:
-               warningf(true, "%s: %s '%s'", es->expression,
+               warningf(true, Tf_sD_s_qs, es->expression,
                    "expression recurses on parameter", str);
                break;
 
        case ET_LVALUE:
-               warningf(true, "%s: %s %s",
+               warningf(true, Tf_sD_s_s,
                    es->expression, str, "requires lvalue");
                break;
 
        case ET_RDONLY:
-               warningf(true, "%s: %s %s",
+               warningf(true, Tf_sD_s_s,
                    es->expression, str, "applied to read-only variable");
                break;
 
        default: /* keep gcc happy */
        case ET_STR:
-               warningf(true, "%s: %s", es->expression, str);
+               warningf(true, Tf_sD_s, es->expression, str);
                break;
        }
        unwind(LAEXPR);
@@ -402,7 +325,7 @@ evalexpr(Expr_state *es, unsigned int prec)
 
        vl = evalexpr(es, prec - 1);
        while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
-           opinfo[(int)op].prec == prec) {
+           opprec[(int)op] == prec) {
                exprtoken(es);
                vasn = vl;
                if (op != O_ASN)
@@ -649,7 +572,7 @@ exprtoken(Expr_state *es)
                        cp += len;
                }
                if (es->noassign) {
-                       es->val = tempvar();
+                       es->val = tempvar("");
                        es->val->flag |= EXPRLVALUE;
                } else {
                        strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
@@ -665,7 +588,10 @@ exprtoken(Expr_state *es)
                goto process_tvar;
 #ifndef MKSH_SMALL
        } else if (c == '\'') {
-               ++cp;
+               if (*++cp == '\0') {
+                       es->tok = END;
+                       evalerr(es, ET_UNEXPECTED, NULL);
+               }
                cp += utf_ptradj(cp);
                if (*cp++ != '\'')
                        evalerr(es, ET_STR,
@@ -684,7 +610,7 @@ exprtoken(Expr_state *es)
                        c = *cp++;
                strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP);
  process_tvar:
-               es->val = tempvar();
+               es->val = tempvar("");
                es->val->flag &= ~INTEGER;
                es->val->type = 0;
                es->val->val.s = tvar;
@@ -695,11 +621,11 @@ exprtoken(Expr_state *es)
        } else {
                int i, n0;
 
-               for (i = 0; (n0 = opinfo[i].name[0]); i++)
-                       if (c == n0 && strncmp(cp, opinfo[i].name,
-                           (size_t)opinfo[i].len) == 0) {
+               for (i = 0; (n0 = opname[i][0]); i++)
+                       if (c == n0 && strncmp(cp, opname[i],
+                           (size_t)oplen[i]) == 0) {
                                es->tok = (enum token)i;
-                               cp += opinfo[i].len;
+                               cp += oplen[i];
                                break;
                        }
                if (!n0)
@@ -713,23 +639,25 @@ assign_check(Expr_state *es, enum token op, struct tbl *vasn)
 {
        if (es->tok == END || !vasn ||
            (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
-               evalerr(es, ET_LVALUE, opinfo[(int)op].name);
+               evalerr(es, ET_LVALUE, opname[(int)op]);
        else if (vasn->flag & RDONLY)
-               evalerr(es, ET_RDONLY, opinfo[(int)op].name);
+               evalerr(es, ET_RDONLY, opname[(int)op]);
 }
 
 struct tbl *
-tempvar(void)
+tempvar(const char *vname)
 {
        struct tbl *vp;
+       size_t vsize;
 
-       vp = alloc(sizeof(struct tbl), ATEMP);
+       vsize = strlen(vname) + 1;
+       vp = alloc(offsetof(struct tbl, name[0]) + vsize, ATEMP);
+       memcpy(vp->name, vname, vsize);
        vp->flag = ISSET|INTEGER;
        vp->type = 0;
        vp->areap = ATEMP;
        vp->ua.hval = 0;
        vp->val.i = 0;
-       vp->name[0] = '\0';
        return (vp);
 }
 
@@ -744,7 +672,7 @@ intvar(Expr_state *es, struct tbl *vp)
            (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
                return (vp);
 
-       vq = tempvar();
+       vq = tempvar("");
        if (setint_v(vq, vp, es->arith) == NULL) {
                if (vp->flag & EXPRINEVAL)
                        evalerr(es, ET_RECURSIVE, vp->name);
@@ -804,15 +732,26 @@ utf_mbswidth(const char *s)
 }
 
 const char *
-utf_skipcols(const char *p, int cols)
+utf_skipcols(const char *p, int cols, int *colp)
 {
        int c = 0;
+       const char *q;
 
        while (c < cols) {
-               if (!*p)
-                       return (p + cols - c);
+               if (!*p) {
+                       /* end of input; special handling for edit.c */
+                       if (!colp)
+                               return (p + cols - c);
+                       *colp = c;
+                       return (p);
+               }
                c += utf_widthadj(p, &p);
        }
+       if (UTFMODE)
+               while (utf_widthadj(p, &q) == 0)
+                       p = q;
+       if (colp)
+               *colp = c;
        return (p);
 }
 
diff --git a/src/exprtok.h b/src/exprtok.h
new file mode 100644 (file)
index 0000000..e4329da
--- /dev/null
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2016
+ *     mirabilos <m@mirbsd.org>
+ *
+ * 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.
+ */
+
+#if defined(EXPRTOK_DEFNS)
+__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
+/* see range comment below */
+#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+#define FN(name, len, prec, enum)      /* nothing */
+#define F1(enum)                       /* nothing */
+#elif defined(EXPRTOK_ENUM)
+#define F0(name, len, prec, enum)      enum = 0,
+#define FN(name, len, prec, enum)      enum,
+#define F1(enum)                       enum,
+#define F2(enum)                       enum,
+#define F9(enum)                       enum
+#elif defined(EXPRTOK_NAME)
+#define FN(name, len, prec, enum)      name,
+#define F1(enum)                       ""
+#elif defined(EXPRTOK_LEN)
+#define FN(name, len, prec, enum)      len,
+#define F1(enum)                       0
+#elif defined(EXPRTOK_PREC)
+#define FN(name, len, prec, enum)      prec,
+#define F1(enum)                       P_PRIMARY
+#endif
+
+#ifndef F0
+#define F0 FN
+#endif
+
+#ifndef F2
+#define F2(enum)                       /* nothing */
+#define F9(enum)                       /* nothing */
+#endif
+
+/* tokens must be ordered so the longest are first (e.g. += before +) */
+
+/* some (long) unary operators */
+FN("++", 2, P_PRIMARY, O_PLUSPLUS = 0) /* before + */
+FN("--", 2, P_PRIMARY, O_MINUSMINUS)   /* before - */
+/* binary operators */
+FN("==", 2, P_EQUALITY, O_EQ)          /* before = */
+FN("!=", 2, P_EQUALITY, O_NE)          /* before ! */
+/* assignments are assumed to be in range O_ASN .. O_BORASN */
+FN("=", 1, P_ASSIGN, O_ASN)
+FN("*=", 2, P_ASSIGN, O_TIMESASN)
+FN("/=", 2, P_ASSIGN, O_DIVASN)
+FN("%=", 2, P_ASSIGN, O_MODASN)
+FN("+=", 2, P_ASSIGN, O_PLUSASN)
+FN("-=", 2, P_ASSIGN, O_MINUSASN)
+#ifndef MKSH_LEGACY_MODE
+FN("^<=", 3, P_ASSIGN, O_ROLASN)       /* before ^< */
+FN("^>=", 3, P_ASSIGN, O_RORASN)       /* before ^> */
+#endif
+FN("<<=", 3, P_ASSIGN, O_LSHIFTASN)
+FN(">>=", 3, P_ASSIGN, O_RSHIFTASN)
+FN("&=", 2, P_ASSIGN, O_BANDASN)
+FN("^=", 2, P_ASSIGN, O_BXORASN)
+FN("|=", 2, P_ASSIGN, O_BORASN)
+/* binary non-assignment operators */
+#ifndef MKSH_LEGACY_MODE
+FN("^<", 2, P_SHIFT, O_ROL)            /* before ^ */
+FN("^>", 2, P_SHIFT, O_ROR)            /* before ^ */
+#endif
+FN("<<", 2, P_SHIFT, O_LSHIFT)
+FN(">>", 2, P_SHIFT, O_RSHIFT)
+FN("<=", 2, P_RELATION, O_LE)
+FN(">=", 2, P_RELATION, O_GE)
+FN("<", 1, P_RELATION, O_LT)
+FN(">", 1, P_RELATION, O_GT)
+FN("&&", 2, P_LAND, O_LAND)
+FN("||", 2, P_LOR, O_LOR)
+FN("*", 1, P_MULT, O_TIMES)
+FN("/", 1, P_MULT, O_DIV)
+FN("%", 1, P_MULT, O_MOD)
+FN("+", 1, P_ADD, O_PLUS)
+FN("-", 1, P_ADD, O_MINUS)
+FN("&", 1, P_BAND, O_BAND)
+FN("^", 1, P_BXOR, O_BXOR)
+FN("|", 1, P_BOR, O_BOR)
+FN("?", 1, P_TERN, O_TERN)
+FN(",", 1, P_COMMA, O_COMMA)
+/* things after this aren't used as binary operators */
+/* unary that are not also binaries */
+FN("~", 1, P_PRIMARY, O_BNOT)
+FN("!", 1, P_PRIMARY, O_LNOT)
+/* misc */
+FN("(", 1, P_PRIMARY, OPEN_PAREN)
+FN(")", 1, P_PRIMARY, CLOSE_PAREN)
+FN(":", 1, P_PRIMARY, CTERN)
+/* things that don't appear in the opinfo[] table */
+F1(VAR)                                /*XXX should be F2 */
+F2(LIT)
+F2(END)
+F9(BAD)
+
+#undef FN
+#undef F0
+#undef F1
+#undef F2
+#undef F9
+#undef EXPRTOK_DEFNS
+#undef EXPRTOK_ENUM
+#undef EXPRTOK_NAME
+#undef EXPRTOK_LEN
+#undef EXPRTOK_PREC
index ab6e3b7..e052fac 100644 (file)
@@ -38,7 +38,7 @@
 #endif
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.293 2016/01/20 21:34:11 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.305 2016/08/01 21:38:02 tg Exp $");
 
 #if HAVE_KILLPG
 /*
@@ -64,6 +64,8 @@ __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.293 2016/01/20 21:34:11 tg Exp $");
 static int c_suspend(const char **);
 #endif
 
+static int do_whence(const char **, int, bool, bool);
+
 /* getn() that prints error */
 static int
 bi_getn(const char *as, int *ai)
@@ -71,7 +73,7 @@ bi_getn(const char *as, int *ai)
        int rv;
 
        if (!(rv = getn(as, ai)))
-               bi_errorf("%s: %s", as, "bad number");
+               bi_errorf(Tf_sD_s, as, "bad number");
        return (rv);
 }
 
@@ -92,15 +94,15 @@ c_false(const char **wp MKSH_A_UNUSED)
  * A leading * means a POSIX special builtin.
  */
 const struct builtin mkshbuiltins[] = {
-       {"*=.", c_dot},
+       {Tsgdot, c_dot},
        {"*=:", c_true},
-       {"[", c_test},
+       {Tbracket, c_test},
        /* no =: AT&T manual wrong */
        {Talias, c_alias},
        {"*=break", c_brkcont},
        {Tgbuiltin, c_builtin},
-       {Tcat, c_cat},
-       {"cd", c_cd},
+       {Tbcat, c_cat},
+       {Tcd, c_cd},
        /* dash compatibility hack */
        {"chdir", c_cd},
        {Tcommand, c_command},
@@ -110,32 +112,32 @@ const struct builtin mkshbuiltins[] = {
        {"*=exec", c_exec},
        {"*=exit", c_exitreturn},
        {Tsgexport, c_typeset},
-       {"false", c_false},
+       {Tfalse, c_false},
        {"fc", c_fc},
-       {"getopts", c_getopts},
+       {Tgetopts, c_getopts},
        {"=global", c_typeset},
-       {"jobs", c_jobs},
+       {Tjobs, c_jobs},
        {"kill", c_kill},
        {"let", c_let},
        {"let]", c_let},
        {"print", c_print},
        {"pwd", c_pwd},
-       {"read", c_read},
+       {Tread, c_read},
        {Tsgreadonly, c_typeset},
-       {"realpath", c_realpath},
-       {"rename", c_rename},
+       {"!realpath", c_realpath},
+       {"~rename", c_rename},
        {"*=return", c_exitreturn},
        {Tsgset, c_set},
        {"*=shift", c_shift},
        {"=source", c_dot},
 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
-       {"suspend", c_suspend},
+       {Tsuspend, c_suspend},
 #endif
        {"test", c_test},
        {"*=times", c_times},
        {"*=trap", c_trap},
-       {"true", c_true},
-       {T_typeset, c_typeset},
+       {Ttrue, c_true},
+       {Tgtypeset, c_typeset},
        {"ulimit", c_ulimit},
        {"umask", c_umask},
        {Tunalias, c_unalias},
@@ -143,8 +145,8 @@ const struct builtin mkshbuiltins[] = {
        {"=wait", c_wait},
        {"whence", c_whence},
 #ifndef MKSH_UNEMPLOYED
-       {"bg", c_fgbg},
-       {"fg", c_fgbg},
+       {Tbg, c_fgbg},
+       {Tfg, c_fgbg},
 #endif
 #ifndef MKSH_NO_CMDLINE_EDITING
        {"bind", c_bind},
@@ -153,7 +155,7 @@ const struct builtin mkshbuiltins[] = {
        {"mknod", c_mknod},
 #endif
 #ifdef MKSH_PRINTF_BUILTIN
-       {Tprintf, c_printf},
+       {"~printf", c_printf},
 #endif
 #if HAVE_SELECT
        {"sleep", c_sleep},
@@ -253,7 +255,7 @@ c_pwd(const char **wp)
        wp += builtin_opt.optind;
 
        if (wp[0]) {
-               bi_errorf("too many arguments");
+               bi_errorf(Ttoo_many_args);
                return (1);
        }
        p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
@@ -262,11 +264,11 @@ c_pwd(const char **wp)
        if (p && access(p, R_OK) < 0)
                p = NULL;
        if (!p && !(p = allocd = ksh_get_wd())) {
-               bi_errorf("%s: %s", "can't determine current directory",
+               bi_errorf(Tf_sD_s, "can't determine current directory",
                    cstrerror(errno));
                return (1);
        }
-       shprintf("%s\n", p);
+       shprintf(Tf_sN, p);
        afree(allocd, ATEMP);
        return (0);
 }
@@ -286,6 +288,9 @@ c_print(const char **wp)
        bool po_nl = true, po_exp = true;
        /* print to history instead of file descriptor / stdout */
        bool po_hist = false;
+       /* print characters */
+       bool po_char = false;
+       char ts[4];
 
        if (wp[0][0] == 'e') {
                /* "echo" builtin */
@@ -350,13 +355,16 @@ c_print(const char **wp)
                }
        } else {
                /* "print" builtin */
-               const char *opts = "npRrsu,";
+               const char *opts = "AnpRrsu,";
                const char *emsg;
                /* print a "--" argument */
                bool po_pminusminus = false;
 
                while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
                        switch (c) {
+                       case 'A':
+                               po_char = true;
+                               break;
                        case 'e':
                                po_exp = true;
                                break;
@@ -365,7 +373,7 @@ c_print(const char **wp)
                                break;
                        case 'p':
                                if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
-                                       bi_errorf("-p: %s", emsg);
+                                       bi_errorf(Tf_coproc, emsg);
                                        return (1);
                                }
                                break;
@@ -405,7 +413,23 @@ c_print(const char **wp)
 
        Xinit(xs, xp, 128, ATEMP);
 
-       if (*wp != NULL) {
+       if (*wp != NULL && po_char) {
+               mksh_ari_t wc;
+
+               do {
+                       if (!evaluate(*wp, &wc, KSH_RETURN_ERROR, true))
+                               return (1);
+                       Xcheck(xs, xp);
+                       if (UTFMODE) {
+                               ts[utf_wctomb(ts, wc)] = 0;
+                               c = 0;
+                               do {
+                                       Xput(xs, xp, ts[c]);
+                               } while (ts[++c]);
+                       } else
+                               Xput(xs, xp, wc & 0xFF);
+               } while (*++wp);
+       } else if (*wp != NULL) {
  print_read_arg:
                s = *wp;
                while ((c = *s++) != '\0') {
@@ -430,8 +454,6 @@ c_print(const char **wp)
                                        }
                                } else if ((unsigned int)c > 0xFF) {
                                        /* generic function returned Unicode */
-                                       char ts[4];
-
                                        ts[utf_wctomb(ts, c - 0x100)] = 0;
                                        c = 0;
                                        do {
@@ -514,14 +536,10 @@ s_put(int c MKSH_A_UNUSED)
 int
 c_whence(const char **wp)
 {
-       struct tbl *tp;
-       const char *id;
-       bool pflag = false, vflag = false, Vflag = false;
-       int rv = 0, optc, fcflags;
-       bool iam_whence = wp[0][0] == 'w';
-       const char *opts = iam_whence ? "pv" : "pvV";
+       int optc;
+       bool pflag = false, vflag = false;
 
-       while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
+       while ((optc = ksh_getopt(wp, &builtin_opt, Tpv)) != -1)
                switch (optc) {
                case 'p':
                        pflag = true;
@@ -529,80 +547,81 @@ c_whence(const char **wp)
                case 'v':
                        vflag = true;
                        break;
+               case '?':
+                       return (1);
+               }
+       wp += builtin_opt.optind;
+
+       return (do_whence(wp, pflag ? FC_PATH :
+           FC_BI | FC_FUNC | FC_PATH | FC_WHENCE, vflag, false));
+}
+
+/* note: command without -vV is dealt with in comexec() */
+int
+c_command(const char **wp)
+{
+       int optc, fcflags = FC_BI | FC_FUNC | FC_PATH | FC_WHENCE;
+       bool vflag = false;
+
+       while ((optc = ksh_getopt(wp, &builtin_opt, TpVv)) != -1)
+               switch (optc) {
+               case 'p':
+                       fcflags |= FC_DEFPATH;
+                       break;
                case 'V':
-                       Vflag = true;
+                       vflag = true;
+                       break;
+               case 'v':
+                       vflag = false;
                        break;
                case '?':
                        return (1);
                }
        wp += builtin_opt.optind;
 
-       fcflags = FC_BI | FC_PATH | FC_FUNC;
-       if (!iam_whence) {
-               /* Note that -p on its own is deal with in comexec() */
-               if (pflag)
-                       fcflags |= FC_DEFPATH;
-               /*
-                * Convert command options to whence options - note that
-                * command -pV uses a different path search than whence -v
-                * or whence -pv. This should be considered a feature.
-                */
-               vflag = Vflag;
-       }
-       if (pflag)
-               fcflags &= ~(FC_BI | FC_FUNC);
+       return (do_whence(wp, fcflags, vflag, true));
+}
 
-       while ((vflag || rv == 0) && (id = *wp++) != NULL) {
-               uint32_t h = 0;
+static int
+do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
+{
+       uint32_t h;
+       int rv = 0;
+       struct tbl *tp;
+       const char *id;
 
+       while ((vflag || rv == 0) && (id = *wp++) != NULL) {
+               h = hash(id);
                tp = NULL;
-               if (!pflag)
-                       tp = ktsearch(&keywords, id, h = hash(id));
-               if (!tp && !pflag) {
-                       tp = ktsearch(&aliases, id, h ? h : hash(id));
+
+               if (fcflags & FC_WHENCE)
+                       tp = ktsearch(&keywords, id, h);
+               if (!tp && (fcflags & FC_WHENCE)) {
+                       tp = ktsearch(&aliases, id, h);
                        if (tp && !(tp->flag & ISSET))
                                tp = NULL;
                }
                if (!tp)
                        tp = findcom(id, fcflags);
-               if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
-                   tp->type != CTALIAS))
-                       shf_puts(id, shl_stdout);
-               if (vflag) {
-                       switch (tp->type) {
-                       case CKEYWD:
-                       case CALIAS:
-                       case CFUNC:
-                       case CSHELL:
-                               shf_puts(" is a", shl_stdout);
-                               break;
-                       }
-                       switch (tp->type) {
-                       case CKEYWD:
-                       case CSHELL:
-                       case CTALIAS:
-                       case CEXEC:
-                               shf_putc(' ', shl_stdout);
-                               break;
-                       }
-               }
 
                switch (tp->type) {
+               case CSHELL:
+               case CFUNC:
                case CKEYWD:
-                       if (vflag)
-                               shf_puts("reserved word", shl_stdout);
+                       shf_puts(id, shl_stdout);
                        break;
-               case CALIAS:
+               }
+
+               switch (tp->type) {
+               case CSHELL:
                        if (vflag)
-                               shprintf("n %s%s for ",
-                                   (tp->flag & EXPORT) ? "exported " : null,
-                                   Talias);
-                       if (!iam_whence && !vflag)
-                               shprintf("%s %s=", Talias, id);
-                       print_value_quoted(shl_stdout, tp->val.s);
+                               shprintf(" is a %sshell %s",
+                                   (tp->flag & SPEC_BI) ? "special " : "",
+                                   Tbuiltin);
                        break;
                case CFUNC:
                        if (vflag) {
+                               shf_puts(" is a", shl_stdout);
                                if (tp->flag & EXPORT)
                                        shf_puts("n exported", shl_stdout);
                                if (tp->flag & TRACE)
@@ -616,34 +635,42 @@ c_whence(const char **wp)
                                shf_puts(T_function, shl_stdout);
                        }
                        break;
-               case CSHELL:
-                       if (vflag) {
-                               if (tp->flag & SPEC_BI)
-                                       shf_puts("special ", shl_stdout);
-                               shprintf("%s %s", "shell", Tbuiltin);
-                       }
-                       break;
-               case CTALIAS:
                case CEXEC:
+               case CTALIAS:
                        if (tp->flag & ISSET) {
                                if (vflag) {
-                                       shf_puts("is ", shl_stdout);
+                                       shprintf("%s is ", id);
                                        if (tp->type == CTALIAS)
                                                shprintf("a tracked %s%s for ",
                                                    (tp->flag & EXPORT) ?
-                                                   "exported " : null,
+                                                   "exported " : "",
                                                    Talias);
                                }
                                shf_puts(tp->val.s, shl_stdout);
                        } else {
                                if (vflag)
-                                       shf_puts("not found", shl_stdout);
+                                       shprintf(Tnot_found_s, id);
                                rv = 1;
                        }
                        break;
-               default:
-                       shf_puts(" is *GOK*", shl_stdout);
+               case CALIAS:
+                       if (vflag) {
+                               shprintf("%s is an %s%s for ", id,
+                                   (tp->flag & EXPORT) ? "exported " : "",
+                                   Talias);
+                       } else if (iscommand)
+                               shprintf("%s %s=", Talias, id);
+                       print_value_quoted(shl_stdout, tp->val.s);
                        break;
+               case CKEYWD:
+                       if (vflag)
+                               shf_puts(" is a reserved word", shl_stdout);
+                       break;
+#ifndef MKSH_SMALL
+               default:
+                       bi_errorf("%s is of unknown type %d", id, tp->type);
+                       return (1);
+#endif
                }
                if (vflag || !rv)
                        shf_putc('\n', shl_stdout);
@@ -651,17 +678,6 @@ c_whence(const char **wp)
        return (rv);
 }
 
-/* Deal with command -vV - command -p dealt with in comexec() */
-int
-c_command(const char **wp)
-{
-       /*
-        * Let c_whence do the work. Note that c_command() must be
-        * a distinct function from c_whence() (tested in comexec()).
-        */
-       return (c_whence(wp));
-}
-
 /* 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,
@@ -798,7 +814,7 @@ c_typeset(const char **wp)
                return (1);
        if (basestr) {
                if (!getn(basestr, &base)) {
-                       bi_errorf("%s: %s", "bad integer base", basestr);
+                       bi_errorf(Tf_sD_s, "bad integer base", basestr);
                        return (1);
                }
                if (base < 1 || base > 36)
@@ -881,7 +897,7 @@ c_typeset(const char **wp)
                                        shf_putc('\n', shl_stdout);
                                }
                        } else if (!typeset(wp[i], fset, fclr, field, base)) {
-                               bi_errorf("%s: %s", wp[i], "is not an identifier");
+                               bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
                                return (1);
                        }
                }
@@ -978,7 +994,7 @@ c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
                /* no arguments */
                if (!thing && !flag) {
                        if (any_set == 1) {
-                               shprintf("%s %s %s\n", Tset, "-A", vp->name);
+                               shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
                                any_set = 2;
                        }
                        /*
@@ -986,31 +1002,31 @@ c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
                         * leftadj, zerofill, etc., but POSIX says must
                         * be suitable for re-entry...
                         */
-                       shprintf("%s %s", Ttypeset, "");
+                       shprintf(Tf_s_s, Ttypeset, "");
                        if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
-                               shprintf("%s ", "-n");
+                               shprintf(Tf__c_, 'n');
                        if ((vp->flag & INTEGER))
-                               shprintf("%s ", "-i");
+                               shprintf(Tf__c_, 'i');
                        if ((vp->flag & EXPORT))
-                               shprintf("%s ", "-x");
+                               shprintf(Tf__c_, 'x');
                        if ((vp->flag & RDONLY))
-                               shprintf("%s ", "-r");
+                               shprintf(Tf__c_, 'r');
                        if ((vp->flag & TRACE))
-                               shprintf("%s ", "-t");
+                               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("%s ", "-Z");
+                               shprintf(Tf__c_, 'Z');
                        if ((vp->flag & LCASEV))
-                               shprintf("%s ", "-l");
+                               shprintf(Tf__c_, 'l');
                        if ((vp->flag & UCASEV_AL))
-                               shprintf("%s ", "-u");
+                               shprintf(Tf__c_, 'u');
                        if ((vp->flag & INT_U))
-                               shprintf("%s ", "-U");
+                               shprintf(Tf__c_, 'U');
                } else if (pflag) {
-                       shprintf("%s %s", istset ? Ttypeset :
+                       shprintf(Tf_s_s, istset ? Ttypeset :
                            (flag & EXPORT) ? Texport : Treadonly, "");
                }
                if (any_set)
@@ -1116,7 +1132,7 @@ c_alias(const char **wp)
                for (p = ktsort(t); (ap = *p++) != NULL; )
                        if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
                                if (pflag)
-                                       shprintf("%s ", Talias);
+                                       shprintf(Tf_s_, Talias);
                                shf_puts(ap->name, shl_stdout);
                                if (prefix != '+') {
                                        shf_putc('=', shl_stdout);
@@ -1141,7 +1157,7 @@ c_alias(const char **wp)
                        ap = ktsearch(t, alias, h);
                        if (ap != NULL && (ap->flag&ISSET)) {
                                if (pflag)
-                                       shprintf("%s ", Talias);
+                                       shprintf(Tf_s_, Talias);
                                shf_puts(ap->name, shl_stdout);
                                if (prefix != '+') {
                                        shf_putc('=', shl_stdout);
@@ -1149,8 +1165,7 @@ c_alias(const char **wp)
                                }
                                shf_putc('\n', shl_stdout);
                        } else {
-                               shprintf("%s %s %s\n", alias, Talias,
-                                   "not found");
+                               shprintf(Tf_s_s_sN, alias, Talias, Tnot_found);
                                rv = 1;
                        }
                        continue;
@@ -1254,7 +1269,7 @@ c_let(const char **wp)
 
        if (wp[1] == NULL)
                /* AT&T ksh does this */
-               bi_errorf("no arguments");
+               bi_errorf(Tno_args);
        else
                for (wp++; *wp; wp++)
                        if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
@@ -1305,7 +1320,7 @@ c_jobs(const char **wp)
 int
 c_fgbg(const char **wp)
 {
-       bool bg = strcmp(*wp, "bg") == 0;
+       bool bg = strcmp(*wp, Tbg) == 0;
        int rv = 0;
 
        if (!Flag(FMONITOR)) {
@@ -1350,7 +1365,7 @@ c_kill(const char **wp)
        if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
            ksh_isupper(p[1]))) {
                if (!(t = gettrap(p + 1, false, false))) {
-                       bi_errorf("bad signal '%s'", p + 1);
+                       bi_errorf(Tbad_sig_s, p + 1);
                        return (1);
                }
                i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
@@ -1365,7 +1380,7 @@ c_kill(const char **wp)
                        case 's':
                                if (!(t = gettrap(builtin_opt.optarg,
                                    true, false))) {
-                                       bi_errorf("bad signal '%s'",
+                                       bi_errorf(Tbad_sig_s,
                                            builtin_opt.optarg);
                                        return (1);
                                }
@@ -1395,9 +1410,9 @@ c_kill(const char **wp)
                                        n -= 128;
 #endif
                                if (n > 0 && n < ksh_NSIG)
-                                       shprintf("%s\n", sigtraps[n].name);
+                                       shprintf(Tf_sN, sigtraps[n].name);
                                else
-                                       shprintf("%d\n", n);
+                                       shprintf(Tf_dN, n);
                        }
                } else if (Flag(FPOSIX)) {
                        n = 1;
@@ -1442,12 +1457,12 @@ c_kill(const char **wp)
                        if (j_kill(p, sig))
                                rv = 1;
                } else if (!getn(p, &n)) {
-                       bi_errorf("%s: %s", p,
+                       bi_errorf(Tf_sD_s, p,
                            "arguments must be jobs or process IDs");
                        rv = 1;
                } else {
                        if (mksh_kill(n, sig) < 0) {
-                               bi_errorf("%s: %s", p, cstrerror(errno));
+                               bi_errorf(Tf_sD_s, p, cstrerror(errno));
                                rv = 1;
                        }
                }
@@ -1479,22 +1494,22 @@ c_getopts(const char **wp)
 
        opts = *wp++;
        if (!opts) {
-               bi_errorf("missing %s argument", "options");
+               bi_errorf(Tf_sD_s, "options", Tno_args);
                return (1);
        }
 
        var = *wp++;
        if (!var) {
-               bi_errorf("missing %s argument", "name");
+               bi_errorf(Tf_sD_s, Tname, Tno_args);
                return (1);
        }
        if (!*var || *skip_varname(var, true)) {
-               bi_errorf("%s: %s", var, "is not an identifier");
+               bi_errorf(Tf_sD_s, var, Tnot_ident);
                return (1);
        }
 
        if (e->loc->next == NULL) {
-               internal_warningf("%s: %s", "c_getopts", "no argv");
+               internal_warningf(Tf_sD_s, Tgetopts, Tno_args);
                return (1);
        }
        /* Which arguments are we parsing... */
@@ -1639,7 +1654,7 @@ c_shift(const char **wp)
                /* nothing to do */
                return (0);
        } else if (n < 0) {
-               bi_errorf("%s: %s", arg, "bad number");
+               bi_errorf(Tf_sD_s, arg, "bad number");
                return (1);
        }
        if (l->argc < n) {
@@ -1679,7 +1694,7 @@ c_umask(const char **wp)
                        old_umask = ~old_umask;
                        p = buf;
                        for (i = 0; i < 3; i++) {
-                               *p++ = "ugo"[i];
+                               *p++ = Tugo[i];
                                *p++ = '=';
                                for (j = 0; j < 3; j++)
                                        if (old_umask & (1 << (8 - (3*i + j))))
@@ -1687,7 +1702,7 @@ c_umask(const char **wp)
                                *p++ = ',';
                        }
                        p[-1] = '\0';
-                       shprintf("%s\n", buf);
+                       shprintf(Tf_sN, buf);
                } else
                        shprintf("%#3.3o\n", (unsigned int)old_umask);
        } else {
@@ -1715,7 +1730,7 @@ c_umask(const char **wp)
                        new_umask = old_umask;
                        positions = 0;
                        while (*cp) {
-                               while (*cp && vstrchr("augo", *cp))
+                               while (*cp && vstrchr(Taugo, *cp))
                                        switch (*cp++) {
                                        case 'a':
                                                positions |= 0111;
@@ -1792,13 +1807,13 @@ int
 c_dot(const char **wp)
 {
        const char *file, *cp, **argv;
-       int argc, i, errcode;
+       int argc, rv, errcode;
 
        if (ksh_getopt(wp, &builtin_opt, null) == '?')
                return (1);
 
        if ((cp = wp[builtin_opt.optind]) == NULL) {
-               bi_errorf("missing argument");
+               bi_errorf(Tno_args);
                return (1);
        }
        file = search_path(cp, path, R_OK, &errcode);
@@ -1806,7 +1821,7 @@ c_dot(const char **wp)
            search_access(cp, R_OK) == 0)
                file = cp;
        if (!file) {
-               bi_errorf("%s: %s", cp, cstrerror(errcode));
+               bi_errorf(Tf_sD_s, cp, cstrerror(errcode));
                return (1);
        }
 
@@ -1821,12 +1836,17 @@ c_dot(const char **wp)
                argc = 0;
                argv = NULL;
        }
-       if ((i = include(file, argc, argv, false)) < 0) {
+       /* SUSv4: OR with a high value never written otherwise */
+       exstat |= 0x4000;
+       if ((rv = include(file, argc, argv, false)) < 0) {
                /* should not happen */
-               bi_errorf("%s: %s", cp, cstrerror(errno));
+               bi_errorf(Tf_sD_s, cp, cstrerror(errno));
                return (1);
        }
-       return (i);
+       if (exstat & 0x4000)
+               /* detect old exstat, use 0 in that case */
+               rv = 0;
+       return (rv);
 }
 
 int
@@ -1904,7 +1924,7 @@ c_read(const char **wp)
                break;
        case 'p':
                if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
-                       bi_errorf("%s: %s", "-p", ccp);
+                       bi_errorf(Tf_coproc, ccp);
                        return (2);
                }
                break;
@@ -1917,7 +1937,7 @@ c_read(const char **wp)
 #if HAVE_SELECT
        case 't':
                if (parse_usec(builtin_opt.optarg, &tv)) {
-                       bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
+                       bi_errorf(Tf_sD_s_qs, Tsynerr, cstrerror(errno),
                            builtin_opt.optarg);
                        return (2);
                }
@@ -1928,7 +1948,7 @@ c_read(const char **wp)
                if (!builtin_opt.optarg[0])
                        fd = 0;
                else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
-                       bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
+                       bi_errorf(Tf_sD_sD_s, "-u", builtin_opt.optarg, ccp);
                        return (2);
                }
                break;
@@ -1940,7 +1960,7 @@ c_read(const char **wp)
                *--wp = REPLY;
 
        if (intoarray && wp[1] != NULL) {
-               bi_errorf("too many arguments");
+               bi_errorf(Ttoo_many_args);
                return (2);
        }
 
@@ -2000,27 +2020,27 @@ c_read(const char **wp)
                        /* fake EOF read; all cases return 1 */
                        goto c_read_didread;
                default:
-                       bi_errorf("%s: %s", Tselect, cstrerror(errno));
+                       bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
                        rv = 2;
                        goto c_read_out;
                }
        }
 #endif
 
-       bytesread = blocking_read(fd, xp, bytesleft);
-       if (bytesread == (size_t)-1) {
-               /* interrupted */
-               if (errno == EINTR && fatal_trap_check()) {
-                       /*
-                        * Was the offending signal one that would
-                        * normally kill a process? If so, pretend
-                        * the read was killed.
-                        */
-                       rv = 2;
-                       goto c_read_out;
+       if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) {
+               if (errno == EINTR) {
+                       /* check whether the signal would normally kill */
+                       if (!fatal_trap_check()) {
+                               /* no, just ignore the signal */
+                               goto c_read_readloop;
+                       }
+                       /* pretend the read was killed */
+               } else {
+                       /* unexpected error */
+                       bi_errorf(Tf_s, cstrerror(errno));
                }
-               /* just ignore the signal */
-               goto c_read_readloop;
+               rv = 2;
+               goto c_read_out;
        }
 
  c_read_didread:
@@ -2119,7 +2139,7 @@ c_read(const char **wp)
                subarray = last_lookup_was_array;
                if (vp->flag & RDONLY) {
  c_read_splitro:
-                       bi_errorf("read-only: %s", *wp);
+                       bi_errorf(Tf_ro, *wp);
  c_read_spliterr:
                        rv = 2;
                        afree(cp, ATEMP);
@@ -2347,7 +2367,7 @@ c_trap(const char **wp)
                        if (p->trap) {
                                shf_puts("trap -- ", shl_stdout);
                                print_value_quoted(shl_stdout, p->trap);
-                               shprintf(" %s\n", p->name);
+                               shprintf(Tf__sN, p->name);
                        }
                        ++p;
                } while (i--);
@@ -2369,8 +2389,7 @@ c_trap(const char **wp)
        i = 0;
        while (*wp)
                if (!(p = gettrap(*wp++, true, true))) {
-                       warningf(true, "%s: %s '%s'", builtin_argv0,
-                           "bad signal", wp[-1]);
+                       warningf(true, Tbad_sig_ss, builtin_argv0, wp[-1]);
                        i = 1;
                } else
                        settrap(p, s);
@@ -2415,7 +2434,7 @@ c_exitreturn(const char **wp)
        /* NOTREACHED */
 
  c_exitreturn_err:
-       bi_errorf("too many arguments");
+       bi_errorf(Ttoo_many_args);
        return (1);
 }
 
@@ -2437,7 +2456,7 @@ c_brkcont(const char **wp)
                goto c_brkcont_err;
        if (n <= 0) {
                /* AT&T ksh does this for non-interactive shells only - weird */
-               bi_errorf("%s: %s", arg, "bad value");
+               bi_errorf("%s: bad value", arg);
                goto c_brkcont_err;
        }
        quit = (unsigned int)n;
@@ -2458,7 +2477,7 @@ c_brkcont(const char **wp)
                 * scripts, but don't generate an error (ie, keep going).
                 */
                if ((unsigned int)n == quit) {
-                       warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
+                       warningf(true, "%s: can't %s", wp[0], wp[0]);
                        return (0);
                }
                /*
@@ -2564,7 +2583,7 @@ c_unset(const char **wp)
                        afree(cp, ATEMP);
 
                        if ((vp->flag&RDONLY)) {
-                               warningf(true, "read-only: %s", vp->name);
+                               warningf(true, Tf_ro, vp->name);
                                rv = 1;
                        } else
                                unset(vp, optc);
@@ -2594,13 +2613,13 @@ c_times(const char **wp MKSH_A_UNUSED)
 
        getrusage(RUSAGE_SELF, &usage);
        p_time(shl_stdout, false, usage.ru_utime.tv_sec,
-           usage.ru_utime.tv_usec, 0, null, " ");
+           usage.ru_utime.tv_usec, 0, null, T1space);
        p_time(shl_stdout, false, usage.ru_stime.tv_sec,
            usage.ru_stime.tv_usec, 0, null, "\n");
 
        getrusage(RUSAGE_CHILDREN, &usage);
        p_time(shl_stdout, false, usage.ru_utime.tv_sec,
-           usage.ru_utime.tv_usec, 0, null, " ");
+           usage.ru_utime.tv_usec, 0, null, T1space);
        p_time(shl_stdout, false, usage.ru_stime.tv_sec,
            usage.ru_stime.tv_usec, 0, null, "\n");
 
@@ -2659,17 +2678,17 @@ timex(struct op *t, int f, volatile int *xerrok)
                timersub(&tv1, &tv0, &tv1);
                if (tf & TF_POSIX)
                        p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
-                           5, "real ", "\n");
+                           5, Treal_sp1, "\n");
                else
                        p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
-                           5, null, " real ");
+                           5, null, Treal_sp2);
        }
        if (tf & TF_POSIX)
                p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
-                   5, "user ", "\n");
+                   5, Tuser_sp1, "\n");
        else
                p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
-                   5, null, " user ");
+                   5, null, Tuser_sp2);
        if (tf & TF_POSIX)
                p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
                    5, "sys  ", "\n");
@@ -2697,11 +2716,11 @@ timex_hook(struct op *t, char **volatile *app)
                        t->str[0] |= TF_POSIX;
                        break;
                case '?':
-                       errorf("time: -%s %s", opt.optarg,
-                           "unknown option");
+                       errorf(Tf_optfoo, Ttime, Tcolsp,
+                           opt.optarg[0], Tunknown_option);
                case ':':
-                       errorf("time: -%s %s", opt.optarg,
-                           "requires an argument");
+                       errorf(Tf_optfoo, Ttime, Tcolsp,
+                           opt.optarg[0], Treq_arg);
                }
        /* Copy command words down over options. */
        if (opt.optind != 0) {
@@ -2791,28 +2810,28 @@ c_mknod(const char **wp)
 
                majnum = strtoul(argv[2], &c, 0);
                if ((c == argv[2]) || (*c != '\0')) {
-                       bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
+                       bi_errorf(Tf_nonnum, "device", "major", argv[2]);
                        goto c_mknod_err;
                }
                minnum = strtoul(argv[3], &c, 0);
                if ((c == argv[3]) || (*c != '\0')) {
-                       bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
+                       bi_errorf(Tf_nonnum, "device", "minor", argv[3]);
                        goto c_mknod_err;
                }
                dv = makedev(majnum, minnum);
                if ((unsigned long)(major(dv)) != majnum) {
-                       bi_errorf("%s %s too large: %lu", "device", "major", majnum);
+                       bi_errorf(Tf_toolarge, "device", "major", majnum);
                        goto c_mknod_err;
                }
                if ((unsigned long)(minor(dv)) != minnum) {
-                       bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
+                       bi_errorf(Tf_toolarge, "device", "minor", minnum);
                        goto c_mknod_err;
                }
                if (mknod(argv[0], mode, dv))
                        goto c_mknod_failed;
        } else if (mkfifo(argv[0], mode)) {
  c_mknod_failed:
-               bi_errorf("%s: %s", argv[0], cstrerror(errno));
+               bi_errorf(Tf_sD_s, argv[0], cstrerror(errno));
  c_mknod_err:
                rv = 1;
        }
@@ -2821,8 +2840,8 @@ c_mknod(const char **wp)
                umask(oldmode);
        return (rv);
  c_mknod_usage:
-       bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
-       bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
+       bi_errorf("usage: mknod [-m mode] name %s", "b|c major minor");
+       bi_errorf("usage: mknod [-m mode] name %s", "p");
        return (1);
 }
 #endif
@@ -2858,6 +2877,7 @@ c_test(const char **wp)
        int argc, rv, invert = 0;
        Test_env te;
        Test_op op;
+       Test_meta tm;
        const char *lhs, **swp;
 
        te.flags = 0;
@@ -2869,7 +2889,7 @@ c_test(const char **wp)
        for (argc = 0; wp[argc]; argc++)
                ;
 
-       if (strcmp(wp[0], "[") == 0) {
+       if (strcmp(wp[0], Tbracket) == 0) {
                if (strcmp(wp[--argc], "]") != 0) {
                        bi_errorf("missing ]");
                        return (T_ERR_EXIT);
@@ -2918,6 +2938,16 @@ c_test(const char **wp)
                        rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
                        goto ptest_out;
                }
+               if (ptest_isa(&te, tm = TM_AND) || ptest_isa(&te, tm = TM_OR)) {
+                       /* XSI */
+                       argc = test_eval(&te, TO_STNZE, lhs, NULL, true);
+                       rv = test_eval(&te, TO_STNZE, *te.pos.wp++, NULL, true);
+                       if (tm == TM_AND)
+                               rv = argc && rv;
+                       else
+                               rv = argc || rv;
+                       goto ptest_out;
+               }
                /* back up to lhs */
                te.pos.wp = swp;
                if (ptest_isa(&te, TM_NOT)) {
@@ -3172,14 +3202,20 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
 
        /* = */
        case TO_STEQL:
-               if (te->flags & TEF_DBRACKET)
-                       return (gmatchx(opnd1, opnd2, false));
+               if (te->flags & TEF_DBRACKET) {
+                       if ((i = gmatchx(opnd1, opnd2, false)))
+                               record_match(opnd1);
+                       return (i);
+               }
                return (strcmp(opnd1, opnd2) == 0);
 
        /* != */
        case TO_STNEQ:
-               if (te->flags & TEF_DBRACKET)
-                       return (!gmatchx(opnd1, opnd2, false));
+               if (te->flags & TEF_DBRACKET) {
+                       if ((i = gmatchx(opnd1, opnd2, false)))
+                               record_match(opnd1);
+                       return (!i);
+               }
                return (strcmp(opnd1, opnd2) != 0);
 
        /* < */
@@ -3336,7 +3372,7 @@ test_primary(Test_env *te, bool do_eval)
                        /* unary expression */
                        opnd1 = (*te->getopnd)(te, op, do_eval);
                        if (!opnd1) {
-                               (*te->error)(te, -1, "missing argument");
+                               (*te->error)(te, -1, Tno_args);
                                return (0);
                        }
 
@@ -3412,9 +3448,9 @@ ptest_error(Test_env *te, int ofs, const char *msg)
 
        te->flags |= TEF_ERROR;
        if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
-               bi_errorf("%s: %s", op, msg);
+               bi_errorf(Tf_sD_s, op, msg);
        else
-               bi_errorf("%s", msg);
+               bi_errorf(Tf_s, msg);
 }
 
 #ifndef MKSH_NO_LIMITS
@@ -3542,7 +3578,7 @@ c_ulimit(const char **wp)
  found:
        if (wp[builtin_opt.optind]) {
                if (all || wp[builtin_opt.optind + 1]) {
-                       bi_errorf("too many arguments");
+                       bi_errorf(Ttoo_many_args);
                        return (1);
                }
                return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
@@ -3643,7 +3679,7 @@ c_rename(const char **wp)
                bi_errorf(Tsynerr);
        else if ((rv = rename(wp[0], wp[1])) != 0) {
                rv = errno;
-               bi_errorf("%s: %s", "failed", cstrerror(rv));
+               bi_errorf(Tf_sD_s, "failed", cstrerror(rv));
        }
 
        return (rv);
@@ -3666,11 +3702,11 @@ c_realpath(const char **wp)
                bi_errorf(Tsynerr);
        else if ((buf = do_realpath(wp[0])) == NULL) {
                rv = errno;
-               bi_errorf("%s: %s", wp[0], cstrerror(rv));
+               bi_errorf(Tf_sD_s, wp[0], cstrerror(rv));
                if ((unsigned int)rv > 255)
                        rv = 255;
        } else {
-               shprintf("%s\n", buf);
+               shprintf(Tf_sN, buf);
                afree(buf, ATEMP);
                rv = 0;
        }
@@ -3716,7 +3752,7 @@ c_cat(const char **wp)
                        if (ksh_isdash(fn))
                                fd = STDIN_FILENO;
                        else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
-                               bi_errorf("%s: %s", fn, cstrerror(errno));
+                               bi_errorf(Tf_sD_s, fn, cstrerror(errno));
                                rv = 1;
                                continue;
                        }
@@ -3733,7 +3769,7 @@ c_cat(const char **wp)
                                        continue;
                                }
                                /* an error occured during reading */
-                               bi_errorf("%s: %s", fn, cstrerror(errno));
+                               bi_errorf(Tf_sD_s, fn, cstrerror(errno));
                                rv = 1;
                                break;
                        } else if (n == 0)
@@ -3758,7 +3794,7 @@ c_cat(const char **wp)
                                        rv = ksh_sigmask(SIGPIPE);
                                } else {
                                        /* an error occured during writing */
-                                       bi_errorf("%s: %s", "<stdout>",
+                                       bi_errorf(Tf_sD_s, "<stdout>",
                                            cstrerror(errno));
                                        rv = 1;
                                }
@@ -3793,7 +3829,7 @@ c_sleep(const char **wp)
        if (!wp[0] || wp[1])
                bi_errorf(Tsynerr);
        else if (parse_usec(wp[0], &tv))
-               bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
+               bi_errorf(Tf_sD_s_qs, Tsynerr, cstrerror(errno), wp[0]);
        else {
 #ifndef MKSH_NOPROSPECTOFWORK
                sigset_t omask, bmask;
@@ -3823,7 +3859,7 @@ c_sleep(const char **wp)
                         */
                        rv = 0;
                else
-                       bi_errorf("%s: %s", Tselect, cstrerror(errno));
+                       bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
 #ifndef MKSH_NOPROSPECTOFWORK
                /* this will re-schedule signal delivery */
                sigprocmask(SIG_SETMASK, &omask, NULL);
@@ -3838,7 +3874,7 @@ static int
 c_suspend(const char **wp)
 {
        if (wp[1] != NULL) {
-               bi_errorf("too many arguments");
+               bi_errorf(Ttoo_many_args);
                return (1);
        }
        if (Flag(FLOGIN)) {
index 57f2124..c0f87f8 100644 (file)
@@ -27,7 +27,7 @@
 #include <sys/file.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.152 2016/01/14 23:18:08 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.158 2016/08/04 20:31:00 tg Exp $");
 
 Trap sigtraps[ksh_NSIG + 1];
 static struct sigaction Sigact_ign;
@@ -69,12 +69,6 @@ static int histfd = -1;
 static off_t histfsize;
 #endif
 
-static const char Tnot_in_history[] = "not in history";
-#define Thistory (Tnot_in_history + 7)
-
-static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
-#define Tspdollaru (TFCEDIT_dollaru + 18)
-
 /* HISTSIZE default: size of saved history, persistent or standard */
 #ifdef MKSH_SMALL
 #define MKSH_DEFHISTSIZE       255
@@ -153,7 +147,7 @@ c_fc(const char **wp)
                        else if (!last)
                                last = p;
                        else {
-                               bi_errorf("too many arguments");
+                               bi_errorf(Ttoo_many_args);
                                return (1);
                        }
                        break;
@@ -183,7 +177,7 @@ c_fc(const char **wp)
                if (!first && (first = *wp))
                        wp++;
                if (last || *wp) {
-                       bi_errorf("too many arguments");
+                       bi_errorf(Ttoo_many_args);
                        return (1);
                }
 
@@ -217,7 +211,7 @@ c_fc(const char **wp)
                                xp += rep_len;
                        }
                        if (!any_subst) {
-                               bi_errorf("bad substitution");
+                               bi_errorf(Tbadsubst);
                                return (1);
                        }
                        len = strlen(s) + 1;
@@ -239,7 +233,7 @@ c_fc(const char **wp)
        if (!last && (last = *wp))
                wp++;
        if (*wp) {
-               bi_errorf("too many arguments");
+               bi_errorf(Ttoo_many_args);
                return (1);
        }
        if (!first) {
@@ -278,7 +272,7 @@ c_fc(const char **wp)
                for (hp = rflag ? hlast : hfirst;
                    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
                        if (!nflag)
-                               shf_fprintf(shl_stdout, "%lu",
+                               shf_fprintf(shl_stdout, Tf_lu,
                                    (unsigned long)hist_source->line -
                                    (unsigned long)(histptr - hp));
                        shf_putc('\t', shl_stdout);
@@ -290,7 +284,7 @@ c_fc(const char **wp)
                                *t++ = '\n';
                                s = t;
                        }
-                       shf_fprintf(shl_stdout, "%s\n", s);
+                       shf_fprintf(shl_stdout, Tf_sN, s);
                }
                shf_flush(shl_stdout);
                return (0);
@@ -300,32 +294,22 @@ c_fc(const char **wp)
 
        tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
        if (!(shf = tf->shf)) {
-               bi_errorf("can't %s temporary file %s: %s",
-                   "create", tf->tffn, cstrerror(errno));
+               bi_errorf(Tf_temp, Tcreate, tf->tffn, cstrerror(errno));
                return (1);
        }
        for (hp = rflag ? hlast : hfirst;
            hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
-               shf_fprintf(shf, "%s\n", *hp);
+               shf_fprintf(shf, Tf_sN, *hp);
        if (shf_close(shf) == -1) {
-               bi_errorf("can't %s temporary file %s: %s",
-                   "write", tf->tffn, cstrerror(errno));
+               bi_errorf(Tf_temp, Twrite, tf->tffn, cstrerror(errno));
                return (1);
        }
 
        /* Ignore setstr errors here (arbitrary) */
        setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
 
-       /* XXX: source should not get trashed by this.. */
-       {
-               Source *sold = source;
-               int ret;
-
-               ret = command(editor ? editor : TFCEDIT_dollaru, 0);
-               source = sold;
-               if (ret)
-                       return (ret);
-       }
+       if ((optc = command(editor ? editor : TFCEDIT_dollaru, 0)))
+               return (optc);
 
        {
                struct stat statb;
@@ -334,16 +318,15 @@ c_fc(const char **wp)
                ssize_t n;
 
                if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
-                       bi_errorf("can't %s temporary file %s: %s",
-                           "open", tf->tffn, cstrerror(errno));
+                       bi_errorf(Tf_temp, Topen, tf->tffn, cstrerror(errno));
                        return (1);
                }
 
                if (stat(tf->tffn, &statb) < 0)
                        n = 128;
                else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
-                       bi_errorf("%s %s too large: %lu", Thistory,
-                           "file", (unsigned long)statb.st_size);
+                       bi_errorf(Tf_toolarge, Thistory,
+                           Tfile, (unsigned long)statb.st_size);
                        goto errout;
                } else
                        n = (size_t)statb.st_size + 1;
@@ -354,8 +337,8 @@ c_fc(const char **wp)
                                XcheckN(xs, xp, Xlength(xs, xp));
                }
                if (n < 0) {
-                       bi_errorf("can't %s temporary file %s: %s",
-                           "read", tf->tffn, cstrerror(shf_errno(shf)));
+                       bi_errorf(Tf_temp, Tread, tf->tffn,
+                           cstrerror(shf_errno(shf)));
  errout:
                        shf_close(shf);
                        return (1);
@@ -372,8 +355,6 @@ static int
 hist_execute(char *cmd, Area *areap)
 {
        static int last_line = -1;
-       Source *sold;
-       int ret;
 
        /* Back up over last histsave */
        if (histptr >= history && last_line != hist_source->line) {
@@ -388,7 +369,7 @@ hist_execute(char *cmd, Area *areap)
        afree(cmd, areap);
        cmd = *histptr;
        /* pdksh says POSIX doesn’t say this is done, testsuite needs it */
-       shellf("%s\n", cmd);
+       shellf(Tf_sN, cmd);
 
        /*-
         * Commands are executed here instead of pushing them onto the
@@ -397,11 +378,7 @@ hist_execute(char *cmd, Area *areap)
         *      X=y fc -e - 42 2> /dev/null
         * are to effect the repeated commands environment.
         */
-       /* XXX: source should not get trashed by this.. */
-       sold = source;
-       ret = command(cmd, 0);
-       source = sold;
-       return (ret);
+       return (command(cmd, 0));
 }
 
 /*
@@ -420,18 +397,18 @@ hist_get(const char *str, bool approx, bool allow_cur)
                        if (approx)
                                hp = hist_get_oldest();
                        else {
-                               bi_errorf("%s: %s", str, Tnot_in_history);
+                               bi_errorf(Tf_sD_s, str, Tnot_in_history);
                                hp = NULL;
                        }
                } else if ((size_t)hp > (size_t)histptr) {
                        if (approx)
                                hp = hist_get_newest(allow_cur);
                        else {
-                               bi_errorf("%s: %s", str, Tnot_in_history);
+                               bi_errorf(Tf_sD_s, str, Tnot_in_history);
                                hp = NULL;
                        }
                } else if (!allow_cur && hp == histptr) {
-                       bi_errorf("%s: %s", str, "invalid range");
+                       bi_errorf(Tf_sD_s, str, "invalid range");
                        hp = NULL;
                }
        } else {
@@ -439,7 +416,7 @@ hist_get(const char *str, bool approx, bool allow_cur)
 
                /* the -1 is to avoid the current fc command */
                if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
-                       bi_errorf("%s: %s", str, Tnot_in_history);
+                       bi_errorf(Tf_sD_s, str, Tnot_in_history);
                else
                        hp = &history[n];
        }
@@ -658,7 +635,9 @@ histsave(int *lnp, const char *cmd, int svmode, bool ignoredups)
        strndupx(c, cmd, ccp - cmd, APERM);
 
        if (svmode != HIST_APPEND) {
-               if (ignoredups && !strcmp(c, *histptr)
+               if (ignoredups &&
+                   histptr >= history &&
+                   !strcmp(c, *histptr)
 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
                    && !histsync()
 #endif
@@ -850,7 +829,7 @@ hist_init(Source *s)
                        goto retry;
                }
                if (hs != hist_init_retry)
-                       bi_errorf("can't %s %s: %s",
+                       bi_errorf(Tf_cant,
                            "unlink HISTFILE", hname, cstrerror(errno));
                histfsize = 0;
                return;
@@ -1015,23 +994,26 @@ inittraps(void)
 {
        int i;
        const char *cs;
+#if !HAVE_SYS_SIGNAME
+       const struct mksh_sigpair *pair;
+#endif
 
        trap_exstat = -1;
 
-       /* Populate sigtraps based on sys_signame and sys_siglist. */
+       /* populate sigtraps based on sys_signame and sys_siglist */
        for (i = 1; i < ksh_NSIG; i++) {
                sigtraps[i].signal = i;
 #if HAVE_SYS_SIGNAME
                cs = sys_signame[i];
 #else
-               const struct mksh_sigpair *pair = mksh_sigpairs;
+               pair = mksh_sigpairs;
                while ((pair->nr != i) && (pair->name != NULL))
                        ++pair;
                cs = pair->name;
 #endif
                if ((cs == NULL) ||
                    (cs[0] == '\0'))
-                       sigtraps[i].name = shf_smprintf("%d", i);
+                       sigtraps[i].name = null;
                else {
                        char *s;
 
@@ -1047,7 +1029,18 @@ inittraps(void)
                        sigtraps[i].name = s;
                        while ((*s = ksh_toupper(*s)))
                                ++s;
+                       /* check for reserved names */
+                       if (!strcmp(sigtraps[i].name, "EXIT") ||
+                           !strcmp(sigtraps[i].name, "ERR")) {
+#ifndef MKSH_SMALL
+                               internal_warningf("ignoring invalid signal name %s",
+                                   sigtraps[i].name);
+#endif
+                               sigtraps[i].name = null;
+                       }
                }
+               if (sigtraps[i].name == null)
+                       sigtraps[i].name = shf_smprintf(Tf_d, i);
 #if HAVE_SYS_SIGLIST
                sigtraps[i].mess = sys_siglist[i];
 #elif HAVE_STRSIGNAL
@@ -1057,7 +1050,7 @@ inittraps(void)
 #endif
                if ((sigtraps[i].mess == NULL) ||
                    (sigtraps[i].mess[0] == '\0'))
-                       sigtraps[i].mess = shf_smprintf("%s %d",
+                       sigtraps[i].mess = shf_smprintf(Tf_sd,
                            "Signal", i);
        }
        sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT;
index 0cecdc5..0366004 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.117 2016/01/14 23:18:09 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.121 2016/07/25 00:04:44 tg Exp $");
 
 #if HAVE_KILLPG
 #define mksh_killpg            killpg
@@ -45,8 +45,8 @@ struct proc {
        int state;
        int status;             /* wait status */
        /* process command string from vistree */
-       char command[256 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) +
-           2 * sizeof(int))];
+       char command[256 - (ALLOC_OVERHEAD + sizeof(Proc *) +
+           sizeof(pid_t) + 2 * sizeof(int))];
 };
 
 /* Notify/print flag - j_print() argument */
@@ -238,11 +238,11 @@ j_suspend(void)
                        mksh_tcset(tty_fd, &tty_state);
                if (restore_ttypgrp >= 0) {
                        if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) {
-                               warningf(false, "%s: %s %s: %s", "j_suspend",
-                                   "tcsetpgrp", "failed", cstrerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "tcsetpgrp", cstrerror(errno));
                        } else if (setpgid(0, restore_ttypgrp) < 0) {
-                               warningf(false, "%s: %s %s: %s", "j_suspend",
-                                   "setpgid", "failed", cstrerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "setpgid", cstrerror(errno));
                        }
                }
        }
@@ -259,12 +259,12 @@ j_suspend(void)
        if (ttypgrp_ok) {
                if (restore_ttypgrp >= 0) {
                        if (setpgid(0, kshpid) < 0) {
-                               warningf(false, "%s: %s %s: %s", "j_suspend",
-                                   "setpgid", "failed", cstrerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "setpgid", cstrerror(errno));
                                ttypgrp_ok = false;
                        } else if (tcsetpgrp(tty_fd, kshpid) < 0) {
-                               warningf(false, "%s: %s %s: %s", "j_suspend",
-                                   "tcsetpgrp", "failed", cstrerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "tcsetpgrp", cstrerror(errno));
                                ttypgrp_ok = false;
                        }
                }
@@ -349,8 +349,8 @@ j_change(void)
                                pid_t ttypgrp;
 
                                if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
-                                       warningf(false, "%s: %s %s: %s",
-                                           "j_init", "tcgetpgrp", "failed",
+                                       warningf(false, Tf_ssfaileds,
+                                           "j_init", "tcgetpgrp",
                                            cstrerror(errno));
                                        ttypgrp_ok = false;
                                        break;
@@ -365,13 +365,13 @@ j_change(void)
                            SS_RESTORE_DFL|SS_FORCE);
                if (ttypgrp_ok && kshpgrp != kshpid) {
                        if (setpgid(0, kshpid) < 0) {
-                               warningf(false, "%s: %s %s: %s", "j_init",
-                                   "setpgid", "failed", cstrerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   "j_init", "setpgid", cstrerror(errno));
                                ttypgrp_ok = false;
                        } else {
                                if (tcsetpgrp(tty_fd, kshpid) < 0) {
-                                       warningf(false, "%s: %s %s: %s",
-                                           "j_init", "tcsetpgrp", "failed",
+                                       warningf(false, Tf_ssfaileds,
+                                           "j_init", "tcsetpgrp",
                                            cstrerror(errno));
                                        ttypgrp_ok = false;
                                } else
@@ -381,7 +381,7 @@ j_change(void)
                }
 #ifndef MKSH_DISABLE_TTY_WARNING
                if (use_tty && !ttypgrp_ok)
-                       warningf(false, "%s: %s", "warning",
+                       warningf(false, Tf_sD_s, "warning",
                            "won't have full job control");
 #endif
        } else {
@@ -415,7 +415,7 @@ ksh_nice(int ness)
        errno = 0;
        /* this is gonna annoy users; complain to your distro, people! */
        if (nice(ness) == -1 && (eno = errno) != 0)
-               warningf(false, "%s: %s", "bgnice", cstrerror(eno));
+               warningf(false, Tf_sD_s, "bgnice", cstrerror(eno));
 #else
        (void)nice(ness);
 #endif
@@ -467,8 +467,7 @@ exchild(struct op *t, int flags,
        if (flags & XPIPEI) {
                /* continuing with a pipe */
                if (!last_job)
-                       internal_errorf("%s %d",
-                           "exchild: XPIPEI and no last_job - pid",
+                       internal_errorf("exchild: XPIPEI and no last_job - pid %d",
                            (int)procpid);
                j = last_job;
                if (last_proc)
@@ -601,7 +600,7 @@ exchild(struct op *t, int flags,
 #ifndef MKSH_SMALL
                if (t->type == TPIPE)
                        unwind(LLEAVE);
-               internal_warningf("%s: %s", "exchild", "execute() returned");
+               internal_warningf("%s: execute() returned", "exchild");
                fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
                    "exchild", t);
                shf_flush(shl_out);
@@ -626,7 +625,7 @@ exchild(struct op *t, int flags,
                        if (Flag(FTALKING)) {
                                shf_fprintf(shl_out, "[%d]", j->job);
                                for (p = j->proc_list; p; p = p->next)
-                                       shf_fprintf(shl_out, " %d",
+                                       shf_fprintf(shl_out, Tf__d,
                                            (int)p->pid);
                                shf_putchar('\n', shl_out);
                                shf_flush(shl_out);
@@ -678,9 +677,9 @@ waitlast(void)
        j = last_job;
        if (!j || !(j->flags & JF_STARTED)) {
                if (!j)
-                       warningf(true, "%s: %s", "waitlast", "no last job");
+                       warningf(true, Tf_sD_s, "waitlast", "no last job");
                else
-                       internal_warningf("%s: %s", "waitlast", "not started");
+                       internal_warningf(Tf_sD_s, "waitlast", Tnot_started);
 #ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
@@ -740,7 +739,7 @@ waitfor(const char *cp, int *sigp)
                sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
                if (ecode != JL_NOSUCH)
-                       bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+                       bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (-1);
        }
 
@@ -774,14 +773,14 @@ j_kill(const char *cp, int sig)
 #ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
-               bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+               bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (1);
        }
 
        if (j->pgrp == 0) {
                /* started when !Flag(FMONITOR) */
                if (kill_job(j, sig) < 0) {
-                       bi_errorf("%s: %s", cp, cstrerror(errno));
+                       bi_errorf(Tf_sD_s, cp, cstrerror(errno));
                        rv = 1;
                }
        } else {
@@ -790,7 +789,7 @@ j_kill(const char *cp, int sig)
                        mksh_killpg(j->pgrp, SIGCONT);
 #endif
                if (mksh_killpg(j->pgrp, sig) < 0) {
-                       bi_errorf("%s: %s", cp, cstrerror(errno));
+                       bi_errorf(Tf_sD_s, cp, cstrerror(errno));
                        rv = 1;
                }
        }
@@ -817,7 +816,7 @@ j_resume(const char *cp, int bg)
 
        if ((j = j_lookup(cp, &ecode)) == NULL) {
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+               bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (1);
        }
 
@@ -862,10 +861,10 @@ j_resume(const char *cp, int bg)
                                if (j->flags & JF_SAVEDTTY)
                                        mksh_tcset(tty_fd, &tty_state);
                                sigprocmask(SIG_SETMASK, &omask, NULL);
-                               bi_errorf("%s %s(%d, %ld) %s: %s",
-                                   "1st", "tcsetpgrp", tty_fd,
+                               bi_errorf(Tf_ldfailed,
+                                   "fg: 1st", "tcsetpgrp", tty_fd,
                                    (long)((j->flags & JF_SAVEDTTYPGRP) ?
-                                   j->saved_ttypgrp : j->pgrp), "failed",
+                                   j->saved_ttypgrp : j->pgrp),
                                    cstrerror(rv));
                                return (1);
                        }
@@ -884,12 +883,12 @@ j_resume(const char *cp, int bg)
                        if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
                                mksh_tcset(tty_fd, &tty_state);
                        if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
-                               warningf(true, "%s %s(%d, %ld) %s: %s",
+                               warningf(true, Tf_ldfailed,
                                    "fg: 2nd", "tcsetpgrp", tty_fd,
-                                   (long)kshpgrp, "failed", cstrerror(errno));
+                                   (long)kshpgrp, cstrerror(errno));
                }
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               bi_errorf("%s %s: %s", "can't continue job",
+               bi_errorf(Tf_s_sD_s, "can't continue job",
                    cp, cstrerror(eno));
                return (1);
        }
@@ -958,7 +957,7 @@ j_jobs(const char *cp, int slp,
 #ifndef MKSH_NOPROSPECTOFWORK
                        sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
-                       bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+                       bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                        return (1);
                }
        } else
@@ -978,7 +977,7 @@ j_jobs(const char *cp, int slp,
        for (j = job_list; j; j = tmp) {
                tmp = j->next;
                if (j->flags & JF_REMOVE)
-                       remove_job(j, "jobs");
+                       remove_job(j, Tjobs);
        }
 #ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
@@ -1052,7 +1051,7 @@ j_set_async(Job *j)
        if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
                remove_job(async_job, "async");
        if (!(j->flags & JF_STARTED)) {
-               internal_warningf("%s: %s", "j_async", "job not started");
+               internal_warningf(Tf_sD_s, "j_async", Tjob_not_started);
                return;
        }
        async_job = j;
@@ -1176,9 +1175,9 @@ j_waitj(Job *j,
                            (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
                                j->flags |= JF_SAVEDTTYPGRP;
                        if (tcsetpgrp(tty_fd, kshpgrp) < 0)
-                               warningf(true, "%s %s(%d, %ld) %s: %s",
+                               warningf(true, Tf_ldfailed,
                                    "j_waitj:", "tcsetpgrp", tty_fd,
-                                   (long)kshpgrp, "failed", cstrerror(errno));
+                                   (long)kshpgrp, cstrerror(errno));
                        if (j->state == PSTOPPED) {
                                j->flags |= JF_SAVEDTTY;
                                mksh_tcget(tty_fd, &j->ttystat);
@@ -1426,8 +1425,8 @@ check_job(Job *j)
 
        /* XXX debugging (nasty - interrupt routine using shl_out) */
        if (!(j->flags & JF_STARTED)) {
-               internal_warningf("check_job: job started (flags 0x%x)",
-                   j->flags);
+               internal_warningf("check_job: job started (flags 0x%X)",
+                   (unsigned int)j->flags);
                return;
        }
 
@@ -1539,7 +1538,7 @@ j_print(Job *j, int how, struct shf *shf)
                 * group leader (ie, !FMONITOR). We arbitrarily return
                 * last pid (which is what $! returns).
                 */
-               shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
+               shf_fprintf(shf, Tf_dN, (int)(j->pgrp ? j->pgrp :
                    (j->last_proc ? j->last_proc->pid : 0)));
                return;
        }
@@ -1627,10 +1626,10 @@ j_print(Job *j, int how, struct shf *shf)
                while (p && p->state == state && p->status == status) {
                        if (how == JP_LONG)
                                shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
-                                   (int)p->pid, " ", p->command,
+                                   (int)p->pid, T1space, p->command,
                                    p->next ? "|" : null);
                        else if (how == JP_MEDIUM)
-                               shf_fprintf(shf, " %s%s", p->command,
+                               shf_fprintf(shf, Tf__ss, p->command,
                                    p->next ? "|" : null);
                        p = p->next;
                }
@@ -1804,7 +1803,7 @@ remove_job(Job *j, const char *where)
                curr = *prev;
        }
        if (curr != j) {
-               internal_warningf("remove_job: job %s (%s)", "not found", where);
+               internal_warningf("remove_job: job %s (%s)", Tnot_found, where);
                return;
        }
        *prev = curr->next;
@@ -1893,22 +1892,22 @@ tty_init_talking(void)
                break;
        case 1:
 #ifndef MKSH_DISABLE_TTY_WARNING
-               warningf(false, "%s: %s %s: %s",
-                   "No controlling tty", "open", "/dev/tty",
-                   cstrerror(errno));
+               warningf(false, Tf_sD_s_sD_s,
+                   "No controlling tty", Topen, T_devtty, cstrerror(errno));
 #endif
                break;
        case 2:
 #ifndef MKSH_DISABLE_TTY_WARNING
-               warningf(false, "%s: %s", "can't find tty fd", cstrerror(errno));
+               warningf(false, Tf_sD_s_s, Tcant_find, Ttty_fd,
+                   cstrerror(errno));
 #endif
                break;
        case 3:
-               warningf(false, "%s: %s %s: %s", "j_ttyinit",
-                   "dup of tty fd", "failed", cstrerror(errno));
+               warningf(false, Tf_ssfaileds, "j_ttyinit",
+                   Ttty_fd_dupof, cstrerror(errno));
                break;
        case 4:
-               warningf(false, "%s: %s: %s", "j_ttyinit",
+               warningf(false, Tf_sD_sD_s, "j_ttyinit",
                    "can't set close-on-exec flag", cstrerror(errno));
                break;
        }
index 4eca6a5..0aff3aa 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009, 2010, 2011, 2013, 2014
+ * Copyright (c) 2009, 2010, 2011, 2013, 2014, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
  */
 
 #include "sh.h"
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+#include <err.h>
+#endif
 
-__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.23 2015/11/29 17:05:01 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.26 2016/02/26 21:53:36 tg Exp $");
 
 /* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */
 #if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0)
@@ -29,19 +32,72 @@ __RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.23 2015/11/29 17:05:01 tg Exp $");
 #define remalloc(p,n)  realloc_osi((p), (n))
 #endif
 
-#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % ALLOC_SIZE)
 
-static ALLOC_ITEM *findptr(ALLOC_ITEM **, char *, Area *);
+static struct lalloc_common *findptr(struct lalloc_common **, char *, Area *);
+
+#ifndef MKSH_ALLOC_CATCH_UNDERRUNS
+#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % sizeof(struct lalloc_common))
+#else
+#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) & 4095)
+#undef remalloc
+#undef free_osimalloc
+
+static void
+free_osimalloc(void *ptr)
+{
+       struct lalloc_item *lp = ptr;
+
+       if (munmap(lp, lp->len))
+               err(1, "free_osimalloc");
+}
+
+static void *
+remalloc(void *ptr, size_t size)
+{
+       struct lalloc_item *lp, *lold = ptr;
+
+       size = (size + 4095) & ~(size_t)4095;
+
+       if (lold && lold->len >= size)
+               return (ptr);
+
+       if ((lp = mmap(NULL, size, PROT_READ | PROT_WRITE,
+           MAP_ANON | MAP_PRIVATE, -1, (off_t)0)) == MAP_FAILED)
+               err(1, "remalloc: mmap(%zu)", size);
+       if (ALLOC_ISUNALIGNED(lp))
+               errx(1, "remalloc: unaligned(%p)", lp);
+       if (mprotect(((char *)lp) + 4096, 4096, PROT_NONE))
+               err(1, "remalloc: mprotect");
+       lp->len = size;
+
+       if (lold) {
+               memcpy(((char *)lp) + 8192, ((char *)lold) + 8192,
+                   lold->len - 8192);
+               if (munmap(lold, lold->len))
+                       err(1, "remalloc: munmap");
+       }
+
+       return (lp);
+}
+#endif
 
 void
 ainit(Area *ap)
 {
-       /* area pointer is an ALLOC_ITEM, just the head of the list */
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+       if (sysconf(_SC_PAGESIZE) != 4096) {
+               fprintf(stderr, "mksh: fatal: pagesize %lu not 4096!\n",
+                   sysconf(_SC_PAGESIZE));
+               fflush(stderr);
+               abort();
+       }
+#endif
+       /* area pointer and items share struct lalloc_common */
        ap->next = NULL;
 }
 
-static ALLOC_ITEM *
-findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap)
+static struct lalloc_common *
+findptr(struct lalloc_common **lpp, char *ptr, Area *ap)
 {
        void *lp;
 
@@ -51,10 +107,10 @@ findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap)
 #endif
        /* get address of ALLOC_ITEM from user item */
        /*
-        * note: the alignment of "ptr" to ALLOC_SIZE is checked
+        * note: the alignment of "ptr" to ALLOC_ITEM is checked
         * above; the "void *" gets us rid of a gcc 2.95 warning
         */
-       *lpp = (lp = ptr - ALLOC_SIZE);
+       *lpp = (lp = ptr - sizeof(ALLOC_ITEM));
        /* search for allocation item in group list */
        while (ap->next != lp)
                if ((ap = ap->next) == NULL) {
@@ -84,35 +140,35 @@ aresize2(void *ptr, size_t fac1, size_t fac2, Area *ap)
 void *
 aresize(void *ptr, size_t numb, Area *ap)
 {
-       ALLOC_ITEM *lp = NULL;
+       struct lalloc_common *lp = NULL;
 
        /* resizing (true) or newly allocating? */
        if (ptr != NULL) {
-               ALLOC_ITEM *pp;
+               struct lalloc_common *pp;
 
                pp = findptr(&lp, ptr, ap);
                pp->next = lp->next;
        }
 
-       if (notoktoadd(numb, ALLOC_SIZE) ||
-           (lp = remalloc(lp, numb + ALLOC_SIZE)) == NULL
+       if (notoktoadd(numb, sizeof(ALLOC_ITEM)) ||
+           (lp = remalloc(lp, numb + sizeof(ALLOC_ITEM))) == NULL
 #ifndef MKSH_SMALL
            || ALLOC_ISUNALIGNED(lp)
 #endif
            )
                internal_errorf(Toomem, numb);
-       /* this only works because Area is an ALLOC_ITEM */
+       /* area pointer and items share struct lalloc_common */
        lp->next = ap->next;
        ap->next = lp;
        /* return user item address */
-       return ((char *)lp + ALLOC_SIZE);
+       return ((char *)lp + sizeof(ALLOC_ITEM));
 }
 
 void
 afree(void *ptr, Area *ap)
 {
        if (ptr != NULL) {
-               ALLOC_ITEM *lp, *pp;
+               struct lalloc_common *lp, *pp;
 
                pp = findptr(&lp, ptr, ap);
                /* unhook */
@@ -125,7 +181,7 @@ afree(void *ptr, Area *ap)
 void
 afreeall(Area *ap)
 {
-       ALLOC_ITEM *lp;
+       struct lalloc_common *lp;
 
        /* traverse group (linked list) */
        while ((lp = ap->next) != NULL) {
index e5305ae..741bb93 100644 (file)
--- a/src/lex.c
+++ b/src/lex.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.218 2016/01/20 21:34:12 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.228 2016/08/01 21:38:03 tg Exp $");
 
 /*
  * states while lexing word
@@ -462,10 +462,12 @@ yylex(int cf)
                                                        break;
                                                }
                                        } else if (c == '/') {
+                                               c2 = ADELIM;
+ parse_adelim_slash:
                                                *wp++ = CHAR;
                                                *wp++ = c;
                                                if ((c = getsc()) == '/') {
-                                                       *wp++ = ADELIM;
+                                                       *wp++ = c2;
                                                        *wp++ = c;
                                                } else
                                                        ungetsc(c);
@@ -475,6 +477,13 @@ yylex(int cf)
                                                statep->ls_adelim.num = 1;
                                                statep->nparen = 0;
                                                break;
+                                       } else if (c == '@') {
+                                               c2 = getsc();
+                                               ungetsc(c2);
+                                               if (c2 == '/') {
+                                                       c2 = CHAR;
+                                                       goto parse_adelim_slash;
+                                               }
                                        }
                                        /*
                                         * If this is a trim operation,
@@ -526,33 +535,13 @@ yylex(int cf)
                                *wp++ = COMSUB;
                                /*
                                 * We need to know whether we are within double
-                                * quotes, since most shells translate \" to "
-                                * within "…`…\"…`…". This is not done in POSIX
-                                * mode (§2.2.3 Double-Quotes: “The backquote
-                                * shall retain its special meaning introducing
-                                * the other form of command substitution (see
-                                * Command Substitution). The portion of the
-                                * quoted string from the initial backquote and
-                                * the characters up to the next backquote that
-                                * is not preceded by a <backslash>, having
-                                * escape characters removed, defines that
-                                * command whose output replaces "`...`" when
-                                * the word is expanded.”; §2.6.3 Command
-                                * Substitution: “Within the backquoted style
-                                * of command substitution, <backslash> shall
-                                * retain its literal meaning, except when
-                                * followed by: '$', '`', or <backslash>. The
-                                * search for the matching backquote shall be
-                                * satisfied by the first unquoted non-escaped
-                                * backquote; during this search, if a
-                                * non-escaped backquote is encountered[…],
-                                * undefined results occur.”).
+                                * quotes in order to translate \" to " within
+                                * "…`…\"…`…" because, unlike for COMSUBs, the
+                                * outer double quoteing changes the backslash
+                                * meaning for the inside. For more details:
+                                * http://austingroupbugs.net/view.php?id=1015
                                 */
                                statep->ls_bool = false;
-#ifdef austingroupbugs1015_is_still_not_resolved
-                               if (Flag(FPOSIX))
-                                       break;
-#endif
                                s2 = statep;
                                base = state_info.base;
                                while (/* CONSTCOND */ 1) {
@@ -907,20 +896,11 @@ yylex(int cf)
 #ifndef MKSH_LEGACY_MODE
            (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
 #endif
-           c == '<' || c == '>')) {
+           c == '<' || c == '>') && ((c2 = Xlength(ws, wp)) == 0 ||
+           (c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) {
                struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
 
-               if (Xlength(ws, wp) == 0)
-                       iop->unit = c == '<' ? 0 : 1;
-               else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) {
-                       if (dp[c2] != CHAR)
-                               goto no_iop;
-                       if (!ksh_isdigit(dp[c2 + 1]))
-                               goto no_iop;
-                       iop->unit = iop->unit * 10 + ksh_numdig(dp[c2 + 1]);
-                       if (iop->unit >= FDBASE)
-                               goto no_iop;
-               }
+               iop->unit = c2 == 2 ? ksh_numdig(dp[1]) : c == '<' ? 0 : 1;
 
                if (c == '&') {
                        if ((c2 = getsc()) != '>') {
@@ -1000,6 +980,16 @@ yylex(int cf)
                                if (cf & CONTIN)
                                        goto Again;
                        }
+               } else if (c == '\0' && !(cf & HEREDELIM)) {
+                       struct ioword **p = heres;
+
+                       while (p < herep)
+                               if ((*p)->ioflag & IOHERESTR)
+                                       ++p;
+                               else
+                                       /* ksh -c 'cat <<EOF' can cause this */
+                                       yyerror(Tf_heredoc,
+                                           evalstr((*p)->delim, 0));
                }
                return (c);
        }
@@ -1173,7 +1163,7 @@ readhere(struct ioword *iop)
        while (c != '\n') {
                if (!c)
                        /* oops, reached EOF */
-                       yyerror("%s '%s' unclosed\n", "here document", eof);
+                       yyerror(Tf_heredoc, eof);
                /* store character */
                Xcheck(xs, xp);
                Xput(xs, xp, c);
@@ -1273,7 +1263,7 @@ getsc_uu(void)
                                s->start = s->str = "\n";
                                s->type = SEOF;
                        } else {
-                               s->start = s->str = " ";
+                               s->start = s->str = T1space;
                                s->type = SWORDS;
                        }
                        break;
@@ -1359,8 +1349,11 @@ getsc_line(Source *s)
                ksh_tmout_state = TMOUT_READING;
                alarm(ksh_tmout);
        }
-       if (interactive)
+       if (interactive) {
+               if (cur_prompt == PS1)
+                       histsave(&s->line, NULL, HIST_FLUSH, true);
                change_winsz();
+       }
 #ifndef MKSH_NO_CMDLINE_EDITING
        if (have_tty && (
 #if !MKSH_S_NOVI
@@ -1481,7 +1474,7 @@ set_prompt(int to, Source *s)
                                if (*ps1 != '!' || *++ps1 == '!')
                                        shf_putchar(*ps1++, shf);
                                else
-                                       shf_fprintf(shf, "%lu", s ?
+                                       shf_fprintf(shf, Tf_lu, s ?
                                            (unsigned long)s->line + 1 : 0UL);
                        ps1 = shf_sclose(shf);
                        saved_lineno = current_lineno;
@@ -1573,38 +1566,55 @@ get_brace_var(XString *wsp, char *wp)
 {
        char c;
        enum parse_state {
-               PS_INITIAL, PS_SAW_HASH, PS_IDENT,
-               PS_NUMBER, PS_VAR1
+               PS_INITIAL, PS_SAW_PERCENT, PS_SAW_HASH, PS_SAW_BANG,
+               PS_IDENT, PS_NUMBER, PS_VAR1
        } state = PS_INITIAL;
 
        while (/* CONSTCOND */ 1) {
                c = getsc();
                /* State machine to figure out where the variable part ends. */
                switch (state) {
+               case PS_SAW_HASH:
+                       if (ctype(c, C_VAR1)) {
+                               char c2;
+
+                               c2 = getsc();
+                               ungetsc(c2);
+                               if (c2 != /*{*/ '}') {
+                                       ungetsc(c);
+                                       goto out;
+                               }
+                       }
+                       goto ps_common;
+               case PS_SAW_BANG:
+                       switch (c) {
+                       case '@':
+                       case '#':
+                       case '-':
+                       case '?':
+                               goto out;
+                       }
+                       goto ps_common;
                case PS_INITIAL:
-                       if (c == '#' || c == '!' || c == '%') {
+                       switch (c) {
+                       case '%':
+                               state = PS_SAW_PERCENT;
+                               goto next;
+                       case '#':
                                state = PS_SAW_HASH;
-                               break;
+                               goto next;
+                       case '!':
+                               state = PS_SAW_BANG;
+                               goto next;
                        }
                        /* FALLTHROUGH */
-               case PS_SAW_HASH:
+               case PS_SAW_PERCENT:
+ ps_common:
                        if (ksh_isalphx(c))
                                state = PS_IDENT;
                        else if (ksh_isdigit(c))
                                state = PS_NUMBER;
-                       else if (c == '#') {
-                               if (state == PS_SAW_HASH) {
-                                       char c2;
-
-                                       c2 = getsc();
-                                       ungetsc(c2);
-                                       if (c2 != /*{*/ '}') {
-                                               ungetsc(c);
-                                               goto out;
-                                       }
-                               }
-                               state = PS_VAR1;
-                       } else if (ctype(c, C_VAR1))
+                       else if (ctype(c, C_VAR1))
                                state = PS_VAR1;
                        else
                                goto out;
@@ -1627,6 +1637,7 @@ get_brace_var(XString *wsp, char *wp)
                                }
                                goto out;
                        }
+ next:
                        break;
                case PS_NUMBER:
                        if (!ksh_isdigit(c))
index 8b26166..95acfca 100644 (file)
@@ -1,6 +1,6 @@
-.\" $MirOS: src/bin/mksh/lksh.1,v 1.16 2015/12/12 22:25:14 tg Exp $
+.\" $MirOS: src/bin/mksh/lksh.1,v 1.18 2016/08/10 18:20:05 tg Exp $
 .\"-
-.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015
+.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016
 .\"    mirabilos <m@mirbsd.org>
 .\"
 .\" Provided that these terms and disclaimer and all copyright notices
@@ -27,7 +27,9 @@
 .\" * ^ is size-reduced and placed atop in groff, so use \*(ha
 .\" * \(en does not work in nroff, so use \*(en
 .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
-.\" Also make sure to use \& especially with two-letter words.
+.\" Also make sure to use \& *before* a punctuation char that is to not
+.\" be interpreted as punctuation, and especially with two-letter words
+.\" but also (after) a period that does not end a sentence (“e.g.\&”).
 .\" The section after the "doc" macropackage has been loaded contains
 .\" additional code to convene between the UCB mdoc macropackage (and
 .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
@@ -72,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: December 12 2015 $
+.Dd $Mdocdate: August 10 2016 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -239,9 +241,6 @@ 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
-.Nm
-only offers the traditional ten file descriptors to scripts.
-.It
 .\"XXX TODO: move this to FPOSIX
 The rotation arithmetic operators are not available.
 .It
index bd013df..4198c87 100644 (file)
@@ -5,7 +5,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *              2011, 2012, 2013, 2014, 2015
+ *              2011, 2012, 2013, 2014, 2015, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -34,7 +34,7 @@
 #include <locale.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.306 2015/10/09 21:36:57 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.317 2016/08/04 20:51:35 tg Exp $");
 
 extern char **environ;
 
@@ -63,7 +63,7 @@ static const char initsubs[] =
 
 static const char *initcoms[] = {
        Ttypeset, "-r", initvsn, NULL,
-       Ttypeset, "-x", "HOME", "PATH", "SHELL", NULL,
+       Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
        Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
        Talias,
        "integer=\\typeset -i",
@@ -82,12 +82,12 @@ static const char *initcoms[] = {
         /* this is what AT&T ksh seems to track, with the addition of emacs */
        Talias, "-tU",
        Tcat, "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
-       "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
+       "make", "mv", "pr", "rm", "sed", Tsh, "vi", "who", NULL,
        NULL
 };
 
 static const char *restr_com[] = {
-       Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
+       Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
 };
 
 static bool initio_done;
@@ -110,13 +110,13 @@ rndsetup(void)
        } *bufptr;
        char *cp;
 
-       cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM);
+       cp = alloc(sizeof(*bufptr) - sizeof(ALLOC_ITEM), APERM);
 #ifdef DEBUG
        /* clear the allocated space, for valgrind */
-       memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE);
+       memset(cp, 0, sizeof(*bufptr) - sizeof(ALLOC_ITEM));
 #endif
        /* undo what alloc() did to the malloc result address */
-       bufptr = (void *)(cp - ALLOC_SIZE);
+       bufptr = (void *)(cp - sizeof(ALLOC_ITEM));
        /* PIE or something similar provides us with deltas here */
        bufptr->dataptr = &rndsetupstate;
        /* ASLR in at least Windows, Linux, some BSDs */
@@ -130,6 +130,9 @@ rndsetup(void)
        /* introduce variation (and yes, second arg MBZ for portability) */
        mksh_TIME(bufptr->tv);
 
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+       mprotect(((char *)bufptr) + 4096, 4096, PROT_READ | PROT_WRITE);
+#endif
        h = chvt_rndsetup(bufptr, sizeof(*bufptr));
 
        afree(cp, APERM);
@@ -146,7 +149,7 @@ chvt_reinit(void)
 }
 
 static const char *empty_argv[] = {
-       "mksh", NULL
+       Tmksh, NULL
 };
 
 static uint8_t
@@ -205,6 +208,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
 
        /* initialise permanent Area */
        ainit(&aperm);
+       /* max. name length: -2147483648 = 11 (+ NUL) */
+       vtemp = alloc(offsetof(struct tbl, name[0]) + 12, APERM);
 
        /* set up base environment */
        env.type = E_NONE;
@@ -312,7 +317,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
 #endif
                /*
                 * this is uniform across all OSes unless it
-                * breaks somewhere; don't try to optimise,
+                * breaks somewhere hard; don't try to optimise,
                 * e.g. add stuff for Interix or remove /usr
                 * for HURD, because e.g. Debian GNU/HURD is
                 * "keeping a regular /usr"; this is supposed
@@ -328,7 +333,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
         * Set PATH to def_path (will set the path global variable).
         * (import of environment below will probably change this setting).
         */
-       vp = global("PATH");
+       vp = global(TPATH);
        /* setstr can't fail here */
        setstr(vp, def_path, KSH_RETURN_ERROR);
 
@@ -361,11 +366,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
        substitute(initsubs, 0);
 
        /* Figure out the current working directory and set $PWD */
-       vp = global("PWD");
+       vp = global(TPWD);
        cp = str_val(vp);
        /* Try to use existing $PWD if it is valid */
-       set_current_wd((mksh_abspath(cp) && test_eval(NULL, TO_FILEQ, cp, ".",
-           true)) ? cp : NULL);
+       set_current_wd((mksh_abspath(cp) && test_eval(NULL, TO_FILEQ, cp,
+           Tdot, true)) ? cp : NULL);
        if (current_wd[0])
                simplify_path(current_wd);
        /* Only set pwd if we know where we are or if it had a bogus value */
@@ -435,7 +440,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
        } else if (Flag(FCOMMAND)) {
                s = pushs(SSTRINGCMDLINE, ATEMP);
                if (!(s->start = s->str = argv[argi++]))
-                       errorf("%s %s", "-c", "requires an argument");
+                       errorf(Tf_optfoo, "", "", 'c', Treq_arg);
                while (*s->str) {
                        if (*s->str != ' ' && ctype(*s->str, C_QUOTE))
                                break;
@@ -470,7 +475,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
                    SHF_MAPHI | SHF_CLEXEC);
                if (s->u.shf == NULL) {
                        shl_stdout_ok = false;
-                       warningf(true, "%s: %s", s->file, cstrerror(errno));
+                       warningf(true, Tf_sD_s, s->file, cstrerror(errno));
                        /* mandated by SUSv4 */
                        exstat = 127;
                        unwind(LERROR);
@@ -704,7 +709,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
                        unwind(i);
                        /* NOTREACHED */
                default:
-                       internal_errorf("%s %d", "include", i);
+                       internal_errorf("include %d", i);
                        /* NOTREACHED */
                }
        }
@@ -729,12 +734,15 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
 int
 command(const char *comm, int line)
 {
-       Source *s;
+       Source *s, *sold = source;
+       int rv;
 
        s = pushs(SSTRING, ATEMP);
        s->start = s->str = comm;
        s->line = line;
-       return (shell(s, false));
+       rv = shell(s, false);
+       source = sold;
+       return (rv);
 }
 
 /*
@@ -796,7 +804,7 @@ shell(Source * volatile s, volatile bool toplevel)
        default:
                source = old_source;
                quitenv(NULL);
-               internal_errorf("%s %d", "shell", i);
+               internal_errorf("shell %d", i);
                /* NOTREACHED */
        }
        while (/* CONSTCOND */ 1) {
@@ -905,13 +913,6 @@ unwind(int i)
                        /* FALLTHROUGH */
                default:
                        quitenv(NULL);
-                       /*
-                        * quitenv() may have reclaimed the memory
-                        * used by source which will end badly when
-                        * we jump to a function that expects it to
-                        * be valid
-                        */
-                       source = NULL;
                }
        }
 }
@@ -926,9 +927,9 @@ newenv(int type)
         * struct env includes ALLOC_ITEM for alignment constraints
         * so first get the actually used memory, then assign it
         */
-       cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
+       cp = alloc(sizeof(struct env) - sizeof(ALLOC_ITEM), ATEMP);
        /* undo what alloc() did to the malloc result address */
-       ep = (void *)(cp - ALLOC_SIZE);
+       ep = (void *)(cp - sizeof(ALLOC_ITEM));
        /* initialise public members of struct env (not the ALLOC_ITEM) */
        ainit(&ep->area);
        ep->oenv = e;
@@ -1024,7 +1025,7 @@ quitenv(struct shf *shf)
 
        /* free the struct env - tricky due to the ALLOC_ITEM inside */
        cp = (void *)ep;
-       afree(cp + ALLOC_SIZE, ATEMP);
+       afree(cp + sizeof(ALLOC_ITEM), ATEMP);
 }
 
 /* Called after a fork to cleanup stuff left over from parents environment */
@@ -1082,15 +1083,25 @@ reclaim(void)
 
        remove_temps(e->temps);
        e->temps = NULL;
+
+       /*
+        * if the memory backing source is reclaimed, things
+        * will end up badly when a function expecting it to
+        * be valid is run; a NULL pointer is easily debugged
+        */
+       if (source && source->areap == &e->area)
+               source = NULL;
        afreeall(&e->area);
 }
 
 static void
 remove_temps(struct temp *tp)
 {
-       for (; tp != NULL; tp = tp->next)
+       while (tp) {
                if (tp->pid == procpid)
                        unlink(tp->tffn);
+               tp = tp->next;
+       }
 }
 
 /*
@@ -1124,7 +1135,7 @@ tty_init_fd(void)
                goto got_fd;
        }
 #endif
-       if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
+       if ((fd = open(T_devtty, O_RDWR, 0)) >= 0) {
                do_close = true;
                goto got_fd;
        }
@@ -1181,13 +1192,13 @@ vwarningf(unsigned int flags, const char *fmt, va_list ap)
 {
        if (fmt) {
                if (flags & VWARNINGF_INTERNAL)
-                       shf_fprintf(shl_out, "internal error: ");
+                       shf_fprintf(shl_out, Tf_sD_, "internal error");
                if (flags & VWARNINGF_ERRORPREFIX)
                        error_prefix(tobool(flags & VWARNINGF_FILELINE));
                if ((flags & VWARNINGF_BUILTIN) &&
                    /* not set when main() calls parse_args() */
                    builtin_argv0 && builtin_argv0 != kshname)
-                       shf_fprintf(shl_out, "%s: ", builtin_argv0);
+                       shf_fprintf(shl_out, Tf_sD_, builtin_argv0);
                shf_vfprintf(shl_out, fmt, ap);
                shf_putchar('\n', shl_out);
        }
@@ -1296,7 +1307,7 @@ error_prefix(bool fileline)
        /* Avoid foo: foo[2]: ... */
        if (!fileline || !source || !source->file ||
            strcmp(source->file, kshname) != 0)
-               shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
+               shf_fprintf(shl_out, Tf_sD_, kshname + (*kshname == '-'));
        if (fileline && source && source->file != NULL) {
                shf_fprintf(shl_out, "%s[%lu]: ", source->file,
                    (unsigned long)(source->errline ?
@@ -1366,7 +1377,7 @@ initio(void)
        if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
                if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
                        errorf("cannot get home directory");
-               lfp = shf_smprintf("%s/mksh-dbg.txt", lfp);
+               lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
        }
 
        if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
@@ -1466,25 +1477,20 @@ closepipe(int *pv)
 int
 check_fd(const char *name, int mode, const char **emsgp)
 {
-       int fd = 0, fl;
+       int fd, fl;
 
-       if (name[0] == 'p' && !name[1])
+       if (!name[0] || name[1])
+               goto illegal_fd_name;
+       if (name[0] == 'p')
                return (coproc_getfd(mode, emsgp));
-       while (ksh_isdigit(*name)) {
-               fd = fd * 10 + ksh_numdig(*name);
-               if (fd >= FDBASE) {
-                       if (emsgp)
-                               *emsgp = "file descriptor too large";
-                       return (-1);
-               }
-               ++name;
-       }
-       if (*name) {
+       if (!ksh_isdigit(name[0])) {
+ illegal_fd_name:
                if (emsgp)
                        *emsgp = "illegal file descriptor name";
                return (-1);
        }
-       if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
+
+       if ((fl = fcntl((fd = ksh_numdig(name[0])), F_GETFL, 0)) < 0) {
                if (emsgp)
                        *emsgp = "bad file descriptor";
                return (-1);
index 2923e35..1cdbf6b 100644 (file)
@@ -3,7 +3,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *              2011, 2012, 2013, 2014, 2015
+ *              2011, 2012, 2013, 2014, 2015, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -30,7 +30,7 @@
 #include <grp.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.240 2015/10/09 16:11:17 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.245 2016/08/01 18:42:42 tg Exp $");
 
 #define KSH_CHVT_FLAG
 #ifdef MKSH_SMALL
@@ -78,12 +78,12 @@ static int make_path(const char *, const char *, char **, XString *, int *);
 void
 setctypes(const char *s, int t)
 {
-       unsigned int i;
-
        if (t & C_IFS) {
-               for (i = 0; i < UCHAR_MAX + 1; i++)
+               unsigned int i = 0;
+
+               while (++i <= UCHAR_MAX)
                        chtypes[i] &= ~C_IFS;
-               /* include \0 in C_IFS */
+               /* include '\0' in C_IFS */
                chtypes[0] |= C_IFS;
        }
        while (*s != 0)
@@ -354,7 +354,7 @@ change_xtrace(unsigned char newval, bool dosnapshot)
  */
 int
 parse_args(const char **argv,
-    /* OF_CMDLINE or OF_SET */
+    /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
     int what,
     bool *setargsp)
 {
@@ -443,7 +443,8 @@ parse_args(const char **argv,
                        else if ((i != (size_t)-1) && (OFF(i) & what))
                                change_flag((enum sh_flag)i, what, set);
                        else {
-                               bi_errorf("%s: %s", go.optarg, "bad option");
+                               bi_errorf(Tf_sD_s, go.optarg,
+                                   Tunknown_option);
                                return (-1);
                        }
                        break;
@@ -504,7 +505,7 @@ parse_args(const char **argv,
                if (*array)
                        ccp = skip_varname(array, false);
                if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
-                       bi_errorf("%s: %s", array, "is not an identifier");
+                       bi_errorf(Tf_sD_s, array, Tnot_ident);
                        return (-1);
                }
        }
@@ -1038,10 +1039,10 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
                        go->buf[0] = c;
                        go->optarg = go->buf;
                } else {
-                       warningf(true, "%s%s-%c: %s",
+                       warningf(true, Tf_optfoo,
                            (go->flags & GF_NONAME) ? "" : argv[0],
-                           (go->flags & GF_NONAME) ? "" : ": ", c,
-                           "unknown option");
+                           (go->flags & GF_NONAME) ? "" : Tcolsp,
+                           c, Tunknown_option);
                        if (go->flags & GF_ERROR)
                                bi_errorfz();
                }
@@ -1066,10 +1067,10 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
                                go->optarg = go->buf;
                                return (':');
                        }
-                       warningf(true, "%s%s-%c: %s",
+                       warningf(true, Tf_optfoo,
                            (go->flags & GF_NONAME) ? "" : argv[0],
-                           (go->flags & GF_NONAME) ? "" : ": ", c,
-                           "requires an argument");
+                           (go->flags & GF_NONAME) ? "" : Tcolsp,
+                           c, Treq_arg);
                        if (go->flags & GF_ERROR)
                                bi_errorfz();
                        return ('?');
@@ -1291,7 +1292,7 @@ print_columns(struct shf *shf, unsigned int n,
                                shf_puts(str, shf);
                        else
                                shf_fprintf(shf, "%*s%*s",
-                                   max_col, str, nspace, null);
+                                   (int)max_col, str, (int)nspace, null);
                }
                shf_putchar('\n', shf);
        }
@@ -1419,7 +1420,7 @@ do_realpath(const char *upath)
                /* upath is a relative pathname, prepend cwd */
                if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
                        return (NULL);
-               ipath = shf_smprintf("%s%s%s", tp, "/", upath);
+               ipath = shf_smprintf(Tf_sss, tp, "/", upath);
                afree(tp, ATEMP);
        }
 
@@ -1517,7 +1518,7 @@ do_realpath(const char *upath)
                         * otherwise continue with currently resolved prefix
                         */
                        /* append rest of current input path to link target */
-                       tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
+                       tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
                        afree(ipath, ATEMP);
                        ip = ipath = tp;
                        if (!mksh_abspath(ldest)) {
@@ -1819,12 +1820,12 @@ c_cd(const char **wp)
        wp += builtin_opt.optind;
 
        if (Flag(FRESTRICTED)) {
-               bi_errorf("restricted shell - can't cd");
+               bi_errorf(Tcant_cd);
                return (2);
        }
 
-       pwd_s = global("PWD");
-       oldpwd_s = global("OLDPWD");
+       pwd_s = global(TPWD);
+       oldpwd_s = global(TOLDPWD);
 
        if (!wp[0]) {
                /* No arguments - go home */
@@ -1840,7 +1841,7 @@ c_cd(const char **wp)
                        allocd = NULL;
                        dir = str_val(oldpwd_s);
                        if (dir == null) {
-                               bi_errorf("no OLDPWD");
+                               bi_errorf(Tno_OLDPWD);
                                return (2);
                        }
                        printpath = true;
@@ -1861,7 +1862,7 @@ c_cd(const char **wp)
                 * we don't
                 */
                if ((cp = strstr(current_wd, wp[0])) == NULL) {
-                       bi_errorf("bad substitution");
+                       bi_errorf(Tbadsubst);
                        return (2);
                }
                /*-
@@ -1880,7 +1881,7 @@ c_cd(const char **wp)
                memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
                printpath = true;
        } else {
-               bi_errorf("too many arguments");
+               bi_errorf(Ttoo_many_args);
                return (2);
        }
 
@@ -1904,9 +1905,9 @@ c_cd(const char **wp)
 
        if (rv < 0) {
                if (cdnode)
-                       bi_errorf("%s: %s", dir, "bad directory");
+                       bi_errorf(Tf_sD_s, dir, "bad directory");
                else
-                       bi_errorf("%s: %s", tryp, cstrerror(errno));
+                       bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
                afree(allocd, ATEMP);
                Xfree(xs, xp);
                return (2);
@@ -1955,7 +1956,7 @@ c_cd(const char **wp)
                        rv = 1;
        }
        if (printpath || cdnode)
-               shprintf("%s\n", pwd);
+               shprintf(Tf_sN, pwd);
 
        afree(allocd, ATEMP);
        Xfree(xs, xp);
@@ -1990,18 +1991,18 @@ chvt(const Getopt *go)
                                memmove(cp + 1, cp, /* /dev/tty */ 8);
                                dv = cp + 1;
                                if (stat(dv, &sb)) {
-                                       errorf("%s: %s: %s", "chvt",
+                                       errorf(Tf_sD_sD_s, "chvt",
                                            "can't find tty", go->optarg);
                                }
                        }
                }
                if (!(sb.st_mode & S_IFCHR))
-                       errorf("%s: %s: %s", "chvt", "not a char device", dv);
+                       errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
 #ifndef MKSH_DISABLE_REVOKE_WARNING
 #if HAVE_REVOKE
                if (revoke(dv))
 #endif
-                       warningf(false, "%s: %s %s", "chvt",
+                       warningf(false, Tf_sD_s_s, "chvt",
                            "new shell is potentially insecure, can't revoke",
                            dv);
 #endif
@@ -2010,13 +2011,13 @@ chvt(const Getopt *go)
        if ((fd = binopen2(dv, O_RDWR)) < 0) {
                sleep(1);
                if ((fd = binopen2(dv, O_RDWR)) < 0) {
-                       errorf("%s: %s %s", "chvt", "can't open", dv);
+                       errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
                }
        }
        if (go->optarg[0] != '!') {
                switch (fork()) {
                case -1:
-                       errorf("%s: %s %s", "chvt", "fork", "failed");
+                       errorf(Tf_sD_s_s, "chvt", "fork", "failed");
                case 0:
                        break;
                default:
@@ -2024,12 +2025,12 @@ chvt(const Getopt *go)
                }
        }
        if (setsid() == -1)
-               errorf("%s: %s %s", "chvt", "setsid", "failed");
+               errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
        if (go->optarg[0] != '-') {
                if (ioctl(fd, TIOCSCTTY, NULL) == -1)
-                       errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed");
+                       errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
                if (tcflush(fd, TCIOFLUSH))
-                       errorf("%s: %s %s", "chvt", "TCIOFLUSH", "failed");
+                       errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
        }
        ksh_dup2(fd, 0, false);
        ksh_dup2(fd, 1, false);
index 923c721..596fa3c 100644 (file)
@@ -1,4 +1,4 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.388 2016/01/20 22:04:54 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.413 2016/08/10 18:20:05 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,
@@ -29,7 +29,9 @@
 .\" * ^ is size-reduced and placed atop in groff, so use \*(ha
 .\" * \(en does not work in nroff, so use \*(en
 .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
-.\" Also make sure to use \& especially with two-letter words.
+.\" Also make sure to use \& *before* a punctuation char that is to not
+.\" be interpreted as punctuation, and especially with two-letter words
+.\" but also (after) a period that does not end a sentence (“e.g.\&”).
 .\" The section after the "doc" macropackage has been loaded contains
 .\" additional code to convene between the UCB mdoc macropackage (and
 .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
@@ -74,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: January 20 2016 $
+.Dd $Mdocdate: August 10 2016 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -431,7 +433,7 @@ statements;
 .Ql \&(( .. ))
 is used in arithmetic expressions;
 and lastly,
-.Ql \&( .. )\&
+.Ql \&( .. \&)
 is used to create subshells.
 .Pp
 Whitespace and meta-characters can be quoted individually using a backslash
@@ -999,26 +1001,29 @@ Third, a double quote
 .Pq Sq \&"
 quotes all characters, except
 .Ql $ ,
-.Ql \`
-and
 .Ql \e ,
-up to the next unquoted double quote.
+and
+.Ql \` ,
+up to the next unescaped double quote.
 .Ql $
 and
 .Ql \`
 inside double quotes have their usual meaning (i.e. parameter, arithmetic,
 or command substitution) except no field splitting is carried out on the
-results of double-quoted substitutions.
+results of double-quoted substitutions, and the old-style form of command
+substitution has backslash-quoting for double quotes enabled.
 If a
 .Ql \e
 inside a double-quoted string is followed by
-.Ql \e ,
+.Ql \&" ,
 .Ql $ ,
-.Ql \` ,
+.Ql \e ,
 or
-.Ql \&" ,
-it is replaced by the second character; if it is followed by a newline, both
-the
+.Ql \` ,
+only the
+.Ql \e
+is removed, i.e. the combination is replaced by the second character;
+if it is followed by a newline, both the
 .Ql \e
 and the newline are stripped; otherwise, both the
 .Ql \e
@@ -1305,13 +1310,13 @@ form, a
 followed by any of
 .Ql $ ,
 .Ql \` ,
-.Ql \&"
-.Pq currently, and violating Tn POSIX ,
 or
 .Ql \e
-is stripped (a
+is stripped (as is
+.Ql \&"
+when the substitution is part of a double-quoted string); a backslash
 .Ql \e
-followed by any other character is unchanged).
+followed by any other character is unchanged.
 As a special case in command substitutions, a command of the form
 .Pf \*(Lt Ar file
 is interpreted to mean substitute the contents of
@@ -1588,6 +1593,8 @@ is a name reference (bound variable), created by the
 .Ic nameref
 command (which is an alias for
 .Ic typeset Fl n ) .
+.Ar name
+cannot be one of most special parameters (see below).
 .Pp
 .It Pf ${! Ns Ar name Ns \&[*]}
 .It Pf ${! Ns Ar name Ns \&[@]}
@@ -1675,6 +1682,25 @@ Inefficiently implemented, may be slow.
 .Pp
 .Sm off
 .It Xo
+.Pf ${ Ar name
+.Pf @/ Ar pattern / Ar string No }
+.Xc
+.Sm on
+The same as
+.Sm off
+.Xo
+.Pf ${ Ar name
+.Pf // Ar pattern / Ar string No } ,
+.Xc
+.Sm on
+except that both
+.Ar pattern
+and
+.Ar string
+are expanded anew for each iteration.
+.Pp
+.Sm off
+.It Xo
 .Pf ${ Ar name : Ns Ar pos
 .Pf : Ns Ar len Ns }
 .Xc
@@ -1847,12 +1873,16 @@ binding to the actual terminal size in favour of the provided value.
 If this parameter is found to be set after any profile files are executed, the
 expanded value is used as a shell startup file.
 It typically contains function and alias definitions.
-.It Ev ERRNO
-Integer value of the shell's
-.Va errno
-variable.
-It indicates the reason the last system call failed.
-Not yet implemented.
+.It Ev EPOCHREALTIME
+Time since the epoch, as returned by
+.Xr gettimeofday 2 ,
+formatted as decimal
+.Va tv_sec
+followed by a dot
+.Pq Sq \&.
+and
+.Va tv_usec
+padded to exactly six decimal digits.
 .It Ev EXECSHELL
 If set, this parameter is assumed to contain the shell that is to be used to
 execute commands that
@@ -1922,6 +1952,70 @@ The effective group id of the shell.
 The real group id of the shell.
 .It Ev KSHUID
 The real user id of the shell.
+.It Ev KSH_MATCH
+The last matched string.
+In a future version, this will be an indexed array,
+with indexes 1 and up capturing matching groups.
+Set by string comparisons (== and !=) in double-bracket test
+expressions when a match is found (when != returns false), by
+.Ic case
+when a match is encountered, and by the substitution operations
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf # Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf ## Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf % Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf %% Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf / Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf /# Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf /% Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf // Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+and
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf @/ Ar pat / Ar rpl No } .
+.Sm on
+.Xc
+See the end of the Emacs editing mode documentation for an example.
 .It Ev KSH_VERSION
 The name and version of the shell (read-only).
 See also the version commands in
@@ -1937,16 +2031,6 @@ Set to the number of lines on the terminal or window.
 Always set, defaults to 24.
 See
 .Ev COLUMNS .
-.It Ev EPOCHREALTIME
-Time since the epoch, as returned by
-.Xr gettimeofday 2 ,
-formatted as decimal
-.Va tv_sec
-followed by a dot
-.Pq Sq .\&
-and
-.Va tv_usec
-padded to exactly six decimal digits.
 .It Ev OLDPWD
 The previous working directory.
 Unset if
@@ -2131,7 +2215,7 @@ If the login name is empty,
 .Ql + ,
 or
 .Ql \- ,
-the value of the
+the simplified value of the
 .Ev HOME ,
 .Ev PWD ,
 or
@@ -2449,16 +2533,13 @@ This is called a here string.
 Standard input is duplicated from file descriptor
 .Ar fd .
 .Ar fd
-can be a number, indicating the number of an existing file descriptor;
+can be a single digit, indicating the number of an existing file descriptor;
 the letter
 .Ql p ,
 indicating the file descriptor associated with the output of the current
 co-process; or the character
 .Ql \- ,
 indicating standard input is to be closed.
-Note that
-.Ar fd
-is limited to a single digit in most shell implementations.
 .It \*(Gt& Ns Ar fd
 Same as
 .Ic \*(Lt& ,
@@ -2470,7 +2551,7 @@ This is a deprecated (legacy) GNU
 .Nm bash
 extension supported by
 .Nm
-which also supports the preceding explicit fd number, for example,
+which also supports the preceding explicit fd digit, for example,
 .Ic 3&\*(Gt Ns Ar file
 is the same as
 .Ic 3\*(Gt Ns Ar file 2\*(Gt&3
@@ -2499,7 +2580,7 @@ extensions.
 In any of the above redirections, the file descriptor that is redirected
 (i.e. standard input or standard output)
 can be explicitly given by preceding the
-redirection with a number (portably, only a single digit).
+redirection with a single digit.
 Parameter, command, and arithmetic
 substitutions, tilde substitutions, and (if the shell is interactive)
 file name generation are all performed on the
@@ -2563,7 +2644,7 @@ Unary operators:
 Binary operators:
 .Bd -literal -offset indent
 ,
-= += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+= += \-= *= /= %= \*(Lt\*(Lt= \*(Gt\*(Gt= \*(ha\*(Lt= \*(ha\*(Gt= &= \*(ha= \*(Ba=
 \*(Ba\*(Ba
 &&
 \*(Ba
@@ -2571,7 +2652,7 @@ Binary operators:
 &
 == !=
 \*(Lt \*(Lt= \*(Gt \*(Gt=
-\*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt \*(Lt\*(Lt \*(Gt\*(Gt
+\*(Lt\*(Lt \*(Gt\*(Gt \*(ha\*(Lt \*(ha\*(Gt
 + \-
 * / %
 .Ed
@@ -2592,7 +2673,7 @@ Integer constants may be specified with arbitrary bases using the notation
 .Ar base Ns # Ns Ar number ,
 where
 .Ar base
-is a decimal integer specifying the base, and
+is a decimal integer specifying the base (up to 36), and
 .Ar number
 is a number in the specified base.
 Additionally, base-16 integers may be specified by prefixing them with
@@ -2600,10 +2681,11 @@ Additionally, base-16 integers may be specified by prefixing them with
 .Pq case-insensitive
 in all forms of arithmetic expressions, except as numeric arguments to the
 .Ic test
-built-in command.
+built-in utility.
 Prefixing numbers with a sole digit zero
 .Pq Sq 0
-does not cause interpretation as octal, as that's unsafe to do.
+does not cause interpretation as octal (except in POSIX mode,
+as required by the standard), as that's unsafe to do.
 .Pp
 As a special
 .Nm mksh
@@ -2660,8 +2742,8 @@ The result is the value of the expression on the right-hand side.
 .It =
 Assignment; the variable on the left is set to the value on the right.
 .It Xo
-.No += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt=
-.No \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+.No += \-= *= /= %= \*(Lt\*(Lt\*(Gt\*(Gt=
+.No \*(ha\*(Lt= \*(ha\*(Gt= &= \*(ha= \*(Ba=
 .Xc
 Assignment operators.
 .Sm off
@@ -2711,14 +2793,14 @@ not.
 Less than or equal, greater than, greater than or equal.
 See
 .Ic \*(Lt .
-.It \*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt
-Rotate left (right); the result is similar to shift (see
-.Ic \*(Lt\*(Lt )
+.It \*(Lt\*(Lt \*(Gt\*(Gt
+Shift left (right); the result is the left argument with its bits
+arithmetically (signed operation) or logically (unsigned expression)
+shifted left (right) by the amount given in the right argument.
+.It \*(ha\*(Lt \*(ha\*(Gt
+Rotate left (right); the result is similar to shift,
 except that the bits shifted out at one end are shifted in
 at the other end, instead of zero or sign bits.
-.It \*(Lt\*(Lt \*(Gt\*(Gt
-Shift left (right); the result is the left argument with its bits shifted left
-(right) by the amount given in the right argument.
 .It + \- * /
 Addition, subtraction, multiplication, and division.
 .It %
@@ -3094,7 +3176,7 @@ The specified editing command is bound to the given
 .Ar string ,
 which should consist of a control character
 optionally preceded by one of the two prefix characters
-and optionally succeded by a tilde character.
+and optionally succeeded by a tilde character.
 Future input of the
 .Ar string
 will cause the editing command to be immediately invoked.
@@ -3698,7 +3780,7 @@ however, distributors may have added this as builtin as a speed hack.
 .Pp
 .It Xo
 .Ic print
-.Oo Fl nprsu Ns Oo Ar n Oc \*(Ba
+.Oo Fl Anprsu Ns Oo Ar n Oc \*(Ba
 .Fl R Op Fl en Oc
 .Op Ar argument ...
 .Xc
@@ -3734,6 +3816,10 @@ and the
 option prints to the co-process (see
 .Sx Co-processes
 above).
+The
+.Fl A
+option prints the character corresponding to each
+.Ar argument Ns 's value .
 .Pp
 The
 .Fl R
@@ -3900,7 +3986,7 @@ 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.
-Similarily, when using the
+Similarly, when using the
 .Fl a
 option, use of the
 .Fl r
@@ -3961,6 +4047,9 @@ it's also checked for existence and whether it is a directory; otherwise,
 .Ic realpath
 returns 0 if the pathname either exists or can be created immediately,
 i.e. all but the last component exist and are directories.
+For calls from the shell, if any options are given, an external
+.Xr realpath 1
+utility is preferred over the builtin.
 .Pp
 .It Xo
 .Ic rename
@@ -3972,21 +4061,22 @@ Renames the file
 to
 .Ar to .
 Both must be complete pathnames and on the same device.
-This builtin is intended for emergency situations where
-.Pa /bin/mv
-becomes unusable, and directly calls
+An external utility is preferred over this builtin,
+which is intended for emergency situations
+.Pq where Pa /bin/mv No becomes unusable
+and directly calls
 .Xr rename 2 .
 .Pp
 .It Ic return Op Ar status
 Returns from a function or
-.Ic .\&
+.Ic \&.
 script, with exit status
 .Ar status .
 If no
 .Ar status
 is given, the exit status of the last executed command is used.
 If used outside of a function or
-.Ic .\&
+.Ic \&.
 script, it has the same effect as
 .Ic exit .
 Note that
@@ -3994,9 +4084,9 @@ Note that
 treats both profile and
 .Ev ENV
 files as
-.Ic .\&
+.Ic \&.
 scripts, while the original Korn shell only treats profiles as
-.Ic .\&
+.Ic \&.
 scripts.
 .Pp
 .It Xo
@@ -4074,7 +4164,7 @@ explicitly tested by a shell construct such as
 .Ic until ,
 .Ic while ,
 or
-.Ic !\&
+.Ic \&!
 statements.
 For
 .Ic &&
@@ -4310,6 +4400,11 @@ options (with single letter names) can be found in the parameter
 with no option name will list all the options and whether each is on or off;
 .Ic set +o
 will print the long names of all options that are currently on.
+In a future version,
+.Ic set +o
+will behave
+.Tn POSIX
+compliant and print commands to restore the current options instead.
 .Pp
 Remaining arguments, if any, are positional parameters and are assigned, in
 order, to the positional parameters (i.e. $1, $2, etc.).
@@ -5139,38 +5234,20 @@ If job monitoring is enabled, the completion status of jobs is printed
 .Op Fl pv
 .Op Ar name ...
 .Xc
-For each
-.Ar name ,
-the type of command is listed (reserved word, built-in, alias,
-function, tracked alias, or executable).
-If the
-.Fl p
-option is used, a path search is performed even if
-.Ar name
-is a reserved word, alias, etc.
 Without the
 .Fl v
-option,
-.Ic whence
-is similar to
-.Ic command Fl v
-except that
-.Ic whence
-will find reserved words and won't print aliases as alias commands.
+option, it is the same as
+.Ic command Fl v ,
+except aliases are not printed as alias command.
 With the
 .Fl v
-option,
-.Ic whence
-is the same as
+option, it is exactly the same as
 .Ic command Fl V .
-Note that for
-.Ic whence ,
-the
+In either case, the
 .Fl p
-option does not affect the search path used, as it does for
-.Ic command .
-If the type of one or more of the names could not be determined, the exit
-status is non-zero.
+option differs: the search path is not affected in
+.Ic whence ,
+but the search is restricted to the path.
 .El
 .Ss Job control
 Job control refers to the shell's ability to monitor and control jobs which
@@ -5348,7 +5425,7 @@ The
 builtin does not interpret backslashes and only supports the exact option
 .Dq Fl n .
 .It
-\&... (list is incomplete and may change for R53)
+\&... (list is incomplete and may change for R54)
 .El
 .Ss SH mode
 Compatibility mode; intended for use with legacy scripts that
@@ -5369,7 +5446,35 @@ The
 builtin does not interpret backslashes and only supports the exact option
 .Dq Fl n .
 .It
-\&... (list is incomplete and may change for R53)
+The substitution operations
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf # Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf ## Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf % Ar pat No } ,
+.Sm on
+.Xc
+and
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf %% Ar pat No }
+.Sm on
+.Xc
+wrongly do not require a parenthesis to be escaped and do not parse extglobs.
+.It
+\&... (list is incomplete and may change for R54)
 .El
 .Ss Interactive input line editing
 The shell supports three modes of reading command lines from a
@@ -5451,16 +5556,20 @@ Note that editing command names are used only with the
 command.
 Furthermore, many editing commands are useful only on terminals with
 a visible cursor.
-The default bindings were chosen to resemble corresponding
-Emacs key bindings.
 The user's
 .Xr tty 4
 characters (e.g.\&
 .Dv ERASE )
 are bound to
-reasonable substitutes and override the default bindings.
+reasonable substitutes and override the default bindings;
+their customary values are shown in parentheses below.
+The default bindings were chosen to resemble corresponding
+Emacs key bindings:
 .Bl -tag -width Ds
-.It abort: \*(haC, \*(haG
+.It Xo abort:
+.No INTR Pq \*(haC ,
+.No \*(haG
+.Xc
 Abort the current command, empty the line buffer and
 set the exit state to interrupted.
 .It auto\-insert: Op Ar n
@@ -5534,7 +5643,8 @@ command above.
 Note that \*(haI is usually generated by the TAB (tabulator) key.
 .It Xo delete\-char\-backward:
 .Op Ar n
-.No ERASE , \*(ha? , \*(haH
+.No ERASE Pq \*(haH ,
+.No \*(ha? , \*(haH
 .Xc
 Deletes
 .Ar n
@@ -5548,7 +5658,9 @@ Deletes
 characters after the cursor.
 .It Xo delete\-word\-backward:
 .Op Ar n
-.No WERASE , \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
+.No Pfx1+ERASE Pq \*(ha[\*(haH ,
+.No WERASE Pq \*(haW ,
+.No \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
 .Xc
 Deletes
 .Ar n
@@ -5598,17 +5710,22 @@ Moves to the end of the history.
 Moves the cursor to the end of the input line.
 .It eot: \*(ha_
 Acts as an end-of-file; this is useful because edit-mode input disables
-normal terminal input canonicalization.
+normal terminal input canonicalisation.
 .It Xo eot\-or\-delete:
 .Op Ar n
-.No \*(haD
+.No EOF Pq \*(haD
 .Xc
-Acts as
-.Ic eot
-if alone on a line; otherwise acts as
+If alone on a line, same as
+.Ic eot ,
+otherwise,
 .Ic delete\-char\-forward .
 .It error: (not bound)
 Error (ring the bell).
+.It evaluate\-region: \*(ha[\*(haE
+Evaluates the text between the mark and the cursor position
+.Pq the entire line if no mark is set
+as function substitution (if it cannot be parsed, the editing state is
+unchanged and the bell is rung to signal an error); $? is updated accordingly.
 .It exchange\-point\-and\-mark: \*(haX\*(haX
 Places the cursor where the mark is and sets the mark to where the cursor was.
 .It expand\-file: \*(ha[*
@@ -5637,8 +5754,13 @@ word.
 .Xc
 Goes to history number
 .Ar n .
-.It kill\-line: KILL
+.It Xo kill\-line:
+.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:
@@ -5675,12 +5797,14 @@ This is only useful after an
 .Ic search\-history
 or
 .Ic search\-history\-up .
-.It no\-op: QUIT
+.It Xo no\-op:
+.No QUIT Pq \*(ha\e
+.Xc
 This does nothing.
 .It prefix\-1: \*(ha[
 Introduces a 2-character command sequence.
 .It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
-Introduces a 2-character command sequence.
+Introduces a multi-character command sequence.
 .It Xo prev\-hist\-word:
 .Op Ar n
 .No \*(ha[. , \*(ha[_
@@ -5782,6 +5906,11 @@ Immediately after a
 .Ic yank ,
 replaces the inserted text string with the next previously killed text string.
 .El
+.Pp
+The tab completion escapes characters the same way as the following code:
+.Bd -literal
+print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
+.Ed
 .Ss Vi editing mode
 .Em Note:
 The vi command-line editing mode is orphaned, yet still functional.
@@ -6594,20 +6723,8 @@ when multiple shells are accessing the file; the rollover process
 for the in-memory portion of the history is slow, should use
 .Xr memmove 3 .
 .Pp
-Handling of backslash plus double-quote inside the (deprecated)
-.Pf \` Ns Ar command Ns \`
-form of command substitution when the substitution itself is
-also inside double quotes currently deliberately violates
-.Tn POSIX
-even in
-.Fl o Ic posix
-mode until Austin group bug 1015 has been resolved either way,
-as the current wording of the standard prohibits the current
-and historic practice of several shells which several scripts
-(admittedly wrongly) depend on.
-.Pp
 This document attempts to describe
-.Nm mksh\ R52b
+.Nm mksh\ R53
 and up,
 .\" with vendor patches from insert-your-name-here,
 compiled without any options impacting functionality, such as
index 239cc23..a6cfb6f 100644 (file)
--- a/src/sh.h
+++ b/src/sh.h
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.757 2016/01/20 21:34:13 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.786 2016/08/12 16:48:05 tg Exp $");
 #endif
-#define MKSH_VERSION "R52 2016/01/20"
+#define MKSH_VERSION "R53 2016/08/12"
 
 /* arithmetic types: C implementation */
 #if !HAVE_CAN_INTTYPES
@@ -578,7 +578,7 @@ char *ucstrstr(char *, const char *);
 #define mkssert(e)     do { } while (/* CONSTCOND */ 0)
 #endif
 
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 521)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 530)
 #error Must run Build.sh to compile this.
 extern void thiswillneverbedefinedIhope(void);
 int
@@ -634,17 +634,11 @@ im_sorry_dave(void)
 #ifndef MKSH_NO_CMDLINE_EDITING
 #define MKSH_NO_CMDLINE_EDITING        /* defined */
 #endif
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS  /* defined */
-#endif
 #undef MKSH_S_NOVI
 #define MKSH_S_NOVI            1
 #endif
 
 #ifdef MKSH_SMALL
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS  /* defined */
-#endif
 #ifndef MKSH_NOPWNAM
 #define MKSH_NOPWNAM           /* defined */
 #endif
@@ -661,14 +655,8 @@ im_sorry_dave(void)
 #define MKSH_UNEMPLOYED                1
 #endif
 
-/* these shall be smaller than 100 */
-#ifdef MKSH_CONSERVATIVE_FDS
 #define NUFILE         32      /* Number of user-accessible files */
 #define FDBASE         10      /* First file usable by Shell */
-#else
-#define NUFILE         56      /* Number of user-accessible files */
-#define FDBASE         24      /* First file usable by Shell */
-#endif
 
 /*
  * simple grouping allocator
@@ -699,16 +687,29 @@ im_sorry_dave(void)
 
 
 /* 1. internal structure */
-struct lalloc {
-       struct lalloc *next;
+struct lalloc_common {
+       struct lalloc_common *next;
 };
 
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+struct lalloc_item {
+       struct lalloc_common *next;
+       size_t len;
+       char dummy[8192 - sizeof(struct lalloc_common *) - sizeof(size_t)];
+};
+#endif
+
 /* 2. sizes */
-#define ALLOC_ITEM     struct lalloc
-#define ALLOC_SIZE     (sizeof(ALLOC_ITEM))
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+#define ALLOC_ITEM     struct lalloc_item
+#define ALLOC_OVERHEAD 0
+#else
+#define ALLOC_ITEM     struct lalloc_common
+#define ALLOC_OVERHEAD (sizeof(ALLOC_ITEM))
+#endif
 
-/* 3. group structure (only the same for lalloc.c) */
-typedef struct lalloc Area;
+/* 3. group structure */
+typedef struct lalloc_common Area;
 
 
 EXTERN Area aperm;             /* permanent object space */
@@ -833,41 +834,318 @@ EXTERN struct {
 
 /* null value for variable; comparison pointer for unset */
 EXTERN char null[] E_INIT("");
-/* helpers for string pooling */
-EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
-EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
-#if defined(__GNUC__)
-/* trust this to have string pooling; -Wformat bitches otherwise */
-#define Tsynerr                "syntax error"
+
+/* string pooling: do we rely on the compiler? */
+#ifndef HAVE_STRING_POOLING
+/* no, we use our own, saves quite some space */
+#elif HAVE_STRING_POOLING == 2
+/* “on demand” */
+#ifdef __GNUC__
+/* only for GCC 4 or later, older ones can get by without */
+#if __GNUC__ < 4
+#undef HAVE_STRING_POOLING
+#endif
 #else
-EXTERN const char Tsynerr[] E_INIT("syntax error");
+/* not GCC, default to on */
 #endif
-EXTERN const char Tselect[] E_INIT("select");
-EXTERN const char T_typeset[] E_INIT("=typeset");
-#define Ttypeset       (T_typeset + 1)         /* "typeset" */
+#elif HAVE_STRING_POOLING == 0
+/* default to on, unless explicitly set to 0 */
+#undef HAVE_STRING_POOLING
+#endif
+
+#ifndef HAVE_STRING_POOLING /* helpers for pooled strings */
+EXTERN const char T4spaces[] E_INIT("    ");
+#define T1space (T4spaces + 3)
+EXTERN const char Tcolsp[] E_INIT(": ");
+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} $_");
+#define Tspdollaru (TFCEDIT_dollaru + 18)
+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 Tunalias[] E_INIT("unalias");
-EXTERN const char Tcat[] E_INIT("cat");
+EXTERN const char Tbadsubst[] E_INIT("bad substitution");
+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 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");
+EXTERN const char Tcant_open[] E_INIT("can't open");
+#define Tbytes (Toomem + 24)
+EXTERN const char Tbcat[] E_INIT("!cat");
+#define Tcat (Tbcat + 1)
+#define Tcd (Tcant_cd + 25)
+EXTERN const char Tcommand[] E_INIT("command");
+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)
 #ifdef __OS2__
 EXTERN const char Textproc[] E_INIT("extproc");
 #endif
-#ifdef MKSH_PRINTF_BUILTIN
-EXTERN const char Tprintf[] E_INIT("printf");
-#endif
-EXTERN const char Tsgset[] E_INIT("*=set");
-#define Tset           (Tsgset + 2)            /* "set" */
-EXTERN const char Tsgexport[] E_INIT("*=export");
-#define Texport                (Tsgexport + 2)         /* "export" */
-EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
-#define Treadonly      (Tsgreadonly + 2)       /* "readonly" */
-EXTERN const char Tgbuiltin[] E_INIT("=builtin");
-#define Tbuiltin       (Tgbuiltin + 1)         /* "builtin" */
+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");
+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)        /* "function" */
+#define Tfunction (T_function + 1)
 EXTERN const char T_funny_command[] E_INIT("funny $() command");
-#define Tcommand       (T_funny_command + 10)  /* "command" */
-EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
-#define TC_IFSWS       (TC_LEX1 + 7)           /* space tab newline */
+EXTERN const char Tgetopts[] E_INIT("getopts");
+EXTERN const char Thistory[] E_INIT("history");
+EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
+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");
+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");
+EXTERN const char Tnot_in_history[] E_INIT("not in history");
+EXTERN const char Tnot_found_s[] E_INIT("%s not found");
+#define Tnot_found (Tnot_found_s + 3)
+#define Tnot_started (Tjob_not_started + 4)
+#define TOLDPWD (Tno_OLDPWD + 3)
+#define Topen (Tcant_open + 6)
+#define TPATH (TFPATH + 1)
+EXTERN const char Tpv[] E_INIT("pv");
+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)
+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 ");
+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 Tsh (Tmksh + 2)
+#define TSHELL (TEXECSHELL + 4)
+EXTERN const char Tshf_read[] E_INIT("shf_read");
+EXTERN const char Tshf_write[] E_INIT("shf_write");
+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 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)
+#define Tugo (Taugo + 1)
+EXTERN const char Tunalias[] E_INIT("unalias");
+#define Tunexpected (TELIF_unexpected + 6)
+EXTERN const char Tunknown_option[] E_INIT("unknown option");
+EXTERN const char Tuser_sp1[] E_INIT("user ");
+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");
+EXTERN const char Tf__ss[] E_INIT(" %s%s");
+EXTERN const char Tf__sN[] E_INIT(" %s\n");
+EXTERN const char Tf_sSs[] E_INIT("%s/%s");
+EXTERN const char Tf_T[] E_INIT("%T");
+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");
+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");
+#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");
+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)
+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_sd[] E_INIT("%s %d");
+#define Tf_s (Tf__ss + 3)
+EXTERN const char Tft_end[] E_INIT("%;");
+EXTERN const char Tft_R[] E_INIT("%R");
+#define Tf_d (Tf__d + 1)
+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");
+EXTERN const char Tf_temp[] E_INIT("can't %s temporary file %s: %s");
+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)
+EXTERN const char T_devtty[] E_INIT("/dev/tty");
+#else /* helpers for string pooling */
+#define T4spaces "    "
+#define T1space " "
+#define Tcolsp ": "
+#define TC_LEX1 "|&;<>() \t\n"
+#define TC_IFSWS " \t\n"
+#define TFCEDIT_dollaru "${FCEDIT:-/bin/ed} $_"
+#define Tspdollaru " $_"
+#define Tsgdot "*=."
+#define Taugo "augo"
+#define Tbracket "["
+#define Tdot "."
+#define Talias "alias"
+#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 Tbuiltin "builtin"
+#define Toomem "can't allocate %zu data bytes"
+#define Tcant_cd "restricted shell - can't cd"
+#define Tcant_find "can't find"
+#define Tcant_open "can't open"
+#define Tbytes "bytes"
+#define Tbcat "!cat"
+#define Tcat "cat"
+#define Tcd "cd"
+#define Tcommand "command"
+#define Tcreate "create"
+#define TELIF_unexpected "TELIF unexpected"
+#define TEXECSHELL "EXECSHELL"
+#define Tsgexport "*=export"
+#define Texport "export"
+#ifdef __OS2__
+#define Textproc "extproc"
+#endif
+#define Tfalse "false"
+#define Tfg "fg"
+#define Tfg_badsubst "fileglob: bad substitution"
+#define Tfile "file"
+#define Tfile_fd "function definition file"
+#define TFPATH "FPATH"
+#define T_function " function"
+#define Tfunction "function"
+#define T_funny_command "funny $() command"
+#define Tgetopts "getopts"
+#define Thistory "history"
+#define Tintovfl "integer overflow %zu %c %zu prevented"
+#define Tjobs "jobs"
+#define Tjob_not_started "job not started"
+#define Tmksh "mksh"
+#define Tname "name"
+#define Tno_args "missing argument"
+#define Tno_OLDPWD "no OLDPWD"
+#define Tnot_ident "is not an identifier"
+#define Tnot_in_history "not in history"
+#define Tnot_found_s "%s not found"
+#define Tnot_found "not found"
+#define Tnot_started "not started"
+#define TOLDPWD "OLDPWD"
+#define Topen "open"
+#define TPATH "PATH"
+#define Tpv "pv"
+#define TpVv "Vpv"
+#define TPWD "PWD"
+#define Tread "read"
+#define Tsgreadonly "*=readonly"
+#define Treadonly "readonly"
+#define Tredirection_dup "can't finish (dup) redirection"
+#define Tredirection "redirection"
+#define Treal_sp1 "real "
+#define Treal_sp2 " real "
+#define Treq_arg "requires an argument"
+#define Tselect "select"
+#define Tsgset "*=set"
+#define Tset "set"
+#define Tsh "sh"
+#define TSHELL "SHELL"
+#define Tshf_read "shf_read"
+#define Tshf_write "shf_write"
+#define Tj_suspend "j_suspend"
+#define Tsuspend "suspend"
+#define Tsynerr "syntax error"
+#define Ttime "time"
+#define Ttoo_many_args "too many arguments"
+#define Ttrue "true"
+#define Ttty_fd_dupof "dup of tty fd"
+#define Ttty_fd "tty fd"
+#define Tgtypeset "=typeset"
+#define Ttypeset "typeset"
+#define Tugo "ugo"
+#define Tunalias "unalias"
+#define Tunexpected "unexpected"
+#define Tunknown_option "unknown option"
+#define Tuser_sp1 "user "
+#define Tuser_sp2 " user "
+#define Twrite "write"
+#define Tf__S " %S"
+#define Tf__d " %d"
+#define Tf__ss " %s%s"
+#define Tf__sN " %s\n"
+#define Tf_sSs "%s/%s"
+#define Tf_T "%T"
+#define Tf_dN "%d\n"
+#define Tf_s_ "%s "
+#define Tf_s_T "%s %T"
+#define Tf_s_s_sN "%s %s %s\n"
+#define Tf_s_s "%s %s"
+#define Tf_s_sD_s "%s %s: %s"
+#define Tf_optfoo "%s%s-%c: %s"
+#define Tf_sD_ "%s: "
+#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"
+#if HAVE_MKNOD
+#define Tf_nonnum "non-numeric %s %s '%s'"
+#endif
+#define Tf_S_ "%S "
+#define Tf_S "%S"
+#define Tf_lu "%lu"
+#define Tf_toolarge "%s %s too large: %lu"
+#define Tf_ldfailed "%s %s(%d, %ld) failed: %s"
+#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_sd "%s %d"
+#define Tf_s "%s"
+#define Tft_end "%;"
+#define Tft_R "%R"
+#define Tf_d "%d"
+#define Tf_sD_s_qs "%s: %s '%s'"
+#define Tf_ro "read-only: %s"
+#define Tf_flags "%s: flags 0x%X"
+#define Tf_temp "can't %s temporary file %s: %s"
+#define Tf_ssfaileds "%s: %s failed: %s"
+#define Tf_sD_sD_s "%s: %s: %s"
+#define Tf__c_ "-%c "
+#define Tf_sD_s_s "%s: %s %s"
+#define Tf_sN "%s\n"
+#define Tf_sD_s "%s: %s"
+#define T_devtty "/dev/tty"
+#endif /* end of string pooling */
 
 typedef uint8_t Temp_type;
 /* expanded heredoc */
@@ -1059,19 +1337,14 @@ EXTERN bool builtin_spec;
 EXTERN char    *current_wd;
 
 /* input line size */
-#define LINE           (4096 - ALLOC_SIZE)
-/*
- * Minimum required space to work with on a line - if the prompt leaves
- * less space than this on a line, the prompt is truncated.
- */
-#define MIN_EDIT_SPACE 7
-/*
- * Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
- */
-#define MIN_COLS       (2 + MIN_EDIT_SPACE + 3)
-#define MIN_LINS       3
-EXTERN mksh_ari_t x_cols E_INIT(80);   /* tty columns */
-EXTERN mksh_ari_t x_lins E_INIT(24);   /* tty lines */
+#ifdef MKSH_SMALL
+#define LINE           (4096 - ALLOC_OVERHEAD)
+#else
+#define LINE           (16384 - ALLOC_OVERHEAD)
+#endif
+/* columns and lines of the tty */
+EXTERN mksh_ari_t x_cols E_INIT(80);
+EXTERN mksh_ari_t x_lins E_INIT(24);
 
 
 /* Determine the location of the system (common) profile */
@@ -1100,7 +1373,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);      /* tty lines */
                                    (shf)->rnleft--, *(shf)->rp++ : \
                                    shf_getchar(shf))
 #define shf_putc_i(c, shf)     ((shf)->wnleft == 0 ? \
-                                   shf_putchar((c), (shf)) : \
+                                   shf_putchar((uint8_t)(c), (shf)) : \
                                    ((shf)->wnleft--, *(shf)->wp++ = (c)))
 #define shf_eof(shf)           ((shf)->flags & SHF_EOF)
 #define shf_error(shf)         ((shf)->flags & SHF_ERROR)
@@ -1189,7 +1462,7 @@ struct tbl {
        char name[4];
 };
 
-EXTERN struct tbl vtemp;
+EXTERN struct tbl *vtemp;
 /* set by global() and local() */
 EXTERN bool last_lookup_was_array;
 
@@ -1225,6 +1498,9 @@ EXTERN bool last_lookup_was_array;
 #define FDELETE                BIT(10) /* function deleted while it was executing */
 #define FKSH           BIT(11) /* function defined with function x (vs x()) */
 #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 */
+
 /*
  * Attributes that can be set by the user (used to decide if an unset
  * param should be repoted by set/typeset). Does not include ARRAY or
@@ -1258,7 +1534,7 @@ enum namerefflag {
 #define FC_BI          (FC_SPECBI | FC_NORMBI)
 #define FC_PATH                BIT(3)  /* do path search */
 #define FC_DEFPATH     BIT(4)  /* use default path in path search */
-
+#define FC_WHENCE      BIT(5)  /* for use by command and whence */
 
 #define AF_ARGV_ALLOC  0x1     /* argv[] array allocated */
 #define AF_ARGS_ALLOCED        0x2     /* argument strings allocated */
@@ -1462,9 +1738,14 @@ struct ioword {
 #define X_EXTRA        20      /* this many extra bytes in X string */
 
 typedef struct XString {
-       char *end, *beg;        /* end, begin of string */
-       size_t len;             /* length */
-       Area *areap;            /* area to allocate/free from */
+       /* begin of string */
+       char *beg;
+       /* length of allocated area, minus safety margin */
+       size_t len;
+       /* end of string */
+       char *end;
+       /* memory area used */
+       Area *areap;
 } XString;
 
 typedef char *XStringP;
@@ -1550,24 +1831,39 @@ typedef struct {
 
 typedef struct source Source;
 struct source {
-       const char *str;        /* input pointer */
-       const char *start;      /* start of current buffer */
+       /* input buffer */
+       XString xs;
+       /* memory area, also checked in reclaim() */
+       Area *areap;
+       /* stacked source */
+       Source *next;
+       /* input pointer */
+       const char *str;
+       /* start of current buffer */
+       const char *start;
+       /* input file name */
+       const char *file;
+       /* extra data */
        union {
-               const char **strv;      /* string [] */
-               struct shf *shf;        /* shell file */
-               struct tbl *tblp;       /* alias (SF_HASALIAS) */
-               char *freeme;           /* also for SREREAD */
+               /* string[] */
+               const char **strv;
+               /* shell file */
+               struct shf *shf;
+               /* alias (SF_HASALIAS) */
+               struct tbl *tblp;
+               /* (also for SREREAD) */
+               char *freeme;
        } u;
-       const char *file;       /* input file name */
-       int     type;           /* input type */
-       int     line;           /* line number */
-       int     errline;        /* line the error occurred on (0 if not set) */
-       int     flags;          /* SF_* */
-       Area    *areap;
-       Source *next;           /* stacked source */
-       XString xs;             /* input buffer */
-       char    ugbuf[2];       /* buffer for ungetsc() (SREREAD) and
-                                * alias (SALIAS) */
+       /* flags */
+       int flags;
+       /* input type */
+       int type;
+       /* line number */
+       int line;
+       /* line the error occurred on (0 if not set) */
+       int errline;
+       /* buffer for ungetsc() (SREREAD) and alias (SALIAS) */
+       char ugbuf[2];
 };
 
 /* Source.type values */
@@ -1704,6 +2000,7 @@ void x_done(void);
 int x_read(char *);
 #endif
 void x_mkraw(int, mksh_ttyst *, bool);
+void x_initterm(const char *);
 /* eval.c */
 char *substitute(const char *, int);
 char **eval(const char **, int);
@@ -1725,6 +2022,7 @@ int search_access(const char *, int);
 const char *search_path(const char *, const char *, int, int *);
 void pr_menu(const char * const *);
 void pr_list(char * const *);
+int herein(struct ioword *, char **);
 /* expr.c */
 int evaluate(const char *, mksh_ari_t *, int, bool);
 int v_evaluate(struct tbl *, const char *, volatile int, bool);
@@ -1733,7 +2031,7 @@ size_t utf_mbtowc(unsigned int *, const char *);
 size_t utf_wctomb(char *, unsigned int);
 int utf_widthadj(const char *, const char **);
 size_t utf_mbswidth(const char *) MKSH_A_PURE;
-const char *utf_skipcols(const char *, int) MKSH_A_PURE;
+const char *utf_skipcols(const char *, int, int *);
 size_t utf_ptradj(const char *) MKSH_A_PURE;
 #ifdef MIRBSD_BOOTFLOPPY
 #define utf_wcwidth(i) wcwidth((wchar_t)(i))
@@ -1741,7 +2039,7 @@ size_t utf_ptradj(const char *) MKSH_A_PURE;
 int utf_wcwidth(unsigned int) MKSH_A_PURE;
 #endif
 int ksh_access(const char *, int);
-struct tbl *tempvar(void);
+struct tbl *tempvar(const char *);
 /* funcs.c */
 int c_hash(const char **);
 int c_pwd(const char **);
@@ -1990,7 +2288,7 @@ 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);
+int assign_command(const char *, bool) MKSH_A_PURE;
 void initkeywords(void);
 struct op *compile(Source *, bool);
 bool parse_usec(const char *, struct timeval *);
@@ -2041,6 +2339,7 @@ uint32_t chvt_rndsetup(const void *, size_t) MKSH_A_PURE;
 mksh_ari_t rndget(void);
 void rndset(unsigned long);
 void rndpush(const void *);
+void record_match(const char *);
 
 enum Test_op {
        /* non-operator */
index 3b63c7b..ace0ab6 100644 (file)
--- 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
+ *              2012, 2013, 2015, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * 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.69 2015/12/31 20:38:59 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.76 2016/07/25 00:04:47 tg Exp $");
 
 /* flags to shf_emptybuf() */
 #define EB_READSW      0x01    /* about to switch to reading */
@@ -119,7 +119,7 @@ shf_open_hlp(int fd, int *sflagsp, const char *where)
        }
 
        if (!(sflags & (SHF_RD | SHF_WR)))
-               internal_errorf("%s: %s", where, "missing read/write");
+               internal_errorf(Tf_sD_s, where, "missing read/write");
 }
 
 /* Set up the shf structure for a file descriptor. Doesn't fail. */
@@ -167,7 +167,7 @@ shf_reopen(int fd, int sflags, struct shf *shf)
 
        shf_open_hlp(fd, &sflags, "shf_reopen");
        if (!shf || !shf->buf || shf->bsize < bsize)
-               internal_errorf("%s: %s", "shf_reopen", "bad shf/buf/bsize");
+               internal_errorf(Tf_sD_s, "shf_reopen", Tbad_bsize);
 
        /* assumes shf->buf and shf->bsize already set up */
        shf->fd = fd;
@@ -197,7 +197,8 @@ shf_sopen(char *buf, ssize_t bsize, int sflags, struct shf *shf)
 {
        /* can't have a read+write string */
        if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR)))
-               internal_errorf("%s: flags 0x%X", "shf_sopen", sflags);
+               internal_errorf(Tf_flags, "shf_sopen",
+                   (unsigned int)sflags);
 
        if (!shf) {
                shf = alloc(sizeof(struct shf), ATEMP);
@@ -292,7 +293,7 @@ shf_flush(struct shf *shf)
                return ((shf->flags & SHF_WR) ? -1 : 0);
 
        if (shf->fd < 0)
-               internal_errorf("%s: %s", "shf_flush", "no fd");
+               internal_errorf(Tf_sD_s, "shf_flush", "no fd");
 
        if (shf->flags & SHF_ERROR) {
                errno = shf->errnosv;
@@ -323,7 +324,7 @@ shf_emptybuf(struct shf *shf, int flags)
        int ret = 0;
 
        if (!(shf->flags & SHF_STRING) && shf->fd < 0)
-               internal_errorf("%s: %s", "shf_emptybuf", "no fd");
+               internal_errorf(Tf_sD_s, "shf_emptybuf", "no fd");
 
        if (shf->flags & SHF_ERROR) {
                errno = shf->errnosv;
@@ -409,7 +410,7 @@ shf_fillbuf(struct shf *shf)
                return (0);
 
        if (shf->fd < 0)
-               internal_errorf("%s: %s", "shf_fillbuf", "no fd");
+               internal_errorf(Tf_sD_s, "shf_fillbuf", "no fd");
 
        if (shf->flags & (SHF_EOF | SHF_ERROR)) {
                if (shf->flags & SHF_ERROR)
@@ -452,10 +453,11 @@ shf_read(char *buf, ssize_t bsize, struct shf *shf)
        ssize_t ncopy, orig_bsize = bsize;
 
        if (!(shf->flags & SHF_RD))
-               internal_errorf("%s: flags 0x%X", "shf_read", shf->flags);
+               internal_errorf(Tf_flags, Tshf_read,
+                   (unsigned int)shf->flags);
 
        if (bsize <= 0)
-               internal_errorf("%s: %s %zd", "shf_write", "bsize", bsize);
+               internal_errorf(Tf_szs, Tshf_read, bsize, Tbsize);
 
        while (bsize > 0) {
                if (shf->rnleft == 0 &&
@@ -489,7 +491,8 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
        char *orig_buf = buf;
 
        if (!(shf->flags & SHF_RD))
-               internal_errorf("%s: flags 0x%X", "shf_getse", shf->flags);
+               internal_errorf(Tf_flags, "shf_getse",
+                   (unsigned int)shf->flags);
 
        if (bsize <= 0)
                return (NULL);
@@ -525,7 +528,8 @@ int
 shf_getchar(struct shf *shf)
 {
        if (!(shf->flags & SHF_RD))
-               internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags);
+               internal_errorf(Tf_flags, "shf_getchar",
+                   (unsigned int)shf->flags);
 
        if (shf->rnleft == 0 && (shf_fillbuf(shf) == -1 || shf->rnleft == 0))
                return (-1);
@@ -541,7 +545,8 @@ int
 shf_ungetc(int c, struct shf *shf)
 {
        if (!(shf->flags & SHF_RD))
-               internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags);
+               internal_errorf(Tf_flags, "shf_ungetc",
+                   (unsigned int)shf->flags);
 
        if ((shf->flags & SHF_ERROR) || c == -1 ||
            (shf->rp == shf->buf && shf->rnleft))
@@ -578,7 +583,8 @@ int
 shf_putchar(int c, struct shf *shf)
 {
        if (!(shf->flags & SHF_WR))
-               internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags);
+               internal_errorf(Tf_flags, "shf_putchar",
+                   (unsigned int)shf->flags);
 
        if (c == -1)
                return (-1);
@@ -588,7 +594,7 @@ shf_putchar(int c, struct shf *shf)
                ssize_t n;
 
                if (shf->fd < 0)
-                       internal_errorf("%s: %s", "shf_putchar", "no fd");
+                       internal_errorf(Tf_sD_s, "shf_putchar", "no fd");
                if (shf->flags & SHF_ERROR) {
                        errno = shf->errnosv;
                        return (-1);
@@ -633,10 +639,11 @@ shf_write(const char *buf, ssize_t nbytes, struct shf *shf)
        ssize_t n, ncopy, orig_nbytes = nbytes;
 
        if (!(shf->flags & SHF_WR))
-               internal_errorf("%s: flags 0x%X", "shf_write", shf->flags);
+               internal_errorf(Tf_flags, Tshf_write,
+                   (unsigned int)shf->flags);
 
        if (nbytes < 0)
-               internal_errorf("%s: %s %zd", "shf_write", "nbytes", nbytes);
+               internal_errorf(Tf_szs, Tshf_write, nbytes, Tbytes);
 
        /* Don't buffer if buffer is empty and we're writting a large amount. */
        if ((ncopy = shf->wnleft) &&
@@ -764,10 +771,10 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
        const char *s;
        char c, *cp;
        int tmp = 0, flags;
-       ssize_t field, precision, len;
+       size_t field, precision, len;
        unsigned long lnum;
        /* %#o produces the longest output */
-       char numbuf[(8 * sizeof(long) + 2) / 3 + 1];
+       char numbuf[(8 * sizeof(long) + 2) / 3 + 1 + /* NUL */ 1];
        /* this stuff for dealing with the buffer */
        ssize_t nwritten = 0;
 
@@ -791,7 +798,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
                 */
                flags = 0;
                field = precision = 0;
-               for ( ; (c = *fmt++) ; ) {
+               while ((c = *fmt++)) {
                        switch (c) {
                        case '#':
                                flags |= FL_HASH;
@@ -821,12 +828,17 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
 
                        case '*':
                                tmp = VA(int);
-                               if (flags & FL_DOT)
-                                       precision = tmp;
-                               else if ((field = tmp) < 0) {
-                                       field = -field;
-                                       flags |= FL_RIGHT;
-                               }
+                               if (tmp < 0) {
+                                       if (flags & FL_DOT)
+                                               precision = 0;
+                                       else {
+                                               field = (unsigned int)-tmp;
+                                               flags |= FL_RIGHT;
+                                       }
+                               } else if (flags & FL_DOT)
+                                       precision = (unsigned int)tmp;
+                               else
+                                       field = (unsigned int)tmp;
                                continue;
 
                        case 'l':
@@ -848,26 +860,23 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
                                bool overflowed = false;
 
                                tmp = ksh_numdig(c);
-                               while (c = *fmt++, ksh_isdigit(c)) {
+                               while (c = *fmt++, ksh_isdigit(c))
                                        if (notok2mul(2147483647, tmp, 10))
                                                overflowed = true;
-                                       tmp = tmp * 10 + ksh_numdig(c);
-                               }
+                                       else
+                                               tmp = tmp * 10 + ksh_numdig(c);
                                --fmt;
                                if (overflowed)
                                        tmp = 0;
                                if (flags & FL_DOT)
-                                       precision = tmp;
+                                       precision = (unsigned int)tmp;
                                else
-                                       field = tmp;
+                                       field = (unsigned int)tmp;
                                continue;
                        }
                        break;
                }
 
-               if (precision < 0)
-                       precision = 0;
-
                if (!c)
                        /* nasty format */
                        break;
@@ -905,6 +914,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
  integral:
                        flags |= FL_NUMBER;
                        cp = numbuf + sizeof(numbuf);
+                       *--cp = '\0';
 
                        switch (c) {
                        case 'd':
@@ -953,9 +963,9 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
                                        *--cp = (flags & FL_UPPER) ? 'X' : 'x';
                                        *--cp = '0';
                                }
+                           }
                        }
-                       }
-                       len = numbuf + sizeof(numbuf) - (s = cp);
+                       len = numbuf + sizeof(numbuf) - 1 - (s = cp);
                        if (flags & FL_DOT) {
                                if (precision > len) {
                                        field = precision;
@@ -999,7 +1009,6 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
                if (field > precision) {
                        field -= precision;
                        if (!(flags & FL_RIGHT)) {
-                               field = -field;
                                /* skip past sign or 0x when padding with 0 */
                                if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
                                        if (*s == '+' || *s == '-' ||
@@ -1012,7 +1021,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
                                                shf_putc(*s, shf);
                                                s++;
                                                nwritten++;
-                                               if (--precision > 0 &&
+                                               if (--precision &&
                                                    ksh_eq(*s, 'X', 'x')) {
                                                        shf_putc(*s, shf);
                                                        s++;
@@ -1023,32 +1032,23 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
                                        c = '0';
                                } else
                                        c = flags & FL_ZERO ? '0' : ' ';
-                               if (field < 0) {
-                                       nwritten += -field;
-                                       while (field < 0) {
-                                               shf_putc(c, shf);
-                                               ++field;
-                                       }
-                               }
+                               nwritten += field;
+                               while (field--)
+                                       shf_putc(c, shf);
+                               field = 0;
                        } else
                                c = ' ';
                } else
                        field = 0;
 
-               if (precision > 0) {
-                       const char *q;
+               nwritten += precision;
+               precision = utf_skipcols(s, precision, &tmp) - s;
+               while (precision--)
+                       shf_putc(*s++, shf);
 
-                       nwritten += precision;
-                       q = utf_skipcols(s, precision);
-                       do {
-                               shf_putc(*s, shf);
-                       } while (++s < q);
-               }
-               if (field > 0) {
-                       nwritten += field;
-                       for ( ; field > 0 ; --field)
-                               shf_putc(c, shf);
-               }
+               nwritten += field;
+               while (field--)
+                       shf_putc(c, shf);
        }
 
        return (shf_error(shf) ? -1 : nwritten);
diff --git a/src/signames.inc b/src/signames.inc
deleted file mode 100644 (file)
index 07811fd..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-               { "ABRT", 6 },
-               { "FPE", 8 },
-               { "ILL", 4 },
-               { "INT", 2 },
-               { "SEGV", 11 },
-               { "TERM", 15 },
-               { "ALRM", 14 },
-               { "BUS", 7 },
-               { "CHLD", 17 },
-               { "CONT", 18 },
-               { "HUP", 1 },
-               { "KILL", 9 },
-               { "PIPE", 13 },
-               { "QUIT", 3 },
-               { "STOP", 19 },
-               { "TSTP", 20 },
-               { "TTIN", 21 },
-               { "TTOU", 22 },
-               { "USR1", 10 },
-               { "USR2", 12 },
-               { "POLL", 29 },
-               { "PROF", 27 },
-               { "SYS", 31 },
-               { "TRAP", 5 },
-               { "URG", 23 },
-               { "VTALRM", 26 },
-               { "XCPU", 24 },
-               { "XFSZ", 25 },
-               { "WINCH", 28 },
-               { "PWR", 30 },
-               { "STKFLT", 16 },
index ab32418..8892c1a 100644 (file)
--- a/src/syn.c
+++ b/src/syn.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.109 2016/01/19 23:12:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.114 2016/08/04 20:32:14 tg Exp $");
 
 struct nesting_state {
        int start_token;        /* token than began nesting (eg, FOR) */
@@ -31,6 +31,7 @@ struct nesting_state {
 };
 
 struct yyrecursive_state {
+       struct ioword *old_heres[HERES];
        struct yyrecursive_state *next;
        struct ioword **old_herep;
        int old_symbol;
@@ -207,7 +208,7 @@ synio(int cf)
                        iop->ioflag |= IOEVAL;
                }
                if (herep > &heres[HERES - 1])
-                       yyerror("too many %ss\n", "<<");
+                       yyerror(Tf_toomany, "<<");
                *herep++ = iop;
        } else
                iop->ioname = yylval.cp;
@@ -216,16 +217,7 @@ synio(int cf)
                char *cp;
 
                nextiop = alloc(sizeof(*iop), ATEMP);
-#ifdef MKSH_CONSERVATIVE_FDS
                nextiop->ioname = cp = alloc(3, ATEMP);
-#else
-               nextiop->ioname = cp = alloc(5, ATEMP);
-
-               if (iop->unit > 9) {
-                       *cp++ = CHAR;
-                       *cp++ = digits_lc[iop->unit / 10];
-               }
-#endif
                *cp++ = CHAR;
                *cp++ = digits_lc[iop->unit % 10];
                *cp = EOS;
@@ -311,8 +303,8 @@ get_command(int cf)
                        case REDIR:
                                while ((iop = synio(cf)) != NULL) {
                                        if (iopn >= NUFILE)
-                                               yyerror("too many %ss\n",
-                                                   "redirection");
+                                               yyerror(Tf_toomany,
+                                                   Tredirection);
                                        iops[iopn++] = iop;
                                }
                                break;
@@ -447,8 +439,8 @@ get_command(int cf)
                t = newtp((c == FOR) ? TFOR : TSELECT);
                musthave(LWORD, CMDASN);
                if (!is_wdvarname(yylval.cp, true))
-                       yyerror("%s: %s\n", c == FOR ? "for" : Tselect,
-                           "bad identifier");
+                       yyerror("%s: bad identifier\n",
+                           c == FOR ? "for" : Tselect);
                strdupx(t->str, ident, ATEMP);
                nesting_push(&old_nesting, c);
                t->vars = wordlist();
@@ -511,7 +503,7 @@ get_command(int cf)
 
        while ((iop = synio(syniocf)) != NULL) {
                if (iopn >= NUFILE)
-                       yyerror("too many %ss\n", "redirection");
+                       yyerror(Tf_toomany, Tredirection);
                iops[iopn++] = iop;
        }
 
@@ -705,7 +697,7 @@ function_body(char *name,
         */
        for (p = sname; *p; p++)
                if (ctype(*p, C_QUOTE))
-                       yyerror("%s: %s\n", sname, "invalid function name");
+                       yyerror("%s: invalid function name\n", sname);
 
        /*
         * Note that POSIX allows only compound statements after foo(),
@@ -812,7 +804,7 @@ static const struct tokeninfo {
        { "done",       DONE,   true },
        { "in",         IN,     true },
        { Tfunction,    FUNCTION, true },
-       { "time",       TIME,   true },
+       { Ttime,        TIME,   true },
        { "{",          '{',    true },
        { Tcbrace,      '}',    true },
        { "!",          BANG,   true },
@@ -859,7 +851,7 @@ syntaxerr(const char *what)
        int c;
 
        if (!what)
-               what = "unexpected";
+               what = Tunexpected;
        REJECT;
        c = token(0);
  Again:
@@ -872,15 +864,15 @@ syntaxerr(const char *what)
                        goto Again;
                }
                /* don't quote the EOF */
-               yyerror("%s: %s %s\n", Tsynerr, "unexpected", "EOF");
+               yyerror("%s: unexpected EOF\n", Tsynerr);
                /* NOTREACHED */
 
        case LWORD:
-               s = snptreef(NULL, 32, "%S", yylval.cp);
+               s = snptreef(NULL, 32, Tf_S, yylval.cp);
                break;
 
        case REDIR:
-               s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
+               s = snptreef(redir, sizeof(redir), Tft_R, yylval.iop);
                break;
 
        default:
@@ -972,9 +964,11 @@ assign_command(const char *s, bool docommand)
 static int
 inalias(struct source *s)
 {
-       for (; s && s->type == SALIAS; s = s->next)
+       while (s && s->type == SALIAS) {
                if (!(s->flags & SF_ALIASEND))
                        return (1);
+               s = s->next;
+       }
        return (0);
 }
 
@@ -1175,7 +1169,9 @@ yyrecursive(int subtype MKSH_A_UNUSED)
        ys->old_reject = reject;
        ys->old_symbol = symbol;
        ACCEPT;
+       memcpy(ys->old_heres, heres, sizeof(heres));
        ys->old_herep = herep;
+       herep = heres;
        ys->old_salias = sALIAS;
        sALIAS = 0;
        ys->next = e->yyrecursive_statep;
@@ -1185,7 +1181,7 @@ yyrecursive(int subtype MKSH_A_UNUSED)
        yyrecursive_pop(false);
 
        /* t->left because nested(TPAREN, ...) hides our goodies there */
-       cp = snptreef(NULL, 0, "%T", t->left);
+       cp = snptreef(NULL, 0, Tf_T, t->left);
        tfree(t, ATEMP);
 
        return (cp);
@@ -1202,6 +1198,7 @@ yyrecursive_pop(bool popall)
        e->yyrecursive_statep = ys->next;
 
        sALIAS = ys->old_salias;
+       memcpy(heres, ys->old_heres, sizeof(heres));
        herep = ys->old_herep;
        reject = ys->old_reject;
        symbol = ys->old_symbol;
index 7a7df5d..ff15077 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.80 2016/01/14 22:30:43 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.86 2016/07/25 00:04:48 tg Exp $");
 
 #define INDENT 8
 
@@ -49,6 +49,7 @@ ptree(struct op *t, int indent, struct shf *shf)
        struct ioword **ioact;
        struct op *t1;
        int i;
+       const char *ccp;
 
  Chain:
        if (t == NULL)
@@ -56,12 +57,9 @@ ptree(struct op *t, int indent, struct shf *shf)
        switch (t->type) {
        case TCOM:
                prevent_semicolon = false;
-               /*
-                * special-case 'var=<<EOF' (rough; see
-                * exec.c:execute() for full code)
-                */
+               /* special-case 'var=<<EOF' (cf. exec.c:execute) */
                if (
-                   /* we have zero arguments, i.e. no programme to run */
+                   /* we have zero arguments, i.e. no program to run */
                    t->args[0] == NULL &&
                    /* we have exactly one variable assignment */
                    t->vars[0] != NULL && t->vars[1] == NULL &&
@@ -69,21 +67,27 @@ ptree(struct op *t, int indent, struct shf *shf)
                    t->ioact != NULL && t->ioact[0] != NULL &&
                    t->ioact[1] == NULL &&
                    /* of type "here document" (or "here string") */
-                   (t->ioact[0]->ioflag & IOTYPE) == IOHERE) {
-                       fptreef(shf, indent, "%S", t->vars[0]);
+                   (t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
+                   /* the variable assignment begins with a valid varname */
+                   (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
+                   /* and has no right-hand side (i.e. "varname=") */
+                   ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
+                   /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
+                   ccp[3] == '=' && ccp[4] == EOS))) {
+                       fptreef(shf, indent, Tf_S, t->vars[0]);
                        break;
                }
 
                if (t->vars) {
                        w = (const char **)t->vars;
                        while (*w)
-                               fptreef(shf, indent, "%S ", *w++);
+                               fptreef(shf, indent, Tf_S_, *w++);
                } else
                        shf_puts("#no-vars# ", shf);
                if (t->args) {
                        w = t->args;
                        while (*w)
-                               fptreef(shf, indent, "%S ", *w++);
+                               fptreef(shf, indent, Tf_S_, *w++);
                } else
                        shf_puts("#no-args# ", shf);
                break;
@@ -115,7 +119,7 @@ ptree(struct op *t, int indent, struct shf *shf)
                w = t->args;
                shf_puts("[[", shf);
                while (*w)
-                       fptreef(shf, indent, " %S", *w++);
+                       fptreef(shf, indent, Tf__S, *w++);
                shf_puts(" ]] ", shf);
                break;
        case TSELECT:
@@ -126,8 +130,8 @@ ptree(struct op *t, int indent, struct shf *shf)
                        shf_puts("in ", shf);
                        w = (const char **)t->vars;
                        while (*w)
-                               fptreef(shf, indent, "%S ", *w++);
-                       fptreef(shf, indent, "%;");
+                               fptreef(shf, indent, Tf_S_, *w++);
+                       fptreef(shf, indent, Tft_end);
                }
                fptreef(shf, indent + INDENT, "do%N%T", t->left);
                fptreef(shf, indent, "%;done ");
@@ -147,11 +151,9 @@ ptree(struct op *t, int indent, struct shf *shf)
                }
                fptreef(shf, indent, "%Nesac ");
                break;
-#ifdef DEBUG
        case TELIF:
-               internal_errorf("TELIF in tree.c:ptree() unexpected");
+               internal_errorf(TELIF_unexpected);
                /* FALLTHROUGH */
-#endif
        case TIF:
                i = 2;
                t1 = t;
@@ -159,19 +161,19 @@ ptree(struct op *t, int indent, struct shf *shf)
                do {
                        t1 = t1->right;
                        i = 0;
-                       fptreef(shf, indent, "%;");
+                       fptreef(shf, indent, Tft_end);
  process_TIF:
                        /* 5 == strlen("elif ") */
                        fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
                        t1 = t1->right;
                        if (t1->left != NULL) {
-                               fptreef(shf, indent, "%;");
+                               fptreef(shf, indent, Tft_end);
                                fptreef(shf, indent + INDENT, "%s%N%T",
                                    "then", t1->left);
                        }
                } while (t1->right && t1->right->type == TELIF);
                if (t1->right != NULL) {
-                       fptreef(shf, indent, "%;");
+                       fptreef(shf, indent, Tft_end);
                        fptreef(shf, indent + INDENT, "%s%N%T",
                            "else", t1->right);
                }
@@ -180,10 +182,10 @@ ptree(struct op *t, int indent, struct shf *shf)
        case TWHILE:
        case TUNTIL:
                /* 6 == strlen("while "/"until ") */
-               fptreef(shf, indent + 6, "%s %T",
+               fptreef(shf, indent + 6, Tf_s_T,
                    (t->type == TWHILE) ? "while" : "until",
                    t->left);
-               fptreef(shf, indent, "%;");
+               fptreef(shf, indent, Tft_end);
                fptreef(shf, indent + INDENT, "do%N%T", t->right);
                fptreef(shf, indent, "%;done ");
                break;
@@ -203,7 +205,7 @@ ptree(struct op *t, int indent, struct shf *shf)
                fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
                break;
        case TTIME:
-               fptreef(shf, indent, "%s %T", "time", t->left);
+               fptreef(shf, indent, Tf_s_T, Ttime, t->left);
                break;
        default:
                shf_puts("<botch>", shf);
@@ -225,7 +227,7 @@ ptree(struct op *t, int indent, struct shf *shf)
                            iop->heredoc) {
                                shf_putc('\n', shf);
                                shf_puts(iop->heredoc, shf);
-                               fptreef(shf, indent, "%s",
+                               fptreef(shf, indent, Tf_s,
                                    evalstr(iop->delim, 0));
                                need_nl = true;
                        }
@@ -254,7 +256,7 @@ pioact(struct shf *shf, struct ioword *iop)
            (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
            iop->unit + 1;
        if (iop->unit != expected)
-               shf_fprintf(shf, "%d", (int)iop->unit);
+               shf_fprintf(shf, Tf_d, (int)iop->unit);
 
        switch (type) {
        case IOREAD:
@@ -322,26 +324,34 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
                                ++wp;
                                goto wdvarput_csubst;
                        }
+                       /* FALLTHROUGH */
                case CHAR:
                        c = *wp++;
                        shf_putc(c, shf);
                        break;
-               case QCHAR: {
-                       bool doq;
-
+               case QCHAR:
                        c = *wp++;
-                       doq = (c == '"' || c == '`' || c == '$' || c == '\\');
-                       if (opmode & WDS_TPUTS) {
-                               if (quotelevel == 0)
-                                       doq = true;
-                       } else {
-                               doq = false;
-                       }
-                       if (doq)
-                               shf_putc('\\', shf);
+                       if (opmode & WDS_TPUTS)
+                               switch (c) {
+                               case '\n':
+                                       if (quotelevel == 0) {
+                                               c = '\'';
+                                               shf_putc(c, shf);
+                                               shf_putc('\n', shf);
+                                       }
+                                       break;
+                               default:
+                                       if (quotelevel == 0)
+                                               /* FALLTHROUGH */
+                               case '"':
+                               case '`':
+                               case '$':
+                               case '\\':
+                                         shf_putc('\\', shf);
+                                       break;
+                               }
                        shf_putc(c, shf);
                        break;
-               }
                case COMSUB:
                        shf_puts("$(", shf);
                        cs = ")";
@@ -460,7 +470,7 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
                                break;
                        case 'd':
                                /* signed decimal */
-                               shf_fprintf(shf, "%d", va_arg(va, int));
+                               shf_fprintf(shf, Tf_d, va_arg(va, int));
                                break;
                        case 'u':
                                /* unsigned decimal */
@@ -631,8 +641,8 @@ wdscan(const char *wp, int c)
                        break;
                default:
                        internal_warningf(
-                           "wdscan: unknown char 0x%x (carrying on)",
-                           wp[-1]);
+                           "wdscan: unknown char 0x%X (carrying on)",
+                           (unsigned char)wp[-1]);
                }
 }
 
@@ -756,7 +766,7 @@ vistree(char *dst, size_t sz, struct op *t)
        size_t n;
 
        buf = alloc(sz + 16, ATEMP);
-       snptreef(buf, sz + 16, "%T", t);
+       snptreef(buf, sz + 16, Tf_T, t);
        cp = buf;
  vist_loop:
        if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
@@ -817,7 +827,7 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
                        return (--wp);
                case ADELIM:
                        if (*wp == /*{*/'}') {
-                               shf_puts("]ADELIM(})", shf);
+                               shf_puts(/*{*/ "]ADELIM(})", shf);
                                return (wp + 1);
                        }
                        shf_puts("ADELIM=", shf);
@@ -852,10 +862,10 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
                        shf_puts("EXPRSUB<", shf);
                        goto dumpsub;
                case OQUOTE:
-                       shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
+                       shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel);
                        break;
                case CQUOTE:
-                       shf_fprintf(shf, "%d}CQUOTE", quotelevel);
+                       shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel);
                        if (quotelevel)
                                quotelevel--;
                        else
@@ -1095,7 +1105,7 @@ dumptree(struct shf *shf, struct op *t)
                goto dumpleftandout;
        OPEN(TFUNCT)
                shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
-                   t->u.ksh_func ? "yes" : "no");
+                   t->u.ksh_func ? Ttrue : Tfalse);
                goto dumpleftandout;
        OPEN(TTIME)
                goto dumpleftandout;
@@ -1122,7 +1132,7 @@ dumptree(struct shf *shf, struct op *t)
                break;
        OPEN(TEOF)
  dumpunexpected:
-               shf_puts("unexpected", shf);
+               shf_puts(Tunexpected, shf);
                break;
        OPEN(TELIF)
                goto dumpunexpected;
index d816eb1..bcde2f4 100644 (file)
--- a/src/var.c
+++ b/src/var.c
@@ -28,7 +28,7 @@
 #include <sys/sysctl.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.197 2016/01/14 22:49:33 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.207 2016/08/01 21:38:07 tg Exp $");
 
 /*-
  * Variables
@@ -133,7 +133,7 @@ initvar(void)
        struct tbl *tp;
 
        ktinit(APERM, &specials,
-           /* currently 14 specials: 75% of 32 = 2^5 */
+           /* currently 15 specials: 75% of 32 = 2^5 */
            5);
        while (i < V_MAX - 1) {
                tp = ktenter(&specials, initvar_names[i],
@@ -193,7 +193,7 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
                        char *cp;
 
                        /* gotcha! */
-                       cp = shf_smprintf("%s%s", str_val(vp), p);
+                       cp = shf_smprintf(Tf_ss, str_val(vp), p);
                        afree(ap, ATEMP);
                        n = ap = cp;
                        goto redo_from_ref;
@@ -241,19 +241,27 @@ global(const char *n)
        c = (unsigned char)vn[0];
        if (!ksh_isalphx(c)) {
                if (array)
-                       errorf("bad substitution");
-               vp = &vtemp;
+                       errorf(Tbadsubst);
+               vp = vtemp;
                vp->flag = DEFINED;
                vp->type = 0;
                vp->areap = ATEMP;
-               *vp->name = c;
                if (ksh_isdigit(c)) {
-                       if (getn(vn, &c) && (c <= l->argc))
-                               /* setstr can't fail here */
-                               setstr(vp, l->argv[c], KSH_RETURN_ERROR);
+                       if (getn(vn, &c)) {
+                               /* main.c:main_init() says 12 */
+                               shf_snprintf(vp->name, 12, Tf_d, c);
+                               if (c <= l->argc) {
+                                       /* setstr can't fail here */
+                                       setstr(vp, l->argv[c],
+                                           KSH_RETURN_ERROR);
+                               }
+                       } else
+                               vp->name[0] = '\0';
                        vp->flag |= RDONLY;
                        goto out;
                }
+               vp->name[0] = c;
+               vp->name[1] = '\0';
                vp->flag |= RDONLY;
                if (vn[1] != '\0')
                        goto out;
@@ -320,7 +328,7 @@ local(const char *n, bool copy)
        vn = array_index_calc(n, &array, &val);
        h = hash(vn);
        if (!ksh_isalphx(*vn)) {
-               vp = &vtemp;
+               vp = vtemp;
                vp->flag = DEFINED|RDONLY;
                vp->type = 0;
                vp->areap = ATEMP;
@@ -433,7 +441,7 @@ setstr(struct tbl *vq, const char *s, int error_ok)
 
        error_ok &= ~0x4;
        if ((vq->flag & RDONLY) && !no_ro_check) {
-               warningf(true, "read-only: %s", vq->name);
+               warningf(true, Tf_ro, vq->name);
                if (!error_ok)
                        errorfxz(2);
                return (0);
@@ -479,13 +487,12 @@ void
 setint(struct tbl *vq, mksh_ari_t n)
 {
        if (!(vq->flag&INTEGER)) {
-               struct tbl *vp = &vtemp;
-               vp->flag = (ISSET|INTEGER);
-               vp->type = 0;
-               vp->areap = ATEMP;
-               vp->val.i = n;
+               vtemp->flag = (ISSET|INTEGER);
+               vtemp->type = 0;
+               vtemp->areap = ATEMP;
+               vtemp->val.i = n;
                /* setstr can't fail here */
-               setstr(vq, str_val(vp), KSH_RETURN_ERROR);
+               setstr(vq, str_val(vtemp), KSH_RETURN_ERROR);
        } else
                vq->val.i = n;
        vq->flag |= ISSET;
@@ -642,14 +649,14 @@ formatstr(struct tbl *vp, const char *s)
 
        p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
        if (vp->flag & (RJUST|LJUST)) {
-               int slen = olen, i = 0;
+               int slen = olen;
 
                if (vp->flag & RJUST) {
-                       const char *qq = s;
+                       const char *qq;
                        int n = 0;
 
-                       while (i < slen)
-                               i += utf_widthadj(qq, &qq);
+                       qq = utf_skipcols(s, slen, &slen);
+
                        /* strip trailing spaces (AT&T uses qq[-1] == ' ') */
                        while (qq > s && ksh_isspace(qq[-1])) {
                                --qq;
@@ -761,7 +768,7 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
        }
        if (*val == '[') {
                if (new_refflag != SRF_NOP)
-                       errorf("%s: %s", var,
+                       errorf(Tf_sD_s, var,
                            "reference variable can't be an array");
                len = array_ref_len(val);
                if (len == 0)
@@ -822,12 +829,13 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
                /* check target value for being a valid variable name */
                ccp = skip_varname(qval, false);
                if (ccp == qval) {
-                       if (ksh_isdigit(qval[0])) {
-                               int c;
+                       int c;
 
-                               if (getn(qval, &c))
-                                       goto nameref_rhs_checked;
-                       } else if (qval[1] == '\0') switch (qval[0]) {
+                       if (!(c = (unsigned char)qval[0]))
+                               goto nameref_empty;
+                       else if (ksh_isdigit(c) && getn(qval, &c))
+                               goto nameref_rhs_checked;
+                       else if (qval[1] == '\0') switch (c) {
                        case '$':
                        case '!':
                        case '?':
@@ -836,7 +844,7 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
                                goto nameref_rhs_checked;
                        }
  nameref_empty:
-                       errorf("%s: %s", var, "empty nameref target");
+                       errorf(Tf_sD_s, var, "empty nameref target");
                }
                len = (*ccp == '[') ? array_ref_len(ccp) : 0;
                if (ccp[len]) {
@@ -845,14 +853,14 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
                         * junk after it" and "invalid array"; in the
                         * latter case, len is also 0 and points to '['
                         */
-                       errorf("%s: %s", qval,
+                       errorf(Tf_sD_s, qval,
                            "nameref target not a valid parameter name");
                }
  nameref_rhs_checked:
                /* prevent nameref loops */
                while (qval) {
                        if (!strcmp(qval, tvar))
-                               errorf("%s: %s", qval,
+                               errorf(Tf_sD_s, qval,
                                    "expression recurses on parameter");
                        varsearch(e->loc, &vp, qval, hash(qval));
                        qval = NULL;
@@ -862,9 +870,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
        }
 
        /* prevent typeset from creating a local PATH/ENV/SHELL */
-       if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
-           strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
-               errorf("%s: %s", tvar, "restricted");
+       if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
+           strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
+               errorf(Tf_sD_s, tvar, "restricted");
 
        innermost_refflag = new_refflag;
        vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
@@ -901,7 +909,7 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
        if ((vpbase->flag & RDONLY) &&
            (val || clr || (set & ~EXPORT)))
                /* XXX check calls - is error here ok by POSIX? */
-               errorfx(2, "read-only: %s", tvar);
+               errorfx(2, Tf_ro, tvar);
        afree(tvar, ATEMP);
 
        /* most calls are with set/clr == 0 */
@@ -974,7 +982,7 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
                char *tval;
 
                if (vappend) {
-                       tval = shf_smprintf("%s%s", str_val(vp), val);
+                       tval = shf_smprintf(Tf_ss, str_val(vp), val);
                        val = tval;
                } else
                        tval = NULL;
@@ -1178,6 +1186,10 @@ unspecial(const char *name)
 static time_t seconds;         /* time SECONDS last set */
 static mksh_uari_t user_lineno;        /* what user set $LINENO to */
 
+/* minimum values from the OS we consider sane, lowered for R53 */
+#define MIN_COLS       4
+#define MIN_LINS       2
+
 static void
 getspec(struct tbl *vp)
 {
@@ -1278,6 +1290,11 @@ setspec(struct tbl *vp)
                /* clear tracked aliases */
                flushcom(true);
                return;
+#ifndef MKSH_NO_CMDLINE_EDITING
+       case V_TERM:
+               x_initterm(str_val(vp));
+               return;
+#endif
        case V_TMPDIR:
                afree(tmpdir, APERM);
                tmpdir = NULL;
@@ -1315,7 +1332,7 @@ setspec(struct tbl *vp)
                if (getint(vp, &num, false) == -1) {
                        s = str_val(vp);
                        if (st != V_RANDOM)
-                               errorf("%s: %s: %s", vp->name, "bad number", s);
+                               errorf(Tf_sD_sD_s, vp->name, "bad number", s);
                        num.u = hash(s);
                }
                vp->flag |= SPECIAL;
@@ -1394,6 +1411,11 @@ unsetspec(struct tbl *vp)
                /* clear tracked aliases */
                flushcom(true);
                break;
+#ifndef MKSH_NO_CMDLINE_EDITING
+       case V_TERM:
+               x_initterm(null);
+               return;
+#endif
        case V_TMPDIR:
                /* should not become unspecial */
                if (tmpdir) {
@@ -1519,7 +1541,7 @@ set_array(const char *var, bool reset, const char **vals)
 
        /* Note: AT&T ksh allows set -A but not set +A of a read-only var */
        if ((vp->flag&RDONLY))
-               errorfx(2, "read-only: %s", ccp);
+               errorfx(2, Tf_ro, ccp);
        /* This code is quite non-optimal */
        if (reset) {
                /* trash existing values and attributes */
@@ -1717,3 +1739,15 @@ rndpush(const void *s)
        BAFHUpdateOctet_reg(h, 0);
        qh_state = h;
 }
+
+/* record last glob match */
+void
+record_match(const char *istr)
+{
+       struct tbl *vp;
+
+       vp = local("KSH_MATCH", false);
+       unset(vp, 1);
+       vp->flag = DEFINED | RDONLY;
+       setstr(vp, istr, 0x4);
+}
index 7d09584..51cda3e 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009, 2011, 2012
+ * Copyright (c) 2009, 2011, 2012, 2016
  *     mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -19,7 +19,7 @@
  */
 
 #if defined(VARSPEC_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.7 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.9 2016/07/25 21:02:13 tg Exp $");
 #define FN(name)                       /* nothing */
 #elif defined(VARSPEC_ENUMS)
 #define FN(name)                       V_##name,
@@ -53,6 +53,9 @@ FN(OPTIND)
 FN(PATH)
 FN(RANDOM)
 FN(SECONDS)
+#ifndef MKSH_NO_CMDLINE_EDITING
+FN(TERM)
+#endif
 FN(TMOUT)
 FN(TMPDIR)