summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2020-05-19 23:25:19 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-05-19 23:25:19 +0000
commitce5d2ec2957457e1267959378ecd8a93e9a9f25d (patch)
tree1b64c7792f0d339770471ce91316a701ec85304e
parent6f444b4cb6ab78d464836588ffad8d719dfae5fe (diff)
parentf54e593e397c3b4f0f4c1a2bd6e6a968787ca963 (diff)
downloadmksh-ce5d2ec2957457e1267959378ecd8a93e9a9f25d.tar.gz
Merge "Upgrade to mksh R59." am: f54e593e39
Change-Id: Ideaba8c15eca297b3d3027498e7c7d52cc4a998d
-rw-r--r--Android.bp2
-rw-r--r--Android.patch.txt24
-rw-r--r--src/Build.sh212
-rw-r--r--src/FAQ2HTML.sh136
-rw-r--r--src/check.pl4
-rw-r--r--src/check.t370
-rw-r--r--src/dot.mkshrc68
-rw-r--r--src/edit.c444
-rw-r--r--src/emacsfn.h7
-rw-r--r--src/eval.c577
-rw-r--r--src/exec.c73
-rw-r--r--src/expr.c57
-rw-r--r--src/exprtok.h18
-rw-r--r--src/funcs.c252
-rw-r--r--src/histrap.c6
-rw-r--r--src/jobs.c31
-rw-r--r--src/lex.c5
-rw-r--r--src/main.c127
-rw-r--r--src/misc.c119
-rw-r--r--src/mksh.11833
-rw-r--r--src/mksh.faq620
-rw-r--r--src/os2.c20
-rw-r--r--src/rlimits.gen14
-rw-r--r--src/rlimits.opt8
-rw-r--r--src/sh.h214
-rw-r--r--src/sh_flags.gen2
-rw-r--r--src/sh_flags.opt4
-rw-r--r--src/shf.c13
-rw-r--r--src/syn.c166
-rw-r--r--src/var.c170
30 files changed, 3628 insertions, 1968 deletions
diff --git a/Android.bp b/Android.bp
index 71a0d67..e3e374b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -107,7 +107,7 @@ cc_defaults {
"-DHAVE_SYS_ERRLIST_DECL=0",
"-DHAVE_SYS_SIGLIST_DECL=1",
"-DHAVE_PERSISTENT_HISTORY=0",
- "-DMKSH_BUILD_R=571",
+ "-DMKSH_BUILD_R=591",
// Additional flags
"-DMKSH_DEFAULT_PROFILEDIR=\"/system/etc\"",
diff --git a/Android.patch.txt b/Android.patch.txt
index 15118e3..d2cd1fa 100644
--- a/Android.patch.txt
+++ b/Android.patch.txt
@@ -1,7 +1,7 @@
-diff -ru mksh-R57/funcs.c src/funcs.c
---- mksh-R57/funcs.c 2018-10-20 14:04:55.000000000 -0700
-+++ src/funcs.c 2019-03-26 12:05:23.976821773 -0700
-@@ -103,7 +103,9 @@
+diff -u mksh-R59/funcs.c src/funcs.c
+--- mksh-R59/funcs.c 2020-04-13 12:51:34.000000000 -0700
++++ src/funcs.c 2020-05-15 16:10:35.035013192 -0700
+@@ -104,7 +104,9 @@
{Tsgbreak, c_brkcont},
{T__builtin, c_builtin},
{Tbuiltin, c_builtin},
@@ -11,7 +11,7 @@ diff -ru mksh-R57/funcs.c src/funcs.c
{Tcd, c_cd},
/* dash compatibility hack */
{"chdir", c_cd},
-@@ -126,7 +128,9 @@
+@@ -125,7 +127,9 @@
{"pwd", c_pwd},
{Tread, c_read},
{Tdsgreadonly, c_typeset},
@@ -20,8 +20,8 @@ diff -ru mksh-R57/funcs.c src/funcs.c
+#endif
{"~rename", c_rename},
{"*=return", c_exitreturn},
- {Tsgset, c_set},
-@@ -160,8 +164,10 @@
+ {Tsghset, c_set},
+@@ -159,8 +163,10 @@
{"~printf", c_printf},
#endif
#if HAVE_SELECT
@@ -32,10 +32,11 @@ diff -ru mksh-R57/funcs.c src/funcs.c
#ifdef __MirBSD__
/* alias to "true" for historical reasons */
{"domainname", c_true},
-diff -ru mksh-R57/main.c src/main.c
---- mksh-R57/main.c 2019-01-05 05:24:45.000000000 -0800
-+++ src/main.c 2019-03-26 12:05:23.980821764 -0700
-@@ -399,6 +399,12 @@
+Only in src: funcs.c.orig
+diff -u mksh-R59/main.c src/main.c
+--- mksh-R59/main.c 2020-04-13 10:04:41.000000000 -0700
++++ src/main.c 2020-05-15 16:10:35.035013192 -0700
+@@ -402,6 +402,12 @@
/* import environment */
init_environ();
@@ -48,3 +49,4 @@ diff -ru mksh-R57/main.c src/main.c
/* for security */
typeset(TinitIFS, 0, 0, 0, 0);
+Only in src: main.c.orig
diff --git a/src/Build.sh b/src/Build.sh
index be3f711..1a95e66 100644
--- a/src/Build.sh
+++ b/src/Build.sh
@@ -1,8 +1,9 @@
#!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.755 2020/04/07 23:15:11 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015, 2016, 2017
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
+# 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@@ -25,8 +26,8 @@ srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
#
# Used environment documentation is at the end of this file.
-LC_ALL=C
-export LC_ALL
+LC_ALL=C; LANGUAGE=C
+export LC_ALL; unset LANGUAGE
case $ZSH_VERSION:$VERSION in
:zsh*) ZSH_VERSION=2 ;;
@@ -53,6 +54,15 @@ alll=qwertyuiopasdfghjklzxcvbnm
alln=0123456789
alls=______________________________________________________________
+test_n() {
+ test x"$1" = x"" || return 0
+ return 1
+}
+
+test_z() {
+ test x"$1" = x""
+}
+
case `echo a | tr '\201' X` in
X)
# EBCDIC build system
@@ -64,11 +74,11 @@ X)
esac
genopt_die() {
- if test -n "$1"; then
- echo >&2 "E: $*"
- echo >&2 "E: in '$srcfile': '$line'"
- else
+ if test_z "$1"; then
echo >&2 "E: invalid input in '$srcfile': '$line'"
+ else
+ echo >&2 "E: $*"
+ echo >&2 "N: in '$srcfile': '$line'"
fi
rm -f "$bn.gen"
exit 1
@@ -172,9 +182,9 @@ do_genopt() {
esac
IFS= read line || genopt_die Unexpected EOF
IFS=$safeIFS
- test -n "$cond" && o_gen=$o_gen$nl"$cond"
+ test_z "$cond" || o_gen=$o_gen$nl"$cond"
o_gen=$o_gen$nl"$line, $optc)"
- test -n "$cond" && o_gen=$o_gen$nl"#endif"
+ test_z "$cond" || o_gen=$o_gen$nl"#endif"
;;
esac
done
@@ -185,11 +195,11 @@ do_genopt() {
esac
echo "$o_str" | sort | while IFS='|' read x opts cond; do
IFS=$safeIFS
- test -n "$x" || continue
+ test_n "$x" || continue
genopt_scond
- test -n "$cond" && echo "$cond"
+ test_z "$cond" || echo "$cond"
echo "\"$opts\""
- test -n "$cond" && echo "#endif"
+ test_z "$cond" || echo "#endif"
done | {
echo "$o_hdr"
echo "#ifndef $o_sym$o_gen"
@@ -234,7 +244,7 @@ vq() {
rmf() {
for _f in "$@"; do
case $_f in
- Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;;
+ *.1|*.faq|*.ico) ;;
*) rm -f "$_f" ;;
esac
done
@@ -343,7 +353,7 @@ ac_testnnd() {
test $ct = pcc && vscan='unsupported'
test $ct = sunpro && vscan='-e ignored -e turned.off'
fi
- test -n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
+ test_n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
return 0
}
ac_testn() {
@@ -406,10 +416,8 @@ ac_flags() {
test x"$ft" = x"" && ft="if $f can be used"
save_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $f"
- if test -n "$fl"; then
- save_LDFLAGS=$LDFLAGS
- LDFLAGS="$LDFLAGS $fl"
- fi
+ save_LDFLAGS=$LDFLAGS
+ test_z "$fl" || LDFLAGS="$LDFLAGS $fl"
if test 1 = $hf; then
ac_testn can_$vn '' "$ft"
else
@@ -421,9 +429,7 @@ ac_flags() {
#'
fi
eval fv=\$HAVE_CAN_`upper $vn`
- if test -n "$fl"; then
- test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
- fi
+ test_z "$fl" || test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
test 11 = $fa$fv || CFLAGS=$save_CFLAGS
}
@@ -512,7 +518,7 @@ ebcdic=false
for i
do
case $last:$i in
- c:combine|c:dragonegg|c:llvm|c:lto)
+ c:dragonegg|c:llvm)
cm=$i
last=
;;
@@ -524,10 +530,6 @@ do
optflags=$i
last=
;;
- t:*)
- tfn=$i
- last=
- ;;
:-c)
last=c
;;
@@ -573,9 +575,6 @@ do
:+T)
textmode=0
;;
- :-t)
- last=t
- ;;
:-v)
echo "Build.sh $srcversion"
echo "for mksh $dstversion"
@@ -591,12 +590,12 @@ do
;;
esac
done
-if test -n "$last"; then
+if test_n "$last"; then
echo "$me: Option -'$last' not followed by argument!" >&2
exit 1
fi
-test -z "$tfn" && if test $legacy = 0; then
+test_n "$tfn" || if test $legacy = 0; then
tfn=mksh
else
tfn=lksh
@@ -606,7 +605,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 *.cat1 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 *.htm
SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
@@ -634,17 +633,17 @@ if test x"$srcdir" = x"."; then
else
CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS"
fi
-test -n "$LDSTATIC" && if test -n "$LDFLAGS"; then
- LDFLAGS="$LDFLAGS $LDSTATIC"
-else
+test_z "$LDSTATIC" || if test_z "$LDFLAGS"; then
LDFLAGS=$LDSTATIC
+else
+ LDFLAGS="$LDFLAGS $LDSTATIC"
fi
-if test -z "$TARGET_OS"; then
+if test_z "$TARGET_OS"; then
x=`uname -s 2>/dev/null || uname`
test x"$x" = x"`uname -n 2>/dev/null`" || TARGET_OS=$x
fi
-if test -z "$TARGET_OS"; then
+if test_z "$TARGET_OS"; then
echo "$me: Set TARGET_OS, your uname is broken!" >&2
exit 1
fi
@@ -652,7 +651,7 @@ oswarn=
ccpc=-Wc,
ccpl=-Wl,
tsts=
-ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;; *) rm -f "$_f" ;; esac; done'
+ccpr='|| for _f in ${tcfn}*; do case $_f in *.1|*.faq|*.ico) ;; *) rm -f "$_f" ;; esac; done'
# Evil hack
if test x"$TARGET_OS" = x"Android"; then
@@ -706,12 +705,12 @@ fi
# Configuration depending on OS revision, on OSes that need them
case $TARGET_OS in
NEXTSTEP)
- test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`hostinfo 2>&1 | \
+ test_n "$TARGET_OSREV" || TARGET_OSREV=`hostinfo 2>&1 | \
grep 'NeXT Mach [0-9][0-9.]*:' | \
sed 's/^.*NeXT Mach \([0-9][0-9.]*\):.*$/\1/'`
;;
QNX|SCO_SV)
- test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
+ test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
;;
esac
@@ -847,6 +846,11 @@ Linux)
LynxOS)
oswarn="; it has minor issues"
;;
+midipix)
+ add_cppflags -D_GNU_SOURCE
+ # their Perl (currently…) identifies as os:linux ☹
+ check_categories="$check_categories os:midipix"
+ ;;
MidnightBSD)
;;
Minix-vmd)
@@ -1037,11 +1041,11 @@ _svr4)
# generic target for SVR4 Unix with uname -s = uname -n
# this duplicates the * target below
oswarn='; it may or may not work'
- test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
+ test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
;;
*)
oswarn='; it may or may not work'
- test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
+ test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
;;
esac
@@ -1084,11 +1088,12 @@ SCO_SV|UnixWare|UNIX_SV)
vv '|' "uname -a >&2"
;;
esac
-test -z "$oswarn" || echo >&2 "
+test_z "$oswarn" || echo >&2 "
Warning: mksh has not yet been ported to or tested on your
operating system '$TARGET_OS'$oswarn. If you can provide
a shell account to the developer, this may improve; please
-drop us a success or failure notice or even send in diffs.
+drop us a success or failure notice or even send in diffs,
+at the very least, complete logs (Build.sh + test.sh) will help.
"
$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
@@ -1115,6 +1120,10 @@ ct="icc"
ct="xlc"
#elif defined(__SUNPRO_C)
ct="sunpro"
+#elif defined(__neatcc__)
+ct="neatcc"
+#elif defined(__lacc__)
+ct="lacc"
#elif defined(__ACK__)
ct="ack"
#elif defined(__BORLANDC__)
@@ -1230,9 +1239,8 @@ dmc)
;;
gcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
- vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
- -dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
- $LIBS -dumpversion`'
+ vv '|' 'eval echo "\`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpmachine\`" \
+ "gcc\`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpversion\`"'
: "${HAVE_STRING_POOLING=i2}"
;;
hpcc)
@@ -1250,6 +1258,9 @@ icc)
kencc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
;;
+lacc)
+ # no version information
+ ;;
lcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
add_cppflags -D__inline__=__inline
@@ -1266,21 +1277,27 @@ msc)
ccpr= # errorlevels are not reliable
case $TARGET_OS in
Interix)
- if [[ -n $C89_COMPILER ]]; then
- C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
- else
+ if test_z "$C89_COMPILER"; then
C89_COMPILER=CL.EXE
- fi
- if [[ -n $C89_LINKER ]]; then
- C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
else
+ C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
+ fi
+ if test_z "$C89_LINKER"; then
C89_LINKER=LINK.EXE
+ else
+ C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
fi
vv '|' "$C89_COMPILER /HELP >&2"
vv '|' "$C89_LINKER /LINK >&2"
;;
esac
;;
+neatcc)
+ add_cppflags -DMKSH_DONT_EMIT_IDSTRING
+ add_cppflags -DMKSH_NO_SIGSETJMP
+ add_cppflags -Dsig_atomic_t=int
+ vv '|' "$CC"
+ ;;
nwcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
;;
@@ -1333,7 +1350,7 @@ xlc)
*)
test x"$ct" = x"untested" && $e "!!! detecting preprocessor failed"
ct=unknown
- vv "$CC --version"
+ vv '|' "$CC --version"
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
;;
@@ -1376,14 +1393,14 @@ if ac_ifcpp 'if 0' compiler_fails '' \
case $ct in
dec)
CFLAGS="$CFLAGS ${ccpl}-non_shared"
- ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF
+ ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-'EOF'
#include <unistd.h>
int main(void) { return (isatty(0)); }
EOF
;;
dmc)
CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE"
- ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF
+ ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-'EOF'
#include <unistd.h>
int main(void) { return (isatty(0)); }
EOF
@@ -1393,9 +1410,8 @@ if ac_ifcpp 'if 0' compiler_fails '' \
;;
esac
test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS
- ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF
- EOF
- test 1 = $HAVE_COMPILER_STILL_FAILS && exit 1
+ ac_ifcpp 'if 0' compiler_still_fails \
+ 'if the compiler still does not fail correctly' && exit 1
fi
if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \
'if this could be tcc'; then
@@ -1478,7 +1494,7 @@ NOWARN=$save_NOWARN
#
i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln`
# optimisation: only if orig_CFLAGS is empty
-test x"$i" = x"" && case $ct in
+test_n "$i" || case $ct in
hpcc)
phase=u
ac_flags 1 otwo +O2
@@ -1526,6 +1542,7 @@ dmc)
ac_flags 1 schk "${ccpc}-s" 'for stack overflow checking'
;;
gcc)
+ ac_flags 1 fnolto -fno-lto 'whether we can explicitly disable buggy GCC LTO' -fno-lto
# The following tests run with -Werror (gcc only) if possible
NOWARN=$DOWARN; phase=u
ac_flags 1 wnodeprecateddecls -Wno-deprecated-declarations
@@ -1708,7 +1725,7 @@ ac_test attribute_format '' 'for __attribute__((__format__))' <<-'EOF'
#undef fprintf
extern int fprintf(FILE *, const char *format, ...)
__attribute__((__format__(__printf__, 2, 3)));
- int main(int ac, char **av) { return (fprintf(stderr, "%s%d", *av, ac)); }
+ int main(int ac, char *av[]) { return (fprintf(stderr, "%s%d", *av, ac)); }
#endif
EOF
ac_test attribute_noreturn '' 'for __attribute__((__noreturn__))' <<-'EOF'
@@ -1733,7 +1750,7 @@ ac_test attribute_pure '' 'for __attribute__((__pure__))' <<-'EOF'
#include <unistd.h>
#undef __attribute__
int foo(const char *) __attribute__((__pure__));
- int main(int ac, char **av) { return (foo(av[ac - 1]) + isatty(0)); }
+ int main(int ac, char *av[]) { return (foo(av[ac - 1]) + isatty(0)); }
int foo(const char *s) { return ((int)s[0]); }
#endif
EOF
@@ -1745,7 +1762,7 @@ ac_test attribute_unused '' 'for __attribute__((__unused__))' <<-'EOF'
#else
#include <unistd.h>
#undef __attribute__
- int main(int ac __attribute__((__unused__)), char **av
+ int main(int ac __attribute__((__unused__)), char *av[]
__attribute__((__unused__))) { return (isatty(0)); }
#endif
EOF
@@ -1863,22 +1880,22 @@ rmf lft* # end of large file support test
ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
- int main(int ac, char **av) { return ((uint32_t)(size_t)*av + (int32_t)ac); }
+ int main(int ac, char *av[]) { return ((uint32_t)(size_t)*av + (int32_t)ac); }
EOF
ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
- int main(int ac, char **av) { return ((u_int32_t)(size_t)*av + (int32_t)ac); }
+ int main(int ac, char *av[]) { return ((u_int32_t)(size_t)*av + (int32_t)ac); }
EOF
ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
- int main(int ac, char **av) { return ((uint8_t)(size_t)av[ac]); }
+ int main(int ac, char *av[]) { return ((uint8_t)(size_t)av[ac]); }
EOF
ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
- int main(int ac, char **av) { return ((u_int8_t)(size_t)av[ac]); }
+ int main(int ac, char *av[]) { return ((u_int8_t)(size_t)av[ac]); }
EOF
ac_test rlim_t <<-'EOF'
@@ -1948,7 +1965,11 @@ else
#define MKSH_INCLUDES_ONLY
#include "sh.h"
__RCSID("$srcversion");
- int main(void) { printf("Hello, World!\\n"); return (isatty(0)); }
+ int main(void) {
+ struct timeval tv;
+ printf("Hello, World!\\n");
+ return (time(&tv.tv_sec));
+ }
EOF
case $cm in
llvm)
@@ -2005,15 +2026,15 @@ ac_cppflags SYS_ERRLIST
for what in name list; do
uwhat=`upper $what`
- ac_testn sys_sig$what '' "the sys_sig${what}[] array" <<-EOF
- extern const char * const sys_sig${what}[];
+ ac_testn sys_sig$what '' "the sys_sig$what[] array" <<-EOF
+ extern const char * const sys_sig$what[];
extern int isatty(int);
- int main(void) { return (sys_sig${what}[0][0] + isatty(0)); }
+ int main(void) { return (sys_sig$what[0][0] + isatty(0)); }
EOF
- ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig${what}[] array" <<-EOF
- extern const char * const _sys_sig${what}[];
+ ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig$what[] array" <<-EOF
+ extern const char * const _sys_sig$what[];
extern int isatty(int);
- int main(void) { return (_sys_sig${what}[0][0] + isatty(0)); }
+ int main(void) { return (_sys_sig$what[0][0] + isatty(0)); }
EOF
eval uwhat_v=\$HAVE__SYS_SIG$uwhat
if test 1 = "$uwhat_v"; then
@@ -2265,6 +2286,17 @@ ac_test sys_siglist_decl sys_siglist 0 'for declaration of sys_siglist[]' <<-'EO
int main(void) { return (sys_siglist[0][0] + isatty(0)); }
EOF
+ac_test st_mtim '' 'for struct stat.st_mtim.tv_nsec' <<-'EOF'
+ #define MKSH_INCLUDES_ONLY
+ #include "sh.h"
+ int main(void) { struct stat sb; return (sizeof(sb.st_mtim.tv_nsec)); }
+EOF
+ac_test st_mtimensec '!' st_mtim 0 'for struct stat.st_mtimensec' <<-'EOF'
+ #define MKSH_INCLUDES_ONLY
+ #include "sh.h"
+ int main(void) { struct stat sb; return (sizeof(sb.st_mtimensec)); }
+EOF
+
#
# other checks
#
@@ -2287,7 +2319,7 @@ if test $legacy = 1; then
#define CHAR_BIT 0
#endif
struct ctasserts {
- #define cta(name, assertion) char name[(assertion) ? 1 : -1]
+ #define cta(name,assertion) char name[(assertion) ? 1 : -1]
cta(char_is_8_bits, (CHAR_BIT) == 8);
cta(long_is_32_bits, sizeof(long) == 4);
};
@@ -2301,7 +2333,7 @@ EOF
#define CHAR_BIT 0
#endif
struct ctasserts {
- #define cta(name, assertion) char name[(assertion) ? 1 : -1]
+ #define cta(name,assertion) char name[(assertion) ? 1 : -1]
cta(char_is_8_bits, (CHAR_BIT) == 8);
cta(long_is_64_bits, sizeof(long) == 8);
};
@@ -2429,7 +2461,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=571
+add_cppflags -DMKSH_BUILD_R=591
$e $bi$me: Finished configuration testing, now producing output.$ao
@@ -2452,7 +2484,10 @@ esac
cat >test.sh <<-EOF
$mkshshebang
LC_ALL=C PATH='$PATH'; export LC_ALL PATH
- test -n "\$KSH_VERSION" || exit 1
+ case \$KSH_VERSION in
+ *MIRBSD*|*LEGACY*) ;;
+ *) exit 1 ;;
+ esac
set -A check_categories -- $check_categories
pflag='$curdir/$mkshexe'
sflag='$srcdir/check.t'
@@ -2614,8 +2649,8 @@ INDSRCS= $extras
NONSRCS_INST= dot.mkshrc \$(MAN)
NONSRCS_NOINST= Build.sh Makefile Rebuild.sh check.pl check.t test.sh
CC= $CC
-CFLAGS= $CFLAGS
CPPFLAGS= $CPPFLAGS
+CFLAGS= $CFLAGS
LDFLAGS= $LDFLAGS
LIBS= $LIBS
@@ -2685,6 +2720,7 @@ test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
test -f $tcfn || exit 1
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 1 = $r || v "(set -- ''; . '$srcdir/FAQ2HTML.sh')" || rmf FAQ.htm
test 0 = $eq && v $SIZE $tcfn
i=install
test -f /usr/ucb/$i && i=/usr/ucb/$i
@@ -2698,7 +2734,13 @@ if test $legacy = 0; then
fi
$e
$e Installing the manual:
+if test -e FAQ.htm; then
+ $e "# $i -c -o root -g bin -m 444 FAQ.htm /usr/share/doc/mksh/"
+fi
if test -f mksh.cat1; then
+ if test -e FAQ.htm; then
+ $e plus either
+ fi
$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" \
@@ -2709,6 +2751,8 @@ $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.
+test -e FAQ.htm || \
+ $e Run FAQ2HTML.sh and place FAQ.htm into a suitable location as well.
exit 0
: <<'EOD'
@@ -2740,6 +2784,7 @@ HAVE_CAN_FSTACKPROTECTORALL ac_flags
==== cpp definitions ====
DEBUG dont use in production, wants gcc, implies:
DEBUG_LEAKS enable freeing resources before exiting
+KSH_VERSIONNAME_VENDOR_EXT when patching; space+plus+word (e.g. " +SuSE")
MKSHRC_PATH "~/.mkshrc" (do not change)
MKSH_A4PB force use of arc4random_pushb
MKSH_ASSUME_UTF8 (0=disabled, 1=enabled; default: unset)
@@ -2768,6 +2813,7 @@ MKSH_S_NOVI=1 disable Vi editing mode (default if MKSH_SMALL)
MKSH_TYPEDEF_SIG_ATOMIC_T define to e.g. 'int' if sig_atomic_t is missing
MKSH_TYPEDEF_SSIZE_T define to e.g. 'long' if your OS has no ssize_t
MKSH_UNEMPLOYED disable job control (but not jobs/co-processes)
+USE_REALLOC_MALLOC define as 0 to not use realloc as malloc
=== generic installation instructions ===
@@ -2777,15 +2823,15 @@ them, set to a value other than 0 or 1. Ensure /bin/ed is installed. For
MKSH_SMALL but with Vi mode, add -DMKSH_S_NOVI=0 to CPPFLAGS as well.
Normally, the following command is what you want to run, then:
-$ (sh Build.sh -r -c lto && ./test.sh -f) 2>&1 | tee log
+$ (sh Build.sh -r && ./test.sh -f) 2>&1 | tee log
Copy dot.mkshrc to /etc/skel/.mkshrc; install mksh into $prefix/bin; or
/bin; install the manpage, if omitting the -r flag a catmanpage is made
using $NROFF. Consider using a forward script as /etc/skel/.mkshrc like
-http://anonscm.debian.org/cgit/collab-maint/mksh.git/plain/debian/.mkshrc
+https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=alioth/mksh.git;a=blob;f=debian/.mkshrc
and put dot.mkshrc as /etc/mkshrc so users need not keep up their HOME.
You may also want to install the lksh binary (also as /bin/sh) built by:
-$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r -c lto
+$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r
EOD
diff --git a/src/FAQ2HTML.sh b/src/FAQ2HTML.sh
new file mode 100644
index 0000000..180ed98
--- /dev/null
+++ b/src/FAQ2HTML.sh
@@ -0,0 +1,136 @@
+#!/bin/mksh
+rcsid='$MirOS: src/bin/mksh/FAQ2HTML.sh,v 1.1 2020/02/03 22:23:33 tg Exp $'
+#-
+# Copyright © 2020
+# mirabilos <m@mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un‐
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person’s immediate fault when using the work as intended.
+#-
+
+set -e
+LC_ALL=C; LANGUAGE=C
+export LC_ALL; unset LANGUAGE
+nl='
+'
+srcdir=$(dirname "$0")
+
+p=--posix
+sed $p -e q </dev/null >/dev/null 2>&1 || p=
+
+v=$1
+if test -z "$v"; then
+ v=$(sed $p -n '/^#define MKSH_VERSION "\(.*\)"$/s//\1/p' "$srcdir"/sh.h)
+fi
+src_id=$(sed $p -n '/^RCSID: /s///p' "$srcdir"/mksh.faq)
+# sanity check
+case $src_id in
+(*"$nl"*)
+ echo >&2 "E: more than one RCSID in mksh.faq?"
+ exit 1 ;;
+esac
+
+sed $p \
+ -e '/^RCSID: \$/s/^.*$/----/' \
+ -e 's!@@RELPATH@@!http://www.mirbsd.org/!g' \
+ -e 's^ <span style="display:none;"> </span>' \
+ "$srcdir"/mksh.faq | tr '\n' '' | sed $p \
+ -e 'sg' \
+ -e 's----g' \
+ -e 's\([^]*\)\1g' \
+ -e 's\([^]*\)\1g' \
+ -e 's\([^]*\)*ToC: \([^]*\)Title: \([^]*\)\([^]*\)\{0,1\}</div><h2 id="\2"><a href="#\2">\3</a></h2><div>g' \
+ -e 's[^]*</div><div>g' \
+ -e 's^</div>*' \
+ -e 's$</div>' \
+ -e 's<><error><>g' \
+ -e 'sg' | tr '' '\n' >FAQ.tmp
+
+exec >FAQ.htm~
+cat <<EOF
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>mksh $v FAQ (local copy)</title>
+ <meta name="source" content="$src_id" />
+ <meta name="generator" content="$rcsid" />
+ <style type="text/css"><!--/*--><![CDATA[/*><!--*/
+ .boxhead {
+ margin-bottom:0px;
+ }
+
+ .boxtext {
+ border:4px ridge green;
+ margin:0px 24px 0px 18px;
+ padding:2px 3px 2px 3px;
+ }
+
+ .boxfoot {
+ margin-top:0px;
+ }
+
+ h2:before {
+ content:"🔗 ";
+ }
+
+ a[href^="ftp://"]:after,
+ a[href^="http://"]:after,
+ a[href^="https://"]:after,
+ a[href^="irc://"]:after,
+ a[href^="mailto:"]:after,
+ a[href^="news:"]:after,
+ a[href^="nntp://"]:after {
+ content:"⏍";
+ color:#FF0000;
+ vertical-align:super;
+ margin:0 0 0 1px;
+ }
+
+ pre {
+ /* ↑ → ↓ ← */
+ margin:0px 9px 0px 15px;
+ }
+
+ tt {
+ white-space:nowrap;
+ }
+ /*]]>*/--></style>
+</head><body>
+<p>Note: Links marked like <a href="irc://chat.freenode.net/!/bin/mksh">this
+ one to the mksh IRC channel</a> connect to external resources.</p>
+<p>⚠ <b>Notice:</b> the website will have <a
+ href="http://www.mirbsd.org/mksh-faq.htm">the latest version of the
+ mksh FAQ</a> online.</p>
+<h1>Table of Contents</h1>
+<ul>
+EOF
+sed $p -n \
+ '/^<h2 id="\([^"]*"\)><a[^>]*\(>.*<\/a><\/\)h2>$/s//<li><a href="#\1\2li>/p' \
+ <FAQ.tmp
+cat <<EOF
+</ul>
+
+<h1>Frequently Asked Questions</h1>
+EOF
+cat FAQ.tmp - <<EOF
+<h1>Imprint</h1>
+<p>This offline HTML page for mksh $v was automatically generated
+ from the sources.</p>
+</body></html>
+EOF
+exec >/dev/null
+rm FAQ.tmp
+mv FAQ.htm~ FAQ.htm
diff --git a/src/check.pl b/src/check.pl
index e9c2437..c08e287 100644
--- a/src/check.pl
+++ b/src/check.pl
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.pl,v 1.49 2017/05/05 21:17:31 tg Exp $
+# $MirOS: src/bin/mksh/check.pl,v 1.50 2019/08/01 20:05:55 tg Exp $
# $OpenBSD: th,v 1.1 2013/12/02 20:39:44 millert Exp $
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
@@ -292,7 +292,7 @@ $all_tests = @ARGV == 0;
# Set up a very minimal environment
%new_env = ();
foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
- 'PATH', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
+ 'PATH', 'PERLIO', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
$new_env{$env} = $ENV{$env} if defined $ENV{$env};
}
$new_env{'CYGWIN'} = 'nodosfilewarning';
diff --git a/src/check.t b/src/check.t
index d5df4cb..a750c50 100644
--- a/src/check.t
+++ b/src/check.t
@@ -1,9 +1,9 @@
-# $MirOS: src/bin/mksh/check.t,v 1.812 2019/03/01 16:17:29 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.838 2020/04/13 19:51:08 tg Exp $
# -*- mode: sh -*-
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-# 2019
+# 2019, 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@@ -31,20 +31,29 @@
# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout:
- @(#)MIRBSD KSH R57 2019/03/01
+ KSH R59 2020/04/14
description:
Check base version of full shell
stdin:
- echo ${KSH_VERSION%%' +'*}
+ vsn=${KSH_VERSION%%' +'*}
+ echo "${vsn#* }"
name: KSH_VERSION
+---
+expected-stdout:
+ @(#)MIRBSD
+description:
+ Check this identifies as legacy shell
+stdin:
+ echo "${KSH_VERSION%% *}"
+name: KSH_VERSION-modern
category: !shell:legacy-yes
---
expected-stdout:
- @(#)LEGACY KSH R57 2019/03/01
+ @(#)LEGACY
description:
- Check base version of legacy shell
+ Check this identifies as legacy shell
stdin:
- echo ${KSH_VERSION%%' +'*}
+ echo "${KSH_VERSION%% *}"
name: KSH_VERSION-legacy
category: !shell:legacy-no
---
@@ -53,6 +62,7 @@ description:
Check that the shell version tag does not include EBCDIC
category: !shell:ebcdic-yes
stdin:
+ set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+EBCDIC' ]] && exit 1
done
@@ -63,6 +73,7 @@ description:
Check that the shell version tag includes EBCDIC
category: !shell:ebcdic-no
stdin:
+ set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+EBCDIC' ]] && exit 0
done
@@ -73,6 +84,7 @@ description:
Check that the shell version tag does not include TEXTMODE
category: !shell:textmode-yes
stdin:
+ set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+TEXTMODE' ]] && exit 1
done
@@ -83,6 +95,7 @@ description:
Check that the shell version tag includes TEXTMODE
category: !shell:textmode-no
stdin:
+ set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+TEXTMODE' ]] && exit 0
done
@@ -178,7 +191,7 @@ stdin:
expected-stdout:
ok
expected-stderr-pattern:
- /mksh: warning: won't have full job control\nXX/
+ /ksh: warning: won't have full job control\nXX/
---
name: selftest-tty-present
description:
@@ -1392,7 +1405,7 @@ need-pass: no
# the mv command fails on Cygwin and z/OS
# Hurd aborts the testsuite (permission denied)
# QNX does not find subdir to cd into
-category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!os:os390,!nosymlink
+category: !os:cygwin,!os:gnu,!os:midipix,!os:msys,!os:nto,!os:os390,!nosymlink
file-setup: file 644 "x"
mkdir noread noread/target noread/target/subdir
ln -s noread link
@@ -1999,7 +2012,7 @@ expected-stdout:
name: eglob-bad-1
description:
Check that globbing isn't done when glob has syntax error
-category: !os:cygwin,!os:msys,!os:os2
+category: !os:cygwin,!os:midipix,!os:msys,!os:os2
file-setup: file 644 "@(a[b|)c]foo"
stdin:
echo @(a[b|)c]*
@@ -2491,7 +2504,7 @@ description:
# breaks on Mac OSX (HFS+ non-standard UTF-8 canonical decomposition)
# breaks on Cygwin 1.7 (files are now UTF-16 or something)
# breaks on QNX 6.4.1 (says RT)
-category: !os:cygwin,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
+category: !os:cygwin,!os:midipix,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
need-pass: no
file-setup: file 644 "ac"
stdin:
@@ -2526,6 +2539,19 @@ expected-stdout:
-bc abc bbc cbc ebc
@bc
---
+name: glob-range-6
+description:
+ ksh93 fails this but POSIX probably demands it
+file-setup: file 644 "abc"
+file-setup: file 644 "cbc"
+stdin:
+ echo *b*
+ [ '*b*' = *b* ] && echo yep; echo $?
+expected-stdout:
+ abc cbc
+ 2
+expected-stderr-pattern: /.*/
+---
name: glob-word-1
description:
Check BSD word boundary matches
@@ -2721,7 +2747,7 @@ expected-stdout:
h\b
done
---
-name: heredoc-9a
+name: heredoc-9
description:
Check that here strings work.
stdin:
@@ -2736,6 +2762,19 @@ stdin:
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"A $(echo "foo bar") B"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$b\$b$bar
+ fnord=42
+ bar="bar
+ \$fnord baz"
+ tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
+ tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
+ echo $(tr r z <<<'bar' 2>/dev/null)
+ cat <<< "$( : )aa"
+ IFS=$'\n'
+ x=(a "b c")
+ tr ac 12 <<< ${x[*]}
+ tr ac 34 <<< "${x[*]}"
+ tr ac 56 <<< ${x[@]}
+ tr ac 78 <<< "${x[@]}"
expected-stdout:
sbb
sbb
@@ -2748,54 +2787,17 @@ expected-stdout:
A sbb one B
$o$oone
onm
----
-name: heredoc-9b
-description:
- Check that a corner case of here strings works like bash
-stdin:
- fnord=42
- bar="bar
- \$fnord baz"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
-expected-stdout:
- one $sabeq onm
-category: bash
----
-name: heredoc-9c
-description:
- Check that a corner case of here strings works like ksh93, zsh
-stdin:
- fnord=42
- bar="bar
- \$fnord baz"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
-expected-stdout:
one
$sabeq onm
----
-name: heredoc-9d
-description:
- Check another corner case of here strings
-stdin:
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
-expected-stdout:
one
----
-name: heredoc-9e
-description:
- Check here string related regression with multiple iops
-stdin:
- echo $(tr r z <<<'bar' 2>/dev/null)
-expected-stdout:
baz
----
-name: heredoc-9f
-description:
- Check long here strings
-stdin:
- cat <<< "$( : )aa"
-expected-stdout:
aa
+ 1
+ b 2
+ 3
+ b 4
+ 5 b 6
+ 7 b 8
---
name: heredoc-10
description:
@@ -4981,6 +4983,17 @@ expected-stdout:
var=onetwo threefour
<onetwo threefour> .
---
+name: IFS-subst-11
+description:
+ Check leading non-whitespace after trim makes only one field
+stdin:
+ showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
+ v="foo!one!two!three"
+ IFS="!"
+ showargs x ${v:3} y
+expected-stdout:
+ <x> <> <one> <two> <three> <y> .
+---
name: IFS-arith-1
description:
http://austingroupbugs.net/view.php?id=832
@@ -6653,12 +6666,35 @@ name: regression-62
description:
Check if test -nt/-ot succeeds if second(first) file is missing.
stdin:
+ matrix() {
+ local a b c d e f g h
+ test a -nt b; a=$?
+ test b -nt a; b=$?
+ test a -ot b; c=$?
+ test b -ot a; d=$?
+ test a -nt a; e=$?
+ test b -nt b; f=$?
+ test a -ot a; g=$?
+ test b -ot b; h=$?
+ echo $1 $a $b $c $d / $e $f $g $h .
+ }
+ matrix a
:>a
- test a -nt b && echo nt OK || echo nt BAD
- test b -ot a && echo ot OK || echo ot BAD
-expected-stdout:
- nt OK
- ot OK
+ matrix b
+ sleep 2 # mtime granularity for OS/2 and FAT
+ :>b
+ matrix c
+ sleep 2
+ echo dummy >a # Debian GNU/Hurd #955270
+ matrix d
+ rm a
+ matrix e
+expected-stdout:
+ a 1 1 1 1 / 1 1 1 1 .
+ b 0 1 1 0 / 1 1 1 1 .
+ c 1 0 0 1 / 1 1 1 1 .
+ d 0 1 1 0 / 1 1 1 1 .
+ e 1 0 0 1 / 1 1 1 1 .
---
name: regression-63
description:
@@ -7118,13 +7154,13 @@ stdin:
name: exec-function-environment-1
description:
Check assignments in function calls and whether they affect
- the current execution environment (ksh93, SUSv4)
+ the current execution environment
stdin:
f() { a=2; }; g() { b=3; echo y$c-; }; a=1 f; b=2; c=1 g
echo x$a-$b- z$c-
expected-stdout:
y1-
- x2-3- z1-
+ x-3- z-
---
name: exec-modern-korn-shell
description:
@@ -7389,6 +7425,8 @@ expected-stdout:
name: xxx-stat-1
description:
Check that tests on files are consistent
+ (fails when run as root, unfortunately)
+category: disabled
stdin:
mkdir a
echo x >a/b
@@ -7580,7 +7618,7 @@ stdin:
showargs 18 "$a"
set -A bla
typeset bla[1]=~:~
- global gbl=~ g2=$1
+ typeset -g gbl=~ g2=$1
local lcl=~ l2=$1
readonly ro=~ r2=$1
showargs 19 "${bla[1]}" a=~ "$gbl" "$lcl" "$ro" "$g2" "$l2" "$r2"
@@ -7761,6 +7799,32 @@ expected-stdout:
1 ok
expected-exit: 1
---
+name: exit-err-10
+description:
+ Debian #269067 (cf. regression-38 but with eval)
+arguments: !-e!
+stdin:
+ eval false || true
+ echo = $? .
+expected-stdout:
+ = 0 .
+---
+name: exit-err-11
+description:
+ Fix -e inside eval, from Martijn Dekker; expected-stdout from ksh93
+stdin:
+ "$__progname" -c 'eval '\''echo ${-//[!eh]}; false; echo phantom e'\''; echo x$?'
+ echo = $?
+ "$__progname" -ec 'eval '\''echo ${-//[!eh]}; false; echo phantom e'\''; echo x$?'
+ echo = $?
+expected-stdout:
+ h
+ phantom e
+ x0
+ = 0
+ eh
+ = 1
+---
name: exit-enoent-1
description:
SUSv4 says that the shell should exit with 126/127 in some situations
@@ -7832,11 +7896,9 @@ expected-exit: 9
---
name: exit-trap-2
description:
- Check that ERR and EXIT traps are run just like ksh93 does.
- GNU bash does not run ERtrap in ±e eval-undef but runs it
- twice (bug?) in +e eval-false, so does ksh93 (bug?), which
- also has a bug to continue execution (echoing "and out" and
- returning 0) in +e eval-undef.
+ Check that ERR and EXIT traps are run just like GNU bash does.
+ ksh93 runs ERtrap after “parameter null or not set” (which mksh
+ used to do) but (bug) continues “and out”, exit 0, in +e eval-undef.
file-setup: file 644 "x"
v=; unset v
trap 'echo EXtrap' EXIT
@@ -7920,7 +7982,6 @@ expected-stdout:
= eval-false 1 .
and run ${v?}
x: v: parameter null or not set
- ERtrap
EXtrap
= eval-undef 1 .
and run true
@@ -7942,12 +8003,12 @@ expected-stdout:
= eval-true 0 .
and run false
ERtrap
+ ERtrap
and out
EXtrap
= eval-false 0 .
and run ${v?}
x: v: parameter null or not set
- ERtrap
EXtrap
= eval-undef 1 .
and run true
@@ -8017,6 +8078,50 @@ expected-stdout:
nein
expected-stderr-pattern: !/unexpected op/
---
+name: test-str-pattern
+description:
+ Check that [[ x = $y ]] can take extglobs, like ksh93
+stdin:
+ x='a\'
+ [[ $x = a\ ]]; echo 1 $? .
+ [[ $x = a\\ ]]; echo 2 $? .
+ y='a\'
+ [[ $x = $y ]]; echo 3 $? .
+ [[ $x = "$y" ]]; echo 4 $? .
+ x='a\b'
+ y='a\b'
+ [[ $x = $y ]]; echo 5 $? .
+ [[ $x = "$y" ]]; echo 6 $? .
+ y='a\\b'
+ [[ $x = $y ]]; echo 7 $? .
+ [[ $x = "$y" ]]; echo 8 $? .
+ x='foo'
+ y='f+(o)'
+ [[ $x = $y ]]; echo 9 $? .
+ [[ $x = "$y" ]]; echo 10 $? .
+ x=$y
+ [[ $x = $y ]]; echo 11 $? .
+ [[ $x = "$y" ]]; echo 12 $? .
+ x='f+(o'
+ y=$x
+ [[ $x = $y ]]; echo 13 $? .
+ [[ $x = "$y" ]]; echo 14 $? .
+expected-stdout:
+ 1 1 .
+ 2 0 .
+ 3 0 .
+ 4 0 .
+ 5 1 .
+ 6 0 .
+ 7 0 .
+ 8 1 .
+ 9 0 .
+ 10 1 .
+ 11 1 .
+ 12 0 .
+ 13 0 .
+ 14 0 .
+---
name: test-precedence-1
description:
Check a weird precedence case (and POSIX echo)
@@ -8672,7 +8777,7 @@ description:
note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
XXX fails when LD_PRELOAD is set with -e and Perl chokes it (ASan)
need-pass: no
-category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
+category: !os:cygwin,!os:midipix,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
env-setup: !FOO=BAR!
stdin:
print '#!'"$__progname"'\nprint "1 a=$ENV{FOO}";' >t1
@@ -9621,8 +9726,7 @@ expected-stdout:
---
name: varexpand-substr-3
description:
- Check that some things that work in bash fail.
- This is by design. Oh and vice versa, nowadays.
+ Match bash5
stdin:
export x=abcdefghi n=2
"$__progname" -c 'echo v${x:(n)}x'
@@ -9630,15 +9734,15 @@ stdin:
"$__progname" -c 'echo x${x:n}x'
"$__progname" -c 'echo y${x:}x'
"$__progname" -c 'echo z${x}x'
- # next fails only in bash
- "$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
+ "$__progname" -c 'x=abcdef;y=123;echo q${x:${y:2:1}:2}q'
expected-stdout:
vcdefghix
wcdefghix
+ xcdefghix
zabcdefghix
- 0
+ qdeq
expected-stderr-pattern:
- /x:n.*bad substitution.*\n.*bad substitution/
+ /x:}.*bad substitution/
---
name: varexpand-substr-4
description:
@@ -10841,6 +10945,43 @@ stdin:
expected-stdout:
okay
---
+name: ulimit-3
+description:
+ Check that there are no duplicate limits (if this fails,
+ immediately contact with system information the developers)
+stdin:
+ [[ -z $(set | grep ^opt) ]]; mis=$?
+ set | grep ^opt | sed 's/^/unexpectedly set in environment: /'
+ opta='<used for showing all limits>'
+ optH='<used to set hard limits>'
+ optS='<used to set soft limits>'
+ ulimit -a >tmpf
+ set -o noglob
+ while IFS= read -r line; do
+ x=${line:1:1}
+ if [[ -z $x || ${#x}/${%x} != 1/1 ]]; then
+ print -r -- "weird line: $line"
+ (( mis |= 1 ))
+ continue
+ fi
+ set -- $line
+ nameref v=opt$x
+ if [[ -n $v ]]; then
+ print -r -- "duplicate -$x \"$2\" already seen as \"$v\""
+ (( mis |= 2 ))
+ fi
+ v=$2
+ done <tmpf
+ if (( mis & 2 )); then
+ echo failed
+ elif (( mis & 1 )); then
+ echo inconclusive
+ else
+ echo done
+ fi
+expected-stdout:
+ done
+---
name: redir-1
description:
Check some of the most basic invariants of I/O redirection
@@ -11201,7 +11342,7 @@ description:
(Inspired by PR 2450 on OpenBSD.) Calling
FOO=bar f
where f is a ksh style function, should not set FOO in the current
- env. If f is a Bourne style function, FOO should be set. Furthermore,
+ env. If f is a Bourne style function, (new) also not. Furthermore,
the function should receive a correct value of FOO. However, differing
from oksh, setting FOO in the function itself must change the value in
setting FOO in the function itself should not change the value in
@@ -11247,7 +11388,7 @@ stdin:
if [ $? != 0 ]; then
exit 1
fi
- if [ x$FOO != xfoo ]; then
+ if [ x$FOO != x ]; then
exit 1
fi
FOO=barbar
@@ -11262,7 +11403,7 @@ stdin:
if [ $? != 0 ]; then
exit 1
fi
- if [ x$FOO != xfoo ]; then
+ if [ x$FOO != xbarbar ]; then
exit 1
fi
---
@@ -12659,7 +12800,7 @@ stdin:
echo =14
(mypid=$$; try mypid)
echo =15
- ) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
+ ) 2>&1 | sed -e 's/^[A-Za-z]://' -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
exit ${PIPESTATUS[0]}
expected-stdout:
y
@@ -13238,7 +13379,7 @@ description:
Crashed during March 2011, fixed on vernal equinōx ☺
category: os:mirbsd,os:openbsd
stdin:
- export MALLOC_OPTIONS=FGJPRSX
+ export MALLOC_OPTIONS=FGJRSX
"$__progname" -c 'x=$(tr z r <<<baz); echo $x'
expected-stdout:
bar
@@ -13362,6 +13503,59 @@ expected-stdout:
after 0='swc' 1='二' 2=''
= done
---
+name: command-set
+description:
+ Same but with set
+stdin:
+ showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
+ showargs 1 "$@"
+ set -- foo bar baz
+ showargs 2 "$@"
+ command set -- miau 'meow nyao'
+ showargs 3 "$@"
+expected-stdout:
+ <1> .
+ <2> <foo> <bar> <baz> .
+ <3> <miau> <meow nyao> .
+---
+name: command-readonly
+description:
+ These should not exit on error when prefixed
+stdin:
+ exec 2>/dev/null
+ "$__progname" -c 'readonly v; export v=foo || echo ok'
+ echo ef=$?
+ "$__progname" -c 'readonly v; command export v=foo || echo ok'
+ echo en=$?
+ "$__progname" -c 'readonly v; readonly v=foo || echo ok'
+ echo rf=$?
+ "$__progname" -c 'readonly v; command readonly v=foo || echo ok'
+ echo rn=$?
+expected-stdout:
+ ef=2
+ ok
+ en=0
+ rf=2
+ ok
+ rn=0
+---
+name: command-dot-regression
+description:
+ Check a regression in fixing the above does not appear
+stdin:
+ cat >test.mksh <<\EOF
+ set -- one two
+ shift
+ for s_arg in "$#" "$@"; do echo -n "<$s_arg> "; done; echo .
+ EOF
+ "$__progname" -c '. ./test.mksh' dummy oh dear this is not good
+ echo =
+ "$__progname" -c 'command . ./test.mksh' dummy oh dear this is not good
+expected-stdout:
+ <1> <two> .
+ =
+ <1> <two> .
+---
name: command-pvV-posix-priorities
description:
For POSIX compatibility, command -v should find aliases and reserved
@@ -13370,13 +13564,17 @@ description:
stdin:
PATH=/bin:/usr/bin
alias foo="bar baz"
+ alias '[ab]=:'
bar() { :; }
- for word in 'if' 'foo' 'bar' 'set' 'true'; do
+ for word in 'if' 'foo' 'bar' 'set' 'true' '[ab]'; do
command -v "$word"
command -pv "$word"
command -V "$word"
command -pV "$word"
done
+ # extra checks
+ alias '[ab]'
+ whence '[ab]'
expected-stdout:
if
if
@@ -13398,6 +13596,12 @@ expected-stdout:
true
true is a shell builtin
true is a shell builtin
+ alias '[ab]'=:
+ alias '[ab]'=:
+ '[ab]' is an alias for :
+ '[ab]' is an alias for :
+ '[ab]'=:
+ :
---
name: whence-preserve-tradition
description:
diff --git a/src/dot.mkshrc b/src/dot.mkshrc
index 4a3dfea..f06dbc6 100644
--- a/src/dot.mkshrc
+++ b/src/dot.mkshrc
@@ -1,8 +1,9 @@
# $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.121 2017/08/08 21:10:21 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.128 2020/04/13 18:39:03 tg Exp $
#-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015, 2016, 2017
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
+# 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@@ -64,11 +65,10 @@ for EDITOR in "${EDITOR:-}" jupp jstar mcedit ed vi; do
done
\\builtin alias ls=ls l='ls -F' la='l -a' ll='l -l' lo='l -alo'
-\: "${HOSTNAME:=$(\\builtin ulimit -c 0; \\builtin print -r -- $(hostname \
- 2>/dev/null))}${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit \
- -c 0; id -un 2>/dev/null)}${USER:=?}"
+\: "${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit -c 0; id -un \
+ 2>/dev/null)}${HOSTNAME:=$(\\builtin ulimit -c 0; hostname 2>/dev/null)}"
[[ $HOSTNAME = ?(?(ip6-)localhost?(6)) ]] && HOSTNAME=nil; \\builtin unalias ls
-\\builtin export EDITOR HOSTNAME TERM USER
+\\builtin export EDITOR HOSTNAME TERM USER="${USER:-?}"
# minimal support for lksh users
if [[ $KSH_VERSION = *LEGACY\ KSH* ]]; then
@@ -109,7 +109,7 @@ elif \\builtin command -v hexdump >/dev/null; then
}
else
function hd {
- \\builtin cat "$@" | hd_mksh "$@"
+ \\builtin cat "$@" | hd_mksh
}
fi
@@ -150,6 +150,48 @@ function hd_mksh {
(( hv == 2147483647 )) || \\builtin print -r -- "$dasc|"
}
+function which {
+ \\builtin typeset p x c
+ \\builtin typeset -i a=0 rv=2 e
+ \\builtin set +e
+ \\builtin set -o noglob
+
+ while \\builtin getopts "a" x; do
+ case $x {
+ (a) a=1 ;;
+ (+a) a=0 ;;
+ (*) \\builtin print -ru2 'Usage: which [-a] name [...]'
+ \\builtin return 255 ;;
+ }
+ done
+ \\builtin shift $((OPTIND - 1))
+
+ # vvvvvvvvvvvvvvvvvvvv should be def_path
+ p=${PATH-/usr/bin$PATHSEP/bin}
+ # ^ no colon!
+
+ # trailing PATHSEP vs field splitting
+ [[ $p = *"$PATHSEP" ]] && p+=.
+
+ IFS=$PATHSEP
+ \\builtin set -A p -- ${p:-.}
+ IFS=$' \t\n'
+
+ for x in "$@"; do
+ if (( !a )) || [[ $x = */* ]]; then
+ \\builtin whence -p -- "$x"
+ e=$?
+ else
+ e=1
+ for c in "${p[@]}"; do
+ PATH=${c:-.} \\builtin whence -p -- "$x" && e=0
+ done
+ fi
+ (( rv = (e == 0) ? (rv & ~2) : (rv == 2 ? 2 : 1) ))
+ done
+ \\builtin return $rv
+}
+
# Berkeley C shell compatible dirs, popd, and pushd functions
# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
DIRSTACKBASE=$(\\builtin realpath ~/. 2>/dev/null || \
@@ -450,7 +492,6 @@ function enable {
i_func[nfunc++]=false
i_func[nfunc++]=fc
i_func[nfunc++]=getopts
- i_func[nfunc++]=global
i_func[nfunc++]=jobs
i_func[nfunc++]=kill
i_func[nfunc++]=let
@@ -499,6 +540,7 @@ function enable {
i_func[nfunc++]=smores
i_func[nfunc++]=hd
i_func[nfunc++]=hd_mksh
+ i_func[nfunc++]=which
i_func[nfunc++]=chpwd
i_func[nfunc++]=cd
i_func[nfunc++]=cd_csh
@@ -604,7 +646,7 @@ function enable {
\: place customisations below this line
-# some defaults follow — you are supposed to adjust these to your
+# some defaults / samples which you are supposed to adjust to your
# liking; by default we add ~/.etc/bin and ~/bin (whichever exist)
# to $PATH, set $SHELL to mksh, set some defaults for man and less
# and show a few more possible things for users to begin moving in
@@ -618,11 +660,15 @@ done
\\builtin export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
\\builtin alias cls='\\builtin print -n \\ec'
-#\\builtin unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
-# LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
+#\\builtin unset LC_ADDRESS LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+# LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+# LC_TELEPHONE LC_TIME LANGUAGE LANG LC_ALL
#p=en_GB.UTF-8
#\\builtin export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
+#\\builtin export LANG=C.UTF-8 LC_CTYPE=C.UTF-8
+#\\builtin export LC_ALL=C.UTF-8
#\\builtin set -U
+#[[ ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} = *[Uu][Tt][Ff]?(-)8* ]] || \\builtin set +U
\\builtin unset p
diff --git a/src/edit.c b/src/edit.c
index c231af1..f38092e 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -5,7 +5,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+ * 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +29,7 @@
#ifndef MKSH_NO_CMDLINE_EDITING
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.343 2018/07/15 16:16:38 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.350 2020/04/13 20:46:37 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
@@ -176,7 +177,7 @@ x_getc(void)
static void
x_putcf(int c)
{
- shf_putc(c, shl_out);
+ shf_putc_i(c, shl_out);
}
/*********************************
@@ -353,7 +354,7 @@ x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
*--cp = '/';
} else {
/* ok, expand and replace */
- cp = shf_smprintf(Tf_sSs, dp, cp);
+ strpathx(cp, dp, cp, 1);
if (magic_flag)
afree(s, ATEMP);
s = cp;
@@ -995,10 +996,7 @@ static int x_search_dir(int);
static int x_match(char *, char *);
static void x_redraw(int);
static void x_push(size_t);
-static char *x_mapin(const char *, Area *);
-static char *x_mapout(int);
-static void x_mapout2(int, char **);
-static void x_print(int, int);
+static void x_bind_showone(int, int);
static void x_e_ungetc(int);
static int x_e_getc(void);
static void x_e_putc2(int);
@@ -1145,6 +1143,7 @@ static struct x_defbindings const x_defbindings[] = {
#ifndef MKSH_SMALL
/* more non-standard ones */
{ XFUNC_eval_region, 1, CTRL_E },
+ { XFUNC_quote_region, 1, 'Q' },
{ XFUNC_edit_line, 2, 'e' }
#endif
};
@@ -2389,190 +2388,223 @@ x_vt_hack(int c)
}
#endif
-static char *
-x_mapin(const char *cp, Area *ap)
+int
+x_bind_check(void)
{
- char *news, *op;
+ return (x_tab == NULL);
+}
- strdupx(news, cp, ap);
- op = news;
- while (*cp) {
- switch (*cp) {
- case '^':
- cp++;
- *op++ = ksh_toctrl(*cp);
- break;
- case '\\':
- if (cp[1] == '\\' || cp[1] == '^')
- ++cp;
- /* FALLTHROUGH */
- default:
- *op++ = *cp;
- }
- cp++;
- }
- *op = '\0';
+static XString x_bind_show_xs;
+static char *x_bind_show_xp;
- return (news);
+static void
+x_bind_show_ch(unsigned char ch)
+{
+ Xcheck(x_bind_show_xs, x_bind_show_xp);
+ switch (ch) {
+ case ORD('^'):
+ case ORD('\\'):
+ case ORD('='):
+ *x_bind_show_xp++ = '\\';
+ *x_bind_show_xp++ = ch;
+ break;
+ default:
+ if (ksh_isctrl(ch)) {
+ *x_bind_show_xp++ = '^';
+ *x_bind_show_xp++ = ksh_unctrl(ch);
+ } else
+ *x_bind_show_xp++ = ch;
+ break;
+ }
}
static void
-x_mapout2(int c, char **buf)
+x_bind_showone(int prefix, int key)
{
- char *p = *buf;
+ unsigned char f = XFUNC_VALUE(x_tab[prefix][key]);
- if (ksh_isctrl(c)) {
- *p++ = '^';
- *p++ = ksh_unctrl(c);
+ if (!x_bind_show_xs.areap)
+ XinitN(x_bind_show_xs, 16, AEDIT);
+
+ x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
+ shf_puts("bind ", shl_stdout);
+#ifndef MKSH_SMALL
+ if (f == XFUNC_ins_string)
+ shf_puts("-m ", shl_stdout);
+#endif
+ switch (prefix) {
+ case 1:
+ x_bind_show_ch(CTRL_BO);
+ break;
+ case 2:
+ x_bind_show_ch(CTRL_X);
+ break;
+ case 3:
+ x_bind_show_ch(0);
+ break;
+ }
+ x_bind_show_ch(key);
+#ifndef MKSH_SMALL
+ if (x_tab[prefix][key] & 0x80)
+ *x_bind_show_xp++ = '~';
+#endif
+ *x_bind_show_xp = '\0';
+ x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
+ print_value_quoted(shl_stdout, x_bind_show_xp);
+ shf_putc('=', shl_stdout);
+#ifndef MKSH_SMALL
+ if (f == XFUNC_ins_string) {
+ const unsigned char *cp = (const void *)x_atab[prefix][key];
+ unsigned char c;
+
+ while ((c = *cp++))
+ x_bind_show_ch(c);
+ *x_bind_show_xp = '\0';
+ x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
+ print_value_quoted(shl_stdout, x_bind_show_xp);
} else
- *p++ = c;
- *p = 0;
- *buf = p;
+#endif
+ shf_puts(x_ftab[f].xf_name, shl_stdout);
+ shf_putc('\n', shl_stdout);
}
-static char *
-x_mapout(int c)
+int
+x_bind_list(void)
{
- static char buf[8];
- char *bp = buf;
+ size_t f;
- x_mapout2(c, &bp);
- return (buf);
+ for (f = 0; f < NELEM(x_ftab); f++)
+ if (!(x_ftab[f].xf_flags & XF_NOBIND))
+ shprintf(Tf_sN, x_ftab[f].xf_name);
+ return (0);
}
-static void
-x_print(int prefix, int key)
+int
+x_bind_showall(void)
{
- int f = x_tab[prefix][key];
+ int prefix, key;
- if (prefix)
- /* prefix == 1 || prefix == 2 || prefix == 3 */
- shf_puts(x_mapout(prefix == 1 ? CTRL_BO :
- prefix == 2 ? CTRL_X : 0), shl_stdout);
-#ifdef MKSH_SMALL
- shprintf("%s = ", x_mapout(key));
-#else
- shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
- if (XFUNC_VALUE(f) != XFUNC_ins_string)
-#endif
- shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
-#ifndef MKSH_SMALL
- else
- shprintf("'%s'\n", x_atab[prefix][key]);
-#endif
+ for (prefix = 0; prefix < X_NTABS; prefix++)
+ for (key = 0; key < X_TABSZ; key++)
+ switch (XFUNC_VALUE(x_tab[prefix][key])) {
+ case XFUNC_error: /* unset */
+ case XFUNC_insert: /* auto-insert */
+ break;
+ default:
+ x_bind_showone(prefix, key);
+ break;
+ }
+ return (0);
+}
+
+static unsigned int
+x_bind_getc(const char **ccpp)
+{
+ unsigned int ch, ec;
+
+ if ((ch = ord(**ccpp)))
+ ++(*ccpp);
+ switch (ch) {
+ case ORD('^'):
+ ch = ksh_toctrl(**ccpp) | 0x100U;
+ if (**ccpp)
+ ++(*ccpp);
+ break;
+ case ORD('\\'):
+ switch ((ec = ord(**ccpp))) {
+ case ORD('^'):
+ case ORD('\\'):
+ case ORD('='):
+ ch = ec | 0x100U;
+ ++(*ccpp);
+ break;
+ }
+ break;
+ }
+ return (ch);
}
int
-x_bind(const char *a1, const char *a2,
-#ifndef MKSH_SMALL
- /* bind -m */
- bool macro,
-#endif
- /* bind -l */
- bool list)
+x_bind(const char *s SMALLP(bool macro))
{
- unsigned char f;
+ const char *ccp = s;
int prefix, key;
- char *m1, *m2;
+ unsigned int c;
#ifndef MKSH_SMALL
- char *sp = NULL;
- bool hastilde;
+ bool hastilde = false;
+ char *ms = NULL;
#endif
- if (x_tab == NULL) {
- bi_errorf("can't bind, not a tty");
+ prefix = 0;
+ c = x_bind_getc(&ccp);
+ if (!c || c == ORD('=')) {
+ bi_errorf("no key to bind");
return (1);
}
- /* List function names */
- if (list) {
- for (f = 0; f < NELEM(x_ftab); f++)
- if (!(x_ftab[f].xf_flags & XF_NOBIND))
- shprintf(Tf_sN, x_ftab[f].xf_name);
- return (0);
- }
- if (a1 == NULL) {
- for (prefix = 0; prefix < X_NTABS; prefix++)
- for (key = 0; key < X_TABSZ; key++) {
- f = XFUNC_VALUE(x_tab[prefix][key]);
- if (f == XFUNC_insert || f == XFUNC_error
-#ifndef MKSH_SMALL
- || (macro && f != XFUNC_ins_string)
-#endif
- )
- continue;
- x_print(prefix, key);
- }
- return (0);
- }
- m2 = m1 = x_mapin(a1, ATEMP);
- prefix = 0;
- for (;; m1++) {
- key = (unsigned char)*m1;
- f = XFUNC_VALUE(x_tab[prefix][key]);
- if (f == XFUNC_meta1)
+ key = c & 0xFF;
+ while ((c = x_bind_getc(&ccp)) != ORD('=')) {
+ if (!c) {
+ x_bind_showone(prefix, key);
+ return (0);
+ }
+ switch (XFUNC_VALUE(x_tab[prefix][key])) {
+ case XFUNC_meta1:
prefix = 1;
- else if (f == XFUNC_meta2)
- prefix = 2;
- else if (f == XFUNC_meta3)
- prefix = 3;
- else
- break;
- }
- if (*++m1
+ if (0)
+ /* FALLTHROUGH */
+ case XFUNC_meta2:
+ prefix = 2;
+ if (0)
+ /* FALLTHROUGH */
+ case XFUNC_meta3:
+ prefix = 3;
+ key = c & 0xFF;
+ continue;
+ }
#ifndef MKSH_SMALL
- && ((*m1 != '~') || *(m1 + 1))
+ if (c == ORD('~')) {
+ hastilde = true;
+ continue;
+ }
#endif
- ) {
- char msg[256];
- const char *c = a1;
- m1 = msg;
- while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
- x_mapout2(*c++, &m1);
- bi_errorf("too long key sequence: %s", msg);
- return (1);
+ bi_errorf("too long key sequence: %s", s);
+ return (-1);
}
-#ifndef MKSH_SMALL
- hastilde = tobool(*m1);
-#endif
- afree(m2, ATEMP);
- if (a2 == NULL) {
- x_print(prefix, key);
- return (0);
- }
- if (*a2 == 0) {
- f = XFUNC_insert;
#ifndef MKSH_SMALL
- } else if (macro) {
- f = XFUNC_ins_string;
- sp = x_mapin(a2, AEDIT);
+ if (macro) {
+ char *cp;
+
+ cp = ms = alloc(strlen(ccp) + 1, AEDIT);
+ while ((c = x_bind_getc(&ccp)))
+ *cp++ = c;
+ *cp = '\0';
+ c = XFUNC_ins_string;
+ } else
#endif
+ if (!*ccp) {
+ c = XFUNC_insert;
} else {
- for (f = 0; f < NELEM(x_ftab); f++)
- if (!strcmp(x_ftab[f].xf_name, a2))
+ for (c = 0; c < NELEM(x_ftab); ++c)
+ if (!strcmp(x_ftab[c].xf_name, ccp))
break;
- if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
- bi_errorf("%s: no such function", a2);
+ if (c == NELEM(x_ftab) || x_ftab[c].xf_flags & XF_NOBIND) {
+ bi_errorf("%s: no such editing command", ccp);
return (1);
}
}
#ifndef MKSH_SMALL
- if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
- x_atab[prefix][key])
+ if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string)
afree(x_atab[prefix][key], AEDIT);
+ x_atab[prefix][key] = ms;
+ if (hastilde)
+ c |= 0x80U;
#endif
- x_tab[prefix][key] = f
-#ifndef MKSH_SMALL
- | (hastilde ? 0x80 : 0)
-#endif
- ;
-#ifndef MKSH_SMALL
- x_atab[prefix][key] = sp;
-#endif
+ x_tab[prefix][key] = c;
- /* Track what the user has bound so x_mode(true) won't toast things */
- if (f == XFUNC_insert)
+ /* track what the user has bound, so x_mode(true) won't toast things */
+ if (c == XFUNC_insert)
x_bound[(prefix * X_TABSZ + key) / 8] &=
~(1 << ((prefix * X_TABSZ + key) % 8));
else
@@ -3507,11 +3539,11 @@ static int inslen; /* length of input buffer */
static int srchlen; /* length of current search pattern */
static char *ybuf; /* yank buffer */
static int yanklen; /* length of yank buffer */
-static int fsavecmd = ' '; /* last find command */
+static uint8_t fsavecmd = ORD(' '); /* last find command */
static int fsavech; /* character to find */
static char lastcmd[MAXVICMD]; /* last non-move command */
static int lastac; /* argcnt for lastcmd */
-static int lastsearch = ' '; /* last search command */
+static uint8_t lastsearch = ORD(' '); /* last search command */
static char srchpat[SRCHLEN]; /* last search pattern */
static int insert; /* <>0 in insert mode */
static int hnum; /* position in history */
@@ -3651,21 +3683,25 @@ vi_hook(int ch)
case VNORMAL:
/* PC scancodes */
- if (!ch) switch (cmdlen = 0, (ch = x_getc())) {
- case 71: ch = '0'; goto pseudo_vi_command;
- case 72: ch = 'k'; goto pseudo_vi_command;
- case 73: ch = 'A'; goto vi_xfunc_search_up;
- case 75: ch = 'h'; goto pseudo_vi_command;
- case 77: ch = 'l'; goto pseudo_vi_command;
- case 79: ch = '$'; goto pseudo_vi_command;
- case 80: ch = 'j'; goto pseudo_vi_command;
- case 83: ch = 'x'; goto pseudo_vi_command;
- default: ch = 0; goto vi_insert_failed;
+ if (!ch) {
+ cmdlen = 0;
+ switch (ch = x_getc()) {
+ case 71: ch = ORD('0'); goto pseudo_vi_command;
+ case 72: ch = ORD('k'); goto pseudo_vi_command;
+ case 73: ch = ORD('A'); goto vi_xfunc_search;
+ case 75: ch = ORD('h'); goto pseudo_vi_command;
+ case 77: ch = ORD('l'); goto pseudo_vi_command;
+ case 79: ch = ORD('$'); goto pseudo_vi_command;
+ case 80: ch = ORD('j'); goto pseudo_vi_command;
+ case 81: ch = ORD('B'); goto vi_xfunc_search;
+ case 83: ch = ORD('x'); goto pseudo_vi_command;
+ default: ch = 0; goto vi_insert_failed;
+ }
}
if (insert != 0) {
if (ch == CTRL_V) {
state = VLIT;
- ch = '^';
+ ch = ORD('^');
}
switch (vi_insert(ch)) {
case -1:
@@ -3699,8 +3735,8 @@ vi_hook(int ch)
save_cbuf();
vs->cursor = 0;
vs->linelen = 0;
- if (putbuf(ch == '/' ? "/" : "?", 1,
- false) != 0)
+ if (putbuf(ord(ch) == ORD('/') ?
+ "/" : "?", 1, false) != 0)
return (-1);
refresh(0);
}
@@ -3861,10 +3897,11 @@ vi_hook(int ch)
break;
case VPREFIX2:
- vi_xfunc_search_up:
+ vi_xfunc_search:
state = VFAIL;
switch (ch) {
- case 'A':
+ case ORD('A'):
+ case ORD('B'):
/* the cursor may not be at the BOL */
if (!vs->cursor)
break;
@@ -3873,13 +3910,14 @@ vi_hook(int ch)
vs->cursor = sizeof(srchpat) - 2;
/* anchor the search pattern */
srchpat[0] = '^';
- /* take the current line up to the cursor */
- memmove(srchpat + 1, vs->cbuf, vs->cursor);
+ /* take current line up to the cursor */
+ memcpy(srchpat + 1, vs->cbuf, vs->cursor);
srchpat[vs->cursor + 1] = '\0';
/* set a magic flag */
argc1 = 2 + (int)vs->cursor;
- /* and emulate a backwards history search */
- lastsearch = '/';
+ /* and emulate a history search */
+ /* search backwards if PgUp, forwards for PgDn */
+ lastsearch = ch == ORD('A') ? '/' : '?';
*curcmd = 'n';
goto pseudo_VCMD;
}
@@ -4442,9 +4480,9 @@ vi_cmd(int argcnt, const char *cmd)
/* FALLTHROUGH */
case ORD('n'):
case ORD('N'):
- if (lastsearch == ' ')
+ if (lastsearch == ORD(' '))
return (-1);
- if (lastsearch == '?')
+ if (lastsearch == ORD('?'))
c1 = 1;
else
c1 = 0;
@@ -4648,10 +4686,10 @@ domove(int argcnt, const char *cmd, int sub)
/* FALLTHROUGH */
case ORD(','):
case ORD(';'):
- if (fsavecmd == ' ')
+ if (fsavecmd == ORD(' '))
return (-1);
i = ksh_eq(fsavecmd, 'F', 'f');
- t = fsavecmd > 'a';
+ t = rtt2asc(fsavecmd) > rtt2asc('a');
if (*cmd == ',')
t = !t;
if ((ncursor = findch(fsavech, argcnt, tobool(t),
@@ -5589,39 +5627,40 @@ x_eval_region_helper(const char *cmd, size_t len)
afree(wds, ATEMP);
strdupx(cp, cp, AEDIT);
} else
+ /* command cannot be parsed */
cp = NULL;
quitenv(NULL);
return (cp);
}
static int
-x_eval_region(int c MKSH_A_UNUSED)
+x_operate_region(char *(*helper)(const char *, size_t))
{
- char *evbeg, *evend, *cp;
+ char *rgbeg, *rgend, *cp;
size_t newlen;
/* only for LINE overflow checking */
size_t restlen;
if (xmp == NULL) {
- evbeg = xbuf;
- evend = xep;
+ rgbeg = xbuf;
+ rgend = xep;
} else if (xmp < xcp) {
- evbeg = xmp;
- evend = xcp;
+ rgbeg = xmp;
+ rgend = xcp;
} else {
- evbeg = xcp;
- evend = xmp;
+ rgbeg = xcp;
+ rgend = xmp;
}
x_e_putc2('\r');
x_clrtoeol(' ', false);
x_flush();
x_mode(false);
- cp = x_eval_region_helper(evbeg, evend - evbeg);
+ cp = helper(rgbeg, rgend - rgbeg);
x_mode(true);
if (cp == NULL) {
- /* command cannot be parsed */
+ /* error return from helper */
x_eval_region_err:
x_e_putc2(KSH_BEL);
x_redraw('\r');
@@ -5629,20 +5668,49 @@ x_eval_region(int c MKSH_A_UNUSED)
}
newlen = strlen(cp);
- restlen = xep - evend;
+ restlen = xep - rgend;
/* check for LINE overflow, until this is dynamically allocated */
- if (evbeg + newlen + restlen >= xend)
+ if (rgbeg + newlen + restlen >= xend)
goto x_eval_region_err;
- xmp = evbeg;
- xcp = evbeg + newlen;
+ xmp = rgbeg;
+ xcp = rgbeg + newlen;
xep = xcp + restlen;
- memmove(xcp, evend, restlen + /* NUL */ 1);
+ memmove(xcp, rgend, restlen + /* NUL */ 1);
memcpy(xmp, cp, newlen);
afree(cp, AEDIT);
x_adjust();
x_modified();
return (KSTD);
}
+
+static int
+x_eval_region(int c MKSH_A_UNUSED)
+{
+ return (x_operate_region(x_eval_region_helper));
+}
+
+static char *
+x_quote_region_helper(const char *cmd, size_t len)
+{
+ char *s;
+ size_t newlen;
+ struct shf shf;
+
+ strndupx(s, cmd, len, ATEMP);
+ newlen = len < 256 ? 256 : 4096;
+ shf_sopen(alloc(newlen, AEDIT), newlen, SHF_WR | SHF_DYNAMIC, &shf);
+ shf.areap = AEDIT;
+ shf.flags |= SHF_ALLOCB;
+ print_value_quoted(&shf, s);
+ afree(s, ATEMP);
+ return (shf_sclose(&shf));
+}
+
+static int
+x_quote_region(int c MKSH_A_UNUSED)
+{
+ return (x_operate_region(x_quote_region_helper));
+}
#endif /* !MKSH_SMALL */
#endif /* !MKSH_NO_CMDLINE_EDITING */
diff --git a/src/emacsfn.h b/src/emacsfn.h
index a1e8e82..6162987 100644
--- a/src/emacsfn.h
+++ b/src/emacsfn.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2009, 2010, 2015, 2016
+ * Copyright (c) 2009, 2010, 2015, 2016, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -19,7 +19,7 @@
*/
#if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.10 2016/09/01 12:59:09 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.11 2020/04/13 20:46:39 tg Exp $");
#define FN(cname,sname,flags) static int x_##cname(int);
#elif defined(EMACSFN_ENUMS)
#define FN(cname,sname,flags) XFUNC_##cname,
@@ -89,6 +89,9 @@ FN(nl_next_com, "newline-and-next", 0)
FN(noop, "no-op", 0)
FN(prev_com, "up-history", XF_ARG)
FN(prev_histword, "prev-hist-word", XF_ARG)
+#ifndef MKSH_SMALL
+FN(quote_region, "quote-region", 0)
+#endif
FN(search_char_back, "search-character-backward", XF_ARG)
FN(search_char_forw, "search-character-forward", XF_ARG)
FN(search_hist, "search-history", 0)
diff --git a/src/eval.c b/src/eval.c
index 6aa1844..6e7b505 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2,7 +2,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+ * 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +24,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.219 2018/01/14 01:29:47 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.230 2020/04/07 23:14:41 tg Exp $");
/*
* string expansion
@@ -50,13 +51,17 @@ typedef struct {
bool split;
} Expand;
-#define XBASE 0 /* scanning original */
-#define XSUB 1 /* expanding ${} string */
-#define XARGSEP 2 /* ifs0 between "$*" */
-#define XARG 3 /* expanding $*, $@ */
-#define XCOM 4 /* expanding $() */
-#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
-#define XSUBMID 6 /* middle of expanding ${} */
+#define XBASE 0 /* scanning original string */
+#define XARGSEP 1 /* ifs0 between "$*" */
+#define XARG 2 /* expanding $*, $@ */
+#define XCOM 3 /* expanding $() */
+#define XNULLSUB 4 /* "$@" when $# is 0, so don't generate word */
+#define XSUB 5 /* expanding ${} string */
+#define XSUBMID 6 /* middle of expanding ${}; must be XSUB+1 */
+#define XSUBPAT 7 /* expanding [[ x = ${} ]] string */
+#define XSUBPATMID 8 /* middle, must be XSUBPAT+1 */
+
+#define isXSUB(t) ((t) == XSUB || (t) == XSUBPAT)
/* States used for field splitting */
#define IFS_WORD 0 /* word has chars (or quotes except "$@") */
@@ -71,7 +76,7 @@ typedef struct {
#define STYPE_SINGLE 0x2FF
#define STYPE_MASK 0x300
-static int varsub(Expand *, const char *, const char *, int *, int *);
+static int varsub(Expand *, const char *, const char *, unsigned int *, int *);
static int comsub(Expand *, const char *, int);
static char *valsub(struct op *, Area *);
static char *trimsub(char *, char *, int);
@@ -205,7 +210,7 @@ typedef struct SubType {
struct SubType *prev; /* old type */
struct SubType *next; /* poped type (to avoid re-allocating) */
size_t base; /* start position of expanded word */
- short stype; /* [=+-?%#] action after expanded word */
+ unsigned short stype; /* [=+-?%#] action after expanded word */
short f; /* saved value of f (DOPAT, etc) */
uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */
uint8_t quotew; /* saved value of quote (for ${..[+-=]..}) */
@@ -334,7 +339,7 @@ expand(
Xcheck(ds, dp);
*dp++ = *sp++;
}
- if ((unsigned int)c == ORD('}'))
+ if ((unsigned int)c == ORD(/*{*/'}'))
*dp++ = ';';
*dp++ = c;
} else {
@@ -381,7 +386,7 @@ expand(
*/
/* skip the { or x (}) */
const char *varname = ++sp;
- int stype;
+ unsigned int stype;
int slen = 0;
/* skip variable */
@@ -437,17 +442,8 @@ expand(
sp += slen;
switch (stype & STYPE_SINGLE) {
case ORD('#') | STYPE_AT:
- x.str = shf_smprintf("%08X",
- (unsigned int)hash(str_val(st->var)));
- break;
- case ORD('Q') | STYPE_AT: {
- struct shf shf;
-
- shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
- print_value_quoted(&shf, str_val(st->var));
- x.str = shf_sclose(&shf);
+ case ORD('Q') | STYPE_AT:
break;
- }
case ORD('0'): {
char *beg, *mid, *end, *stg;
mksh_ari_t from = 0, num = -1, flen, finc = 0;
@@ -497,10 +493,11 @@ expand(
}
case ORD('/') | STYPE_AT:
case ORD('/'): {
- char *s, *p, *d, *sbeg, *end;
- char *pat = NULL, *rrep = null;
+ char *s, *p, *d, *sbeg;
+ char *pat = NULL, *rrep;
char fpat = 0, *tpat1, *tpat2;
- char *ws, *wpat, *wrep;
+ char *ws, *wpat, *wrep, tch;
+ size_t rreplen;
s = ws = wdcopy(sp, ATEMP);
p = s + (wdscan(sp, ADELIM) - sp);
@@ -516,11 +513,17 @@ expand(
ctype(s[1], C_SUB2))
fpat = s[1];
wpat = s + (fpat ? 2 : 0);
- wrep = d ? p : NULL;
- if (!(stype & STYPE_AT)) {
- rrep = wrep ? evalstr(wrep,
- DOTILDE | DOSCALAR) :
- null;
+ if (!(wrep = d ? p : NULL)) {
+ rrep = null;
+ rreplen = 0;
+ } else if (!(stype & STYPE_AT)) {
+ rrep = evalstr(wrep,
+ DOTILDE | DOSCALAR);
+ rreplen = strlen(rrep);
+ } else {
+ rrep = NULL;
+ /* shut up GCC */
+ rreplen = 0;
}
/* prepare string on which to work */
@@ -567,17 +570,17 @@ expand(
*/
if (!gmatchx(sbeg, tpat1, false))
goto end_repl;
- end = strnul(s);
+ d = strnul(s);
/* now anchor the beginning of the match */
if (ord(fpat) != ORD('#'))
- while (sbeg <= end) {
+ while (sbeg <= d) {
if (gmatchx(sbeg, tpat2, false))
break;
else
sbeg++;
}
/* now anchor the end of the match */
- p = end;
+ p = d;
if (ord(fpat) != ORD('%'))
while (p >= sbeg) {
bool gotmatch;
@@ -590,22 +593,56 @@ expand(
break;
p--;
}
- strndupx(end, sbeg, p - sbeg, ATEMP);
- record_match(end);
- afree(end, ATEMP);
- if (stype & STYPE_AT) {
- if (rrep != null)
- afree(rrep, ATEMP);
- rrep = wrep ? evalstr(wrep,
- DOTILDE | DOSCALAR) :
- null;
+
+ /* record partial string as match */
+ tch = *p;
+ *p = '\0';
+ record_match(sbeg);
+ *p = tch;
+ /* get replacement string, if necessary */
+ if ((stype & STYPE_AT) &&
+ rrep != null) {
+ afree(rrep, ATEMP);
+ /* might access match! */
+ rrep = evalstr(wrep,
+ DOTILDE | DOSCALAR);
+ rreplen = strlen(rrep);
+ }
+
+ /*
+ * string:
+ * |--------|---------|-------\0
+ * s n1 sbeg n2 p n3 d
+ *
+ * replacement:
+ * |------------|
+ * rrep rreplen
+ */
+
+ /* move strings around and replace */
+ {
+ size_t n1 = sbeg - s;
+ size_t n2 = p - sbeg;
+ size_t n3 = d - p;
+ /* move part3 to the front, OR… */
+ if (rreplen < n2)
+ memmove(sbeg + rreplen,
+ p, n3 + 1);
+ /* … adjust size, move to back */
+ if (rreplen > n2) {
+ s = aresize(s,
+ n1 + rreplen + n3 + 1,
+ ATEMP);
+ memmove(s + n1 + rreplen,
+ s + n1 + n2,
+ n3 + 1);
+ }
+ /* insert replacement */
+ if (rreplen)
+ memcpy(s + n1, rrep, rreplen);
+ /* continue after the place */
+ sbeg = s + n1 + rreplen;
}
- strndupx(end, s, sbeg - s, ATEMP);
- d = shf_smprintf(Tf_sss, end, rrep, p);
- afree(end, ATEMP);
- sbeg = d + (sbeg - s) + strlen(rrep);
- afree(s, ATEMP);
- s = d;
if (stype & STYPE_AT) {
afree(tpat1, ATEMP);
afree(pat, ATEMP);
@@ -769,11 +806,22 @@ expand(
errorf(Tf_sD_s, st->var->name,
debunk(dp, dp, strlen(dp) + 1));
break;
+ case ORD('#') | STYPE_AT:
+ x.str = shf_smprintf("%08X",
+ (unsigned int)hash(str_val(st->var)));
+ goto common_CSUBST;
+ case ORD('Q') | STYPE_AT: {
+ struct shf shf;
+
+ shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
+ print_value_quoted(&shf, str_val(st->var));
+ x.str = shf_sclose(&shf);
+ goto common_CSUBST;
+ }
case ORD('0'):
case ORD('/') | STYPE_AT:
case ORD('/'):
- case ORD('#') | STYPE_AT:
- case ORD('Q') | STYPE_AT:
+ common_CSUBST:
dp = Xrestpos(ds, dp, st->base);
type = XSUB;
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
@@ -822,9 +870,69 @@ expand(
}
continue;
+ case XSUBPAT:
+ case XSUBPATMID:
+ XSUBPAT_beg:
+ switch ((c = ord(*x.str++))) {
+ case 0:
+ goto XSUB_end;
+ case ORD('\\'):
+ if ((c = ord(*x.str)) == 0)
+ /* keep backslash at EOS */
+ c = ORD('\\');
+ else
+ ++x.str;
+ break;
+ /* ctype(c, C_PATMO) */
+ case ORD('!'):
+ case ORD('*'):
+ case ORD('+'):
+ case ORD('?'):
+ case ORD('@'):
+ if (ord(*x.str) == ORD('('/*)*/)) {
+ ++x.str;
+ c |= 0x80U;
+ make_magic = true;
+ }
+ break;
+ case ORD('('):
+ c = ORD(' ') | 0x80U;
+ /* FALLTHROUGH */
+ case ORD('|'):
+ case ORD(')'):
+ make_magic = true;
+ break;
+ }
+ break;
+
case XSUB:
+ if (!quote && (f & DODBMAGIC)) {
+ const char *cs = x.str;
+ int level = 0;
+
+ while ((c = *cs++))
+ switch (c) {
+ case '\\':
+ if ((c = *cs))
+ ++cs;
+ break;
+ case ORD('('):
+ ++level;
+ break;
+ case ORD(')'):
+ --level;
+ break;
+ }
+ /* balanced parentheses? */
+ if (!level) {
+ type = XSUBPAT;
+ goto XSUBPAT_beg;
+ }
+ }
+ /* FALLTHROUGH */
case XSUBMID:
if ((c = ord(*x.str++)) == 0) {
+ XSUB_end:
type = XBASE;
if (f & DOBLANK)
doblank--;
@@ -980,23 +1088,17 @@ expand(
Xinit(ds, dp, 128, ATEMP);
} else if (c == 0) {
return;
- } else if (type == XSUB && ctype(c, C_IFS) &&
+ } else if (isXSUB(type) && ctype(c, C_IFS) &&
!ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
*(cp = alloc(1, ATEMP)) = '\0';
XPput(*wp, cp);
- type = XSUBMID;
+ ++type;
}
if (word != IFS_NWS)
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
} else {
- if (type == XSUB) {
- if (word == IFS_NWS &&
- Xlength(ds, dp) == 0) {
- *(cp = alloc(1, ATEMP)) = '\0';
- XPput(*wp, cp);
- }
- type = XSUBMID;
- }
+ if (isXSUB(type))
+ ++type;
/* age tilde_ok info - ~ code tests second bit */
tilde_ok <<= 1;
@@ -1111,16 +1213,20 @@ hasnonempty(const char **strv)
*/
static int
varsub(Expand *xp, const char *sp, const char *word,
- int *stypep, /* becomes qualifier type */
- int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */
+ /* becomes qualifier type */
+ unsigned int *stypep,
+ /* becomes qualifier type len (=, :=, etc.) valid iff *stypep != 0 */
+ int *slenp)
{
- int c;
- int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
- int stype; /* substitution type */
+ unsigned int c;
+ int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
+ unsigned int stype; /* substitution type */
int slen = 0;
const char *p;
struct tbl *vp;
bool zero_ok = false;
+ int sc;
+ XPtrV wv;
if ((stype = ord(sp[0])) == '\0')
/* Bad variable name */
@@ -1128,108 +1234,166 @@ varsub(Expand *xp, const char *sp, const char *word,
xp->var = NULL;
+ /* entirety of named array? */
+ if ((p = cstrchr(sp, '[')) && (sc = ord(p[1])) &&
+ ord(p[2]) == ORD(']'))
+ /* keep p (for ${!foo[1]} below)! */
+ switch (sc) {
+ case ORD('*'):
+ sc = 3;
+ break;
+ case ORD('@'):
+ sc = 7;
+ break;
+ default:
+ /* bit2 = @, bit1 = array, bit0 = enabled */
+ sc = 0;
+ }
+ else
+ /* $* and $@ checked below */
+ sc = 0;
+
/*-
- * ${#var}, string length (-U: characters, +U: octets) or array size
* ${%var}, string width (-U: screen columns, +U: octets)
+ * ${#var}, string length (-U: characters, +U: octets) or array size
+ * ${!var}, variable name
+ * ${*…} -> set flag for argv
+ * ${@…} -> set flag for argv
*/
- c = ord(sp[1]);
- if ((unsigned int)stype == ORD('%') && c == '\0')
- return (-1);
- if (ctype(stype, C_SUB2) && c != '\0') {
- /* Can't have any modifiers for ${#...} or ${%...} */
+ if (ctype(stype, C_SUB2 | CiVAR1)) {
+ switch (stype) {
+ case ORD('*'):
+ if (!sc)
+ sc = 1;
+ goto nopfx;
+ case ORD('@'):
+ if (!sc)
+ sc = 5;
+ goto nopfx;
+ }
+ /* varname required */
+ if ((c = ord(sp[1])) == '\0') {
+ if (stype == ORD('%'))
+ /* $% */
+ return (-1);
+ /* $# or $! */
+ goto nopfx;
+ }
+ /* can’t have any modifiers for ${#…} or ${%…} or ${!…} */
if (*word != CSUBST)
return (-1);
- sp++;
- /* Check for size of array */
- if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
- ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
- int n = 0;
-
- if ((unsigned int)stype != ORD('#'))
- return (-1);
- vp = global(arrayname(sp));
- if (vp->flag & (ISSET|ARRAY))
- zero_ok = true;
- for (; vp; vp = vp->u.array)
- if (vp->flag & ISSET)
- n++;
- c = n;
- } else if ((unsigned int)c == ORD('*') ||
- (unsigned int)c == ORD('@')) {
- if ((unsigned int)stype != ORD('#'))
+ /* check for argv past prefix */
+ if (!sc) switch (c) {
+ case ORD('*'):
+ sc = 1;
+ break;
+ case ORD('@'):
+ sc = 5;
+ break;
+ }
+ /* skip past prefix */
+ ++sp;
+ /* determine result */
+ switch (stype) {
+ case ORD('!'):
+ if (sc & 2) {
+ stype = 0;
+ XPinit(wv, 32);
+ vp = global(arrayname(sp));
+ do {
+ if (vp->flag & ISSET)
+ XPput(wv, shf_smprintf(Tf_lu,
+ arrayindex(vp)));
+ } while ((vp = vp->u.array));
+ goto arraynames;
+ }
+ xp->var = global(sp);
+ /* use saved p from above */
+ xp->str = p ? shf_smprintf("%s[%lu]", xp->var->name,
+ arrayindex(xp->var)) : xp->var->name;
+ break;
+#ifdef DEBUG
+ default:
+ internal_errorf("stype mismatch");
+ /* NOTREACHED */
+#endif
+ case ORD('%'):
+ /* cannot do this on an array */
+ if (sc)
return (-1);
- c = e->loc->argc;
- } else {
p = str_val(global(sp));
zero_ok = p != null;
- if ((unsigned int)stype == ORD('#'))
- c = utflen(p);
- else {
- /* partial utf_mbswidth reimplementation */
- const char *s = p;
- unsigned int wc;
- size_t len;
- int cw;
-
- c = 0;
- while (*s) {
- if (!UTFMODE || (len = utf_mbtowc(&wc,
- s)) == (size_t)-1)
- /* not UTFMODE or not UTF-8 */
- wc = rtt2asc(*s++);
- else
- /* UTFMODE and UTF-8 */
- s += len;
- /* wc == char or wchar at s++ */
- if ((cw = utf_wcwidth(wc)) == -1) {
- /* 646, 8859-1, 10646 C0/C1 */
- c = -1;
- break;
- }
- c += cw;
+ /* partial utf_mbswidth reimplementation */
+ sc = 0;
+ while (*p) {
+ if (!UTFMODE ||
+ (wv.len = utf_mbtowc(&c, p)) == (size_t)-1)
+ /* not UTFMODE or not UTF-8 */
+ c = rtt2asc(*p++);
+ else
+ /* UTFMODE and UTF-8 */
+ p += wv.len;
+ /* c == char or wchar at p++ */
+ if ((slen = utf_wcwidth(c)) == -1) {
+ /* 646, 8859-1, 10646 C0/C1 */
+ sc = -1;
+ break;
}
+ sc += slen;
+ }
+ if (0)
+ /* FALLTHROUGH */
+ case ORD('#'):
+ switch (sc & 3) {
+ case 3:
+ vp = global(arrayname(sp));
+ if (vp->flag & (ISSET|ARRAY))
+ zero_ok = true;
+ sc = 0;
+ do {
+ if (vp->flag & ISSET)
+ sc++;
+ } while ((vp = vp->u.array));
+ break;
+ case 1:
+ sc = e->loc->argc;
+ break;
+ default:
+ p = str_val(global(sp));
+ zero_ok = p != null;
+ sc = utflen(p);
+ break;
}
+ /* ${%var} also here */
+ if (Flag(FNOUNSET) && sc == 0 && !zero_ok)
+ errorf(Tf_parm, sp);
+ xp->str = shf_smprintf(Tf_d, sc);
+ break;
}
- if (Flag(FNOUNSET) && c == 0 && !zero_ok)
- errorf(Tf_parm, sp);
/* unqualified variable/string substitution */
*stypep = 0;
- xp->str = shf_smprintf(Tf_d, c);
- return (XSUB);
- }
- if ((unsigned int)stype == ORD('!') && c != '\0' && *word == CSUBST) {
- sp++;
- if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
- ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
- c = ORD('!');
- stype = 0;
- goto arraynames;
- }
- xp->var = global(sp);
- xp->str = p ? shf_smprintf("%s[%lu]",
- xp->var->name, arrayindex(xp->var)) : xp->var->name;
- *stypep = 0;
return (XSUB);
}
+ nopfx:
- /* Check for qualifiers in word part */
+ /* check for qualifiers in word part */
stype = 0;
- c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
- if ((unsigned int)c == ORD(':')) {
+ /*slen = 0;*/
+ c = word[/*slen +*/ 0] == CHAR ? ord(word[/*slen +*/ 1]) : 0;
+ if (c == ORD(':')) {
slen += 2;
stype = STYPE_DBL;
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
}
- if (!stype && (unsigned int)c == ORD('/')) {
+ if (!stype && c == ORD('/')) {
slen += 2;
stype = c;
if (word[slen] == ADELIM &&
- ord(word[slen + 1]) == (unsigned int)c) {
+ ord(word[slen + 1]) == c) {
slen += 2;
stype |= STYPE_DBL;
}
- } else if (stype == STYPE_DBL && ((unsigned int)c == ORD(' ') ||
- (unsigned int)c == ORD('0'))) {
+ } else if (stype == STYPE_DBL && (c == ORD(' ') || c == ORD('0'))) {
stype |= ORD('0');
} else if (ctype(c, C_SUB1)) {
slen += 2;
@@ -1238,12 +1402,11 @@ varsub(Expand *xp, const char *sp, const char *word,
/* Note: ksh88 allows :%, :%%, etc */
slen += 2;
stype = c;
- if (word[slen + 0] == CHAR &&
- ord(word[slen + 1]) == (unsigned int)c) {
+ if (word[slen + 0] == CHAR && ord(word[slen + 1]) == c) {
stype |= STYPE_DBL;
slen += 2;
}
- } else if ((unsigned int)c == ORD('@')) {
+ } else if (c == ORD('@')) {
/* @x where x is command char */
switch (c = ord(word[slen + 2]) == CHAR ?
ord(word[slen + 3]) : 0) {
@@ -1262,84 +1425,70 @@ varsub(Expand *xp, const char *sp, const char *word,
if (!stype && *word != CSUBST)
return (-1);
- c = ord(sp[0]);
- if ((unsigned int)c == ORD('*') || (unsigned int)c == ORD('@')) {
- switch (stype & STYPE_SINGLE) {
- /* can't assign to a vector */
- case ORD('='):
- /* can't trim a vector (yet) */
- case ORD('%'):
- case ORD('#'):
- case ORD('?'):
- case ORD('0'):
- case ORD('/') | STYPE_AT:
- case ORD('/'):
- case ORD('#') | STYPE_AT:
- case ORD('Q') | STYPE_AT:
+ if (!sc) {
+ xp->var = global(sp);
+ xp->str = str_val(xp->var);
+ /* can't assign things like $! or $1 */
+ if ((stype & STYPE_SINGLE) == ORD('=') &&
+ !*xp->str && ctype(*sp, C_VAR1 | C_DIGIT))
return (-1);
- }
- if (e->loc->argc == 0) {
- xp->str = null;
- xp->var = global(sp);
- state = (unsigned int)c == ORD('@') ? XNULLSUB : XSUB;
- } else {
- xp->u.strv = (const char **)e->loc->argv + 1;
- xp->str = *xp->u.strv++;
- /* $@ */
- xp->split = tobool((unsigned int)c == ORD('@'));
- state = XARG;
- }
- /* POSIX 2009? */
- zero_ok = true;
- } else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
- ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
- XPtrV wv;
-
+ state = XSUB;
+ } else {
+ /* can’t assign/trim a vector (yet) */
switch (stype & STYPE_SINGLE) {
- /* can't assign to a vector */
- case ORD('='):
- /* can't trim a vector (yet) */
- case ORD('%'):
- case ORD('#'):
+ case ORD('-'):
+ case ORD('+'):
+ /* allowed ops */
+ case 0:
+ /* or no ops */
+ break;
+ /* case ORD('='):
case ORD('?'):
- case ORD('0'):
- case ORD('/') | STYPE_AT:
+ case ORD('#'):
+ case ORD('%'):
case ORD('/'):
+ case ORD('/') | STYPE_AT:
+ case ORD('0'):
case ORD('#') | STYPE_AT:
case ORD('Q') | STYPE_AT:
+ */ default:
return (-1);
}
- c = 0;
+ /* do what we can */
+ if (sc & 2) {
+ XPinit(wv, 32);
+ vp = global(arrayname(sp));
+ do {
+ if (vp->flag & ISSET)
+ XPput(wv, str_val(vp));
+ } while ((vp = vp->u.array));
arraynames:
- XPinit(wv, 32);
- vp = global(arrayname(sp));
- for (; vp; vp = vp->u.array) {
- if (!(vp->flag&ISSET))
- continue;
- XPput(wv, (unsigned int)c == ORD('!') ?
- shf_smprintf(Tf_lu, arrayindex(vp)) :
- str_val(vp));
+ if ((c = (XPsize(wv) == 0)))
+ XPfree(wv);
+ else {
+ XPput(wv, NULL);
+ xp->u.strv = (const char **)XPptrv(wv);
+ }
+ } else {
+ if ((c = (e->loc->argc == 0)))
+ xp->var = global(sp);
+ else
+ xp->u.strv = (const char **)e->loc->argv + 1;
+ /* POSIX 2009? */
+ zero_ok = true;
}
- if (XPsize(wv) == 0) {
+ /* have we got any elements? */
+ if (c) {
+ /* no */
xp->str = null;
- state = ord(p[1]) == ORD('@') ? XNULLSUB : XSUB;
- XPfree(wv);
+ state = sc & 4 ? XNULLSUB : XSUB;
} else {
- XPput(wv, 0);
- xp->u.strv = (const char **)XPptrv(wv);
+ /* yes → load first */
xp->str = *xp->u.strv++;
- /* ${foo[@]} */
- xp->split = tobool(ord(p[1]) == ORD('@'));
+ /* $@ or ${foo[@]} */
+ xp->split = tobool(sc & 4);
state = XARG;
}
- } else {
- xp->var = global(sp);
- xp->str = str_val(xp->var);
- /* can't assign things like $! or $1 */
- if ((unsigned int)(stype & STYPE_SINGLE) == ORD('=') &&
- !*xp->str && ctype(*sp, C_VAR1 | C_DIGIT))
- return (-1);
- state = XSUB;
}
c = stype & STYPE_CHAR;
@@ -1348,15 +1497,15 @@ varsub(Expand *xp, const char *sp, const char *word,
(((stype & STYPE_DBL) ? *xp->str == '\0' : xp->str == null) &&
(state != XARG || (ifs0 || xp->split ?
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
- ctype(c, C_EQUAL | C_MINUS | C_QUEST) : (unsigned int)c == ORD('+')))) ||
- (unsigned int)stype == (ORD('0') | STYPE_DBL) ||
- (unsigned int)stype == (ORD('#') | STYPE_AT) ||
- (unsigned int)stype == (ORD('Q') | STYPE_AT) ||
- (unsigned int)(stype & STYPE_CHAR) == ORD('/'))
+ ctype(c, C_EQUAL | C_MINUS | C_QUEST) : c == ORD('+')))) ||
+ stype == (ORD('0') | STYPE_DBL) ||
+ stype == (ORD('#') | STYPE_AT) ||
+ stype == (ORD('Q') | STYPE_AT) ||
+ (stype & STYPE_CHAR) == ORD('/'))
/* expand word instead of variable value */
state = XBASE;
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
- (ctype(c, C_SUB2) || (state != XBASE && (unsigned int)c != ORD('+'))))
+ (ctype(c, C_SUB2) || (state != XBASE && c != ORD('+'))))
errorf(Tf_parm, sp);
*stypep = stype;
*slenp = slen;
@@ -1950,7 +2099,7 @@ valsub(struct op *t, Area *ap)
newenv(E_FUNC);
newblock();
if (ap)
- vp = local("REPLY", false);
+ vp = local(TREPLY, false);
if (!kshsetjmp(e->jbuf))
execute(t, XXCOM | XERROK, NULL);
if (vp)
diff --git a/src/exec.c b/src/exec.c
index 8231b54..9d1b69b 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
- * 2019
+ * 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -24,7 +24,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.206 2019/03/01 16:17:53 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.223 2020/04/07 23:14:41 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@@ -108,8 +108,8 @@ execute(struct op * volatile t,
if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
cp = NULL;
- dp = shf_smprintf(Tf_ss, evalstr(t->vars[0],
- DOASNTILDE | DOSCALAR), rv ? null : cp);
+ strdup2x(dp, evalstr(t->vars[0], DOASNTILDE | DOSCALAR),
+ rv ? null : cp);
typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
/* free the expanded value */
afree(cp, APERM);
@@ -127,7 +127,7 @@ execute(struct op * volatile t,
timex_hook(t, &up);
ap = (const char **)up;
if (ap[0])
- tp = findcom(ap[0], FC_BI|FC_FUNC);
+ tp = findcom(ap[0], FC_BI | FC_FUNC);
}
flags &= ~XTIME;
@@ -446,7 +446,7 @@ execute(struct op * volatile t,
if (rv == ENOEXEC)
scriptexec(t, (const char **)up);
else
- errorf(Tf_sD_s, t->str, cstrerror(rv));
+ errorfx(126, Tf_sD_s, t->str, cstrerror(rv));
}
Break:
exstat = rv & 0xFF;
@@ -464,14 +464,9 @@ execute(struct op * volatile t,
unwind(LEXIT);
if (rv != 0 && !(flags & XERROK) &&
(xerrok == NULL || !*xerrok)) {
- if (Flag(FERREXIT) & 0x80) {
- /* inside eval */
- Flag(FERREXIT) = 0;
- } else {
- trapsig(ksh_SIGERR);
- if (Flag(FERREXIT))
- unwind(LERROR);
- }
+ trapsig(ksh_SIGERR);
+ if (Flag(FERREXIT))
+ unwind(LERREXT);
}
return (rv);
}
@@ -492,7 +487,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
static struct op texec;
int type_flags;
bool resetspec;
- int fcflags = FC_BI|FC_FUNC|FC_PATH;
+ int fcflags = FC_BI | FC_FUNC | FC_PATH;
struct block *l_expand, *l_assign;
int optc;
const char *exec_argv0 = NULL;
@@ -529,7 +524,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
resetspec = false;
while (tp && tp->type == CSHELL) {
/* undo effects of command */
- fcflags = FC_BI|FC_FUNC|FC_PATH;
+ fcflags = FC_BI | FC_FUNC | FC_PATH;
if (tp->val.f == c_builtin) {
if ((cp = *++ap) == NULL ||
(!strcmp(cp, "--") && (cp = *++ap) == NULL)) {
@@ -578,7 +573,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
/* command -vV or something */
break;
/* don't look for functions */
- fcflags = FC_BI|FC_PATH;
+ fcflags = FC_BI | FC_PATH;
if (saw_p) {
if (Flag(FRESTRICTED)) {
warningf(true, Tf_sD_s,
@@ -608,8 +603,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
(tp->flag & LOWER_BI)) {
struct tbl *ext_cmd;
- ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC);
- if (ext_cmd && (ext_cmd->type != CTALIAS ||
+ ext_cmd = findcom(tp->name, FC_FUNC | FC_PATH);
+ if (ext_cmd && (ext_cmd->type == CFUNC ||
(ext_cmd->flag & ISSET)))
tp = ext_cmd;
}
@@ -619,7 +614,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
break;
} else
break;
- tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
+ tp = findcom(ap[0], fcflags & (FC_BI | FC_FUNC));
}
if (t->u.evalflags & DOTCOMEXEC)
flags |= XEXEC;
@@ -629,12 +624,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
else {
/* create new variable/function block */
newblock();
- /* ksh functions don't keep assignments, POSIX functions do. */
- if (!resetspec && tp && tp->type == CFUNC &&
- !(tp->flag & FKSH))
- type_flags = EXPORT;
- else
- type_flags = LOCAL|LOCAL_COPY|EXPORT;
+ /* all functions keep assignments */
+ type_flags = LOCAL | LOCAL_COPY | EXPORT;
}
l_assign = e->loc;
if (exec_clrenv)
@@ -694,11 +685,9 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
/* shell built-in */
case CSHELL:
do_call_builtin:
+ if (l_expand != l_assign)
+ l_assign->flags |= (tp->flag & NEXTLOC_BI);
rv = call_builtin(tp, (const char **)ap, null, resetspec);
- if (resetspec && tp->val.f == c_shift) {
- l_expand->argc = l_assign->argc;
- l_expand->argv = l_assign->argv;
- }
break;
/* function call */
@@ -804,6 +793,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
switch (i) {
case LRETURN:
case LERROR:
+ case LERREXT:
rv = exstat & 0xFF;
break;
case LINTR:
@@ -1147,6 +1137,10 @@ builtin(const char *name, int (*func) (const char **))
/* is declaration utility (POSIX: export, readonly) */
flag |= DECL_UTIL;
break;
+ case '#':
+ /* is set or shift */
+ flag |= NEXTLOC_BI;
+ break;
default:
goto flags_seen;
}
@@ -1309,10 +1303,10 @@ search_access(const char *fn, int mode)
}
#ifdef __OS2__
-/* check if path is something we want to find, adding executable extensions */
-#define search_access(fn, mode) access_ex((search_access), (fn), (mode))
+/* check if path is something we want to find adding executable extensions */
+#define search_access(fn,mode) access_ex((search_access), (fn), (mode))
#else
-#define search_access(fn, mode) (search_access)((fn), (mode))
+#define search_access(fn,mode) (search_access)((fn), (mode))
#endif
/*
@@ -1605,7 +1599,7 @@ hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
return (-2);
}
if (iop->ioflag & IOHERESTR) {
- ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC);
+ ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR);
} else if (sub) {
/* do substitutions on the content of heredoc */
s = pushs(SSTRING, ATEMP);
@@ -1682,7 +1676,7 @@ static const char *
do_selectargs(const char **ap, bool print_menu)
{
static const char *read_args[] = {
- Tread, "-r", "REPLY", NULL
+ Tread, Tdr, TREPLY, NULL
};
char *s;
int i, argct;
@@ -1696,13 +1690,13 @@ do_selectargs(const char **ap, bool print_menu)
* - the user enters a blank line
* - the REPLY parameter is empty
*/
- if (print_menu || !*str_val(global("REPLY")))
+ if (print_menu || !*str_val(global(TREPLY)))
pr_menu(ap);
shellf(Tf_s, str_val(global("PS3")));
if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect,
false))
return (NULL);
- if (*(s = str_val(global("REPLY"))))
+ if (*(s = str_val(global(TREPLY))))
return ((getn(s, &i) && i >= 1 && i <= argct) ?
ap[i - 1] : null);
print_menu = true;
@@ -1861,8 +1855,11 @@ dbteste_getopnd(Test_env *te, Test_op op, bool do_eval)
if (!do_eval)
return (null);
- if (op == TO_STEQL || op == TO_STNEQ)
+ if (op == TO_STEQL || op == TO_STNEQ) {
flags |= DOPAT;
+ if (!Flag(FSH))
+ flags |= DODBMAGIC;
+ }
return (evalstr(s, flags));
}
diff --git a/src/expr.c b/src/expr.c
index 5d2c869..6764279 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2016, 2017, 2018
+ * 2011, 2012, 2013, 2014, 2016, 2017, 2018, 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.105 2018/08/10 02:53:33 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.107 2020/03/27 02:49:40 tg Exp $");
#define EXPRTOK_DEFNS
#include "exprtok.h"
@@ -864,7 +864,8 @@ ksh_access(const char *fn, int mode)
int rv;
struct stat sb;
- if ((rv = access(fn, mode)) == 0 && kshuid == 0 && (mode & X_OK) &&
+ if ((rv = access(fn, mode)) == 0 && (mode & X_OK) &&
+ (kshuid == 0 || ksheuid == 0) &&
(rv = stat(fn, &sb)) == 0 && !S_ISDIR(sb.st_mode) &&
(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
rv = -1;
@@ -885,8 +886,43 @@ static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems,
unsigned int val) MKSH_A_PURE;
/*
- * Generated from the UCD 11.0.0 by
- * MirOS: contrib/code/Snippets/eawparse,v 1.12 2017/09/06 16:05:45 tg Exp $
+ * Generated from the UCD 13.0.0 by
+ * MirOS: contrib/code/Snippets/eawparse,v 1.14 2020/03/27 01:33:21 tg Exp $
+ */
+
+/*-
+ * Parts Copyright © 1991–2020 Unicode, Inc. All rights reserved.
+ * Distributed under the Terms of Use in
+ * https://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation
+ * (the "Data Files") or Unicode software and any associated documentation
+ * (the "Software") to deal in the Data Files or Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, and/or sell copies of
+ * the Data Files or Software, and to permit persons to whom the Data Files
+ * or Software are furnished to do so, provided that either
+ * (a) this copyright and permission notice appear with all copies
+ * of the Data Files or Software, or
+ * (b) this copyright and permission notice appear in associated
+ * Documentation.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+ * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in these Data Files or Software without prior
+ * written authorization of the copyright holder.
*/
static const struct mb_ucsrange mb_ucs_combining[] = {
@@ -949,7 +985,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
{ 0x0B3F, 0x0B3F },
{ 0x0B41, 0x0B44 },
{ 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 },
+ { 0x0B55, 0x0B56 },
{ 0x0B62, 0x0B63 },
{ 0x0B82, 0x0B82 },
{ 0x0BC0, 0x0BC0 },
@@ -972,6 +1008,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
{ 0x0D41, 0x0D44 },
{ 0x0D4D, 0x0D4D },
{ 0x0D62, 0x0D63 },
+ { 0x0D81, 0x0D81 },
{ 0x0DCA, 0x0DCA },
{ 0x0DD2, 0x0DD4 },
{ 0x0DD6, 0x0DD6 },
@@ -979,8 +1016,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
{ 0x0E34, 0x0E3A },
{ 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 },
- { 0x0EB4, 0x0EB9 },
- { 0x0EBB, 0x0EBC },
+ { 0x0EB4, 0x0EBC },
{ 0x0EC8, 0x0ECD },
{ 0x0F18, 0x0F19 },
{ 0x0F35, 0x0F35 },
@@ -1030,7 +1066,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
{ 0x1A65, 0x1A6C },
{ 0x1A73, 0x1A7C },
{ 0x1A7F, 0x1A7F },
- { 0x1AB0, 0x1ABE },
+ { 0x1AB0, 0x1AC0 },
{ 0x1B00, 0x1B03 },
{ 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A },
@@ -1073,6 +1109,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
{ 0xA806, 0xA806 },
{ 0xA80B, 0xA80B },
{ 0xA825, 0xA826 },
+ { 0xA82C, 0xA82C },
{ 0xA8C4, 0xA8C5 },
{ 0xA8E0, 0xA8F1 },
{ 0xA8FF, 0xA8FF },
@@ -1081,7 +1118,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
{ 0xA980, 0xA982 },
{ 0xA9B3, 0xA9B3 },
{ 0xA9B6, 0xA9B9 },
- { 0xA9BC, 0xA9BC },
+ { 0xA9BC, 0xA9BD },
{ 0xA9E5, 0xA9E5 },
{ 0xAA29, 0xAA2E },
{ 0xAA31, 0xAA32 },
diff --git a/src/exprtok.h b/src/exprtok.h
index e4329da..e98e51f 100644
--- a/src/exprtok.h
+++ b/src/exprtok.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2016
+ * Copyright (c) 2016, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -19,25 +19,25 @@
*/
#if defined(EXPRTOK_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.4 2020/04/07 11:56:46 tg Exp $");
/* see range comment below */
#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
-#define FN(name, len, prec, enum) /* nothing */
+#define FN(name,len,prec,enum) /* nothing */
#define F1(enum) /* nothing */
#elif defined(EXPRTOK_ENUM)
-#define F0(name, len, prec, enum) enum = 0,
-#define FN(name, len, prec, enum) enum,
+#define F0(name,len,prec,enum) enum = 0,
+#define FN(name,len,prec,enum) enum,
#define F1(enum) enum,
#define F2(enum) enum,
#define F9(enum) enum
#elif defined(EXPRTOK_NAME)
-#define FN(name, len, prec, enum) name,
+#define FN(name,len,prec,enum) name,
#define F1(enum) ""
#elif defined(EXPRTOK_LEN)
-#define FN(name, len, prec, enum) len,
+#define FN(name,len,prec,enum) len,
#define F1(enum) 0
#elif defined(EXPRTOK_PREC)
-#define FN(name, len, prec, enum) prec,
+#define FN(name,len,prec,enum) prec,
#define F1(enum) P_PRIMARY
#endif
@@ -53,7 +53,7 @@ __RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
/* tokens must be ordered so the longest are first (e.g. += before +) */
/* some (long) unary operators */
-FN("++", 2, P_PRIMARY, O_PLUSPLUS = 0) /* before + */
+F0("++", 2, P_PRIMARY, O_PLUSPLUS) /* before + */
FN("--", 2, P_PRIMARY, O_MINUSMINUS) /* before - */
/* binary operators */
FN("==", 2, P_EQUALITY, O_EQ) /* before = */
diff --git a/src/funcs.c b/src/funcs.c
index ade00d6..a5cfc69 100644
--- a/src/funcs.c
+++ b/src/funcs.c
@@ -5,7 +5,8 @@
/*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
+ * 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -38,7 +39,7 @@
#endif
#endif
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.355 2018/10/20 21:04:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.372 2020/04/13 19:51:07 tg Exp $");
#if HAVE_KILLPG
/*
@@ -119,8 +120,6 @@ const struct builtin mkshbuiltins[] = {
{Tfalse, c_false},
{"fc", c_fc},
{Tgetopts, c_getopts},
- /* deprecated, replaced by typeset -g */
- {"^=global", c_typeset},
{Tjobs, c_jobs},
{"kill", c_kill},
{"let", c_let},
@@ -133,8 +132,8 @@ const struct builtin mkshbuiltins[] = {
#endif
{"~rename", c_rename},
{"*=return", c_exitreturn},
- {Tsgset, c_set},
- {"*=shift", c_shift},
+ {Tsghset, c_set},
+ {"*=#shift", c_shift},
{Tgsource, c_dot},
#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
{Tsuspend, c_suspend},
@@ -183,11 +182,8 @@ struct kill_info {
int name_width;
};
-static const struct t_op {
- char op_text[4];
- Test_op op_num;
-} u_ops[] = {
- {"-a", TO_FILAXST },
+const struct t_op u_ops[] = {
+/* 0*/ {"-a", TO_FILAXST },
{"-b", TO_FILBDEV },
{"-c", TO_FILCDEV },
{"-d", TO_FILID },
@@ -199,22 +195,23 @@ static const struct t_op {
{"-h", TO_FILSYM },
{"-k", TO_FILSTCK },
{"-L", TO_FILSYM },
- {"-n", TO_STNZE },
+/*12*/ {"-n", TO_STNZE },
{"-O", TO_FILUID },
- {"-o", TO_OPTION },
+/*14*/ {"-o", TO_OPTION },
{"-p", TO_FILFIFO },
- {"-r", TO_FILRD },
+/*16*/ {"-r", TO_FILRD },
{"-S", TO_FILSOCK },
{"-s", TO_FILGZ },
{"-t", TO_FILTT },
- {"-u", TO_FILSETU },
+/*20*/ {"-u", TO_FILSETU },
{"-v", TO_ISSET },
{"-w", TO_FILWR },
- {"-x", TO_FILEX },
+/*23*/ {"-x", TO_FILEX },
{"-z", TO_STZER },
{"", TO_NONOP }
};
-static const struct t_op b_ops[] = {
+cta(u_ops_size, NELEM(u_ops) == 26);
+const struct t_op b_ops[] = {
{"=", TO_STEQL },
{"==", TO_STEQL },
{"!=", TO_STNEQ },
@@ -337,7 +334,7 @@ c_print(const char **wp)
/* BSD "echo" cmd, Debian Policy 10.4 compliant */
++wp;
bsd_echo:
- if (*wp && !strcmp(*wp, "-n")) {
+ if (*wp && !strcmp(*wp, Tdn)) {
po.nl = false;
++wp;
}
@@ -724,12 +721,16 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
}
break;
case CALIAS:
- if (vflag) {
- shprintf("%s is an %s%s for ", id,
+ if (!vflag && iscommand)
+ shprintf(Tf_s_, Talias);
+ if (vflag || iscommand)
+ print_value_quoted(shl_stdout, id);
+ if (vflag)
+ shprintf(" is an %s%s for ",
(tp->flag & EXPORT) ? "exported " : "",
Talias);
- } else if (iscommand)
- shprintf("%s %s=", Talias, id);
+ else if (iscommand)
+ shf_putc('=', shl_stdout);
print_value_quoted(shl_stdout, tp->val.s);
break;
case CKEYWD:
@@ -751,10 +752,15 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
bool
valid_alias_name(const char *cp)
{
- if (ord(*cp) == ORD('-'))
- return (false);
- if (ord(cp[0]) == ORD('[') && ord(cp[1]) == ORD('[') && !cp[2])
+ switch (ord(*cp)) {
+ case ORD('+'):
+ case ORD('-'):
return (false);
+ case ORD('['):
+ if (ord(cp[1]) == ORD('[') && !cp[2])
+ return (false);
+ break;
+ }
while (*cp)
if (ctype(*cp, C_ALIAS))
++cp;
@@ -843,7 +849,7 @@ c_alias(const char **wp)
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
if (pflag)
shprintf(Tf_s_, Talias);
- shf_puts(ap->name, shl_stdout);
+ print_value_quoted(shl_stdout, ap->name);
if (prefix != '+') {
shf_putc('=', shl_stdout);
print_value_quoted(shl_stdout, ap->val.s);
@@ -873,7 +879,7 @@ c_alias(const char **wp)
if (ap != NULL && (ap->flag&ISSET)) {
if (pflag)
shprintf(Tf_s_, Talias);
- shf_puts(ap->name, shl_stdout);
+ print_value_quoted(shl_stdout, ap->name);
if (prefix != '+') {
shf_putc('=', shl_stdout);
print_value_quoted(shl_stdout, ap->val.s);
@@ -1297,54 +1303,32 @@ c_bind(const char **wp)
#ifndef MKSH_SMALL
bool macro = false;
#endif
- bool list = false;
- const char *cp;
- char *up;
- while ((optc = ksh_getopt(wp, &builtin_opt,
-#ifndef MKSH_SMALL
- "lm"
-#else
- "l"
-#endif
- )) != -1)
+ if (x_bind_check()) {
+ bi_errorf("can't bind, not a tty");
+ return (1);
+ }
+
+ while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != -1)
switch (optc) {
case 'l':
- list = true;
- break;
+ return (x_bind_list());
#ifndef MKSH_SMALL
case 'm':
macro = true;
break;
#endif
- case '?':
+ default:
return (1);
}
wp += builtin_opt.optind;
if (*wp == NULL)
- /* list all */
- rv = x_bind(NULL, NULL,
-#ifndef MKSH_SMALL
- false,
-#endif
- list);
+ return (x_bind_showall());
- for (; *wp != NULL; wp++) {
- if ((cp = cstrchr(*wp, '=')) == NULL)
- up = NULL;
- else {
- strdupx(up, *wp, ATEMP);
- up[cp++ - *wp] = '\0';
- }
- if (x_bind(up ? up : *wp, cp,
-#ifndef MKSH_SMALL
- macro,
-#endif
- false))
- rv = 1;
- afree(up, ATEMP);
- }
+ do {
+ rv |= x_bind(*wp SMALLP(macro));
+ } while (*++wp);
return (rv);
}
@@ -1353,10 +1337,17 @@ c_bind(const char **wp)
int
c_shift(const char **wp)
{
- struct block *l = e->loc;
int n;
mksh_ari_t val;
const char *arg;
+ struct block *l = e->loc;
+
+ if ((l->flags & BF_RESETSPEC)) {
+ /* prevent pollution */
+ l->flags &= ~BF_RESETSPEC;
+ /* operate on parent environment */
+ l = l->next;
+ }
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return (1);
@@ -1375,6 +1366,7 @@ c_shift(const char **wp)
bi_errorf(Tf_sD_s, Tbadnum, arg);
return (1);
}
+
if (l->argc < n) {
bi_errorf("nothing to shift");
return (1);
@@ -1589,7 +1581,6 @@ c_wait(const char **wp)
return (rv);
}
-static const char REPLY[] = "REPLY";
int
c_read(const char **wp)
{
@@ -1670,7 +1661,7 @@ c_read(const char **wp)
if (!builtin_opt.optarg[0])
fd = 0;
else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
- bi_errorf(Tf_sD_sD_s, "-u", builtin_opt.optarg, ccp);
+ bi_errorf(Tf_sD_sD_s, Tdu, builtin_opt.optarg, ccp);
return (2);
}
break;
@@ -1679,7 +1670,7 @@ c_read(const char **wp)
}
wp += builtin_opt.optind;
if (*wp == NULL)
- *--wp = REPLY;
+ *--wp = TREPLY;
if (intoarray && wp[1] != NULL) {
bi_errorf(Ttoo_many_args);
@@ -2029,7 +2020,6 @@ int
c_eval(const char **wp)
{
struct source *s, *saves = source;
- unsigned char savef;
int rv;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
@@ -2072,10 +2062,7 @@ c_eval(const char **wp)
/* SUSv4: OR with a high value never written otherwise */
exstat |= 0x4000;
- savef = Flag(FERREXIT);
- Flag(FERREXIT) |= 0x80;
rv = shell(s, 2);
- Flag(FERREXIT) = savef;
source = saves;
afree(s, ATEMP);
if (exstat & 0x4000)
@@ -2237,7 +2224,13 @@ c_set(const char **wp)
int argi;
bool setargs;
struct block *l = e->loc;
- const char **owp;
+
+ if ((l->flags & BF_RESETSPEC)) {
+ /* prevent pollution */
+ l->flags &= ~BF_RESETSPEC;
+ /* operate on parent environment */
+ l = l->next;
+ }
if (wp[1] == NULL) {
static const char *args[] = { Tset, "-", NULL };
@@ -2248,6 +2241,8 @@ c_set(const char **wp)
return (2);
/* set $# and $* */
if (setargs) {
+ const char **owp;
+
wp += argi - 1;
owp = wp;
/* save $0 */
@@ -2579,25 +2574,25 @@ c_mknod(const char **wp)
#endif
/*-
- test(1) roughly accepts the following grammar:
- oexpr ::= aexpr | aexpr "-o" oexpr ;
- aexpr ::= nexpr | nexpr "-a" aexpr ;
- nexpr ::= primary | "!" nexpr ;
- primary ::= unary-operator operand
- | operand binary-operator operand
- | operand
- | "(" oexpr ")"
- ;
-
- unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
- "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
- "-u"|"-v"|"-w"|"-x"|"-z";
-
- binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
- "-lt"|"-le"|"-ef"|"-nt"|"-ot";
-
- operand ::= <anything>
-*/
+ * test(1) roughly accepts the following grammar:
+ * oexpr ::= aexpr | aexpr "-o" oexpr ;
+ * aexpr ::= nexpr | nexpr "-a" aexpr ;
+ * nexpr ::= primary | "!" nexpr ;
+ * primary ::= unary-operator operand
+ * | operand binary-operator operand
+ * | operand
+ * | "(" oexpr ")"
+ * ;
+ *
+ * unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
+ * "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
+ * "-u"|"-v"|"-w"|"-x"|"-z";
+ *
+ * binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
+ * "-lt"|"-le"|"-ef"|"-nt"|"-ot";
+ *
+ * operand ::= <anything>
+ */
/* POSIX says > 1 for errors */
#define T_ERR_EXIT 2
@@ -2749,12 +2744,35 @@ test_isop(Test_meta meta, const char *s)
}
#ifdef __OS2__
-#define test_access(name, mode) access_ex(access, (name), (mode))
-#define test_stat(name, buffer) stat_ex((name), (buffer))
+#define test_access(name,mode) access_ex(access, (name), (mode))
+#define test_stat(name,buffer) stat_ex(stat, (name), (buffer))
+#define test_lstat(name,buffer) stat_ex(lstat, (name), (buffer))
#else
-#define test_access(name, mode) access((name), (mode))
-#define test_stat(name, buffer) stat((name), (buffer))
+#define test_access(name,mode) access((name), (mode))
+#define test_stat(name,buffer) stat((name), (buffer))
+#define test_lstat(name,buffer) lstat((name), (buffer))
+#endif
+
+#if HAVE_ST_MTIM
+#undef st_mtimensec
+#define st_mtimensec st_mtim.tv_nsec
+#endif
+
+static int
+mtimecmp(const struct stat *sb1, const struct stat *sb2)
+{
+ if (sb1->st_mtime < sb2->st_mtime)
+ return (-1);
+ if (sb1->st_mtime > sb2->st_mtime)
+ return (1);
+#if (HAVE_ST_MTIMENSEC || HAVE_ST_MTIM)
+ if (sb1->st_mtimensec < sb2->st_mtimensec)
+ return (-1);
+ if (sb1->st_mtimensec > sb2->st_mtimensec)
+ return (1);
#endif
+ return (0);
+}
int
test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
@@ -2849,31 +2867,31 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -d */
case TO_FILID:
- return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
/* -c */
case TO_FILCDEV:
- return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
/* -b */
case TO_FILBDEV:
- return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
/* -p */
case TO_FILFIFO:
- return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
/* -h or -L */
case TO_FILSYM:
#ifdef MKSH__NO_SYMLINK
return (0);
#else
- return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
+ return (test_lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
#endif
/* -S */
case TO_FILSOCK:
- return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
/* -H => HP context dependent files (directories) */
case TO_FILCDF:
@@ -2892,7 +2910,7 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
*/
nv = shf_smprintf("%s+", opnd1);
- i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
+ i = (test_stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
afree(nv, ATEMP);
return (i);
}
@@ -2902,18 +2920,18 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -u */
case TO_FILSETU:
- return (stat(opnd1, &b1) == 0 &&
+ return (test_stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISUID) == S_ISUID);
/* -g */
case TO_FILSETG:
- return (stat(opnd1, &b1) == 0 &&
+ return (test_stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISGID) == S_ISGID);
/* -k */
case TO_FILSTCK:
#ifdef S_ISVTX
- return (stat(opnd1, &b1) == 0 &&
+ return (test_stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISVTX) == S_ISVTX);
#else
return (0);
@@ -2921,7 +2939,8 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -s */
case TO_FILGZ:
- return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
+ return (test_stat(opnd1, &b1) == 0 &&
+ (off_t)b1.st_size > (off_t)0);
/* -t */
case TO_FILTT:
@@ -2934,11 +2953,13 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -O */
case TO_FILUID:
- return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
+ return (test_stat(opnd1, &b1) == 0 &&
+ (uid_t)b1.st_uid == ksheuid);
/* -G */
case TO_FILGID:
- return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
+ return (test_stat(opnd1, &b1) == 0 &&
+ (gid_t)b1.st_gid == kshegid);
/*
* Binary Operators
@@ -2976,9 +2997,9 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
* ksh88/ksh93 succeed if file2 can't be stated
* (subtly different from 'does not exist').
*/
- return (stat(opnd1, &b1) == 0 &&
- (((s = stat(opnd2, &b2)) == 0 &&
- b1.st_mtime > b2.st_mtime) || s < 0));
+ return (test_stat(opnd1, &b1) == 0 &&
+ (((s = test_stat(opnd2, &b2)) == 0 &&
+ mtimecmp(&b1, &b2) > 0) || s < 0));
/* -ot */
case TO_FILOT:
@@ -2986,13 +3007,14 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
* ksh88/ksh93 succeed if file1 can't be stated
* (subtly different from 'does not exist').
*/
- return (stat(opnd2, &b2) == 0 &&
- (((s = stat(opnd1, &b1)) == 0 &&
- b1.st_mtime < b2.st_mtime) || s < 0));
+ return (test_stat(opnd2, &b2) == 0 &&
+ (((s = test_stat(opnd1, &b1)) == 0 &&
+ mtimecmp(&b1, &b2) < 0) || s < 0));
/* -ef */
case TO_FILEQ:
- return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
+ return (test_stat(opnd1, &b1) == 0 &&
+ test_stat(opnd2, &b2) == 0 &&
b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
/* all other cases */
@@ -3155,7 +3177,7 @@ ptest_isa(Test_env *te, Test_meta meta)
{
/* Order important - indexed by Test_meta values */
static const char * const tokens[] = {
- "-o", "-a", "!", "(", ")"
+ Tdo, Tda, "!", "(", ")"
};
Test_op rv;
@@ -3469,7 +3491,7 @@ c_cat(const char **wp)
#define MKSH_CAT_BUFSIZ 4096
/* parse options: POSIX demands we support "-u" as no-op */
- while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
+ while ((rv = ksh_getopt(wp, &builtin_opt, Tu)) != -1) {
switch (rv) {
case 'u':
/* we already operate unbuffered */
diff --git a/src/histrap.c b/src/histrap.c
index 985b2a3..84211c6 100644
--- a/src/histrap.c
+++ b/src/histrap.c
@@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2014, 2015, 2016, 2017, 2018
+ * 2011, 2012, 2014, 2015, 2016, 2017, 2018, 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -27,7 +27,7 @@
#include <sys/file.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.167 2018/04/28 17:16:54 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.169 2019/09/16 21:10:33 tg Exp $");
Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign;
@@ -504,6 +504,8 @@ findhist(int start, int fwd, const char *str, bool anchored)
void
sethistsize(mksh_ari_t n)
{
+ if (n > 65535)
+ n = 65535;
if (n > 0 && n != histsize) {
int cursize = histptr - history;
diff --git a/src/jobs.c b/src/jobs.c
index 66e31aa..8663264 100644
--- a/src/jobs.c
+++ b/src/jobs.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- * 2012, 2013, 2014, 2015, 2016, 2018
+ * 2012, 2013, 2014, 2015, 2016, 2018, 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.127 2018/07/15 16:23:10 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.128 2019/12/11 19:46:20 tg Exp $");
#if HAVE_KILLPG
#define mksh_killpg killpg
@@ -88,6 +88,7 @@ struct proc {
typedef struct job Job;
struct job {
+ ALLOC_ITEM alloc_INT; /* internal, do not touch */
Job *next; /* next job in list */
Proc *proc_list; /* process list */
Proc *last_proc; /* last process in list */
@@ -1778,16 +1779,26 @@ new_job(void)
if (free_jobs != NULL) {
newj = free_jobs;
free_jobs = free_jobs->next;
- } else
- newj = alloc(sizeof(Job), APERM);
+ } else {
+ char *cp;
- /* brute force method */
- for (i = 1; ; i++) {
- for (j = job_list; j && j->job != i; j = j->next)
- ;
- if (j == NULL)
- break;
+ /*
+ * struct job includes ALLOC_ITEM for alignment constraints
+ * so first get the actually used memory, then assign it
+ */
+ cp = alloc(sizeof(Job) - sizeof(ALLOC_ITEM), APERM);
+ /* undo what alloc() did to the malloc result address */
+ newj = (void *)(cp - sizeof(ALLOC_ITEM));
}
+
+ /* brute force method */
+ i = 0;
+ do {
+ ++i;
+ j = job_list;
+ while (j && j->job != i)
+ j = j->next;
+ } while (j);
newj->job = i;
return (newj);
diff --git a/src/lex.c b/src/lex.c
index 8c75a98..21c2f35 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.250 2018/10/20 18:34:14 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.251 2020/03/10 23:48:40 tg Exp $");
/*
* states while lexing word
@@ -451,8 +451,7 @@ yylex(int cf)
statep->ls_adelim.num = 1;
statep->nparen = 0;
break;
- } else if (ctype(c, C_DIGIT | C_DOLAR | C_SPC) ||
- /*XXX what else? */
+ } else if (ctype(c, C_ALNUX | C_DOLAR | C_SPC) ||
c == '(' /*)*/) {
/* substring subst. */
if (c != ' ') {
diff --git a/src/main.c b/src/main.c
index 41dd6da..3d440dd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -6,7 +6,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
- * 2019
+ * 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -35,7 +35,7 @@
#include <locale.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.351 2019/01/05 13:24:18 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.364 2020/04/13 17:04:14 tg Exp $");
#ifndef MKSHRC_PATH
#define MKSHRC_PATH "~/.mkshrc"
@@ -65,8 +65,8 @@ static const char initsubs[] =
"${EPOCHREALTIME=}";
static const char *initcoms[] = {
- Ttypeset, "-r", initvsn, NULL,
- Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
+ Ttypeset, Tdr, initvsn, NULL,
+ Ttypeset, Tdx, "HOME", TPATH, TSHELL, NULL,
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias,
"integer=\\\\builtin typeset -i",
@@ -90,7 +90,7 @@ static const char *initcoms[] = {
};
static const char *restr_com[] = {
- Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
+ Ttypeset, Tdr, TPATH, TENV, TSHELL, NULL
};
static bool initio_done;
@@ -100,7 +100,6 @@ static struct env env;
struct env *e = &env;
/* compile-time assertions */
-#define cta(name, expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
/* this one should be defined by the standard */
cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
@@ -227,7 +226,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
int argi, i;
Source *s = NULL;
struct block *l;
- unsigned char restricted_shell, errexit, utf_flag;
+ unsigned char restricted_shell = 0, errexit, utf_flag;
char *cp;
const char *ccp, **wp;
struct tbl *vp;
@@ -303,7 +302,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
/* define built-in commands and see if we were called as one */
ktinit(APERM, &builtins,
- /* currently up to 54 builtins: 75% of 128 = 2^7 */
+ /* currently up to 52 builtins: 75% of 128 = 2^7 */
7);
for (i = 0; mkshbuiltins[i].name != NULL; i++)
if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
@@ -315,7 +314,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
argi = parse_args(argv, OF_FIRSTTIME, NULL);
if (argi < 0)
return (1);
-
+ /* called as rsh, rmksh, -rsh, -rmksh, etc.? */
+ if (ord(*ccp) == ORD('r')) {
+ ++ccp;
+ ++restricted_shell;
+ }
#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
/* are we called as -sh or /bin/sh or so? */
if (!strcmp(ccp, "sh" MKSH_EXE_EXT)) {
@@ -642,7 +645,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
}
/* Disable during .profile/ENV reading */
- restricted_shell = Flag(FRESTRICTED);
+ restricted_shell |= Flag(FRESTRICTED);
Flag(FRESTRICTED) = 0;
errexit = Flag(FERREXIT);
Flag(FERREXIT) = 0;
@@ -656,7 +659,16 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
if (Flag(FLOGIN))
include(MKSH_SYSTEM_PROFILE, 0, NULL, true);
- if (!Flag(FPRIVILEGED)) {
+ if (Flag(FPRIVILEGED)) {
+ include(MKSH_SUID_PROFILE, 0, NULL, true);
+ /* note whether -p was enabled during startup */
+ if (Flag(FPRIVILEGED) == 1)
+ /* allow set -p to setuid() later */
+ Flag(FPRIVILEGED) = 3;
+ else
+ /* turn off -p if not set explicitly */
+ change_flag(FPRIVILEGED, OF_INTERNAL, false);
+ } else {
if (Flag(FLOGIN))
include(substitute("$HOME/.profile", 0), 0, NULL, true);
if (Flag(FTALKING)) {
@@ -664,13 +676,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
if (cp[0] != '\0')
include(cp, 0, NULL, true);
}
- } else {
- include(MKSH_SUID_PROFILE, 0, NULL, true);
- /* turn off -p if not set explicitly */
- if (Flag(FPRIVILEGED) != 1)
- change_flag(FPRIVILEGED, OF_INTERNAL, false);
}
-
if (restricted_shell) {
c_builtin(restr_com);
/* After typeset command... */
@@ -741,6 +747,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
switch (i) {
case LRETURN:
case LERROR:
+ case LERREXT:
/* see below */
return (exstat & 0xFF);
case LINTR:
@@ -808,6 +815,8 @@ shell(Source * volatile s, volatile int level)
int i;
newenv(level == 2 ? E_EVAL : E_PARSE);
+ if (level == 2)
+ e->flags |= EF_IN_EVAL;
if (interactive)
really_exit = false;
switch ((i = kshsetjmp(e->jbuf))) {
@@ -815,18 +824,21 @@ shell(Source * volatile s, volatile int level)
break;
case LBREAK:
case LCONTIN:
- if (level != 2) {
- source = old_source;
- quitenv(NULL);
- internal_errorf(Tf_cant_s, Tshell,
- i == LBREAK ? Tbreak : Tcontinue);
+ /* assert: interactive == false */
+ source = old_source;
+ quitenv(NULL);
+ if (level == 2) {
+ /* keep on going */
+ unwind(i);
/* NOTREACHED */
}
- /* assert: interactive == false */
- /* FALLTHROUGH */
+ internal_errorf(Tf_cant_s, Tshell,
+ i == LBREAK ? Tbreak : Tcontinue);
+ /* NOTREACHED */
case LINTR:
/* we get here if SIGINT not caught or ignored */
case LERROR:
+ case LERREXT:
case LSHELL:
if (interactive) {
if (i == LINTR)
@@ -857,6 +869,8 @@ shell(Source * volatile s, volatile int level)
case LRETURN:
source = old_source;
quitenv(NULL);
+ if (i == LERREXT && level == 2)
+ return (exstat & 0xFF);
/* keep on going */
unwind(i);
/* NOTREACHED */
@@ -916,8 +930,8 @@ shell(Source * volatile s, volatile int level)
source_no_tree:
reclaim();
}
- quitenv(NULL);
source = old_source;
+ quitenv(NULL);
return (exstat & 0xFF);
}
@@ -926,36 +940,25 @@ shell(Source * volatile s, volatile int level)
void
unwind(int i)
{
- /*
- * This is a kludge. We need to restore everything that was
- * changed in the new environment, see cid 1005090337C7A669439
- * and 10050903386452ACBF1, but fail to even save things most of
- * the time. funcs.c:c_eval() changes FERREXIT temporarily to 0,
- * which needs to be restored thus (related to Debian #696823).
- * We did not save the shell flags, so we use a special or'd
- * value here... this is mostly to clean up behind *other*
- * callers of unwind(LERROR) here; exec.c has the regular case.
- */
- if (Flag(FERREXIT) & 0x80) {
- /* GNU bash does not run this trapsig */
- trapsig(ksh_SIGERR);
- Flag(FERREXIT) &= ~0x80;
- }
+ /* during eval, skip FERREXIT trap */
+ if (i == LERREXT && (e->flags & EF_IN_EVAL))
+ goto defer_traps;
/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
- if (i == LEXIT || ((i == LERROR || i == LINTR) &&
+ if (i == LEXIT || ((i == LERROR || i == LERREXT || i == LINTR) &&
sigtraps[ksh_SIGEXIT].trap &&
(!Flag(FTALKING) || Flag(FERREXIT)))) {
++trap_nested;
runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
--trap_nested;
i = LLEAVE;
- } else if (Flag(FERREXIT) == 1 && (i == LERROR || i == LINTR)) {
+ } else if (Flag(FERREXIT) && (i == LERROR || i == LERREXT || i == LINTR)) {
++trap_nested;
runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
--trap_nested;
i = LLEAVE;
}
+ defer_traps:
while (/* CONSTCOND */ 1) {
switch (e->type) {
@@ -998,8 +1001,7 @@ newenv(int type)
ep->temps = NULL;
ep->yyrecursive_statep = NULL;
ep->type = type;
- ep->flags = 0;
- /* jump buffer is invalid because flags == 0 */
+ ep->flags = e->flags & EF_IN_EVAL;
e = ep;
}
@@ -1336,6 +1338,39 @@ bi_errorf(const char *fmt, ...)
}
}
+/*
+ * Used by functions called by builtins and not:
+ * identical to errorfx if first argument is nil,
+ * like bi_errorf storing the errorlevel into it otherwise
+ */
+void
+maybe_errorf(int *ep, int rc, const char *fmt, ...)
+{
+ va_list va;
+
+ /* debugging: note that stdout not valid */
+ shl_stdout_ok = false;
+
+ exstat = rc;
+
+ va_start(va, fmt);
+ vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
+ (ep ? VWARNINGF_BUILTIN : 0), fmt, va);
+ va_end(va);
+
+ if (!ep)
+ goto and_out;
+ *ep = rc;
+
+ /* POSIX special builtins cause non-interactive shells to exit */
+ if (builtin_spec) {
+ builtin_argv0 = NULL;
+ /* may not want to use LERROR here */
+ and_out:
+ unwind(LERROR);
+ }
+}
+
/* Called when something that shouldn't happen does */
void
internal_errorf(const char *fmt, ...)
@@ -1435,7 +1470,7 @@ initio(void)
if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
errorf("can't get home directory");
- lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
+ strpathx(lfp, lfp, "mksh-dbg.txt", 1);
}
if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
@@ -1997,7 +2032,7 @@ init_environ(void)
errno = 0;
if ((dent = readdir(dirp)) != NULL) {
if (skip_varname(dent->d_name, true)[0] == '\0') {
- xp = shf_smprintf(Tf_sSs, MKSH_ENVDIR, dent->d_name);
+ strpathx(xp, MKSH_ENVDIR, dent->d_name, 1);
if (!(shf = shf_open(xp, O_RDONLY, 0, 0))) {
warningf(false,
"cannot read environment %s from %s: %s",
diff --git a/src/misc.c b/src/misc.c
index cddc516..9e5b572 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019
* mirabilos <m@mirbsd.org>
* Copyright (c) 2015
* Daniel Richard G. <skunk@iSKUNK.ORG>
@@ -32,7 +32,7 @@
#include <grp.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.293 2018/08/10 02:53:35 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.297 2020/04/07 11:56:46 tg Exp $");
#define KSH_CHVT_FLAG
#ifdef MKSH_SMALL
@@ -62,13 +62,13 @@ static int make_path(const char *, const char *, char **, XString *, int *);
#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
/* we don't need to check for other codes, EPERM won't happen */
-#define DO_SETUID(func, argvec) do { \
+#define DO_SETUID(func,argvec) do { \
if ((func argvec) && errno == EAGAIN) \
errorf("%s failed with EAGAIN, probably due to a" \
" too low process limit; aborting", #func); \
} while (/* CONSTCOND */ 0)
#else
-#define DO_SETUID(func, argvec) func argvec
+#define DO_SETUID(func,argvec) func argvec
#endif
@@ -216,70 +216,85 @@ getoptions(void)
void
change_flag(enum sh_flag f, int what, bool newset)
{
- unsigned char oldval;
+ unsigned char oldval = Flag(f);
unsigned char newval = (newset ? 1 : 0);
if (f == FXTRACE) {
change_xtrace(newval, true);
return;
- }
- oldval = Flag(f);
- Flag(f) = newval = (newset ? 1 : 0);
-#ifndef MKSH_UNEMPLOYED
- if (f == FMONITOR) {
- if (what != OF_CMDLINE && newval != oldval)
- j_change();
- } else
-#endif
-#ifndef MKSH_NO_CMDLINE_EDITING
- if ((
-#if !MKSH_S_NOVI
- f == FVI ||
-#endif
- f == FEMACS || f == FGMACS) && newval) {
-#if !MKSH_S_NOVI
- Flag(FVI) =
-#endif
- Flag(FEMACS) = Flag(FGMACS) = 0;
- Flag(f) = newval;
- } else
-#endif
- if (f == FPRIVILEGED && oldval && !newval) {
- /* Turning off -p? */
-
- /*XXX this can probably be optimised */
- kshegid = kshgid = getgid();
- ksheuid = kshuid = getuid();
+ } else if (f == FPRIVILEGED) {
+ if (!oldval)
+ /* no getting back dropped privs */
+ return;
+ else if (!newval) {
+ /* turning off -p */
+ kshegid = kshgid;
+ ksheuid = kshuid;
+ } else if (oldval != 3)
+ /* nor going full sugid */
+ goto change_flag;
+
+ /* +++ set group IDs +++ */
#if HAVE_SETRESUGID
- DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
-#if HAVE_SETGROUPS
- /* setgroups doesn't EAGAIN on Linux */
- setgroups(1, &kshegid);
-#endif
- DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
+ DO_SETUID(setresgid, (kshegid, kshegid, kshgid));
#else /* !HAVE_SETRESUGID */
- /* setgid, setegid, seteuid don't EAGAIN on Linux */
+ /* setgid, setegid don't EAGAIN on Linux */
setgid(kshegid);
#ifndef MKSH__NO_SETEUGID
setegid(kshegid);
-#endif
+#endif /* !MKSH__NO_SETEUGID */
+#endif /* !HAVE_SETRESUGID */
+
+ /* +++ wipe groups vector +++ */
+#if HAVE_SETGROUPS
+ /* setgroups doesn't EAGAIN on Linux */
+ setgroups(0, NULL);
+#endif /* HAVE_SETGROUPS */
+
+ /* +++ set user IDs +++ */
+#if HAVE_SETRESUGID
+ DO_SETUID(setresuid, (ksheuid, ksheuid, kshuid));
+#else /* !HAVE_SETRESUGID */
+ /* seteuid doesn't EAGAIN on Linux */
DO_SETUID(setuid, (ksheuid));
#ifndef MKSH__NO_SETEUGID
seteuid(ksheuid);
-#endif
+#endif /* !MKSH__NO_SETEUGID */
#endif /* !HAVE_SETRESUGID */
+
+ /* +++ privs changed +++ */
} else if ((f == FPOSIX || f == FSH) && newval) {
- /* Turning on -o posix or -o sh? */
- Flag(FBRACEEXPAND) = 0;
/* Turning on -o posix? */
- if (f == FPOSIX) {
+ if (f == FPOSIX)
/* C locale required for compliance */
UTFMODE = 0;
- }
- } else if (f == FTALKING) {
+ /* Turning on -o posix or -o sh? */
+ Flag(FBRACEEXPAND) = 0;
+#ifndef MKSH_NO_CMDLINE_EDITING
+ } else if ((f == FEMACS ||
+#if !MKSH_S_NOVI
+ f == FVI ||
+#endif
+ f == FGMACS) && newval) {
+#if !MKSH_S_NOVI
+ Flag(FVI) = 0;
+#endif
+ Flag(FEMACS) = Flag(FGMACS) = 0;
+#endif
+ }
+
+ change_flag:
+ Flag(f) = newval;
+
+ if (f == FTALKING) {
/* Changing interactive flag? */
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
Flag(FTALKING_I) = newval;
+#ifndef MKSH_UNEMPLOYED
+ } else if (f == FMONITOR) {
+ if (what != OF_CMDLINE && newval != oldval)
+ j_change();
+#endif
}
}
@@ -1674,14 +1689,13 @@ do_realpath(const char *upath)
if (getdrvwd(&ldest, ord(*upath)))
return (NULL);
/* A:foo -> A:/cwd/foo; A: -> A:/cwd */
- ipath = shf_smprintf(Tf_sss, ldest,
- upath[2] ? "/" : "", upath + 2);
+ strpathx(ipath, ldest, upath + 2, 0);
#endif
} else {
/* upath is a relative pathname, prepend cwd */
if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
return (NULL);
- ipath = shf_smprintf(Tf_sss, tp, "/", upath);
+ strpathx(ipath, tp, upath, 1);
afree(tp, ATEMP);
}
@@ -1783,7 +1797,7 @@ do_realpath(const char *upath)
assemble_symlink:
#endif
/* append rest of current input path to link target */
- tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
+ strpathx(tp, ldest, ip, 0);
afree(ipath, ATEMP);
ip = ipath = tp;
if (!mksh_abspath(ipath)) {
@@ -2199,8 +2213,7 @@ c_cd(const char **wp)
tryp = NULL;
if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
!getdrvwd(&tryp, ord(*dir))) {
- dir = shf_smprintf(Tf_sss, tryp,
- dir[2] ? "/" : "", dir + 2);
+ strpathx(dir, tryp, dir + 2, 0);
afree(tryp, ATEMP);
afree(allocd, ATEMP);
allocd = dir;
diff --git a/src/mksh.1 b/src/mksh.1
index 2e83428..672e6b6 100644
--- a/src/mksh.1
+++ b/src/mksh.1
@@ -1,9 +1,9 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.463 2019/03/01 16:17:31 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.489 2020/04/14 22:45:21 tg Exp $
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
-.\" 2018, 2019
+.\" 2018, 2019, 2020
.\" mirabilos <m@mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +28,8 @@
.\" thus use - for hyphens and \- for minus signs and option dashes
.\" * ~ is size-reduced and placed atop in groff, so use \*(TI
.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
-.\" * \(en does not work in nroff, so use \*(en
+.\" * \(en does not work in nroff, so use \*(en for a solo en dash
+.\" * and \*(EM for a correctly spaced em dash
.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
.\" Also make sure to use \& *before* a punctuation char that is to not
.\" be interpreted as punctuation, and especially with two-letter words
@@ -62,6 +63,12 @@
. ds ha ^
. ds en \(em
.\}
+.ie n \{\
+. ds EM \ \*(en\ \&
+.\}
+.el \{\
+. ds EM \f(TR\^\(em\^\fP
+.\}
.\"
.\" Implement .Dd with the Mdocdate RCS keyword
.\"
@@ -77,7 +84,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: March 1 2019 $
+.Dd $Mdocdate: April 14 2020 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
@@ -221,7 +228,8 @@ into account all information is first and foremost presented with
.Nm
in mind and should be taken as such.
.Ss I use Android, OS/2, etc. so what...?
-Please see the FAQ at the end of this document.
+Please refer to:
+.Pa http://www.mirbsd.org/mksh\-faq.htm#sowhatismksh
.Ss Invocation
Most builtins can be called directly, for example if a link points from its
name to the shell; not all make sense, have been tested or work at all though.
@@ -279,7 +287,7 @@ effective user ID or group ID (see
and
.Xr getgid 2 ) .
Clearing the privileged option causes the shell to set
-its effective user ID (group ID) to its real user ID (group ID).
+its effective user ID (group ID) to its initial real user ID (group ID).
For further implications, see
.Sx Startup files .
If the shell is privileged and this flag is not explicitly set, the
@@ -289,8 +297,9 @@ option is cleared automatically after processing the startup files.
Restricted shell.
A shell is
.Dq restricted
-if this
-option is used.
+if the basename the shell is called with (i.e. argv[0]) starts with
+.Ql r
+or if this option is used.
The following restrictions come into effect after the shell processes any
profile and
.Ev ENV
@@ -481,8 +490,8 @@ The first three of these are the above mentioned quoting characters (see
.Sx Quoting
below);
.Ql # ,
-if used at the beginning of a word, introduces a comment \*(en everything after
-the
+if used at the beginning of a word,
+introduces a comment\*(EMeverything after the
.Ql #
up to the nearest newline is ignored;
.Ql $
@@ -594,8 +603,7 @@ only if the exit status of
.Ar cmd1
is zero;
.Dq Li \*(Ba\*(Ba
-is the opposite \*(en
-.Ar cmd2
+.No is the opposite\*(EM Ns Ar cmd2
is executed only if the exit status of
.Ar cmd1
is non-zero.
@@ -681,29 +689,14 @@ This is not valid:
.Pp
.Dl $ { echo foo; echo bar }
.Bl -tag -width 4n
-.It Pq Ar list
-Execute
-.Ar list
-in a subshell.
-There is no implicit way to pass environment changes from a
-subshell back to its parent.
-.It { Ar list ; No }
-Compound construct;
-.Ar list
-is executed, but not in a subshell.
-Note that
-.Dq Li {
-and
-.Dq Li }
-are reserved words, not meta-characters.
-.It Xo case Ar word No in
+.It Xo Ic case Ar word Ic in
.Oo Op \&(
.Ar pattern
.Op \*(Ba Ar pattern
.No ... Ns )
.Ar list
-.Ic terminator
-.Oc No ... esac
+.Aq terminator
+.Oc No ... Ic esac
.Xc
The
.Ic case
@@ -731,12 +724,12 @@ well as tilde substitution.
For historical reasons, open and close braces may be used instead of
.Ic in
and
-.Ic esac
-e.g.\&
-.Ic case $foo { *) echo bar ;; } .
+.Ic esac ,
+for example:
+.Dq Li case $foo { (ba[rz]\*(Bablah) date ;; }
.Pp
The list
-.Ic terminator Ns s
+.Ao terminator Ac Ns s
are:
.Bl -tag -width 4n
.It Dq Li ;;
@@ -754,9 +747,9 @@ statement is that of the executed
if no
.Ar list
is executed, the exit status is zero.
-.It Xo for Ar name
-.Oo in Ar word No ... Oc ;
-.No do Ar list ; No done
+.It Xo Ic for Ar name
+.Oo Ic in Ar word ... Oc Ic ;
+.Ic do Ar list ; Ic done
.Xc
For each
.Ar word
@@ -765,16 +758,6 @@ in the specified word list, the parameter
is set to the word and
.Ar list
is executed.
-If
-.Ic in
-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
-.Ic done
-e.g.\&
-.Ic for i; { echo $i; } .
The exit status of a
.Ic for
statement is the last exit status of
@@ -782,13 +765,52 @@ statement is the last exit status of
if
.Ar list
is never executed, the exit status is zero.
-.It Xo if Ar list ;
-.No then Ar list ;
-.Oo elif Ar list ;
-.No then Ar list ; Oc
+If
+.Ic in
+is not used to specify a word list, the positional parameters ($1, $2,
+etc.) are used instead; in this case, use a newline instead of the semicolon
+.Pq Sq Ic ;\&
+for portability.
+For historical reasons, open and close braces may be used instead of
+.Ic do
+and
+.Ic done ,
+as in
+.Dq Li for i; { echo $i; }
+.Pq not portable .
+.It Xo Ic function Ar name
+.No { Ar list ; No }
+.Xc
+Defines the function
+.Ar name
+(see
+.Sx Functions
+below).
+All redirections specified after a function definition are performed whenever
+the function is executed, not when the function definition is executed.
+.It Ar name Ns \&() Ar command
+Mostly the same as
+.Ic function
+(see above and
+.Sx Functions
+below).
+Most amounts of space and tab after
+.Ar name
+will be ignored.
+.It Xo Ic function Ar name Ns \&()
+.No { Ar list ; No }
+.Xc
+.Nm bash Ns ism for
+.Ar name Ns Li ()\ {
+.Ar list Ns Li ;\ }
+.Pq the Ic function No keyword is ignored .
+.It Xo Ic if Ar list ;
+.Ic then Ar list ;
+.Oo Ic elif Ar list ;
+.Ic then Ar list ; Oc
.No ...
-.Oo else Ar list ; Oc
-.No fi
+.Oo Ic else Ar list ; Oc
+.Ic fi
.Xc
If the exit status of the first
.Ar list
@@ -810,21 +832,22 @@ following the
is executed.
The exit status of an
.Ic if
-statement is that of non-conditional
+statement is that of whatever non-conditional
+.Pq not the first
.Ar list
that is executed; if no non-conditional
.Ar list
is executed, the exit status is zero.
-.It Xo select Ar name
-.Oo in Ar word No ... Oc ;
-.No do Ar list ; No done
+.It Xo Ic select Ar name
+.Oo Ic in Ar word No ... Oc ;
+.Ic do Ar list ; Ic done
.Xc
The
.Ic select
statement provides an automatic method of presenting the user with a menu and
selecting from it.
An enumerated list of the specified
-.Ar word Ns (s)
+.Ar word Ns s
is printed on standard error, followed by a prompt
.Po
.Ev PS3 :
@@ -836,7 +859,7 @@ standard input,
.Ar name
is set to the selected word (or unset if the selection is not valid),
.Ev REPLY
-is set to what was read (leading/trailing space is stripped), and
+is set to what was read (leading and trailing space is stripped), and
.Ar list
is executed.
If a blank line (i.e. zero or more
@@ -853,38 +876,47 @@ 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 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
-.Ic do
-and
-.Ic done
-e.g.\&
-.Ic select i; { echo $i; } .
The exit status of a
.Ic select
statement is zero if a
.Ic break
statement is used to exit the loop, non-zero otherwise.
-.It Xo until Ar list ;
-.No do Ar list ;
-.No done
+If
+.Dq Ic in Ar word ...
+is omitted, the positional parameters are used.
+For historical reasons, open and close braces may be used instead of
+.Ic do
+and
+.Ic done ,
+as in:
+.Dq Li select i; { echo $i; }
+.It Xo Ic time Op Fl p
+.Op Ar pipeline
+.Xc
+The
+.Sx Command execution
+section describes the
+.Ic time
+reserved word.
+.It Xo Ic until Ar list ;
+.Ic do Ar list ; Ic done
.Xc
This works like
-.Ic while ,
-except that the body is executed only while the exit status of the first
+.Ic while Pq see below ,
+except that the body
+.Ar list
+is executed only while the exit status of the first
.Ar list
is non-zero.
-.It Xo while Ar list ;
-.No do Ar list ;
-.No done
+.It Xo Ic while Ar list ;
+.Ic do Ar list ; Ic done
.Xc
A
.Ic while
is a pre-checked loop.
-Its body is executed as often as the exit status of the first
+Its body
+.Ar list
+is executed as often as the exit status of the first
.Ar list
is zero.
The exit status of a
@@ -892,53 +924,6 @@ The exit status of a
statement is the last exit status of the
.Ar list
in the body of the loop; if the body is not executed, the exit status is zero.
-.It Xo function Ar name
-.No { Ar list ; No }
-.Xc
-Defines the function
-.Ar name
-(see
-.Sx Functions
-below).
-Note that redirections specified after a function definition are
-performed whenever the function is executed, not when the function definition
-is executed.
-.It Ar name Ns \&() Ar command
-Mostly the same as
-.Ic function
-(see
-.Sx Functions
-below).
-Whitespace (space or tab) after
-.Ar name
-will be ignored most of the time.
-.It Xo function Ar name Ns \&()
-.No { Ar list ; No }
-.Xc
-The same as
-.Ar name Ns \&()
-.Pq Nm bash Ns ism .
-The
-.Ic function
-keyword is ignored.
-.It Xo Ic time Op Fl p
-.Op Ar pipeline
-.Xc
-The
-.Sx Command execution
-section describes the
-.Ic time
-reserved word.
-.It \&(( Ar expression No ))
-The arithmetic expression
-.Ar expression
-is evaluated; equivalent to
-.Dq Li let \&" Ns Ar expression Ns \&"
-(see
-.Sx Arithmetic expressions
-and the
-.Ic let
-command, below) in a compound construct.
.It Bq Bq Ar \ \&expression\ \&
Similar to the
.Ic test
@@ -947,7 +932,7 @@ and
commands (described later), with the following exceptions:
.Bl -bullet
.It
-Field splitting and file name generation are not performed on arguments.
+Field splitting and globbing are not performed on arguments.
.It
The
.Fl a
@@ -955,23 +940,22 @@ The
and
.Fl o
.Pq OR
-operators are replaced with
-.Dq Li &&
+operators are replaced, respectively, with
+.Dq Ic &&
and
-.Dq Li \*(Ba\*(Ba ,
-respectively.
+.Dq Ic \*(Ba\*(Ba .
.It
Operators (e.g.\&
-.Dq Li \-f ,
-.Dq Li = ,
-.Dq Li \&! )
+.Dq Ic \-f ,
+.Dq Ic = ,
+.Dq Ic \&! )
must be unquoted.
.It
Parameter, command and arithmetic substitutions are performed as expressions
are evaluated and lazy expression evaluation is used for the
-.Dq Li &&
+.Dq Ic &&
and
-.Dq Li \*(Ba\*(Ba
+.Dq Ic \*(Ba\*(Ba
operators.
This means that in the following statement,
.Ic $(\*(Ltfoo)
@@ -983,23 +967,47 @@ $ [[ \-r foo && $(\*(Ltfoo) = b*r ]]
.Ed
.It
The second operand of the
-.Dq Li !=
+.Dq Ic =
and
-.Dq Li =
-expressions are a subset of patterns (e.g. the comparison
+.Dq Ic !=
+expressions is a pattern (e.g. the comparison
.Ic \&[[ foobar = f*r ]]
succeeds).
-This even works indirectly:
+This even works indirectly, while quoting forces literal interpretation:
.Bd -literal -offset indent
-$ bar=foobar; baz=\*(aqf*r\*(aq
-$ [[ $bar = $baz ]]; echo $?
-$ [[ $bar = \&"$baz" ]]; echo $?
+$ bar=foobar; baz=\*(aqf*r\*(aq # or: baz=\*(aqf+(o)b?r\*(aq
+$ [[ $bar = $baz ]]; echo $? # 0
+$ [[ $bar = \&"$baz" ]]; echo $? # 1
.Ed
-.Pp
-Perhaps surprisingly, the first comparison succeeds,
-whereas the second doesn't.
-This does not apply to all extglob metacharacters, currently.
.El
+.It { Ar list ; No }
+Compound construct;
+.Ar list
+is executed, but not in a subshell.
+.br
+Note that
+.Dq Li {
+and
+.Dq Li }
+are reserved words, not meta-characters.
+.It Pq Ar list
+Execute
+.Ar list
+in a subshell, forking.
+There is no implicit way to pass environment changes from a
+subshell back to its parent.
+.It \&(( Ar expression No ))
+The arithmetic expression
+.Ar expression
+is evaluated; equivalent to
+.Sq Li let \&" Ns Ar expression Ns \&"
+in a compound construct.
+.br
+See the
+.Ic let
+command and
+.Sx Arithmetic expressions
+below.
.El
.Ss Quoting
Quoting is used to prevent the shell from treating characters or words
@@ -1078,9 +1086,11 @@ For
.Dq Li \eU########
and
.Dq Li \eu#### ,
-.Dq #
-means a hexadecimal digit, of which there may be none up to four or eight;
-these escapes translate a Universal Coded Character Set codepoint to UTF-8.
+.Sq Li #
+means a hexadecimal digit (up to 4 or 8); these translate a
+Universal Coded Character Set codepoint to UTF-8 (see
+.Sx CAVEATS
+on UCS limitations).
Furthermore,
.Dq Li \eE
and
@@ -1090,44 +1100,48 @@ expand to the escape character.
In the
.Ic print
builtin mode,
-.Dq Li \e" ,
-.Dq Li \e\*(aq
-and
-.Dq Li \e?
-are explicitly excluded;
-octal sequences must have the none up to three octal digits
-.Dq #
+octal sequences must have the optional up to three octal digits
+.Sq Li #
prefixed with the digit zero
.Pq Dq Li \e0### ;
hexadecimal sequences
.Dq Li \ex##
-are limited to none up to two hexadecimal digits
-.Dq # ;
+are limited to up to two hexadecimal digits
+.Sq Li # ;
both octal and hexadecimal sequences convert to raw octets;
-.Dq Li \e# ,
-where # is none of the above, translates to \e# (backslashes are retained).
+.Dq Li \e% ,
+where
+.Sq Li %
+is none of the above, translates to
+.Li \e%
+.Pq backslashes are retained .
.Pp
-Backslash expansion in the C style mode slightly differs: octal sequences
+In C style mode, raw octet-yielding octal sequences
.Dq Li \e###
-must have no digit zero prefixing the one up to three octal digits
-.Dq #
-and yield raw octets; hexadecimal sequences
-.Dq Li \ex#*
+must not have the one up to three octal digits prefixed with the
+digit zero; hexadecimal sequences
+.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 Universal Coded Character Set codepoint to UTF-8.
+.Sq Li #
+as they can and terminate with the first non-xdigit; below
+.Li \ex100
+these produce raw octets; above, they are equivalent to
+.Dq Li \eU# .
The sequence
-.Dq Li \ec# ,
+.Dq Li \ec% ,
where
-.Dq #
-is any octet, translates to Ctrl-# (which basically means,
+.Sq Li %
+is any octet, translates to
+.Ic Ctrl- Ns Li % ,
+that is,
.Dq Li \ec?
-becomes DEL, everything else is bitwise ANDed with 0x1F).
-Finally,
-.Dq Li \e# ,
-where # is none of the above, translates to # (has the backslash trimmed),
-even if it is a newline.
+becomes DEL, everything else is bitwise ANDed with 0x9F.
+.Dq Li \e% ,
+where
+.Sq Li %
+is none of the above, translates to
+.Li % :
+backslashes are trimmed even before newlines.
.Ss Aliases
There are two types of aliases: normal command aliases and tracked aliases.
Command aliases are normally used as a short hand for a long or often used
@@ -1438,10 +1452,10 @@ Note that both the parameter name and the
must be unquoted for the shell to recognise a parameter assignment.
The construct
.Ic FOO+=baz
-is also recognised; the old and new values are immediately concatenated.
+is also recognised;
+the old and new values are string-concatenated with no separator.
The fourth way of setting a parameter is with the
.Ic export ,
-.Ic global ,
.Ic readonly
and
.Ic typeset
@@ -1623,8 +1637,6 @@ A single
.Ql #
results in the shortest match, and two
of them result in the longest match.
-Cannot be applied to a vector
-.Pq ${*} or ${@} or ${array[*]} or ${array[@]} .
.Pp
.Sm off
.It Xo
@@ -1636,8 +1648,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.
-Cannot be applied to a vector.
+Like ${...#...} but deletes from the end of the value.
.Pp
.Sm off
.It Xo
@@ -1680,8 +1691,7 @@ cause no replacement; a single leading slash or use of a
that matches the empty string causes the replacement to
happen only once; two leading slashes cause all occurrences
of matches in the value to be replaced.
-Cannot be applied to a vector.
-Inefficiently implemented, may be slow.
+May be slow on long strings.
.Pp
.Sm off
.It Xo
@@ -1701,6 +1711,8 @@ except that both
and
.Ar string
are expanded anew for each iteration.
+Use with
+.Ev KSH_MATCH .
.Pp
.Sm off
.It Xo
@@ -1733,10 +1745,6 @@ Both
and
.Ar len
are evaluated as arithmetic expressions.
-Currently,
-.Ar pos
-must start with a space, opening parenthesis or digit to be recognised.
-Cannot be applied to a vector.
.Pp
.It Pf ${ Ns Ar name Ns @#}
The hash (using the BAFH algorithm) of the expansion of
@@ -1924,9 +1932,7 @@ This is different from
.It Ev HISTSIZE
The number of commands normally stored for history.
The default is 2047.
-Do not set this value to insanely high values such as 1000000000 because
-.Nm
-can then not allocate enough memory for the history and will not start.
+The maximum is 65535.
.It Ev HOME
The default directory for the
.Ic cd
@@ -1948,16 +1954,16 @@ above for details.
This parameter is not imported from the environment when the shell is
started.
.It Ev KSHEGID
-The effective group id of the shell.
+The effective group id of the shell at startup.
.It Ev KSHGID
-The real group id of the shell.
+The real group id of the shell at startup.
.It Ev KSHUID
-The real user id of the shell.
+The real user id of the shell at startup.
.It Ev KSH_MATCH
The last matched string.
In a future version, this will be an indexed array,
with indexes 1 and up capturing matching groups.
-Set by string comparisons (== and !=) in double-bracket test
+Set by string comparisons (= and !=) in double-bracket test
expressions when a match is found (when != returns false), by
.Ic case
when a match is encountered, and by the substitution operations
@@ -2203,7 +2209,7 @@ set or does not contain the absolute path of a writable directory, temporary
files are created in
.Pa /tmp .
.It Ev USER_ID
-The effective user id of the shell.
+The effective user id of the shell at startup.
.El
.Ss Tilde expansion
Tilde expansion, which is done in parallel with parameter substitution,
@@ -2699,16 +2705,16 @@ instead of
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.
-In UTF-8 mode, raw octets are mapped into the range EF80..EFFF as in
-OPTU-8, which is in the PUA and has been assigned by CSUR for this use.
-If more than one octet in ASCII mode, or a sequence of more than one
-octet not forming a valid and minimal CESU-8 sequence is passed, the
-behaviour is undefined (usually, the shell aborts with a parse error,
-but rarely, it succeeds, e.g. on the sequence C2 20).
-That's why you should always use ASCII mode unless you know that the
-input is well-formed UTF-8 in the range of 0000..FFFD if you use this
-feature, as opposed to
-.Ic read Fl a .
+If
+.Sq Li x
+isn't comprised of exactly one valid character, the behaviour is undefined
+(usually, the shell aborts with a parse error, but rarely, it succeeds,
+e.g. on the sequence C2 20); users of this feature (as opposed to
+.Ic read Fl a )
+must validate the input first.
+See
+.Sx CAVEATS
+for UTF-8 mode handling.
.Pp
The operators are evaluated as follows:
.Bl -tag -width Ds -offset indent
@@ -2965,7 +2971,7 @@ Note that when called in a subshell,
will only exit that subshell and will not cause the original shell to exit
a running function (see the
.Ic while Ns Li \&... Ns Ic read
-loop FAQ below).
+loop FAQ).
.Pp
Functions defined with the
.Ic function
@@ -2978,9 +2984,6 @@ notation:
The $0 parameter is set to the name of the function
(Bourne-style functions leave $0 untouched).
.It
-Parameter assignments preceding function calls are not kept in the shell
-environment (executing Bourne-style functions will keep assignments).
-.It
.Ev OPTIND
is saved/reset and restored on entry and exit from the function so
.Ic getopts
@@ -3026,11 +3029,6 @@ Regular built-in commands are different only in that the
.Ev PATH
parameter is not used to find them.
.Pp
-The original
-.Nm ksh
-and POSIX differ somewhat in which commands are considered
-special or regular.
-.Pp
POSIX special built-in utilities:
.Pp
.Ic \&. , \&: , break , continue ,
@@ -3042,11 +3040,11 @@ Additional
.Nm
commands keeping assignments:
.Pp
-.Ic global , source , typeset
+.Ic source , typeset
.Pp
-Builtins that are not special:
+All other builtins are not special; these are at least:
.Pp
-.Ic [ , alias , bg , bind ,
+.Ic [\& , alias , bg , bind ,
.Ic builtin , cat , cd , command ,
.Ic echo , false , fc , fg ,
.Ic getopts , jobs , kill , let ,
@@ -3059,10 +3057,11 @@ Once the type of command has been determined, any command-line parameter
assignments are performed and exported for the duration of the command.
.Pp
The following describes the special and regular built-in commands and
-builtin-like reserved words:
+builtin-like reserved words, as well as some optional utilities:
.Pp
.Bl -tag -width false -compact
.It Ic \&. Ar file Op Ar arg ...
+.Pq keeps assignments , special
This is called the
.Dq dot
command.
@@ -3079,22 +3078,62 @@ If no arguments are given, the positional parameters are
those of the environment the command is used in.
.Pp
.It Ic \&: Op Ar ...
+.Pq keeps assignments , special
The null command.
+.br
Exit status is set to zero.
.Pp
+.It Ic Lb64decode Op Ar string
+.Pq Li dot.mkshrc No function
+Decode
+.Ar string
+or standard input to binary.
+.Pp
+.It Ic Lb64encode Op Ar string
+.Pq Li dot.mkshrc No function
+Encode
+.Ar string
+or standard input as base64.
+.Pp
+.It Ic Lbafh_init
+.It Ic Lbafh_add Op Ar string
+.It Ic Lbafh_finish
+.Pq Li dot.mkshrc No functions
+Implement the Better Avalance for the Jenkins Hash.
+This is the same hash
+.Nm
+currently uses internally.
+.No "After calling" Ic Lbafh_init , No call Ic Lbafh_add
+multiple times until all input is read, then call
+.Ic Lbafh_finish ,
+which writes the result to the unsigned integer
+.Va Lbafh_v
+variable for your consumption.
+.Pp
+.It Ic Lstripcom Op Ar
+.Pq Li dot.mkshrc No function
+Same as
+.Ic cat
+but strips any empty lines and comments (from any
+.Sq #
+character onwards, no escapes)
+and reduces any amount of whitespace to one space character.
+.Pp
.It Ic \&[ Ar expression Ic \&]
+.Pq regular
See
.Ic test .
.Pp
.It Xo Ic alias
.Oo Fl d \*(Ba t Oo Fl r Oc \*(Ba
-.Cm +\-x Oc
+.Fl +x Oc
.Op Fl p
.Op Cm +
.Oo Ar name
.Op Ns = Ns Ar value
.Ar ... Oc
.Xc
+.Pq regular
Without arguments,
.Ic alias
lists all aliases.
@@ -3102,8 +3141,8 @@ For any name without a value, the existing alias is listed.
Any name with a value defines an alias; see
.Sx Aliases
above.
-.Li \&[][A\-Za\-z0\-9_!%,.@:\-]
-are valid in names, except they may not begin with a hyphen-minus, and
+.Li \&[][A\-Za\-z0\-9_!%+,.@:\-]
+are valid in names, except they may not begin with a plus or hyphen-minus, and
.Ic \&[[
is not a valid alias name.
.Pp
@@ -3112,7 +3151,7 @@ Normally, aliases are listed as
.Ar name Ns = Ns Ar value ,
where
.Ar value
-is quoted.
+is quoted as necessary.
If options were preceded with
.Ql + ,
or a lone
@@ -3128,15 +3167,17 @@ listed or set (see
.Sx Tilde expansion
above).
.Pp
-If the
-.Fl p
-option is used, each alias is prefixed with the string
-.Dq Li alias\ \& .
+With
+.Fl p ,
+each alias is listed with the string
+.Dq Li alias\ \&
+prefixed.
.Pp
The
.Fl t
-option indicates that tracked aliases are to be listed/set (values specified on
-the command line are ignored for tracked aliases).
+option indicates that tracked aliases are to be listed/set (values given
+with the command are ignored for tracked aliases).
+.Pp
The
.Fl r
option indicates that all tracked aliases are to be reset.
@@ -3148,7 +3189,14 @@ option sets
the export attribute of an alias, or, if no names are given, lists the aliases
with the export attribute (exporting an alias has no effect).
.Pp
+.It Ic autoload
+.Pq built-in alias
+See
+.Sx Functions
+above.
+.Pp
.It Ic bg Op Ar job ...
+.Pq regular , needs job control
Resume the specified stopped job(s) in the background.
If no jobs are specified,
.Ic %+
@@ -3157,70 +3205,62 @@ See
.Sx Job control
below for more information.
.Pp
-.It Ic bind Op Fl l
-The current bindings are listed.
-If the
-.Fl l
-flag is given,
-.Ic bind
-instead lists the names of the functions to which keys may be bound.
+.It Ic bind Fl l
+.Pq regular
+The names of editing commands strings can be bound to are listed.
See
.Sx Emacs editing mode
for more information.
.Pp
-.It Xo Ic bind Op Fl m
-.Ar string Ns = Ns Op Ar substitute
-.Ar ...
-.Xc
+.It Ic bind Op Ar string ...
+The current bindings, for
+.Ar string ,
+if given, else all, are listed.
+.Em Note:
+Default prefix bindings
+.Pq 1=Esc , 2=\*(haX , 3=NUL
+assumed.
+.Pp
.It Xo Ic bind
.Ar string Ns = Ns Op Ar editing-command
-.Ar ...
+.Op Ar ...
.Xc
-The specified editing command is bound to the given
+.It Xo Ic bind Fl m
+.Ar string Ns = Ns Ar substitute
+.Op Ar ...
+.Xc
+To
.Ar string ,
which should consist of a control character
-optionally preceded by one of the two prefix characters
-and optionally succeeded by a tilde character.
-Future input of the
+optionally preceded by one of the three prefix characters
+and optionally succeeded by a tilde character, the
+.Ar editing-command
+is bound so that future input of the
.Ar string
-will cause the editing command to be immediately invoked.
-If the
+will immediately invoke that editing command.
+If a tilde postfix is given, a tilde trailing the control character is ignored.
+If
.Fl m
-flag is given, the specified input
+.Pq macro
+is given, future input of the
.Ar string
-will afterwards be immediately replaced by the given
+will be replaced by the given NUL-terminated
.Ar substitute
-string which may contain editing commands but not other macros.
-If a tilde postfix is given, a tilde trailing the one or
-two prefices and the control character is ignored, any
-other trailing character will be processed afterwards.
-.Pp
-Control characters may be written using caret notation
-i.e. \*(haX represents Ctrl-X.
-The caret itself can be escaped by a backslash, which also escapes itself.
-Note that although only three prefix characters (usually ESC, \*(haX and NUL)
+string, wherein prefix/control/tilde characters mapped to editing commands
+.Pq but not those mapped to other macros
+will be processed.
+.Pp
+Prefix and control characters may be written using caret notation, i.e.\&
+.No \*(ha Ns Li Z
+represents
+.No Ctrl- Ns Li Z .
+Use a backslash to escape the caret, an equals sign or another backslash.
+Note that, although only three prefix characters
+.Pq usually Esc, \*(haX and NUL
are supported, some multi-character sequences can be supported.
.Pp
-The following default bindings show how the arrow keys, the home, end and
-delete key on a BSD wsvt25, xterm\-xfree86 or GNU screen terminal are bound
-(of course some escape sequences won't work out quite this nicely):
-.Bd -literal -offset indent
-bind \*(aq\*(haX\*(aq=prefix\-2
-bind \*(aq\*(ha[[\*(aq=prefix\-2
-bind \*(aq\*(haXA\*(aq=up\-history
-bind \*(aq\*(haXB\*(aq=down\-history
-bind \*(aq\*(haXC\*(aq=forward\-char
-bind \*(aq\*(haXD\*(aq=backward\-char
-bind \*(aq\*(haX1\*(TI\*(aq=beginning\-of\-line
-bind \*(aq\*(haX7\*(TI\*(aq=beginning\-of\-line
-bind \*(aq\*(haXH\*(aq=beginning\-of\-line
-bind \*(aq\*(haX4\*(TI\*(aq=end\-of\-line
-bind \*(aq\*(haX8\*(TI\*(aq=end\-of\-line
-bind \*(aq\*(haXF\*(aq=end\-of\-line
-bind \*(aq\*(haX3\*(TI\*(aq=delete\-char\-forward
-.Ed
-.Pp
.It Ic break Op Ar level
+.Pq keeps assignments , special
Exit the
.Ar level Ns th
inner-most
@@ -3238,6 +3278,7 @@ defaults to 1.
.Op Fl \-
.Ar command Op Ar arg ...
.Xc
+.Pq regular
Execute the built-in command
.Ar command .
.Pp
@@ -3245,6 +3286,7 @@ Execute the built-in command
.Ic \ebuiltin
.Ar command Op Ar arg ...
.Xc
+.Pq regular , decl-forwarder
Same as
.Ic builtin .
Additionally acts as declaration utility forwarder, i.e. this is a
@@ -3258,8 +3300,8 @@ is a declaration utility.
.Op Fl u
.Op Ar
.Xc
-Read files sequentially, in command line order, and write them to
-standard output.
+.Pq defer with flags
+Copy files in command line order to standard output.
If a
.Ar file
is a single dash
@@ -3288,6 +3330,7 @@ utility is preferred over the builtin.
.Op Fl eLP
.Op Ar dir
.Xc
+.Pq regular
Set the working directory to
.Ar dir .
If the parameter
@@ -3355,6 +3398,7 @@ error occurred, 0 otherwise.
.Op Fl eLP
.Ar old new
.Xc
+.Pq regular
The string
.Ar new
is substituted for
@@ -3362,12 +3406,17 @@ is substituted for
in the current directory, and the shell attempts to change to the new
directory.
.Pp
+.It Ic cls
+.Pq Li dot.mkshrc No alias
+Reinitialise the display (hard reset).
+.Pp
.It Xo
.Ic command
.Op Fl pVv
.Ar cmd
.Op Ar arg ...
.Xc
+.Pq regular , decl-forwarder
If neither the
.Fl v
nor
@@ -3383,20 +3432,18 @@ cannot be a shell function;
and secondly, special built-in commands lose their specialness
(i.e. redirection and utility errors do not cause the shell to
exit, and command assignments are not permanent).
-The declaration utility property is not reset.
.Pp
If the
.Fl p
-option is given, a default search path is used instead of the current value of
-.Ev PATH ,
-the actual value of which is system dependent.
+option is given, a default search path, whose actual value is
+system-dependent, is used instead of the current
+.Ev PATH .
.Pp
If the
.Fl v
option is given, instead of executing
.Ar cmd ,
-information about what would be executed is given (and the same is done for
-.Ar arg ... ) .
+information about what would be executed is given for each argument.
For builtins, functions and keywords, their names are simply printed;
for aliases, a command that defines them is printed;
for utilities found by searching the
@@ -3410,9 +3457,10 @@ The
.Fl V
option is like the
.Fl v
-option, except it is more verbose.
+option, but more verbose.
.Pp
.It Ic continue Op Ar level
+.Pq keeps assignments , special
Jumps to the beginning of the
.Ar level Ns th
inner-most
@@ -3425,17 +3473,33 @@ loop.
.Ar level
defaults to 1.
.Pp
+.It Ic dirs Op Fl lnv
+.Pq Li dot.mkshrc No function
+Print the directory stack.
+.Fl l
+causes tilde expansion to occur in the output.
+.Fl n
+causes line wrapping before 80 columns, whereas
+.Fl v
+causes numbered vertical output.
+.Pp
+.It Ic doch
+.Pq Li dot.mkshrc No alias
+Execute the last command with
+.Xr sudo 8 .
+.Pp
.It Xo
.Ic echo
.Op Fl Een
.Op Ar arg ...
.Xc
+.Pq regular
.Em Warning:
-this utility is not portable; use the Korn shell builtin
+this utility is not portable; use the standard Korn shell built-in utility
.Ic print
-instead.
+in new code instead.
.Pp
-Prints its arguments (separated by spaces) followed by a newline, to the
+Print arguments, separated by spaces, followed by a newline, to
standard output.
The newline is suppressed if any of the arguments contain the
backslash sequence
@@ -3448,12 +3512,13 @@ The options are provided for compatibility with
.Bx
shell scripts.
The
-.Fl n
-option suppresses the trailing newline,
-.Fl e
-enables backslash interpretation (a no-op, since this is normally done), and
.Fl E
-suppresses backslash interpretation.
+option suppresses backslash interpretation,
+.Fl e
+enables it (normally default),
+.Fl n
+suppresses the trailing newline,
+and anything else causes the word to be printed as argument instead.
.Pp
If the
.Ic posix
@@ -3466,9 +3531,40 @@ only the first argument is treated as an option, and only if it is exactly
.Dq Li \-n .
Backslash interpretation is disabled.
.Pp
+.It Xo
+.Ic enable
+.Op Fl anps
+.Op Ar name ...
+.Xc
+.Pq Li dot.mkshrc No function
+Hide and unhide built-in utilities, aliases and functions and those defined in
+.Li dot.mkshrc .
+.Pp
+If no
+.Ar name
+is given or the
+.Fl p
+option is used, builtins are printed (behind the string
+.Dq Li enable\ \& ,
+followed by
+.Dq Li \-n\ \&
+if the builtin is currently disabled), otherwise, they are disabled (if
+.Fl n
+is given) or re-enabled.
+.Pp
+When printing, only enabled builtins are printed by default; the
+.Fl a
+options prints all builtins, while
+.Fl n
+prints only disabled builtins instead;
+.Fl s
+limits the list to POSIX special builtins.
+.Pp
.It Ic eval Ar command ...
-The arguments are concatenated (with spaces between them) to form a single
-string which the shell then parses and executes in the current environment.
+.Pq keeps assignments , special
+The arguments are concatenated, with a space between each,
+to form a single string which the shell then parses
+and executes in the current execution environment.
.Pp
.It Xo
.Ic exec
@@ -3476,8 +3572,9 @@ string which the shell then parses and executes in the current environment.
.Op Fl c
.Op Ar command Op Ar arg ...
.Xc
-The command is executed without forking, replacing the shell process.
-This is currently absolute, i.e.\&
+.Pq keeps assignments , special
+The command (with arguments) is executed without forking,
+fully replacing the shell process; this is absolute, i.e.\&
.Ic exec
never returns, even if the
.Ar command
@@ -3490,7 +3587,7 @@ value, and
.Fl c
clears the environment before executing the child process, except for the
.Ev _
-variable and direct assignments.
+parameter and direct assignments.
.Pp
If no command is given except for I/O redirection, the I/O redirection is
permanent and the shell is
@@ -3503,32 +3600,38 @@ Note that the Bourne shell differs here;
it does pass these file descriptors on.
.Pp
.It Ic exit Op Ar status
-The shell or subshell exits with the specified exit status.
-If
-.Ar status
-is not specified, the exit status is the current value of the
-.Ic \&$?
-parameter.
+.Pq keeps assignments , special
+The shell or subshell exits with the specified errorlevel
+(or the current value of the
+.Va $?\&
+parameter).
.Pp
.It Xo
.Ic export
.Op Fl p
.Op Ar parameter Ns Op = Ns Ar value
.Xc
+.Pq keeps assignments , special, decl-util
Sets the export attribute of the named parameters.
Exported parameters are passed in the environment to executed commands.
If values are specified, the named parameters are also assigned.
This is a declaration utility.
.Pp
If no parameters are specified, all parameters with the export attribute
-set are printed one per line; either their names, or, if a
+set are printed one per line: either their names, or, if a
.Dq Li \-
-with no option letter is specified, name=value pairs, or, with
-.Fl p ,
+with no option letter is specified, name=value pairs, or, with the
+.Fl p
+option,
.Ic export
commands suitable for re-entry.
.Pp
+.It Ic extproc
+.Pq OS/2
+Null command required for shebang-like functionality.
+.Pp
.It Ic false
+.Pq regular
A command that exits with a non-zero status.
.Pp
.It Xo
@@ -3538,6 +3641,7 @@ A command that exits with a non-zero status.
.Op Fl r
.Op Ar first Op Ar last
.Xc
+.Pq regular
.Ar first
and
.Ar last
@@ -3563,7 +3667,7 @@ is specified, the editor specified by the
.Ev FCEDIT
parameter (if this parameter is not set,
.Pa /bin/ed
-is used), and then executed by the shell.
+is used), and the result is executed by the shell.
.Pp
.It Xo
.Ic fc
@@ -3572,6 +3676,7 @@ is used), and then executed by the shell.
.Op Ar old Ns = Ns Ar new
.Op Ar prefix
.Xc
+.Pq regular
Re-execute the selected command (the previous command by default) after
performing the optional substitution of
.Ar old
@@ -3592,71 +3697,73 @@ This command is usually accessed with the predefined:
.Ic alias r=\*(aqfc \-e \-\*(aq
.Pp
.It Ic fg Op Ar job ...
+.Pq regular , needs job control
Resume the specified job(s) in the foreground.
If no jobs are specified,
.Ic %+
is assumed.
+.br
See
.Sx Job control
below for more information.
.Pp
+.It Ic functions Op Ar name ...
+.Pq built-in alias
+Display the function definition commands corresponding to the listed,
+or all defined, functions.
+.Pp
.It Xo
.Ic getopts
.Ar optstring name
.Op Ar arg ...
.Xc
+.Pq regular
Used by shell procedures to parse the specified arguments (or positional
parameters, if no arguments are given) and to check for legal options.
-.Ar optstring
-contains the option letters that
-.Ic getopts
-is to recognise.
-If a letter is followed by a colon, the option is expected to
-have an argument.
Options that do not take arguments may be grouped in a single argument.
-If an option takes an argument and the option character is not the
-last character of the argument it is found in, the remainder of the argument is
-taken to be the option's argument; otherwise, the next argument is the option's
-argument.
+If an option takes an argument and the option character is not the last
+character of the word it is found in, the remainder of the word is taken to
+be the option's argument; otherwise, the next word is the option's argument.
+.Pp
+.Ar optstring
+contains the option letters to be recognised.
+If a letter is followed by a colon, the option takes an argument.
.Pp
Each time
.Ic getopts
is invoked, it places the next option in the shell parameter
-.Ar name
-and the index of the argument to be processed by the next call to
-.Ic getopts
-in the shell parameter
-.Ev OPTIND .
+.Ar name .
If the option was introduced with a
.Ql + ,
-the option placed in
+the character placed in
.Ar name
is prefixed with a
.Ql + .
-When an option requires an argument,
-.Ic getopts
-places it in the shell parameter
-.Ev OPTARG .
+If the option takes an argument, it is placed in the shell parameter
+.Ev OPTARG.
.Pp
When an illegal option or a missing option argument is encountered, a question
mark or a colon is placed in
.Ar name
(indicating an illegal option or missing argument, respectively) and
.Ev OPTARG
-is set to the option character that caused the problem.
-Furthermore, if
+is set to the option letter that caused the problem.
+Furthermore, unless
.Ar optstring
-does not begin with a colon, a question mark is placed in
+begins with a colon, a question mark is placed in
.Ar name ,
.Ev OPTARG
-is unset, and an error message is printed to standard error.
+is unset and a diagnostic is shown on standard error.
.Pp
+.Ic getopts
+records the index of the argument to be processed by the next call in
+.Ev OPTIND .
When the end of the options is encountered,
.Ic getopts
-exits with a non-zero exit status.
-Options end at the first (non-option
-argument) argument that does not start with a
-.Ql \- ,
+returns a non-zero exit status.
+Options end at the first argument that does not start with a
+.Ql \-
+.Pq non-option argument
or when a
.Dq Li \-\-
argument is encountered.
@@ -3666,7 +3773,8 @@ Option parsing can be reset by setting
to 1 (this is done automatically whenever the shell or a shell procedure is
invoked).
.Pp
-Warning: Changing the value of the shell parameter
+.Em Warning:
+Changing the value of the shell parameter
.Ev OPTIND
to a value other than 1 or parsing different sets of arguments without
resetting
@@ -3674,41 +3782,50 @@ resetting
may lead to unexpected results.
.Pp
.It Xo
-.Ic global
-.Op Ic +\-aglpnrtUux
-.Oo Fl L Ns Op Ar n
-.No \*(Ba Fl R Ns Op Ar n
-.No \*(Ba Fl Z Ns Op Ar n Oc
-.Op Fl i Ns Op Ar n
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
-.Xc
-See
-.Ic typeset Fl g .
-.No Deprecated , Em will
-be removed from a future version of
-.Nm .
-.Pp
-.It Xo
.Ic hash
.Op Fl r
.Op Ar name ...
.Xc
-Without arguments, any hashed executable command pathnames are listed.
+.Pq built-in alias
+Without arguments, any hashed executable command paths are listed.
The
.Fl r
-option causes all hashed commands to be removed from the hash table.
+option causes all hashed commands to be removed from the cache.
Each
.Ar name
-is searched as if it were a command name and added to the hash table if it is
-an executable command.
+is searched as if it were a command name
+and added to the cache if it is an executable command.
+.Pp
+.It Ic hd Op Ar
+.Pq Li dot.mkshrc No alias or function
+Hexdump stdin or arguments legibly.
+.Pp
+.It Xo
+.Ic history
+.Op Fl nr
+.Op Ar first Op Ar last
+.Xc
+.Pq built-in alias
+Same as
+.Ic fc Fl l Pq see above .
+.Pp
+.It Xo
+.Ic integer
+.Op flags
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+.Pq built-in alias
+Same as
+.Ic typeset Fl i Pq see below .
.Pp
.It Xo
.Ic jobs
.Op Fl lnp
.Op Ar job ...
.Xc
+.Pq regular
Display information about the specified job(s); if no jobs are specified, all
jobs are displayed.
The
@@ -3735,6 +3852,7 @@ and the displayed job.
.No { Ar job \*(Ba pid \*(Ba pgrp No }
.Ar ...
.Xc
+.Pq regular
Send the specified signal to the specified jobs, process IDs or process
groups.
If no signal is specified, the
@@ -3751,23 +3869,36 @@ below for the format of
.Fl l
.Op Ar exit-status ...
.Xc
+.Pq regular
Print the signal name corresponding to
.Ar exit-status .
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 ...
+.Pq regular
Each expression is evaluated (see
.Sx Arithmetic expressions
above).
-If all expressions are successfully evaluated, the exit status is 0 (1)
-if the last expression evaluated to non-zero (zero).
+If all expressions evaluate successfully, the exit status is
+0 (1) if the last expression evaluated to non-zero (zero).
If an error occurs during
the parsing or evaluation of an expression, the exit status is greater than 1.
Since expressions may need to be quoted,
-.No \&(( Ar expr No ))
+.Li \&(( Ar expr Li ))
is syntactic sugar for:
-.Dl "{ \e\ebuiltin let \*(aq" Ns Ar expr Ns "\*(aq; }"
+.Dl "{ \e\ebuiltin let \*(aq" Ns Ar expr Ns Li "\*(aq; }"
+.Pp
+.It Xo
+.Ic local
+.Op flags
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+.Pq built-in alias
+Same as
+.Ic typeset Pq see below .
.Pp
.It Xo
.Ic mknod
@@ -3782,8 +3913,9 @@ is syntactic sugar for:
.Ar name
.Cm p
.Xc
+.Pq optional
Create a device special file.
-The file type may be
+The file type may be one of
.Cm b
(block type device),
.Cm c
@@ -3806,11 +3938,36 @@ This is not normally part of
however, distributors may have added this as builtin as a speed hack.
.Pp
.It Xo
+.Ic nameref
+.Op flags
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+.Pq built-in alias
+Same as
+.Ic typeset Fl n Pq see below .
+.Pp
+.It Xo
+.Ic popd
+.Op Fl lnv
+.Op Ic + Ns Ar n
+.Xc
+.Pq Li dot.mkshrc No function
+Pops the directory stack and returns to the new top directory.
+The flags are as in
+.Ic dirs Pq see above .
+A numeric argument
+.Ic + Ns Ar n
+selects the entry in the stack to discard.
+.Pp
+.It Xo
.Ic print
.Oo Fl AcelNnprsu Ns Oo Ar n Oc \*(Ba
.Fl R Op Fl n Oc
.Op Ar argument ...
.Xc
+.Pq regular
Print the specified argument(s) on the standard output,
separated by spaces, terminated with a newline.
The escapes mentioned in
@@ -3822,7 +3979,7 @@ which is equivalent to using the
option, are interpreted.
.Pp
The options are as follows:
-.Bl -tag -width Ds
+.Bl -tag -width XuXnX
.It Fl A
Each
.Ar argument
@@ -3872,24 +4029,73 @@ its first argument as option only if it is exactly
.Dq Li \-n
.Pq to suppress the trailing newline .
.Pp
+.It Ic printf Ar format Op Ar arguments ...
+.Pq optional , defer always
+If compiled in, format and print the arguments, supporting the bare
+.Tn POSIX Ns -mandated
+minimum.
+If an external utility of the same name is found, it is deferred to,
+unless run as direct builtin call or from the
+.Ic builtin
+utility.
+.Pp
+.It Ic pushd Op Fl lnv
+.Pq Li dot.mkshrc No function
+Rotate the top two elements of the directory stack.
+The options are the same as for
+.Ic dirs Pq see above ,
+and
+.Ic pushd
+changes to the topmost directory stack entry after acting.
+.Pp
+.It Xo
+.Ic pushd
+.Op Fl lnv
+.Ic + Ns Ar n
+.Xc
+.Pq Li dot.mkshrc No function
+Rotate the element number
+.Ar n
+to the top.
+.Pp
+.It Xo
+.Ic pushd
+.Op Fl lnv
+.Ar name
+.Xc
+.Pq Li dot.mkshrc No function
+Push
+.Ar name
+on top of the stack.
+.Pp
.It Ic pwd Op Fl LP
+.Pq regular
Print the present working directory.
-If the
-.Fl L
-option is used or if the
-.Ic physical
-option isn't set (see the
-.Ic set
-command below), the logical path is printed (i.e. the path used to
-.Ic cd
-to the current directory).
-If the
+If no options are given,
+.Ic pwd
+behaves as if the
.Fl P
-option (physical path) is used or if the
+option (print physical path) was used if the
.Ic physical
-option is set, the path determined from the filesystem (by following
-.Dq Li ..
-directories to the root directory) is printed.
+shell option is set, the
+.Fl L
+option (print logical path) otherwise.
+The logical path is the path used to
+.Ic cd
+to the current directory;
+the physical path is determined from the filesystem (by following
+.Dq Li ..\&
+directories to the root directory).
+.Pp
+.It Xo
+.Ic r
+.Op Fl g
+.Op Ar old Ns = Ns Ar new
+.Op Ar prefix
+.Xc
+.Pq built-in alias
+Same as
+.Ic fc Fl e \& Pq see above .
.Pp
.It Xo
.Ic read
@@ -3903,20 +4109,17 @@ directories to the root directory) is printed.
.Op Fl rs
.Op Ar p ...
.Xc
+.Pq regular
Reads a line of input, separates the input into fields using the
.Ev IFS
parameter (see
.Sx Substitution
-above), and assigns each field to the specified parameters
+above) or other specified means,
+and assigns each field to the specified parameters
.Ar p .
If no parameters are specified, the
.Ev REPLY
parameter is used to store the result.
-With the
-.Fl A
-and
-.Fl a
-options, only no or one parameter is accepted.
If there are more parameters than fields, the extra parameters are set to
the empty string or 0; if there are more fields than parameters, the last
parameter is assigned the remaining fields (including the word separators).
@@ -3929,8 +4132,9 @@ Store the result into the parameter
(or
.Ev REPLY )
as array of words.
+Only no or one parameter is accepted.
.It Fl a
-Store the result without word splitting into the parameter
+Store the result, without applying IFS word splitting, into the parameter
.Ar p
(or
.Ev REPLY )
@@ -3938,11 +4142,12 @@ as array of characters (wide characters if the
.Ic utf8\-mode
option is enacted, octets otherwise); the codepoints are
encoded as decimal numbers by default.
+Only no or one parameter is accepted.
.It Fl d Ar x
Use the first byte of
.Ar x ,
.Dv NUL
-if empty, instead of the ASCII newline character as input line delimiter.
+if empty, instead of the ASCII newline character to delimit input lines.
.It Fl N Ar z
Instead of reading till end-of-line, read exactly
.Ar z
@@ -3957,13 +4162,14 @@ Instead of reading till end-of-line, read up to
bytes but return as soon as any bytes are read, e.g.\& from a
slow terminal device, or if EOF or a timeout occurs.
.It Fl p
-Read from the currently active co-process, see
+Read from the currently active co-process (see
.Sx Co-processes
-above for details on this.
+above for details) instead of from a file descriptor.
.It Fl u Ns Op Ar n
-Read from the file descriptor
+Read from the file descriptor number
.Ar n
(defaults to 0, i.e.\& standard input).
+.br
The argument must immediately follow the option character.
.It Fl t Ar n
Interrupt reading after
@@ -3975,12 +4181,10 @@ 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;
+Normally,
.Ic read
-does not stop when encountering a backslash-newline sequence and
-does not store that newline in the result.
-This option enables raw mode, in which backslashes are not processed.
+strips backslash-newline sequences and any remaining backslashes from input.
+This option enables raw mode, in which backslashes are retained and ignored.
.It Fl s
The input line is saved to the history.
.El
@@ -4012,12 +4216,12 @@ exits with a non-zero status.
.Op Ns = Ns Ar value
.Ar ... Oc
.Xc
+.Pq keeps assignments , special, decl-util
Sets the read-only attribute of the named parameters.
-This is a declaration utility.
If values are given,
-parameters are set to them before setting the attribute.
-Once a parameter is
-made read-only, it cannot be unset and its value cannot be changed.
+parameters are assigned these before disallowing writes.
+Once a parameter is made read-only,
+it cannot be unset and its value cannot be changed.
.Pp
If no parameters are specified, the names of all parameters with the read-only
attribute are printed one per line, unless the
@@ -4032,40 +4236,40 @@ printed.
.Op Fl \-
.Ar name
.Xc
-Prints the resolved absolute pathname corresponding to
+.Pq defer with flags
+Resolves an absolute pathname corresponding to
.Ar name .
+If the resolved pathname either exists or can be created immediately,
+.Ic realpath
+returns 0 and prints the resolved pathname,
+otherwise or if an error occurs, it issues a diagnostic and returns nonzero.
If
.Ar name
ends with a slash
.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,
-i.e. all but the last component exist and are directories.
-For calls from the shell, if any options are given, an external
-.Xr realpath 1
-utility is preferred over the builtin.
+resolving to an extant non-directory is also treated as error.
.Pp
.It Xo
.Ic rename
.Op Fl \-
.Ar from to
.Xc
+.Pq defer always
Renames the file
.Ar from
to
.Ar to .
Both must be complete pathnames and on the same device.
-An external utility is preferred over this builtin,
-which is intended for emergency situations
-.Pq where Pa /bin/mv No becomes unusable
-and directly calls
+Intended for emergency situations
+.Pq where Pa /bin/mv No becomes unusable ;
+directly calls
.Xr rename 2 .
.Pp
.It Ic return Op Ar status
+.Pq keeps assignments , special
Returns from a function or
.Ic \&.
-script, with exit status
+script with errorlevel
.Ar status .
If no
.Ar status
@@ -4080,50 +4284,65 @@ treats both profile and
.Ev ENV
files as
.Ic \&.
-scripts, while the original Korn shell only treats profiles as
+scripts, while the original Korn shell only treated profiles as
.Ic \&.
scripts.
.Pp
+.It Ic rot13
+.Pq Li dot.mkshrc No alias
+ROT13-encrypts/-decrypts stdin to stdout.
+.Pp
.It Xo
-.Ic set Op Ic +\-abCefhiklmnprsUuvXx
-.Op Ic +\-o Ar option
-.Op Ic +\-A Ar name
+.Ic set Op Fl +abCefhiklmnprsUuvXx
+.Op Fl +o Ar option
+.Op Fl +A Ar name
.Op Fl \-
.Op Ar arg ...
.Xc
+.Pq keeps assignments , special
The
.Ic set
-command can be used to set
+command can be used to show all shell parameters
+.Pq like Ic typeset \- ,
+set
.Pq Ic \-
or clear
.Pq Ic +
-shell options, set the positional parameters, or set an array parameter.
+shell options, set an array parameter or the positional parameters.
+.Pp
Options can be changed using the
-.Cm +\-o Ar option
+.Fl +o Ar option
syntax, where
.Ar option
is the long name of an option, or using the
-.Cm +\- Ns Ar letter
+.Fl + Ns Ar letter
syntax, where
.Ar letter
is the option's single letter name (not all options have a single letter name).
-The following table lists both option letters (if they exist) and long names
-along with a description of what the option does:
+The following table lists short (if extant) and long names
+along with a description of what each option does:
.Bl -tag -width 3n
.It Fl A Ar name
Sets the elements of the array parameter
.Ar name
to
.Ar arg ...
+.Pp
If
.Fl A
is used, the array is reset (i.e. emptied) first; if
.Ic +A
is used, the first N elements are set (where N is the number of arguments);
the rest are left untouched.
+If
+.Ar name
+ends with a
+.Sq + ,
+the array is appended to instead.
.Pp
An alternative syntax for the command
-.Ic set \-A foo \-\- a b c
+.Ic set \-A foo \-\- a b c;
+.Ic set \-A foo+ \-\- d e
which is compatible to
.Tn GNU
.Nm bash
@@ -4135,14 +4354,15 @@ is:
.It Fl a \*(Ba Fl o Ic allexport
All new parameters are created with the export attribute.
.It Fl b \*(Ba Fl o Ic notify
-Print job notification messages asynchronously, instead of just before the
+Print job notification messages asynchronously instead of just before the
prompt.
-Only used if job control is enabled
+Only used with job control
.Pq Fl m .
.It Fl C \*(Ba Fl o Ic noclobber
Prevent \*(Gt redirection from overwriting existing files.
Instead, \*(Gt\*(Ba must be used to force an overwrite.
-Note that this is not safe to use for creation of temporary files or
+.Em Note:
+This is not safe to use for creation of temporary files or
lockfiles due to a TOCTOU in a check allowing one to redirect output to
.Pa /dev/null
or other device files even in
@@ -4155,16 +4375,17 @@ trap) as soon as an error occurs or a command fails (i.e. exits with a
non-zero status).
This does not apply to commands whose exit status is
explicitly tested by a shell construct such as
+.Ic !\& ,
.Ic if ,
-.Ic until ,
-.Ic while
+.Ic until
or
-.Ic \&!
+.Ic while
statements.
For
-.Ic &&
-or
-.Ic \*(Ba\*(Ba ,
+.Ic && ,
+.Ic \*(Ba\*(Ba
+and pipelines (but mind
+.Fl o Ic pipefail ) ,
only the status of the last command is tested.
.It Fl f \*(Ba Fl o Ic noglob
Do not expand file name patterns.
@@ -4176,29 +4397,34 @@ Enabled by default for non-interactive shells.
.It Fl i \*(Ba Fl o Ic interactive
The shell is an interactive shell.
This option can only be used when the shell is invoked.
-See above for a description of what this means.
+See above for details.
.It Fl k \*(Ba Fl o Ic keyword
Parameter assignments are recognised anywhere in a command.
.It Fl l \*(Ba Fl o Ic login
The shell is a login shell.
This option can only be used when the shell is invoked.
-See above for a description of what this means.
+See above for what this means.
.It Fl m \*(Ba Fl o Ic monitor
Enable job control (default for interactive shells).
.It Fl n \*(Ba Fl o Ic noexec
Do not execute any commands.
-Useful for checking the syntax of scripts
-(ignored if interactive).
+Useful for checking the syntax of scripts.
+Ignored if reading commands from a tty.
.It Fl p \*(Ba Fl o Ic privileged
The shell is a privileged shell.
It is set automatically if, when the shell starts,
the real UID or GID does not match
the effective UID (EUID) or GID (EGID), respectively.
See above for a description of what this means.
+.Pp
+If the shell is privileged, setting this flag after startup files
+have been processed let it go full setuid and/or setgid.
+Clearing this flag makes the shell drop privileges.
+Changing this flag resets the groups vector.
.It Fl r \*(Ba Fl o Ic restricted
The shell is a restricted shell.
This option can only be used when the shell is invoked.
-See above for a description of what this means.
+See above for what this means.
.It Fl s \*(Ba Fl o Ic stdin
If used when the shell is invoked, commands are read from standard input.
Set automatically if the shell is invoked with no arguments.
@@ -4207,12 +4433,11 @@ When
.Fl s
is used with the
.Ic set
-command it causes the specified arguments to be sorted before assigning them to
-the positional parameters (or to array
+command it causes the specified arguments to be sorted ASCIIbetically
+before assigning them to the positional parameters (or to array
.Ar name ,
-if
-.Fl A
-is used).
+with
+.Fl A ) .
.It Fl U \*(Ba Fl o Ic utf8\-mode
Enable UTF-8 support in the
.Sx Emacs editing mode
@@ -4258,23 +4483,24 @@ Write shell input to standard error as it is read.
.It Fl X \*(Ba Fl o Ic markdirs
Mark directories with a trailing
.Ql /
-during file name generation.
+during globbing.
.It Fl x \*(Ba Fl o Ic xtrace
-Print command trees when they are executed, preceded by
-the value of
+Print commands when they are executed, preceded by
.Ev PS4 .
.It Fl o Ic bgnice
Background jobs are run with lower priority.
.It Fl o Ic braceexpand
-Enable brace expansion (a.k.a. alternation).
+Enable brace expansion.
This is enabled by default.
.It Fl o Ic emacs
Enable BRL emacs-like command-line editing (interactive shells only); see
.Sx Emacs editing mode .
+Enabled by default.
.It Fl o Ic gmacs
Enable gmacs-like command-line editing (interactive shells only).
-Currently identical to emacs editing except that transpose\-chars (\*(haT) acts
-slightly differently.
+Currently identical to emacs editing except that
+.Li transpose\-chars Pq \*(haT
+acts slightly differently.
.It Fl o Ic ignoreeof
The shell will not (easily) exit when end-of-file is read;
.Ic exit
@@ -4285,8 +4511,7 @@ is read 13 times in a row.
.It Fl o Ic inherit\-xtrace
Do not reset
.Fl o Ic xtrace
-upon entering functions.
-This is enabled by default.
+upon entering functions (default).
.It Fl o Ic nohup
Do not kill running jobs with a
.Dv SIGHUP
@@ -4301,7 +4526,7 @@ doesn't have this option, but does send the
signal.
.It Fl o Ic nolog
No effect.
-In the original Korn shell, this prevents function definitions from
+In the original Korn shell, this prevented function definitions from
being stored in the history file.
.It Fl o Ic physical
Causes the
@@ -4324,14 +4549,14 @@ parameter; only the
.Ic cd
command changes
.Ev PWD .
-See the
+See
.Ic cd
and
.Ic pwd
-commands above for more details.
+above for more details.
.It Fl o Ic pipefail
-Make the exit status of a pipeline (before logically complementing) the
-rightmost non-zero errorlevel, or zero if all commands exited with zero.
+Make the exit status of a pipeline the rightmost non-zero errorlevel,
+or zero if all commands exited with zero.
.It Fl o Ic posix
Behave closer to the standards
(see
@@ -4339,15 +4564,17 @@ Behave closer to the standards
for details).
Automatically enabled if the basename of the shell invocation begins with
.Dq sh
-and this autodetection feature is compiled in
-.Pq not in MirBSD .
+and this autodetection feature is compiled in (not in MirBSD; commonly used for
+.Nm lksh
+only).
As a side effect, setting this flag turns off the
.Ic braceexpand
and
.Ic utf8\-mode
flags, which can be turned back on manually, and
+.Pq unless both are enabled at the same time
.Ic sh
-mode (unless both are enabled at the same time).
+mode.
.It Fl o Ic sh
Enable
.Pa /bin/sh
@@ -4357,7 +4584,7 @@ mode (see
Automatically enabled if the basename of the shell invocation begins with
.Dq sh
and this autodetection feature is compiled in
-.Pq not in MirBSD .
+.Pq rather uncommon .
As a side effect, setting this flag turns off
.Ic braceexpand
mode, which can be turned back on manually, and
@@ -4371,19 +4598,20 @@ See
.Sx Vi editing mode
for documentation and limitations.
.It Fl o Ic vi\-esccomplete
-In vi command-line editing, do command and file name completion when escape
-(\*(ha[) is entered in command mode.
+In vi command-line editing, do command and file name completion when Esc
+.Pq \*(ha[
+is entered in command mode.
.It Fl o Ic vi\-tabcomplete
-In vi command-line editing, do command and file name completion when tab (\*(haI)
-is entered in insert mode.
-This is the default.
+In vi command-line editing, do command and file name completion when Tab
+.Pq \*(haI
+is entered in insert mode (default).
.It Fl o Ic viraw
No effect.
In the original Korn shell, unless
.Ic viraw
was set, the vi command-line mode would let the
.Xr tty 4
-driver do the work until ESC (\*(ha[) was entered.
+driver do the work until Esc was entered.
.Nm
is always in viraw mode.
.El
@@ -4407,40 +4635,65 @@ order, to the positional parameters (i.e. $1, $2, etc.).
If options end with
.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
.Dq Li \-
-option is treated specially \*(en it clears both the
+option is treated specially\*(EMit clears both the
.Fl v
and
.Fl x
options.
+If no options or arguments are given, the values of all parameters are printed
+.Pq suitably quoted .
+.Pp
+.It Xo
+.Ic setenv
+.Op Ar name Op Ar value
+.Xc
+.Pq Li dot.mkshrc No function
+Without arguments, display the names and values of all exported parameters.
+Otherwise, set
+.Ar name Ns 's
+export attribute, and its value to
+.Ar value Pq empty string if none given .
.Pp
.It Ic shift Op Ar number
+.Pq keeps assignments , special
The positional parameters
.Ar number Ns +1 ,
.Ar number Ns +2 ,
-etc. are renamed to 1, 2, etc.
-.Ar number
-defaults to 1.
+etc.
+.Pq Ar number No defaults to 1
+are renamed to 1, 2, etc.
.Pp
.It Ic sleep Ar seconds
+.Pq regular , needs Xr select 2
Suspends execution for a minimum of the
.Ar seconds
-specified as positive decimal value with an optional fractional part.
+(specified as positive decimal value with an optional fractional part).
Signal delivery may continue execution earlier.
.Pp
+.It Ic smores Op Ar
+.Pq Li dot.mkshrc No function
+Simple pager:
+.Aq Enter
+next;
+.So q Sc Ns + Ns Aq Enter
+quit
+.Pp
.It Ic source Ar file Op Ar arg ...
+.Pq keeps assignments
Like
-.Ic \&. Po Do dot Dc Pc ,
+.Ic \&.
+.Pq Dq dot ,
except that the current working directory is appended to the
-search path (GNU
-.Nm bash
-extension).
+search path.
+.Pq GNU Nm bash No extension
.Pp
.It Ic suspend
+.Pq needs job control and Xr getsid 2
Stops the shell as if it had received the suspend character from
the terminal.
+.Pp
It is not possible to suspend a login shell unless the parent process
is a member of the same terminal session but is a member of a different
process group.
@@ -4450,22 +4703,24 @@ it can be suspended.
.Pp
.It Ic test Ar expression
.It Ic \&[ Ar expression Ic \&]
+.Pq regular
.Ic test
evaluates the
.Ar expression
-and returns zero status if true, 1 if false, or greater than 1 if there
-was an error.
-It is normally used as the condition command of
+and exits with status code 0 if true, 1 if false,
+or greater than 1 if there was an error.
+It is often used as the condition command of
.Ic if
and
.Ic while
statements.
-Symbolic links are followed for all
+All
.Ar file
-expressions except
+expressions, except
.Fl h
and
-.Fl L .
+.Fl L ,
+follow symbolic links.
.Pp
The following basic expressions are available:
.Bl -tag -width 17n
@@ -4502,7 +4757,7 @@ is a symbolic link.
.It Fl k Ar file
.Ar file Ns 's
mode has the
-.Xr sticky 8
+.Xr sticky 7
bit set.
.It Fl L Ar file
.Ar file
@@ -4592,7 +4847,7 @@ The same can be achieved with [ \-o ?foo ] like in
.At
.Nm ksh93 .
.Ar option
-can also be the short flag led by either
+can also be the short flag prefixed with either
.Ql \-
or
.Ql +
@@ -4605,14 +4860,22 @@ instead of
.Dq Li xtrace .
.It Ar string No = Ar string
Strings are equal.
+In double brackets, pattern matching
+.Pq R59+ using extglobs
+occurs if the right-hand string isn't quoted.
.It Ar string No == Ar string
-Strings are equal.
+Same as
+.Sq =
+.Pq deprecated .
+.It Ar string No != Ar string
+Strings are not equal.
+See
+.Sq =
+regarding pattern matching.
.It Ar string No \*(Gt Ar string
First string operand is greater than second string operand.
.It Ar string No \*(Lt Ar string
First string operand is less than second string operand.
-.It Ar string No != Ar string
-Strings are not equal.
.It Ar number Fl eq Ar number
Numbers compare equal.
.It Ar number Fl ne Ar number
@@ -4659,6 +4922,7 @@ respectively; three-argument forms ultimately prefer binary operations,
followed by negation and parenthesis lowering; two- and four-argument forms
prefer negation followed by parenthesis; the one-argument form always implies
.Fl n .
+To assume this is not necessarily portable.
.Pp
.Sy Note :
A common mistake is to use
@@ -4673,14 +4937,13 @@ or
.Dq Li \-n .
Use tests like
.Dq Li if \&[ x\&"$foo\&" = x"bar" \&]
-instead, or the double-bracket operator
-.Dq Li if \&[[ $foo = bar \&]]
-or, to avoid pattern matching (see
+instead, or the double-bracket operator (see
.Ic \&[[
above):
-.Dq Li if \&[[ $foo = \&"$bar" \&]]
-.Pp
-The
+.Dq Li if \&[[ $foo = bar \&]]
+or, to avoid pattern matching,
+.Dq Li if \&[[ $foo = \&"$bar" \&]] ;
+the
.Ic \&[[ ... \&]]
construct is not only more secure to use but also often faster.
.Pp
@@ -4689,37 +4952,35 @@ construct is not only more secure to use but also often faster.
.Op Fl p
.Op Ar pipeline
.Xc
+.Pq reserved word
If a
.Ar pipeline
is given, the times used to execute the pipeline are reported.
If no pipeline
is given, then the user and system time used by the shell itself, and all the
commands it has run since it was started, are reported.
+.Pp
The times reported are the real time (elapsed time from start to finish),
the user CPU time (time spent running in user mode), and the system CPU time
(time spent running in kernel mode).
+.Pp
Times are reported to standard error; the format of the output is:
.Pp
.Dl "0m0.03s real 0m0.02s user 0m0.01s system"
.Pp
If the
.Fl p
-option is given the output is slightly longer:
+option is given (which is only permitted if
+.Ar pipeline
+is a simple command), the output is slightly longer:
.Bd -literal -offset indent
real 0.03
user 0.02
sys 0.01
.Ed
.Pp
-It is an error to specify the
-.Fl p
-option unless
-.Ar pipeline
-is a simple command.
-.Pp
-Simple redirections of standard error do not affect the output of the
-.Ic time
-command:
+Simple redirections of standard error do not affect
+.Ic time Ns 's output :
.Pp
.Dl $ time sleep 1 2\*(Gtafile
.Dl $ { time sleep 1; } 2\*(Gtafile
@@ -4729,8 +4990,9 @@ Times for the first command do not go to
but those of the second command do.
.Pp
.It Ic times
-Print the accumulated user and system times used both by the shell
-and by processes that the shell started which have exited.
+.Pq keeps assignments , special
+Print the accumulated user and system times (see above) used both
+by the shell and by processes that the shell started which have exited.
The format of the output is:
.Bd -literal -offset indent
0m0.01s 0m0.00s
@@ -4738,6 +5000,7 @@ The format of the output is:
.Ed
.Pp
.It Ic trap Ar n Op Ar signal ...
+.Pq keeps assignments , special
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
@@ -4745,11 +5008,10 @@ with a dash
.Pq Dq Li \-
as
.Ar handler ,
-followed by the arguments
-.Pq Ar n Op Ar signal ... ,
-all of which are treated as signals.
+followed by the arguments (interpreted as signals).
.Pp
.It Ic trap Op Ar handler signal ...
+.Pq keeps assignments , special
Sets a trap handler that is to be executed when any of the specified
.Ar signal Ns s
are received.
@@ -4758,13 +5020,21 @@ 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
+or a string comprised of shell commands to be executed at the first opportunity
(i.e. when the current command completes or before printing the next
.Ev PS1
prompt) after receipt of one of the signals.
.Ar signal
-is the name of a signal
-.Pq e.g.\& Dv PIPE or Dv ALRM
+is the name, possibly prefixed with
+.Dq Li SIG ,
+of a signal
+.Po
+e.g.\&
+.Dv PIPE ,
+.Dv ALRM
+or
+.Dv SIGINT
+.Pc
or the number of the signal (see the
.Ic kill Fl l
command above).
@@ -4782,6 +5052,13 @@ or
option were set.
.Dv EXIT
handlers are executed in the environment of the last executed command.
+The original Korn shell's
+.Dv DEBUG
+trap and handling of
+.Dv ERR
+and
+.Dv EXIT
+in functions are not yet implemented.
.Pp
Note that, for non-interactive shells, the trap handler cannot be changed
for signals that were ignored when the shell started.
@@ -4792,23 +5069,22 @@ the shell started is shown as a series of
commands.
Note that the output of
.Ic trap
-cannot be usefully piped to another process (an artifact of the fact that
-traps are cleared when subprocesses are created).
-.Pp
-The original Korn shell's
-.Dv DEBUG
-trap and the handling of
-.Dv ERR
-and
-.Dv EXIT
-traps in functions are not yet implemented.
+cannot be usefully captured or piped to another process (an artifact
+of the fact that traps are cleared when subprocesses are created).
.Pp
.It Ic true
-A command that exits with a zero value.
+.Pq regular
+A command that exits with a zero status.
+.Pp
+.It Ic type Ar name ...
+.Pq built-in alias
+Reveal how
+.Ar name
+would be interpreted as command.
.Pp
.It Xo
.Ic typeset
-.Op Ic +\-aglpnrtUux
+.Op Fl +aglpnrtUux
.Oo Fl L Ns Op Ar n
.No \*(Ba Fl R Ns Op Ar n
.No \*(Ba Fl Z Ns Op Ar n Oc
@@ -4822,29 +5098,35 @@ A command that exits with a zero value.
.Fl f Op Fl tux
.Op Ar name ...
.Xc
-Display or set parameter attributes.
-This is a declaration utility.
+.Pq keeps assignments , decl-util
+Display or set attributes of shell parameters or functions.
With no
.Ar name
-arguments, parameter attributes are displayed; if no options are used, the
-current attributes of all parameters are printed as
+arguments, parameter attributes are shown;
+if no options are used, the current attributes of all parameters are printed as
.Ic typeset
commands; if an option is given (or
.Dq Li \-
with no option letter), all parameters and their values with the specified
attributes are printed; if options are introduced with
-.Ql + ,
-parameter values are not printed.
+.Ql +
+.Po
+or
+.Dq Li +
+alone
+.Pc ,
+only names are printed.
.Pp
-If
+If any
.Ar name
-arguments are given, the attributes of the named parameters are set
+arguments are given, the attributes of the so named parameters are set
.Pq Ic \&\-
or cleared
.Pq Ic \&+ ;
-inside a function, this will cause the parameters to be created
-(with no value) in the local scope (but see
-.Fl g ) .
+inside a function, this will cause the parameters to be created (and set to
+.Dq
+if no value is given) in the local scope
+.Pq except if Fl g No is used .
Values for parameters may optionally be specified.
For
.Ar name Ns \&[*] ,
@@ -4861,36 +5143,39 @@ arguments are given,
functions are listed with their values (i.e. definitions) unless
options are introduced with
.Ql + ,
-in which case only the function names are reported.
+in which case only the names are displayed.
.Bl -tag -width Ds
.It Fl a
Indexed array attribute.
.It Fl f
Function mode.
-Display or set functions and their attributes, instead of parameters.
+Display or set shell functions and their attributes,
+instead of shell parameters.
.It Fl g
+.Dq global
+mode.
Do not cause named parameters to be created in
the local scope when called inside a function.
.It Fl i Ns Op Ar n
Integer attribute.
.Ar n
-specifies the base to use when displaying the integer (if not specified, the
-base given in the first assignment is used).
-Parameters with this attribute may
-be assigned values containing arithmetic expressions.
+specifies the base to use when stringifying the integer
+(if not specified, the base given in the first assignment is used).
+Parameters with this attribute
+may be assigned arithmetic expressions for values.
.It Fl L Ns Op Ar n
Left justify attribute.
.Ar n
specifies the field width.
If
.Ar n
-is not specified, the current width of a parameter (or the width of its first
-assigned value) is used.
-Leading whitespace (and zeros, if used with the
+is not specified, the current width of the parameter
+(or the width of its first assigned value) is used.
+Leading whitespace (and digit zeros, if used with the
.Fl Z
option) is stripped.
-If necessary, values are either truncated or space padded
-to fit the field width.
+If necessary, values are either truncated
+or padded with space to fit the field width.
.It Fl l
Lower case attribute.
All upper case ASCII characters in values are converted to lower case.
@@ -4917,7 +5202,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 parameters, instead of using
+passed as parameters, instead of resorting to
.Ic eval .
.It Fl p
Print complete
@@ -4930,32 +5215,32 @@ Right justify attribute.
specifies the field width.
If
.Ar n
-is not specified, the current width of a parameter (or the width of its first
-assigned value) is used.
+is not specified, the current width of the parameter
+(or the width of its first assigned value) is used.
Trailing whitespace is stripped.
-If necessary, values are either stripped of leading characters or space
-padded to make them fit the field width.
+If necessary, values are either stripped of leading characters
+or padded with space to fit the field width.
.It Fl r
Read-only attribute.
Parameters with this attribute may not be assigned to or unset.
Once this attribute is set, it cannot be turned off.
.It Fl t
Tag attribute.
-Has no meaning to the shell; provided for application use.
+This attribute has no meaning to the shell for parameters
+and is provided for application use.
.Pp
For functions,
.Fl t
is the trace attribute.
When functions with the trace attribute are executed, the
-.Ic xtrace
+.Fl o Ic xtrace
.Pq Fl x
shell option is temporarily turned on.
.It Fl U
Unsigned integer attribute.
-Integers are printed as unsigned values (combine with the
+Integers are printed as unsigned values (combined with the
.Fl i
option).
-This option is not in the original Korn shell.
.It Fl u
Upper case attribute.
All lower case ASCII characters in values are converted to upper case.
@@ -4971,7 +5256,8 @@ above.)
.Pp
For functions,
.Fl u
-is the undefined attribute.
+is the undefined attribute, used with
+.Ev FPATH .
See
.Sx Functions
above for the implications of this.
@@ -5005,29 +5291,30 @@ unless they are also given on the same command line.
.Pp
.It Xo
.Ic ulimit
-.Op Fl aBCcdefHilMmnOPpqrSsTtVvw
+.Op Fl aBCcdefHilMmnOPpqrSsTtVvwx
.Op Ar value
.Xc
+.Pq regular
Display or set process limits.
If no options are used, the file size limit
.Pq Fl f
is assumed.
.Ar value ,
if specified, may be either an arithmetic expression or the word
-.Dq unlimited .
+.Dq Li unlimited .
The limits affect the shell and any processes created by the shell after a
limit is imposed.
-Note that some systems may not allow limits to be increased
+Note that systems may not allow some limits to be increased
once they are set.
Also note that the types of limits available are system
-dependent \*(en some systems have only the
+dependent\*(EMsome systems have only the
.Fl f
-limit, or not even that, or can set only the soft limits
+limit, or not even that, or can set only the soft limits, etc.
.Bl -tag -width 5n
.It Fl a
-Display all limits; unless
+Display all limits (soft limits unless
.Fl H
-is used, soft limits are displayed.
+is used).
.It Fl B Ar n
Set the socket buffer size to
.Ar n
@@ -5040,19 +5327,22 @@ Impose a size limit of
.Ar n
blocks on the size of core dumps.
.It Fl d Ar n
-Impose a size limit of
+Limit the size of the data area to
.Ar n
-kibibytes on the size of the data area.
+kibibytes.
.It Fl e Ar n
Set the maximum niceness to
.Ar n .
.It Fl f Ar n
Impose a size limit of
.Ar n
-blocks on files written by the shell and its child processes (files of any
-size may be read).
+blocks on files written by the shell and its child processes
+.Pq any size may be read .
.It Fl H
Set the hard limit only (the default is to set both hard and soft limits).
+With
+.Fl a ,
+display all hard limits.
.It Fl i Ar n
Set the number of pending signals to
.Ar n .
@@ -5078,10 +5368,23 @@ Set the number of AIO operations to
.It Fl P Ar n
Limit the number of threads per process to
.Ar n .
+.Pp
+This option mostly matches
+.At
+.Nm ksh93 Ns 's
+.Fl T ;
+.br
+on
+.Tn AIX ,
+see
+.Fl r
+as used by its
+.Nm ksh
+though.
.It Fl p Ar n
Impose a limit of
.Ar n
-processes that can be run by the user at any one time.
+processes that can be run by the user (uid) at any one time.
.It Fl q Ar n
Limit the size of
.Tn POSIX
@@ -5089,18 +5392,28 @@ message queues to
.Ar n
bytes.
.It Fl r Ar n
+.Pq Cm AIX
+Limit the number of threads per process to
+.Ar n .
+.br
+.Pq Cm Linux
Set the maximum real-time priority to
.Ar n .
.It Fl S
Set the soft limit only (the default is to set both hard and soft limits).
+With
+.Fl a ,
+display soft limits (default).
.It Fl s Ar n
-Impose a size limit of
+Limit the size of the stack area to
.Ar n
-kibibytes on the size of the stack area.
+kibibytes.
.It Fl T Ar n
Impose a time limit of
.Ar n
-real seconds to be used by each process.
+real seconds
+.Pq Dq humantime
+to be used by each process.
.It Fl t Ar n
Impose a time limit of
.Ar n
@@ -5113,9 +5426,12 @@ Impose a limit of
.Ar n
kibibytes on the amount of virtual memory (address space) used.
.It Fl w Ar n
-Impose a limit of
+Limit the amount of swap space used to at most
.Ar n
-kibibytes on the amount of swap space used.
+kibibytes.
+.It Fl x Ar n
+Set the maximum number of file locks to
+.Ar n .
.El
.Pp
As far as
@@ -5127,6 +5443,7 @@ is concerned, a block is 512 bytes.
.Op Fl S
.Op Ar mask
.Xc
+.Pq regular
Display or set the file permission creation mask or umask (see
.Xr umask 2 ) .
If the
@@ -5150,6 +5467,7 @@ and is equivalent (on most systems) to the octal mask
.Op Fl adt
.Op Ar name ...
.Xc
+.Pq regular
The aliases for the given names are removed.
If the
.Fl a
@@ -5166,31 +5484,27 @@ directory aliases, respectively.
.Op Fl fv
.Ar parameter ...
.Xc
+.Pq keeps assignments , special
Unset the named parameters
-.Po
-.Fl v ,
-the default
-.Pc
+.Pq Fl v , No the default
or functions
.Pq Fl f .
With
.Ar parameter Ns \&[*] ,
-attributes are kept, only values are unset.
-.Pp
-The exit status is non-zero if any of the parameters have the read-only
-attribute set, zero otherwise.
+attributes are retained, only values are unset.
+The exit status is non-zero if any of the parameters are read-only,
+zero otherwise (not portable).
.Pp
.It Ic wait Op Ar job ...
+.Pq regular
Wait for the specified job(s) to finish.
The exit status of
.Ic wait
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
+exit status is 128 + the signal number (see
.Ic kill Fl l Ar exit-status
-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.
+above); if the last specified job cannot be found (because it never existed
+or had already finished), the exit status is 127.
See
.Sx Job control
below for the format of
@@ -5215,20 +5529,44 @@ If job monitoring is enabled, the completion status of jobs is printed
.Op Fl pv
.Op Ar name ...
.Xc
+.Pq regular
Without the
.Fl v
option, it is the same as
.Ic command Fl v ,
-except aliases are not printed as alias command.
+except aliases are printed as their definition only.
With the
.Fl v
-option, it is exactly the same as
+option, it is exactly identical to
.Ic command Fl V .
-In either case, the
+In either case, with the
.Fl p
-option differs: the search path is not affected in
-.Ic whence ,
-but the search is restricted to the path.
+option the search is restricted to the (current)
+.Ev PATH .
+.Pp
+.It Xo Ic which
+.Op Fl a
+.Op Ar name ...
+.Xc
+.Pq Li dot.mkshrc No function
+Without
+.Fl a ,
+behaves like
+.Ic whence Fl p
+(does a
+.Ev PATH
+search for each
+.Ar name
+printing the resulting pathname if found); with
+.Fl a ,
+matches in all
+.Ev PATH
+components are printed, i.e. the search is not stopped after a match.
+If no
+.Ar name
+was matched, the exit status is 2; if every name was matched, it is zero,
+otherwise it is 1.
+No diagnostics are produced on failure to match.
.El
.Ss Job control
Job control refers to the shell's ability to monitor and control jobs which
@@ -5323,9 +5661,9 @@ The job exited.
.Ar number
is the exit status of the job which is omitted if the status is zero.
.It Running
-The job has neither stopped nor exited (note that running does not necessarily
-mean consuming CPU time \*(en
-the process could be blocked waiting for some event).
+The job has neither stopped nor exited (note that running does not
+necessarily mean consuming CPU time\*(EMthe process could be blocked
+waiting for some event).
.It Stopped Op Ar signal
The job was stopped by the indicated
.Ar signal
@@ -5560,7 +5898,7 @@ an
.Op Ar n
(if the command can be prefixed with a count); and any keys the command is
bound to by default, written using caret notation
-e.g. the ASCII ESC character is written as \*(ha[.
+e.g. the ASCII Esc character is written as \*(ha[.
These control sequences are not case sensitive.
A count prefix for a command is entered using the sequence
.Pf \*(ha[ Ns Ar n ,
@@ -5660,7 +5998,7 @@ If only one completion is possible,
match as in the
.Ic complete
command above.
-Note that \*(haI is usually generated by the TAB (tabulator) key.
+Note that \*(haI is usually generated by the Tab (tabulator) key.
.It Xo delete\-char\-backward:
.Op Ar n
.No ERASE Pq \*(haH ,
@@ -5833,6 +6171,10 @@ third-last, etc.) command is inserted at the cursor.
Use of this editing command trashes the mark.
.It quote: \*(ha\*(ha , \*(haV
The following character is taken literally rather than as an editing command.
+.It quote\-region: \*(ha[Q
+Escapes the text between the mark and the cursor position
+.Pq the entire line if no mark is set
+into a shell command argument.
.It redraw: \*(haL
Reprints the last line of the prompt string and the current input line
on a new line.
@@ -5926,11 +6268,12 @@ replaces the inserted text string with the next previously killed text string.
.Pp
The tab completion escapes characters the same way as the following code:
.Bd -literal
-print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
+print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e`\e{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
.Ed
.Ss Vi editing mode
.Em Note:
-The vi command-line editing mode is orphaned, yet still functional.
+The vi command-line editing mode has not yet been brought up to the
+same quality and feature set as the emacs mode.
It is 8-bit clean but specifically does not support UTF-8 or MBCS.
.Pp
The vi command-line editor in
@@ -5944,9 +6287,9 @@ You start out in insert mode.
.It
There are file name and command completion commands:
=, \e, *, \*(haX, \*(haE, \*(haF and, optionally,
-.Aq tab
+.Aq Tab
and
-.Aq esc .
+.Aq Esc .
.It
The
.Ic _
@@ -6010,9 +6353,9 @@ The next character typed is not treated specially (can be used
to insert the characters being described here).
.It \*(haX
Command and file name expansion (see below).
-.It Aq esc
+.It Aq Esc
Puts the editor in command mode (see below).
-.It Aq tab
+.It Aq Tab
Optional file name and command completion (see
.Ic \*(haF
above), enabled with
@@ -6089,7 +6432,7 @@ The actual command executed is
Command or file name expansion is applied to the current big-word (with an
appended
.Ql *
-if the word contains no file globbing characters) \*(en the big-word is replaced
+if the word contains no file globbing characters)\*(EMthe big-word is replaced
with the resulting words.
If the current big-word is the first on the line
or follows one of the characters
@@ -6114,18 +6457,18 @@ word and the editor is in insert mode.
.It Xo
.Oo Ar n Oc Ns \e ,
.Oo Ar n Oc Ns \*(haF ,
-.Oo Ar n Oc Ns Aq tab ,
+.Oo Ar n Oc Ns Aq Tab ,
.No and
-.Oo Ar n Oc Ns Aq esc
+.Oo Ar n Oc Ns Aq Esc
.Xc
Command/file name completion.
Replace the current big-word with the
longest unique match obtained after performing command and file name expansion.
-.Aq tab
+.Aq Tab
is only recognised if the
.Ic vi\-tabcomplete
option is set, while
-.Aq esc
+.Aq Esc
is only recognised if the
.Ic vi\-esccomplete
option is set (see
@@ -6147,7 +6490,7 @@ The restoring keypress is ignored.
.It @ Ns Ar c
Macro expansion.
Execute the commands found in the alias
-.Ar c .
+.Li _ Ns Ar c .
.El
.Pp
Intra-line movement commands:
@@ -6334,7 +6677,12 @@ occurrence of the last search string;
the direction of the search is the opposite of the last search.
.It Ar ANSI-CurUp , PC-PgUp
Take the characters from the beginning of the line to the current
-cursor position as search string and do a backwards history search
+cursor position as search string and do a history search, backwards,
+for lines beginning with this string; keep the cursor position.
+This works only in insert mode and keeps it enabled.
+.It Ar ANSI-CurDown , PC-PgDn
+Take the characters from the beginning of the line to the current
+cursor position as search string and do a history search, forwards,
for lines beginning with this string; keep the cursor position.
This works only in insert mode and keeps it enabled.
.El
@@ -6349,7 +6697,7 @@ Append text
times; goes into insert mode just after the current position.
The append is
only replicated if command mode is re-entered i.e.\&
-.Aq esc
+.Aq Esc
is used.
.It Xo
.Oo Ar n Oc Ns A
@@ -6365,7 +6713,7 @@ Insert text
times; goes into insert mode at the current position.
The insertion is only
replicated if command mode is re-entered i.e.\&
-.Aq esc
+.Aq Esc
is used.
.It Xo
.Oo Ar n Oc Ns I
@@ -6494,7 +6842,7 @@ removed to the history and a new prompt to be printed.
.It Pa \*(TI/.mkshrc
User mkshrc profile (non-privileged interactive shells); see
.Sx Startup files.
-The location can be changed at compile time (for embedded systems);
+The location can be changed at compile time (e.g. for embedded systems);
AOSP Android builds use
.Pa /system/etc/mkshrc .
.It Pa \*(TI/.profile
@@ -6507,7 +6855,7 @@ System profile (login shells); see
.It Pa /etc/shells
Shell database.
.It Pa /etc/suid_profile
-Suid profile (privileged shells); see
+Privileged shells' profile (sugid); see
.Sx Startup files.
.El
.Pp
@@ -6545,6 +6893,12 @@ contains the system and suid profile.
.Xr utf\-8 7 ,
.Xr mknod 8
.Pp
+The FAQ at
+.Pa http://www.mirbsd.org/mksh\-faq.htm
+or in the
+.Pa mksh.faq
+file.
+.Pp
.Pa http://www.mirbsd.org/ksh\-chan.htm
.Rs
.%A Morris Bolsky
@@ -6575,7 +6929,7 @@ contains the system and suid profile.
.Re
.Rs
.%A "IEEE Inc."
-.%T "\\*(tNIEEE\\*(sP Standard for Information Technology \*(en Portable Operating System Interface (POSIX)"
+.%T "\\*(tNIEEE\\*(sP Standard for Information Technology\*(EMPortable Operating System Interface (POSIX)"
.%V "Part 2: Shell and Utilities"
.%D 1993
.%I "IEEE Press"
@@ -6669,70 +7023,25 @@ The complete legalese is at:
.\"
.Sh CAVEATS
.Nm mksh
-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.
This may deviate from POSIX in historic or opinionated places.
.Ic set Fl o Ic posix
(see
.Sx POSIX mode
for details)
-will cause the shell to behave more conformant.
-.Pp
-For the purpose of
-.Tn POSIX ,
+will make the shell more conformant, but mind the FAQ (see
+.Sx SEE ALSO ) ,
+especially regarding locales.
.Nm mksh
-supports only the
-.Dq C
-locale.
-.Nm mksh Ns 's
-.Ic utf8\-mode
-.Em must
-be disabled in POSIX mode, and it
-only supports the BMP (Basic Multilingual Plane) of UCS and maps
-raw octets into the U+EF80..U+EFFF wide character range; compare
-.Sx Arithmetic expressions .
-The following
-.Tn POSIX
-.Nm sh Ns -compatible
-code toggles the
-.Ic utf8\-mode
-option dependent on the current
-.Tn POSIX
-locale for mksh to allow using the UTF-8 mode, within the constraints
-outlined above, in code portable across various shell implementations:
-.Bd -literal -offset indent
-case ${KSH_VERSION:\-} in
-*MIRBSD\ KSH*\*(Ba*LEGACY\ KSH*)
- case ${LC_ALL:\-${LC_CTYPE:\-${LANG:\-}}} in
- *[Uu][Tt][Ff]8*\*(Ba*[Uu][Tt][Ff]\-8*) set \-U ;;
- *) set +U ;;
- esac ;;
-esac
-.Ed
-In near future, (UTF-8) locale tracking will be implemented though.
+.Pq but not Nm lksh
+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
-Using
-.Ic set Fl o Ic pipefail
-makes the following construct error out:
-.Bd -literal -offset indent
-set -e
-for x in 1 2; do
- false && echo $x
-done \*(Ba cat
-.Ed
-This is because, while the
-.Dq Li &&\&
-ensures that the inner command's failure is not taken, it sets
-the entire for..done loop's errorlevel, which is passed on by
-.Fl o Ic pipefail .
-Invert the inner command:
-.Li true \*(Ba\*(Ba echo $x
-.Pp
-See also the FAQ below.
+.Nm mksh
+currently uses OPTU-16 internally, which is the same as UTF-8 and CESU-8
+with 0000..FFFD being valid codepoints; raw octets are mapped into the
+PUA range EF80..EFFF, which is assigned by CSUR for this purpose.
.Sh BUGS
Suspending (using \*(haZ) pipelines like the one below will only suspend
the currently running part of the pipeline; in this example,
@@ -6756,7 +7065,7 @@ for the in-memory portion of the history is slow, should use
.Xr memmove 3 .
.Pp
This document attempts to describe
-.Nm mksh\ R57
+.Nm mksh\ R59
and up,
.\" with vendor patches from insert-your-name-here,
compiled without any options impacting functionality, such as
@@ -6784,187 +7093,3 @@ IRC channel at
.Pq Port 6697 SSL, 6667 unencrypted ,
or at:
.Pa https://launchpad.net/mksh
-.Sh FREQUENTLY ASKED QUESTIONS
-This FAQ attempts to document some of the questions users of
-.Nm
-or readers of this manual page may encounter.
-.Ss I'm an Android user, so what's mksh?
-.Nm mksh
-is a
-.Ux
-shell / command interpreter, similar to
-.Nm COMMAND.COM
-or
-.Nm CMD.EXE ,
-which has been included with
-.Tn Android Open Source Project
-for a while now.
-Basically, it's a program that runs in a terminal (console window),
-takes user input and runs commands or scripts, which it can also
-be asked to do by other programs, even in the background.
-Any privilege pop-ups you might be encountering are thus not
-.Nm mksh
-issues but questions by some other program utilising it.
-.Ss "I'm an OS/2 user, what do I need to know?"
-Unlike the native command prompt, the current working directory is,
-for security reasons common on Unix systems which the shell is designed for,
-not in the search path at all; if you really need this, run the command
-.Li PATH=.$PATHSEP$PATH
-or add that to a suitable initialisation file.
-.Pp
-There are two different newline modes for mksh-os2: standard (Unix) mode,
-in which only LF (0A hex) is supported as line separator, and "textmode",
-which also accepts ASCII newlines (CR+LF), like most other tools on OS/2,
-but creating an incompatibility with standard
-.Nm .
-If you compiled mksh from source, you will get the standard Unix mode unless
-.Fl T
-is added during compilation; you will most likely have gotten this shell
-through komh's port on Hobbes, or from his OS/2 Factory on eComStation
-Korea, which uses "textmode", though.
-Most OS/2 users will want to use "textmode" unless they need absolute
-compatibility with Unix
-.Nm .
-.Ss "How do I start mksh on a specific terminal?"
-Normally:
-.Dl mksh \-T/dev/tty2
-.Pp
-However, if you want for it to return (e.g. for an embedded
-system rescue shell), use this on your real console device instead:
-.Dl mksh \-T!/dev/ttyACM0
-.Pp
-.Nm
-can also daemonise (send to the background):
-.Dl mksh \-T\- \-c \*(aqexec cdio lock\*(aq
-.Ss "POSIX says..."
-Run the shell in POSIX mode (and possibly
-.Nm lksh
-instead of
-.Nm mksh ) :
-.Dl set \-o posix
-.Ss "I forbid stat(2) in my SELinux policy, and some things do not work!"
-Don't break Unix.
-Read up on the GIGO principle.
-Duh.
-.Ss "My prompt from <some other shell> does not work!"
-Contact us on the mailing list or on IRC, we'll convert it for you.
-.Ss "Something is going wrong with my while...read loop"
-Most likely, you've encountered the problem in which the shell runs
-all parts of a pipeline as subshell.
-The inner loop will be executed in a subshell and variable changes
-cannot be propagated if run in a pipeline:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba while read foo; do ...; done
-.Ed
-.Pp
-Note that
-.Ic exit
-in the inner loop will only exit the subshell and not the original shell.
-Likewise, if the code is inside a function,
-.Ic return
-in the inner loop will only exit the subshell and won't terminate the function.
-.Pp
-Use co-processes instead:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba&
-while read \-p foo; do ...; done
-exec 3\*(Gt&p; exec 3\*(Gt&\-
-.Ed
-.Pp
-If
-.Ic read
-is run in a loop such as
-.Ic while read foo; do ...; done
-then leading whitespace will be removed (IFS) and backslashes processed.
-You might want to use
-.Ic while IFS= read \-r foo; do ...; done
-for pristine I/O.
-Similarly, when using the
-.Fl a
-option, use of the
-.Fl r
-option might be prudent
-.Pq Dq Li read \-raN\-1 arr \*(Ltfile ;
-the same applies for NUL-terminated lines:
-.Bd -literal -offset indent
-find . \-type f \-print0 \*(Ba& \e
- while IFS= read \-d \*(aq\*(aq \-pr filename; do
- print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
-done
-.Ed
-.Pp
-.Ss "What differences in function-local scopes are there?"
-.Nm
-has a different scope model from
-.At
-.Nm ksh ,
-which leads to subtle differences in semantics for identical builtins.
-This can cause issues with a
-.Ic nameref
-to suddenly point to a local variable by accident.
-.Pp
-.Tn GNU
-.Nm bash
-allows unsetting local variables; in
-.Nm ,
-doing so in a function allows back access to the global variable
-(actually the one in the next scope up) with the same name.
-The following code, when run before the function definitions, changes
-the behaviour of
-.Ic unset
-to behave like other shells (the alias can be removed after the definitions):
-.Bd -literal -offset indent
-case ${KSH_VERSION:\-} in
-*MIRBSD\ KSH*\*(Ba*LEGACY\ KSH*)
- function unset_compat {
- \e\ebuiltin typeset unset_compat_x
-
- for unset_compat_x in "$@"; do
- eval "\e\e\e\ebuiltin unset $unset_compat_x[*]"
- done
- }
- \e\ebuiltin alias unset=unset_compat
- ;;
-esac
-.Ed
-.Pp
-When a local variable is created (e.g. using
-.Ic local ,
-.Ic typeset ,
-.Ic integer ,
-.Ic \e\ebuiltin typeset )
-it does not, like in other shells, inherit the value from the global
-(next scope up) variable with the same name; it is rather created
-without any value (unset but defined).
-.Ss "I get an error in this regex comparison"
-Use extglobs instead of regexes:
-.Dl "[[ foo =~ (foo\*(Babar).*baz ]] # becomes"
-.Dl "[[ foo = *@(foo\*(Babar)*baz* ]] # instead"
-.Ss "Are there any extensions to avoid?"
-.Tn GNU
-.Nm bash
-supports
-.Dq Li &\*(Gt
-.Pq and Dq Li \*(Ba&
-to redirect both stdout and stderr in one go, but this breaks POSIX
-and Korn Shell syntax; use POSIX redirections instead:
-.Dl "foo \*(Ba& bar \*(Ba& baz &\*(Gtlog # GNU bash"
-.Dl "foo 2\*(Gt&1 \*(Ba bar 2\*(Gt&1 \*(Ba baz \*(Gtlog 2\*(Gt&1 # POSIX"
-.Ss "\*(haL (Ctrl-L) does not clear the screen"
-Use \*(ha[\*(haL (Escape+Ctrl-L) or rebind it:
-.Dl bind \*(aq\*(haL=clear-screen\*(aq
-.Ss "\*(haU (Ctrl-U) clears the entire line"
-If it should only delete the line up to the cursor, use:
-.Dl bind \-m \*(haU=\*(aq\*(ha[0\*(haK\*(aq
-.Ss "Cursor Up behaves differently from zsh"
-Some shells make Cursor Up search in the history only for
-commands starting with what was already entered.
-.Nm
-separates the shortcuts: Cursor Up goes up one command
-and PgUp searches the history as described above.
-.Ss "My question is not answered here!"
-Check
-.Pa http://www.mirbsd.org/mksh\-faq.htm
-which contains a collection of frequently asked questions about
-.Nm
-in general, for packagers, etc. while these above are in user scope.
diff --git a/src/mksh.faq b/src/mksh.faq
new file mode 100644
index 0000000..1fc3b7d
--- /dev/null
+++ b/src/mksh.faq
@@ -0,0 +1,620 @@
+RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.6 2020/04/13 20:46:39 tg Exp $
+ToC: spelling
+Title: How do you spell <tt>mksh</tt>? How do you pronounce it?
+
+<p>This <a href="@@RELPATH@@mksh.htm">shell</a> is spelt either
+ “<tt>mksh</tt>” (with, even at the beginning of a sentence, <a
+ href="https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Capital_letters#Items_that_require_initial_lower_case">an
+ initial lowercase letter</a>; this is important) or “MirBSD Korn Shell”,
+ possibly with “the”.</p>
+<p>I usually pronounce it as “<span xml:lang="de-DE-1901">em-ka-es-ha</span>”,
+ that is, the letters individually in my native German, or say “MirBSD Korn
+ Shell”, although it is manageable, mostly for Slavic speakers, to actually
+ say “mksh” as if it were a word ☺</p>
+<p>Oh… I’ve run into this one, didn’t I? “MirBSD” is pronounced “<span
+ xml:lang="de-DE-1901">Mir-Be-Es-De</span>” germanically, for anglophones
+ “Mir-beas’tie” is fine.</p>
+----
+ToC: sowhatismksh
+Title: I’m a $OS (<i>Android, OS/2, …</i>) user, so what’s mksh?
+
+<p>mksh is a so-called (Unix) “shell” or “command interpreter”, similar to
+ <tt>COMMAND.COM</tt>, <tt>CMD.EXE</tt> or PowerShell on other operating
+ systems you might know. Basically, it runs in a terminal (“console” or
+ “DOS box”) window, taking user input and running that as commands. It’s
+ also used to write so-called (shell) “script”s, short programs made by
+ putting several of those commands into a “batch file”.</p>
+<p>On Android, mksh is used as the system shell — basically, the one
+ running commands at system startup, in the background, and on user
+ behalf (but never of its own). Any privilege pop-ups you might <a
+ href="https://forum.xda-developers.com/showthread.php?t=1963976">be
+ encountering</a> are therefore <a
+ href="https://forum.xda-developers.com/showpost.php?p=33550523&amp;postcount=1553">not
+ caused by mksh</a> but by some other code invoking mksh to do something
+ on its behalf.</p>
+----
+ToC: os2
+Title: I’m an OS/2 user, what else do I need to know?
+
+<p>Unlike the native command prompt, the current working directory is,
+ for security reasons common on Unix systems which the shell is designed
+ for, not in the search path at all; if you really need this, run the
+ command <tt>PATH=.$PATHSEP$PATH</tt> or add that to a suitable
+ initialisation file (<tt>~/.mkshrc</tt>).</p>
+<p>There are two different newline modes for mksh-os2: standard (Unix)
+ mode, in which only LF (0A hex) is supported as line separator, and
+ “textmode”, which also accepts ASCII newlines (CR+LF), like most other
+ tools on OS/2, but creating an incompatibility with standard mksh. If
+ you compiled mksh from source, you will get the standard Unix mode unless
+ <tt>-T</tt> is added during compilation; however, you will most likely
+ have gotten this shell through komh’s port on Hobbes, or from his OS/2
+ Factory on eComStation Korea, which uses “textmode”, though. Most OS/2
+ users will want to use “textmode” unless they need absolute compatibility
+ with Unix mksh and other Unix shells and tools.</p>
+----
+ToC: kornshell
+Title: How does this relate to ksh or the Korn Shell?
+
+<p>The Korn Shell (AT&amp;T ksh) was authored by David Korn; two major
+ flavours exist (ksh88 and ksh93), the latter having been maintained
+ until 2012 (last formal release) and 2014 (last beta snapshot, buggy).
+ A ksh86 did exist.</p>
+<p>There’s now <tt>ksh2020</tt>, a project having restarted development
+ around November 2017 forking the last <tt>ksh93 v-</tt> (beta) snapshot
+ and continuing to develop it, presented at FOSDEM.</p>
+<p>AT&amp;T ksh88 is “the (original) Korn Shell”. Other implementations,
+ of varying quality (MKS Toolkit’s MKS ksh being named as an example of
+ the lower end, MirBSD’s mksh at the upper end). They are all <em>not</em>
+ “Korn Shell” or “ksh”. However, mksh got blessed by David Korn, as long
+ as it cannot be confused with the original Korn Shell.</p>
+<p>The POSIX shell standard, while lacking most Korn Shell features, was
+ largely based on AT&amp;T ksh88, with some from the Bourne shell.</p>
+<p>mksh is the currently active development of what started as the Public
+ Domain Bourne Shell in the mid-1980s with ksh88-compatibl-ish extensions
+ having been added later, making the Public Domain Korn Shell (pdksh),
+ which, while never officially blessed, was the only way for most to get
+ a Korn Shell-like command interpreter for AT&amp;T’s was proprietary,
+ closed-source code for a very long time. pdksh’s development ended in
+ 1999, with some projects like Debian and NetBSD® creating small bug fixes
+ (which often introduced new bugs) as part of maintenance. Around 2003,
+ OpenBSD started cleaning up their shipped version of pdksh, removing old
+ and compatibility code and modernising it. In 2002, development of what
+ is now mksh started as the system shell of MirBSD, which took over almost
+ all of OpenBSD’s cleanup, adding compatibility to other operating systems
+ back on top of it, and after 2004, independent, massive development of
+ bugfixes including a complete reorganisation of the way the parser works,
+ and of new features both independent and compatible with other shells
+ (ksh93, GNU bash, zsh, BSD csh) started and was followed by working with
+ the group behind POSIX to fix issues both in the standard and in mksh.
+ mksh became the system shell in several other operating systems and Linux
+ distributions and Android and thus is likely the Korn shell, if not Unix
+ shell, flavour with the largest user base. It has replaced pdksh in all
+ contemporary systems except QNX, NetBSD® and OpenBSD (who continue to
+ maintain their variant on “low flame”).</p>
+<p>dtksh is the “Desktop Korn Shell”, a build of AT&amp;T ksh93 with some
+ additional built-in utilities for graphics programming (windows, menu
+ bars, dialogue boxes, etc.) utilising Motif bindings.</p>
+<p>MKS ksh is a proprietary reimplemention aiming for, but not quite
+ getting close to, ksh88 compatibility.</p>
+<p>SKsh is an AmigaOS-specific Korn Shell-lookalike by Steve Koren.</p>
+<p>The <a href="@@RELPATH@@ksh-chan.htm">Homepage of the <tt>#ksh</tt>
+ channel on Freenode IRC</a> contains more information about the Korn
+ Shell in general and its flavours.</p>
+----
+ToC: packaging
+Title: How should I package mksh? (common cases)
+
+<p>Export a few environment variables, namely <tt>CC</tt> (the C compiler),
+ <tt>CPPFLAGS</tt> (all C præprocessor definitions), <tt>CFLAGS</tt> (only
+ compiler flags, <em>no</em> <tt>-Dfoo</tt> or anything!), <tt>LDFLAGS</tt>
+ (for anything to pass to the C compiler while linking) and <tt>LIBS</tt>
+ (appended to the linking command line after everything else. You might
+ wish to <tt>export LDSTATIC=-static</tt> for a static build as well.</p>
+<p>When cross-compiling, <tt>CC</tt> is the <em>cross</em> compiler (mksh
+ currently does not require a compiler targetting the build system), but
+ you <em>must</em> also export <tt>TARGET_OS</tt> to whatever system you
+ are compiling for, e.g. “Linux”. For most operating systems, that’s just
+ the uname(1) output. Some very rare systems also need <tt>TARGET_OSREV</tt>;
+ consult the source code of <tt>Build.sh</tt> for details.</p>
+<p>Create two subdirectories, say <tt>build-mksh</tt> and <tt>build-lksh</tt>.
+ In each of them, start a compilation by issuing <tt>sh ../Build.sh -r</tt>
+ followed by running the testsuite<a href="#packaging-fn1">¹</a> via
+ <tt>./test.sh</tt>. For lksh(1) add <tt>-DMKSH_BINSHPOSIX</tt> to
+ <tt>CPPFLAGS</tt> and use <tt>sh ../Build.sh -r -L</tt> to compile.</p>
+<p>See <a href="#testsuite-fails">below</a> if the testsuite fails.</p>
+<p>Install <tt>build-mksh/mksh</tt> as <tt>/bin/mksh</tt> (or similar),
+ <tt>build-lksh/lksh</tt> as <tt>/bin/lksh</tt> with a symlink(7) to it
+ from <tt>/bin/sh</tt> (if desred), and <tt>mksh.1</tt> and <tt>lksh.1</tt>
+ as manpages (mdoc macropackage required). Install <tt>dot.mkshrc</tt>
+ either as <tt>/etc/skel/.mkshrc</tt> (meaning your users will have to
+ manually resynchronise their home directories’ copies after every package
+ upgrade) or as <tt>/etc/mkshrc</tt>, in which case you install a <a
+ href="https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=alioth/mksh.git;a=blob;f=debian/.mkshrc;hb=HEAD">redirection
+ script like Debian’s</a> into <tt>/etc/skel/.mkshrc</tt>. You may need a <a
+ href="@@RELPATH@@TaC-mksh.txt">summary of the licence information</a>.</p>
+<p>At runtime, the presence of <tt>/bin/ed</tt> as default history editor
+ is recommended, as well as a manpage formatter; you can also install
+ preformatted manpages from <tt>build-*ksh/*ksh.cat1</tt> if nroff(1) (or
+ <tt>$NROFF</tt>) is available at build time by removing the <tt>-r</tt>
+ flag from either <tt>Build.sh</tt> invocation.</p>
+<p>Some shell features require the ability to create temporary files and
+ FIFOS (cf. mkfifo(2))/pipes at runtime. Set <tt>TMPDIR</tt> to a suitable
+ location if <tt>/tmp</tt> isn’t it; if this is known ahead of time, you
+ can add <tt>-DMKSH_DEFAULT_TMPDIR=\"/path/to/tmp\"</tt> to CPPFLAGS. We
+ currently are unable to determine one on Android because its bionic libc
+ does not expose any method suitable to do so in the generic case.</p>
+<p id="packaging-fn1">① To run the testsuite, ed(1) must be available as
+ <tt>/bin/ed</tt>, and perl(1) is needed. When cross-compiling, the version
+ of the first <tt>ed</tt> binary on the <tt>PATH</tt> <em>must</em> be the
+ same as that in the target system on which the tests are to be run, in
+ order to be able to detect which flavour of ed to adjust the tests for.
+ Busybox ed is broken beyond repair, and all three ed-related tests will
+ always fail with it.</p>
+----
+ToC: mkshrc
+Title: How does mksh load configuration files?
+
+<p>The shell loads first <tt>/etc/profile</tt> then <tt>~/.profile</tt>
+ if called as login shell or with the <tt>-l</tt> flag, then loads the file
+ <tt>$ENV</tt> points to (defaulting to <tt>~/.mkshrc</tt>) for interactive
+ shells (that includes login shells).</p>
+<p>Distributors should take care to either install the <tt>dot.mkshrc</tt>
+ example provided into <tt>/etc/skel/.mkshrc</tt> (so that it’s available
+ for newly created user accounts) and ensure it can propagate to existing
+ accounts or, if upgrading these is difficult, install the shipped file
+ as, for example, <tt>/etc/mkshrc</tt> and install a skeleton file, such
+ as the one in Debian, that sources the file in <tt>/etc</tt>.</p>
+<p>It’s vital that users can change the configuration, so do not force a
+ root-provided config file onto them; the shipped file, after all, is just
+ an example.</p>
+<p>If you need central user and configuration management and cannot use
+ something that installs skeleton files upon home directory creation
+ (like pam_mkhomedir), you can <tt>export ENV</tt> in <tt>/etc/profile</tt>
+ to a file (say <tt>/etc/shellrc</tt>) that sources the per-shell file.
+ Users can, this way, still override it by setting a different <tt>$ENV</tt>
+ in their <tt>~/.profile</tt>.</p>
+----
+ToC: testsuite-fails
+Title: The testsuite fails!
+
+<p>The mksh testsuite has uncovered numerous bugs in operating systems
+ (kernels, libraries), compilers and toolchains. It is likely that you
+ just ran into one. If you’re using LTO (the <tt>Build.sh</tt> option
+ <tt>-c lto</tt>) try to disable it first — especially GCC is a repeat
+ offender breaking LTO and its antecessor <tt>-fwhole-program --combine</tt>
+ and tends to do wrong code generation quite a bit. Otherwise, try
+ lowering the optimisation levels, bisecting, etc.</p>
+----
+ToC: selinux-androidiocy
+Title: I forbid stat(2) in my SELinux policy, and some things do not work!
+
+Don’t break Unix. Read up on the GIGO principle. Duh.
+----
+ToC: makefile
+Title: Why doesn’t this use a Makefile to build?
+
+<p>Not all supported target operating environments have a make utility
+ available, and shell was required for “mirtoconf” (like autoconf)
+ already anyway, so it was chosen to run the make part as well.</p>
+<p>You can, however, add the <tt>-M</tt> flag to your <tt>Build.sh</tt>
+ invocations to let it produce a <tt>Makefrag.inc</tt> file <em>tailored
+ for this specific build</em> which you can then include in a Makefile,
+ such as with the BSD make(1) “.include” command or <a
+ href="https://www.gnu.org/software/make/manual/make.html#Include">GNU
+ make</a> equivalent. It even contains, for the user to start out with,
+ a commented-out example of how to do that in the most basic manner.</p>
+----
+ToC: oldbsd
+Title: Why do other BSDs and QNX still use pdksh instead of mksh?
+
+<p>Some systems are resistent to change, mostly due to bikeshedding
+ (some people would, for example, rather see all shells banned to
+ ports/pkgsrc®) and hysterial raisins (historical reasons ☻). Most
+ BSDs have mksh packages available, and it works on all of them and
+ QNX just fine.</p>
+<p>In fact, on all of these systems, you can replace their 1999-era
+ <tt>/bin/ksh</tt> (which is a pdksh) with mksh. On at least NetBSD®
+ 1.6 and up (not 1.5) and OpenBSD, even <tt>/bin/sh</tt> is fair game.</p>
+<p>MidnightBSD notably has adopted mksh as system shell (thanks laffer1).</p>
+----
+ToC: openbsd
+Title: Why is there no mksh in OpenBSD’s ports tree?
+
+OpenBSD don’t like people who fork off their project at all; heck,
+they don’t even like the people they themselves forked off (NetBSD®).
+Several people tried over the years to get one committed, but nobody
+dared so as to not lose their commit bit. If you try, succeed, and
+survive Theo, however, kudos to you! See also <a href="#oldbsd">the
+“other BSDs” FAQ entry</a>.
+----
+ToC: book
+Title: I’d like an introduction.
+
+Unfortunately, nobody has written a book about mksh yet, although
+other shells have received (sometimes decent) attention from authors
+and publishers. This FAQ lists a subset of things packagers and
+generic people ask, and the mksh(1) manpage is more of a reference,
+so you are probably best off starting with a shell-agnostic, POSIX
+or ksh88 reference such as the first edition (the second one deals
+with ksh93 which differs far more from mksh than ksh88, as ancient
+as it is, does) of the O’Reilly book (⚠ disclaimer: only an example,
+not a recommendation) and going forward by reading scripts (the
+“shellsnippets” repository referenced in the <tt>#ksh</tt> channel
+homepage (see the top of this document) has many examples) and
+trying to understand them and the mksh specifics from the manpage.
+----
+ToC: ps1conv
+Title: My prompt from &lt;<i>some other shell</i>&gt; does not work!
+
+<a href="#contact">Contact</a> us on the mailing list or on IRC,
+we’ll convert it for you. Also have a look at the PS1 section in
+the mksh(1) manpage (search for “otherwise unused char”, e.g. with
+<tt>/</tt> in less(1), to spot it quickly).
+----
+ToC: ps1weird
+Title: My prompt is weird!
+
+<p>There are several reasons why your <tt>PS1</tt> might be not
+ what you’d expect:</p><ul>
+<li><tt>$PS1</tt> is <tt>export</tt>ed. <strong>Do not export PS1!</strong>
+ (This was agreed upon as suggestion in a discussion between bash, zsh and
+ Korn shell developers.) The feature set of different shells vastly differs
+ and each shell should use its default PS1 or from its startup files.</li>
+<li><tt>$ENV</tt> <a href="#env">is set and/or <tt>export</tt>ed</a>.</li>
+<li>Your prompt is just “<tt># </tt>”: you’re entering a root shell, and
+ <tt>$PS1</tt> does not contain the ‘#’ character, in which case the shell
+ forces this prompt, making extra privileges obvious.</li>
+<li>Your prompt is just “<tt>$ </tt>”: perhaps your system administrator
+ did not install the shipped <tt>dot.mkshrc</tt> file, or you did not copy
+ <tt>/etc/skel/.mkshrc</tt> into your home directory (perhaps it was created
+ before <tt>mksh</tt> was installed?). Without another idea for a fix, get <a
+ href="http://www.mirbsd.org/cvs.cgi/~checkout~/src/bin/mksh/dot.mkshrc?rev=HEAD;content-type=text%2Fplain">this
+ file</a> and store it as <tt>~/.mkshrc</tt> then run <tt>mksh</tt>; this
+ will at the very least install our sample (“user@host:path $ ”) prompt.</li>
+<li>Your prompt contains things like “\u” or “\w”: it is for another shell
+ and <a href="#ps1conv">needs converting</a>.</li>
+<li>Your prompt contains colours, and when the command line is long the
+ cursor position or screen contents, especially using the history, is off:
+ terminal escapes must be escaped from the shell; check the PS1 section in
+ the manpage: search for “otherwise unused char” (see above).</li>
+<li>If the prompt doesn’t leave enough space on the right, the shell inserts
+ a line break after it when rendering.</li>
+</ul>
+----
+ToC: env
+Title: On startup files and <tt>$ENV</tt> across and detecting various shells
+
+Interactive shells look at <tt>~/.mkshrc</tt> (or <tt>/system/etc/mkshrc</tt>
+on Android and <tt>/etc/mkshrc</tt> on FreeWRT and OpenWrt) by default. This
+location can, however, be overridden by setting the <tt>ENV</tt> environment
+variable. (FreeBSD is rumoured to set it in their system profile.) It’s better
+to not set <tt>$ENV</tt> if possible and let every shell user their native
+startup files; otherwise, you must ensure that it runs under all shells. Check
+<tt>$BASH_VERSION</tt> (GNU bash), <tt>$KSH_VERSION</tt> (contains “LEGACY KSH”
+or “MIRBSD KSH” for mksh, “PD KSH” for ancient mirbsdksh/oksh/pdksh, “Version”
+for ksh93); <tt>$NETBSD_SHELL</tt> (NetBSD ash); <tt>POSH_VERSION</tt> (posh, a
+pdksh derivative); <tt>$SH_VERSION</tt> (“PD KSH” as sh), <tt>$YASH_VERSION</tt>
+(yash), <tt>$ZSH_VERSION</tt> (or if <tt>$VERSION</tt> begins with “zsh”); a <a
+href="@@RELPATH@@ksh-chan.htm#which-shell">list of more approaches</a> exists.
+----
+ToC: ctrl-l-cls
+Title: ^L (Ctrl-L) does not clear the screen
+
+Use ^[^L (Escape+Ctrl-L) or rebind it:<br />
+<tt>bind '^L=clear-screen'</tt>
+----
+ToC: ctrl-u-pico
+Title: ^U (Ctrl-U) clears the entire line
+
+If it should only delete the line up to the cursor, use:<br />
+<tt>bind -m ^U='^[0^K'</tt>
+----
+ToC: cur-up-zsh
+Title: Cursor Up behaves differently from zsh
+
+Some shells make Cursor Up search in the history only for commands
+starting with what was already entered. mksh separates the shortcuts:
+Cursor Up goes up one command and PgUp searches the history as described
+above. You can, of course, rebind:<br />
+<tt>bind '^XA=search-history-up'</tt>
+----
+ToC: current
+Title: Can mksh set the title of the window according to the command running?
+
+There’s no such thing as “the command currently running”; consider
+pipelines and delays (<tt>cmd1 | (cmd2; sleep 3; cmd3) | cmd4</tt>).
+There is, however, a way to make the shell display the command <em>line</em>
+during the time it is executed; for testing, you will need to download <a
+href="https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=shellsnippets/shellsnippets.git;a=blob;f=mksh/terminal-title;hb=HEAD">this
+script</a> and <tt>source</tt> it. For merging into your <tt>~/.mkshrc</tt>
+you should first understand how it works: lines 4–18 set a <tt>PS1</tt>
+(prompt) equivalent to lines 84–96 of the stock <tt>dot.mkshrc</tt>, with
+one change: line 15 (<tt>print &gt;/dev/tty …</tt>) is new, inserted just
+before the <tt>return</tt> command of the function substitution in the
+default prompt; this is what you’ll need to merge into your own, custom,
+prompt (if you have one; otherwise pull this adaption to the default
+one). Line 19 is the only other thing in this script rebinding the Ctrl-M
+key (which is normally produced by the Enter/Return key) to code that…
+does <em>something crazy</em>. This trick however <em>does funny things with
+multiline commands</em>, so if you type something out in multiple lines,
+for example <strong>here documents</strong> or <strong>loops</strong> press
+<strong>Ctrl-J instead of Enter/Return</strong> after <em>each</em> line
+including the first (at PS1) and final (at PS2) one.
+----
+ToC: other-tty
+Title: How do I start mksh on a specific terminal?
+
+<p>Normally: <tt>mksh -T<i>/dev/tty2</i></tt></p>
+<p>However, if you want for it to return (e.g. for an embedded system rescue
+ shell), use this on your real console device instead:
+ <tt>mksh -T!<i>/dev/ttyACM0</i></tt></p>
+<p>mksh can also daemonise (send to the background):
+ <tt>mksh -T- -c 'exec cdio lock'</tt></p>
+----
+ToC: completion
+Title: What about programmable tab completion?
+
+The shell itself provides static deterministic tab completion.
+However, you can use hooks like reprogramming the Tab key to a
+command line editor macro, and using the <tt>evaluate-region</tt>
+editor command (modulo a bugfix) together with <tt>quote-region<tt> and shell functions to
+implement a programmable completion engine. Multiple people have
+been considering doing so in our IRC channel; we’ll hyperlink to
+these engines when they are available.
+----
+ToC: posix-mode
+Title: How POSIX compliant is mksh? Also, UTF-8 vs. locales?
+
+<p>You’ll need to use the <tt>lksh</tt> binary, unless your C <tt>long</tt>
+ type is 32 bits wide, for POSIX-compliant arithmetic in the shell. This is
+ because <tt>mksh</tt> provides consistent, wraparound-defined, 32-bit
+ arithmetics on all platforms normally. You’ll also need to enable POSIX mode
+ (<tt>set -o posix</tt>) explicitly, which also disables brace expansion upon
+ being enabled (use <tt>set -o braceexpand</tt> to reenable if needed).</p>
+<p>For the purpose of POSIX, mksh supports only the <tt>C</tt> locale. mksh’s
+ <tt>utf8-mode</tt> (which only supports the BMP (Basic Multilingual Plane) of
+ UCS and maps raw octets into the U+EF80‥U+EFFF wide character range; see
+ <tt>Arithmetic expressions</tt> in mksh(1) for details) <em>must</em> stay
+ disabled in POSIX mode (it is disabled upon enabling POSIX mode in R56+).</p>
+<p class="boxhead">The following POSIX sh-compatible code toggles the
+ <tt>utf8-mode</tt> option dependent on the current POSIX locale, for mksh
+ to allow using the UTF-8 mode, within the constraints outlined above, in
+ code portable across various shell implementations:</p>
+<div class="boxtext">
+ <pre>
+ case ${KSH_VERSION:-} in
+ *MIRBSD KSH*|*LEGACY KSH*)
+ case ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} in
+ *[Uu][Tt][Ff]8*|*[Uu][Tt][Ff]-8*) set -U ;;
+ *) set +U ;;
+ esac ;;
+ esac
+ </pre>
+</div><p class="boxfoot">In near future, (UTF-8) locale tracking will
+ be implemented, though.</p>
+<p>The shell is pretty close to POSIX, when run as <tt>lksh -o posix</tt>
+ under the "C" locale it is intended to match. It does not do everything
+ like other POSIX-compatible or ‑compliant shells, though.</p>
+----
+ToC: function-local-scopes
+Title: What differences in function-local scopes are there?
+
+<p><tt>mksh</tt> has a different scope model from AT&amp;T <tt>ksh</tt>,
+ which leads to subtle differences in semantics for identical builtins.
+ This can cause issues with a <tt>nameref</tt> to suddenly point to a
+ local variable by accident. (Other common shells share mksh’s scoping
+ model.)</p>
+<p class="boxhead">GNU <tt>bash</tt> allows unsetting local variables; in
+ <tt>mksh</tt>, doing so in a function allows back access to the global
+ variable (actually the one in the next scope up) with the same name. The
+ following code, when run before function definitions, changes the behaviour
+ of <tt>unset</tt> to behave like other shells (the alias can be removed
+ after the definitions):</p>
+<div class="boxtext">
+ <pre>
+ case ${KSH_VERSION:-} in
+ *MIRBSD KSH*|*LEGACY KSH*)
+ function unset_compat {
+ \\builtin typeset unset_compat_x
+
+ for unset_compat_x in "$@"; do
+ eval "\\\\builtin unset $unset_compat_x[*]"
+ done
+ }
+ \\builtin alias unset=unset_compat
+ ;;
+ esac
+ </pre>
+</div><p class="boxfoot">When a local variable is created (e.g. using
+ <tt>local</tt>, <tt>typeset</tt>, <tt>integer</tt> or
+ <tt>\\builtin typeset</tt>) it does not, like in other shells, inherit
+ the value from the global (next scope up) variable with the same name;
+ it is rather created without any value (unset but defined).</p>
+----
+ToC: regex-comparison
+Title: I get an error in this regex comparison
+
+<p>Use extglobs instead of regexes:<br />
+ <tt>[[ foo =~ (foo|bar).*baz ]]</tt><br />
+ … becomes…<br />
+ <tt>[[ foo = *@(foo|bar)*baz* ]]</tt></p>
+----
+ToC: extensions-to-avoid
+Title: Are there any extensions to avoid?
+
+<p>GNU <tt>bash</tt> supports “<tt>&amp;&gt;</tt>” (and “|&amp;”) to redirect
+ both stdout and stderr in one go, but this breaks POSIX and Korn Shell syntax;
+ use POSIX redirections instead:</p>
+<table border="1" cellpadding="3">
+ <tr><td>GNU bash</td><td>
+ <tt>foo |&amp; bar |&amp; baz &amp;&gt;log</tt>
+ </td></tr>
+ <tr><td>POSIX</td><td>
+ <tt>foo 2&gt;&amp;1 | bar 2&gt;&amp;1 | baz &gt;log 2&gt;&amp;1</tt>
+ </td></tr>
+</table>
+----
+ToC: while-read-pipe
+Title: Something is going wrong with my while...read loop
+
+<p class="boxhead">Most likely, you’ve encountered the problem in which
+ the shell runs all parts of a pipeline as subshell. The inner loop will
+ be executed in a subshell and variable changes cannot be propagated if
+ run in a pipeline:</p>
+<div class="boxtext">
+ <pre>
+ bar | baz | while read foo; do ...; done
+ </pre>
+</div><p class="boxfoot">Note that <tt>exit</tt> in the inner loop will
+ also only exit the subshell and not the original shell. Likewise, if the
+ code is inside a function, <tt>return</tt> in the inner loop will only
+ exit the subshell and won’t terminate the function.</p>
+<p class="boxhead">Use co-processes instead:</p>
+<div class="boxtext">
+ <pre>
+ bar | baz |&amp;
+ while read -p foo; do ...; done
+ exec 3&gt;&amp;p; exec 3&gt;&amp;-
+ </pre>
+</div><p class="boxfoot">If <tt>read</tt> is run in a way such as
+ <tt>while read foo; do ...; done</tt> then leading whitespace will be
+ removed (IFS) and backslashes processed. You might want to use
+ <tt>while IFS= read -r foo; do ...; done</tt> for pristine I/O.</p>
+<p class="boxhead">Similarly, when using the <tt>-a</tt> option, use of the
+ <tt>-r</tt> option might be prudent (<tt>read -raN-1 arr &lt;file</tt>);
+ the same applies for NUL-terminated lines:</p>
+<div class="boxtext">
+ <pre>
+ find . -type f -print0 |&amp; \
+ while IFS= read -d '' -pr filename; do
+ print -r -- "found &lt;${filename#./}&gt;"
+ done
+ </pre>
+</div>
+----
+ToC: command-alias
+Title: “command” doesn’t expand aliases as in ksh93
+
+This is because AT&amp;T ksh93 ships a predefined alias enabling this:<br />
+<tt>alias command='command '</tt><br />
+put this into your <tt>~/.mkshrc</tt>
+(note the space before the closing single quote)
+----
+ToC: builtin-rename
+Title: “rename” doesn’t work as expected!
+
+<p>There’s a <tt>rename</tt> built-in utility in mksh, which is a very
+ thin wrapper around the rename(2) syscall. It receives two pathnames,
+ source and destination where the first is then atomically renamed to
+ the latter. It does not move, i.e. fails for different filesystems.</p>
+<p>The GNU package <tt>util-linux</tt> has a different <tt>rename</tt>
+ command. If you wish to invoke an external utility (in favour over a
+ builtin), you can use <tt>dot.mkshrc</tt>’s function <tt>enable</tt>
+ or put the following into your <tt>~/.mkshrc</tt>:</p>
+<pre>alias rename="$(whence -p rename)"</pre>
+----
+ToC: builtin-sleep
+Title: “sleep” does not accept ‘m’ for minutes!
+
+<p>mksh contains a <tt>sleep</tt> built-in utility, in order to be
+ able to offer sub-second sleep to shell scripts for most platforms.
+ (It does not exist if the platform lacks select(2) — which should
+ be rare.)</p>
+<p>GNU coreutils contains a sleep implementation accepting suffixed
+ numbers. If you wish to invoke an external utility (in favour over a
+ builtin), you can use <tt>dot.mkshrc</tt>’s function <tt>enable</tt>
+ or put something along the following lines into <tt>~/.mkshrc</tt>:</p>
+<pre>alias sleep="$(whence -p sleep)"</pre>
+<pre>timer() { sleep $(($1*60${2:++$2})); } # timer mins [secs]</pre>
+<pre>timer() {
+ local arg=${1/m/'*60+'}
+ [[ $arg = *+ ]] &amp;&amp; arg+=0
+ sleep $(($arg)
+}</pre>
+----
+ToC: string-concat
+Title: “+=” behaves differently from other shells
+
+<p>In POSIX shell, “=” in code like <tt>var=content</tt> is a string
+ assignment, always. You can use <tt>var=$((content))</tt> for an
+ arithmetic assignment that mostly uses C language rules.</p>
+<p>It stands to consider that the common shell extension “+=” as in
+ <tt>var+=content</tt> would always do string concatenation; it does
+ in mksh, but not in some other shells, in which, when <tt>var</tt> has
+ been declared integer, addition is done instead.</p>
+<p>You can make the code portable by using “((…))” (a.k.a. <tt>let</tt>)
+ instead: <tt>(( var += content ))</tt> does arithmetic addition in
+ all shells involved.</p>
+----
+ToC: set-e
+Title: I use “set -e” and my code unexpectedly errors out
+
+<p>I personally recommend people to not use “<tt>set -e</tt>”, as it
+makes error handling more difficult. However, some insist. There have
+been bugfixes (relative to e.g. oksh/loksh and posh) in this aspect,
+and the user has to make sure <tt>$?</tt> is always 0 ASAP even after
+a command that doesn’t check it.</p>
+<pre>istwo() {
+ for i in "$@"; do
+ test x"$i" = x"2" &amp;&amp; echo two
+ done
+}
+set -e
+istwo 1
+echo END</pre>
+<p>This can be fixed by either adding an explicit “<tt>:</tt>” (or
+“<tt>true</tt>”) after the comparison, or even…</p>
+<pre>test x"$i" = x"2" &amp;&amp; echo two || :</pre>
+<p>… or right after the <tt>done</tt> inside the function, but…</p>
+<pre>test x"$i" != x"2" || echo two</pre>
+<p>… negating the condition and using “<tt>||</tt>” is preferable.</p>
+
+<p>Remember that Korn shell-style functions (with <tt>function</tt>
+ keyword and <strong>without</strong> parenthesēs) in AT&amp;T ksh93
+ and mksh R51 and up have their own shell option scope, but while…</p>
+<pre>function istwo {
+ set +e
+ …
+}</pre>
+<p>… might help in error handling, the return status of a function is
+ still the last errorlevel inside, so an explicit true (“<tt>:</tt>”)
+ or, more explicitly, “<tt>return 0</tt>” at its end is still needed
+ if the <em>caller</em> runs under <tt>set -e</tt>.</p>
+----
+ToC: set-eo-pipefail
+Title: I use “set -eo pipefail” and my code unexpectedly errors out
+
+<p class="boxhead">Related to the above FAQ entry, using
+ <tt>set -o pipefail</tt> makes the following construct error out:</p>
+<div class="boxtext">
+ <pre>
+ set -e
+ for x in 1 2; do
+ false &amp;&amp; echo $x
+ done | cat
+ </pre>
+</div><p class="boxfoot">This is because, while the <tt>&amp;&amp;</tt>
+ ensures that the inner command’s failure is not taken, it sets the entire
+ <tt>for</tt>‥<tt>done</tt> loop’s errorlevel, which is passed on by
+ <tt>-o pipefail</tt>.</p>
+<p>Invert the inner command:<br />
+ <tt>true || echo $x</tt></p>
+----
+ToC: faq
+Title: My question is not answered here!
+
+Do read the mksh(1) manual page. You might also wish to read the <a
+ href="@@RELPATH@@ksh-chan.htm">homepage of the <tt>#ksh</tt> IRC channel
+on Freenode</a> which lists several resources for Korn or POSIX-compatible
+shells in general. Or, <a href="#contact">contact</a> us (developer and
+users), for example via IRC.
+----
+ToC: contact
+Title: How do I contact you (to say thanks)?
+
+You can say hi in the <tt>#!/bin/mksh</tt> channel on Freenode <a
+ href="@@RELPATH@@irc.htm">IRC</a>, although a <a
+ href="@@RELPATH@@danke.htm">donation</a> wouldn’t be amiss ☺ The <a
+ href="http://www.mail-archive.com/miros-mksh@mirbsd.org/">mailing
+list</a> can also be used.
+----
diff --git a/src/os2.c b/src/os2.c
index 2bc63ed..8a80782 100644
--- a/src/os2.c
+++ b/src/os2.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2015, 2017
+ * Copyright (c) 2015, 2017, 2020
* KO Myung-Hun <komh@chollian.net>
* Copyright (c) 2017
* mirabilos <m@mirbsd.org>
@@ -20,6 +20,7 @@
* of said person's immediate fault when using the work as intended.
*/
+#define INCL_KBD
#define INCL_DOS
#include <os2.h>
@@ -31,7 +32,7 @@
#include <unistd.h>
#include <process.h>
-__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.8 2017/12/22 16:41:42 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.10 2020/04/07 11:13:45 tg Exp $");
static char *remove_trailing_dots(char *);
static int access_stat_ex(int (*)(), const char *, void *);
@@ -172,6 +173,8 @@ init_extlibpath(void)
void
os2_init(int *argcp, const char ***argvp)
{
+ KBDINFO ki;
+
response(argcp, argvp);
init_extlibpath();
@@ -183,6 +186,12 @@ os2_init(int *argcp, const char ***argvp)
if (!isatty(STDERR_FILENO))
setmode(STDERR_FILENO, O_BINARY);
+ /* ensure ECHO mode is ON so that read command echoes. */
+ memset(&ki, 0, sizeof(ki));
+ ki.cb = sizeof(ki);
+ ki.fsMask |= KEYBOARD_ECHO_ON;
+ KbdSetStatus(&ki, 0);
+
atexit(cleanup);
}
@@ -295,11 +304,12 @@ access_ex(int (*fn)(const char *, int), const char *name, int mode)
return (access_stat_ex(fn, name, (void *)mode));
}
-/* stat() version */
+/* stat()/lstat() version */
int
-stat_ex(const char *name, struct stat *buffer)
+stat_ex(int (*fn)(const char *, struct stat *),
+ const char *name, struct stat *buffer)
{
- return (access_stat_ex(stat, name, buffer));
+ return (access_stat_ex(fn, name, buffer));
}
static int
diff --git a/src/rlimits.gen b/src/rlimits.gen
index 96f5eff..5ccd007 100644
--- a/src/rlimits.gen
+++ b/src/rlimits.gen
@@ -21,7 +21,7 @@
#ifndef RLIMITS_OPTCS
#if defined(RLIMITS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.4 2019/04/24 20:56:31 tg Exp $");
#elif defined(RLIMITS_ITEMS)
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
#endif
@@ -82,6 +82,9 @@ FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024, 'B')
#ifdef RLIMIT_PTHREAD
FN("threadsperprocess", RLIMIT_PTHREAD, 1, 'P')
#endif
+#ifdef RLIMIT_THREADS
+FN("threadsperprocess", RLIMIT_THREADS, 1, 'r')
+#endif
#ifdef RLIMIT_NICE
FN("maxnice", RLIMIT_NICE, 1, 'e')
#endif
@@ -100,6 +103,9 @@ FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024, 'v')
#ifdef ULIMIT_V_IS_AS
FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
#endif
+#ifdef RLIMIT_LOCKS
+FN("filelocks", RLIMIT_LOCKS, 1, 'x')
+#endif
#undef F0
#undef FN
#undef RLIMITS_DEFNS
@@ -158,6 +164,9 @@ FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
#ifdef RLIMIT_RTPRIO
"r"
#endif
+#ifdef RLIMIT_THREADS
+"r"
+#endif
"S"
#ifdef RLIMIT_STACK
"s"
@@ -180,5 +189,8 @@ FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
#ifdef RLIMIT_SWAP
"w"
#endif
+#ifdef RLIMIT_LOCKS
+"x"
+#endif
#undef RLIMITS_OPTCS
#endif
diff --git a/src/rlimits.opt b/src/rlimits.opt
index 4f933ab..db2a531 100644
--- a/src/rlimits.opt
+++ b/src/rlimits.opt
@@ -19,7 +19,7 @@
*/
@RLIMITS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.4 2019/04/24 20:56:31 tg Exp $");
@RLIMITS_ITEMS
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
@@
@@ -86,6 +86,9 @@ FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024
>P|RLIMIT_PTHREAD
FN("threadsperprocess", RLIMIT_PTHREAD, 1
+>r|RLIMIT_THREADS
+FN("threadsperprocess", RLIMIT_THREADS, 1
+
>e|RLIMIT_NICE
FN("maxnice", RLIMIT_NICE, 1
@@ -102,4 +105,7 @@ FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024
>v|ULIMIT_V_IS_AS
FN("address-space(KiB)", RLIMIT_AS, 1024
+>x|RLIMIT_LOCKS
+FN("filelocks", RLIMIT_LOCKS, 1
+
|RLIMITS_OPTCS
diff --git a/src/sh.h b/src/sh.h
index a974bbd..c5e4dc5 100644
--- a/src/sh.h
+++ b/src/sh.h
@@ -10,7 +10,8 @@
/*-
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+ * 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -116,7 +117,7 @@
#if (defined(__KLIBC__) || defined(__dietlibc__)) && \
((defined(__GNUC__) && (__GNUC__ > 3)) || defined(__NWCC__))
#undef offsetof
-#define offsetof(s, e) __builtin_offsetof(s, e)
+#define offsetof(s,e) __builtin_offsetof(s, e)
#endif
#undef __attribute__
@@ -170,9 +171,17 @@
#define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p
#define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p)
#ifdef MKSH_DONT_EMIT_IDSTRING
-#define __IDSTRING(prefix, string) /* nothing */
+#define __IDSTRING(prefix,string) /* nothing */
+#elif defined(__ELF__) && defined(__GNUC__) && \
+ !(defined(__GNUC__) && defined(__mips16) && (__GNUC__ >= 8)) && \
+ !defined(__llvm__) && !defined(__NWCC__) && !defined(NO_ASM)
+#define __IDSTRING(prefix,string) \
+ __asm__(".section .comment" \
+ "\n .ascii \"@(\"\"#)" #prefix ": \"" \
+ "\n .asciz \"" string "\"" \
+ "\n .previous")
#else
-#define __IDSTRING(prefix, string) \
+#define __IDSTRING(prefix,string) \
static const char __IDSTRING_EXPAND(__LINE__,prefix) [] \
MKSH_A_USED = "@(""#)" #prefix ": " string
#endif
@@ -182,9 +191,9 @@
#endif
#ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.870 2019/03/01 16:18:14 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.892 2020/04/14 22:45:22 tg Exp $");
#endif
-#define MKSH_VERSION "R57 2019/03/01"
+#define MKSH_VERSION "R59 2020/04/14"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@@ -255,6 +264,16 @@ typedef MKSH_TYPEDEF_SIG_ATOMIC_T sig_atomic_t;
typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
#endif
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#define MKSH_SHF_NO_INLINE
+#endif
+
+/* do not merge these conditionals as neatcc’s preprocessor is simple */
+#ifdef __neatcc__
+/* parsing of comma operator <,> in expressions broken */
+#define MKSH_SHF_NO_INLINE
+#endif
+
/* un-do vendor damage */
#undef BAD /* AIX defines that somewhere */
@@ -264,6 +283,9 @@ typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
#ifndef MKSH_INCLUDES_ONLY
+/* compile-time assertions */
+#define cta(name,expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
+
/* EBCDIC fun */
/* see the large comment in shf.c for an EBCDIC primer */
@@ -307,7 +329,7 @@ struct rusage {
} while (/* CONSTCOND */ 0)
#endif
#ifndef timeradd
-#define timeradd(tvp, uvp, vvp) \
+#define timeradd(tvp,uvp,vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
@@ -318,7 +340,7 @@ struct rusage {
} while (/* CONSTCOND */ 0)
#endif
#ifndef timersub
-#define timersub(tvp, uvp, vvp) \
+#define timersub(tvp,uvp,vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
@@ -453,7 +475,7 @@ extern int getrusage(int, struct rusage *);
#if !HAVE_MEMMOVE
/* we assume either memmove or bcopy exist, at the moment */
-#define memmove(dst, src, len) bcopy((src), (dst), (len))
+#define memmove(dst,src,len) bcopy((src), (dst), (len))
#endif
#if !HAVE_REVOKE_DECL
@@ -647,7 +669,7 @@ char *ucstrstr(char *, const char *);
#endif
#endif
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 571)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 591)
#error Must run Build.sh to compile this.
extern void thiswillneverbedefinedIhope(void);
int
@@ -661,20 +683,20 @@ im_sorry_dave(void)
/* use this ipv strchr(s, 0) but no side effects in s! */
#define strnul(s) ((s) + strlen((const void *)s))
-#define utf_ptradjx(src, dst) do { \
+#define utf_ptradjx(src,dst) do { \
(dst) = (src) + utf_ptradj(src); \
} while (/* CONSTCOND */ 0)
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-#define strdupx(d, s, ap) do { \
+#define strdupx(d,s,ap) do { \
(d) = strdup_i((s), (ap)); \
} while (/* CONSTCOND */ 0)
-#define strndupx(d, s, n, ap) do { \
+#define strndupx(d,s,n,ap) do { \
(d) = strndup_i((s), (n), (ap)); \
} while (/* CONSTCOND */ 0)
#else
/* be careful to evaluate arguments only once! */
-#define strdupx(d, s, ap) do { \
+#define strdupx(d,s,ap) do { \
const char *strdup_src = (const void *)(s); \
char *strdup_dst = NULL; \
\
@@ -685,7 +707,7 @@ im_sorry_dave(void)
} \
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
-#define strndupx(d, s, n, ap) do { \
+#define strndupx(d,s,n,ap) do { \
const char *strdup_src = (const void *)(s); \
char *strdup_dst = NULL; \
\
@@ -698,6 +720,33 @@ im_sorry_dave(void)
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
#endif
+#define strdup2x(d,s1,s2) do { \
+ const char *strdup_src = (const void *)(s1); \
+ const char *strdup_app = (const void *)(s2); \
+ size_t strndup_len = strlen(strdup_src); \
+ size_t strndup_ln2 = strlen(strdup_app) + 1; \
+ char *strdup_dst = alloc(strndup_len + strndup_ln2, ATEMP); \
+ \
+ memcpy(strdup_dst, strdup_src, strndup_len); \
+ memcpy(strdup_dst + strndup_len, strdup_app, strndup_ln2); \
+ (d) = strdup_dst; \
+} while (/* CONSTCOND */ 0)
+#define strpathx(d,s1,s2,cond) do { \
+ const char *strdup_src = (const void *)(s1); \
+ const char *strdup_app = (const void *)(s2); \
+ size_t strndup_len = strlen(strdup_src) + 1; \
+ size_t strndup_ln2 = ((cond) || *strdup_app) ? \
+ strlen(strdup_app) + 1 : 0; \
+ char *strdup_dst = alloc(strndup_len + strndup_ln2, ATEMP); \
+ \
+ memcpy(strdup_dst, strdup_src, strndup_len); \
+ if (strndup_ln2) { \
+ strdup_dst[strndup_len - 1] = '/'; \
+ memcpy(strdup_dst + strndup_len, strdup_app, \
+ strndup_ln2); \
+ } \
+ (d) = strdup_dst; \
+} while (/* CONSTCOND */ 0)
#ifdef MKSH_SMALL
#ifndef MKSH_NOPWNAM
@@ -839,6 +888,7 @@ extern struct env {
/* struct env.flag values */
#define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */
#define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */
+#define EF_IN_EVAL BIT(3) /* inside an eval */
/* Do breaks/continues stop at env type e? */
#define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE || \
@@ -851,12 +901,13 @@ extern struct env {
#define LRETURN 1 /* return statement */
#define LEXIT 2 /* exit statement */
#define LERROR 3 /* errorf() called */
-#define LLEAVE 4 /* untrappable exit/error */
+#define LERREXT 4 /* set -e caused */
#define LINTR 5 /* ^C noticed */
#define LBREAK 6 /* break statement */
#define LCONTIN 7 /* continue statement */
#define LSHELL 8 /* return to interactive shell() */
#define LAEXPR 9 /* error in arithmetic expression */
+#define LLEAVE 10 /* untrappable exit/error */
/* sort of shell global state */
EXTERN pid_t procpid; /* PID of executing process */
@@ -868,9 +919,9 @@ EXTERN uint8_t trap_nested; /* running nested traps */
EXTERN uint8_t shell_flags[FNFLAGS];
EXTERN const char *kshname; /* $0 */
EXTERN struct {
- uid_t kshuid_v; /* real UID of shell */
+ uid_t kshuid_v; /* real UID of shell at startup */
uid_t ksheuid_v; /* effective UID of shell */
- gid_t kshgid_v; /* real GID of shell */
+ gid_t kshgid_v; /* real GID of shell at startup */
gid_t kshegid_v; /* effective GID of shell */
pid_t kshpgrp_v; /* process group of shell */
pid_t kshppid_v; /* PID of parent of shell */
@@ -955,6 +1006,7 @@ EXTERN const char Tsgcontinue[] E_INIT("*=continue");
EXTERN const char Tcreate[] E_INIT("create");
EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected");
EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL");
+EXTERN const char TENV[] E_INIT("ENV");
EXTERN const char Tdsgexport[] E_INIT("^*=export");
#define Texport (Tdsgexport + 3)
#ifdef __OS2__
@@ -997,10 +1049,11 @@ EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection");
#define Tredirection (Tredirection_dup + 19)
#define Treal_sp1 (Treal_sp2 + 1)
EXTERN const char Treal_sp2[] E_INIT(" real ");
+EXTERN const char TREPLY[] E_INIT("REPLY");
EXTERN const char Treq_arg[] E_INIT("requires an argument");
EXTERN const char Tselect[] E_INIT("select");
-EXTERN const char Tsgset[] E_INIT("*=set");
#define Tset (Tf_parm + 18)
+EXTERN const char Tsghset[] E_INIT("*=#set");
#define Tsh (Tmksh + 2)
#define TSHELL (TEXECSHELL + 4)
#define Tshell (Ttoo_many_files + 23)
@@ -1032,7 +1085,6 @@ EXTERN const char Tf__S[] E_INIT(" %S");
#define Tf__d (Tunexpected_type + 22)
EXTERN const char Tf__ss[] E_INIT(" %s%s");
#define Tf__sN (Tf_s_s_sN + 5)
-EXTERN const char Tf_sSs[] E_INIT("%s/%s");
#define Tf_T (Tf_s_T + 3)
EXTERN const char Tf_dN[] E_INIT("%d\n");
EXTERN const char Tf_s_[] E_INIT("%s ");
@@ -1056,8 +1108,6 @@ EXTERN const char Tf_S_[] E_INIT("%S ");
#define Tf_lu (Tf_toolarge + 17)
EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu");
EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s");
-#define Tf_ss (Tf_sss + 2)
-EXTERN const char Tf_sss[] E_INIT("%s%s%s");
EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s");
EXTERN const char Tf_toomany[] E_INIT("too many %ss");
EXTERN const char Tf_sd[] E_INIT("%s %d");
@@ -1116,6 +1166,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tcreate "create"
#define TELIF_unexpected "TELIF unexpected"
#define TEXECSHELL "EXECSHELL"
+#define TENV "ENV"
#define Tdsgexport "^*=export"
#define Texport "export"
#ifdef __OS2__
@@ -1158,10 +1209,11 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tredirection "redirection"
#define Treal_sp1 "real "
#define Treal_sp2 " real "
+#define TREPLY "REPLY"
#define Treq_arg "requires an argument"
#define Tselect "select"
-#define Tsgset "*=set"
#define Tset "set"
+#define Tsghset "*=#set"
#define Tsh "sh"
#define TSHELL "SHELL"
#define Tshell "shell"
@@ -1193,7 +1245,6 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tf__d " %d"
#define Tf__ss " %s%s"
#define Tf__sN " %s\n"
-#define Tf_sSs "%s/%s"
#define Tf_T "%T"
#define Tf_dN "%d\n"
#define Tf_s_ "%s "
@@ -1217,8 +1268,6 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tf_lu "%lu"
#define Tf_toolarge "%s %s too large: %lu"
#define Tf_ldfailed "%s %s(%d, %ld) failed: %s"
-#define Tf_ss "%s%s"
-#define Tf_sss "%s%s%s"
#define Tf_sD_s_sD_s "%s: %s %s: %s"
#define Tf_toomany "too many %ss"
#define Tf_sd "%s %d"
@@ -1372,7 +1421,7 @@ EXTERN bool really_exit;
#define CiCOLON BIT(26) /* : */
#define CiEQUAL BIT(27) /* = */
#define CiQUEST BIT(28) /* ? */
-#define CiBRACK BIT(29) /* ] */
+#define CiBRACK BIT(29) /* [] */
#define CiUNDER BIT(30) /* _ */
#define CiGRAVE BIT(31) /* ` */
/* out of space, but one for *@ would make sense, possibly others */
@@ -1386,8 +1435,8 @@ EXTERN char ifs0;
/* external types */
-/* !%,-.0‥9:@A‥Z[]_a‥z valid characters in alias names */
-#define C_ALIAS (CiALIAS | CiBRACK | CiCOLON | CiDIGIT | CiLOWER | CiMINUS | CiOCTAL | CiPERCT | CiUNDER | CiUPPER)
+/* !%+,-.0‥9:@A‥Z[]_a‥z valid characters in alias names */
+#define C_ALIAS (CiALIAS | CiBRACK | CiCOLON | CiDIGIT | CiLOWER | CiMINUS | CiOCTAL | CiPERCT | CiPLUS | CiUNDER | CiUPPER)
/* 0‥9A‥Za‥z alphanumerical */
#define C_ALNUM (CiDIGIT | CiLOWER | CiOCTAL | CiUPPER)
/* 0‥9A‥Z_a‥z alphanumerical plus underscore (“word character”) */
@@ -1533,6 +1582,12 @@ extern void ebcdic_init(void);
#define ksh_toctrl(c) asc2rtt(ord(c) == ORD('?') ? 0x7F : rtt2asc(c) & 0x9F)
#define ksh_unctrl(c) asc2rtt(rtt2asc(c) ^ 0x40U)
+#ifdef MKSH_SMALL
+#define SMALLP(x) /* nothing */
+#else
+#define SMALLP(x) , x
+#endif
+
/* Argument parsing for built-in commands and getopts command */
/* Values for Getopt.flags */
@@ -1626,7 +1681,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
#define shf_getc_i(shf) ((shf)->rnleft > 0 ? \
(shf)->rnleft--, (int)ord(*(shf)->rp++) : \
shf_getchar(shf))
-#define shf_putc_i(c, shf) ((shf)->wnleft == 0 ? \
+#define shf_putc_i(c,shf) ((shf)->wnleft == 0 ? \
shf_putchar((uint8_t)(c), (shf)) : \
((shf)->wnleft--, *(shf)->wp++ = (c)))
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
@@ -1637,7 +1692,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
/* Flags passed to shf_*open() */
#define SHF_RD 0x0001
#define SHF_WR 0x0002
-#define SHF_RDWR (SHF_RD|SHF_WR)
+#define SHF_RDWR (SHF_RD | SHF_WR)
#define SHF_ACCMODE 0x0003 /* mask */
#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
#define SHF_UNBUF 0x0008 /* unbuffered I/O */
@@ -1756,14 +1811,15 @@ EXTERN bool last_lookup_was_array;
#define LOW_BI BIT(14) /* external utility overrides built-in one */
#define DECL_UTIL BIT(15) /* is declaration utility */
#define DECL_FWDR BIT(16) /* is declaration utility forwarder */
+#define NEXTLOC_BI BIT(17) /* needs BF_RESETSPEC on e->loc */
/*
* Attributes that can be set by the user (used to decide if an unset
* param should be repoted by set/typeset). Does not include ARRAY or
* LOCAL.
*/
-#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
- LCASEV|UCASEV_AL|INT_U|INT_L)
+#define USERATTRIB (EXPORT | INTEGER | RDONLY | LJUST | RJUST | ZEROFIL | \
+ LCASEV | UCASEV_AL | INT_U | INT_L)
#define arrayindex(vp) ((unsigned long)((vp)->flag & AINDEX ? \
(vp)->ua.index : 0))
@@ -1794,7 +1850,7 @@ enum namerefflag {
#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
-#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
+#define AI_ARGV(a,i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
#define AI_ARGC(a) ((a).ai_argc - (a).skip)
/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
@@ -1824,6 +1880,8 @@ struct block {
/* Values for struct block.flags */
#define BF_DOGETOPTS BIT(0) /* save/restore getopts state */
#define BF_STOPENV BIT(1) /* do not export further */
+/* BF_RESETSPEC and NEXTLOC_BI must be numerically identical! */
+#define BF_RESETSPEC BIT(17) /* use ->next for set and shift */
/*
* Used by ktwalk() and ktnext() routines.
@@ -1993,8 +2051,14 @@ struct ioword {
#define DOSCALAR BIT(12) /* change field handling to non-list context */
#define DOHEREDOC BIT(13) /* change scalar handling to heredoc body */
#define DOHERESTR BIT(14) /* append a newline char */
+#define DODBMAGIC BIT(15) /* add magic to expansions for [[ x = $y ]] */
#define X_EXTRA 20 /* this many extra bytes in X string */
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#define X_WASTE 15 /* allowed extra bytes to avoid shrinking, */
+#else
+#define X_WASTE 255 /* … must be 2ⁿ-1 */
+#endif
typedef struct XString {
/* beginning of string */
@@ -2008,44 +2072,44 @@ typedef struct XString {
} XString;
/* initialise expandable string */
-#define XinitN(xs, length, area) do { \
+#define XinitN(xs,length,area) do { \
(xs).len = (length); \
(xs).areap = (area); \
(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \
(xs).end = (xs).beg + (xs).len; \
} while (/* CONSTCOND */ 0)
-#define Xinit(xs, xp, length, area) do { \
+#define Xinit(xs,xp,length,area) do { \
XinitN((xs), (length), (area)); \
(xp) = (xs).beg; \
} while (/* CONSTCOND */ 0)
/* stuff char into string */
-#define Xput(xs, xp, c) (*xp++ = (c))
+#define Xput(xs,xp,c) (*xp++ = (c))
/* check if there are at least n bytes left */
-#define XcheckN(xs, xp, n) do { \
+#define XcheckN(xs,xp,n) do { \
ssize_t more = ((xp) + (n)) - (xs).end; \
if (more > 0) \
(xp) = Xcheck_grow(&(xs), (xp), (size_t)more); \
} while (/* CONSTCOND */ 0)
/* check for overflow, expand string */
-#define Xcheck(xs, xp) XcheckN((xs), (xp), 1)
+#define Xcheck(xs,xp) XcheckN((xs), (xp), 1)
/* free string */
-#define Xfree(xs, xp) afree((xs).beg, (xs).areap)
+#define Xfree(xs,xp) afree((xs).beg, (xs).areap)
/* close, return string */
-#define Xclose(xs, xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
+#define Xclose(xs,xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
/* beginning of string */
-#define Xstring(xs, xp) ((xs).beg)
+#define Xstring(xs,xp) ((xs).beg)
-#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
-#define Xlength(xs, xp) ((xp) - (xs).beg)
-#define Xsize(xs, xp) ((xs).end - (xs).beg)
-#define Xsavepos(xs, xp) ((xp) - (xs).beg)
-#define Xrestpos(xs, xp, n) ((xs).beg + (n))
+#define Xnleft(xs,xp) ((xs).end - (xp)) /* may be less than 0 */
+#define Xlength(xs,xp) ((xp) - (xs).beg)
+#define Xsize(xs,xp) ((xs).end - (xs).beg)
+#define Xsavepos(xs,xp) ((xp) - (xs).beg)
+#define Xrestpos(xs,xp,n) ((xs).beg + (n))
char *Xcheck_grow(XString *, const char *, size_t);
@@ -2062,13 +2126,13 @@ typedef struct {
size_t siz;
} XPtrV;
-#define XPinit(x, n) do { \
+#define XPinit(x,n) do { \
(x).siz = (n); \
(x).len = 0; \
(x).beg = alloc2((x).siz, sizeof(void *), ATEMP); \
} while (/* CONSTCOND */ 0) \
-#define XPput(x, p) do { \
+#define XPput(x,p) do { \
if ((x).len == (x).siz) { \
(x).beg = aresize2((x).beg, (x).siz, \
2 * sizeof(void *), ATEMP); \
@@ -2296,12 +2360,12 @@ EXTERN mksh_ari_t histsize; /* history size */
/* user and system time of last j_waitjed job */
EXTERN struct timeval j_usrtime, j_systime;
-#define notok2mul(max, val, c) (((val) != 0) && ((c) != 0) && \
+#define notok2mul(max,val,c) (((val) != 0) && ((c) != 0) && \
(((max) / (c)) < (val)))
-#define notok2add(max, val, c) ((val) > ((max) - (c)))
-#define notoktomul(val, cnst) notok2mul(SIZE_MAX, (val), (cnst))
-#define notoktoadd(val, cnst) notok2add(SIZE_MAX, (val), (cnst))
-#define checkoktoadd(val, cnst) do { \
+#define notok2add(max,val,c) ((val) > ((max) - (c)))
+#define notoktomul(val,cnst) notok2mul(SIZE_MAX, (val), (cnst))
+#define notoktoadd(val,cnst) notok2add(SIZE_MAX, (val), (cnst))
+#define checkoktoadd(val,cnst) do { \
if (notoktoadd((val), (cnst))) \
internal_errorf(Tintovfl, (size_t)(val), \
'+', (size_t)(cnst)); \
@@ -2312,18 +2376,20 @@ EXTERN struct timeval j_usrtime, j_systime;
void ainit(Area *);
void afreeall(Area *);
/* these cannot fail and can take NULL (not for ap) */
-#define alloc(n, ap) aresize(NULL, (n), (ap))
-#define alloc2(m, n, ap) aresize2(NULL, (m), (n), (ap))
+#define alloc(n,ap) aresize(NULL, (n), (ap))
+#define alloc2(m,n,ap) aresize2(NULL, (m), (n), (ap))
void *aresize(void *, size_t, Area *);
void *aresize2(void *, size_t, size_t, Area *);
void afree(void *, Area *); /* can take NULL */
+#define aresizeif(z,p,n,ap) (((p) == NULL) || ((z) < (n)) || \
+ (((z) & ~X_WASTE) > ((n) & ~X_WASTE)) ? \
+ aresize((p), (n), (ap)) : (p))
/* edit.c */
#ifndef MKSH_NO_CMDLINE_EDITING
-#ifndef MKSH_SMALL
-int x_bind(const char *, const char *, bool, bool);
-#else
-int x_bind(const char *, const char *, bool);
-#endif
+int x_bind(const char * SMALLP(bool));
+int x_bind_check(void);
+int x_bind_list(void);
+int x_bind_showall(void);
void x_init(void);
#ifdef DEBUG_LEAKS
void x_done(void);
@@ -2511,6 +2577,8 @@ void warningf(bool, const char *, ...)
MKSH_A_FORMAT(__printf__, 2, 3);
void bi_errorf(const char *, ...)
MKSH_A_FORMAT(__printf__, 1, 2);
+void maybe_errorf(int *, int, const char *, ...)
+ MKSH_A_FORMAT(__printf__, 3, 4);
#define errorfz() errorf(NULL)
#define errorfxz(rc) errorfx((rc), NULL)
#define bi_errorfz() bi_errorf(NULL)
@@ -2543,7 +2611,7 @@ struct temp *maketemp(Area *, Temp_type, struct temp **);
void ktinit(Area *, struct table *, uint8_t);
struct tbl *ktscan(struct table *, const char *, uint32_t, struct tbl ***);
/* table, name (key) to search for, hash(n) */
-#define ktsearch(tp, s, h) ktscan((tp), (s), (h), NULL)
+#define ktsearch(tp,s,h) ktscan((tp), (s), (h), NULL)
struct tbl *ktenter(struct table *, const char *, uint32_t);
#define ktdelete(p) do { p->flag = 0; } while (/* CONSTCOND */ 0)
void ktwalk(struct tstate *, struct table *);
@@ -2591,7 +2659,7 @@ int unbksl(bool, int (*)(void), void (*)(int));
void os2_init(int *, const char ***);
void setextlibpath(const char *, const char *);
int access_ex(int (*)(const char *, int), const char *, int);
-int stat_ex(const char *, struct stat *);
+int stat_ex(int (*)(const char *, struct stat *), const char *, struct stat *);
const char *real_exec_name(const char *);
#endif
/* shf.c */
@@ -2607,7 +2675,7 @@ ssize_t shf_read(char *, ssize_t, struct shf *);
char *shf_getse(char *, ssize_t, struct shf *);
int shf_getchar(struct shf *s);
int shf_ungetc(int, struct shf *);
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#ifdef MKSH_SHF_NO_INLINE
int shf_getc(struct shf *);
int shf_putc(int, struct shf *);
#else
@@ -2712,6 +2780,24 @@ enum Test_meta {
};
typedef enum Test_meta Test_meta;
+struct t_op {
+ const char op_text[4];
+ Test_op op_num;
+};
+
+/* for string reuse */
+extern const struct t_op u_ops[];
+extern const struct t_op b_ops[];
+/* ensure order with funcs.c */
+#define Tda (u_ops[0].op_text)
+#define Tdn (u_ops[12].op_text)
+#define Tdo (u_ops[14].op_text)
+#define Tdr (u_ops[16].op_text)
+#define Tdu (u_ops[20].op_text) /* "-u" */
+#define Tdx (u_ops[23].op_text)
+
+#define Tu (Tdu + 1) /* "u" */
+
#define TEF_ERROR BIT(0) /* set if we've hit an error */
#define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */
diff --git a/src/sh_flags.gen b/src/sh_flags.gen
index af44328..a0a0f6a 100644
--- a/src/sh_flags.gen
+++ b/src/sh_flags.gen
@@ -21,7 +21,7 @@
#ifndef SHFLAGS_OPTCS
#if defined(SHFLAGS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.8 2019/12/30 03:58:58 tg Exp $");
#elif defined(SHFLAGS_ENUMS)
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
diff --git a/src/sh_flags.opt b/src/sh_flags.opt
index c2f554e..d81d9d9 100644
--- a/src/sh_flags.opt
+++ b/src/sh_flags.opt
@@ -19,7 +19,7 @@
*/
@SHFLAGS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.8 2019/12/30 03:58:58 tg Exp $");
@SHFLAGS_ENUMS
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
@@ -184,7 +184,7 @@ FN("xtrace", FXTRACE, OF_ANY
FN("", FCOMMAND, OF_CMDLINE
/*
- * anonymous flags: used internally by shell only (not visible to user
+ * anonymous flags: used internally by shell only (not visible to user)
*/
/* ./. direct builtin call (divined from argv[0] multi-call binary) */
diff --git a/src/shf.c b/src/shf.c
index a37f4da..8e95fc6 100644
--- a/src/shf.c
+++ b/src/shf.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- * 2012, 2013, 2015, 2016, 2017, 2018
+ * 2012, 2013, 2015, 2016, 2017, 2018, 2019
* mirabilos <m@mirbsd.org>
* Copyright (c) 2015
* Daniel Richard G. <skunk@iSKUNK.ORG>
@@ -27,7 +27,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.98 2018/08/10 02:53:39 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.101 2019/12/11 17:56:58 tg Exp $");
/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
@@ -523,7 +523,8 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
buf += ncopy;
bsize -= ncopy;
#ifdef MKSH_WITH_TEXTMODE
- if (end && buf > orig_buf + 1 && buf[-2] == '\r') {
+ if (buf > orig_buf + 1 && ord(buf[-2]) == ORD('\r') &&
+ ord(buf[-1]) == ORD('\n')) {
buf--;
bsize++;
buf[-1] = '\n';
@@ -531,9 +532,9 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
#endif
} while (!end && bsize);
#ifdef MKSH_WITH_TEXTMODE
- if (!bsize && buf[-1] == '\r') {
+ if (!bsize && ord(buf[-1]) == ORD('\r')) {
int c = shf_getc(shf);
- if (c == '\n')
+ if (ord(c) == ORD('\n'))
buf[-1] = '\n';
else if (c != -1)
shf_ungetc(c, shf);
@@ -1073,7 +1074,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
return (shf_error(shf) ? -1 : nwritten);
}
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#ifdef MKSH_SHF_NO_INLINE
int
shf_getc(struct shf *shf)
{
diff --git a/src/syn.c b/src/syn.c
index e4c38e3..3387cf5 100644
--- a/src/syn.c
+++ b/src/syn.c
@@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017,
- * 2018
+ * 2018, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -24,7 +24,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.127 2018/01/14 00:22:30 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.128 2020/03/31 00:30:05 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@@ -271,6 +271,7 @@ get_command(int cf, int sALIAS)
struct ioword *iop, **iops;
XPtrV args, vars;
struct nesting_state old_nesting;
+ bool check_decl_utility;
/* NUFILE is small enough to leave this addition unchecked */
iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP);
@@ -294,100 +295,93 @@ get_command(int cf, int sALIAS)
t = newtp(TCOM);
t->lineno = source->line;
goto get_command_start;
- while (/* CONSTCOND */ 1) {
- bool check_decl_utility;
- if (XPsize(args) == 0) {
+ get_command_loop:
+ if (XPsize(args) == 0) {
get_command_start:
- check_decl_utility = true;
- cf = sALIAS | CMDASN;
- } else if (t->u.evalflags)
- cf = CMDWORD | CMDASN;
+ check_decl_utility = true;
+ cf = sALIAS | CMDASN;
+ } else if (t->u.evalflags)
+ cf = CMDWORD | CMDASN;
+ else
+ cf = CMDWORD;
+
+ switch (tpeek(cf)) {
+ case REDIR:
+ while ((iop = synio(cf)) != NULL) {
+ if (iopn >= NUFILE)
+ yyerror(Tf_toomany, Tredirection);
+ iops[iopn++] = iop;
+ }
+ goto get_command_loop;
+
+ case LWORD:
+ ACCEPT;
+ if (check_decl_utility) {
+ struct tbl *tt = get_builtin(ident);
+ uint32_t flag;
+
+ flag = tt ? tt->flag : 0;
+ if (flag & DECL_UTIL)
+ t->u.evalflags = DOVACHECK;
+ if (!(flag & DECL_FWDR))
+ check_decl_utility = false;
+ }
+ if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
+ is_wdvarassign(yylval.cp))
+ XPput(vars, yylval.cp);
else
- cf = CMDWORD;
- switch (tpeek(cf)) {
- case REDIR:
- while ((iop = synio(cf)) != NULL) {
- if (iopn >= NUFILE)
- yyerror(Tf_toomany,
- Tredirection);
- iops[iopn++] = iop;
- }
- break;
+ XPput(args, yylval.cp);
+ goto get_command_loop;
- case LWORD:
+ case ORD('(' /*)*/):
+ if (XPsize(args) == 0 && XPsize(vars) == 1 &&
+ is_wdvarassign(yylval.cp)) {
+ char *tcp;
+
+ /* wdarrassign: foo=(bar) */
ACCEPT;
- if (check_decl_utility) {
- struct tbl *tt = get_builtin(ident);
- uint32_t flag;
-
- flag = tt ? tt->flag : 0;
- if (flag & DECL_UTIL)
- t->u.evalflags = DOVACHECK;
- if (!(flag & DECL_FWDR))
- check_decl_utility = false;
- }
- if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
- is_wdvarassign(yylval.cp))
- XPput(vars, yylval.cp);
- else
+
+ /* manipulate the vars string */
+ tcp = XPptrv(vars)[(vars.len = 0)];
+ /* 'varname=' -> 'varname' */
+ tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
+
+ /* construct new args strings */
+ XPput(args, wdcopy(builtin_cmd, ATEMP));
+ XPput(args, wdcopy(setA_cmd0, ATEMP));
+ XPput(args, wdcopy(setA_cmd1, ATEMP));
+ XPput(args, tcp);
+ XPput(args, wdcopy(setA_cmd2, ATEMP));
+
+ /* slurp in words till closing paren */
+ while (token(CONTIN) == LWORD)
XPput(args, yylval.cp);
+ if (symbol != /*(*/ ')')
+ syntaxerr(NULL);
break;
+ }
- case ORD('(' /*)*/):
- if (XPsize(args) == 0 && XPsize(vars) == 1 &&
- is_wdvarassign(yylval.cp)) {
- char *tcp;
-
- /* wdarrassign: foo=(bar) */
- ACCEPT;
-
- /* manipulate the vars string */
- tcp = XPptrv(vars)[(vars.len = 0)];
- /* 'varname=' -> 'varname' */
- tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
-
- /* construct new args strings */
- XPput(args, wdcopy(builtin_cmd, ATEMP));
- XPput(args, wdcopy(setA_cmd0, ATEMP));
- XPput(args, wdcopy(setA_cmd1, ATEMP));
- XPput(args, tcp);
- XPput(args, wdcopy(setA_cmd2, ATEMP));
-
- /* slurp in words till closing paren */
- while (token(CONTIN) == LWORD)
- XPput(args, yylval.cp);
- if (symbol != /*(*/ ')')
- syntaxerr(NULL);
- } else {
- /*
- * Check for "> foo (echo hi)"
- * which AT&T ksh allows (not
- * POSIX, but not disallowed)
- */
- afree(t, ATEMP);
- if (XPsize(args) == 0 &&
- XPsize(vars) == 0) {
- ACCEPT;
- goto Subshell;
- }
-
- /* must be a function */
- if (iopn != 0 || XPsize(args) != 1 ||
- XPsize(vars) != 0)
- syntaxerr(NULL);
- ACCEPT;
- musthave(/*(*/ ')', 0);
- t = function_body(XPptrv(args)[0],
- sALIAS, false);
- }
- goto Leave;
-
- default:
- goto Leave;
+ afree(t, ATEMP);
+
+ /*
+ * Check for "> foo (echo hi)" which AT&T ksh allows
+ * (not POSIX, but not disallowed)
+ */
+ if (XPsize(args) == 0 && XPsize(vars) == 0) {
+ ACCEPT;
+ goto Subshell;
}
+
+ /* must be a function */
+ if (iopn != 0 || XPsize(args) != 1 || XPsize(vars) != 0)
+ syntaxerr(NULL);
+ ACCEPT;
+ musthave(/*(*/ ')', 0);
+ t = function_body(XPptrv(args)[0],
+ sALIAS, false);
+ break;
}
- Leave:
break;
case ORD('(' /*)*/): {
diff --git a/src/var.c b/src/var.c
index 1263547..ade60c5 100644
--- a/src/var.c
+++ b/src/var.c
@@ -2,7 +2,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+ * 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +29,7 @@
#include <sys/sysctl.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.226 2018/07/15 17:21:24 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.236 2020/04/13 16:29:34 tg Exp $");
/*-
* Variables
@@ -49,7 +50,7 @@ static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
bool);
static char *formatstr(struct tbl *, const char *);
-static void exportprep(struct tbl *, const char *);
+static void exportprep(struct tbl *, const char *, size_t);
static int special(const char *);
static void unspecial(const char *);
static void getspec(struct tbl *);
@@ -57,6 +58,7 @@ static void setspec(struct tbl *);
static void unsetspec(struct tbl *, bool);
static int getint(struct tbl *, mksh_ari_u *, bool);
static const char *array_index_calc(const char *, bool *, uint32_t *);
+static struct tbl *vtypeset(int *, const char *, uint32_t, uint32_t, int, int);
/*
* create a new block for function calls and simple commands
@@ -196,7 +198,7 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
char *cp;
/* gotcha! */
- cp = shf_smprintf(Tf_ss, str_val(vp), p);
+ strdup2x(cp, str_val(vp), p);
afree(ap, ATEMP);
n = ap = cp;
goto redo_from_ref;
@@ -207,16 +209,21 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
if (p != n && ord(*p) == ORD('[') && (len = array_ref_len(p))) {
char *sub, *tmp;
mksh_ari_t rval;
+ size_t tmplen = p - n;
/* calculate the value of the subscript */
*arrayp = true;
- strndupx(tmp, p + 1, len - 2, ATEMP);
+ len -= 2;
+ tmp = alloc((len > tmplen ? len : tmplen) + 1, ATEMP);
+ memcpy(tmp, p + 1, len);
+ tmp[len] = '\0';
sub = substitute(tmp, 0);
- afree(tmp, ATEMP);
- strndupx(n, n, p - n, ATEMP);
evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
*valp = (uint32_t)rval;
afree(sub, ATEMP);
+ memcpy(tmp, n, tmplen);
+ tmp[tmplen] = '\0';
+ n = tmp;
}
return (n);
}
@@ -273,7 +280,7 @@ isglobal(const char *n, bool docreate)
vp->name[0] = c;
vp->name[1] = '\0';
vp->flag |= RDONLY;
- if (vn[1] != '\0')
+ if (!c || vn[1] != '\0')
goto out;
vp->flag |= ISSET|INTEGER;
switch (c) {
@@ -450,7 +457,6 @@ str_val(struct tbl *vp)
int
setstr(struct tbl *vq, const char *s, int error_ok)
{
- char *salloc = NULL;
bool no_ro_check = tobool(error_ok & 0x4);
error_ok &= ~0x4;
@@ -462,28 +468,33 @@ setstr(struct tbl *vq, const char *s, int error_ok)
}
if (!(vq->flag&INTEGER)) {
/* string dest */
+ char *salloc = NULL;
+ size_t cursz;
if ((vq->flag&ALLOC)) {
+ cursz = strlen(vq->val.s) + 1;
#ifndef MKSH_SMALL
/* debugging */
- if (s >= vq->val.s &&
- s <= strnul(vq->val.s)) {
+ if (s >= vq->val.s && s < (vq->val.s + cursz)) {
internal_errorf(
"setstr: %s=%s: assigning to self",
vq->name, s);
}
#endif
- afree(vq->val.s, vq->areap);
- }
- vq->flag &= ~(ISSET|ALLOC);
- vq->type = 0;
+ } else
+ cursz = 0;
if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
s = salloc = formatstr(vq, s);
if ((vq->flag&EXPORT))
- exportprep(vq, s);
+ exportprep(vq, s, cursz);
else {
- strdupx(vq->val.s, s, vq->areap);
+ size_t n = strlen(s) + 1;
+ vq->val.s = aresizeif(cursz, (vq->flag & ALLOC) ?
+ vq->val.s : NULL, n, vq->areap);
+ memcpy(vq->val.s, s, n);
vq->flag |= ALLOC;
+ vq->type = 0;
}
+ afree(salloc, ATEMP);
} else {
/* integer dest */
if (!v_evaluate(vq, s, error_ok, true))
@@ -492,7 +503,6 @@ setstr(struct tbl *vq, const char *s, int error_ok)
vq->flag |= ISSET;
if ((vq->flag&SPECIAL))
setspec(vq);
- afree(salloc, ATEMP);
return (1);
}
@@ -728,25 +738,19 @@ formatstr(struct tbl *vp, const char *s)
* make vp->val.s be "name=value" for quick exporting.
*/
static void
-exportprep(struct tbl *vp, const char *val)
+exportprep(struct tbl *vp, const char *val, size_t cursz)
{
- char *xp;
- char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
- size_t namelen, vallen;
-
- namelen = strlen(vp->name);
- vallen = strlen(val) + 1;
+ char *cp = (vp->flag & ALLOC) ? vp->val.s : NULL;
+ size_t namelen = strlen(vp->name);
+ size_t vallen = strlen(val) + 1;
vp->flag |= ALLOC;
+ vp->type = namelen + 1;
/* since name+val are both in memory this can go unchecked */
- xp = alloc(namelen + 1 + vallen, vp->areap);
- memcpy(vp->val.s = xp, vp->name, namelen);
- xp += namelen;
- *xp++ = '=';
- /* offset to value */
- vp->type = xp - vp->val.s;
- memcpy(xp, val, vallen);
- afree(op, vp->areap);
+ vp->val.s = aresizeif(cursz, cp, vp->type + vallen, vp->areap);
+ memmove(vp->val.s + vp->type, val == cp ? vp->val.s : val, vallen);
+ memcpy(vp->val.s, vp->name, namelen);
+ vp->val.s[namelen] = '=';
}
/*
@@ -757,14 +761,23 @@ exportprep(struct tbl *vp, const char *val)
struct tbl *
typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
{
+ return (vtypeset(NULL, var, set, clr, field, base));
+}
+static struct tbl *
+vtypeset(int *ep, const char *var, uint32_t set, uint32_t clr,
+ int field, int base)
+{
struct tbl *vp;
struct tbl *vpbase, *t;
- char *tvar;
+ char *tvar, tvarbuf[32];
const char *val;
size_t len;
bool vappend = false;
enum namerefflag new_refflag = SRF_NOP;
+ if (ep)
+ *ep = 0;
+
if ((set & (ARRAY | ASSOC)) == ASSOC) {
new_refflag = SRF_ENABLE;
set &= ~(ARRAY | ASSOC);
@@ -782,8 +795,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
}
if (ord(*val) == ORD('[')) {
if (new_refflag != SRF_NOP)
- errorf(Tf_sD_s, var,
- "reference variable can't be an array");
+ return (maybe_errorf(ep, 1, Tf_sD_s, var,
+ "reference variable can't be an array"), NULL);
len = array_ref_len(val);
if (len == 0)
return (NULL);
@@ -804,13 +817,19 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
val += len;
}
if (ord(val[0]) == ORD('=')) {
- strndupx(tvar, var, val - var, ATEMP);
+ len = val - var;
+ tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
+ memcpy(tvar, var, len);
+ tvar[len] = '\0';
++val;
} else if (set & IMPORT) {
/* environment invalid variable name or no assignment */
return (NULL);
} else if (ord(val[0]) == ORD('+') && ord(val[1]) == ORD('=')) {
- strndupx(tvar, var, val - var, ATEMP);
+ len = val - var;
+ tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
+ memcpy(tvar, var, len);
+ tvar[len] = '\0';
val += 2;
vappend = true;
} else if (val[0] != '\0') {
@@ -818,10 +837,12 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
return (NULL);
} else {
/* just varname with no value part nor equals sign */
- strdupx(tvar, var, ATEMP);
+ len = strlen(var);
+ tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
+ memcpy(tvar, var, len);
+ tvar[len] = '\0';
val = NULL;
/* handle foo[*] => foo (whole array) mapping for R39b */
- len = strlen(tvar);
if (len > 3 && ord(tvar[len - 3]) == ORD('[') &&
ord(tvar[len - 2]) == ORD('*') &&
ord(tvar[len - 1]) == ORD(']'))
@@ -833,7 +854,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
/* bail out on 'nameref foo+=bar' */
if (vappend)
- errorf("appending not allowed for nameref");
+ return (maybe_errorf(ep, 1,
+ "appending not allowed for nameref"), NULL);
/* find value if variable already exists */
if ((qval = val) == NULL) {
varsearch(e->loc, &vp, tvar, hash(tvar));
@@ -859,7 +881,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
goto nameref_rhs_checked;
}
nameref_empty:
- errorf(Tf_sD_s, var, "empty nameref target");
+ return (maybe_errorf(ep, 1, Tf_sD_s, var,
+ "empty nameref target"), NULL);
}
len = (ord(*ccp) == ORD('[')) ? array_ref_len(ccp) : 0;
if (ccp[len]) {
@@ -868,15 +891,15 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
* junk after it" and "invalid array"; in the
* latter case, len is also 0 and points to '['
*/
- errorf(Tf_sD_s, qval,
- "nameref target not a valid parameter name");
+ return (maybe_errorf(ep, 1, Tf_sD_s, qval,
+ "nameref target not a valid parameter name"), NULL);
}
nameref_rhs_checked:
/* prevent nameref loops */
while (qval) {
if (!strcmp(qval, tvar))
- errorf(Tf_sD_s, qval,
- "expression recurses on parameter");
+ return (maybe_errorf(ep, 1, Tf_sD_s, qval,
+ "expression recurses on parameter"), NULL);
varsearch(e->loc, &vp, qval, hash(qval));
qval = NULL;
if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
@@ -886,8 +909,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
/* prevent typeset from creating a local PATH/ENV/SHELL */
if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
- strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
- errorf(Tf_sD_s, tvar, "restricted");
+ strcmp(tvar, TENV) == 0 || strcmp(tvar, TSHELL) == 0))
+ return (maybe_errorf(ep, 1, Tf_sD_s,
+ tvar, "restricted"), NULL);
innermost_refflag = new_refflag;
vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
@@ -923,9 +947,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
*/
if ((vpbase->flag & RDONLY) &&
(val || clr || (set & ~(EXPORT | RDONLY))))
- /* XXX check calls - is error here ok by POSIX? */
- errorfx(2, Tf_ro, tvar);
- afree(tvar, ATEMP);
+ return (maybe_errorf(ep, 2, Tf_ro, tvar), NULL);
+ if (tvar != tvarbuf)
+ afree(tvar, ATEMP);
/* most calls are with set/clr == 0 */
if (set | clr) {
@@ -990,18 +1014,24 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
}
}
if (!ok)
- errorfz();
+ return (maybe_errorf(ep, 1, NULL), NULL);
}
- if (val != NULL) {
- char *tval;
-
- if (vappend) {
- tval = shf_smprintf(Tf_ss, str_val(vp), val);
- val = tval;
- } else
- tval = NULL;
-
+ if (vappend) {
+ size_t tlen;
+ if ((vp->flag & (ISSET|ALLOC|SPECIAL|INTEGER|UCASEV_AL|LCASEV|LJUST|RJUST)) != (ISSET|ALLOC)) {
+ /* cannot special-case this */
+ strdup2x(tvar, str_val(vp), val);
+ val = tvar;
+ goto vassign;
+ }
+ /* trivial string appending */
+ len = strlen(vp->val.s);
+ tlen = strlen(val) + 1;
+ vp->val.s = aresize(vp->val.s, len + tlen, vp->areap);
+ memcpy(vp->val.s + len, val, tlen);
+ } else if (val != NULL) {
+ vassign:
if (vp->flag&INTEGER) {
/* do not zero base before assignment */
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
@@ -1012,13 +1042,16 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
/* setstr can't fail (readonly check already done) */
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
- afree(tval, ATEMP);
+ /* came here from vappend? need to free temp val */
+ if (vappend)
+ afree(tvar, ATEMP);
}
/* only x[0] is ever exported, so use vpbase */
- if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
+ if ((vpbase->flag & (EXPORT|INTEGER)) == EXPORT &&
vpbase->type == 0)
- exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
+ exportprep(vpbase, (vpbase->flag & ISSET) ?
+ vpbase->val.s : null, 0);
return (vp);
}
@@ -1829,7 +1862,7 @@ record_match(const char *istr)
setstr(vp, istr, 0x4);
}
-/* typeset, global(deprecated), export, and readonly */
+/* typeset, export and readonly */
int
c_typeset(const char **wp)
{
@@ -2026,7 +2059,7 @@ c_typeset(const char **wp)
if (wp[builtin_opt.optind] &&
/* not "typeset -p varname" */
!(!func && pflag && !(fset | fclr))) {
- int rv = 0;
+ int rv = 0, x;
struct tbl *f;
if (localv && !func)
@@ -2049,7 +2082,10 @@ c_typeset(const char **wp)
wp[i], f->val.t);
shf_putc('\n', shl_stdout);
}
- } else if (!typeset(wp[i], fset, fclr, field, base)) {
+ } else if (!vtypeset(&x, wp[i], fset, fclr,
+ field, base)) {
+ if (x)
+ return (x);
bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
return (1);
}