OSDN Git Service

Upgrade to mksh R40.
authorElliott Hughes <enh@google.com>
Thu, 8 Dec 2016 23:56:04 +0000 (15:56 -0800)
committerElliott Hughes <enh@google.com>
Thu, 8 Dec 2016 23:59:56 +0000 (15:59 -0800)
From the release notes...

R54 is a bugfix release with moderate new features:

[tg] Simplify and improve code and manual page
[tg] Try GCC 5’s new -malign-data=abi
[tg] Allow interrupting builtin cat even on fast devices (LP#1616692)
[tg] Update to Unicode 9.0.0
[Andreas Buschka] Correct English spelling
[tg] Handle set -e-related error propagation in || and && constructs correctly
[tg] Initialise memory for RNG even when not targeting Valgrind
[tg] Shrink binary size
[Brian Callahan] Improve support for the contemporary pcc compiler
[tg] Fix side effects with lazy evaluation; spotted by ormaaj
[tg] New flags -c (columnise), -l, -N for the print builtin
[Larry Hynes] Fix English, spelling mistakes, typos in the manpage
[tg, ormaah] Return 128+SIGALRM if read -t times out, like GNU bash
[Martijn Dekker] Install both manpages from Build.sh
[Martijn Dekker] Document case changes are ASCII-only
[Ronald G. Minnich, Elbing Miss, Álvaro Jurado, tg] Begin porting to Harvey-OS and APEX (similar to Plan 9 and APE)
[KO Myung-Hun] More infrastructure for the OS/2 (EMX, KLIBC) port

I've continued to disable the cat, realpath, and sleep builtins,
and added print to the list (now I've realized it's unrelated to
the print on the desktop).

Bug: N/A
Test: manual
Change-Id: Iead4c1b572c6f288b2ab2b884125dcb606f1e356

20 files changed:
Android.mk
Makefrag.inc [deleted file]
src/Build.sh [changed mode: 0644->0755]
src/check.t
src/edit.c
src/emacsfn.h
src/eval.c
src/exec.c
src/expr.c
src/funcs.c
src/histrap.c
src/lksh.1
src/main.c
src/misc.c
src/mksh.1
src/sh.h
src/signames.inc [new file with mode: 0644]
src/syn.c
src/var.c
src/var_spec.h

index 4cff508..427aea9 100644 (file)
@@ -83,6 +83,6 @@ LOCAL_CFLAGS += \
     -DHAVE_SETGROUPS=1 -DHAVE_STRERROR=1 -DHAVE_STRSIGNAL=0 \
     -DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \
     -DHAVE_SYS_ERRLIST_DECL=0 -DHAVE_SYS_SIGLIST_DECL=1 \
-    -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=530
+    -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=541
 
 include $(BUILD_EXECUTABLE)
diff --git a/Makefrag.inc b/Makefrag.inc
deleted file mode 100644 (file)
index 0f29116..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# Makefile fragment for building mksh R51 2015/07/10
-
-PROG=          mksh
-MAN=           mksh.1
-SRCS=          lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c lex.c main.c misc.c shf.c syn.c tree.c var.c edit.c
-SRCS_FP=       ../src/lalloc.c ../src/eval.c ../src/exec.c ../src/expr.c ../src/funcs.c ../src/histrap.c ../src/jobs.c ../src/lex.c ../src/main.c ../src/misc.c ../src/shf.c ../src/syn.c ../src/tree.c ../src/var.c ../src/edit.c
-OBJS_BP=        lalloc.o eval.o exec.o expr.o funcs.o histrap.o jobs.o lex.o main.o misc.o shf.o syn.o tree.o var.o edit.o
-INDSRCS=       emacsfn.h rlimits.opt sh.h sh_flags.opt var_spec.h
-NONSRCS_INST=  dot.mkshrc $(MAN)
-NONSRCS_NOINST=        Build.sh Makefile Rebuild.sh check.pl check.t test.sh
-CC=            /huge-ssd/aosp-arm64/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/*-gcc
-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 -Wno-deprecated-declarations -fno-asynchronous-unwind-tables -fstack-protector-strong -fwrapv
-CPPFLAGS=      -I. -I'../src'  -isystem /huge-ssd/aosp-arm64/bionic/libc/arch-arm64/include -isystem /huge-ssd/aosp-arm64/bionic/libc/include -isystem /huge-ssd/aosp-arm64/bionic/libc/kernel/uapi -isystem /huge-ssd/aosp-arm64/bionic/libc/kernel/uapi/asm-arm64 -isystem /huge-ssd/aosp-arm64/bionic/libm/include -isystem /huge-ssd/aosp-arm64/bionic/libm/include/arm64 -D_FORTIFY_SOURCE=2 -include /huge-ssd/aosp-arm64/build/core/combo/include/arch/linux-arm64/AndroidConfig.h -I/huge-ssd/aosp-arm64/build/core/combo/include/arch/linux-arm64/ -DANDROID -DNDEBUG -UDEBUG -DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 -DMKSH_CONSERVATIVE_FDS -DMKSH_DONT_EMIT_IDSTRING -DMKSH_NOPWNAM -DMKSH_BUILDSH -D_GNU_SOURCE -DSETUID_CAN_FAIL_WITH_EAGAIN -DHAVE_ATTRIBUTE_BOUNDED=0 -DHAVE_ATTRIBUTE_FORMAT=1 -DHAVE_ATTRIBUTE_NORETURN=1 -DHAVE_ATTRIBUTE_PURE=1 -DHAVE_ATTRIBUTE_UNUSED=1 -DHAVE_ATTRIBUTE_USED=1 -DHAVE_SYS_TIME_H=1 -DHAVE_TIME_H=1 -DHAVE_BOTH_TIME_H=1 -DHAVE_SYS_BSDTYPES_H=0 -DHAVE_SYS_FILE_H=1 -DHAVE_SYS_MKDEV_H=0 -DHAVE_SYS_MMAN_H=1 -DHAVE_SYS_PARAM_H=1 -DHAVE_SYS_RESOURCE_H=1 -DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SYSMACROS_H=1 -DHAVE_BSTRING_H=0 -DHAVE_GRP_H=1 -DHAVE_IO_H=0 -DHAVE_LIBGEN_H=1 -DHAVE_LIBUTIL_H=0 -DHAVE_PATHS_H=1 -DHAVE_STDINT_H=1 -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_SYS_SIGLIST=1 -DHAVE_FLOCK=1 -DHAVE_LOCK_FCNTL=1 -DHAVE_GETRUSAGE=1 -DHAVE_GETSID=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_ISSETUGID=0 -DHAVE_KILLPG=1 -DHAVE_MEMMOVE=1 -DHAVE_MKNOD=0 -DHAVE_MMAP=1 -DHAVE_NICE=1 -DHAVE_REVOKE=0 -DHAVE_SETLOCALE_CTYPE=0 -DHAVE_LANGINFO_CODESET=0 -DHAVE_SELECT=1 -DHAVE_SETRESUGID=1 -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=511
-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 /huge-ssd/aosp-arm64/out/target/product/flounder/obj/lib/crtbegin_dynamic.o
-LIBS=           -L/huge-ssd/aosp-arm64/out/target/product/flounder/obj/lib -Wl,-rpath-link=/huge-ssd/aosp-arm64/out/target/product/flounder/obj/lib -Wl,--no-whole-archive /huge-ssd/aosp-arm64/out/target/product/flounder/obj/STATIC_LIBRARIES/libcompiler_rt-extras_intermediates/libcompiler_rt-extras.a -lc /huge-ssd/aosp-arm64/out/target/product/flounder/obj/lib/crtend_android.o
-
-.depend $(OBJS_BP): rlimits.gen sh_flags.gen
-rlimits.gen: ../src/Build.sh ../src/rlimits.opt
-                       srcfile=../src/rlimits.opt; BUILDSH_RUN_GENOPT=1; . ../src/Build.sh
-sh_flags.gen: ../src/Build.sh ../src/sh_flags.opt
-                       srcfile=../src/sh_flags.opt; BUILDSH_RUN_GENOPT=1; . ../src/Build.sh
-
-# not BSD make only:
-#VPATH=                ../src
-#all: $(PROG)
-#$(PROG): $(OBJS_BP)
-#      $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS_BP) $(LIBS)
-#$(OBJS_BP): $(SRCS_FP) $(NONSRCS)
-#.c.o:
-#      $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
-
-# for all make variants:
-#REGRESS_FLAGS=        -f
-#regress:
-#      ./test.sh $(REGRESS_FLAGS)
-check_categories= shell:legacy-no int:32 android convfds no-histfile
-
-# for BSD make only:
-#.PATH: ../src
-#.include <bsd.prog.mk>
old mode 100644 (file)
new mode 100755 (executable)
index d31a7ca..d0ff130
@@ -1,5 +1,5 @@
 #!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.702 2016/08/10 18:20:16 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.707 2016/11/11 23:31:29 tg Exp $'
 #-
 # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 #              2011, 2012, 2013, 2014, 2015, 2016
@@ -120,7 +120,7 @@ do_genopt() {
                        state=3
                        ;;
                1:@@)
-                       # begin of data block
+                       # start of data block
                        o_gen=$o_gen$nl"#endif"
                        o_gen=$o_gen$nl"#ifndef F0"
                        o_gen=$o_gen$nl"#define F0 FN"
@@ -133,7 +133,7 @@ do_genopt() {
                        o_hdr=$o_hdr$nl$line
                        ;;
                0:@*|1:@*)
-                       # begin of a definition block
+                       # start of a definition block
                        sym=`echo "$line" | sed 's/^@//'`
                        if test $state = 0; then
                                o_gen=$o_gen$nl"#if defined($sym)"
@@ -584,7 +584,7 @@ if test -d $tfn || test -d $tfn.exe; then
        exit 1
 fi
 rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \
-    *.ll *.o *.gen Rebuild.sh lft no signames.inc test.sh x vv.out
+    *.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out
 
 SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
 SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
@@ -755,6 +755,24 @@ GNU/kFreeBSD)
 Haiku)
        add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
        ;;
+Harvey)
+       add_cppflags -D_POSIX_SOURCE
+       add_cppflags -D_LIMITS_EXTENSION
+       add_cppflags -D_BSD_EXTENSION
+       add_cppflags -D_SUSV2_SOURCE
+       add_cppflags -D_GNU_SOURCE
+       add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+       add_cppflags -DMKSH_NO_CMDLINE_EDITING
+       add_cppflags -DMKSH__NO_SETEUGID
+       oswarn=' and will currently not work'
+       add_cppflags -DMKSH_UNEMPLOYED
+       add_cppflags -DMKSH_NOPROSPECTOFWORK
+       # these taken from Harvey-OS github and need re-checking
+       add_cppflags -D_setjmp=setjmp -D_longjmp=longjmp
+       : "${HAVE_CAN_NO_EH_FRAME=0}"
+       : "${HAVE_CAN_FNOSTRICTALIASING=0}"
+       : "${HAVE_CAN_FSTACKPROTECTORSTRONG=0}"
+       ;;
 HP-UX)
        ;;
 Interix)
@@ -833,12 +851,14 @@ OpenBSD)
 OS/2)
        HAVE_TERMIOS_H=0
        HAVE_MKNOD=0    # setmode() incompatible
-       oswarn="; it is currently being ported"
+       oswarn="; it is currently being ported, get it from"
+       oswarn="$oswarn${nl}https://github.com/komh/mksh-os2 in the meanwhile"
        check_categories="$check_categories nosymlink"
        : "${CC=gcc}"
        : "${SIZE=: size}"
        add_cppflags -DMKSH_UNEMPLOYED
        add_cppflags -DMKSH_NOPROSPECTOFWORK
+       add_cppflags -DMKSH_NO_LIMITS
        ;;
 OSF1)
        HAVE_SIG_T=0    # incompatible
@@ -980,7 +1000,7 @@ drop us a success or failure notice or even send in diffs.
 $e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
 
 #
-# Begin of mirtoconf checks
+# Start of mirtoconf checks
 #
 $e $bi$me: Scanning for functions... please ignore any errors.$ao
 
@@ -1458,6 +1478,7 @@ gcc)
                ac_flags $t_use $t_name "$t_cflags" \
                    "if gcc supports $t_cflags $t_ldflags" "$t_ldflags"
        done
+       ac_flags 1 data_abi_align -malign-data=abi
        i=1
        ;;
 hpcc)
@@ -1486,8 +1507,12 @@ msc)
        ac_flags 1 wp64 "${ccpc}/Wp64" 'to enable 64-bit warnings'
        ;;
 nwcc)
-       i=1
        #broken# ac_flags 1 ssp -stackprotect
+       i=1
+       ;;
+pcc)
+       ac_flags 1 fstackprotectorall -fstack-protector-all
+       i=1
        ;;
 sunpro)
        phase=u
@@ -2342,7 +2367,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=530
+add_cppflags -DMKSH_BUILD_R=541
 
 $e $bi$me: Finished configuration testing, now producing output.$ao
 
@@ -2588,8 +2613,8 @@ esac
 tcfn=$mkshexe
 test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
 test -f $tcfn || exit 1
-test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >$tfn.cat1" || \
-    rmf $tfn.cat1
+test 1 = $r || v "$NROFF -mdoc <'$srcdir/lksh.1' >lksh.cat1" || rmf lksh.cat1
+test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >mksh.cat1" || rmf mksh.cat1
 test 0 = $eq && v $SIZE $tcfn
 i=install
 test -f /usr/ucb/$i && i=/usr/ucb/$i
@@ -2603,12 +2628,14 @@ if test $legacy = 0; then
 fi
 $e
 $e Installing the manual:
-if test -f $tfn.cat1; then
-       $e "# $i -c -o root -g bin -m 444 $tfn.cat1" \
-           "/usr/share/man/cat1/$tfn.0"
+if test -f mksh.cat1; then
+       $e "# $i -c -o root -g bin -m 444 lksh.cat1" \
+           "/usr/share/man/cat1/lksh.0"
+       $e "# $i -c -o root -g bin -m 444 mksh.cat1" \
+           "/usr/share/man/cat1/mksh.0"
        $e or
 fi
-$e "# $i -c -o root -g bin -m 444 $tfn.1 /usr/share/man/man1/$tfn.1"
+$e "# $i -c -o root -g bin -m 444 lksh.1 mksh.1 /usr/share/man/man1/"
 $e
 $e Run the regression test suite: ./test.sh
 $e Please also read the sample file dot.mkshrc and the fine manual.
index f221442..1b03af6 100644 (file)
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.t,v 1.751 2016/08/12 16:48:02 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.756 2016/11/11 23:31:31 tg Exp $
 # -*- mode: sh -*-
 #-
 # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
@@ -30,7 +30,7 @@
 # (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
 
 expected-stdout:
-       @(#)MIRBSD KSH R53 2016/08/12
+       @(#)MIRBSD KSH R54 2016/11/11
 description:
        Check version of shell.
 stdin:
@@ -39,7 +39,7 @@ name: KSH_VERSION
 category: shell:legacy-no
 ---
 expected-stdout:
-       @(#)LEGACY KSH R53 2016/08/12
+       @(#)LEGACY KSH R54 2016/11/11
 description:
        Check version of legacy shell.
 stdin:
@@ -340,6 +340,62 @@ expected-stdout:
        2
        0
 ---
+name: arith-lazy-5-arr-n
+description: Check lazy evaluation with side effects
+stdin:
+       a=0; echo "$((0&&b[a++],a))"
+expected-stdout:
+       0
+---
+name: arith-lazy-5-arr-p
+description: Check lazy evaluation with side effects
+stdin:
+       a=0; echo "$((0&&(b[a++]),a))"
+expected-stdout:
+       0
+---
+name: arith-lazy-5-str-n
+description: Check lazy evaluation with side effects
+stdin:
+       a=0 b=a++; ((0&&b)); echo $a
+expected-stdout:
+       0
+---
+name: arith-lazy-5-str-p
+description: Check lazy evaluation with side effects
+stdin:
+       a=0 b=a++; ((0&&(b))); echo $a
+expected-stdout:
+       0
+---
+name: arith-lazy-5-tern-l-n
+description: Check lazy evaluation with side effects
+stdin:
+       a=0; echo "$((0?b[a++]:999,a))"
+expected-stdout:
+       0
+---
+name: arith-lazy-5-tern-l-p
+description: Check lazy evaluation with side effects
+stdin:
+       a=0; echo "$((0?(b[a++]):999,a))"
+expected-stdout:
+       0
+---
+name: arith-lazy-5-tern-r-n
+description: Check lazy evaluation with side effects
+stdin:
+       a=0; echo "$((1?999:b[a++],a))"
+expected-stdout:
+       0
+---
+name: arith-lazy-5-tern-r-p
+description: Check lazy evaluation with side effects
+stdin:
+       a=0; echo "$((1?999:(b[a++]),a))"
+expected-stdout:
+       0
+---
 name: arith-ternary-prec-1
 description:
        Check precedence of ternary operator vs assignment
@@ -4892,7 +4948,7 @@ name: integer-base-check-flat
 description:
        Check behaviour does not match POSuX (except if set -o posix),
        because a not type-safe scripting language has *no* business
-       interpreting the string "010" as octal numer eight (dangerous).
+       interpreting the string "010" as octal number eight (dangerous).
 stdin:
        echo 1 "$("$__progname" -c 'echo :$((10))/$((010)),$((0x10)):')" .
        echo 2 "$("$__progname" -o posix -c 'echo :$((10))/$((010)),$((0x10)):')" .
@@ -7152,6 +7208,19 @@ stdin:
        db_go
        exit 0
 ---
+name: exit-err-9
+description:
+       "set -e" versus bang pipelines
+stdin:
+       set -e
+       ! false | false
+       echo 1 ok
+       ! false && false
+       echo 2 wrong
+expected-stdout:
+       1 ok
+expected-exit: 1
+---
 name: exit-enoent-1
 description:
        SUSv4 says that the shell should exit with 126/127 in some situations
@@ -11754,7 +11823,7 @@ stdin:
        echo "before:   x<$x> y<$y> z<$z> R<$REPLY>"
        x=${|
                local y
-               echo "begin:    x<$x> y<$y> z<$z> R<$REPLY>"
+               echo "start:    x<$x> y<$y> z<$z> R<$REPLY>"
                x=5
                y=6
                z=7
@@ -11769,7 +11838,7 @@ stdin:
        echo ${|true;}$(true).
 expected-stdout:
        before: x<1> y<2> z<3> R<4>
-       begin:  x<1> y<> z<3> R<>
+       start:  x<1> y<> z<3> R<>
        end:    x<5> y<6> z<7> R<8>
        after:  x<8> y<2> z<7> R<4>
        typeset t=$'foo\n\n'
index d13df5e..4ed5ca3 100644 (file)
@@ -28,7 +28,7 @@
 
 #ifndef MKSH_NO_CMDLINE_EDITING
 
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.306 2016/08/01 18:42:40 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.312 2016/11/11 23:48:28 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
@@ -238,6 +238,7 @@ x_print_expansions(int nwords, char * const *words, bool is_command)
        bool use_copy = false;
        size_t prefix_len;
        XPtrV l = { NULL, 0, 0 };
+       struct columnise_opts co;
 
        /*
         * Check if all matches are in the same directory (in this
@@ -257,7 +258,8 @@ x_print_expansions(int nwords, char * const *words, bool is_command)
                                break;
                /* All in same directory? */
                if (i == nwords) {
-                       while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
+                       while (prefix_len > 0 &&
+                           !mksh_cdirsep(words[0][prefix_len - 1]))
                                prefix_len--;
                        use_copy = true;
                        XPinit(l, nwords + 1);
@@ -271,7 +273,11 @@ x_print_expansions(int nwords, char * const *words, bool is_command)
         */
        x_putc('\r');
        x_putc('\n');
-       pr_list(use_copy ? (char **)XPptrv(l) : words);
+       co.shf = shl_out;
+       co.linesep = '\n';
+       co.do_last = true;
+       co.prefcol = false;
+       pr_list(&co, use_copy ? (char **)XPptrv(l) : words);
 
        if (use_copy)
                /* not x_free_words() */
@@ -333,7 +339,7 @@ x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
         * and if so, discern "~foo/bar" and "~/baz" from "~blah";
         * if we have a directory part (the former), try to expand
         */
-       if (*s == '~' && (cp = strchr(s, '/')) != NULL) {
+       if (*s == '~' && (cp = mksh_sdirsep(s)) != NULL) {
                /* ok, so split into "~foo"/"bar" or "~"/"baz" */
                *cp++ = 0;
                /* try to expand the tilde */
@@ -588,7 +594,7 @@ x_locate_word(const char *buf, int buflen, int pos, int *startp,
                         * like file globbing.
                         */
                        for (p = start; p < end; p++)
-                               if (buf[p] == '/')
+                               if (mksh_cdirsep(buf[p]))
                                        break;
                        iscmd = p == end;
                }
@@ -652,7 +658,7 @@ x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
                        }
                }
 
-               if (*toglob == '~' && !vstrchr(toglob, '/')) {
+               if (*toglob == '~' && !mksh_vdirsep(toglob)) {
                        /* neither for '~foo' (but '~foo/bar') */
                        *flagsp |= XCF_IS_NOSPACE;
                        goto dont_add_glob;
@@ -741,13 +747,15 @@ x_basename(const char *s, const char *se)
        if (s == se)
                return (0);
 
-       /* Skip trailing slashes */
-       for (p = se - 1; p > s && *p == '/'; p--)
-               ;
-       for (; p > s && *p != '/'; p--)
-               ;
-       if (*p == '/' && p + 1 < se)
-               p++;
+       /* skip trailing directory separators */
+       p = se - 1;
+       while (p > s && mksh_cdirsep(*p))
+               --p;
+       /* drop last component */
+       while (p > s && !mksh_cdirsep(*p))
+               --p;
+       if (mksh_cdirsep(*p) && p + 1 < se)
+               ++p;
 
        return (p - s);
 }
@@ -1048,7 +1056,7 @@ static struct x_defbindings const x_defbindings[] = {
        { XFUNC_end_hist,               1,      '>'     },
        { XFUNC_goto_hist,              1,      'g'     },
        { XFUNC_mv_end,                 0, CTRL('E')    },
-       { XFUNC_mv_begin,               0, CTRL('A')    },
+       { XFUNC_mv_beg,                 0, CTRL('A')    },
        { XFUNC_draw_line,              0, CTRL('L')    },
        { XFUNC_cls,                    1, CTRL('L')    },
        { XFUNC_meta1,                  0, CTRL('[')    },
@@ -1106,8 +1114,8 @@ static struct x_defbindings const x_defbindings[] = {
        { XFUNC_mv_back,                2,      'D'     },
 #ifndef MKSH_SMALL
        { XFUNC_vt_hack,                2,      '1'     },
-       { XFUNC_mv_begin | 0x80,        2,      '7'     },
-       { XFUNC_mv_begin,               2,      'H'     },
+       { XFUNC_mv_beg | 0x80,          2,      '7'     },
+       { XFUNC_mv_beg,                 2,      'H'     },
        { XFUNC_mv_end | 0x80,          2,      '4'     },
        { XFUNC_mv_end | 0x80,          2,      '8'     },
        { XFUNC_mv_end,                 2,      'F'     },
@@ -1119,7 +1127,7 @@ static struct x_defbindings const x_defbindings[] = {
        /* PC scancodes */
 #if !defined(MKSH_SMALL) || defined(__OS2__)
        { XFUNC_meta3,                  0,      0       },
-       { XFUNC_mv_begin,               3,      71      },
+       { XFUNC_mv_beg,                 3,      71      },
        { XFUNC_prev_com,               3,      72      },
 #ifndef MKSH_SMALL
        { XFUNC_search_hist_up,         3,      73      },
@@ -2044,7 +2052,7 @@ x_mv_end(int c MKSH_A_UNUSED)
 }
 
 static int
-x_mv_begin(int c MKSH_A_UNUSED)
+x_mv_beg(int c MKSH_A_UNUSED)
 {
        x_goto(xbuf);
        return (KSTD);
@@ -2328,7 +2336,7 @@ x_vt_hack(int c)
        case '~':
                x_arg = 1;
                x_arg_defaulted = true;
-               return (x_mv_begin(0));
+               return (x_mv_beg(0));
        case ';':
                /* "interesting" sequence detected */
                break;
@@ -2778,7 +2786,7 @@ do_complete(
         * append a space if this is a single non-directory match
         * and not a parameter or homedir substitution
         */
-       if (nwords == 1 && words[0][nlen - 1] != '/' &&
+       if (nwords == 1 && !mksh_cdirsep(words[0][nlen - 1]) &&
            !(flags & XCF_IS_NOSPACE)) {
                x_ins(T1space);
        }
@@ -4016,7 +4024,7 @@ vi_insert(int ch)
                else
                        return (redo_insert(lastac - 1));
 
-       /* { Begin nonstandard vi commands */
+       /* { start nonstandard vi commands */
        case CTRL('x'):
                expand_word(0);
                break;
@@ -4035,7 +4043,7 @@ vi_insert(int ch)
                        break;
                }
                /* FALLTHROUGH */
-       /* End nonstandard vi commands } */
+       /* end nonstandard vi commands } */
 
        default:
                if (es->linelen >= es->cbufsize - 1)
@@ -4157,6 +4165,8 @@ vi_cmd(int argcnt, const char *cmd)
                case 'Y':
                        cmd = "y$";
                        /* ahhhhhh... */
+
+                       /* FALLTHROUGH */
                case 'c':
                case 'd':
                case 'y':
@@ -4393,6 +4403,8 @@ vi_cmd(int argcnt, const char *cmd)
                        if (hnum == hlast)
                                hnum = -1;
                        /* ahhh */
+
+                       /* FALLTHROUGH */
                case '/':
                        c3 = 1;
                        srchlen = 0;
@@ -4531,6 +4543,7 @@ vi_cmd(int argcnt, const char *cmd)
                case CTRL('['):
                        if (!Flag(FVIESCCOMPLETE))
                                return (-1);
+                       /* FALLTHROUGH */
                /* AT&T ksh */
                case '\\':
                /* Nonstandard vi/ksh */
@@ -4603,8 +4616,7 @@ domove(int argcnt, const char *cmd, int sub)
        case 'T':
                fsavecmd = *cmd;
                fsavech = cmd[1];
-               /* drop through */
-
+               /* FALLTHROUGH */
        case ',':
        case ';':
                if (fsavecmd == ' ')
@@ -5399,7 +5411,7 @@ complete_word(int cmd, int count)
                 * append a space if this is a non-directory match
                 * and not a parameter or homedir substitution
                 */
-               if (match_len > 0 && match[match_len - 1] != '/' &&
+               if (match_len > 0 && !mksh_cdirsep(match[match_len - 1]) &&
                    !(flags & XCF_IS_NOSPACE))
                        rval = putbuf(T1space, 1, false);
        }
index 008db94..a1e8e82 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 #if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.9 2016/07/26 21:50:30 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.10 2016/09/01 12:59:09 tg Exp $");
 #define FN(cname,sname,flags)  static int x_##cname(int);
 #elif defined(EMACSFN_ENUMS)
 #define FN(cname,sname,flags)  XFUNC_##cname,
@@ -78,7 +78,7 @@ FN(meta2, "prefix-2", XF_PREFIX)
 FN(meta3, "prefix-3", XF_PREFIX)
 FN(meta_yank, "yank-pop", 0)
 FN(mv_back, "backward-char", XF_ARG)
-FN(mv_begin, "beginning-of-line", 0)
+FN(mv_beg, "beginning-of-line", 0)
 FN(mv_bword, "backward-word", XF_ARG)
 FN(mv_end, "end-of-line", 0)
 FN(mv_forw, "forward-char", XF_ARG)
index ab1cc88..a9980c7 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.192 2016/08/01 21:38:01 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.194 2016/11/11 23:31:34 tg Exp $");
 
 /*
  * string expansion
@@ -62,7 +62,7 @@ typedef struct {
 #define IFS_WORD       0       /* word has chars (or quotes except "$@") */
 #define IFS_WS         1       /* have seen IFS white-space */
 #define IFS_NWS                2       /* have seen IFS non-white-space */
-#define IFS_IWS                3       /* begin of word, ignore IFS WS */
+#define IFS_IWS                3       /* beginning of word, ignore IFS WS */
 #define IFS_QUOTE      4       /* beg.w/quote, become IFS_WORD unless "$@" */
 
 static int varsub(Expand *, const char *, const char *, int *, int *);
@@ -198,7 +198,7 @@ typedef struct SubType {
        struct tbl *var;        /* variable for ${var..} */
        struct SubType *prev;   /* old type */
        struct SubType *next;   /* poped type (to avoid re-allocating) */
-       size_t  base;           /* begin position of expanded word */
+       size_t  base;           /* start position of expanded word */
        short   stype;          /* [=+-?%#] action after expanded word */
        short   f;              /* saved value of f (DOPAT, etc) */
        uint8_t quotep;         /* saved value of quote (for ${..[%#]..}) */
@@ -1576,7 +1576,7 @@ globit(XString *xs,       /* dest string */
                         * SunOS 4.1.3 does this...
                         */
                        if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
-                           xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&
+                           mksh_cdirsep(xp[-1]) && !S_ISDIR(lstatb.st_mode) &&
                            (!S_ISLNK(lstatb.st_mode) ||
                            stat_check() < 0 || !S_ISDIR(statb.st_mode)))
                                return;
@@ -1586,7 +1586,7 @@ globit(XString *xs,       /* dest string */
                         * directory
                         */
                        if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
-                           xp > Xstring(*xs, xp) && xp[-1] != '/' &&
+                           xp > Xstring(*xs, xp) && !mksh_cdirsep(xp[-1]) &&
                            (S_ISDIR(lstatb.st_mode) ||
                            (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
                            S_ISDIR(statb.st_mode)))) {
@@ -1601,11 +1601,11 @@ globit(XString *xs,     /* dest string */
 
        if (xp > Xstring(*xs, xp))
                *xp++ = '/';
-       while (*sp == '/') {
+       while (mksh_cdirsep(*sp)) {
                Xcheck(*xs, xp);
                *xp++ = *sp++;
        }
-       np = strchr(sp, '/');
+       np = mksh_sdirsep(sp);
        if (np != NULL) {
                se = np;
                /* don't assume '/', can be multiple kinds */
@@ -1713,8 +1713,8 @@ maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign)
 
        Xinit(ts, tp, 16, ATEMP);
        /* : only for DOASNTILDE form */
-       while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))
-       {
+       while (p[0] == CHAR && !mksh_cdirsep(p[1]) &&
+           (!isassign || p[1] != ':')) {
                Xcheck(ts, tp);
                *tp++ = p[1];
                p += 2;
index e3149cd..882b628 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.179 2016/08/01 21:38:02 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.186 2016/11/11 23:31:34 tg Exp $");
 
 #ifndef MKSH_DEFAULT_EXECSHELL
 #define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@@ -282,7 +282,7 @@ execute(struct op * volatile t,
 
        case TOR:
        case TAND:
-               rv = execute(t->left, XERROK, xerrok);
+               rv = execute(t->left, XERROK, NULL);
                if ((rv == 0) == (t->type == TAND))
                        rv = execute(t->right, flags & XERROK, xerrok);
                else {
@@ -339,16 +339,14 @@ execute(struct op * volatile t,
                                rv = execute(t->left, flags & XERROK, xerrok);
                        }
                } else {
-                       /* TSELECT */
-                       for (;;) {
-                               if (!(ccp = do_selectargs(ap, is_first))) {
-                                       rv = 1;
-                                       break;
-                               }
+ do_TSELECT:
+                       if ((ccp = do_selectargs(ap, is_first))) {
                                is_first = false;
                                setstr(global(t->str), ccp, KSH_UNWIND_ERROR);
                                execute(t->left, flags & XERROK, xerrok);
+                               goto do_TSELECT;
                        }
+                       rv = 1;
                }
                break;
        }
@@ -674,7 +672,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
                rv = subst_exstat;
                goto Leave;
        } else if (!tp) {
-               if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
+               if (Flag(FRESTRICTED) && mksh_vdirsep(cp)) {
                        warningf(true, Tf_sD_s, cp, "restricted");
                        rv = 1;
                        goto Leave;
@@ -913,7 +911,7 @@ scriptexec(struct op *tp, const char **ap)
                /* replace newline by NUL */
                *cp = '\0';
 
-               /* restore begin of shebang position (buf+0 or buf+3) */
+               /* restore start of shebang position (buf+0 or buf+3) */
                cp = buf + n;
                /* bail out if no shebang magic found */
                if (cp[0] == '#' && cp[1] == '!')
@@ -1125,7 +1123,7 @@ findcom(const char *name, int flags)
        char *fpath;
        union mksh_cchack npath;
 
-       if (vstrchr(name, '/')) {
+       if (mksh_vdirsep(name)) {
                insert = 0;
                /* prevent FPATH search below */
                flags &= ~FC_FUNC;
@@ -1242,8 +1240,12 @@ search_access(const char *fn, int mode)
                eno = errno;
                return (eno ? eno : EACCES);
        }
+#ifdef __OS2__
+       /* treat all files as executable on OS/2 */
+       sb.st_mode &= S_IXUSR | S_IXGRP | S_IXOTH;
+#endif
        if (mode == X_OK && (!S_ISREG(sb.st_mode) ||
-           !(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))))
+           !(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
                /* access(2) may say root can execute everything */
                return (S_ISDIR(sb.st_mode) ? EISDIR : EACCES);
        return (0);
@@ -1265,7 +1267,7 @@ search_path(const char *name, const char *lpath,
        size_t namelen;
        int ec = 0, ev;
 
-       if (vstrchr(name, '/')) {
+       if (mksh_vdirsep(name)) {
                if ((ec = search_access(name, mode)) == 0) {
  search_path_ok:
                        if (errnop)
@@ -1658,6 +1660,7 @@ pr_menu(const char * const *ap)
        const char * const *pp;
        size_t acols = 0, aocts = 0, i;
        unsigned int n;
+       struct columnise_opts co;
 
        /*
         * width/column calculations were done once and saved, but this
@@ -1686,9 +1689,11 @@ pr_menu(const char * const *ap)
                smi.num_width++;
 
        smi.args = ap;
-       print_columns(shl_out, n, select_fmt_entry, (void *)&smi,
-           smi.num_width + 2 + aocts, smi.num_width + 2 + acols,
-           true);
+       co.shf = shl_out;
+       co.linesep = '\n';
+       co.prefcol = co.do_last = true;
+       print_columns(&co, n, select_fmt_entry, (void *)&smi,
+           smi.num_width + 2 + aocts, smi.num_width + 2 + acols);
 }
 
 static void
@@ -1698,7 +1703,7 @@ plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
 }
 
 void
-pr_list(char * const *ap)
+pr_list(struct columnise_opts *cop, char * const *ap)
 {
        size_t acols = 0, aocts = 0, i;
        unsigned int n;
@@ -1713,8 +1718,8 @@ pr_list(char * const *ap)
                        acols = i;
        }
 
-       print_columns(shl_out, n, plain_fmt_entry, (const void *)ap,
-           aocts, acols, false);
+       print_columns(cop, n, plain_fmt_entry, (const void *)ap,
+           aocts, acols);
 }
 
 /*
index e40108f..f259b0c 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.88 2016/07/27 00:55:27 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.90 2016/11/07 16:58:48 tg Exp $");
 
 #define EXPRTOK_DEFNS
 #include "exprtok.h"
@@ -326,7 +326,15 @@ 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 &&
            opprec[(int)op] == prec) {
-               exprtoken(es);
+               switch ((int)op) {
+               case O_TERN:
+               case O_LAND:
+               case O_LOR:
+                       break;
+               default:
+                       exprtoken(es);
+               }
+
                vasn = vl;
                if (op != O_ASN)
                        /* vl may not have a value yet */
@@ -340,14 +348,15 @@ evalexpr(Expr_state *es, unsigned int prec)
 
                        if (!ev)
                                es->noassign++;
+                       exprtoken(es);
                        vl = evalexpr(es, MAX_PREC);
                        if (!ev)
                                es->noassign--;
                        if (es->tok != CTERN)
                                evalerr(es, ET_STR, "missing :");
-                       exprtoken(es);
                        if (ev)
                                es->noassign++;
+                       exprtoken(es);
                        vr = evalexpr(es, P_TERN);
                        if (ev)
                                es->noassign--;
@@ -502,6 +511,7 @@ evalexpr(Expr_state *es, unsigned int prec)
                case O_LAND:
                        if (!t1)
                                es->noassign++;
+                       exprtoken(es);
                        vr = intvar(es, evalexpr(es, prec - 1));
                        res = t1 && vr->val.u;
                        if (!t1)
@@ -510,6 +520,7 @@ evalexpr(Expr_state *es, unsigned int prec)
                case O_LOR:
                        if (t1)
                                es->noassign++;
+                       exprtoken(es);
                        vr = intvar(es, evalexpr(es, prec - 1));
                        res = t1 || vr->val.u;
                        if (t1)
@@ -857,7 +868,7 @@ ksh_access(const char *fn, int mode)
 }
 
 #ifndef MIRBSD_BOOTFLOPPY
-/* From: X11/xc/programs/xterm/wcwidth.c,v 1.8 2014/06/24 19:53:53 tg Exp $ */
+/* From: X11/xc/programs/xterm/wcwidth.c,v 1.9 */
 
 struct mb_ucsrange {
        unsigned short beg;
@@ -868,8 +879,8 @@ static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems,
     unsigned int val) MKSH_A_PURE;
 
 /*
- * Generated by MirOS: contrib/code/Snippets/eawparse,v 1.2 2013/11/30 13:45:17 tg Exp $
- * from the Unicode Character Database, Version 7.0.0
+ * Generated from the Unicode Character Database, Version 9.0.0, by
+ * MirOS: contrib/code/Snippets/eawparse,v 1.3 2014/11/16 12:16:24 tg Exp $
  */
 
 static const struct mb_ucsrange mb_ucs_combining[] = {
@@ -899,7 +910,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
        { 0x0825, 0x0827 },
        { 0x0829, 0x082D },
        { 0x0859, 0x085B },
-       { 0x08E4, 0x0902 },
+       { 0x08D4, 0x0902 },
        { 0x093A, 0x093A },
        { 0x093C, 0x093C },
        { 0x0941, 0x0948 },
@@ -994,6 +1005,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
        { 0x17C9, 0x17D3 },
        { 0x17DD, 0x17DD },
        { 0x180B, 0x180E },
+       { 0x1885, 0x1886 },
        { 0x18A9, 0x18A9 },
        { 0x1920, 0x1922 },
        { 0x1927, 0x1928 },
@@ -1032,7 +1044,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
        { 0x1CF4, 0x1CF4 },
        { 0x1CF8, 0x1CF9 },
        { 0x1DC0, 0x1DF5 },
-       { 0x1DFC, 0x1DFF },
+       { 0x1DFB, 0x1DFF },
        { 0x200B, 0x200F },
        { 0x202A, 0x202E },
        { 0x2060, 0x2064 },
@@ -1045,13 +1057,13 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
        { 0x3099, 0x309A },
        { 0xA66F, 0xA672 },
        { 0xA674, 0xA67D },
-       { 0xA69F, 0xA69F },
+       { 0xA69E, 0xA69F },
        { 0xA6F0, 0xA6F1 },
        { 0xA802, 0xA802 },
        { 0xA806, 0xA806 },
        { 0xA80B, 0xA80B },
        { 0xA825, 0xA826 },
-       { 0xA8C4, 0xA8C4 },
+       { 0xA8C4, 0xA8C5 },
        { 0xA8E0, 0xA8F1 },
        { 0xA926, 0xA92D },
        { 0xA947, 0xA951 },
@@ -1078,14 +1090,47 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
        { 0xABED, 0xABED },
        { 0xFB1E, 0xFB1E },
        { 0xFE00, 0xFE0F },
-       { 0xFE20, 0xFE2D },
+       { 0xFE20, 0xFE2F },
        { 0xFEFF, 0xFEFF },
        { 0xFFF9, 0xFFFB }
 };
 
 static const struct mb_ucsrange mb_ucs_fullwidth[] = {
        { 0x1100, 0x115F },
+       { 0x231A, 0x231B },
        { 0x2329, 0x232A },
+       { 0x23E9, 0x23EC },
+       { 0x23F0, 0x23F0 },
+       { 0x23F3, 0x23F3 },
+       { 0x25FD, 0x25FE },
+       { 0x2614, 0x2615 },
+       { 0x2648, 0x2653 },
+       { 0x267F, 0x267F },
+       { 0x2693, 0x2693 },
+       { 0x26A1, 0x26A1 },
+       { 0x26AA, 0x26AB },
+       { 0x26BD, 0x26BE },
+       { 0x26C4, 0x26C5 },
+       { 0x26CE, 0x26CE },
+       { 0x26D4, 0x26D4 },
+       { 0x26EA, 0x26EA },
+       { 0x26F2, 0x26F3 },
+       { 0x26F5, 0x26F5 },
+       { 0x26FA, 0x26FA },
+       { 0x26FD, 0x26FD },
+       { 0x2705, 0x2705 },
+       { 0x270A, 0x270B },
+       { 0x2728, 0x2728 },
+       { 0x274C, 0x274C },
+       { 0x274E, 0x274E },
+       { 0x2753, 0x2755 },
+       { 0x2757, 0x2757 },
+       { 0x2795, 0x2797 },
+       { 0x27B0, 0x27B0 },
+       { 0x27BF, 0x27BF },
+       { 0x2B1B, 0x2B1C },
+       { 0x2B50, 0x2B50 },
+       { 0x2B55, 0x2B55 },
        { 0x2E80, 0x303E },
        { 0x3040, 0xA4CF },
        { 0xA960, 0xA97F },
index 0630eb0..7c65111 100644 (file)
@@ -38,7 +38,7 @@
 #endif
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.305 2016/08/01 21:38:02 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.319 2016/11/11 23:48:29 tg Exp $");
 
 #if HAVE_KILLPG
 /*
@@ -122,7 +122,9 @@ const struct builtin mkshbuiltins[] = {
        {"kill", c_kill},
        {"let", c_let},
        {"let]", c_let},
+#if !defined(__ANDROID__)
        {"print", c_print},
+#endif
        {"pwd", c_pwd},
        {Tread, c_read},
        {Tsgreadonly, c_typeset},
@@ -162,9 +164,9 @@ const struct builtin mkshbuiltins[] = {
        {"~printf", c_printf},
 #endif
 #if HAVE_SELECT
-# if !defined(__ANDROID__)
+#if !defined(__ANDROID__)
        {"sleep", c_sleep},
-# endif
+#endif
 #endif
 #ifdef __MirBSD__
        /* alias to "true" for historical reasons */
@@ -286,17 +288,46 @@ static void s_put(int);
 int
 c_print(const char **wp)
 {
-       int fd = 1, c;
+       int c;
        const char *s;
-       XString xs;
        char *xp;
-       /* print newline;  expand backslash sequences */
-       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];
+       XString xs;
+       struct {
+               /* storage for columnisation */
+               XPtrV words;
+               /* temporary storage for a wide character */
+               mksh_ari_t wc;
+               /* output file descriptor (if any) */
+               int fd;
+               /* temporary storage for a multibyte character */
+               char ts[4];
+               /* output word separator */
+               char ws;
+               /* output line separator */
+               char ls;
+               /* output a trailing line separator? */
+               bool nl;
+               /* expand backslash sequences? */
+               bool exp;
+               /* columnise output? */
+               bool col;
+               /* print to history instead of file descriptor / stdout? */
+               bool hist;
+               /* print words as wide characters? */
+               bool chars;
+               /* print a "--" argument? */
+               bool pminusminus;
+               /* writing to a coprocess (SIGPIPE blocked)? */
+               bool coproc;
+               bool copipe;
+       } po;
+
+       memset(&po, 0, sizeof(po));
+       po.fd = 1;
+       po.ws = ' ';
+       po.ls = '\n';
+       po.nl = true;
+       po.exp = true;
 
        if (wp[0][0] == 'e') {
                /* "echo" builtin */
@@ -308,7 +339,7 @@ c_print(const char **wp)
                         * one that supports -e but does not enable it by
                         * default
                         */
-                       po_exp = false;
+                       po.exp = false;
                }
 #endif
                if (Flag(FPOSIX) ||
@@ -319,13 +350,13 @@ c_print(const char **wp)
                        /* Debian Policy 10.4 compliant "echo" builtin */
                        if (*wp && !strcmp(*wp, "-n")) {
                                /* recognise "-n" only as the first arg */
-                               po_nl = false;
+                               po.nl = false;
                                ++wp;
                        }
                        /* print everything as-is */
-                       po_exp = false;
+                       po.exp = false;
                } else {
-                       bool new_exp = po_exp, new_nl = po_nl;
+                       bool new_exp = po.exp, new_nl = po.nl;
 
                        /**
                         * a compromise between sysV and BSD echo commands:
@@ -352,8 +383,8 @@ c_print(const char **wp)
                                        new_nl = false;
                                        goto print_tradparse_ch;
                                case '\0':
-                                       po_exp = new_exp;
-                                       po_nl = new_nl;
+                                       po.exp = new_exp;
+                                       po.nl = new_nl;
                                        ++wp;
                                        goto print_tradparse_arg;
                                }
@@ -361,44 +392,54 @@ c_print(const char **wp)
                }
        } else {
                /* "print" builtin */
-               const char *opts = "AnpRrsu,";
+               const char *opts = "AclNnpRrsu,";
                const char *emsg;
-               /* print a "--" argument */
-               bool po_pminusminus = false;
+
+               po.pminusminus = false;
 
                while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
                        switch (c) {
                        case 'A':
-                               po_char = true;
+                               po.chars = true;
+                               break;
+                       case 'c':
+                               po.col = true;
                                break;
                        case 'e':
-                               po_exp = true;
+                               po.exp = true;
+                               break;
+                       case 'l':
+                               po.ws = '\n';
+                               break;
+                       case 'N':
+                               po.ws = '\0';
+                               po.ls = '\0';
                                break;
                        case 'n':
-                               po_nl = false;
+                               po.nl = false;
                                break;
                        case 'p':
-                               if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
+                               if ((po.fd = coproc_getfd(W_OK, &emsg)) < 0) {
                                        bi_errorf(Tf_coproc, emsg);
                                        return (1);
                                }
                                break;
                        case 'R':
                                /* fake BSD echo command */
-                               po_pminusminus = true;
-                               po_exp = false;
+                               po.pminusminus = true;
+                               po.exp = false;
                                opts = "en";
                                break;
                        case 'r':
-                               po_exp = false;
+                               po.exp = false;
                                break;
                        case 's':
-                               po_hist = true;
+                               po.hist = true;
                                break;
                        case 'u':
                                if (!*(s = builtin_opt.optarg))
-                                       fd = 0;
-                               else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
+                                       po.fd = 0;
+                               else if ((po.fd = check_fd(s, W_OK, &emsg)) < 0) {
                                        bi_errorf("-u%s: %s", s, emsg);
                                        return (1);
                                }
@@ -412,35 +453,45 @@ c_print(const char **wp)
                        if (wp[builtin_opt.optind] &&
                            ksh_isdash(wp[builtin_opt.optind]))
                                builtin_opt.optind++;
-                       } else if (po_pminusminus)
+                       } else if (po.pminusminus)
                                builtin_opt.optind--;
                wp += builtin_opt.optind;
        }
 
-       Xinit(xs, xp, 128, ATEMP);
+       if (po.col) {
+               if (*wp == NULL)
+                       return (0);
 
-       if (*wp != NULL && po_char) {
-               mksh_ari_t wc;
+               XPinit(po.words, 16);
+       }
 
-               do {
-                       if (!evaluate(*wp, &wc, KSH_RETURN_ERROR, true))
+       Xinit(xs, xp, 128, ATEMP);
+
+       if (*wp == NULL)
+               goto print_no_arg;
+ print_read_arg:
+       if (po.chars) {
+               while (*wp != NULL) {
+                       s = *wp++;
+                       if (*s == '\0')
+                               break;
+                       if (!evaluate(s, &po.wc, KSH_RETURN_ERROR, true))
                                return (1);
                        Xcheck(xs, xp);
                        if (UTFMODE) {
-                               ts[utf_wctomb(ts, wc)] = 0;
+                               po.ts[utf_wctomb(po.ts, po.wc)] = 0;
                                c = 0;
                                do {
-                                       Xput(xs, xp, ts[c]);
-                               } while (ts[++c]);
+                                       Xput(xs, xp, po.ts[c]);
+                               } while (po.ts[++c]);
                        } else
-                               Xput(xs, xp, wc & 0xFF);
-               } while (*++wp);
-       } else if (*wp != NULL) {
- print_read_arg:
-               s = *wp;
+                               Xput(xs, xp, po.wc & 0xFF);
+               }
+       } else {
+               s = *wp++;
                while ((c = *s++) != '\0') {
                        Xcheck(xs, xp);
-                       if (po_exp && c == '\\') {
+                       if (po.exp && c == '\\') {
                                s_ptr = s;
                                c = unbksl(false, s_get, s_put);
                                s = s_ptr;
@@ -448,7 +499,7 @@ c_print(const char **wp)
                                        /* rejected by generic function */
                                        switch ((c = *s++)) {
                                        case 'c':
-                                               po_nl = false;
+                                               po.nl = false;
                                                /* AT&T brain damage */
                                                continue;
                                        case '\0':
@@ -460,33 +511,55 @@ c_print(const char **wp)
                                        }
                                } else if ((unsigned int)c > 0xFF) {
                                        /* generic function returned Unicode */
-                                       ts[utf_wctomb(ts, c - 0x100)] = 0;
+                                       po.ts[utf_wctomb(po.ts, c - 0x100)] = 0;
                                        c = 0;
                                        do {
-                                               Xput(xs, xp, ts[c]);
-                                       } while (ts[++c]);
+                                               Xput(xs, xp, po.ts[c]);
+                                       } while (po.ts[++c]);
                                        continue;
                                }
                        }
                        Xput(xs, xp, c);
                }
-               if (*++wp != NULL) {
-                       Xput(xs, xp, ' ');
-                       goto print_read_arg;
-               }
        }
-       if (po_nl)
-               Xput(xs, xp, '\n');
+       if (po.col) {
+               Xput(xs, xp, '\0');
+               XPput(po.words, Xclose(xs, xp));
+               Xinit(xs, xp, 128, ATEMP);
+       }
+       if (*wp != NULL) {
+               if (!po.col)
+                       Xput(xs, xp, po.ws);
+               goto print_read_arg;
+       }
+       if (po.col) {
+               size_t w = XPsize(po.words);
+               struct columnise_opts co;
+
+               XPput(po.words, NULL);
+               co.shf = shf_sopen(NULL, 128, SHF_WR | SHF_DYNAMIC, NULL);
+               co.linesep = po.ls;
+               co.prefcol = co.do_last = false;
+               pr_list(&co, (char **)XPptrv(po.words));
+               while (w--)
+                       afree(XPptrv(po.words)[w], ATEMP);
+               XPfree(po.words);
+               w = co.shf->wp - co.shf->buf;
+               XcheckN(xs, xp, w);
+               memcpy(xp, co.shf->buf, w);
+               xp += w;
+               shf_sclose(co.shf);
+       }
+ print_no_arg:
+       if (po.nl)
+               Xput(xs, xp, po.ls);
 
        c = 0;
-       if (po_hist) {
+       if (po.hist) {
                Xput(xs, xp, '\0');
                histsave(&source->line, Xstring(xs, xp), HIST_STORE, false);
-               Xfree(xs, xp);
        } else {
                size_t len = Xlength(xs, xp);
-               bool po_coproc = false;
-               int opipe = 0;
 
                /*
                 * Ensure we aren't killed by a SIGPIPE while writing to
@@ -494,24 +567,25 @@ c_print(const char **wp)
                 * to just check that the co-process is alive which is
                 * not enough).
                 */
-               if (coproc.write >= 0 && coproc.write == fd) {
-                       po_coproc = true;
-                       opipe = block_pipe();
-               }
+               if (coproc.write >= 0 && coproc.write == po.fd) {
+                       po.coproc = true;
+                       po.copipe = block_pipe();
+               } else
+                       po.coproc = po.copipe = false;
 
                s = Xstring(xs, xp);
                while (len > 0) {
                        ssize_t nwritten;
 
-                       if ((nwritten = write(fd, s, len)) < 0) {
+                       if ((nwritten = write(po.fd, s, len)) < 0) {
                                if (errno == EINTR) {
-                                       if (po_coproc)
-                                               restore_pipe(opipe);
+                                       if (po.copipe)
+                                               restore_pipe();
                                        /* give the user a chance to ^C out */
                                        intrcheck();
                                        /* interrupted, try again */
-                                       if (po_coproc)
-                                               opipe = block_pipe();
+                                       if (po.coproc)
+                                               po.copipe = block_pipe();
                                        continue;
                                }
                                c = 1;
@@ -520,9 +594,10 @@ c_print(const char **wp)
                        s += nwritten;
                        len -= nwritten;
                }
-               if (po_coproc)
-                       restore_pipe(opipe);
+               if (po.copipe)
+                       restore_pipe();
        }
+       Xfree(xs, xp);
 
        return (c);
 }
@@ -1431,6 +1506,7 @@ c_kill(const char **wp)
                        ssize_t w, mess_cols = 0, mess_octs = 0;
                        int j = ksh_NSIG - 1;
                        struct kill_info ki = { 0, 0 };
+                       struct columnise_opts co;
 
                        do {
                                ki.num_width++;
@@ -1448,11 +1524,14 @@ c_kill(const char **wp)
                                        mess_cols = w;
                        }
 
-                       print_columns(shl_stdout, (unsigned int)(ksh_NSIG - 1),
+                       co.shf = shl_stdout;
+                       co.linesep = '\n';
+                       co.prefcol = co.do_last = true;
+
+                       print_columns(&co, (unsigned int)(ksh_NSIG - 1),
                            kill_fmt_entry, (void *)&ki,
                            ki.num_width + 1 + ki.name_width + 1 + mess_octs,
-                           ki.num_width + 1 + ki.name_width + 1 + mess_cols,
-                           true);
+                           ki.num_width + 1 + ki.name_width + 1 + mess_cols);
                }
                return (0);
        }
@@ -2013,7 +2092,7 @@ c_read(const char **wp)
                timersub(&tvlim, &tv, &tv);
                if (tv.tv_sec < 0) {
                        /* timeout expired globally */
-                       rv = 1;
+                       rv = 3;
                        goto c_read_out;
                }
 
@@ -2023,8 +2102,8 @@ c_read(const char **wp)
                case 0:
                        /* timeout expired for this call */
                        bytesread = 0;
-                       /* fake EOF read; all cases return 1 */
-                       goto c_read_didread;
+                       rv = 3;
+                       goto c_read_readdone;
                default:
                        bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
                        rv = 2;
@@ -2049,7 +2128,6 @@ c_read(const char **wp)
                goto c_read_out;
        }
 
- c_read_didread:
        switch (readmode) {
        case READALL:
                if (bytesread == 0) {
@@ -2123,13 +2201,13 @@ c_read(const char **wp)
        /*-
         * state: we finished reading the input and NUL terminated it
         * Xstring(xs, xp) -> xp-1 = input string without trailing delim
-        * rv = 1 if EOF, 0 otherwise (errors handled already)
+        * rv = 3 if timeout, 1 if EOF, 0 otherwise (errors handled already)
         */
 
-       if (rv == 1) {
-               /* clean up coprocess if needed, on EOF */
+       if (rv) {
+               /* clean up coprocess if needed, on EOF/error/timeout */
                coproc_read_close(fd);
-               if (readmode == READALL)
+               if (readmode == READALL && (rv == 1 || (rv == 3 && bytesread)))
                        /* EOF is no error here */
                        rv = 0;
        }
@@ -2295,7 +2373,7 @@ c_read(const char **wp)
        Xfree(xs, xp);
        if (restore_tios)
                mksh_tcset(fd, &tios);
-       return (rv);
+       return (rv == 3 ? ksh_sigmask(SIGALRM) : rv);
 #undef is_ifsws
 }
 
@@ -3727,7 +3805,7 @@ c_cat(const char **wp)
        ssize_t n, w;
        const char *fn = "<stdin>";
        char *buf, *cp;
-       int opipe = 0;
+       bool opipe;
 #define MKSH_CAT_BUFSIZ 4096
 
        /* parse options: POSIX demands we support "-u" as no-op */
@@ -3767,7 +3845,8 @@ c_cat(const char **wp)
                        if ((n = blocking_read(fd, (cp = buf),
                            MKSH_CAT_BUFSIZ)) == -1) {
                                if (errno == EINTR) {
-                                       restore_pipe(opipe);
+                                       if (opipe)
+                                               restore_pipe();
                                        /* give the user a chance to ^C out */
                                        intrcheck();
                                        /* interrupted, try again */
@@ -3782,13 +3861,17 @@ c_cat(const char **wp)
                                /* end of file reached */
                                break;
                        while (n) {
+                               if (intrsig)
+                                       goto has_intrsig;
                                if ((w = write(STDOUT_FILENO, cp, n)) != -1) {
                                        n -= w;
                                        cp += w;
                                        continue;
                                }
                                if (errno == EINTR) {
-                                       restore_pipe(opipe);
+ has_intrsig:
+                                       if (opipe)
+                                               restore_pipe();
                                        /* give the user a chance to ^C out */
                                        intrcheck();
                                        /* interrupted, try again */
@@ -3814,7 +3897,8 @@ c_cat(const char **wp)
        } while (*wp);
 
  out:
-       restore_pipe(opipe);
+       if (opipe)
+               restore_pipe();
        free_osfunc(buf);
        return (rv);
 }
index c0f87f8..56723ff 100644 (file)
@@ -27,7 +27,7 @@
 #include <sys/file.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.158 2016/08/04 20:31:00 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.159 2016/11/11 18:44:32 tg Exp $");
 
 Trap sigtraps[ksh_NSIG + 1];
 static struct sigaction Sigact_ign;
@@ -1399,33 +1399,32 @@ settrap(Trap *p, const char *s)
 }
 
 /*
- * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
- * kill shell (unless user catches it and exits)
+ * called by c_print() when writing to a co-process to ensure
+ * SIGPIPE won't kill shell (unless user catches it and exits)
  */
-int
+bool
 block_pipe(void)
 {
-       int restore_dfl = 0;
+       bool restore_dfl = false;
        Trap *p = &sigtraps[SIGPIPE];
 
        if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
                setsig(p, SIG_IGN, SS_RESTORE_CURR);
                if (p->flags & TF_ORIG_DFL)
-                       restore_dfl = 1;
+                       restore_dfl = true;
        } else if (p->cursig == SIG_DFL) {
                setsig(p, SIG_IGN, SS_RESTORE_CURR);
                /* restore to SIG_DFL */
-               restore_dfl = 1;
+               restore_dfl = true;
        }
        return (restore_dfl);
 }
 
-/* Called by c_print() to undo whatever block_pipe() did */
+/* called by c_print() to undo whatever block_pipe() did */
 void
-restore_pipe(int restore_dfl)
+restore_pipe(void)
 {
-       if (restore_dfl)
-               setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
+       setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
 }
 
 /*
index 95acfca..a9f8be7 100644 (file)
@@ -1,4 +1,4 @@
-.\" $MirOS: src/bin/mksh/lksh.1,v 1.18 2016/08/10 18:20:05 tg Exp $
+.\" $MirOS: src/bin/mksh/lksh.1,v 1.20 2016/11/11 23:31:35 tg Exp $
 .\"-
 .\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016
 .\"    mirabilos <m@mirbsd.org>
@@ -74,7 +74,7 @@
 .\" with -mandoc, it might implement .Mx itself, but we want to
 .\" use our own definition. And .Dd must come *first*, always.
 .\"
-.Dd $Mdocdate: August 10 2016 $
+.Dd $Mdocdate: November 11 2016 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -209,9 +209,9 @@ The
 string identifies
 .Nm
 as
-.Dq LEGACY KSH
+.Dq Li LEGACY KSH
 instead of
-.Dq MIRBSD KSH .
+.Dq Li MIRBSD KSH .
 Note that the rest of the version string is identical between
 the two shell flavours, and the behaviour and differences can
 change between versions; see the accompanying manual page
@@ -221,8 +221,8 @@ for the versions this document applies to.
 .Nm
 uses
 .Tn POSIX
-arithmetics, which has quite a few implications:
-The data type for arithmetics is the host
+arithmetic, which has quite a few implications:
+The data type for arithmetic operations is the host
 .Tn ISO
 C
 .Vt long
index 4198c87..ebbadd9 100644 (file)
@@ -34,7 +34,7 @@
 #include <locale.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.317 2016/08/04 20:51:35 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.322 2016/11/11 23:48:30 tg Exp $");
 
 extern char **environ;
 
@@ -59,7 +59,12 @@ static void x_sigwinch(int);
 static const char initifs[] = "IFS= \t\n";
 
 static const char initsubs[] =
-    "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0} ${EPOCHREALTIME=}";
+    "${PS2=> }"
+    "${PS3=#? }"
+    "${PS4=+ }"
+    "${SECONDS=0}"
+    "${TMOUT=0}"
+    "${EPOCHREALTIME=}";
 
 static const char *initcoms[] = {
        Ttypeset, "-r", initvsn, NULL,
@@ -111,10 +116,8 @@ rndsetup(void)
        char *cp;
 
        cp = alloc(sizeof(*bufptr) - sizeof(ALLOC_ITEM), APERM);
-#ifdef DEBUG
-       /* clear the allocated space, for valgrind */
+       /* clear the allocated space, for valgrind and to avoid UB */
        memset(cp, 0, sizeof(*bufptr) - sizeof(ALLOC_ITEM));
-#endif
        /* undo what alloc() did to the malloc result address */
        bufptr = (void *)(cp - sizeof(ALLOC_ITEM));
        /* PIE or something similar provides us with deltas here */
@@ -222,11 +225,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
 
        /* determine the basename (without '-' or path) of the executable */
        ccp = kshname;
-       goto begin_parse_kshname;
+       goto begin_parsing_kshname;
        while ((i = ccp[argi++])) {
-               if (i == '/') {
+               if (mksh_cdirsep(i)) {
                        ccp += argi;
- begin_parse_kshname:
+ begin_parsing_kshname:
                        argi = 0;
                        if (*ccp == '-')
                                ++ccp;
@@ -563,8 +566,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
 #endif
                if (!isuc(ccp))
                        ccp = null;
-               /* FALLTHROUGH */
 #endif
+               /* FALLTHROUGH */
 
        /* auto-detect from environment */
        case 3:
index 1cdbf6b..647d1d1 100644 (file)
@@ -30,7 +30,7 @@
 #include <grp.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.245 2016/08/01 18:42:42 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.249 2016/11/11 23:31:35 tg Exp $");
 
 #define KSH_CHVT_FLAG
 #ifdef MKSH_SMALL
@@ -195,6 +195,7 @@ printoptions(bool verbose)
        if (verbose) {
                size_t n = 0, len, octs = 0;
                struct options_info oi;
+               struct columnise_opts co;
 
                /* verbose version */
                shf_puts("Current option settings\n", shl_stdout);
@@ -211,8 +212,11 @@ printoptions(bool verbose)
                        }
                        ++i;
                }
-               print_columns(shl_stdout, n, options_fmt_entry, &oi,
-                   octs + 4, oi.opt_width + 4, true);
+               co.shf = shl_stdout;
+               co.linesep = '\n';
+               co.prefcol = co.do_last = true;
+               print_columns(&co, n, options_fmt_entry, &oi,
+                   octs + 4, oi.opt_width + 4);
        } else {
                /* short version like AT&T ksh93 */
                shf_puts(Tset, shl_stdout);
@@ -387,7 +391,7 @@ parse_args(const char **argv,
                 */
                if (*p != '-')
                        for (q = p; *q; )
-                               if (*q++ == '/')
+                               if (mksh_cdirsep(*q++))
                                        p = q;
                Flag(FLOGIN) = (*p == '-');
                opts = cmd_opts;
@@ -1226,11 +1230,11 @@ print_value_quoted(struct shf *shf, const char *s)
  * the i-th element
  */
 void
-print_columns(struct shf *shf, unsigned int n,
+print_columns(struct columnise_opts *opts, unsigned int n,
     void (*func)(char *, size_t, unsigned int, const void *),
-    const void *arg, size_t max_oct, size_t max_colz, bool prefcol)
+    const void *arg, size_t max_oct, size_t max_colz)
 {
-       unsigned int i, r, c, rows, cols, nspace, max_col;
+       unsigned int i, r = 0, c, rows, cols, nspace, max_col;
        char *str;
 
        if (!n)
@@ -1265,16 +1269,18 @@ print_columns(struct shf *shf, unsigned int n,
 
        /* if we can only print one column anyway, skip the goo */
        if (cols < 2) {
-               for (i = 0; i < n; ++i) {
-                       (*func)(str, max_oct, i, arg);
-                       shf_puts(str, shf);
-                       shf_putc('\n', shf);
+               goto prcols_easy;
+               while (r < n) {
+                       shf_putc(opts->linesep, opts->shf);
+ prcols_easy:
+                       (*func)(str, max_oct, r++, arg);
+                       shf_puts(str, opts->shf);
                }
                goto out;
        }
 
        rows = (n + cols - 1) / cols;
-       if (prefcol && cols > rows) {
+       if (opts->prefcol && cols > rows) {
                cols = rows;
                rows = (n + cols - 1) / cols;
        }
@@ -1283,20 +1289,25 @@ print_columns(struct shf *shf, unsigned int n,
        if (nspace < 2)
                nspace = 2;
        max_col = -max_col;
-       for (r = 0; r < rows; r++) {
+       goto prcols_hard;
+       while (r < rows) {
+               shf_putchar(opts->linesep, opts->shf);
+ prcols_hard:
                for (c = 0; c < cols; c++) {
                        if ((i = c * rows + r) >= n)
                                break;
                        (*func)(str, max_oct, i, arg);
                        if (i + rows >= n)
-                               shf_puts(str, shf);
+                               shf_puts(str, opts->shf);
                        else
-                               shf_fprintf(shf, "%*s%*s",
+                               shf_fprintf(opts->shf, "%*s%*s",
                                    (int)max_col, str, (int)nspace, null);
                }
-               shf_putchar('\n', shf);
+               ++r;
        }
  out:
+       if (opts->do_last)
+               shf_putchar(opts->linesep, opts->shf);
        afree(str, ATEMP);
 }
 
@@ -1432,14 +1443,14 @@ do_realpath(const char *upath)
 
        while (*ip) {
                /* skip slashes in input */
-               while (*ip == '/')
+               while (mksh_cdirsep(*ip))
                        ++ip;
                if (!*ip)
                        break;
 
                /* get next pathname component from input */
                tp = ip;
-               while (*ip && *ip != '/')
+               while (*ip && !mksh_cdirsep(*ip))
                        ++ip;
                len = ip - tp;
 
@@ -1451,7 +1462,7 @@ do_realpath(const char *upath)
                        else if (len == 2 && tp[1] == '.') {
                                /* strip off last pathname component */
                                while (xp > Xstring(xs, xp))
-                                       if (*--xp == '/')
+                                       if (mksh_cdirsep(*--xp))
                                                break;
                                /* then continue with the next one */
                                continue;
@@ -1474,7 +1485,7 @@ do_realpath(const char *upath)
                        /* lstat failed */
                        if (errno == ENOENT) {
                                /* because the pathname does not exist */
-                               while (*ip == '/')
+                               while (mksh_cdirsep(*ip))
                                        /* skip any trailing slashes */
                                        ++ip;
                                /* no more components left? */
@@ -1534,6 +1545,7 @@ do_realpath(const char *upath)
                                /* assert: xp == xs.beg => start of path */
 
                                /* exactly two leading slashes? (SUSv4 3.266) */
+                               /* @komh do NOT use mksh_cdirsep() here */
                                if (ip[1] == '/' && ip[2] != '/') {
                                        /* keep them, e.g. for UNC pathnames */
                                        Xput(xs, xp, '/');
@@ -1559,7 +1571,7 @@ do_realpath(const char *upath)
         * if source path had a trailing slash, check if target path
         * is not a non-directory existing file
         */
-       if (ip > ipath && ip[-1] == '/') {
+       if (ip > ipath && mksh_cdirsep(ip[-1])) {
                if (stat(Xstring(xs, xp), &sb)) {
                        if (errno != ENOENT)
                                goto notfound;
@@ -1628,7 +1640,7 @@ make_path(const char *cwd, const char *file,
 
                        if (c == '.')
                                c = file[2];
-                       if (c == '/' || c == '\0')
+                       if (mksh_cdirsep(c) || c == '\0')
                                use_cdpath = false;
                }
 
@@ -1650,7 +1662,7 @@ make_path(const char *cwd, const char *file,
                        XcheckN(*xsp, xp, len);
                        memcpy(xp, cwd, len);
                        xp += len;
-                       if (cwd[len - 1] != '/')
+                       if (!mksh_cdirsep(cwd[len - 1]))
                                Xput(*xsp, xp, '/');
                }
                *phys_pathp = Xlength(*xsp, xp);
@@ -1658,7 +1670,7 @@ make_path(const char *cwd, const char *file,
                        XcheckN(*xsp, xp, plen);
                        memcpy(xp, plist, plen);
                        xp += plen;
-                       if (plist[plen - 1] != '/')
+                       if (!mksh_cdirsep(plist[plen - 1]))
                                Xput(*xsp, xp, '/');
                        rval = 1;
                }
@@ -1700,9 +1712,14 @@ simplify_path(char *p)
                return;
        case '/':
                /* exactly two leading slashes? (SUSv4 3.266) */
+               /* @komh no mksh_cdirsep() here! */
                if (p[1] == '/' && p[2] != '/')
                        /* keep them, e.g. for UNC pathnames */
                        ++p;
+#ifdef __OS2__
+               /* FALLTHROUGH */
+       case '\\':
+#endif
                needslash = true;
                break;
        default:
@@ -1712,14 +1729,14 @@ simplify_path(char *p)
 
        while (*ip) {
                /* skip slashes in input */
-               while (*ip == '/')
+               while (mksh_cdirsep(*ip))
                        ++ip;
                if (!*ip)
                        break;
 
                /* get next pathname component from input */
                tp = ip;
-               while (*ip && *ip != '/')
+               while (*ip && !mksh_cdirsep(*ip))
                        ++ip;
                len = ip - tp;
 
@@ -1739,7 +1756,7 @@ simplify_path(char *p)
  strip_last_component:
                                        /* strip off last pathname component */
                                        while (dp > sp)
-                                               if (*--dp == '/')
+                                               if (mksh_cdirsep(*--dp))
                                                        break;
                                } else {
                                        /* relative path, at its beginning */
index 596fa3c..0de2b8f 100644 (file)
@@ -1,4 +1,4 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.413 2016/08/10 18:20:05 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.420 2016/11/11 23:31:36 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,
@@ -76,7 +76,7 @@
 .\" with -mandoc, it might implement .Mx itself, but we want to
 .\" use our own definition. And .Dd must come *first*, always.
 .\"
-.Dd $Mdocdate: August 10 2016 $
+.Dd $Mdocdate: November 11 2016 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -223,7 +223,7 @@ to a
 .Xr tty 4 .
 An interactive shell has job control enabled, ignores the
 .Dv SIGINT ,
-.Dv SIGQUIT ,
+.Dv SIGQUIT
 and
 .Dv SIGTERM
 signals, and prints prompts before reading input (see the
@@ -286,7 +286,7 @@ command is disabled.
 .It
 The
 .Ev SHELL ,
-.Ev ENV ,
+.Ev ENV
 and
 .Ev PATH
 parameters cannot be changed.
@@ -300,10 +300,10 @@ option of the built-in command
 can't be used.
 .It
 Redirections that create files can't be used (i.e.\&
-.Ql \*(Gt ,
-.Ql \*(Gt\*(Ba ,
-.Ql \*(Gt\*(Gt ,
-.Ql \*(Lt\*(Gt ) .
+.Dq Li \*(Gt ,
+.Dq Li \*(Gt\*(Ba ,
+.Dq Li \*(Gt\*(Gt ,
+.Dq Li \*(Lt\*(Gt ) .
 .El
 .It Fl s
 The shell reads commands from standard input; all non-option arguments
@@ -323,12 +323,12 @@ are attempted in order.
 Unless
 .Ar name
 begins with an exclamation mark
-.Pq Sq \&! ,
+.Pq Ql \&! ,
 this is done in a subshell and returns immediately.
 If
 .Ar name
 is a dash
-.Pq Sq \&\- ,
+.Pq Ql \&\- ,
 detach from controlling terminal (daemonise) instead.
 .El
 .Pp
@@ -360,7 +360,7 @@ The exit status of the shell is 127 if the command file specified on the
 command line could not be opened, or non-zero if a fatal syntax error
 occurred during the execution of a script.
 In the absence of fatal errors,
-the exit status is that of the last command executed, or zero, if no
+the exit status is that of the last command executed, or zero if no
 command is executed.
 .Ss Startup files
 For the actual location of these files, see
@@ -371,7 +371,7 @@ A non-privileged login shell processes the user profile next.
 A non-privileged interactive shell checks the value of the
 .Ev ENV
 parameter after subjecting it to parameter, command, arithmetic and tilde
-.Pq Sq \*(TI
+.Pq Ql \*(TI
 substitution; if unset or empty, the user mkshrc profile is processed;
 otherwise, if a file whose name is the substitution result exists,
 it is processed; non-existence is silently ignored.
@@ -383,14 +383,14 @@ The shell begins parsing its input by removing any backslash-newline
 combinations, then breaking it into
 .Em words .
 Words (which are sequences of characters) are delimited by unquoted whitespace
-characters (space, tab, and newline) or meta-characters
+characters (space, tab and newline) or meta-characters
 .Po
 .Ql \*(Lt ,
 .Ql \*(Gt ,
 .Ql \*(Ba ,
 .Ql \&; ,
 .Ql \&( ,
-.Ql \&) ,
+.Ql \&)
 and
 .Ql &
 .Pc .
@@ -398,56 +398,56 @@ Aside from delimiting words, spaces and tabs are ignored, while newlines
 usually delimit commands.
 The meta-characters are used in building the following
 .Em tokens :
-.Ql \*(Lt ,
-.Ql \*(Lt& ,
-.Ql \*(Lt\*(Lt ,
-.Ql \*(Lt\*(Lt\*(Lt ,
-.Ql \*(Gt ,
-.Ql \*(Gt& ,
-.Ql \*(Gt\*(Gt ,
-.Ql &\*(Gt ,
+.Dq Li \*(Lt ,
+.Dq Li \*(Lt& ,
+.Dq Li \*(Lt\*(Lt ,
+.Dq Li \*(Lt\*(Lt\*(Lt ,
+.Dq Li \*(Gt ,
+.Dq Li \*(Gt& ,
+.Dq Li \*(Gt\*(Gt ,
+.Dq Li &\*(Gt ,
 etc. are used to specify redirections (see
 .Sx Input/output redirection
 below);
-.Ql \*(Ba
+.Dq Li \*(Ba
 is used to create pipelines;
-.Ql \*(Ba&
+.Dq Li \*(Ba&
 is used to create co-processes (see
 .Sx Co-processes
 below);
-.Ql \&;
+.Dq Li \&;
 is used to separate commands;
-.Ql &
+.Dq Li &
 is used to create asynchronous pipelines;
-.Ql &&
+.Dq Li &&
 and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
 are used to specify conditional execution;
-.Ql ;; ,
-.Ql ;&\&
+.Dq Li \&;; ,
+.Dq Li \&;&
 and
-.Ql ;\*(Ba\&
+.Dq Li \&;\*(Ba
 are used in
 .Ic case
 statements;
-.Ql \&(( .. ))
+.Dq Li \&(( ... \&))
 is used in arithmetic expressions;
 and lastly,
-.Ql \&( .. \&)
+.Dq Li \&( ... \&)
 is used to create subshells.
 .Pp
 Whitespace and meta-characters can be quoted individually using a backslash
-.Pq Sq \e ,
+.Pq Ql \e ,
 or in groups using double
-.Pq Sq \&"
+.Pq Ql \&"
 or single
-.Pq Dq \*(aq
+.Pq Dq Li \*(aq
 quotes.
 Note that the following characters are also treated specially by the
 shell and must be quoted if they are to represent themselves:
 .Ql \e ,
 .Ql \&" ,
-.Ql \*(aq ,
+.Dq Li \*(aq ,
 .Ql # ,
 .Ql $ ,
 .Ql \` ,
@@ -455,7 +455,7 @@ shell and must be quoted if they are to represent themselves:
 .Ql { ,
 .Ql } ,
 .Ql * ,
-.Ql \&? ,
+.Ql \&?
 and
 .Ql \&[ .
 The first three of these are the above mentioned quoting characters (see
@@ -467,7 +467,7 @@ the
 .Ql #
 up to the nearest newline is ignored;
 .Ql $
-is used to introduce parameter, command, and arithmetic substitutions (see
+is used to introduce parameter, command and arithmetic substitutions (see
 .Sx Substitution
 below);
 .Ql \`
@@ -488,7 +488,7 @@ alternations (see
 below);
 and finally,
 .Ql * ,
-.Ql \&? ,
+.Ql \&?
 and
 .Ql \&[
 are used in file name generation (see
@@ -504,7 +504,7 @@ such as
 .Ic for
 and
 .Ic if
-statements, grouping constructs, and function definitions.
+statements, grouping constructs and function definitions.
 .Pp
 A simple-command consists of some combination of parameter assignments
 (see
@@ -512,12 +512,12 @@ A simple-command consists of some combination of parameter assignments
 below),
 input/output redirections (see
 .Sx Input/output redirections
-below),
+below)
 and command words; the only restriction is that parameter assignments come
 before any command words.
 The command words, if any, define the command
 that is to be executed and its arguments.
-The command may be a shell built-in command, a function,
+The command may be a shell built-in command, a function
 or an external command
 (i.e. a separate executable file that is located using the
 .Ev PATH
@@ -537,7 +537,7 @@ assignments is that of the last command substitution performed during the
 parameter assignment or 0 if there were no command substitutions.
 .Pp
 Commands can be chained together using the
-.Ql \*(Ba
+.Dq Li \*(Ba
 token to form pipelines, in which the standard output of each command but the
 last is piped (see
 .Xr pipe 2 )
@@ -553,7 +553,7 @@ where all but the last command were executed in subshells; see the
 .Ic read
 builtin's description for implications and workarounds.
 A pipeline may be prefixed by the
-.Ql \&!
+.Dq Li \&!
 reserved word which causes the exit status of the pipeline to be logically
 complemented: if the original status was 0, the complemented status will be 1;
 if the original status was not 0, the complemented status will be 0.
@@ -561,12 +561,12 @@ if the original status was not 0, the complemented status will be 0.
 .Em Lists
 of commands can be created by separating pipelines by any of the following
 tokens:
-.Ql && ,
-.Ql \*(Ba\*(Ba ,
-.Ql & ,
-.Ql \*(Ba& ,
+.Dq Li && ,
+.Dq Li \*(Ba\*(Ba ,
+.Dq Li & ,
+.Dq Li \*(Ba&
 and
-.Ql \&; .
+.Dq Li \&; .
 The first two are for conditional execution:
 .Dq Ar cmd1 No && Ar cmd2
 executes
@@ -574,25 +574,25 @@ executes
 only if the exit status of
 .Ar cmd1
 is zero;
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
 is the opposite \*(en
 .Ar cmd2
 is executed only if the exit status of
 .Ar cmd1
 is non-zero.
-.Ql &&
+.Dq Li &&
 and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
 have equal precedence which is higher than that of
-.Ql & ,
-.Ql \*(Ba& ,
+.Dq Li & ,
+.Dq Li \*(Ba&
 and
-.Ql \&; ,
+.Dq Li \&; ,
 which also have equal precedence.
 Note that the
-.Ql &&
+.Dq Li &&
 and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
 operators are
 .Qq left-associative .
 For example, both of these commands will print only
@@ -603,7 +603,7 @@ $ true \*(Ba\*(Ba echo foo && echo bar
 .Ed
 .Pp
 The
-.Ql &
+.Dq Li &
 token causes the preceding command to be executed asynchronously; that is,
 the shell starts the command but does not wait for it to complete (the shell
 does keep track of the status of asynchronous commands; see
@@ -618,20 +618,20 @@ ignored and with input redirected from
 .Pa /dev/null
 (however, redirections specified in the asynchronous command have precedence).
 The
-.Ql \*(Ba&
+.Dq Li \*(Ba&
 operator starts a co-process which is a special kind of asynchronous process
 (see
 .Sx Co-processes
 below).
 Note that a command must follow the
-.Ql &&
+.Dq Li &&
 and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
 operators, while it need not follow
-.Ql & ,
-.Ql \*(Ba& ,
+.Dq Li & ,
+.Dq Li \*(Ba&
 or
-.Ql \&; .
+.Dq Li \&; .
 The exit status of a list is that of the last command executed, with the
 exception of asynchronous lists, for which the exit status is 0.
 .Pp
@@ -649,7 +649,7 @@ elif     for      select       while     }
 .Pp
 In the following compound command descriptions, command lists (denoted as
 .Em list )
-that are followed by reserved words must end with a semicolon, a newline, or
+that are followed by reserved words must end with a semicolon, a newline or
 a (syntactically correct) reserved word.
 For example, the following are all valid:
 .Bd -literal -offset indent
@@ -673,9 +673,9 @@ Compound construct;
 .Ar list
 is executed, but not in a subshell.
 Note that
-.Ql {
+.Dq Li {
 and
-.Ql }
+.Dq Li }
 are reserved words, not meta-characters.
 .It Xo case Ar word No in
 .Oo Op \&(
@@ -706,7 +706,7 @@ are dropped.
 Note that any unquoted space before and after a pattern is
 stripped; any space within a pattern must be quoted.
 Both the word and the
-patterns are subject to parameter, command, and arithmetic substitution, as
+patterns are subject to parameter, command and arithmetic substitution, as
 well as tilde substitution.
 .Pp
 For historical reasons, open and close braces may be used instead of
@@ -720,11 +720,11 @@ The list
 .Ic terminator Ns s
 are:
 .Bl -tag -width 4n
-.It Ql ;;
+.It Dq Li ;;
 Terminate after the list.
-.It Ql ;&\&
+.It Dq Li \&;&
 Fall through into the next list.
-.It Ql ;\*(Ba\&
+.It Dq Li \&;\*(Ba
 Evaluate the remaining pattern-list tuples.
 .El
 .Pp
@@ -748,9 +748,8 @@ is set to the word and
 is executed.
 If
 .Ic in
-is not used to specify a word list, the positional parameters
-($1, $2, etc.)\&
-are used instead.
+is not used to specify a word list, the positional parameters ($1, $2,
+etc.) are used instead.
 For historical reasons, open and close braces may be used instead of
 .Ic do
 and
@@ -809,8 +808,9 @@ An enumerated list of the specified
 .Ar word Ns (s)
 is printed on standard error, followed by a prompt
 .Po
-.Ev PS3: normally
-.Sq #?\ \&
+.Ev PS3 :
+normally
+.Dq Li #?\ \&
 .Pc .
 A number corresponding to one of the enumerated words is then read from
 standard input,
@@ -829,15 +829,13 @@ When
 .Ar list
 completes, the enumerated list is printed if
 .Ev REPLY
-is
-.Dv NULL ,
-the prompt is printed, and so on.
+is empty, the prompt is printed, and so on.
 This process continues until an end-of-file
 is read, an interrupt is received, or a
 .Ic break
 statement is executed inside the loop.
 If
-.Dq in word ...
+.Dq in Ar word ...
 is omitted, the positional parameters are used
 (i.e. $1, $2, etc.).
 For historical reasons, open and close braces may be used instead of
@@ -916,7 +914,7 @@ reserved word.
 The arithmetic expression
 .Ar expression
 is evaluated; equivalent to
-.Dq let expression
+.Dq Li let \&" Ns Ar expression Ns \&"
 (see
 .Sx Arithmetic expressions
 and the
@@ -939,22 +937,22 @@ and
 .Fl o
 .Pq OR
 operators are replaced with
-.Ql &&
+.Dq Li &&
 and
-.Ql \*(Ba\*(Ba ,
+.Dq Li \*(Ba\*(Ba ,
 respectively.
 .It
 Operators (e.g.\&
-.Sq Fl f ,
-.Sq = ,
-.Sq \&! )
+.Dq Li \-f ,
+.Dq Li = ,
+.Dq Li \&! )
 must be unquoted.
 .It
-Parameter, command, and arithmetic substitutions are performed as expressions
+Parameter, command and arithmetic substitutions are performed as expressions
 are evaluated and lazy expression evaluation is used for the
-.Ql &&
+.Dq Li &&
 and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
 operators.
 This means that in the following statement,
 .Ic $(\*(Ltfoo)
@@ -966,9 +964,9 @@ $ [[ \-r foo && $(\*(Ltfoo) = b*r ]]
 .Ed
 .It
 The second operand of the
-.Sq !=
+.Dq Li !=
 and
-.Sq =
+.Dq Li =
 expressions are a subset of patterns (e.g. the comparison
 .Ic \&[[ foobar = f*r ]]
 succeeds).
@@ -995,20 +993,20 @@ case both the
 .Ql \e
 and the newline are stripped.
 Second, a single quote
-.Pq Dq \*(aq
+.Pq Dq Li \*(aq
 quotes everything up to the next single quote (this may span lines).
 Third, a double quote
-.Pq Sq \&"
+.Pq Ql \&"
 quotes all characters, except
 .Ql $ ,
-.Ql \e ,
+.Ql \e
 and
 .Ql \` ,
 up to the next unescaped double quote.
 .Ql $
 and
 .Ql \`
-inside double quotes have their usual meaning (i.e. parameter, arithmetic,
+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, and the old-style form of command
 substitution has backslash-quoting for double quotes enabled.
@@ -1017,7 +1015,7 @@ If a
 inside a double-quoted string is followed by
 .Ql \&" ,
 .Ql $ ,
-.Ql \e ,
+.Ql \e
 or
 .Ql \` ,
 only the
@@ -1047,68 +1045,68 @@ or GNU
 .Nm bash
 style escapes are translated.
 These include
-.Ql \ea ,
-.Ql \eb ,
-.Ql \ef ,
-.Ql \en ,
-.Ql \er ,
-.Ql \et ,
-.Ql \eU######## ,
-.Ql \eu#### ,
+.Dq Li \ea ,
+.Dq Li \eb ,
+.Dq Li \ef ,
+.Dq Li \en ,
+.Dq Li \er ,
+.Dq Li \et ,
+.Dq Li \eU######## ,
+.Dq Li \eu####
 and
-.Ql \ev .
+.Dq Li \ev .
 For
-.Ql \eU########
+.Dq Li \eU########
 and
-.Ql \eu#### ,
+.Dq Li \eu#### ,
 .Dq #
-means a hexadecimal digit, of thich there may be none up to four or eight;
+means a hexadecimal digit, of which there may be none up to four or eight;
 these escapes translate a Unicode codepoint to UTF-8.
 Furthermore,
-.Ql \eE
+.Dq Li \eE
 and
-.Ql \ee
+.Dq Li \ee
 expand to the escape character.
 .Pp
 In the
 .Ic print
 builtin mode,
-.Ql \e" ,
-.Ql \e\*(aq ,
+.Dq Li \e" ,
+.Dq Li \e\*(aq
 and
-.Ql \e?
+.Dq Li \e?
 are explicitly excluded;
 octal sequences must have the none up to three octal digits
 .Dq #
 prefixed with the digit zero
-.Pq Ql \e0### ;
+.Pq Dq Li \e0### ;
 hexadecimal sequences
-.Ql \ex##
+.Dq Li \ex##
 are limited to none up to two hexadecimal digits
 .Dq # ;
 both octal and hexadecimal sequences convert to raw octets;
-.Ql \e# ,
+.Dq Li \e# ,
 where # is none of the above, translates to \e# (backslashes are retained).
 .Pp
 Backslash expansion in the C style mode slightly differs: octal sequences
-.Ql \e###
+.Dq Li \e###
 must have no digit zero prefixing the one up to three octal digits
 .Dq #
 and yield raw octets; hexadecimal sequences
-.Ql \ex#*
+.Dq Li \ex#*
 greedily eat up as many hexadecimal digits
 .Dq #
 as they can and terminate with the first non-hexadecimal digit;
 these translate a Unicode codepoint to UTF-8.
 The sequence
-.Ql \ec# ,
+.Dq Li \ec# ,
 where
 .Dq #
 is any octet, translates to Ctrl-# (which basically means,
-.Ql \ec?
+.Dq Li \ec?
 becomes DEL, everything else is bitwise ANDed with 0x1F).
 Finally,
-.Ql \e# ,
+.Dq Li \e# ,
 where # is none of the above, translates to # (has the backslash trimmed),
 even if it is a newline.
 .Ss Aliases
@@ -1182,14 +1180,14 @@ automatically tracked:
 .Xr rm 1 ,
 .Xr sed 1 ,
 .Xr sh 1 ,
-.Xr vi 1 ,
+.Xr vi 1
 and
 .Xr who 1 .
 .Ss Substitution
 The first step the shell takes in executing a simple-command is to perform
 substitutions on the words of the command.
 There are three kinds of
-substitution: parameter, command, and arithmetic.
+substitution: parameter, command and arithmetic.
 Parameter substitutions,
 which are described in detail in the next section, take the form
 .Pf $ Ns Ar name
@@ -1233,7 +1231,7 @@ parameter.
 The
 .Ev IFS
 parameter specifies a list of octets which are used to break a string up
-into several words; any octets from the set space, tab, and newline that
+into several words; any octets from the set space, tab and newline that
 appear in the
 .Ev IFS
 octets are called
@@ -1253,37 +1251,36 @@ whitespace does create an empty field.
 Example: If
 .Ev IFS
 is set to
-.Dq \*(Ltspace\*(Gt: ,
+.Dq Li \*(Ltspace\*(Gt:
 and VAR is set to
-.Dq \*(Ltspace\*(GtA\*(Ltspace\*(Gt:\*(Ltspace\*(Gt\*(Ltspace\*(GtB::D ,
+.Dq Li \*(Ltspace\*(GtA\*(Ltspace\*(Gt:\*(Ltspace\*(Gt\*(Ltspace\*(GtB::D ,
 the substitution for $VAR results in four fields:
-.Sq A ,
-.Sq B ,
-.Sq
-(an empty field),
-and
-.Sq D .
+.Dq Li A ,
+.Dq Li B ,
+.Dq
+(an empty field) and
+.Dq Li D .
 Note that if the
 .Ev IFS
 parameter is set to the empty string, no field splitting is done;
-if it is unset, the default value of space, tab, and newline is used.
+if it is unset, the default value of space, tab and newline is used.
 .Pp
 Also, note that the field splitting applies only to the immediate result of
 the substitution.
 Using the previous example, the substitution for $VAR:E
 results in the fields:
-.Sq A ,
-.Sq B ,
-.Sq ,
+.Dq Li A ,
+.Dq Li B ,
+.Dq
 and
-.Sq D:E ,
+.Dq Li D:E ,
 not
-.Sq A ,
-.Sq B ,
-.Sq ,
-.Sq D ,
+.Dq Li A ,
+.Dq Li B ,
+.Dq ,
+.Dq Li D
 and
-.Sq E .
+.Dq Li E .
 This behavior is POSIX compliant, but incompatible with some other shell
 implementations which do field splitting on the word which contained the
 substitution or use
@@ -1309,7 +1306,7 @@ form, a
 .Ql \e
 followed by any of
 .Ql $ ,
-.Ql \` ,
+.Ql \`
 or
 .Ql \e
 is stripped (as is
@@ -1328,18 +1325,18 @@ has the same effect as
 .Pp
 Note that some shells do not use a recursive parser for command substitutions,
 leading to failure for certain constructs; to be portable, use as workaround
-.Ql x=$(cat) \*(Lt\*(Lt"EOF"
+.Dq Li x=$(cat) \*(Lt\*(Lt\eEOF
 (or the newline-keeping
-.Ql x=\*(Lt\*(Lt"EOF"
+.Dq Li x=\*(Lt\*(Lt\eEOF
 extension) instead to merely slurp the string.
 .St -p1003.1
-recommends to use case statements of the form
-.Ql "x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac)"
+recommends using case statements of the form
+.Li "x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac)"
 instead, which would work but not serve as example for this portability issue.
 .Bd -literal -offset indent
 x=$(case $foo in bar) echo $bar ;; *) echo $baz ;; esac)
 # above fails to parse on old shells; below is the workaround
-x=$(eval $(cat)) \*(Lt\*(Lt"EOF"
+x=$(eval $(cat)) \*(Lt\*(Lt\eEOF
 case $foo in bar) echo $bar ;; *) echo $baz ;; esac
 EOF
 .Ed
@@ -1374,7 +1371,7 @@ That is, they are a 32-bit unsigned integer.
 .Pp
 Parameter substitutions take the form
 .Pf $ Ns Ar name ,
-.Pf ${ Ns Ar name Ns } ,
+.Pf ${ Ns Ar name Ns }
 or
 .Sm off
 .Pf ${ Ar name Oo Ar expr Oc }
@@ -1389,30 +1386,26 @@ and
 works equivalent to $* and $@ for positional parameters.
 If substitution is performed on a parameter
 (or an array parameter element)
-that is not set, a null string is substituted unless the
+that is not set, an empty string is substituted unless the
 .Ic nounset
 option
-.Po
-.Ic set Fl o Ic nounset
-or
-.Ic set Fl u
-.Pc
+.Pq Ic set Fl u
 is set, in which case an error occurs.
 .Pp
 Parameters can be assigned values in a number of ways.
 First, the shell implicitly sets some parameters like
-.Ql # ,
-.Ql PWD ,
+.Dq Li # ,
+.Dq Li PWD
 and
-.Ql $ ;
+.Dq Li $ ;
 this is the only way the special single character parameters are set.
 Second, parameters are imported from the shell's environment at startup.
 Third, parameters can be assigned values on the command line: for example,
 .Ic FOO=bar
 sets the parameter
-.Dq FOO
+.Dq Li FOO
 to
-.Dq bar ;
+.Dq Li bar ;
 multiple parameter assignments can be given on a single command line and they
 can be followed by a simple-command, in which case the assignments are in
 effect only for the duration of the command (such assignments are also
@@ -1426,7 +1419,7 @@ is also recognised; the old and new values are immediately concatenated.
 The fourth way of setting a parameter is with the
 .Ic export ,
 .Ic global ,
-.Ic readonly ,
+.Ic readonly
 and
 .Ic typeset
 commands; see their descriptions in the
@@ -1438,7 +1431,7 @@ and
 .Ic select
 loops set parameters as well as the
 .Ic getopts ,
-.Ic read ,
+.Ic read
 and
 .Ic set Fl A
 commands.
@@ -1476,8 +1469,7 @@ form of parameter substitution:
 .Sm on
 If
 .Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
 it is substituted; otherwise,
 .Ar word
 is substituted.
@@ -1486,8 +1478,7 @@ is substituted.
 .Sm on
 If
 .Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
 .Ar word
 is substituted; otherwise, nothing is substituted.
 .Sm off
@@ -1495,8 +1486,7 @@ is substituted; otherwise, nothing is substituted.
 .Sm on
 If
 .Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
 it is substituted; otherwise, it is assigned
 .Ar word
 and the resulting value of
@@ -1507,25 +1497,20 @@ is substituted.
 .Sm on
 If
 .Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
 it is substituted; otherwise,
 .Ar word
 is printed on standard error (preceded by
 .Ar name : )
 and an error occurs (normally causing termination of a shell script, function,
-or script sourced using the
-.Sq \&.
+or script sourced using the
+.Dq Li \&.
 built-in).
 If
 .Ar word
 is omitted, the string
-.Dq parameter null or not set
+.Dq Li parameter null or not set
 is used instead.
-Currently a bug, if
-.Ar word
-is a variable which expands to the null string, the
-error message is also printed.
 .El
 .Pp
 Note that, for all of the above,
@@ -1534,18 +1519,17 @@ is actually considered quoted, and special parsing rules apply.
 The parsing rules also differ on whether the expression is double-quoted:
 .Ar word
 then uses double-quoting rules, except for the double quote itself
-.Pq Sq \&"
+.Pq Ql \&"
 and the closing brace, which, if backslash escaped, gets quote removal applied.
 .Pp
 In the above modifiers, the
 .Ql \&:
 can be omitted, in which case the conditions only depend on
 .Ar name
-being set (as opposed to set and not
-.Dv NULL ) .
+being set (as opposed to set and not empty).
 If
 .Ar word
-is needed, parameter, command, arithmetic, and tilde substitution are performed
+is needed, parameter, command, arithmetic and tilde substitution are performed
 on it; if
 .Ar word
 is not needed, it is not evaluated.
@@ -1561,8 +1545,8 @@ will be substituted in scalar context):
 The number of positional parameters if
 .Ar name
 is
-.Ql * ,
-.Ql @ ,
+.Dq Li * ,
+.Dq Li @
 or not specified; otherwise the length
 .Pq in characters
 of the string value of parameter
@@ -1633,7 +1617,7 @@ Cannot be applied to a vector
 .Pf %% Ar pattern No }
 .Xc
 .Sm on
-Like ${..#..} substitution, but it deletes from the end of the value.
+Like ${...#...} substitution, but it deletes from the end of the value.
 Cannot be applied to a vector.
 .Pp
 .Sm off
@@ -1767,7 +1751,7 @@ If no background processes have been started, the parameter is not set.
 .It Ev \&#
 The number of positional parameters ($1, $2, etc.).
 .It Ev \&$
-The PID of the shell, or the PID of the original shell if it is a subshell.
+The PID of the shell or, if it is a subshell, the PID of the original shell.
 Do
 .Em NOT
 use this mechanism for generating temporary file names; see
@@ -1780,7 +1764,7 @@ command below for a list of options).
 .It Ev \&?
 The exit status of the last non-asynchronous command executed.
 If the last command was killed by a signal,
-.Ic $?\&
+.Ic \&$?
 is set to 128 plus the signal number, but at most 255.
 .It Ev 0
 The name of the shell, determined as follows:
@@ -1801,7 +1785,7 @@ keyword (i.e. a Korn shell style function).
 .It Ev 1 No .. Ev 9
 The first nine positional parameters that were supplied to the shell, function,
 or script sourced using the
-.Sq \&.
+.Dq Li \&.
 built-in.
 Further positional parameters may be accessed using
 .Pf ${ Ar number Ns } .
@@ -1815,18 +1799,16 @@ by the first character of the
 .Ev IFS
 parameter (or the empty string if
 .Ev IFS
-is
-.Dv NULL ) .
+is unset.
 .It Ev @
 Same as
 .Ic $* ,
 unless it is used inside double quotes, in which case a separate word is
 generated for each positional parameter.
 If there are no positional parameters, no word is generated.
-.Ic $@
+.Ic \&"$@"
 can be used to access arguments, verbatim, without losing
-.Dv NULL
-arguments or splitting arguments with spaces.
+empty arguments or splitting arguments with spaces (IFS, actually).
 .El
 .Pp
 The following parameters are set and/or used by the shell:
@@ -1848,7 +1830,7 @@ built-in command.
 Note that if
 .Ev CDPATH
 is set and does not contain
-.Sq \&.
+.Dq Li \&.
 or an empty string element, the current directory is not searched.
 Also, the
 .Ic cd
@@ -1861,9 +1843,9 @@ value as reported by
 .Xr stty 1
 is non-zero and sane enough (minimum is 12x3); similar for
 .Ev LINES .
-This parameter is used by the interactive line editing modes, and by the
+This parameter is used by the interactive line editing modes and by the
 .Ic select ,
-.Ic set Fl o ,
+.Ic set Fl o
 and
 .Ic kill Fl l
 commands to format information columns.
@@ -1879,7 +1861,7 @@ Time since the epoch, as returned by
 formatted as decimal
 .Va tv_sec
 followed by a dot
-.Pq Sq \&.
+.Pq Ql \&.
 and
 .Va tv_usec
 padded to exactly six decimal digits.
@@ -1888,7 +1870,7 @@ If set, this parameter is assumed to contain the shell that is to be used to
 execute commands that
 .Xr execve 2
 fails to execute and which do not start with a
-.Dq #! Ns Ar shell
+.Dq Li #! Ns Ar shell
 sequence.
 .It Ev FCEDIT
 The editor used by the
@@ -1937,7 +1919,7 @@ below).
 .It Ev IFS
 Internal field separator, used during substitution and by the
 .Ic read
-command, to split values into distinct arguments; normally set to space, tab,
+command, to split values into distinct arguments; normally set to space, tab
 and newline.
 See
 .Sx Substitution
@@ -2035,7 +2017,7 @@ See
 The previous working directory.
 Unset if
 .Ic cd
-has not successfully changed directories since the shell started, or if the
+has not successfully changed directories since the shell started or if the
 shell doesn't know where it is.
 .It Ev OPTARG
 When using
@@ -2050,11 +2032,11 @@ to process arguments from the beginning the next time it is invoked.
 .It Ev PATH
 A colon (semicolon on OS/2) separated list of directories that are
 searched when looking for commands and files sourced using the
-.Sq \&.
+.Dq Li \&.
 command (see below).
 An empty string resulting from a leading or trailing
 colon, or two adjacent colons, is treated as a
-.Sq \&.
+.Dq Li \&.
 (the current directory).
 .It Ev PGRP
 The process ID of the shell's process group leader.
@@ -2065,7 +2047,7 @@ one by one, of the last pipeline run in the foreground.
 The process ID of the shell's parent.
 .It Ev PS1
 The primary prompt for interactive shells.
-Parameter, command, and arithmetic
+Parameter, command and arithmetic
 substitutions are performed, and
 .Ql \&!
 is replaced with the current command number (see the
@@ -2074,21 +2056,21 @@ command below).
 A literal
 .Ql \&!
 can be put in the prompt by placing
-.Ql !!
+.Dq Li !!
 in
 .Ev PS1 .
 .Pp
 The default prompt is
-.Sq $\ \&
+.Dq Li $\ \&
 for non-root users,
-.Sq #\ \&
+.Dq Li #\ \&
 for root.
 If
 .Nm
 is invoked by root and
 .Ev PS1
 does not contain a
-.Sq #
+.Ql #
 character, the default value will be used even if
 .Ev PS1
 already exists in the environment.
@@ -2125,7 +2107,7 @@ if you did not have any non-printing characters.
 Since Backslashes and other special characters may be
 interpreted by the shell, to set
 .Ev PS1
-either escape the backslash itself,
+either escape the backslash itself
 or use double quotes.
 The latter is more practical.
 This is a more complex example,
@@ -2149,30 +2131,28 @@ PS1=$'\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt '
 .Ed
 .It Ev PS2
 Secondary prompt string, by default
-.Sq \*(Gt\ \& ,
+.Dq Li \*(Gt\ \& ,
 used when more input is needed to complete a command.
 .It Ev PS3
 Prompt used by the
 .Ic select
 statement when reading a menu selection.
 The default is
-.Sq #?\ \& .
+.Dq Li #?\ \& .
 .It Ev PS4
 Used to prefix commands that are printed during execution tracing (see the
 .Ic set Fl x
 command below).
-Parameter, command, and arithmetic substitutions are performed
+Parameter, command and arithmetic substitutions are performed
 before it is printed.
 The default is
-.Sq +\ \& .
+.Dq Li +\ \& .
 You may want to set it to
-.Sq \&[$EPOCHREALTIME]\ \&
+.Dq Li \&[$EPOCHREALTIME]\ \&
 instead, to include timestamps.
 .It Ev PWD
 The current working directory.
-May be unset or
-.Dv NULL
-if the shell doesn't know where it is.
+May be unset or empty if the shell doesn't know where it is.
 .It Ev RANDOM
 Each time
 .Ev RANDOM
@@ -2198,7 +2178,7 @@ If the time is exceeded, the shell exits.
 .It Ev TMPDIR
 The directory temporary shell files are created in.
 If this parameter is not
-set, or does not contain the absolute path of a writable directory, temporary
+set or does not contain the absolute path of a writable directory, temporary
 files are created in
 .Pa /tmp .
 .It Ev USER_ID
@@ -2212,12 +2192,12 @@ The characters following the tilde, up to the first
 .Ql / ,
 if any, are assumed to be a login name.
 If the login name is empty,
-.Ql + ,
+.Ql +
 or
 .Ql \- ,
 the simplified value of the
 .Ev HOME ,
-.Ev PWD ,
+.Ev PWD
 or
 .Ev OLDPWD
 parameter is substituted, respectively.
@@ -2234,19 +2214,19 @@ in the arguments of
 .Ic alias ,
 .Ic export ,
 .Ic global ,
-.Ic readonly ,
+.Ic readonly
 and
 .Ic typeset ) ,
 tilde expansion is done after any assignment
 (i.e. after the equals sign)
 or after an unquoted colon
-.Pq Sq \&: ;
+.Pq Ql \&: ;
 login names are also delimited by colons.
 .Pp
 The home directory of previously expanded login names are cached and re-used.
 The
 .Ic alias Fl d
-command may be used to list, change, and add to this cache (e.g.\&
+command may be used to list, change and add to this cache (e.g.\&
 .Ic alias \-d fac=/usr/local/facilities; cd \*(TIfac/bin ) .
 .Ss Brace expansion (alternation)
 Brace expressions take the following form:
@@ -2263,21 +2243,21 @@ The expressions are expanded to
 .Ar N
 words, each of which is the concatenation of
 .Ar prefix ,
-.Ar str Ns i ,
+.Ar str Ns i
 and
 .Ar suffix
 (e.g.\&
-.Dq a{c,b{X,Y},d}e
+.Dq Li a{c,b{X,Y},d}e
 expands to four words:
-.Dq ace ,
-.Dq abXe ,
-.Dq abYe ,
+.Dq Li ace ,
+.Dq Li abXe ,
+.Dq Li abYe
 and
-.Dq ade ) .
+.Dq Li ade ) .
 As noted in the example, brace expressions can be nested and the resulting
 words are not sorted.
 Brace expressions must contain an unquoted comma
-.Pq Sq \&,
+.Pq Ql \&,
 for expansion to occur (e.g.\&
 .Ic {}
 and
@@ -2290,11 +2270,11 @@ A file name pattern is a word containing one or more unquoted
 .Ql \&? ,
 .Ql * ,
 .Ql + ,
-.Ql @ ,
+.Ql @
 or
 .Ql \&!
 characters or
-.Dq \&[..]
+.Dq Li \&[...]
 sequences.
 Once brace expansion has been performed, the shell replaces file
 name patterns with the sorted names of all the files that match the pattern
@@ -2305,14 +2285,14 @@ The pattern elements have the following meaning:
 Matches any single character.
 .It \&*
 Matches any sequence of octets.
-.It \&[..]
+.It \&[...]
 Matches any of the octets inside the brackets.
 Ranges of octets can be specified by separating two octets by a
 .Ql \-
 (e.g.\&
-.Dq \&[a0\-9]
+.Dq Li \&[a0\-9]
 matches the letter
-.Sq a
+.Ql a
 or any digit).
 In order to represent itself, a
 .Ql \-
@@ -2325,8 +2305,8 @@ Also, a
 .Ql \&!
 appearing at the start of the list has special meaning (see below), so to
 represent itself it must be quoted or appear later in the list.
-.It \&[!..]
-Like [..],
+.It \&[!...]
+Like [...],
 except it matches any octet not inside the brackets.
 .Sm off
 .It *( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
@@ -2337,9 +2317,9 @@ Example: The pattern
 .Ic *(foo\*(Babar)
 matches the strings
 .Dq ,
-.Dq foo ,
-.Dq bar ,
-.Dq foobarfoo ,
+.Dq Li foo ,
+.Dq Li bar ,
+.Dq Li foobarfoo ,
 etc.
 .Sm off
 .It +( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
@@ -2349,9 +2329,9 @@ specified patterns.
 Example: The pattern
 .Ic +(foo\*(Babar)
 matches the strings
-.Dq foo ,
-.Dq bar ,
-.Dq foobar ,
+.Dq Li foo ,
+.Dq Li bar ,
+.Dq Li foobar ,
 etc.
 .Sm off
 .It ?( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
@@ -2362,9 +2342,9 @@ Example: The pattern
 .Ic ?(foo\*(Babar)
 only matches the strings
 .Dq ,
-.Dq foo ,
+.Dq Li foo
 and
-.Dq bar .
+.Dq Li bar .
 .Sm off
 .It @( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
 .Sm on
@@ -2372,9 +2352,9 @@ Matches a string that matches one of the specified patterns.
 Example: The pattern
 .Ic @(foo\*(Babar)
 only matches the strings
-.Dq foo
+.Dq Li foo
 and
-.Dq bar .
+.Dq Li bar .
 .Sm off
 .It !( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
 .Sm on
@@ -2382,13 +2362,13 @@ Matches any string that does not match one of the specified patterns.
 Examples: The pattern
 .Ic !(foo\*(Babar)
 matches all strings except
-.Dq foo
+.Dq Li foo
 and
-.Dq bar ;
+.Dq Li bar ;
 the pattern
-.Ic !(*)
+.Ic \&!(*)
 matches no strings; the pattern
-.Ic !(?)*\&
+.Ic \&!(?)*
 matches all strings (think about it).
 .El
 .Pp
@@ -2399,28 +2379,28 @@ Note that
 .Nm mksh
 .Po and Nm pdksh Pc
 never matches
-.Sq \&.
+.Dq Li \&.
 and
-.Sq .. ,
+.Dq Li .. ,
 but
 .At
 .Nm ksh ,
 Bourne
-.Nm sh ,
+.Nm sh
 and GNU
 .Nm bash
 do.
 .Pp
 Note that none of the above pattern elements match either a period
-.Pq Sq \&.
+.Pq Ql \&.
 at the start of a file name or a slash
-.Pq Sq / ,
-even if they are explicitly used in a [..] sequence; also, the names
-.Sq \&.
+.Pq Ql / ,
+even if they are explicitly used in a [...] sequence; also, the names
+.Dq Li \&.
 and
-.Sq ..
+.Dq Li ..
 are never matched, even by the pattern
-.Sq .* .
+.Dq Li .* .
 .Pp
 If the
 .Ic markdirs
@@ -2428,13 +2408,13 @@ option is set, any directories that result from file name generation are marked
 with a trailing
 .Ql / .
 .Ss Input/output redirection
-When a command is executed, its standard input, standard output, and standard
-error (file descriptors 0, 1, and 2, respectively) are normally inherited from
+When a command is executed, its standard input, standard output and standard
+error (file descriptors 0, 1 and 2, respectively) are normally inherited from
 the shell.
 Three exceptions to this are commands in pipelines, for which
 standard input and/or standard output are those set up by the pipeline,
 asynchronous commands created when job control is disabled, for which standard
-input is initially set to be from
+input is initially set to
 .Pa /dev/null ,
 and commands for which any of the following redirections have been specified:
 .Bl -tag -width XXxxmarker
@@ -2490,15 +2470,15 @@ If
 .Ar marker
 contains no quoted characters, the contents of the temporary file are processed
 as if enclosed in double quotes each time the command is executed, so
-parameter, command, and arithmetic substitutions are performed, along with
+parameter, command and arithmetic substitutions are performed, along with
 backslash
-.Pq Sq \e
+.Pq Ql \e
 escapes for
 .Ql $ ,
 .Ql \` ,
-.Ql \e ,
+.Ql \e
 and
-.Ql \enewline ,
+.Dq Li \enewline ,
 but not for
 .Ql \&" .
 If multiple here documents are used on the same command line, they are saved in
@@ -2512,9 +2492,9 @@ and substitution will be performed.
 If
 .Ar marker
 is only a set of either single
-.Dq \*(aq\*(aq
+.Dq Li \*(aq\*(aq
 or double
-.Sq \&""
+.Ql \&""
 quotes with nothing in between, the here document ends at the next empty line
 and substitution will not be performed.
 .It \*(Lt\*(Lt\- Ns Ar marker
@@ -2566,7 +2546,7 @@ but a syntax error in GNU
 .Xc
 Same as
 .Ic \*(Gt\*(Ba Ns Ar file ,
-.Ic \*(Gt\*(Gt Ns Ar file ,
+.Ic \*(Gt\*(Gt Ns Ar file
 or
 .Ic \*(Gt& Ns Ar fd ,
 followed by
@@ -2581,11 +2561,11 @@ 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 single digit.
-Parameter, command, and arithmetic
-substitutions, tilde substitutions, and (if the shell is interactive)
+Parameter, command and arithmetic
+substitutions, tilde substitutions, and, if the shell is interactive,
 file name generation are all performed on the
 .Ar file ,
-.Ar marker ,
+.Ar marker
 and
 .Ar fd
 arguments of redirections.
@@ -2612,7 +2592,7 @@ File descriptors created by I/O redirections are private to the shell.
 .Ss Arithmetic expressions
 Integer arithmetic expressions can be used with the
 .Ic let
-command, inside $((..)) expressions, inside array references (e.g.\&
+command, inside $((...)) expressions, inside array references (e.g.\&
 .Ar name Ns Bq Ar expr ) ,
 as numeric arguments to the
 .Ic test
@@ -2622,17 +2602,17 @@ This also affects implicit conversion to integer, for example as done by the
 .Ic let
 command.
 .Em Never
-use unchecked user input, e.g. from the environment, in arithmetics!
+use unchecked user input, e.g. from the environment, in an arithmetic context!
 .Pp
 Expressions are calculated using signed arithmetic and the
 .Vt mksh_ari_t
 type (a 32-bit signed integer), unless they begin with a sole
-.Sq #
+.Ql #
 character, in which case they use
 .Vt mksh_uari_t
 .Po a 32-bit unsigned integer Pc .
 .Pp
-Expressions may contain alpha-numeric parameter identifiers, array references,
+Expressions may contain alpha-numeric parameter identifiers, array references
 and integer constants and may be combined with the following C operators
 (listed and grouped in increasing order of precedence):
 .Pp
@@ -2677,13 +2657,13 @@ 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
-.Sq 0x
+.Dq Li 0x
 .Pq case-insensitive
 in all forms of arithmetic expressions, except as numeric arguments to the
 .Ic test
 built-in utility.
 Prefixing numbers with a sole digit zero
-.Pq Sq 0
+.Pq Dq Li 0
 does not cause interpretation as octal (except in POSIX mode,
 as required by the standard), as that's unsafe to do.
 .Pp
@@ -2697,9 +2677,9 @@ The
 .At
 .Nm ksh93
 syntax of
-.Dq \*(aqx\*(aq
+.Dq Li \*(aqx\*(aq
 instead of
-.Dq 1#x
+.Dq Li 1#x
 is also supported.
 Note that NUL bytes (integral value of zero) cannot be used.
 An unset or empty parameter evaluates to 0 in integer context.
@@ -2764,9 +2744,9 @@ with any operator precedence in
 .Aq Ar expr
 preserved.
 For example,
-.Dq var1 *= 5 + 3
+.Dq Li var1 *= 5 + 3
 is the same as specifying
-.Dq var1 = var1 * (5 + 3) .
+.Dq Li var1 = var1 * (5 + 3) .
 .It \*(Ba\*(Ba
 Logical OR;
 the result is 1 if either argument is non-zero, 0 if not.
@@ -2802,7 +2782,7 @@ 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 + \- * /
-Addition, subtraction, multiplication, and division.
+Addition, subtraction, multiplication and division.
 .It %
 Remainder; the result is the symmetric remainder of the division of the left
 argument by the right.
@@ -2830,7 +2810,7 @@ The non-result argument is not evaluated.
 .El
 .Ss Co-processes
 A co-process (which is a pipeline created with the
-.Sq \*(Ba&
+.Dq Li \*(Ba&
 operator) is an asynchronous process that the shell can both write to (using
 .Ic print Fl p )
 and read from (using
@@ -2890,7 +2870,7 @@ syntax (see below for the difference between the two forms).
 Functions are like
 .Li .\(hyscripts
 (i.e. scripts sourced using the
-.Sq \&.
+.Dq Li \&.
 built-in)
 in that they are executed in the current environment.
 However, unlike
@@ -3011,9 +2991,9 @@ The EXIT trap, if set in a function, will be executed after the function
 returns.
 .El
 .Ss Command execution
-After evaluation of command-line arguments, redirections, and parameter
+After evaluation of command-line arguments, redirections and parameter
 assignments, the type of command is determined: a special built-in command,
-a function, a normal builtin, or the name of a file to execute found using the
+a function, a normal builtin or the name of a file to execute found using the
 .Ev PATH
 parameter.
 The checks are made in the above order.
@@ -3127,7 +3107,7 @@ above).
 If the
 .Fl p
 option is used, each alias is prefixed with the string
-.Dq alias\ \& .
+.Dq Li alias\ \& .
 .Pp
 The
 .Fl t
@@ -3221,7 +3201,7 @@ Exit the
 inner-most
 .Ic for ,
 .Ic select ,
-.Ic until ,
+.Ic until
 or
 .Ic while
 loop.
@@ -3246,7 +3226,7 @@ standard output.
 If a
 .Ar file
 is a single dash
-.Pq Sq -
+.Pq Dq Li \-
 or absent, read from standard input.
 For direct builtin calls, the
 .Tn POSIX
@@ -3277,16 +3257,13 @@ If the parameter
 .Ev CDPATH
 is set, it lists the search path for the directory containing
 .Ar dir .
-A
-.Dv NULL
-path means the current directory.
+An unset or empty path means the current directory.
 If
 .Ar dir
 is found in any component of the
 .Ev CDPATH
-search path other than the
-.Dv NULL
-path, the name of the new working directory will be written to standard output.
+search path other than an unset or empty path,
+the name of the new working directory will be written to standard output.
 If
 .Ar dir
 is missing, the home directory
@@ -3295,7 +3272,7 @@ is used.
 If
 .Ar dir
 is
-.Ql \- ,
+.Dq Li \- ,
 the previous working directory is used (see the
 .Ev OLDPWD
 parameter).
@@ -3307,7 +3284,7 @@ option (logical path) is used or if the
 option isn't set (see the
 .Ic set
 command below), references to
-.Sq ..
+.Dq Li ..
 in
 .Ar dir
 are relative to the path used to get to the directory.
@@ -3316,7 +3293,7 @@ If the
 option (physical path) is used or if the
 .Ic physical
 option is set,
-.Sq ..
+.Dq Li ..
 is relative to the filesystem directory tree.
 The
 .Ev PWD
@@ -3326,7 +3303,7 @@ parameters are updated to reflect the current and old working directory,
 respectively.
 If the
 .Fl e
-option is set for physical filesystem traversal, and
+option is set for physical filesystem traversal and
 .Ev PWD
 could not be set, the exit code is 1; greater than 1 if an
 error occurred, 0 otherwise.
@@ -3403,7 +3380,7 @@ Jumps to the beginning of the
 inner-most
 .Ic for ,
 .Ic select ,
-.Ic until ,
+.Ic until
 or
 .Ic while
 loop.
@@ -3424,7 +3401,7 @@ Prints its arguments (separated by spaces) followed by a newline, to the
 standard output.
 The newline is suppressed if any of the arguments contain the
 backslash sequence
-.Ql \ec .
+.Dq Li \ec .
 See the
 .Ic print
 command below for a list of other backslash sequences that are recognised.
@@ -3446,7 +3423,7 @@ or
 .Ic sh
 option is set or this is a direct builtin call, only the first argument
 is treated as an option, and only if it is exactly
-.Dq Fl n .
+.Dq Li \-n .
 Backslash interpretation is disabled.
 .Pp
 .It Ic eval Ar command ...
@@ -3490,7 +3467,7 @@ The shell exits with the specified exit status.
 If
 .Ar status
 is not specified, the exit status is the current value of the
-.Ic $?\&
+.Ic \&$?
 parameter.
 .Pp
 .It Xo
@@ -3504,7 +3481,7 @@ If values are specified, the named parameters are also assigned.
 .Pp
 If no parameters are specified, all parameters with the export attribute
 set are printed one per line; either their names, or, if a
-.Ql \-
+.Dq Li \-
 with no option letter is specified, name=value pairs, or, with
 .Fl p ,
 .Ic export
@@ -3539,7 +3516,7 @@ Without
 .Fl l ,
 the selected commands are edited by the editor specified with the
 .Fl e
-option, or if no
+option or, if no
 .Fl e
 is specified, the editor specified by the
 .Ev FCEDIT
@@ -3640,7 +3617,7 @@ Options end at the first (non-option
 argument) argument that does not start with a
 .Ql \- ,
 or when a
-.Ql \-\-
+.Dq Li \-\-
 argument is encountered.
 .Pp
 Option parsing can be reset by setting
@@ -3650,9 +3627,9 @@ invoked).
 .Pp
 Warning: Changing the value of the shell parameter
 .Ev OPTIND
-to a value other than 1, or parsing different sets of arguments without
+to a value other than 1 or parsing different sets of arguments without
 resetting
-.Ev OPTIND ,
+.Ev OPTIND
 may lead to unexpected results.
 .Pp
 .It global Ar ...
@@ -3704,7 +3681,7 @@ and the displayed job.
 .No { Ar job \*(Ba pid \*(Ba pgrp No }
 .Ar ...
 .Xc
-Send the specified signal to the specified jobs, process IDs, or process
+Send the specified signal to the specified jobs, process IDs or process
 groups.
 If no signal is specified, the
 .Dv TERM
@@ -3722,8 +3699,8 @@ below for the format of
 .Xc
 Print the signal name corresponding to
 .Ar exit-status .
-If no arguments are specified, a list of all the signals, their numbers, and
-a short description of them are printed.
+If no arguments are specified, a list of all the signals with their numbers
+and a short description of each are printed.
 .Pp
 .It Ic let Op Ar expression ...
 Each expression is evaluated (see
@@ -3760,7 +3737,7 @@ The file type may be
 .Cm b
 (block type device),
 .Cm c
-(character type device),
+(character type device)
 or
 .Cm p
 .Pq named pipe , Tn FIFO .
@@ -3780,46 +3757,57 @@ however, distributors may have added this as builtin as a speed hack.
 .Pp
 .It Xo
 .Ic print
-.Oo Fl Anprsu Ns Oo Ar n Oc \*(Ba
+.Oo Fl AclNnprsu Ns Oo Ar n Oc \*(Ba
 .Fl R Op Fl en Oc
 .Op Ar argument ...
 .Xc
-.Ic print
-prints its arguments on the standard output, separated by spaces and
-terminated with a newline.
-The
-.Fl n
-option suppresses the newline.
-By default, certain C escapes are translated.
-These include these mentioned in
+Print the specified argument(s) on the standard output,
+separated by spaces, terminated with a newline.
+The C escapes mentioned in
 .Sx Backslash expansion
 above, as well as
-.Ql \ec ,
+.Dq Li \ec ,
 which is equivalent to using the
 .Fl n
-option.
-Backslash expansion may be inhibited with the
-.Fl r
-option.
-The
-.Fl s
-option prints to the history file instead of standard output; the
-.Fl u
-option prints to file descriptor
-.Ar n
-.Po
-.Ar n
-defaults to 1 if omitted
-.Pc ;
-and the
-.Fl p
-option prints to the co-process (see
+option, are interpreted.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl A
+Each
+.Ar argument
+is arithmetically evaluated; the character corresponding to the
+resulting value is printed.
+Empty
+.Ar argument Ns s
+separate input words.
+.It Fl c
+The output is printed columnised, line by line, similar to how the
+.Xr rs 1
+utility, tab completion, the
+.Ic kill Fl l
+built-in utility and the
+.Ic select
+statement do.
+.It Fl l
+Change the output word separator to newline.
+.It Fl N
+Change the output word and line separator to ASCII NUL.
+.It Fl n
+Do not print the trailing line separator.
+.It Fl p
+Print to the co-process (see
 .Sx Co-processes
 above).
-The
-.Fl A
-option prints the character corresponding to each
-.Ar argument Ns 's value .
+.It Fl r
+Inhibit backslash expansion.
+.It Fl s
+Print to the history file instead of standard output.
+.It Fl u Op Ar n
+Print to the file descriptor
+.Ar n Pq defaults to 1 if omitted
+instead of standard output.
+.El
 .Pp
 The
 .Fl R
@@ -3865,7 +3853,7 @@ If the
 option (physical path) is used or if the
 .Ic physical
 option is set, the path determined from the filesystem (by following
-.Sq ..
+.Dq Li ..
 directories to the root directory) is printed.
 .Pp
 .It Xo
@@ -3924,7 +3912,10 @@ if empty, instead of the ASCII newline character as input line delimiter.
 Instead of reading till end-of-line, read exactly
 .Ar z
 bytes.
-If EOF or a timeout occurs, a partial read is returned with exit status 1.
+Upon EOF, a partial read is returned with exit status 1.
+After timeout, a partial read is returned with an exit status as if
+.Dv SIGALRM
+were caught.
 .It Fl n Ar z
 Instead of reading till end-of-line, read up to
 .Ar z
@@ -3945,7 +3936,9 @@ Interrupt reading after
 seconds (specified as positive decimal value with an optional fractional part).
 The exit status of
 .Nm read
-is 1 if the timeout occurred, but partial reads may still be returned.
+is the same as if
+.Dv SIGALRM
+were caught if the timeout occurred, but partial reads may still be returned.
 .It Fl r
 Normally, the ASCII backslash character escapes the special
 meaning of the following character and is stripped from the input;
@@ -4042,7 +4035,7 @@ Prints the resolved absolute pathname corresponding to
 If
 .Ar name
 ends with a slash
-.Pq Sq / ,
+.Pq Ql / ,
 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,
@@ -4162,7 +4155,7 @@ This does not apply to commands whose exit status is
 explicitly tested by a shell construct such as
 .Ic if ,
 .Ic until ,
-.Ic while ,
+.Ic while
 or
 .Ic \&!
 statements.
@@ -4230,7 +4223,7 @@ and optionally
 .Fn nl_langinfo CODESET ,
 or the
 .Ev LC_ALL ,
-.Ev LC_CTYPE ,
+.Ev LC_CTYPE
 or
 .Ev LANG
 environment variables,
@@ -4249,12 +4242,12 @@ is changed whenever one of the
 locale-related environment variables changes.
 .It Fl u \*(Ba Fl o Ic nounset
 Referencing of an unset parameter, other than
-.Dq $@
+.Dq Li $@
 or
-.Dq $* ,
+.Dq Li $* ,
 is treated as an error, unless one of the
 .Ql \- ,
-.Ql + ,
+.Ql +
 or
 .Ql =
 modifiers is used.
@@ -4317,11 +4310,11 @@ and
 commands to use
 .Dq physical
 (i.e. the filesystem's)
-.Sq ..
+.Dq Li ..
 directories instead of
 .Dq logical
 directories (i.e. the shell handles
-.Sq .. ,
+.Dq Li .. ,
 which allows the user to be oblivious of symbolic links to directories).
 Clear by default.
 Note that setting this option does not affect the current value of the
@@ -4395,7 +4388,7 @@ is always in viraw mode.
 These options can also be used upon invocation of the shell.
 The current set of
 options (with single letter names) can be found in the parameter
-.Sq $\- .
+.Dq Li $\- .
 .Ic set Fl o
 with no option name will list all the options and whether each is on or off;
 .Ic set +o
@@ -4409,11 +4402,11 @@ compliant and print commands to restore the current options instead.
 Remaining arguments, if any, are positional parameters and are assigned, in
 order, to the positional parameters (i.e. $1, $2, etc.).
 If options end with
-.Ql \-\-
+.Dq Li \-\-
 and there are no remaining arguments, all positional parameters are cleared.
 If no options or arguments are given, the values of all names are printed.
 For unknown historical reasons, a lone
-.Ql \-
+.Dq Li \-
 option is treated specially \*(en it clears both the
 .Fl v
 and
@@ -4424,10 +4417,7 @@ options.
 The positional parameters
 .Ar number Ns +1 ,
 .Ar number Ns +2 ,
-etc. are renamed to
-.Sq 1 ,
-.Sq 2 ,
-etc.
+etc. are renamed to 1, 2, etc.
 .Ar number
 defaults to 1.
 .Pp
@@ -4540,11 +4530,11 @@ or
 .Ql +
 .Pq no logical negation ,
 for example
-.Ql \-x
+.Dq Li \-x
 or
-.Ql +x
+.Dq Li +x
 instead of
-.Ql xtrace .
+.Dq Li xtrace .
 .It Fl p Ar file
 .Ar file
 is a named pipe
@@ -4653,7 +4643,7 @@ Note that some special rules are applied (courtesy of
 or inside the brackets
 .Ic \&[ ... \&]
 is less than five: if leading
-.Ql \&!
+.Dq Li \&!
 arguments can be stripped such that only one to three arguments remain,
 then the lowered comparison is executed; (thanks to XSI) parentheses
 .Ic \e( ... \e)
@@ -4665,28 +4655,26 @@ prefer negation followed by parenthesis; the one-argument form always implies
 .Pp
 .Sy Note :
 A common mistake is to use
-.Dq if \&[ $foo = bar \&]
+.Dq Li if \&[ $foo = bar \&]
 which fails if parameter
 .Dq foo
-is
-.Dv NULL
-or unset, if it has embedded spaces (i.e.\&
+is empty or unset, if it has embedded spaces (i.e.\&
 .Ev IFS
-octets), or if it is a unary operator like
-.Sq \&!
+octets) or if it is a unary operator like
+.Dq Li \&!
 or
-.Sq Fl n .
+.Dq Li \-n .
 Use tests like
-.Dq if \&[ x\&"$foo\&" = x"bar" \&]
+.Dq Li if \&[ x\&"$foo\&" = x"bar" \&]
 instead, or the double-bracket operator
-.Dq if \&[[ $foo = bar \&]]
+.Dq Li if \&[[ $foo = bar \&]]
 or, to avoid pattern matching (see
 .Ic \&[[
 above):
-.Dq if \&[[ $foo = \&"$bar" \&]]
+.Dq Li if \&[[ $foo = \&"$bar" \&]]
 .Pp
 The
-.Ic \&[[ ... ]]
+.Ic \&[[ ... \&]]
 construct is not only more secure to use but also often faster.
 .Pp
 .It Xo
@@ -4746,8 +4734,8 @@ The format of the output is:
 If the first operand is a decimal unsigned integer, this resets all
 specified signals to the default action, i.e. is the same as calling
 .Ic trap
-with a minus sign
-.Pq Sq \-
+with a dash
+.Pq Dq Li \-
 as
 .Ar handler ,
 followed by the arguments
@@ -4759,13 +4747,12 @@ Sets a trap handler that is to be executed when any of the specified
 .Ar signal Ns s
 are received.
 .Ar handler
-is either an empty string, indicating the signals are to be ignored,
-a minus sign
-.Pq Sq \- ,
+is either an empty string, indicating the signals are to be ignored, a dash
+.Pq Dq Li \- ,
 indicating that the default action is to be taken for the signals
 .Pq see Xr signal 3 ,
 or a string containing shell commands to be executed at the first opportunity
-(i.e. when the current command completes, or before printing the next
+(i.e. when the current command completes or before printing the next
 .Ev PS1
 prompt) after receipt of one of the signals.
 .Ar signal
@@ -4841,7 +4828,7 @@ arguments, parameter attributes are displayed; if no options are used, the
 current attributes of all parameters are printed as
 .Ic typeset
 commands; if an option is given (or
-.Ql \-
+.Dq Li \-
 with no option letter), all parameters and their values with the specified
 attributes are printed; if options are introduced with
 .Ql + ,
@@ -4912,7 +4899,7 @@ If necessary, values are either truncated or space padded
 to fit the field width.
 .It Fl l
 Lower case attribute.
-All upper case characters in values are converted to lower case.
+All upper case ASCII characters in values are converted to lower case.
 (In the original Korn shell, this parameter meant
 .Dq long integer
 when used with the
@@ -4936,7 +4923,7 @@ is lazily evaluated at the time
 .Ar name
 is accessed.
 This can be used by functions to access variables whose names are
-passed as parametres, instead of using
+passed as parameters, instead of using
 .Ic eval .
 .It Fl p
 Print complete
@@ -4977,7 +4964,7 @@ option).
 This option is not in the original Korn shell.
 .It Fl u
 Upper case attribute.
-All lower case characters in values are converted to upper case.
+All lower case ASCII characters in values are converted to upper case.
 (In the original Korn shell, this parameter meant
 .Dq unsigned integer
 when used with the
@@ -5016,7 +5003,7 @@ If any of the
 .Fl l ,
 .Fl R ,
 .Fl U ,
-.Fl u ,
+.Fl u
 or
 .Fl Z
 options are changed, all others from this set are cleared,
@@ -5146,7 +5133,7 @@ is concerned, a block is 512 bytes.
 .Op Fl S
 .Op Ar mask
 .Xc
-Display or set the file permission creation mask, or umask (see
+Display or set the file permission creation mask or umask (see
 .Xr umask 2 ) .
 If the
 .Fl S
@@ -5158,11 +5145,11 @@ Symbolic masks are like those used by
 When used, they describe what permissions may be made available (as opposed to
 octal masks in which a set bit means the corresponding bit is to be cleared).
 For example,
-.Dq ug=rwx,o=
-sets the mask so files will not be readable, writable, or executable by
+.Dq Li ug=rwx,o=
+sets the mask so files will not be readable, writable or executable by
 .Dq others ,
 and is equivalent (on most systems) to the octal mask
-.Dq 007 .
+.Dq Li 007 .
 .Pp
 .It Xo
 .Ic unalias
@@ -5206,7 +5193,7 @@ The exit status of
 is that of the last specified job; if the last job is killed by a signal, the
 exit status is 128 + the number of the signal (see
 .Ic kill Fl l Ar exit-status
-above); if the last specified job can't be found (because it never existed, or
+above); if the last specified job can't be found (because it never existed or
 had already finished), the exit status of
 .Ic wait
 is 127.
@@ -5215,9 +5202,9 @@ See
 below for the format of
 .Ar job .
 .Ic wait
-will return if a signal for which a trap has been set is received, or if a
+will return if a signal for which a trap has been set is received or if a
 .Dv SIGHUP ,
-.Dv SIGINT ,
+.Dv SIGINT
 or
 .Dv SIGQUIT
 signal is received.
@@ -5273,33 +5260,32 @@ commands, and the state of the terminal is saved or restored when a foreground
 job is stopped or restarted, respectively.
 .Pp
 Note that only commands that create processes (e.g. asynchronous commands,
-subshell commands, and non-built-in, non-function commands) can be stopped;
+subshell commands and non-built-in, non-function commands) can be stopped;
 commands like
 .Ic read
 cannot be.
 .Pp
 When a job is created, it is assigned a job number.
 For interactive shells, this number is printed inside
-.Dq \&[..] ,
+.Dq Li \&[...] ,
 followed by the process IDs of the processes in the job when an asynchronous
 command is run.
 A job may be referred to in the
 .Ic bg ,
 .Ic fg ,
 .Ic jobs ,
-.Ic kill ,
+.Ic kill
 and
 .Ic wait
 commands either by the process ID of the last process in the command pipeline
 (as stored in the
-.Ic $!\&
-parameter) or by prefixing the job number with a percent
-sign
-.Pq Sq % .
+.Ic \&$!
+parameter) or by prefixing the job number with a percent sign
+.Pq Ql % .
 Other percent sequences can also be used to refer to jobs:
 .Bl -tag -width "%+ x %% x %XX"
 .It %+ \*(Ba %% \*(Ba %
-The most recently stopped job, or, if there are no stopped jobs, the oldest
+The most recently stopped job or, if there are no stopped jobs, the oldest
 running job.
 .It %\-
 The job that would be the
@@ -5358,7 +5344,7 @@ The job was killed by a signal (e.g. memory fault, hangup); use
 .Ic kill Fl l
 for a list of signal descriptions.
 The
-.Dq core dumped
+.Dq Li core dumped
 message indicates the process created a core file.
 .El
 .It Ar command
@@ -5395,17 +5381,17 @@ to behave even more
 compliant in places where the defaults or opinions differ.
 Note that
 .Nm mksh
-will still operate with unsigned 32-bit arithmetics; use
+will still operate with unsigned 32-bit arithmetic; use
 .Nm lksh
-if arithmetics on the host
+if arithmetic on the host
 .Vt long
-data type, complete with ISO C Undefined Behaviour, are required;
+data type, complete with ISO C Undefined Behaviour, is required;
 refer to the
 .Xr lksh 1
 manual page for details.
 Most other historic,
 .At
-.Nm ksh Ns -compatible ,
+.Nm ksh Ns -compatible
 or opinionated differences can be disabled by using this mode; these are:
 .Bl -bullet
 .It
@@ -5423,7 +5409,7 @@ Numbers with a leading digit zero are interpreted as octal.
 The
 .Nm echo
 builtin does not interpret backslashes and only supports the exact option
-.Dq Fl n .
+.Dq Li \-n .
 .It
 \&... (list is incomplete and may change for R54)
 .El
@@ -5444,7 +5430,7 @@ child processes.
 The
 .Nm echo
 builtin does not interpret backslashes and only supports the exact option
-.Dq Fl n .
+.Dq Li \-n .
 .It
 The substitution operations
 .Sm off
@@ -5481,7 +5467,7 @@ The shell supports three modes of reading command lines from a
 .Xr tty 4
 in an interactive session, controlled by the
 .Ic emacs ,
-.Ic gmacs ,
+.Ic gmacs
 and
 .Ic vi
 options (at most one of these can be set at once).
@@ -5509,7 +5495,7 @@ In these editing modes, if a line is longer than the screen width (see the
 parameter),
 a
 .Ql \*(Gt ,
-.Ql + ,
+.Ql +
 or
 .Ql \*(Lt
 character is displayed in the last column indicating that there are more
@@ -5518,7 +5504,7 @@ respectively.
 The line is scrolled horizontally as necessary.
 .Pp
 Completed lines are pushed into the history, unless they begin with an
-IFS octet or IFS white space, or are the same as the previous line.
+IFS octet or IFS white space or are the same as the previous line.
 .Ss Emacs editing mode
 When the
 .Ic emacs
@@ -5588,9 +5574,9 @@ characters.
 .Xc
 Moves the cursor backward to the beginning of the word; words consist of
 alphanumerics, underscore
-.Pq Sq _ ,
+.Pq Ql _
 and dollar sign
-.Pq Sq $
+.Pq Ql $
 characters.
 .It beginning\-of\-history: \*(ha[\*(Lt
 Moves to the beginning of the history.
@@ -5600,7 +5586,7 @@ Moves the cursor to the beginning of the edited input line.
 .Op Ar n
 .No \*(ha[C , \*(ha[c
 .Xc
-Uppercase the first character in the next
+Uppercase the first ASCII character in the next
 .Ar n
 words, leaving the cursor past the end of the last word.
 .It clear\-screen: \*(ha[\*(haL
@@ -5634,7 +5620,7 @@ partial word up to the cursor as its prefix, as in the
 .Ic complete
 command described above.
 .It complete\-list: \*(haI, \*(ha[=
-Complete as much as is possible of the current word,
+Complete as much as is possible of the current word
 and list the possible completions for it.
 If only one completion is possible,
 match as in the
@@ -5809,7 +5795,7 @@ Introduces a multi-character command sequence.
 .Op Ar n
 .No \*(ha[. , \*(ha[_
 .Xc
-The last word, or, if given, the
+The last word or, if given, the
 .Ar n Ns th
 word (zero-based) of the previous (on repeated execution, second-last,
 third-last, etc.) command is inserted at the cursor.
@@ -5875,7 +5861,7 @@ or
 .It set\-mark\-command: \*(ha[ Ns Aq space
 Set the mark at the cursor position.
 .It transpose\-chars: \*(haT
-If at the end of line, or if the
+If at the end of line or, if the
 .Ic gmacs
 option is set, this exchanges the two previous characters; otherwise, it
 exchanges the previous and current characters and moves the cursor one
@@ -5926,7 +5912,7 @@ editor with the following exceptions:
 You start out in insert mode.
 .It
 There are file name and command completion commands:
-=, \e, *, \*(haX, \*(haE, \*(haF, and, optionally,
+=, \e, *, \*(haX, \*(haE, \*(haF and, optionally,
 .Aq tab
 and
 .Aq esc .
@@ -5971,7 +5957,7 @@ settings
 (see
 .Xr stty 1 )
 and have their usual meaning (normal values are in parentheses): kill (\*(haU),
-erase (\*(ha?), werase (\*(haW), eof (\*(haD), intr (\*(haC), and quit (\*(ha\e).
+erase (\*(ha?), werase (\*(haW), eof (\*(haD), intr (\*(haC) and quit (\*(ha\e).
 In addition to
 the above, the following characters are also treated specially in insert mode:
 .Bl -tag -width XJXXXXM
@@ -5986,7 +5972,7 @@ is undone.
 Erases previous character.
 .It \*(haJ \*(Ba \*(haM
 End of line.
-The current line is read, parsed, and executed by the shell.
+The current line is read, parsed and executed by the shell.
 .It \*(haV
 Literal next.
 The next character typed is not treated specially (can be used
@@ -6019,9 +6005,9 @@ refers to the position between the cursor and the character preceding the
 cursor.
 A
 .Dq word
-is a sequence of letters, digits, and underscore characters or a sequence of
-non-letter, non-digit, non-underscore, and non-whitespace characters (e.g.\&
-.Dq ab2*&\*(ha
+is a sequence of letters, digits and underscore characters or a sequence of
+non-letter, non-digit, non-underscore and non-whitespace characters (e.g.\&
+.Dq Li ab2*&\*(ha
 contains two words) and a
 .Dq big-word
 is a sequence of non-whitespace characters.
@@ -6044,7 +6030,7 @@ insert mode; if
 is not specified, the last word is inserted.
 .It #
 Insert the comment character
-.Pq Sq #
+.Pq Ql #
 at the start of the current line and return the line to the shell (equivalent
 to
 .Ic I#\*(haJ ) .
@@ -6079,13 +6065,13 @@ or follows one of the characters
 .Ql \&; ,
 .Ql \*(Ba ,
 .Ql & ,
-.Ql \&( ,
+.Ql \&(
 or
-.Ql \&) ,
+.Ql \&)
 and does not contain a slash
-.Pq Sq / ,
+.Pq Ql / ,
 then command expansion is done; otherwise file name expansion is done.
-Command expansion will match the big-word against all aliases, functions, and
+Command expansion will match the big-word against all aliases, functions and
 built-in commands as well as any executable files found by searching the
 directories in the
 .Ev PATH
@@ -6198,8 +6184,8 @@ Move forward
 big-words.
 .It %
 Find match.
-The editor looks forward for the nearest parenthesis, bracket, or
-brace and then moves the cursor to the matching parenthesis, bracket, or brace.
+The editor looks forward for the nearest parenthesis, bracket or
+brace and then moves the cursor to the matching parenthesis, bracket or brace.
 .It Xo
 .Oo Ar n Oc Ns f Ns Ar c
 .Xc
@@ -6232,7 +6218,7 @@ occurrence of the character
 .Oo Ar n Oc Ns \&;
 .Xc
 Repeats the last
-.Ic f , F , t ,
+.Ic f , F , t
 or
 .Ic T
 command.
@@ -6240,7 +6226,7 @@ command.
 .Oo Ar n Oc Ns \&,
 .Xc
 Repeats the last
-.Ic f , F , t ,
+.Ic f , F , t
 or
 .Ic T
 command, but moves in the opposite direction.
@@ -6453,7 +6439,7 @@ except the buffer is pasted at the current position.
 Miscellaneous vi commands
 .Bl -tag -width Ds
 .It \*(haJ and \*(haM
-The current line is read, parsed, and executed by the shell.
+The current line is read, parsed and executed by the shell.
 .It \*(haL and \*(haR
 Redraw the current line.
 .It Xo
@@ -6466,7 +6452,7 @@ times.
 Undo the last edit command.
 .It U
 Undo all changes that have been made to the current line.
-.It PC Home, End, Del, and cursor keys
+.It PC Home, End, Del and cursor keys
 They move as expected, both in insert and command mode.
 .It Ar intr No and Ar quit
 The interrupt and quit terminal characters cause the current line to be
@@ -6600,14 +6586,14 @@ This shell is based on the public domain 7th edition Bourne shell clone by
 .An Charles Forsyth ,
 who kindly agreed to, in countries where the Public Domain status of the work
 may not be valid, grant a copyright licence to the general public to deal in
-the work without restriction and permission to sublicence derivates under the
-terms of any (OSI approved) Open Source licence,
+the work without restriction and permission to sublicence derivatives under
+the terms of any (OSI approved) Open Source licence,
 and parts of the BRL shell by
 .An Doug A. Gwyn ,
 .An Doug Kingston ,
 .An Ron Natalie ,
 .An Arnold Robbins ,
-.An Lou Salkind ,
+.An Lou Salkind
 and others.
 The first release of
 .Nm pdksh
@@ -6615,12 +6601,12 @@ was created by
 .An Eric Gisin ,
 and it was subsequently maintained by
 .An John R. MacMillan ,
-.An Simon J. Gerraty ,
+.An Simon J. Gerraty
 and
 .An Michael Rendell .
 The effort of several projects, such as Debian and OpenBSD, and other
 contributors including our users, to improve the shell is appreciated.
-See the documentation, CVS, and web site for details.
+See the documentation, web site and CVS for details.
 .Pp
 The BSD daemon is Copyright \(co Marshall Kirk McKusick.
 The complete legalese is at:
@@ -6643,7 +6629,7 @@ The complete legalese is at:
 has a different scope model from
 .At
 .Nm ksh ,
-which leads to subtile differences in semantics for identical builtins.
+which leads to subtle differences in semantics for identical builtins.
 This can cause issues with a
 .Ic nameref
 to suddenly point to a local variable by accident; fixing this is hard.
@@ -6658,9 +6644,9 @@ foo \*(Ba bar \*(Ba& read \-p baz        # will, however, do so
 .Ed
 .Pp
 .Nm mksh
-provides a consistent set of 32-bit integer arithmetics, both signed
-and unsigned, with defined wraparound and sign of the result of a
-remainder operation, even (defying POSIX) on 36-bit and 64-bit systems.
+provides a consistent 32-bit integer arithmetic implementation, both
+signed and unsigned, with sign of the result of a remainder operation
+and wraparound defined, even (defying POSIX) on 36-bit and 64-bit systems.
 .Pp
 .Nm mksh
 provides a consistent, clear interface normally.
@@ -6704,7 +6690,7 @@ In near future, (Unicode) locale tracking will be implemented though.
 .Sh BUGS
 Suspending (using \*(haZ) pipelines like the one below will only suspend
 the currently running part of the pipeline; in this example,
-.Dq fubar
+.Dq Li fubar
 is immediately printed on suspension (but not later after an
 .Ic fg ) .
 .Bd -literal -offset indent
@@ -6715,7 +6701,7 @@ The truncation process involved when changing
 .Ev HISTFILE
 does not free old history entries (leaks memory) and leaks
 old entries into the new history if their line numbers are
-not overwritten by same-numer entries from the persistent
+not overwritten by same-number entries from the persistent
 history file; truncating the on-disc file to
 .Ev HISTSIZE
 lines has always been broken and prone to history file corruption
@@ -6724,7 +6710,7 @@ for the in-memory portion of the history is slow, should use
 .Xr memmove 3 .
 .Pp
 This document attempts to describe
-.Nm mksh\ R53
+.Nm mksh\ R54
 and up,
 .\" with vendor patches from insert-your-name-here,
 compiled without any options impacting functionality, such as
index a6cfb6f..c9b9078 100644 (file)
--- a/src/sh.h
+++ b/src/sh.h
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.786 2016/08/12 16:48:05 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.791 2016/11/11 23:31:38 tg Exp $");
 #endif
-#define MKSH_VERSION "R53 2016/08/12"
+#define MKSH_VERSION "R54 2016/11/11"
 
 /* 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 != 530)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 541)
 #error Must run Build.sh to compile this.
 extern void thiswillneverbedefinedIhope(void);
 int
@@ -1350,7 +1350,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
 /* Determine the location of the system (common) profile */
 
 #ifndef MKSH_DEFAULT_PROFILEDIR
-#define MKSH_DEFAULT_PROFILEDIR        "/etc"
+#define MKSH_DEFAULT_PROFILEDIR        MKSH_UNIXROOT "/etc"
 #endif
 
 #define MKSH_SYSTEM_PROFILE    MKSH_DEFAULT_PROFILEDIR "/profile"
@@ -1738,7 +1738,7 @@ struct ioword {
 #define X_EXTRA        20      /* this many extra bytes in X string */
 
 typedef struct XString {
-       /* begin of string */
+       /* beginning of string */
        char *beg;
        /* length of allocated area, minus safety margin */
        size_t len;
@@ -1748,8 +1748,6 @@ typedef struct XString {
        Area *areap;
 } XString;
 
-typedef char *XStringP;
-
 /* initialise expandable string */
 #define XinitN(xs, length, area) do {                          \
        (xs).len = (length);                                    \
@@ -1781,7 +1779,7 @@ typedef char *XStringP;
 /* close, return string */
 #define Xclose(xs, xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
 
-/* begin of string */
+/* beginning of string */
 #define Xstring(xs, xp)        ((xs).beg)
 
 #define Xnleft(xs, xp) ((xs).end - (xp))       /* may be less than 0 */
@@ -1797,7 +1795,7 @@ char *Xcheck_grow(XString *, const char *, size_t);
  */
 
 typedef struct {
-       /* begin of allocated area */
+       /* beginning of allocated area */
        void **beg;
        /* currently used number of entries */
        size_t len;
@@ -1825,6 +1823,15 @@ typedef struct {
 #define XPclose(x)     aresize2((x).beg, XPsize(x), sizeof(void *), ATEMP)
 #define XPfree(x)      afree((x).beg, ATEMP)
 
+/* for print_columns */
+
+struct columnise_opts {
+       struct shf *shf;
+       char linesep;
+       bool do_last;
+       bool prefcol;
+};
+
 /*
  * Lexer internals
  */
@@ -2021,7 +2028,7 @@ void flushcom(bool);
 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 *);
+void pr_list(struct columnise_opts *, char * const *);
 int herein(struct ioword *, char **);
 /* expr.c */
 int evaluate(const char *, mksh_ari_t *, int, bool);
@@ -2122,8 +2129,8 @@ void runtrap(Trap *, bool);
 void cleartraps(void);
 void restoresigs(void);
 void settrap(Trap *, const char *);
-int block_pipe(void);
-void restore_pipe(int);
+bool block_pipe(void);
+void restore_pipe(void);
 int setsig(Trap *, sig_t, int);
 void setexecsig(Trap *, int);
 #if HAVE_FLOCK || HAVE_LOCK_FCNTL
@@ -2237,9 +2244,9 @@ void ksh_getopt_reset(Getopt *, int);
 int ksh_getopt(const char **, Getopt *, const char *);
 void print_value_quoted(struct shf *, const char *);
 char *quote_value(const char *);
-void print_columns(struct shf *, unsigned int,
+void print_columns(struct columnise_opts *, unsigned int,
     void (*)(char *, size_t, unsigned int, const void *),
-    const void *, size_t, size_t, bool);
+    const void *, size_t, size_t);
 void strip_nuls(char *, size_t)
     MKSH_A_BOUNDED(__string__, 1, 2);
 ssize_t blocking_read(int, char *, size_t)
@@ -2420,13 +2427,26 @@ extern int tty_init_fd(void);   /* initialise tty_fd, tty_devtty */
 })
 #define mksh_abspath(s)                        __extension__({                 \
        const char *mksh_abspath_s = (s);                               \
-       (mksh_abspath_s[0] == '/' || (ksh_isalphx(mksh_abspath_s[0]) && \
+       (mksh_cdirsep(mksh_abspath_s[0]) ||                             \
+           (ksh_isalphx(mksh_abspath_s[0]) &&                          \
            mksh_abspath_s[1] == ':'));                                 \
 })
+#define mksh_cdirsep(c)                        __extension__({                 \
+       char mksh_cdirsep_c = (c);                                      \
+       (mksh_cdirsep_c == '/' || mksh_cdirsep_c == '\\');              \
+})
+/*
+ * I've seen mksh_sdirsep(s) and mksh_vdirsep(s) but need to think
+ * more about the OS/2 port (and, possibly, toy with it) before I
+ * can merge this upstream, but good job so far @komh, thanks!
+ */
 #else
 #define binopen2(path,flags)           open((path), (flags) | O_BINARY)
 #define binopen3(path,flags,mode)      open((path), (flags) | O_BINARY, (mode))
 #define mksh_abspath(s)                        ((s)[0] == '/')
+#define mksh_cdirsep(c)                        ((c) == '/')
+#define mksh_sdirsep(s)                        strchr((s), '/')
+#define mksh_vdirsep(s)                        vstrchr((s), '/')
 #endif
 
 /* be sure not to interfere with anyone else's idea about EXTERN */
diff --git a/src/signames.inc b/src/signames.inc
new file mode 100644 (file)
index 0000000..07811fd
--- /dev/null
@@ -0,0 +1,31 @@
+               { "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 8892c1a..87b7c75 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.114 2016/08/04 20:32:14 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.115 2016/09/01 12:59:12 tg Exp $");
 
 struct nesting_state {
        int start_token;        /* token than began nesting (eg, FOR) */
@@ -287,12 +287,12 @@ get_command(int cf)
                syniocf &= ~(KEYWORD|sALIAS);
                t = newtp(TCOM);
                t->lineno = source->line;
-               goto get_command_begin;
+               goto get_command_start;
                while (/* CONSTCOND */ 1) {
                        bool check_assign_cmd;
 
                        if (XPsize(args) == 0) {
- get_command_begin:
+ get_command_start:
                                check_assign_cmd = true;
                                cf = sALIAS | CMDASN;
                        } else if (t->u.evalflags)
index bcde2f4..64de0f9 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.207 2016/08/01 21:38:07 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.209 2016/11/11 23:31:39 tg Exp $");
 
 /*-
  * Variables
@@ -133,7 +133,7 @@ initvar(void)
        struct tbl *tp;
 
        ktinit(APERM, &specials,
-           /* currently 15 specials: 75% of 32 = 2^5 */
+           /* currently 18 specials: 75% of 32 = 2^5 */
            5);
        while (i < V_MAX - 1) {
                tp = ktenter(&specials, initvar_names[i],
@@ -1149,6 +1149,13 @@ makenv(void)
                                        /* setstr can't fail here */
                                        setstr(vp, val, KSH_RETURN_ERROR);
                                }
+#ifdef __OS2__
+                               /* these special variables are not exported */
+                               if (!strcmp(vp->name, "BEGINLIBPATH") ||
+                                   !strcmp(vp->name, "ENDLIBPATH") ||
+                                   !strcmp(vp->name, "LIBPATHSTRICT"))
+                                       continue;
+#endif
                                XPput(denv, vp->val.s);
                        }
                if (l->flags & BF_STOPENV)
@@ -1274,6 +1281,13 @@ setspec(struct tbl *vp)
        int st;
 
        switch ((st = special(vp->name))) {
+#ifdef __OS2__
+       case V_BEGINLIBPATH:
+       case V_ENDLIBPATH:
+       case V_LIBPATHSTRICT:
+               setextlibpath(vp->name, str_val(vp));
+               return;
+#endif
 #if HAVE_PERSISTENT_HISTORY
        case V_HISTFILE:
                sethistfile(str_val(vp));
@@ -1396,6 +1410,13 @@ unsetspec(struct tbl *vp)
         */
 
        switch (special(vp->name)) {
+#ifdef __OS2__
+       case V_BEGINLIBPATH:
+       case V_ENDLIBPATH:
+       case V_LIBPATHSTRICT:
+               setextlibpath(vp->name, "");
+               return;
+#endif
 #if HAVE_PERSISTENT_HISTORY
        case V_HISTFILE:
                sethistfile(NULL);
@@ -1686,10 +1707,8 @@ rndset(unsigned long v)
                short r;
        } z;
 
-#ifdef DEBUG
-       /* clear the allocated space, for valgrind */
+       /* clear the allocated space, for valgrind and to avoid UB */
        memset(&z, 0, sizeof(z));
-#endif
 
        h = lcg_state;
        BAFHFinish_reg(h);
index 51cda3e..45fa0eb 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 #if defined(VARSPEC_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.9 2016/07/25 21:02:13 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.10 2016/11/11 23:31:39 tg Exp $");
 #define FN(name)                       /* nothing */
 #elif defined(VARSPEC_ENUMS)
 #define FN(name)                       V_##name,
@@ -40,13 +40,22 @@ F0(NONE)
 
 /* 1 and up are special variables */
 FN(BASHPID)
+#ifdef __OS2__
+FN(BEGINLIBPATH)
+#endif
 FN(COLUMNS)
+#ifdef __OS2__
+FN(ENDLIBPATH)
+#endif
 FN(EPOCHREALTIME)
 #if HAVE_PERSISTENT_HISTORY
 FN(HISTFILE)
 #endif
 FN(HISTSIZE)
 FN(IFS)
+#ifdef __OS2__
+FN(LIBPATHSTRICT)
+#endif
 FN(LINENO)
 FN(LINES)
 FN(OPTIND)