[Midnightbsd-cvs] src [12141] trunk/contrib/mksh: mksh R56c
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Fri Jan 18 16:15:43 EST 2019
Revision: 12141
http://svnweb.midnightbsd.org/src/?rev=12141
Author: laffer1
Date: 2019-01-18 16:15:37 -0500 (Fri, 18 Jan 2019)
Log Message:
-----------
mksh R56c
Modified Paths:
--------------
trunk/contrib/mksh/Build.sh
trunk/contrib/mksh/check.pl
trunk/contrib/mksh/check.t
trunk/contrib/mksh/dot.mkshrc
trunk/contrib/mksh/edit.c
trunk/contrib/mksh/emacsfn.h
trunk/contrib/mksh/eval.c
trunk/contrib/mksh/exec.c
trunk/contrib/mksh/expr.c
trunk/contrib/mksh/funcs.c
trunk/contrib/mksh/histrap.c
trunk/contrib/mksh/jobs.c
trunk/contrib/mksh/lalloc.c
trunk/contrib/mksh/lex.c
trunk/contrib/mksh/lksh.1
trunk/contrib/mksh/main.c
trunk/contrib/mksh/mirhash.h
trunk/contrib/mksh/misc.c
trunk/contrib/mksh/mksh.1
trunk/contrib/mksh/rlimits.gen
trunk/contrib/mksh/rlimits.opt
trunk/contrib/mksh/sh.h
trunk/contrib/mksh/sh_flags.gen
trunk/contrib/mksh/sh_flags.opt
trunk/contrib/mksh/shf.c
trunk/contrib/mksh/strlcpy.c
trunk/contrib/mksh/syn.c
trunk/contrib/mksh/tree.c
trunk/contrib/mksh/var.c
trunk/contrib/mksh/var_spec.h
Added Paths:
-----------
trunk/contrib/mksh/exprtok.h
trunk/contrib/mksh/jehanne.c
trunk/contrib/mksh/mksh.ico
trunk/contrib/mksh/os2.c
trunk/contrib/mksh/test.sh
Removed Paths:
-------------
trunk/contrib/mksh/Makefile
Property Changed:
----------------
trunk/contrib/mksh/
Index: trunk/contrib/mksh
===================================================================
--- trunk/contrib/mksh 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh 2019-01-18 21:15:37 UTC (rev 12141)
Property changes on: trunk/contrib/mksh
___________________________________________________________________
Modified: svn:mergeinfo
## -1,2 +1,2 ##
/branches/MIROS/contrib/mksh:5952-6384
-/vendor/MirOS/mksh/dist:6385-6980
\ No newline at end of property
+/vendor/MirOS/mksh/dist:6385-12140
\ No newline at end of property
Modified: trunk/contrib/mksh/Build.sh
===================================================================
--- trunk/contrib/mksh/Build.sh 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/Build.sh 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
#!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.669.2.2 2015/03/01 15:42:50 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.731 2018/01/13 21:38:06 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014
-# Thorsten Glaser <tg at mirbsd.org>
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017
+# mirabilos <m at mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
@@ -53,6 +53,16 @@
alln=0123456789
alls=______________________________________________________________
+case `echo a | tr '\201' X` in
+X)
+ # EBCDIC build system
+ lfcr='\n\r'
+ ;;
+*)
+ lfcr='\012\015'
+ ;;
+esac
+
genopt_die() {
if test -n "$1"; then
echo >&2 "E: $*"
@@ -100,6 +110,7 @@
srcfile=$1
test -f "$srcfile" || genopt_die Source file \$srcfile not set.
bn=`basename "$srcfile" | sed 's/.opt$//'`
+ o_hdr='/* +++ GENERATED FILE +++ DO NOT EDIT +++ */'
o_gen=
o_str=
o_sym=
@@ -119,7 +130,7 @@
state=3
;;
1:@@)
- # begin of data block
+ # start of data block
o_gen=$o_gen$nl"#endif"
o_gen=$o_gen$nl"#ifndef F0"
o_gen=$o_gen$nl"#define F0 FN"
@@ -128,8 +139,11 @@
;;
*:@@*)
genopt_die ;;
+ 0:/\*-|0:\ \**|0:)
+ o_hdr=$o_hdr$nl$line
+ ;;
0:@*|1:@*)
- # begin of a definition block
+ # start of a definition block
sym=`echo "$line" | sed 's/^@//'`
if test $state = 0; then
o_gen=$o_gen$nl"#if defined($sym)"
@@ -177,6 +191,7 @@
echo "\"$opts\""
test -n "$cond" && echo "#endif"
done | {
+ echo "$o_hdr"
echo "#ifndef $o_sym$o_gen"
echo "#else"
cat
@@ -314,6 +329,7 @@
vv ']' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN conftest.c $LIBS $ccpr"
test $tcfn = no && test -f a.out && tcfn=a.out
test $tcfn = no && test -f a.exe && tcfn=a.exe
+ test $tcfn = no && test -f conftest.exe && tcfn=conftest.exe
test $tcfn = no && test -f conftest && tcfn=conftest
if test -f $tcfn; then
test 1 = $fr || fv=1
@@ -419,7 +435,7 @@
na=0
fi
hf=$1; shift
- hv=`echo "$hf" | tr -d '\012\015' | tr -c $alll$allu$alln $alls`
+ hv=`echo "$hf" | tr -d "$lfcr" | tr -c $alll$allu$alln $alls`
echo "/* NeXTstep bug workaround */" >x
for i
do
@@ -489,6 +505,8 @@
last=
tfn=
legacy=0
+textmode=0
+ebcdic=false
for i
do
@@ -512,6 +530,9 @@
:-c)
last=c
;;
+ :-E)
+ ebcdic=true
+ ;;
:-G)
echo "$me: Do not call me with '-G'!" >&2
exit 1
@@ -545,6 +566,12 @@
:-r)
r=1
;;
+ :-T)
+ textmode=1
+ ;;
+ :+T)
+ textmode=0
+ ;;
:-t)
last=t
;;
@@ -577,22 +604,30 @@
echo "$me: Error: ./$tfn is a directory!" >&2
exit 1
fi
-rmf a.exe* a.out* conftest.c *core core.* lft ${tfn}* no *.bc *.ll *.o *.gen \
- Rebuild.sh signames.inc test.sh x vv.out
+rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \
+ *.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out
-SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
+SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
if test $legacy = 0; then
- SRCS="$SRCS edit.c"
check_categories="$check_categories shell:legacy-no int:32"
else
check_categories="$check_categories shell:legacy-yes"
add_cppflags -DMKSH_LEGACY_MODE
- HAVE_PERSISTENT_HISTORY=0
- HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1 # from sh.h
fi
+if $ebcdic; then
+ add_cppflags -DMKSH_EBCDIC
+fi
+
+if test $textmode = 0; then
+ check_categories="$check_categories shell:textmode-no shell:binmode-yes"
+else
+ check_categories="$check_categories shell:textmode-yes shell:binmode-no"
+ add_cppflags -DMKSH_WITH_TEXTMODE
+fi
+
if test x"$srcdir" = x"."; then
CPPFLAGS="-I. $CPPFLAGS"
else
@@ -682,14 +717,13 @@
# Configuration depending on OS name
case $TARGET_OS in
386BSD)
- : ${HAVE_CAN_OTWO=0}
+ : "${HAVE_CAN_OTWO=0}"
add_cppflags -DMKSH_NO_SIGSETJMP
add_cppflags -DMKSH_TYPEDEF_SIG_ATOMIC_T=int
- add_cppflags -DMKSH_CONSERVATIVE_FDS
;;
AIX)
add_cppflags -D_ALL_SOURCE
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
BeOS)
case $KSH_VERSION in
@@ -708,7 +742,7 @@
add_cppflags -DMKSH__NO_SETEUGID
;;
BSD/OS)
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
Coherent)
oswarn="; it has major issues"
@@ -715,11 +749,10 @@
add_cppflags -DMKSH__NO_SYMLINK
check_categories="$check_categories nosymlink"
add_cppflags -DMKSH__NO_SETEUGID
- add_cppflags -DMKSH_CONSERVATIVE_FDS
add_cppflags -DMKSH_DISABLE_TTY_WARNING
;;
CYGWIN*)
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
Darwin)
add_cppflags -D_DARWIN_C_SOURCE
@@ -731,8 +764,7 @@
FreeMiNT)
oswarn="; it has minor issues"
add_cppflags -D_GNU_SOURCE
- add_cppflags -DMKSH_CONSERVATIVE_FDS
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
GNU)
case $CC in
@@ -739,6 +771,7 @@
*tendracc*) ;;
*) add_cppflags -D_GNU_SOURCE ;;
esac
+ add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
# define MKSH__NO_PATH_MAX to use Hurd-only functions
add_cppflags -DMKSH__NO_PATH_MAX
;;
@@ -747,10 +780,35 @@
*tendracc*) ;;
*) add_cppflags -D_GNU_SOURCE ;;
esac
+ add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
;;
Haiku)
- add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ add_cppflags -DMKSH_ASSUME_UTF8
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=0
;;
+Harvey)
+ add_cppflags -D_POSIX_SOURCE
+ add_cppflags -D_LIMITS_EXTENSION
+ add_cppflags -D_BSD_EXTENSION
+ add_cppflags -D_SUSV2_SOURCE
+ add_cppflags -D_GNU_SOURCE
+ add_cppflags -DMKSH_ASSUME_UTF8
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=0
+ add_cppflags -DMKSH__NO_SYMLINK
+ check_categories="$check_categories nosymlink"
+ add_cppflags -DMKSH_NO_CMDLINE_EDITING
+ add_cppflags -DMKSH__NO_SETEUGID
+ oswarn=' and will currently not work'
+ add_cppflags -DMKSH_UNEMPLOYED
+ add_cppflags -DMKSH_NOPROSPECTOFWORK
+ # these taken from Harvey-OS github and need re-checking
+ add_cppflags -D_setjmp=setjmp -D_longjmp=longjmp
+ : "${HAVE_CAN_NO_EH_FRAME=0}"
+ : "${HAVE_CAN_FNOSTRICTALIASING=0}"
+ : "${HAVE_CAN_FSTACKPROTECTORSTRONG=0}"
+ ;;
HP-UX)
;;
Interix)
@@ -757,12 +815,26 @@
ccpc='-X '
ccpl='-Y '
add_cppflags -D_ALL_SOURCE
- : ${LIBS='-lcrypt'}
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${LIBS=-lcrypt}"
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
IRIX*)
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
+Jehanne)
+ add_cppflags -DMKSH_ASSUME_UTF8
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=0
+ add_cppflags -DMKSH__NO_SYMLINK
+ check_categories="$check_categories nosymlink"
+ add_cppflags -DMKSH_NO_CMDLINE_EDITING
+ add_cppflags -DMKSH_DISABLE_REVOKE_WARNING
+ add_cppflags '-D_PATH_DEFPATH=\"/cmd\"'
+ add_cppflags '-DMKSH_DEFAULT_EXECSHELL=\"/cmd/mksh\"'
+ add_cppflags '-DMKSH_DEFAULT_PROFILEDIR=\"/cfg/mksh\"'
+ add_cppflags '-DMKSH_ENVDIR=\"/env\"'
+ SRCS="$SRCS jehanne.c"
+ ;;
Linux)
case $CC in
*tendracc*) ;;
@@ -769,7 +841,7 @@
*) add_cppflags -D_GNU_SOURCE ;;
esac
add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
- : ${HAVE_REVOKE=0}
+ : "${HAVE_REVOKE=0}"
;;
LynxOS)
oswarn="; it has minor issues"
@@ -779,27 +851,27 @@
Minix-vmd)
add_cppflags -DMKSH__NO_SETEUGID
add_cppflags -DMKSH_UNEMPLOYED
- add_cppflags -DMKSH_CONSERVATIVE_FDS
add_cppflags -D_MINIX_SOURCE
oldish_ed=no-stderr-ed # no /bin/ed, maybe see below
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
Minix3)
add_cppflags -DMKSH_UNEMPLOYED
- add_cppflags -DMKSH_CONSERVATIVE_FDS
add_cppflags -DMKSH_NO_LIMITS
add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX
oldish_ed=no-stderr-ed # /usr/bin/ed(!) is broken
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
MirBSD)
;;
MSYS_*)
- add_cppflags -DMKSH_ASSUME_UTF8=0; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ add_cppflags -DMKSH_ASSUME_UTF8=0
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=1
# almost same as CYGWIN* (from RT|Chatzilla)
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
# broken on this OE (from ir0nh34d)
- : ${HAVE_STDINT_H=0}
+ : "${HAVE_STDINT_H=0}"
;;
NetBSD)
;;
@@ -806,7 +878,8 @@
NEXTSTEP)
add_cppflags -D_NEXT_SOURCE
add_cppflags -D_POSIX_SOURCE
- : ${AWK=gawk} ${CC=cc -posix}
+ : "${AWK=gawk}"
+ : "${CC=cc -posix}"
add_cppflags -DMKSH_NO_SIGSETJMP
# NeXTstep cannot get a controlling tty
add_cppflags -DMKSH_UNEMPLOYED
@@ -816,19 +889,64 @@
oswarn="; it needs libposix.a"
;;
esac
- add_cppflags -DMKSH_CONSERVATIVE_FDS
;;
Ninix3)
# similar to Minix3
add_cppflags -DMKSH_UNEMPLOYED
- add_cppflags -DMKSH_CONSERVATIVE_FDS
add_cppflags -DMKSH_NO_LIMITS
# but no idea what else could be needed
oswarn="; it has unknown issues"
;;
OpenBSD)
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
+OS/2)
+ add_cppflags -DMKSH_ASSUME_UTF8=0
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=1
+ HAVE_TERMIOS_H=0
+ HAVE_MKNOD=0 # setmode() incompatible
+ oswarn="; it is being ported"
+ check_categories="$check_categories nosymlink"
+ : "${CC=gcc}"
+ : "${SIZE=: size}"
+ SRCS="$SRCS os2.c"
+ add_cppflags -DMKSH_UNEMPLOYED
+ add_cppflags -DMKSH_NOPROSPECTOFWORK
+ add_cppflags -DMKSH_NO_LIMITS
+ add_cppflags -DMKSH_DOSPATH
+ if test $textmode = 0; then
+ x='dis'
+ y='standard OS/2 tools'
+ else
+ x='en'
+ y='standard Unix mksh and other tools'
+ fi
+ echo >&2 "
+OS/2 Note: mksh can be built with or without 'textmode'.
+Without 'textmode' it will behave like a standard Unix utility,
+compatible to mksh on all other platforms, using only ASCII LF
+(0x0A) as line ending character. This is supported by the mksh
+upstream developer.
+With 'textmode', mksh will be modified to behave more like other
+OS/2 utilities, supporting ASCII CR+LF (0x0D 0x0A) as line ending
+at the cost of deviation from standard mksh. This is supported by
+the mksh-os2 porter.
+
+] You are currently compiling with textmode ${x}abled, introducing
+] incompatibilities with $y.
+"
+ ;;
+OS/390)
+ add_cppflags -DMKSH_ASSUME_UTF8=0
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=1
+ : "${CC=xlc}"
+ : "${SIZE=: size}"
+ add_cppflags -DMKSH_FOR_Z_OS
+ add_cppflags -D_ALL_SOURCE
+ oswarn='; EBCDIC support is incomplete'
+ ;;
OSF1)
HAVE_SIG_T=0 # incompatible
add_cppflags -D_OSF_SOURCE
@@ -835,7 +953,7 @@
add_cppflags -D_POSIX_C_SOURCE=200112L
add_cppflags -D_XOPEN_SOURCE=600
add_cppflags -D_XOPEN_SOURCE_EXTENDED
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
Plan9)
add_cppflags -D_POSIX_SOURCE
@@ -842,7 +960,11 @@
add_cppflags -D_LIMITS_EXTENSION
add_cppflags -D_BSD_EXTENSION
add_cppflags -D_SUSV2_SOURCE
- add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ add_cppflags -DMKSH_ASSUME_UTF8
+ HAVE_ISSET_MKSH_ASSUME_UTF8=1
+ HAVE_ISOFF_MKSH_ASSUME_UTF8=0
+ add_cppflags -DMKSH__NO_SYMLINK
+ check_categories="$check_categories nosymlink"
add_cppflags -DMKSH_NO_CMDLINE_EDITING
add_cppflags -DMKSH__NO_SETEUGID
oswarn=' and will currently not work'
@@ -853,7 +975,7 @@
PW32*)
HAVE_SIG_T=0 # incompatible
oswarn=' and will currently not work'
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
QNX)
add_cppflags -D__NO_EXT_QNX
@@ -863,7 +985,7 @@
oldish_ed=no-stderr-ed # oldish /bin/ed is broken
;;
esac
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
SCO_SV)
case $TARGET_OSREV in
@@ -879,8 +1001,7 @@
oswarn="$oswarn$nl$TARGET_OS ${TARGET_OSREV}, please tell me what to do"
;;
esac
- add_cppflags -DMKSH_CONSERVATIVE_FDS
- : ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0}
+ : "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}"
;;
skyos)
oswarn="; it has minor issues"
@@ -895,15 +1016,13 @@
oswarn=' and will currently not work'
;;
ULTRIX)
- : ${CC=cc -YPOSIX}
+ : "${CC=cc -YPOSIX}"
add_cppflags -DMKSH_TYPEDEF_SSIZE_T=int
- add_cppflags -DMKSH_CONSERVATIVE_FDS
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
UnixWare|UNIX_SV)
# SCO UnixWare
- add_cppflags -DMKSH_CONSERVATIVE_FDS
- : ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0}
+ : "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}"
;;
UWIN*)
ccpc='-Yc,'
@@ -911,7 +1030,7 @@
tsts=" 3<>/dev/tty"
oswarn="; it will compile, but the target"
oswarn="$oswarn${nl}platform itself is very flakey/unreliable"
- : ${HAVE_SETLOCALE_CTYPE=0}
+ : "${HAVE_SETLOCALE_CTYPE=0}"
;;
_svr4)
# generic target for SVR4 Unix with uname -s = uname -n
@@ -925,11 +1044,11 @@
;;
esac
-: ${HAVE_MKNOD=0}
+: "${HAVE_MKNOD=0}"
-: ${AWK=awk} ${CC=cc} ${NROFF=nroff} ${SIZE=size}
+: "${AWK=awk}${CC=cc}${NROFF=nroff}${SIZE=size}"
test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \
- NROFF="$NROFF -c"
+ echo | $NROFF -c >/dev/null 2>&1 && NROFF="$NROFF -c"
# this aids me in tracing FTBFSen without access to the buildd
$e "Hi from$ao $bi$srcversion$ao on:"
@@ -972,7 +1091,7 @@
$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
#
-# Begin of mirtoconf checks
+# Start of mirtoconf checks
#
$e $bi$me: Scanning for functions... please ignore any errors.$ao
@@ -985,7 +1104,7 @@
# - LLVM+clang defines __GNUC__ too
# - nwcc defines __GNUC__ too
CPP="$CC -E"
-$e ... which compiler seems to be used
+$e ... which compiler type seems to be used
cat >conftest.c <<'EOF'
const char *
#if defined(__ICC) || defined(__INTEL_COMPILER)
@@ -1050,7 +1169,7 @@
#endif
;
const char *
-#if defined(__KLIBC__)
+#if defined(__KLIBC__) && !defined(__OS2__)
et="klibc"
#else
et="unknown"
@@ -1095,6 +1214,7 @@
:*) ;;
*:) CCC_LD=$CCC_CC; export CCC_LD ;;
esac
+ : "${HAVE_STRING_POOLING=i1}"
;;
dec)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
@@ -1111,6 +1231,7 @@
vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
-dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
$LIBS -dumpversion`'
+ : "${HAVE_STRING_POOLING=i2}"
;;
hpcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
@@ -1183,7 +1304,7 @@
;;
tendra)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V 2>&1 | \
- fgrep -i -e version -e release"
+ grep -F -i -e version -e release"
;;
ucode)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
@@ -1194,7 +1315,7 @@
SCO_SV:3.2*)
# SCO OpenServer 5
CFLAGS="$CFLAGS -g"
- : ${HAVE_CAN_OTWO=0} ${HAVE_CAN_OPTIMISE=0}
+ : "${HAVE_CAN_OTWO=0}${HAVE_CAN_OPTIMISE=0}"
;;
esac
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
@@ -1220,6 +1341,7 @@
vv '|' "llc -version"
;;
esac
+etd=" on $et"
case $et in
klibc)
add_cppflags -DMKSH_NO_LIMITS
@@ -1226,14 +1348,14 @@
;;
unknown)
# nothing special detected, don’t worry
- unset et
+ etd=
;;
*)
# huh?
;;
esac
-$e "$bi==> which compiler seems to be used...$ao $ui$ct${et+ on $et}$ao"
-rmf conftest.c conftest.o conftest a.out* a.exe* vv.out
+$e "$bi==> which compiler type seems to be used...$ao $ui$ct$etd$ao"
+rmf conftest.c conftest.o conftest a.out* a.exe* conftest.exe* vv.out
#
# Compiler: works as-is, with -Wno-error and -Werror
@@ -1248,7 +1370,7 @@
if ac_ifcpp 'if 0' compiler_fails '' \
'if the compiler does not fail correctly'; then
save_CFLAGS=$CFLAGS
- : ${HAVE_CAN_DELEXE=x}
+ : "${HAVE_CAN_DELEXE=x}"
case $ct in
dec)
CFLAGS="$CFLAGS ${ccpl}-non_shared"
@@ -1327,8 +1449,16 @@
DOWARN=-Wc,-we
;;
xlc)
- save_NOWARN=-qflag=i:e
- DOWARN=-qflag=i:i
+ case $TARGET_OS in
+ OS/390)
+ save_NOWARN=-qflag=e
+ DOWARN=-qflag=i
+ ;;
+ *)
+ save_NOWARN=-qflag=i:e
+ DOWARN=-qflag=i:i
+ ;;
+ esac
;;
*)
test x"$save_NOWARN" = x"" && save_NOWARN=-Wno-error
@@ -1447,6 +1577,7 @@
ac_flags $t_use $t_name "$t_cflags" \
"if gcc supports $t_cflags $t_ldflags" "$t_ldflags"
done
+ ac_flags 1 data_abi_align -malign-data=abi
i=1
;;
hpcc)
@@ -1475,9 +1606,13 @@
ac_flags 1 wp64 "${ccpc}/Wp64" 'to enable 64-bit warnings'
;;
nwcc)
+ #broken# ac_flags 1 ssp -stackprotect
i=1
- #broken# ac_flags 1 ssp -stackprotect
;;
+pcc)
+ ac_flags 1 fstackprotectorall -fstack-protector-all
+ i=1
+ ;;
sunpro)
phase=u
ac_flags 1 v -v
@@ -1493,10 +1628,24 @@
ac_flags 1 extansi -Xa
;;
xlc)
- ac_flags 1 rodata "-qro -qroconst -qroptr"
- ac_flags 1 rtcheck -qcheck=all
- #ac_flags 1 rtchkc -qextchk # reported broken
- ac_flags 1 wformat "-qformat=all -qformat=nozln"
+ case $TARGET_OS in
+ OS/390)
+ # On IBM z/OS, the following are warnings by default:
+ # CCN3296: #include file <foo.h> not found.
+ # CCN3944: Attribute "__foo__" is not supported and is ignored.
+ # CCN3963: The attribute "foo" is not a valid variable attribute and is ignored.
+ ac_flags 1 halton '-qhaltonmsg=CCN3296 -qhaltonmsg=CCN3944 -qhaltonmsg=CCN3963'
+ # CCN3290: Unknown macro name FOO on #undef directive.
+ # CCN4108: The use of keyword '__attribute__' is non-portable.
+ ac_flags 1 supprss '-qsuppress=CCN3290 -qsuppress=CCN4108'
+ ;;
+ *)
+ ac_flags 1 rodata '-qro -qroconst -qroptr'
+ ac_flags 1 rtcheck -qcheck=all
+ #ac_flags 1 rtchkc -qextchk # reported broken
+ ac_flags 1 wformat '-qformat=all -qformat=nozln'
+ ;;
+ esac
#ac_flags 1 wp64 -qwarn64 # too verbose for now
;;
esac
@@ -1506,6 +1655,16 @@
ac_flags 1 fwrapv -fwrapv
fi
+# “on demand” means: GCC version >= 4
+fd='if to rely on compiler for string pooling'
+ac_cache string_pooling || case $HAVE_STRING_POOLING in
+2) fx=' (on demand, cached)' ;;
+i1) fv=1 ;;
+i2) fv=2; fx=' (on demand)' ;;
+esac
+ac_testdone
+test x"$HAVE_STRING_POOLING" = x"0" || ac_cppflags
+
phase=x
# The following tests run with -Werror or similar (all compilers) if possible
NOWARN=$DOWARN
@@ -1610,10 +1769,9 @@
#
if ac_ifcpp 'ifdef MKSH_SMALL' isset_MKSH_SMALL '' \
"if a reduced-feature mksh is requested"; then
- : ${HAVE_NICE=0}
- : ${HAVE_PERSISTENT_HISTORY=0}
+ : "${HAVE_NICE=0}"
+ : "${HAVE_PERSISTENT_HISTORY=0}"
check_categories="$check_categories smksh"
- HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1 # from sh.h
fi
ac_ifcpp 'if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)' \
isset_MKSH_BINSH '' 'if invoking as sh should be handled specially' && \
@@ -1625,10 +1783,11 @@
"if mksh will be built without job signals" && \
check_categories="$check_categories arge nojsig"
ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \
- 'if the default UTF-8 mode is specified' && : ${HAVE_SETLOCALE_CTYPE=0}
-ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
- 'if traditional/conservative fd use is requested' && \
- check_categories="$check_categories convfds"
+ 'if the default UTF-8 mode is specified' && : "${HAVE_SETLOCALE_CTYPE=0}"
+ac_ifcpp 'if !MKSH_ASSUME_UTF8' isoff_MKSH_ASSUME_UTF8 \
+ isset_MKSH_ASSUME_UTF8 0 \
+ 'if the default UTF-8 mode is disabled' && \
+ check_categories="$check_categories noutf8"
#ac_ifcpp 'ifdef MKSH_DISABLE_DEPRECATED' isset_MKSH_DISABLE_DEPRECATED '' \
# "if deprecated features are to be omitted" && \
# check_categories="$check_categories nodeprecated"
@@ -1662,6 +1821,7 @@
ac_header sys/sysmacros.h
ac_header bstring.h
ac_header grp.h sys/types.h
+ac_header io.h
ac_header libgen.h
ac_header libutil.h sys/types.h
ac_header paths.h
@@ -1692,6 +1852,7 @@
ac_testn can_lfs_aix '!' can_lfs 0 "... with -D_LARGE_FILES=1" <lft.c
test 1 = $HAVE_CAN_LFS_AIX || CPPFLAGS=$save_CPPFLAGS
fi
+rm -f lft.c
rmf lft* # end of large file support test
#
@@ -1780,12 +1941,12 @@
HAVE_LINK_WORKS=x
ac_testinit link_works '' 'checking if the final link command may succeed'
fv=1
- cat >conftest.c <<-'EOF'
+ cat >conftest.c <<-EOF
#define EXTERN
#define MKSH_INCLUDES_ONLY
#include "sh.h"
- __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.669.2.2 2015/03/01 15:42:50 tg Exp $");
- int main(void) { printf("Hello, World!\n"); return (isatty(0)); }
+ __RCSID("$srcversion");
+ int main(void) { printf("Hello, World!\\n"); return (isatty(0)); }
EOF
case $cm in
llvm)
@@ -1904,11 +2065,6 @@
int main(void) { struct timeval tv; return (gettimeofday(&tv, NULL)); }
EOF
-ac_test issetugid <<-'EOF'
- #include <unistd.h>
- int main(void) { return (issetugid()); }
-EOF
-
ac_test killpg <<-'EOF'
#include <signal.h>
int main(int ac, char *av[]) { return (av[0][killpg(123, ac)]); }
@@ -1952,6 +2108,11 @@
munmap(NULL, 0)); }
EOF
+ac_test ftruncate mmap 0 'for ftruncate' <<-'EOF'
+ #include <unistd.h>
+ int main(void) { return (ftruncate(0, 0)); }
+EOF
+
ac_test nice <<-'EOF'
#include <unistd.h>
int main(void) { return (nice(4)); }
@@ -2106,74 +2267,13 @@
# other checks
#
fd='if to use persistent history'
-ac_cache PERSISTENT_HISTORY || case $HAVE_MMAP$HAVE_FLOCK$HAVE_LOCK_FCNTL in
-11*|101) fv=1 ;;
+ac_cache PERSISTENT_HISTORY || case $HAVE_FTRUNCATE$HAVE_MMAP$HAVE_FLOCK$HAVE_LOCK_FCNTL in
+111*|1101) fv=1 ;;
esac
test 1 = $fv || check_categories="$check_categories no-histfile"
ac_testdone
ac_cppflags
-save_CFLAGS=$CFLAGS
-ac_testn compile_time_asserts_$$ '' 'whether compile-time assertions pass' <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- #ifndef CHAR_BIT
- #define CHAR_BIT 8 /* defuse this test on really legacy systems */
- #endif
- struct ctasserts {
- #define cta(name, assertion) char name[(assertion) ? 1 : -1]
-/* this one should be defined by the standard */
-cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
- (sizeof(unsigned char) == 1));
-cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) &&
- ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF));
-/* the next assertion is probably not really needed */
-cta(short_is_2_char, sizeof(short) == 2);
-cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short));
-/* the next assertion is probably not really needed */
-cta(int_is_4_char, sizeof(int) == 4);
-cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int));
-
-cta(long_ge_int, sizeof(long) >= sizeof(int));
-cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long));
-
-#ifndef MKSH_LEGACY_MODE
-/* the next assertion is probably not really needed */
-cta(ari_is_4_char, sizeof(mksh_ari_t) == 4);
-/* but this is */
-cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1));
-/* the next assertion is probably not really needed */
-cta(uari_is_4_char, sizeof(mksh_uari_t) == 4);
-/* but the next three are; we REQUIRE unsigned integer wraparound */
-cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1));
-cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3));
-cta(uari_wrap_32_bit,
- (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) >
- (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4));
-#define NUM 21
-#else
-#define NUM 15
-#endif
-/* these are always required */
-cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0);
-cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0);
-/* we require these to have the precisely same size and assume 2s complement */
-cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t));
-
-cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t));
-cta(sizet_voidptr_same_size, sizeof(size_t) == sizeof(void *));
-cta(sizet_funcptr_same_size, sizeof(size_t) == sizeof(void (*)(void)));
-/* our formatting routines assume this */
-cta(ptr_fits_in_long, sizeof(size_t) <= sizeof(long));
-/* for struct alignment people */
- char padding[64 - NUM];
- };
-char ctasserts_dblcheck[sizeof(struct ctasserts) == 64 ? 1 : -1];
- int main(void) { return (sizeof(ctasserts_dblcheck) + isatty(0)); }
-EOF
-CFLAGS=$save_CFLAGS
-eval test 1 = \$HAVE_COMPILE_TIME_ASSERTS_$$ || exit 1
-
#
# extra checks for legacy mksh
#
@@ -2246,20 +2346,22 @@
sigseenone=:
sigseentwo=:
echo '#include <signal.h>
-#ifndef NSIG
-#if defined(_NSIG)
-#define NSIG _NSIG
+#if defined(NSIG_MAX)
+#define cfg_NSIG NSIG_MAX
+#elif defined(NSIG)
+#define cfg_NSIG NSIG
+#elif defined(_NSIG)
+#define cfg_NSIG _NSIG
#elif defined(SIGMAX)
-#define NSIG (SIGMAX+1)
+#define cfg_NSIG (SIGMAX + 1)
#elif defined(_SIGMAX)
-#define NSIG (_SIGMAX+1)
+#define cfg_NSIG (_SIGMAX + 1)
#else
-/* XXX better error out, see sh.h */
-#define NSIG 64
+/*XXX better error out, see sh.h */
+#define cfg_NSIG 64
#endif
-#endif
int
-mksh_cfg= NSIG
+mksh_cfg= cfg_NSIG
;' >conftest.c
# GNU sed 2.03 segfaults when optimising this to sed -n
NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
@@ -2281,8 +2383,8 @@
$printf "NSIG=$NSIG ... "
sigs="ABRT FPE ILL INT SEGV TERM ALRM BUS CHLD CONT HUP KILL PIPE QUIT"
sigs="$sigs STOP TSTP TTIN TTOU USR1 USR2 POLL PROF SYS TRAP URG VTALRM"
- sigs="$sigs XCPU XFSZ INFO WINCH EMT IO DIL LOST PWR SAK CLD IOT RESV"
- sigs="$sigs STKFLT UNUSED"
+ sigs="$sigs XCPU XFSZ INFO WINCH EMT IO DIL LOST PWR SAK CLD IOT STKFLT"
+ sigs="$sigs ABND DCE DUMP IOERR TRACE DANGER THCONT THSTOP RESV UNUSED"
test 1 = $HAVE_CPP_DD && test $NSIG -gt 1 && sigs="$sigs "`vq \
"$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c" | \
grep '[ ]SIG[A-Z0-9][A-Z0-9]*[ ]' | \
@@ -2308,7 +2410,7 @@
sed 's/^ *mksh_cfg *=[ ]*\([0-9][0-9x]*\).*$/:\1 '$name/
done | sed -n '/^:[^ ]/s/^://p' | while read nr name; do
test $printf = echo || nr=`printf %d "$nr" 2>/dev/null`
- test $nr -gt 0 && test $nr -le $NSIG || continue
+ test $nr -gt 0 && test $nr -lt $NSIG || continue
case $sigseentwo in
*:$nr:*) ;;
*) echo " { \"$name\", $nr },"
@@ -2325,7 +2427,7 @@
addsrcs USE_PRINTF_BUILTIN printf.c
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
-add_cppflags -DMKSH_BUILD_R=505
+add_cppflags -DMKSH_BUILD_R=563
$e $bi$me: Finished configuration testing, now producing output.$ao
@@ -2333,8 +2435,13 @@
objs=
sp=
case $tcfn in
-a.exe) mkshexe=$tfn.exe ;;
-*) mkshexe=$tfn ;;
+a.exe|conftest.exe)
+ mkshexe=$tfn.exe
+ add_cppflags -DMKSH_EXE_EXT
+ ;;
+*)
+ mkshexe=$tfn
+ ;;
esac
case $curdir in
*\ *) mkshshebang="#!./$mkshexe" ;;
@@ -2347,8 +2454,8 @@
set -A check_categories -- $check_categories
pflag='$curdir/$mkshexe'
sflag='$srcdir/check.t'
- usee=0 Pflag=0 Sflag=0 uset=0 vflag=1 xflag=0
- while getopts "C:e:fPp:QSs:t:v" ch; do case \$ch {
+ usee=0 useU=0 Pflag=0 Sflag=0 uset=0 vflag=1 xflag=0
+ while getopts "C:e:fPp:QSs:t:U:v" ch; do case \$ch {
(C) check_categories[\${#check_categories[*]}]=\$OPTARG ;;
(e) usee=1; eflag=\$OPTARG ;;
(f) check_categories[\${#check_categories[*]}]=fastbox ;;
@@ -2361,6 +2468,7 @@
(+S) Sflag=0 ;;
(s) sflag=\$OPTARG ;;
(t) uset=1; tflag=\$OPTARG ;;
+ (U) useU=1; Uflag=\$OPTARG ;;
(v) vflag=1 ;;
(+v) vflag=0 ;;
(*) xflag=1 ;;
@@ -2368,6 +2476,9 @@
done
shift \$((OPTIND - 1))
set -A args -- '$srcdir/check.pl' -p "\$pflag"
+ if $ebcdic; then
+ args[\${#args[*]}]=-E
+ fi
x=
for y in "\${check_categories[@]}"; do
x=\$x,\$y
@@ -2385,6 +2496,10 @@
args[\${#args[*]}]=-t
args[\${#args[*]}]=\$tflag
fi
+ if (( useU )); then
+ args[\${#args[*]}]=-U
+ args[\${#args[*]}]=\$Uflag
+ fi
(( vflag )) && args[\${#args[*]}]=-v
(( xflag )) && args[\${#args[*]}]=-x # force usage by synerr
if [[ -n \$TMPDIR && -d \$TMPDIR/. ]]; then
@@ -2392,7 +2507,7 @@
args[\${#args[*]}]=\$TMPDIR
fi
print Testing mksh for conformance:
- fgrep -e MirOS: -e MIRBSD "\$sflag"
+ grep -F -e Mir''OS: -e MIRBSD "\$sflag"
print "This shell is actually:\\n\\t\$KSH_VERSION"
print 'test.sh built for mksh $dstversion'
cstr='\$os = defined \$^O ? \$^O : "unknown";'
@@ -2476,7 +2591,7 @@
echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh
if test $cm = makefile; then
- extras='emacsfn.h rlimits.opt sh.h sh_flags.opt var_spec.h'
+ extras='emacsfn.h exprtok.h rlimits.opt sh.h sh_flags.opt var_spec.h'
test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
gens= genq=
for file in $optfiles; do
@@ -2566,8 +2681,8 @@
tcfn=$mkshexe
test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
test -f $tcfn || exit 1
-test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >$tfn.cat1" || \
- rmf $tfn.cat1
+test 1 = $r || v "$NROFF -mdoc <'$srcdir/lksh.1' >lksh.cat1" || rmf lksh.cat1
+test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >mksh.cat1" || rmf mksh.cat1
test 0 = $eq && v $SIZE $tcfn
i=install
test -f /usr/ucb/$i && i=/usr/ucb/$i
@@ -2581,12 +2696,14 @@
fi
$e
$e Installing the manual:
-if test -f $tfn.cat1; then
- $e "# $i -c -o root -g bin -m 444 $tfn.cat1" \
- "/usr/share/man/cat1/$tfn.0"
+if test -f mksh.cat1; then
+ $e "# $i -c -o root -g bin -m 444 lksh.cat1" \
+ "/usr/share/man/cat1/lksh.0"
+ $e "# $i -c -o root -g bin -m 444 mksh.cat1" \
+ "/usr/share/man/cat1/mksh.0"
$e or
fi
-$e "# $i -c -o root -g bin -m 444 $tfn.1 /usr/share/man/man1/$tfn.1"
+$e "# $i -c -o root -g bin -m 444 lksh.1 mksh.1 /usr/share/man/man1/"
$e
$e Run the regression test suite: ./test.sh
$e Please also read the sample file dot.mkshrc and the fine manual.
@@ -2626,9 +2743,7 @@
MKSH_ASSUME_UTF8 (0=disabled, 1=enabled; default: unset)
MKSH_BINSHPOSIX if */sh or */-sh, enable set -o posix
MKSH_BINSHREDUCED if */sh or */-sh, enable set -o sh
-MKSH_CLRTOEOL_STRING "\033[K"
-MKSH_CLS_STRING "\033[;H\033[J"
-MKSH_CONSERVATIVE_FDS fd 0-9 for scripts, shell only up to 31
+MKSH_CLS_STRING KSH_ESC_STRING "[;H" KSH_ESC_STRING "[J"
MKSH_DEFAULT_EXECSHELL "/bin/sh" (do not change)
MKSH_DEFAULT_PROFILEDIR "/etc" (do not change)
MKSH_DEFAULT_TMPDIR "/tmp" (do not change)
@@ -2636,12 +2751,12 @@
MKSH_DISABLE_EXPERIMENTAL disable code not yet comfy for (LTS) snapshots
MKSH_DISABLE_TTY_WARNING shut up warning about ctty if OS cant be fixed
MKSH_DONT_EMIT_IDSTRING omit RCS IDs from binary
+MKSH_EARLY_LOCALE_TRACKING track utf8-mode from POSIX locale, for SuSE
MKSH_MIDNIGHTBSD01ASH_COMPAT set -o sh: additional compatibility quirk
MKSH_NOPROSPECTOFWORK disable jobs, co-processes, etc. (do not use)
MKSH_NOPWNAM skip PAM calls, for -static on glibc or Solaris
MKSH_NO_CMDLINE_EDITING disable command line editing code entirely
MKSH_NO_DEPRECATED_WARNING omit warning when deprecated stuff is run
-MKSH_NO_EXTERNAL_CAT omit hack to skip cat builtin when flags passed
MKSH_NO_LIMITS omit ulimit code
MKSH_NO_SIGSETJMP define if sigsetjmp is broken or not available
MKSH_NO_SIGSUSPEND use sigprocmask+pause instead of sigsuspend
@@ -2668,4 +2783,7 @@
http://anonscm.debian.org/cgit/collab-maint/mksh.git/plain/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
+
EOD
Deleted: trunk/contrib/mksh/Makefile
===================================================================
--- trunk/contrib/mksh/Makefile 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/Makefile 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,77 +0,0 @@
-# $MirOS: src/bin/mksh/Makefile,v 1.74 2009/05/16 16:59:31 tg Stab $
-#-
-# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
-# Thorsten Glaser <tg at 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.
-#-
-# use CPPFLAGS=-DDEBUG __CRAZY=Yes to check for certain more stuff
-
-.include <bsd.own.mk>
-
-PROG= mksh
-SRCS= edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c \
- lalloc.c lex.c main.c misc.c shf.c syn.c tree.c var.c
-.if !make(test-build)
-CPPFLAGS+= -DMKSH_ASSUME_UTF8 \
- -DHAVE_ATTRIBUTE=1 -DHAVE_ATTRIBUTE_BOUNDED=1 \
- -DHAVE_ATTRIBUTE_USED=1 -DHAVE_SYS_PARAM_H=1 \
- -DHAVE_SYS_MKDEV_H=0 -DHAVE_SYS_MMAN_H=1 \
- -DHAVE_SYS_SYSMACROS_H=0 -DHAVE_LIBGEN_H=1 -DHAVE_LIBUTIL_H=0 \
- -DHAVE_PATHS_H=1 -DHAVE_STDBOOL_H=1 -DHAVE_STRINGS_H=1 \
- -DHAVE_GRP_H=1 -DHAVE_ULIMIT_H=0 -DHAVE_VALUES_H=0 \
- -DHAVE_STDINT_H=1 -DHAVE_RLIM_T=1 -DHAVE_SIG_T=1 \
- -DHAVE_SYS_SIGNAME=1 -DHAVE_SYS_SIGLIST=1 -DHAVE_STRSIGNAL=0 \
- -DHAVE_ARC4RANDOM=1 -DHAVE_ARC4RANDOM_PUSHB=1 \
- -DHAVE_GETRUSAGE=1 -DHAVE_MKNOD=1 -DHAVE_MKSTEMP=1 \
- -DHAVE_NICE=1 -DHAVE_REALPATH=1 -DHAVE_REVOKE=1 \
- -DHAVE_SETLOCALE_CTYPE=0 -DHAVE_LANGINFO_CODESET=0 \
- -DHAVE_SETMODE=1 -DHAVE_SETRESUGID=1 -DHAVE_SETGROUPS=1 \
- -DHAVE_STRCASESTR=1 -DHAVE_STRLCPY=1 -DHAVE_ARC4RANDOM_DECL=1 \
- -DHAVE_ARC4RANDOM_PUSHB_DECL=1 -DHAVE_FLOCK_DECL=1 \
- -DHAVE_REVOKE_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \
- -DHAVE_PERSISTENT_HISTORY=1
-COPTS+= -std=gnu99 -Wall
-.endif
-
-LINKS+= ${BINDIR}/${PROG} ${BINDIR}/sh
-MLINKS+= ${PROG}.1 sh.1
-
-regress: ${PROG} check.pl check.t
- -rm -rf regress-dir
- mkdir -p regress-dir
- echo export FNORD=666 >regress-dir/.mkshrc
- HOME=$$(realpath regress-dir) perl ${.CURDIR}/check.pl \
- -s ${.CURDIR}/check.t -v -p ./${PROG}
-
-test-build: .PHONY
- -rm -rf build-dir
- mkdir -p build-dir
- cd build-dir; env CC=${CC:Q} CFLAGS=${CFLAGS:M*:Q} \
- CPPFLAGS=${CPPFLAGS:M*:Q} LDFLAGS=${LDFLAGS:M*:Q} \
- LIBS= NOWARN=-Wno-error TARGET_OS= CPP= /bin/sh \
- ${.CURDIR}/Build.sh -Q -r && ./test.sh -v
-
-cleandir: clean-extra
-
-clean-extra: .PHONY
- -rm -rf build-dir regress-dir
-
-distribution:
- ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${CONFGRP} -m 0644 \
- ${.CURDIR}/dot.mkshrc ${DESTDIR}/etc/skel/.mkshrc
-
-.include <bsd.prog.mk>
Modified: trunk/contrib/mksh/check.pl
===================================================================
--- trunk/contrib/mksh/check.pl 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/check.pl 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-# $MirOS: src/bin/mksh/check.pl,v 1.37 2014/08/19 07:43:32 tg Exp $
+# $MirOS: src/bin/mksh/check.pl,v 1.49 2017/05/05 21:17:31 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,
-# 2012, 2013, 2014
-# Thorsten Glaser <tg at mirbsd.org>
+# 2012, 2013, 2014, 2015, 2017
+# mirabilos <m at mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
@@ -73,7 +73,7 @@
# the following minimal environment:
# HOME, LD_LIBRARY_PATH, LOCPATH,
# LOGNAME, PATH, SHELL, UNIXMODE,
-# USER
+# UNIXROOT, USER
# (values taken from the environment of
# the test harness).
# CYGWIN is set to nodosfilewarning.
@@ -80,6 +80,7 @@
# ENV is set to /nonexistant.
# __progname is set to the -p argument.
# __perlname is set to $^X (perlexe).
+# @utflocale@ is substituted from -U.
# file-setup mps Used to create files, directories
# and symlinks. First word is either
# file, dir or symlink; second word is
@@ -152,9 +153,15 @@
# p tag takes parameters (used with m).
# s tag can be used several times.
+# require Config only if it exists
# pull EINTR from POSIX.pm or Errno.pm if they exist
# otherwise just skip it
BEGIN {
+ eval {
+ require Config;
+ import Config;
+ 1;
+ };
$EINTR = 0;
eval {
require POSIX;
@@ -171,7 +178,6 @@
};
use Getopt::Std;
-use Config;
$os = defined $^O ? $^O : 'unknown';
@@ -179,7 +185,7 @@
$Usage = <<EOF ;
Usage: $prog [-Pv] [-C cat] [-e e=v] [-p prog] [-s fn] [-T dir] \
- [-t tmo] name ...
+ [-t tmo] [-U lcl] name ...
-C c Specify the comma separated list of categories the program
belongs to (see category field).
-e e=v Set the environment variable e to v for all tests
@@ -192,6 +198,7 @@
scaned for test files (which end in .t).
-T dir Use dir instead of /tmp to hold temporary files
-t t Use t as default time limit for tests (default is unlimited)
+ -U lcl Use lcl as UTF-8 locale (e.g. C.UTF-8) instead of the default
-v Verbose mode: print reason test failed.
name specifies the name of the test(s) to run; if none are
specified, all tests are run.
@@ -240,7 +247,7 @@
%known_tests = ();
-if (!getopts('C:e:Pp:s:T:t:v')) {
+if (!getopts('C:Ee:Pp:s:T:t:U:v')) {
print STDERR $Usage;
exit 1;
}
@@ -249,8 +256,10 @@
die "$prog: no test set specified (use -s)\n" if !defined $opt_s;
$test_prog = $opt_p;
$verbose = defined $opt_v && $opt_v;
+$is_ebcdic = defined $opt_E && $opt_E;
$test_set = $opt_s;
$temp_base = $opt_T || "/tmp";
+$utflocale = $opt_U || (($os eq "hpux") ? "en_US.utf8" : "en_US.UTF-8");
if (defined $opt_t) {
die "$prog: bad -t argument (should be number > 0): $opt_t\n"
if $opt_t !~ /^\d+$/ || $opt_t <= 0;
@@ -258,6 +267,14 @@
}
$program_kludge = defined $opt_P ? $opt_P : 0;
+if ($is_ebcdic) {
+ $categories{'shell:ebcdic-yes'} = 1;
+ $categories{'shell:ascii-no'} = 1;
+} else {
+ $categories{'shell:ebcdic-no'} = 1;
+ $categories{'shell:ascii-yes'} = 1;
+}
+
if (defined $opt_C) {
foreach $c (split(',', $opt_C)) {
$c =~ s/\s+//;
@@ -275,16 +292,29 @@
# Set up a very minimal environment
%new_env = ();
foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
- 'PATH', 'SHELL', 'UNIXMODE', 'USER')) {
+ 'PATH', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
$new_env{$env} = $ENV{$env} if defined $ENV{$env};
}
$new_env{'CYGWIN'} = 'nodosfilewarning';
$new_env{'ENV'} = '/nonexistant';
+
if (($os eq 'VMS') || ($Config{perlpath} =~ m/$Config{_exe}$/i)) {
$new_env{'__perlname'} = $Config{perlpath};
} else {
$new_env{'__perlname'} = $Config{perlpath} . $Config{_exe};
}
+$new_env{'__perlname'} = $^X if ($new_env{'__perlname'} eq '') and -f $^X and -x $^X;
+if ($new_env{'__perlname'} eq '') {
+ foreach $pathelt (split /:/,$ENV{'PATH'}) {
+ chomp($pathelt = `pwd`) if $pathelt eq '';
+ my $x = $pathelt . '/' . $^X;
+ next unless -f $x and -x $x;
+ $new_env{'__perlname'} = $x;
+ last;
+ }
+}
+$new_env{'__perlname'} = $^X if ($new_env{'__perlname'} eq '');
+
if (defined $opt_e) {
# XXX need a way to allow many -e arguments...
if ($opt_e =~ /^([a-zA-Z_]\w*)(|=(.*))$/) {
@@ -312,9 +342,10 @@
die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd);
if (!$program_kludge) {
- $test_prog = "$pwd/$test_prog" if substr($test_prog, 0, 1) ne '/';
+ $test_prog = "$pwd/$test_prog" if (substr($test_prog, 0, 1) ne '/') &&
+ ($os ne 'os2' || substr($test_prog, 1, 1) ne ':');
die "$prog: $test_prog is not executable - bye\n"
- if (! -x $test_prog && $os ne 'os2');
+ if (! -x $test_prog && $os ne 'os2');
}
@trap_sigs = ('TERM', 'QUIT', 'INT', 'PIPE', 'HUP');
@@ -568,7 +599,7 @@
}
push(@argv, $temps) if defined $test{'script'};
- #XXX realpathise, use which/whence -p, or sth. like that
+ #XXX realpathise, use command -v/whence -p/which, or sth. like that
#XXX if !$program_kludge, we get by with not doing it for now tho
$new_env{'__progname'} = $argv[0];
@@ -863,38 +894,50 @@
$char = 1;
}
}
- return "first difference: line $lineno, char $char (wanted '"
- . &format_char($ce) . "', got '"
- . &format_char($cg) . "'";
+ return "first difference: line $lineno, char $char (wanted " .
+ &format_char($ce) . ", got " . &format_char($cg);
}
sub
format_char
{
- local($ch, $s);
+ local($ch, $s, $q);
$ch = ord($_[0]);
+ $q = "'";
+
+ if ($is_ebcdic) {
+ if ($ch == 0x15) {
+ return $q . '\n' . $q;
+ } elsif ($ch == 0x16) {
+ return $q . '\b' . $q;
+ } elsif ($ch == 0x05) {
+ return $q . '\t' . $q;
+ } elsif ($ch < 64 || $ch == 255) {
+ return sprintf("X'%02X'", $ch);
+ }
+ return sprintf("'%c' (X'%02X')", $ch, $ch);
+ }
+
+ $s = sprintf("0x%02X (", $ch);
if ($ch == 10) {
- return '\n';
+ return $s . $q . '\n' . $q . ')';
} elsif ($ch == 13) {
- return '\r';
+ return $s . $q . '\r' . $q . ')';
} elsif ($ch == 8) {
- return '\b';
+ return $s . $q . '\b' . $q . ')';
} elsif ($ch == 9) {
- return '\t';
+ return $s . $q . '\t' . $q . ')';
} elsif ($ch > 127) {
- $ch -= 127;
- $s = "M-";
- } else {
- $s = '';
+ $ch -= 128;
+ $s .= "M-";
}
if ($ch < 32) {
- $s .= '^';
- $ch += ord('@');
+ return sprintf("%s^%c)", $s, $ch + ord('@'));
} elsif ($ch == 127) {
- return $s . "^?";
+ return $s . "^?)";
}
- return $s . sprintf("%c", $ch);
+ return sprintf("%s'%c')", $s, $ch);
}
sub
@@ -1156,6 +1199,8 @@
print STDERR "$prog:$test{':long-name'}: env-setup field doesn't start and end with the same character\n";
return undef;
}
+
+ $test{'env-setup'} =~ s/\@utflocale\@/$utflocale/g;
}
if (defined $test{'expected-exit'}) {
local($val) = $test{'expected-exit'};
@@ -1165,7 +1210,7 @@
print STDERR "$prog:$test{':long-name'}: expected-exit value $val not in 0..255\n";
return undef;
}
- } elsif ($val !~ /^([\s<>+-=*%\/&|!()]|\b[wse]\b|\bSIG[A-Z][A-Z0-9]*\b)+$/) {
+ } elsif ($val !~ /^([\s\d<>+=*%\/&|!()-]|\b[wse]\b|\bSIG[A-Z][A-Z0-9]*\b)+$/) {
print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $val\n";
return undef;
}
Modified: trunk/contrib/mksh/check.t
===================================================================
--- trunk/contrib/mksh/check.t 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/check.t 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-# $MirOS: src/bin/mksh/check.t,v 1.667.2.3 2015/03/01 15:42:51 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.801 2018/01/14 01:47:33 tg Exp $
# -*- mode: sh -*-
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015
-# Thorsten Glaser <tg at mirbsd.org>
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017
+# mirabilos <m at mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
@@ -27,26 +27,66 @@
# http://svnweb.freebsd.org/base/head/bin/test/tests/legacy_test.sh?view=co&content-type=text%2Fplain
#
# Integrated testsuites from:
-# (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
+# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout:
- @(#)MIRBSD KSH R50 2015/03/01
+ @(#)MIRBSD KSH R56 2018/01/14
description:
- Check version of shell.
+ Check base version of full shell
stdin:
- echo $KSH_VERSION
+ echo ${KSH_VERSION%%' +'*}
name: KSH_VERSION
-category: shell:legacy-no
+category: !shell:legacy-yes
---
expected-stdout:
- @(#)LEGACY KSH R50 2015/03/01
+ @(#)LEGACY KSH R56 2018/01/14
description:
- Check version of legacy shell.
+ Check base version of legacy shell
stdin:
- echo $KSH_VERSION
+ echo ${KSH_VERSION%%' +'*}
name: KSH_VERSION-legacy
-category: shell:legacy-yes
+category: !shell:legacy-no
---
+name: KSH_VERSION-ascii
+description:
+ Check that the shell version tag does not include EBCDIC
+category: !shell:ebcdic-yes
+stdin:
+ for x in $KSH_VERSION; do
+ [[ $x = '+EBCDIC' ]] && exit 1
+ done
+ exit 0
+---
+name: KSH_VERSION-ebcdic
+description:
+ Check that the shell version tag includes EBCDIC
+category: !shell:ebcdic-no
+stdin:
+ for x in $KSH_VERSION; do
+ [[ $x = '+EBCDIC' ]] && exit 0
+ done
+ exit 1
+---
+name: KSH_VERSION-binmode
+description:
+ Check that the shell version tag does not include TEXTMODE
+category: !shell:textmode-yes
+stdin:
+ for x in $KSH_VERSION; do
+ [[ $x = '+TEXTMODE' ]] && exit 1
+ done
+ exit 0
+---
+name: KSH_VERSION-textmode
+description:
+ Check that the shell version tag includes TEXTMODE
+category: !shell:textmode-no
+stdin:
+ for x in $KSH_VERSION; do
+ [[ $x = '+TEXTMODE' ]] && exit 0
+ done
+ exit 1
+---
name: selftest-1
description:
Regression test self-testing
@@ -92,23 +132,6 @@
stdin:
set
---
-name: selftest-legacy
-description:
- Check some things in the LEGACY KSH
-category: shell:legacy-yes
-stdin:
- set +o emacs
- set +o vi
- [[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 1=emacs
- [[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 1=vi
- set -o emacs
- set -o vi
- [[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 2=emacs
- [[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 2=vi
-expected-stdout:
- 2=emacs
- 2=vi
----
name: selftest-direct-builtin-call
description:
Check that direct builtin calls work
@@ -119,6 +142,26 @@
expected-stdout:
-c echo foo
---
+name: selftest-pathsep-unix
+description:
+ Check that $PATHSEP is set correctly.
+category: !os:os2
+stdin:
+ PATHSEP=.; export PATHSEP
+ "$__progname" -c 'print -r -- $PATHSEP'
+expected-stdout:
+ :
+---
+name: selftest-pathsep-dospath
+description:
+ Check that $PATHSEP is set correctly.
+category: os:os2
+stdin:
+ PATHSEP=.; export PATHSEP
+ "$__progname" -c 'print -r -- $PATHSEP'
+expected-stdout:
+ ;
+---
name: alias-1
description:
Check that recursion is detected/avoided in aliases.
@@ -199,7 +242,7 @@
stdin:
alias X='case '
alias Y=Z
- X Y in 'Y') echo is y ;; Z) echo is z ; esac
+ X Y in 'Y') echo is y ;; Z) echo is z ;; esac
expected-stdout:
is z
---
@@ -232,7 +275,7 @@
stdin:
print '#!'"$__progname"'\necho tf' >lq
chmod +x lq
- PATH=$PWD:$PATH
+ PATH=$PWD$PATHSEP$PATH
alias lq=lq
lq
echo = now
@@ -257,6 +300,55 @@
expected-stdout:
hello world
---
+name: alias-11
+description:
+ Check that special argument handling still applies with escaped aliases
+stdin:
+ alias local1='\typeset'
+ alias local2='\\builtin typeset'
+ function fooa {
+ local1 x=$1 y=z
+ print -r -- "$x,$y"
+ }
+ function foob {
+ local2 x=$1 y=z
+ print -r -- "$x,$y"
+ }
+ x=1 y=2; fooa 'bar - baz'
+ x=1 y=2; foob 'bar - baz'
+expected-stdout:
+ bar - baz,z
+ bar - baz,z
+---
+name: alias-12
+description:
+ Something weird from Martijn Dekker
+stdin:
+ alias echo=print
+ x() { echo a; (echo b); x=$(echo c); }
+ typeset -f x
+ alias OPEN='{' CLOSE='};'
+ { OPEN echo hi1; CLOSE }
+ var=`{ OPEN echo hi2; CLOSE }` && echo "$var"
+ var=$({ OPEN echo hi3; CLOSE }) && echo "$var"
+expected-stdout:
+ x() {
+ \print a
+ ( \print b )
+ x=$(\print c )
+ }
+ hi1
+ hi2
+ hi3
+---
+name: arith-compound
+description:
+ Check that arithmetic expressions are compound constructs
+stdin:
+ { ! (( 0$(cat >&2) )) <<<1; } <<<2
+expected-stderr:
+ 1
+---
name: arith-lazy-1
description:
Check that only one side of ternary operator is evaluated
@@ -319,6 +411,62 @@
2
0
---
+name: arith-lazy-5-arr-n
+description: Check lazy evaluation with side effects
+stdin:
+ a=0; echo "$((0&&b[a++],a))"
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-arr-p
+description: Check lazy evaluation with side effects
+stdin:
+ a=0; echo "$((0&&(b[a++]),a))"
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-str-n
+description: Check lazy evaluation with side effects
+stdin:
+ a=0 b=a++; ((0&&b)); echo $a
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-str-p
+description: Check lazy evaluation with side effects
+stdin:
+ a=0 b=a++; ((0&&(b))); echo $a
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-tern-l-n
+description: Check lazy evaluation with side effects
+stdin:
+ a=0; echo "$((0?b[a++]:999,a))"
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-tern-l-p
+description: Check lazy evaluation with side effects
+stdin:
+ a=0; echo "$((0?(b[a++]):999,a))"
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-tern-r-n
+description: Check lazy evaluation with side effects
+stdin:
+ a=0; echo "$((1?999:b[a++],a))"
+expected-stdout:
+ 0
+---
+name: arith-lazy-5-tern-r-p
+description: Check lazy evaluation with side effects
+stdin:
+ a=0; echo "$((1?999:(b[a++]),a))"
+expected-stdout:
+ 0
+---
name: arith-ternary-prec-1
description:
Check precedence of ternary operator vs assignment
@@ -338,6 +486,18 @@
expected-stdout:
20
---
+name: arith-prec-1
+description:
+ Prove arithmetic expressions with embedded parameter
+ substitutions cannot be parsed ahead of time
+stdin:
+ a='3 + 4'
+ print 1 $((2 * a)) .
+ print 2 $((2 * $a)) .
+expected-stdout:
+ 1 14 .
+ 2 10 .
+---
name: arith-div-assoc-1
description:
Check associativity of division operator
@@ -462,6 +622,9 @@
va[1975973142]=right
va[4123456789]=wrong
echo x7 ${va[#4123456789%2147483647]}
+ # make sure multiple calculations don't interfere with each other
+ let '# mca = -4 % -2' ' mcb = -4 % -2'
+ echo x8 $mca $mcb
expected-stdout:
x1 -1 4294967295
x2 -171510507 4123456789
@@ -470,6 +633,7 @@
x5 -171510507 4123456789
x6 1975973142 1975973142
x7 right
+ x8 -4 0
---
name: arith-limit32-1
description:
@@ -859,6 +1023,8 @@
echo end-$i
done
echo end-3
+ for i in a b c; do echo $i; eval break; echo bad-$i; done
+ echo end-4
expected-stdout:
a
end-1
@@ -871,6 +1037,8 @@
c:x
end-c
end-3
+ a
+ end-4
---
name: break-2
description:
@@ -940,6 +1108,8 @@
echo end-$i
done
echo end-3
+ for i in a b c; do echo $i; eval continue; echo bad-$i ; done
+ echo end-4
expected-stdout:
a
b
@@ -962,6 +1132,10 @@
c:z
end-c
end-3
+ a
+ b
+ c
+ end-4
---
name: continue-2
description:
@@ -1182,10 +1356,10 @@
description:
Check package for cd -Pe
need-pass: no
-# the mv command fails on Cygwin
+# 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,!nosymlink
+category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!os:os390,!nosymlink
file-setup: file 644 "x"
mkdir noread noread/target noread/target/subdir
ln -s noread link
@@ -1203,7 +1377,7 @@
cd -P$1 subdir
echo 2=$?,${PWD#$bwd/}
cd $bwd
- chmod 755 renamed
+ chmod 755 noread renamed 2>/dev/null
rm -rf noread link renamed
stdin:
export TSHELL="$__progname"
@@ -1286,6 +1460,7 @@
(echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>/dev/null || echo failed in 38
foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>/dev/null || echo failed in 39
foo="a b c"; (echo -n '40 '; ./pfs "${foo#a}"; echo .) 2>/dev/null || echo failed in 40
+ (foo() { return 100; }; foo; echo 41 ${#+${#:+${#?}}\ \}\}\}}) 2>/dev/null || echo failed in 41
expected-stdout:
1 }z
2 ''z}
@@ -1327,6 +1502,7 @@
38 xay / x'a'y .
39 x' / x' .
40 < b c> .
+ 41 3 }}}
---
name: expand-unglob-dblq
description:
@@ -1375,6 +1551,7 @@
(echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>/dev/null || \
echo "$1 QSTN brac -> error"
}
+ : '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
tl_norm 1 -
tl_norm 2 ''
tl_norm 3 x
@@ -1505,6 +1682,7 @@
(echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>/dev/null || \
echo "$1 QSTN brac -> error"
}
+ : '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
tl_norm 1 -
tl_norm 2 ''
tl_norm 3 x
@@ -1612,7 +1790,7 @@
---
name: expand-weird-1
description:
- Check corner case of trim expansion vs. $# vs. ${#var}
+ Check corner cases of trim expansion vs. $# vs. ${#var} vs. ${var?}
stdin:
set 1 2 3 4 5 6 7 8 9 10 11
echo ${#} # value of $#
@@ -1620,23 +1798,59 @@
echo ${##1} # $# trimmed 1
set 1 2 3 4 5 6 7 8 9 10 11 12
echo ${##1}
+ (exit 0)
+ echo $? = ${#?} .
+ (exit 111)
+ echo $? = ${#?} .
expected-stdout:
11
2
1
2
+ 0 = 1 .
+ 111 = 3 .
---
name: expand-weird-2
description:
- Check corner case of ${var?} vs. ${#var}
+ Check more substitution and extension corner cases
stdin:
- (exit 0)
- echo $? = ${#?} .
- (exit 111)
- echo $? = ${#?} .
+ :& set -C; pid=$$; sub=$!; flg=$-; set -- i; exec 3>x.tmp
+ #echo "D: !=$! #=$# \$=$$ -=$- ?=$?"
+ echo >&3 3 = s^${!-word} , ${#-word} , p^${$-word} , f^${--word} , ${?-word} .
+ echo >&3 4 = ${!+word} , ${#+word} , ${$+word} , ${-+word} , ${?+word} .
+ echo >&3 5 = s^${!=word} , ${#=word} , p^${$=word} , f^${-=word} , ${?=word} .
+ echo >&3 6 = s^${!?word} , ${#?word} , p^${$?word} , f^${-?word} , ${??word} .
+ echo >&3 7 = sl^${#!} , ${##} , pl^${#$} , fl^${#-} , ${#?} .
+ echo >&3 8 = sw^${%!} , ${%#} , pw^${%$} , fw^${%-} , ${%?} .
+ echo >&3 9 = ${!!} , s^${!#} , ${!$} , s^${!-} , s^${!?} .
+ echo >&3 10 = s^${!#pattern} , ${##pattern} , p^${$#pattern} , f^${-#pattern} , ${?#pattern} .
+ echo >&3 11 = s^${!%pattern} , ${#%pattern} , p^${$%pattern} , f^${-%pattern} , ${?%pattern} .
+ echo >&3 12 = $# : ${##} , ${##1} .
+ set --
+ echo >&3 14 = $# : ${##} , ${##1} .
+ set -- 1 2 3 4 5
+ echo >&3 16 = $# : ${##} , ${##1} .
+ set -- 1 2 3 4 5 6 7 8 9 a b c d e
+ echo >&3 18 = $# : ${##} , ${##1} .
+ exec 3>&-
+ <x.tmp sed \
+ -e "s/ pl^${#pid} / PID /g" -e "s/ sl^${#sub} / SUB /g" -e "s/ fl^${#flg} / FLG /g" \
+ -e "s/ pw^${%pid} / PID /g" -e "s/ sw^${%sub} / SUB /g" -e "s/ fw^${%flg} / FLG /g" \
+ -e "s/ p^$pid / PID /g" -e "s/ s^$sub / SUB /g" -e "s/ f^$flg / FLG /g"
expected-stdout:
- 0 = 1 .
- 111 = 3 .
+ 3 = SUB , 1 , PID , FLG , 0 .
+ 4 = word , word , word , word , word .
+ 5 = SUB , 1 , PID , FLG , 0 .
+ 6 = SUB , 1 , PID , FLG , 0 .
+ 7 = SUB , 1 , PID , FLG , 1 .
+ 8 = SUB , 1 , PID , FLG , 1 .
+ 9 = ! , SUB , $ , SUB , SUB .
+ 10 = SUB , 1 , PID , FLG , 0 .
+ 11 = SUB , 1 , PID , FLG , 0 .
+ 12 = 1 : 1 , .
+ 14 = 0 : 1 , 0 .
+ 16 = 5 : 1 , 5 .
+ 18 = 14 : 2 , 4 .
---
name: expand-weird-3
description:
@@ -1651,6 +1865,61 @@
1=02.
2=02.
---
+name: expand-weird-4
+description:
+ Check that tilde expansion is enabled in ${x#~}
+ and cases that are modelled after it (${x/~/~})
+stdin:
+ HOME=/etc
+ a="~/x"
+ echo "<${a#~}> <${a#\~}> <${b:-~}> <${b:-\~}> <${c:=~}><$c> <${a/~}> <${a/x/~}> <${a/x/\~}>"
+expected-stdout:
+ <~/x> </x> <~> <\~> <~><~> <~/x> <~//etc> <~/~>
+---
+name: expand-bang-1
+description:
+ Check corner case of ${!?} with ! being var vs. op
+stdin:
+ echo ${!?}
+expected-exit: 1
+expected-stderr-pattern: /not set/
+---
+name: expand-bang-2
+description:
+ Check corner case of ${!var} vs. ${var op} with var=!
+stdin:
+ echo 1 $! .
+ echo 2 ${!#} .
+ echo 3 ${!#[0-9]} .
+ echo 4 ${!-foo} .
+ # get an at least three-digit bg pid
+ while :; do
+ :&
+ x=$!
+ if [[ $x != +([0-9]) ]]; then
+ echo >&2 "cannot test, pid '$x' not numeric"
+ echo >&2 report this with as many details as possible
+ exit 1
+ fi
+ [[ $x = [0-9][0-9][0-9]* ]] && break
+ done
+ y=${x#?}
+ t=$!; [[ $t = $x ]]; echo 5 $? .
+ t=${!#}; [[ $t = $x ]]; echo 6 $? .
+ t=${!#[0-9]}; [[ $t = $y ]]; echo 7 $? .
+ t=${!-foo}; [[ $t = $x ]]; echo 8 $? .
+ t=${!?bar}; [[ $t = $x ]]; echo 9 $? .
+expected-stdout:
+ 1 .
+ 2 .
+ 3 .
+ 4 foo .
+ 5 0 .
+ 6 0 .
+ 7 0 .
+ 8 0 .
+ 9 0 .
+---
name: expand-number-1
description:
Check that positional arguments do not overflow
@@ -1659,18 +1928,50 @@
expected-stdout:
1 .
---
+name: expand-slashes-1
+description:
+ Check that side effects in substring replacement are handled correctly
+stdin:
+ foo=n1n1n1n2n3
+ i=2
+ n=1
+ echo 1 ${foo//n$((n++))/[$((++i))]} .
+ echo 2 $n , $i .
+expected-stdout:
+ 1 [3][3][3]n2n3 .
+ 2 2 , 3 .
+---
+name: expand-slashes-2
+description:
+ Check that side effects in substring replacement are handled correctly
+stdin:
+ foo=n1n1n1n2n3
+ i=2
+ n=1
+ echo 1 ${foo@/n$((n++))/[$((++i))]} .
+ echo 2 $n , $i .
+expected-stdout:
+ 1 [3]n1n1[4][5] .
+ 2 5 , 5 .
+---
+name: expand-slashes-3
+description:
+ Check that we can access the replaced string
+stdin:
+ foo=n1n1n1n2n3
+ echo 1 ${foo@/n[12]/[$KSH_MATCH]} .
+expected-stdout:
+ 1 [n1][n1][n1][n2]n3 .
+---
name: eglob-bad-1
description:
Check that globbing isn't done when glob has syntax error
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcz"
-file-setup: file 644 "bbc"
+category: !os:cygwin,!os:msys,!os:os2
+file-setup: file 644 "@(a[b|)c]foo"
stdin:
- echo !([*)*
- echo +(a|b[)*
+ echo @(a[b|)c]*
expected-stdout:
- !([*)*
- +(a|b[)*
+ @(a[b|)c]*
---
name: eglob-bad-2
description:
@@ -1757,9 +2058,11 @@
case foo in *(a|b[)) echo yes;; *) echo no;; esac
case foo in *(a|b[)|f*) echo yes;; *) echo no;; esac
case '*(a|b[)' in *(a|b[)) echo yes;; *) echo no;; esac
+ case 'aab[b[ab[a' in *(a|b[)) echo yes;; *) echo no;; esac
expected-stdout:
no
yes
+ no
yes
---
name: eglob-trim-1
@@ -1822,7 +2125,7 @@
[[ -n $BASH_VERSION ]] && shopt -s extglob
x=1222321_ab/cde_b/c_1221
y=xyz
- echo 1: ${x/2}
+ echo 1: ${x/2} . ${x/}
echo 2: ${x//2}
echo 3: ${x/+(2)}
echo 4: ${x//+(2)}
@@ -1844,15 +2147,17 @@
echo 20: ${x/\//.}
echo 21: ${x//\//.}
echo 22: ${x///.}
- echo 23: ${x//#1/9}
- echo 24: ${x//%1/9}
- echo 25: ${x//\%1/9}
- echo 26: ${x//\\%1/9}
- echo 27: ${x//\a/9}
- echo 28: ${x//\\a/9}
- echo 29: ${x/2/$y}
+ echo 23: ${x/#1/9}
+ echo 24: ${x//#1/9}
+ echo 25: ${x/%1/9}
+ echo 26: ${x//%1/9}
+ echo 27: ${x//\%1/9}
+ echo 28: ${x//\\%1/9}
+ echo 29: ${x//\a/9}
+ echo 30: ${x//\\a/9}
+ echo 31: ${x/2/$y}
expected-stdout:
- 1: 122321_ab/cde_b/c_1221
+ 1: 122321_ab/cde_b/c_1221 . 1222321_ab/cde_b/c_1221
2: 131_ab/cde_b/c_11
3: 1321_ab/cde_b/c_1221
4: 131_ab/cde_b/c_11
@@ -1875,12 +2180,14 @@
21: 1222321_ab.cde_b.c_1221
22: 1222321_ab/cde_b/c_1221
23: 9222321_ab/cde_b/c_1221
- 24: 1222321_ab/cde_b/c_1229
+ 24: 1222321_ab/cde_b/c_1221
25: 1222321_ab/cde_b/c_1229
26: 1222321_ab/cde_b/c_1221
- 27: 1222321_9b/cde_b/c_1221
- 28: 1222321_9b/cde_b/c_1221
- 29: 1xyz22321_ab/cde_b/c_1221
+ 27: 1222321_ab/cde_b/c_1221
+ 28: 1222321_ab/cde_b/c_1221
+ 29: 1222321_9b/cde_b/c_1221
+ 30: 1222321_ab/cde_b/c_1221
+ 31: 1xyz22321_ab/cde_b/c_1221
---
name: eglob-substrpl-2
description:
@@ -1905,6 +2212,7 @@
description:
Check substring replacement works with variables and slashes, too
stdin:
+ HOME=/etc
pfx=/home/user
wd=/home/user/tmp
echo "${wd/#$pfx/~}"
@@ -1914,9 +2222,9 @@
echo "${wd/#"\$pfx"/~}"
echo "${wd/#'\$pfx'/~}"
expected-stdout:
- ~/tmp
+ /etc/tmp
/home/user/tmp
- ~/tmp
+ /etc/tmp
/home/user/tmp
/home/user/tmp
/home/user/tmp
@@ -1925,6 +2233,7 @@
description:
More of this, bash fails it (bash4 passes)
stdin:
+ HOME=/etc
pfx=/home/user
wd=/home/user/tmp
echo "${wd/#$(echo /home/user)/~}"
@@ -1931,8 +2240,8 @@
echo "${wd/#"$(echo /home/user)"/~}"
echo "${wd/#'$(echo /home/user)'/~}"
expected-stdout:
- ~/tmp
- ~/tmp
+ /etc/tmp
+ /etc/tmp
/home/user/tmp
---
name: eglob-substrpl-3c
@@ -1939,6 +2248,7 @@
description:
Even more weird cases
stdin:
+ HOME=/etc
pfx=/home/user
wd='$pfx/tmp'
echo 1: ${wd/#$pfx/~}
@@ -1964,31 +2274,31 @@
echo 17: ${ts/+($tp)/$tr}
echo 18: ${ts/+($tp)/c/d}
echo 19: ${ts/+($tp)/c\/d}
- echo 25: ${ts//a\/b/$tr}
- echo 26: ${ts//a\/b/\$tr}
- echo 27: ${ts//$tp/$tr}
- echo 28: ${ts//$tp/c/d}
- echo 29: ${ts//$tp/c\/d}
- echo 30: ${ts//+(a\/b)/$tr}
- echo 31: ${ts//+(a\/b)/\$tr}
- echo 32: ${ts//+($tp)/$tr}
- echo 33: ${ts//+($tp)/c/d}
- echo 34: ${ts//+($tp)/c\/d}
+ echo 20: ${ts//a\/b/$tr}
+ echo 21: ${ts//a\/b/\$tr}
+ echo 22: ${ts//$tp/$tr}
+ echo 23: ${ts//$tp/c/d}
+ echo 24: ${ts//$tp/c\/d}
+ echo 25: ${ts//+(a\/b)/$tr}
+ echo 26: ${ts//+(a\/b)/\$tr}
+ echo 27: ${ts//+($tp)/$tr}
+ echo 28: ${ts//+($tp)/c/d}
+ echo 29: ${ts//+($tp)/c\/d}
tp="+($tp)"
- echo 40: ${ts/$tp/$tr}
- echo 41: ${ts//$tp/$tr}
+ echo 30: ${ts/$tp/$tr}
+ echo 31: ${ts//$tp/$tr}
expected-stdout:
1: $pfx/tmp
- 2: ~/tmp
+ 2: /etc/tmp
3: $pfx/tmp
- 4: ~/tmp
- 5: ~/tmp
- 6: ~/tmp
+ 4: /etc/tmp
+ 5: /etc/tmp
+ 6: $pfx/tmp
7: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
8: $tra/b$tp$tp_a/b$tp_*(a/b)_*($tp)
9: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
10: a/ba/bc/d$tp_a/b$tp_*(a/b)_*($tp)
- 11: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+ 11: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
12: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
13: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
14: c\/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
@@ -1997,25 +2307,26 @@
17: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
18: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
19: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
- 25: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 26: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp)
- 27: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 28: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 29: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 30: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 31: $tr$tp$tp_$tr$tp_*($tr)_*($tp)
- 32: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 33: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 34: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 40: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 41: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+ 20: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 21: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp)
+ 22: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 23: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 24: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 25: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 26: $tr$tp$tp_$tr$tp_*($tr)_*($tp)
+ 27: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 28: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 29: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+ 30: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+ 31: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
# This is what GNU bash does:
-# 40: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-# 41: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+# 30: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
+# 31: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
---
name: eglob-utf8-1
description:
UTF-8 mode differences for eglobbing
+category: !shell:ebcdic-yes
stdin:
s=blöd
set +U
@@ -2047,7 +2358,8 @@
---
name: glob-bad-1
description:
- Check that globbing isn't done when glob has syntax error
+ Check that [ matches itself if it's not a valid bracket expr
+ but does not prevent globbing, while backslash-escaping does
file-setup: dir 755 "[x"
file-setup: file 644 "[x/foo"
stdin:
@@ -2054,17 +2366,25 @@
echo [*
echo *[x
echo [x/*
+ :>'ab[x'
+ :>'a[a-z][x'
+ echo a[a-z][*
+ echo a[a-z]*
+ echo a[a\-z]*
expected-stdout:
- [*
- *[x
+ [x
+ [x
[x/foo
+ ab[x
+ ab[x
+ a[a-z]*
---
name: glob-bad-2
description:
Check that symbolic links aren't stat()'d
+# breaks on Dell UNIX 4.0 R2.2 (SVR4) where unlink also fails
# breaks on FreeMiNT (cannot unlink dangling symlinks)
-# breaks on MSYS (does not support symlinks)
-# breaks on Dell UNIX 4.0 R2.2 (SVR4) where unlink also fails
+# breaks on MSYS, OS/2 (do not support symlinks)
category: !os:mint,!os:msys,!os:svr4.0,!nosymlink
file-setup: dir 755 "dir"
file-setup: symlink 644 "dir/abc"
@@ -2076,6 +2396,18 @@
dir/abc
dir/abc
---
+name: glob-bad-3
+description:
+ Check that the slash is parsed before the glob
+stdin:
+ mkdir a 'a[b'
+ (cd 'a[b'; echo ok >'c]d')
+ echo nok >abd
+ echo fail >a/d
+ cat a[b/c]d
+expected-stdout:
+ ok
+---
name: glob-range-1
description:
Test range matching
@@ -2084,6 +2416,12 @@
file-setup: file 644 "bbc"
file-setup: file 644 "cbc"
file-setup: file 644 "-bc"
+file-setup: file 644 "!bc"
+file-setup: file 644 "^bc"
+file-setup: file 644 "+bc"
+file-setup: file 644 ",bc"
+file-setup: file 644 "0bc"
+file-setup: file 644 "1bc"
stdin:
echo [ab-]*
echo [-ab]*
@@ -2090,18 +2428,19 @@
echo [!-ab]*
echo [!ab]*
echo []ab]*
- :>'./!bc'
- :>'./^bc'
echo [^ab]*
- echo [!ab]*
+ echo [+--]*
+ echo [--1]*
+
expected-stdout:
-bc abc bbc
-bc abc bbc
- cbc
- -bc cbc
+ !bc +bc ,bc 0bc 1bc ^bc cbc
+ !bc +bc ,bc -bc 0bc 1bc ^bc cbc
abc bbc
^bc abc bbc
- !bc -bc ^bc cbc
+ +bc ,bc -bc
+ -bc 0bc 1bc
---
name: glob-range-2
description:
@@ -2119,7 +2458,7 @@
# breaks on Mac OSX (HFS+ non-standard Unicode 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
+category: !os:cygwin,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
need-pass: no
file-setup: file 644 "a\xC2c"
stdin:
@@ -2146,11 +2485,33 @@
file-setup: file 644 "dbc"
file-setup: file 644 "ebc"
file-setup: file 644 "-bc"
+file-setup: file 644 "@bc"
stdin:
echo [a-c-e]*
+ echo [a--@]*
expected-stdout:
-bc abc bbc cbc ebc
+ @bc
---
+name: glob-word-1
+description:
+ Check BSD word boundary matches
+stdin:
+ t() { [[ $1 = *[[:\<:]]bar[[:\>:]]* ]]; echo =$?; }
+ t 'foo bar baz'
+ t 'foobar baz'
+ t 'foo barbaz'
+ t 'bar'
+ t '_bar'
+ t 'bar_'
+expected-stdout:
+ =0
+ =1
+ =1
+ =0
+ =1
+ =1
+---
name: glob-trim-1
description:
Check against a regression from fixing IFS-subst-2
@@ -2206,7 +2567,7 @@
hi
there
---
-name: heredoc-4
+name: heredoc-4a
description:
Check that an error occurs if the heredoc-delimiter is missing.
stdin: !
@@ -2216,6 +2577,34 @@
expected-exit: e > 0
expected-stderr-pattern: /.*/
---
+name: heredoc-4an
+description:
+ Check that an error occurs if the heredoc-delimiter is missing.
+arguments: !-n!
+stdin: !
+ cat << EOF
+ hi
+ there
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
+name: heredoc-4b
+description:
+ Check that an error occurs if the heredoc is missing.
+stdin: !
+ cat << EOF
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
+name: heredoc-4bn
+description:
+ Check that an error occurs if the heredoc is missing.
+arguments: !-n!
+stdin: !
+ cat << EOF
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
name: heredoc-5
description:
Check that backslash quotes a $, ` and \ and kills a \newline
@@ -2312,6 +2701,8 @@
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$bar
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<-foo
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")"
+ tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"A $(echo "foo bar") B"
+ tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$b\$b$bar
expected-stdout:
sbb
sbb
@@ -2321,6 +2712,9 @@
$one
-sbb
sbb one
+ A sbb one B
+ $o$oone
+ onm
---
name: heredoc-9b
description:
@@ -2373,6 +2767,7 @@
name: heredoc-10
description:
Check direct here document assignment
+category: !shell:ebcdic-yes
stdin:
x=u
va=<<EOF
@@ -2410,7 +2805,7 @@
print -r -- "| ${v//$'\n'/^} |"
expected-stdout:
function foo {
- vc=<<-EOF
+ vc=<<-EOF
=c $x \x40=
EOF
@@ -2425,6 +2820,62 @@
} |
| vapp1^vapp2^ |
---
+name: heredoc-10-ebcdic
+description:
+ Check direct here document assignment
+category: !shell:ebcdic-no
+stdin:
+ x=u
+ va=<<EOF
+ =a $x \x7C=
+ EOF
+ vb=<<'EOF'
+ =b $x \x7C=
+ EOF
+ function foo {
+ vc=<<-EOF
+ =c $x \x7C=
+ EOF
+ }
+ fnd=$(typeset -f foo)
+ print -r -- "$fnd"
+ function foo {
+ echo blub
+ }
+ foo
+ eval "$fnd"
+ foo
+ # rather nonsensical, but…
+ vd=<<<"=d $x \x7C="
+ ve=<<<'=e $x \x7C='
+ vf=<<<$'=f $x \x7C='
+ # now check
+ print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |"
+ # check append
+ v=<<-EOF
+ vapp1
+ EOF
+ v+=<<-EOF
+ vapp2
+ EOF
+ print -r -- "| ${v//$'\n'/^} |"
+expected-stdout:
+ function foo {
+ vc=<<-EOF
+ =c $x \x7C=
+ EOF
+
+ }
+ blub
+ | va={=a u \x7C=
+ } vb={=b $x \x7C=
+ } vc={=c u \x7C=
+ } vd={=d u \x7C=
+ } ve={=e $x \x7C=
+ } vf={=f $x @=
+ } |
+ | vapp1^vapp2^ |
+---
name: heredoc-11
description:
Check here documents with no or empty delimiter
@@ -2453,6 +2904,10 @@
eval "$fnd"
foo
print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
+ x=y
+ foo
+ typeset -f foo
+ print -r -- "| vc={$vc} vd={$vd} |"
# check append
v=<<-
vapp1
@@ -2463,11 +2918,11 @@
print -r -- "| ${v//$'\n'/^} |"
expected-stdout:
function foo {
- vc=<<-
+ vc=<<-
=c $x \x40=
<<
- vd=<<-""
+ vd=<<-""
=d $x \x40=
@@ -2478,6 +2933,19 @@
} vc={=c u \x40=
} vd={=d $x \x40=
} |
+ function foo {
+ vc=<<-
+ =c $x \x40=
+ <<
+
+ vd=<<-""
+ =d $x \x40=
+
+
+ }
+ | vc={=c y \x40=
+ } vd={=d $x \x40=
+ } |
| vapp1^vapp2^ |
---
name: heredoc-12
@@ -2525,6 +2993,46 @@
3 baz a b baz
3 bla "a b" bla
---
+name: heredoc-14
+description:
+ Check that using multiple here documents works
+stdin:
+ foo() {
+ echo "got $(cat) on stdin"
+ echo "got $(cat <&4) on fd#4"
+ echo "got $(cat <&5) on fd#5"
+ }
+ bar() {
+ foo 4<<-a <<-b 5<<-c
+ four
+ a
+ zero
+ b
+ five
+ c
+ }
+ x=$(typeset -f bar)
+ eval "$x"
+ y=$(typeset -f bar)
+ [[ $x = "$y" ]]; echo $?
+ typeset -f bar
+ bar
+expected-stdout:
+ 0
+ bar() {
+ \foo 4<<-a <<-b 5<<-c
+ four
+ a
+ zero
+ b
+ five
+ c
+
+ }
+ got zero on stdin
+ got four on fd#4
+ got five on fd#5
+---
name: heredoc-comsub-1
description:
Tests for here documents in COMSUB, taken from Austin ML
@@ -2569,6 +3077,54 @@
expected-stdout:
= these parens \( ) are a problem =
---
+name: heredoc-comsub-5
+description:
+ Check heredoc and COMSUB mixture in input
+stdin:
+ prefix() { sed -e "s/^/$1:/"; }
+ XXX() { echo x-en; }
+ YYY() { echo y-es; }
+
+ prefix A <<XXX && echo "$(prefix B <<XXX
+ echo line 1
+ XXX
+ echo line 2)" && prefix C <<YYY
+ echo line 3
+ XXX
+ echo line 4)"
+ echo line 5
+ YYY
+ XXX
+expected-stdout:
+ A:echo line 3
+ B:echo line 1
+ line 2
+ C:echo line 4)"
+ C:echo line 5
+ x-en
+---
+name: heredoc-comsub-6
+description:
+ Check here documents and here strings can be used
+ without a specific command, like $(<…) (extension)
+stdin:
+ foo=bar
+ x=$(<<<EO${foo}F)
+ echo "3<$x>"
+ y=$(<<-EOF
+ hi!
+
+ $foo) is not a problem
+
+
+ EOF)
+ echo "7<$y>"
+expected-stdout:
+ 3<EObarF>
+ 7<hi!
+
+ bar) is not a problem>
+---
name: heredoc-subshell-1
description:
Tests for here documents in subshells, taken from Austin ML
@@ -2772,7 +3328,7 @@
echo B
) &
' &
- sleep 2
+ sleep 5
echo Left overs: *
expected-stdout:
A
@@ -2836,7 +3392,7 @@
(sleep 1; foo) &
foo
' &
- sleep 2
+ sleep 5
echo Left overs: *
expected-stdout:
hi
@@ -3031,6 +3587,37 @@
expected-stderr-pattern:
/(.*can't unlink HISTFILE.*\n)?X*$/
---
+name: history-multiline
+description:
+ Check correct multiline history, Debian #783978
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!
+file-setup: file 644 "Env"
+ PS1=X
+ PS2=Y
+stdin:
+ for i in A B C
+ do
+ print $i
+ print $i
+ done
+ fc -l
+expected-stdout:
+ A
+ A
+ B
+ B
+ C
+ C
+ 1 for i in A B C
+ do
+ print $i
+ print $i
+ done
+expected-stderr-pattern:
+ /^XYYYYXX$/
+---
name: history-e-minus-1
description:
Check if more recent command is executed
@@ -3542,7 +4129,6 @@
description:
Newly created multi line commands show up as single command
in history.
- (NOTE: adjusted for COMPLEX HISTORY compile time option)
(ksh88 fails 'cause it lists the fc command)
category: stdout-ed
need-ctty: yes
@@ -3569,7 +4155,7 @@
a new line
1 echo abc def
2 echo FOOBAR def
- 3 echo a new line
+ echo a new line
expected-stderr-pattern:
/^X*echo FOOBAR def\necho a new line\nX*$/
---
@@ -3651,7 +4237,7 @@
a new line
1 echo abc def
2 echo FOOBAR def
- 3 echo a new line
+ echo a new line
expected-stderr-pattern:
/^X*13\n32\necho FOOBAR def\necho a new line\nX*$/
---
@@ -4313,6 +4899,23 @@
8 ok
<9> <ab> <a b> .
---
+name: IFS-subst-10
+description:
+ Scalar context in ${var=$subst}
+stdin:
+ showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
+ set -- one "two three" four
+ unset -v var
+ save_IFS=$IFS
+ IFS=
+ set -- ${var=$*}
+ IFS=$save_IFS
+ echo "var=$var"
+ showargs "$@"
+expected-stdout:
+ var=onetwo threefour
+ <onetwo threefour> .
+---
name: IFS-arith-1
description:
http://austingroupbugs.net/view.php?id=832
@@ -4532,11 +5135,25 @@
64
64
---
+name: integer-base-8
+description:
+ Check that base-36 works (full span)
+stdin:
+ echo 1:$((36#109AZ)).
+ typeset -i36 x=1691675
+ echo 2:$x.
+ typeset -Uui36 x
+ echo 3:$x.
+expected-stdout:
+ 1:1691675.
+ 2:36#109az.
+ 3:36#109AZ.
+---
name: integer-base-check-flat
description:
Check behaviour does not match POSuX (except if set -o posix),
because a not type-safe scripting language has *no* business
- interpreting the string "010" as octal numer eight (dangerous).
+ interpreting the string "010" as octal number eight (dangerous).
stdin:
echo 1 "$("$__progname" -c 'echo :$((10))/$((010)),$((0x10)):')" .
echo 2 "$("$__progname" -o posix -c 'echo :$((10))/$((010)),$((0x10)):')" .
@@ -4546,18 +5163,34 @@
2 :10/8,16: .
3 :10/10,16: .
---
-name: integer-base-check-numeric-from
+name: integer-base-check-numeric-from-1
description:
- Check behaviour for base one to 36, and that 37 errors out
+ Check behaviour for base one
+category: !shell:ebcdic-yes
stdin:
echo 1:$((1#1))0.
+expected-stdout:
+ 1:490.
+---
+name: integer-base-check-numeric-from-1-ebcdic
+description:
+ Check behaviour for base one
+category: !shell:ebcdic-no
+stdin:
+ echo 1:$((1#1))0.
+expected-stdout:
+ 1:2410.
+---
+name: integer-base-check-numeric-from-2
+description:
+ Check behaviour for base two to 36, and that 37 degrades to 10
+stdin:
i=1
- while (( ++i <= 36 )); do
+ while (( ++i <= 37 )); do
eval 'echo '$i':$(('$i'#10)).'
done
echo 37:$($__progname -c 'echo $((37#10))').$?:
expected-stdout:
- 1:490.
2:2.
3:3.
4:4.
@@ -4593,15 +5226,38 @@
34:34.
35:35.
36:36.
- 37:.0:
-expected-stderr-pattern:
- /.*bad number '37#10'/
+ 37:10.
+ 37:10.0:
---
-name: integer-base-check-numeric-to
+name: integer-base-check-numeric-to-1
description:
- Check behaviour for base one to 36, and that 37 errors out
+ Check behaviour for base one
+category: !shell:ebcdic-yes
stdin:
- i=0
+ i=1
+ typeset -Uui$i x=0x40
+ eval "typeset -i10 y=$x"
+ print $i:$x.$y.
+expected-stdout:
+ 1:1#@.64.
+---
+name: integer-base-check-numeric-to-1-ebcdic
+description:
+ Check behaviour for base one
+category: !shell:ebcdic-no
+stdin:
+ i=1
+ typeset -Uui$i x=0x7C
+ eval "typeset -i10 y=$x"
+ print $i:$x.$y.
+expected-stdout:
+ 1:1#@.124.
+---
+name: integer-base-check-numeric-to-2
+description:
+ Check behaviour for base two to 36, and that 37 degrades to 10
+stdin:
+ i=1
while (( ++i <= 37 )); do
typeset -Uui$i x=0x40
eval "typeset -i10 y=$x"
@@ -4608,7 +5264,6 @@
print $i:$x.$y.
done
expected-stdout:
- 1:1#@.64.
2:2#1000000.64.
3:3#2101.64.
4:4#1000.64.
@@ -4644,9 +5299,7 @@
34:34#1U.64.
35:35#1T.64.
36:36#1S.64.
- 37:36#1S.64.
-expected-stderr-pattern:
- /.*bad integer base: 37/
+ 37:64.64.
---
name: integer-arithmetic-span
description:
@@ -4796,6 +5449,24 @@
line <6>
expected-exit: 1
---
+name: lineno-eval-alias
+description:
+ Check if LINENO is trapped in eval and aliases
+stdin:
+ ${ZSH_VERSION+false} || emulate sh; echo $LINENO
+ echo $LINENO
+ eval ' echo $LINENO
+ echo $LINENO
+ echo $LINENO'
+ echo $LINENO
+expected-stdout:
+ 1
+ 2
+ 3
+ 3
+ 3
+ 6
+---
name: unknown-trap
description:
Ensure unknown traps are not a syntax error
@@ -4813,7 +5484,7 @@
PROG: trap: bad signal 'UNKNOWNSIGNAL'
PROG: trap: bad signal '999999'
PROG: trap: bad signal 'FNORD'
- = 3
+ = 1
trap 2 executed
---
name: read-IFS-1
@@ -4832,6 +5503,66 @@
1a:
2: x[A B]
---
+name: read-IFS-2
+description:
+ Complex tests, IFS either colon (IFS-NWS) or backslash (tricky)
+stdin:
+ n=0
+ showargs() { print -nr "$1"; shift; for s_arg in "$@"; do print -nr -- " [$s_arg]"; done; print; }
+ (IFS=\\ a=\<\\\>; showargs 3 $a)
+ (IFS=: b=\<:\>; showargs 4 $b)
+ print -r '<\>' | (IFS=\\ read f g; showargs 5 "$f" "$g")
+ print -r '<\\>' | (IFS=\\ read f g; showargs 6 "$f" "$g")
+ print '<\\\n>' | (IFS=\\ read f g; showargs 7 "$f" "$g")
+ print -r '<\>' | (IFS=\\ read f; showargs 8 "$f")
+ print -r '<\\>' | (IFS=\\ read f; showargs 9 "$f")
+ print '<\\\n>' | (IFS=\\ read f; showargs 10 "$f")
+ print -r '<\>' | (IFS=\\ read -r f g; showargs 11 "$f" "$g")
+ print -r '<\\>' | (IFS=\\ read -r f g; showargs 12 "$f" "$g")
+ print '<\\\n>' | (IFS=\\ read -r f g; showargs 13 "$f" "$g")
+ print -r '<\>' | (IFS=\\ read -r f; showargs 14 "$f")
+ print -r '<\\>' | (IFS=\\ read -r f; showargs 15 "$f")
+ print '<\\\n>' | (IFS=\\ read -r f; showargs 16 "$f")
+ print -r '<:>' | (IFS=: read f g; showargs 17 "$f" "$g")
+ print -r '<::>' | (IFS=: read f g; showargs 18 "$f" "$g")
+ print '<:\n>' | (IFS=: read f g; showargs 19 "$f" "$g")
+ print -r '<:>' | (IFS=: read f; showargs 20 "$f")
+ print -r '<::>' | (IFS=: read f; showargs 21 "$f")
+ print '<:\n>' | (IFS=: read f; showargs 22 "$f")
+ print -r '<:>' | (IFS=: read -r f g; showargs 23 "$f" "$g")
+ print -r '<::>' | (IFS=: read -r f g; showargs 24 "$f" "$g")
+ print '<:\n>' | (IFS=: read -r f g; showargs 25 "$f" "$g")
+ print -r '<:>' | (IFS=: read -r f; showargs 26 "$f")
+ print -r '<::>' | (IFS=: read -r f; showargs 27 "$f")
+ print '<:\n>' | (IFS=: read -r f; showargs 28 "$f")
+expected-stdout:
+ 3 [<] [>]
+ 4 [<] [>]
+ 5 [<] [>]
+ 6 [<] [>]
+ 7 [<>] []
+ 8 [<>]
+ 9 [<\>]
+ 10 [<>]
+ 11 [<] [>]
+ 12 [<] [\>]
+ 13 [<] []
+ 14 [<\>]
+ 15 [<\\>]
+ 16 [<]
+ 17 [<] [>]
+ 18 [<] [:>]
+ 19 [<] []
+ 20 [<:>]
+ 21 [<::>]
+ 22 [<]
+ 23 [<] [>]
+ 24 [<] [:>]
+ 25 [<] []
+ 26 [<:>]
+ 27 [<::>]
+ 28 [<]
+---
name: read-ksh-1
description:
If no var specified, REPLY is used
@@ -4935,8 +5666,8 @@
need to be moved out of the switch to before findcom() is
called - I don't know what this will break.
stdin:
- : ${PWD:-`pwd 2> /dev/null`}
- : ${PWD:?"PWD not set - can't do test"}
+ : "${PWD:-`pwd 2> /dev/null`}"
+ : "${PWD:?"PWD not set - cannot do test"}"
mkdir Y
cat > Y/xxxscript << EOF
#!/bin/sh
@@ -5474,7 +6205,7 @@
stdin:
print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
- done >env; chmod +x env; PATH=.:$PATH
+ done >env; chmod +x env; PATH=.$PATHSEP$PATH
foo=bar
readonly foo
foo=stuff env | grep '^foo'
@@ -5857,7 +6588,7 @@
description:
Check if test -nt/-ot succeeds if second(first) file is missing.
stdin:
- touch a
+ :>a
test a -nt b && echo nt OK || echo nt BAD
test b -ot a && echo ot OK || echo ot BAD
expected-stdout:
@@ -5971,7 +6702,7 @@
description:
check for a regression with sleep builtin and signal mask
category: !nojsig
-time-limit: 3
+time-limit: 5
stdin:
sleep 1
echo blub |&
@@ -6004,6 +6735,106 @@
ac_space=' '
ac_newline=$'\n'
---
+name: regression-67
+description:
+ Check that we can both break and use source on the same line
+stdin:
+ for s in s; do break; done; print -s s
+---
+name: regression-68
+description:
+ Check that all common arithmetic operators work as expected
+stdin:
+ echo 1 $(( a = 5 )) .
+ echo 2 $(( ++a )) , $(( a++ )) , $(( a )) .
+ echo 3 $(( --a )) , $(( a-- )) , $(( a )) .
+ echo 4 $(( a == 5 )) , $(( a == 6 )) .
+ echo 5 $(( a != 5 )) , $(( a != 6 )) .
+ echo 6 $(( a *= 3 )) .
+ echo 7 $(( a /= 5 )) .
+ echo 8 $(( a %= 2 )) .
+ echo 9 $(( a += 9 )) .
+ echo 10 $(( a -= 4 )) .
+ echo 11 $(( a <<= 1 )) .
+ echo 12 $(( a >>= 1 )) .
+ echo 13 $(( a &= 4 )) .
+ echo 14 $(( a ^= a )) .
+ echo 15 $(( a |= 5 )) .
+ echo 16 $(( 5 << 1 )) .
+ echo 17 $(( 5 >> 1 )) .
+ echo 18 $(( 5 <= 6 )) , $(( 5 <= 5 )) , $(( 5 <= 4 )) .
+ echo 19 $(( 5 >= 6 )) , $(( 5 >= 5 )) , $(( 5 >= 4 )) .
+ echo 20 $(( 5 < 6 )) , $(( 5 < 5 )) , $(( 5 < 4 )) .
+ echo 21 $(( 5 > 6 )) , $(( 5 > 5 )) , $(( 5 > 4 )) .
+ echo 22 $(( 0 && 0 )) , $(( 0 && 1 )) , $(( 1 && 0 )) , $(( 1 && 1 )) .
+ echo 23 $(( 0 || 0 )) , $(( 0 || 1 )) , $(( 1 || 0 )) , $(( 1 || 1 )) .
+ echo 24 $(( 5 * 3 )) .
+ echo 25 $(( 7 / 2 )) .
+ echo 26 $(( 5 % 5 )) , $(( 5 % 4 )) , $(( 5 % 1 )) , $(( 5 % -1 )) , $(( 5 % -2 )) .
+ echo 27 $(( 5 + 2 )) , $(( 5 + 0 )) , $(( 5 + -2 )) .
+ echo 28 $(( 5 - 2 )) , $(( 5 - 0 )) , $(( 5 - -2 )) .
+ echo 29 $(( 6 & 4 )) , $(( 6 & 8 )) .
+ echo 30 $(( 4 ^ 2 )) , $(( 4 ^ 4 )) .
+ echo 31 $(( 4 | 2 )) , $(( 4 | 4 )) , $(( 4 | 0 )) .
+ echo 32 $(( 0 ? 1 : 2 )) , $(( 3 ? 4 : 5 )) .
+ echo 33 $(( 5 , 2 , 3 )) .
+ echo 34 $(( ~0 )) , $(( ~1 )) , $(( ~~1 )) , $(( ~~2 )) .
+ echo 35 $(( !0 )) , $(( !1 )) , $(( !!1 )) , $(( !!2 )) .
+ echo 36 $(( (5) )) .
+expected-stdout:
+ 1 5 .
+ 2 6 , 6 , 7 .
+ 3 6 , 6 , 5 .
+ 4 1 , 0 .
+ 5 0 , 1 .
+ 6 15 .
+ 7 3 .
+ 8 1 .
+ 9 10 .
+ 10 6 .
+ 11 12 .
+ 12 6 .
+ 13 4 .
+ 14 0 .
+ 15 5 .
+ 16 10 .
+ 17 2 .
+ 18 1 , 1 , 0 .
+ 19 0 , 1 , 1 .
+ 20 1 , 0 , 0 .
+ 21 0 , 0 , 1 .
+ 22 0 , 0 , 0 , 1 .
+ 23 0 , 1 , 1 , 1 .
+ 24 15 .
+ 25 3 .
+ 26 0 , 1 , 0 , 0 , 1 .
+ 27 7 , 5 , 3 .
+ 28 3 , 5 , 7 .
+ 29 4 , 0 .
+ 30 6 , 0 .
+ 31 6 , 4 , 4 .
+ 32 2 , 4 .
+ 33 3 .
+ 34 -1 , -2 , 1 , 2 .
+ 35 1 , 0 , 1 , 1 .
+ 36 5 .
+---
+name: regression-69
+description:
+ Check that all non-lksh arithmetic operators work as expected
+category: shell:legacy-no
+stdin:
+ a=5 b=0x80000005
+ echo 1 $(( a ^<= 1 )) , $(( b ^<= 1 )) .
+ echo 2 $(( a ^>= 2 )) , $(( b ^>= 2 )) .
+ echo 3 $(( 5 ^< 1 )) .
+ echo 4 $(( 5 ^> 1 )) .
+expected-stdout:
+ 1 10 , 11 .
+ 2 -2147483646 , -1073741822 .
+ 3 10 .
+ 4 -2147483646 .
+---
name: readonly-0
description:
Ensure readonly is honoured for assignments and unset
@@ -6075,6 +6906,13 @@
expected-stderr-pattern:
/read[ -]?only/
---
+name: readonly-5
+description:
+ Ensure readonly is idempotent
+stdin:
+ readonly x=1
+ readonly x
+---
name: syntax-1
description:
Check that lone ampersand is a syntax error
@@ -6180,7 +7018,7 @@
stdin:
print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
- done >env; chmod +x env; PATH=.:$PATH
+ done >env; chmod +x env; PATH=.$PATHSEP$PATH
FOO=bar exec env
expected-stdout-pattern:
/(^|.*\n)FOO=bar\n/
@@ -6192,7 +7030,7 @@
stdin:
print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
- done >env; chmod +x env; PATH=.:$PATH
+ done >env; chmod +x env; PATH=.$PATHSEP$PATH
env >bar1
FOO=bar exec; env >bar2
cmp -s bar1 bar2
@@ -6208,6 +7046,48 @@
y1-
x2-3- z1-
---
+name: exec-modern-korn-shell
+description:
+ Check that exec can execute any command that makes it
+ through syntax and parser
+stdin:
+ print '#!'"$__progname"'\necho tf' >lq
+ chmod +x lq
+ PATH=$PWD
+ exec 2>&1
+ foo() { print two; }
+ print =1
+ (exec print one)
+ print =2
+ (exec foo)
+ print =3
+ (exec ls)
+ print =4
+ (exec lq)
+expected-stdout-pattern:
+ /=1\none\n=2\ntwo\n=3\n.*: ls: not found\n=4\ntf\n/
+---
+name: exec-ksh88
+description:
+ Check that exec only executes after a PATH search
+arguments: !-o!posix!
+stdin:
+ print '#!'"$__progname"'\necho tf' >lq
+ chmod +x lq
+ PATH=$PWD
+ exec 2>&1
+ foo() { print two; }
+ print =1
+ (exec print one)
+ print =2
+ (exec foo)
+ print =3
+ (exec ls)
+ print =4
+ (exec lq)
+expected-stdout-pattern:
+ /=1\n.*: print: not found\n=2\n.*: foo: not found\n=3\n.*: ls: not found\n=4\ntf\n/
+---
name: xxx-what-do-you-call-this-1
stdin:
echo "${foo:-"a"}*"
@@ -6252,6 +7132,41 @@
/bad substitution/
expected-exit: 1
---
+name: xxx-variable-syntax-4
+description:
+ Not all kinds of trims are currently impossible, check those who do
+stdin:
+ foo() {
+ echo "<$*> X${*:+ }X"
+ }
+ foo a b
+ foo "" c
+ foo ""
+ foo "" ""
+ IFS=:
+ foo a b
+ foo "" c
+ foo ""
+ foo "" ""
+ IFS=
+ foo a b
+ foo "" c
+ foo ""
+ foo "" ""
+expected-stdout:
+ <a b> X X
+ < c> X X
+ <> XX
+ < > X X
+ <a:b> X X
+ <:c> X X
+ <> XX
+ <:> X X
+ <ab> X X
+ <c> X X
+ <> XX
+ <> XX
+---
name: xxx-substitution-eval-order
description:
Check order of evaluation of expressions
@@ -6377,19 +7292,45 @@
description:
Check suppresion of error message with null string. According to
POSIX, it shouldn't print the error as 'word' isn't ommitted.
- ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error,
- that's why the condition is reversed.
+ ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error.
stdin:
unset foo
x=
echo x${foo?$x}
expected-exit: 1
-# POSIX
-#expected-fail: yes
-#expected-stderr-pattern: !/not set/
-# common use
-expected-stderr-pattern: /parameter null or not set/
+expected-stderr-pattern: !/not set/
---
+name: xxx-param-subst-qmark-namespec
+description:
+ Check special names are output correctly
+stdin:
+ doit() {
+ "$__progname" -c "$@" >o1 2>o2
+ rv=$?
+ echo RETVAL: $rv
+ sed -e "s^${__progname%.exe}\.*e*x*e*: PROG: " -e 's/^/STDOUT: /g' <o1
+ sed -e "s^${__progname%.exe}\.*e*x*e*: PROG: " -e 's/^/STDERR: /g' <o2
+ }
+ doit 'echo ${1x}'
+ doit 'echo "${1x}"'
+ doit 'echo ${1?}'
+ doit 'echo ${19?}'
+ doit 'echo ${!:?}'
+ doit -u 'echo ${*:?}' foo ""
+expected-stdout:
+ RETVAL: 1
+ STDERR: PROG: ${1x}: bad substitution
+ RETVAL: 1
+ STDERR: PROG: ${1x}: bad substitution
+ RETVAL: 1
+ STDERR: PROG: 1: parameter null or not set
+ RETVAL: 1
+ STDERR: PROG: 19: parameter null or not set
+ RETVAL: 1
+ STDERR: PROG: !: parameter null or not set
+ RETVAL: 1
+ STDERR: foo: ${*:?}: bad substitution
+---
name: xxx-param-_-1
# fails due to weirdness of execv stuff
category: !os:uwin-nt
@@ -6404,12 +7345,24 @@
env-setup: !HOME=/sweet!
stdin:
echo ${A=a=}~ b=~ c=d~ ~
- set +o braceexpand
- unset A
+ export e=~ f=d~
+ command command export g=~ h=d~
+ echo ". $e . $f ."
+ echo ". $g . $h ."
+ set -o posix
+ unset A e f g h
echo ${A=a=}~ b=~ c=d~ ~
+ export e=~ f=d~
+ command command export g=~ h=d~
+ echo ". $e . $f ."
+ echo ". $g . $h ."
expected-stdout:
a=/sweet b=/sweet c=d~ /sweet
+ . /sweet . d~ .
+ . /sweet . d~ .
a=~ b=~ c=d~ /sweet
+ . /sweet . d~ .
+ . /sweet . d~ .
---
name: tilde-expand-2
description:
@@ -6416,6 +7369,11 @@
Check tilde expansion works
env-setup: !HOME=/sweet!
stdin:
+ :>'c=a'
+ typeset c=[ab]
+ :>'d=a'
+ x=typeset; $x d=[ab]
+ echo "<$c>" "<$d>"
wd=$PWD
cd /
plus=$(print -r -- ~+)
@@ -6425,10 +7383,106 @@
[[ $minus = "$wd" ]]; echo two $? .
[[ $nix = /sweet ]]; echo nix $? .
expected-stdout:
+ <[ab]> <a>
one 0 .
two 0 .
nix 0 .
---
+name: tilde-expand-3
+description:
+ Check mostly Austin 351 stuff
+stdin:
+ showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
+ set "1 b=2" "3 d=4"
+ export a=$1 \c=$2
+ showargs 1 "$a" "$b" "$c" "$d"
+ unset a b c d
+ HOME=/tmp
+ export \a=~ b=~
+ command export c=~
+ builtin export d=~
+ \\builtin export e=~
+ showargs 2 "$a" "$b" "$c" "$d" "$e" ksh
+ unset a b c d e
+ set -o posix
+ export \a=~ b=~
+ command export c=~
+ builtin export d=~
+ \\builtin export e=~
+ showargs 3 "$a" "$b" "$c" "$d" "$e" posix
+ unset a b c d e
+ set +o posix
+ export a=$1
+ showargs 4 "$a" "$b" ksh
+ unset a b
+ showargs 5 a=$1 ksh
+ export \a=$1
+ showargs 6 "$a" "$b" ksh
+ unset a b
+ set -o posix
+ export a=$1
+ showargs 7 "$a" "$b" posix
+ unset a b
+ showargs 8 a=$1 posix
+ export \a=$1
+ showargs 9 "$a" "$b" posix
+ unset a b
+ set +o posix
+ command echo 10 ksh a=~
+ command command export a=~
+ showargs 11 "$a"
+ unset a
+ set -o posix
+ command echo 12 posix a=~
+ command command export a=~
+ showargs 13 "$a"
+ unset a
+ # unspecified whether /tmp or ~
+ var=export; command $var a=~
+ showargs 14 "$a"
+ echo 'echo "<$foo>"' >bar
+ "$__progname" bar
+ var=foo
+ export $var=1
+ "$__progname" bar
+ export $var=~
+ "$__progname" bar
+ # unspecified
+ command -- export a=~
+ showargs 18 "$a"
+ set -A bla
+ typeset bla[1]=~:~
+ global gbl=~ g2=$1
+ local lcl=~ l2=$1
+ readonly ro=~ r2=$1
+ showargs 19 "${bla[1]}" a=~ "$gbl" "$lcl" "$ro" "$g2" "$l2" "$r2"
+ set +o posix
+ echo "20 some arbitrary stuff "=~
+ set -o posix
+ echo "21 some arbitrary stuff "=~
+expected-stdout:
+ <1> <1 b=2> <> <3> <4> .
+ <2> </tmp> </tmp> </tmp> </tmp> </tmp> <ksh> .
+ <3> <~> </tmp> </tmp> <~> </tmp> <posix> .
+ <4> <1 b=2> <> <ksh> .
+ <5> <a=1> <b=2> <ksh> .
+ <6> <1> <2> <ksh> .
+ <7> <1 b=2> <> <posix> .
+ <8> <a=1> <b=2> <posix> .
+ <9> <1> <2> <posix> .
+ 10 ksh a=/tmp
+ <11> </tmp> .
+ 12 posix a=~
+ <13> </tmp> .
+ <14> <~> .
+ <>
+ <1>
+ <~>
+ <18> <~> .
+ <19> </tmp:/tmp> <a=~> </tmp> </tmp> </tmp> <1 b=2> <1 b=2> <1 b=2> .
+ 20 some arbitrary stuff =/tmp
+ 21 some arbitrary stuff =~
+---
name: exit-err-1
description:
Check some "exit on error" conditions
@@ -6436,7 +7490,7 @@
print '#!'"$__progname"'\nexec "$1"' >env
print '#!'"$__progname"'\nexit 1' >false
chmod +x env false
- PATH=.:$PATH
+ PATH=.$PATHSEP$PATH
set -ex
env false && echo something
echo END
@@ -6454,7 +7508,7 @@
print '#!'"$__progname"'\nexit 1' >false
print '#!'"$__progname"'\nexit 0' >true
chmod +x env false
- PATH=.:$PATH
+ PATH=.$PATHSEP$PATH
set -ex
if env true; then
env false && echo something
@@ -6566,6 +7620,19 @@
db_go
exit 0
---
+name: exit-err-9
+description:
+ "set -e" versus bang pipelines
+stdin:
+ set -e
+ ! false | false
+ echo 1 ok
+ ! false && false
+ echo 2 wrong
+expected-stdout:
+ 1 ok
+expected-exit: 1
+---
name: exit-enoent-1
description:
SUSv4 says that the shell should exit with 126/127 in some situations
@@ -6788,7 +7855,7 @@
After error 2
Exit trap
expected-stderr-pattern:
- /syntax error: 'newline' unexpected/
+ /syntax error: unexpected 'newline'/
---
name: test-stlt-1
description:
@@ -6898,6 +7965,183 @@
2- 1 1 1 =
3- 0 0 0 =
---
+name: test-varset-1
+description:
+ Test the test -v operator
+stdin:
+ [[ -v a ]]
+ rv=$?; echo $((++i)) $rv
+ a=
+ [[ -v a ]]
+ rv=$?; echo $((++i)) $rv
+ unset a
+ [[ -v a ]]
+ rv=$?; echo $((++i)) $rv
+ a=x
+ [[ -v a ]]
+ rv=$?; echo $((++i)) $rv
+ nameref b=a
+ [[ -v b ]]
+ rv=$?; echo $((++i)) $rv
+ unset a
+ [[ -v b ]]
+ rv=$?; echo $((++i)) $rv
+ x[1]=y
+ [[ -v x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ -v x[0] ]]
+ rv=$?; echo $((++i)) $rv
+ [[ -v x[1] ]]
+ rv=$?; echo $((++i)) $rv
+ [[ -v x[2] ]]
+ rv=$?; echo $((++i)) $rv
+expected-stdout:
+ 1 1
+ 2 0
+ 3 1
+ 4 0
+ 5 0
+ 6 1
+ 7 1
+ 8 1
+ 9 0
+ 10 1
+---
+name: test-varset-2
+description:
+ test -v works only on scalars
+stdin:
+ [[ -v x[*] ]]
+ echo ok
+expected-exit: e != 0
+expected-stderr-pattern:
+ /unexpected '\*'/
+---
+name: test-stnze-1
+description:
+ Check that the short form [ $x ] works
+stdin:
+ i=0
+ [ -n $x ]
+ rv=$?; echo $((++i)) $rv
+ [ $x ]
+ rv=$?; echo $((++i)) $rv
+ [ -n "$x" ]
+ rv=$?; echo $((++i)) $rv
+ [ "$x" ]
+ rv=$?; echo $((++i)) $rv
+ x=0
+ [ -n $x ]
+ rv=$?; echo $((++i)) $rv
+ [ $x ]
+ rv=$?; echo $((++i)) $rv
+ [ -n "$x" ]
+ rv=$?; echo $((++i)) $rv
+ [ "$x" ]
+ rv=$?; echo $((++i)) $rv
+ x='1 -a 1 = 2'
+ [ -n $x ]
+ rv=$?; echo $((++i)) $rv
+ [ $x ]
+ rv=$?; echo $((++i)) $rv
+ [ -n "$x" ]
+ rv=$?; echo $((++i)) $rv
+ [ "$x" ]
+ rv=$?; echo $((++i)) $rv
+expected-stdout:
+ 1 0
+ 2 1
+ 3 1
+ 4 1
+ 5 0
+ 6 0
+ 7 0
+ 8 0
+ 9 1
+ 10 1
+ 11 0
+ 12 0
+---
+name: test-stnze-2
+description:
+ Check that the short form [[ $x ]] works (ksh93 extension)
+stdin:
+ i=0
+ [[ -n $x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ $x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ -n "$x" ]]
+ rv=$?; echo $((++i)) $rv
+ [[ "$x" ]]
+ rv=$?; echo $((++i)) $rv
+ x=0
+ [[ -n $x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ $x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ -n "$x" ]]
+ rv=$?; echo $((++i)) $rv
+ [[ "$x" ]]
+ rv=$?; echo $((++i)) $rv
+ x='1 -a 1 = 2'
+ [[ -n $x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ $x ]]
+ rv=$?; echo $((++i)) $rv
+ [[ -n "$x" ]]
+ rv=$?; echo $((++i)) $rv
+ [[ "$x" ]]
+ rv=$?; echo $((++i)) $rv
+expected-stdout:
+ 1 1
+ 2 1
+ 3 1
+ 4 1
+ 5 0
+ 6 0
+ 7 0
+ 8 0
+ 9 0
+ 10 0
+ 11 0
+ 12 0
+---
+name: test-numeq
+description:
+ Check numeric -eq works (R40d regression); spotted by Martijn Dekker
+stdin:
+ tst() {
+ eval "$2"
+ case $? in
+ (0) echo yepp 0 \#"$*" ;;
+ (1) echo nope 1 \#"$*" ;;
+ (2) echo terr 2 \#"$*" ;;
+ (*) echo wtf\? $? \#"$*" ;;
+ esac
+ }
+ tst 1 'test 2 -eq 2'
+ tst 2 'test 2 -eq 2a'
+ tst 3 'test 2 -eq 3'
+ tst 4 'test 2 -ne 2'
+ tst 5 'test 2 -ne 2a'
+ tst 6 'test 2 -ne 3'
+ tst 7 'test \! 2 -eq 2'
+ tst 8 'test \! 2 -eq 2a'
+ tst 9 'test \! 2 -eq 3'
+expected-stdout:
+ yepp 0 #1 test 2 -eq 2
+ terr 2 #2 test 2 -eq 2a
+ nope 1 #3 test 2 -eq 3
+ nope 1 #4 test 2 -ne 2
+ terr 2 #5 test 2 -ne 2a
+ yepp 0 #6 test 2 -ne 3
+ nope 1 #7 test \! 2 -eq 2
+ terr 2 #8 test \! 2 -eq 2a
+ yepp 0 #9 test \! 2 -eq 3
+expected-stderr-pattern:
+ /bad number/
+---
name: mkshrc-1
description:
Check that ~/.mkshrc works correctly.
@@ -7123,11 +8367,11 @@
---
name: typeset-1
description:
- Check that global does what typeset is supposed to do
+ Check that typeset -g works correctly
stdin:
set -A arrfoo 65
foo() {
- global -Uui16 arrfoo[*]
+ typeset -g -Uui16 arrfoo[*]
}
echo before ${arrfoo[0]} .
foo
@@ -7137,7 +8381,7 @@
echo inside before ${arrbar[0]} .
arrbar[0]=97
echo inside changed ${arrbar[0]} .
- global -Uui16 arrbar[*]
+ typeset -g -Uui16 arrbar[*]
echo inside typeset ${arrbar[0]} .
arrbar[0]=48
echo inside changed ${arrbar[0]} .
@@ -7155,6 +8399,24 @@
inside changed 16#30 .
after 16#30 .
---
+name: typeset-2
+description:
+ Check that typeset -p on arrays works correctly
+stdin:
+ set -A x -- a b c
+ echo =
+ typeset -p x
+ echo =
+ typeset -p x[1]
+expected-stdout:
+ =
+ set -A x
+ typeset x[0]=a
+ typeset x[1]=b
+ typeset x[2]=c
+ =
+ typeset x[1]=b
+---
name: typeset-padding-1
description:
Check if left/right justification works as per TFM
@@ -7188,7 +8450,7 @@
multibyte character of the shell input (with -c, from standard
input, as file, or as eval argument), but nowhere else
# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
-category: !os:darwin
+category: !os:darwin,!shell:ebcdic-yes
stdin:
mkdir foo
print '#!/bin/sh\necho ohne' >foo/fnord
@@ -7198,7 +8460,7 @@
set -A anzahl -- foo/*
echo got ${#anzahl[*]} files
chmod +x foo/*
- export PATH=$(pwd)/foo:$PATH
+ export PATH=$(pwd)/foo$PATHSEP$PATH
"$__progname" -c 'fnord'
echo =
"$__progname" -c 'fnord; fnord; fnord; fnord'
@@ -7265,11 +8527,10 @@
expected-stderr-pattern:
/(Unrecognized character .... ignored at \..t4 line 1)*/
---
-name: utf8opt-1a
+name: utf8opt-1
description:
Check that the utf8-mode flag is not set at non-interactive startup
-category: !os:hpux
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
+env-setup: !PS1=!PS2=!LC_CTYPE=@utflocale@!
stdin:
if [[ $- = *U* ]]; then
echo is set
@@ -7279,32 +8540,15 @@
expected-stdout:
is not set
---
-name: utf8opt-1b
+name: utf8opt-2
description:
- Check that the utf8-mode flag is not set at non-interactive startup
-category: os:hpux
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
-stdin:
- if [[ $- = *U* ]]; then
- echo is set
- else
- echo is not set
- fi
-expected-stdout:
- is not set
----
-name: utf8opt-2a
-description:
Check that the utf8-mode flag is set at interactive startup.
- -DMKSH_ASSUME_UTF8=0 => expected failure, please ignore
- -DMKSH_ASSUME_UTF8=1 => not expected, please investigate
- -UMKSH_ASSUME_UTF8 => not expected, but if your OS is old,
- try passing HAVE_SETLOCALE_CTYPE=0 to Build.sh
+ If your OS is old, try passing HAVE_SETLOCALE_CTYPE=0 to Build.sh
need-pass: no
-category: !os:hpux,!os:msys
+category: !noutf8
need-ctty: yes
arguments: !-i!
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
+env-setup: !PS1=!PS2=!LC_CTYPE=@utflocale@!
stdin:
if [[ $- = *U* ]]; then
echo is set
@@ -7316,25 +8560,6 @@
expected-stderr-pattern:
/(# )*/
---
-name: utf8opt-2b
-description:
- Check that the utf8-mode flag is set at interactive startup
- Expected failure if -DMKSH_ASSUME_UTF8=0
-category: os:hpux
-need-ctty: yes
-arguments: !-i!
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
-stdin:
- if [[ $- = *U* ]]; then
- echo is set
- else
- echo is not set
- fi
-expected-stdout:
- is set
-expected-stderr-pattern:
- /(# )*/
----
name: utf8opt-3a
description:
Ensure ±U on the command line is honoured
@@ -7365,163 +8590,86 @@
2 off
3 done
---
-name: aliases-1
+name: utf8bug-1
description:
- Check if built-in shell aliases are okay
-category: !android,!arge
+ Ensure trailing combining characters are not lost
stdin:
- alias
- typeset -f
+ set -U
+ a=a
+ b=$'\u0301'
+ x=$a$b
+ print -r -- "<e$x>"
+ x=$a
+ x+=$b
+ print -r -- "<e$x>"
+ b=$'\u0301'b
+ x=$a
+ x+=$b
+ print -r -- "<e$x>"
expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- stop='kill -STOP'
- type='whence -v'
+ <eá>
+ <eá>
+ <eáb>
---
-name: aliases-1-hartz4
+name: aliases-1
description:
Check if built-in shell aliases are okay
-category: android,arge
stdin:
alias
typeset -f
expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
+ autoload='\\builtin typeset -fu'
+ functions='\\builtin typeset -f'
+ hash='\\builtin alias -t'
+ history='\\builtin fc -l'
+ integer='\\builtin typeset -i'
+ local='\\builtin typeset'
+ login='\\builtin exec login'
+ nameref='\\builtin typeset -n'
nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- type='whence -v'
+ r='\\builtin fc -e -'
+ type='\\builtin whence -v'
---
-name: aliases-2a
-description:
- Check if “set -o sh” disables built-in aliases (except a few)
-category: disabled
-arguments: !-o!sh!
-stdin:
- alias
- typeset -f
-expected-stdout:
- integer='typeset -i'
- local=typeset
----
-name: aliases-3a
-description:
- Check if running as sh disables built-in aliases (except a few)
-category: disabled
-stdin:
- cp "$__progname" sh
- ./sh -c 'alias; typeset -f'
- rm -f sh
-expected-stdout:
- integer='typeset -i'
- local=typeset
----
name: aliases-2b
description:
Check if “set -o sh” does not influence built-in aliases
-category: !android,!arge
arguments: !-o!sh!
stdin:
alias
typeset -f
expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
+ autoload='\\builtin typeset -fu'
+ functions='\\builtin typeset -f'
+ hash='\\builtin alias -t'
+ history='\\builtin fc -l'
+ integer='\\builtin typeset -i'
+ local='\\builtin typeset'
+ login='\\builtin exec login'
+ nameref='\\builtin typeset -n'
nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- stop='kill -STOP'
- type='whence -v'
+ r='\\builtin fc -e -'
+ type='\\builtin whence -v'
---
name: aliases-3b
description:
Check if running as sh does not influence built-in aliases
-category: !android,!arge
stdin:
cp "$__progname" sh
./sh -c 'alias; typeset -f'
rm -f sh
expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
+ autoload='\\builtin typeset -fu'
+ functions='\\builtin typeset -f'
+ hash='\\builtin alias -t'
+ history='\\builtin fc -l'
+ integer='\\builtin typeset -i'
+ local='\\builtin typeset'
+ login='\\builtin exec login'
+ nameref='\\builtin typeset -n'
nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- stop='kill -STOP'
- type='whence -v'
+ r='\\builtin fc -e -'
+ type='\\builtin whence -v'
---
-name: aliases-2b-hartz4
-description:
- Check if “set -o sh” does not influence built-in aliases
-category: android,arge
-arguments: !-o!sh!
-stdin:
- alias
- typeset -f
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- type='whence -v'
----
-name: aliases-3b-hartz4
-description:
- Check if running as sh does not influence built-in aliases
-category: android,arge
-stdin:
- cp "$__progname" sh
- ./sh -c 'alias; typeset -f'
- rm -f sh
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- type='whence -v'
----
name: aliases-cmdline
description:
Check that aliases work from the command line (Debian #517009)
@@ -7543,7 +8691,7 @@
}
foo
expected-stdout:
- funktion
+ makro
---
name: aliases-funcdef-2
description:
@@ -7555,7 +8703,7 @@
}
foo
expected-stdout:
- funktion
+ makro
---
name: aliases-funcdef-3
description:
@@ -7577,8 +8725,8 @@
:|| local() { :; }
alias local
expected-stdout:
- local=typeset
- local=typeset
+ local='\\builtin typeset'
+ local='\\builtin typeset'
---
name: arrays-1
description:
@@ -7878,6 +9026,15 @@
.c:a b.c d..:
.d:a b.c d..:
---
+name: arrassign-eol
+description:
+ Commands after array assignments are not permitted
+stdin:
+ foo=(a b) env
+expected-exit: e != 0
+expected-stderr-pattern:
+ /syntax error: unexpected 'env'/
+---
name: arrassign-fnc-none
description:
Check locality of array access inside a function
@@ -7988,21 +9145,21 @@
name: arrassign-fnc-global
description:
Check locality of array access inside a function
- with the mksh-specific global keyword
+ with the bash4/mksh/yash/zsh typeset -g keyword
stdin:
function fn {
- global x
+ typeset -g x
x+=(f)
echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
}
function rfn {
set -A y
- global y
+ typeset -g y
y+=(f)
echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
}
function fnr {
- global z
+ typeset -g z
set -A z
z+=(f)
echo ".fnr:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
@@ -8140,21 +9297,21 @@
name: strassign-fnc-global
description:
Check locality of string access inside a function
- with the mksh-specific global keyword
+ with the bash4/mksh/yash/zsh typeset -g keyword
stdin:
function fn {
- global x
+ typeset -g x
x+=f
echo ".fn:$x:"
}
function rfn {
y=
- global y
+ typeset -g y
y+=f
echo ".rfn:$y:"
}
function fnr {
- global z
+ typeset -g z
z=
z+=f
echo ".fnr:$z:"
@@ -8300,7 +9457,7 @@
name: varexpand-substr-3
description:
Check that some things that work in bash fail.
- This is by design. And that some things fail in both.
+ This is by design. Oh and vice versa, nowadays.
stdin:
export x=abcdefghi n=2
"$__progname" -c 'echo v${x:(n)}x'
@@ -8308,12 +9465,13 @@
"$__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 $?
expected-stdout:
vcdefghix
wcdefghix
zabcdefghix
- 1
+ 0
expected-stderr-pattern:
/x:n.*bad substitution.*\n.*bad substitution/
---
@@ -8379,6 +9537,7 @@
name: varexpand-special-hash
description:
Check special ${var at x} expansion for x=hash
+category: !shell:ebcdic-yes
stdin:
typeset -i8 foo=10
bar=baz
@@ -8387,9 +9546,22 @@
expected-stdout:
9B15FBFB CFBDD32B 00000000 .
---
+name: varexpand-special-hash-ebcdic
+description:
+ Check special ${var at x} expansion for x=hash
+category: !shell:ebcdic-no
+stdin:
+ typeset -i8 foo=10
+ bar=baz
+ unset baz
+ print ${foo@#} ${bar@#} ${baz@#} .
+expected-stdout:
+ 016AE33D 9769C4AF 00000000 .
+---
name: varexpand-special-quote
description:
Check special ${var at Q} expansion for quoted strings
+category: !shell:faux-ebcdic
stdin:
set +U
i=x
@@ -8409,6 +9581,29 @@
typeset v='a b'
typeset w=$'c\nd\240e\u20ACf'
---
+name: varexpand-special-quote-faux-EBCDIC
+description:
+ Check special ${var at Q} expansion for quoted strings
+category: shell:faux-ebcdic
+stdin:
+ set +U
+ i=x
+ j=a\ b
+ k=$'c
+ d\xA0''e€f'
+ print -r -- "<i=$i j=$j k=$k>"
+ s="u=${i at Q} v=${j at Q} w=${k at Q}"
+ print -r -- "s=\"$s\""
+ eval "$s"
+ typeset -p u v w
+expected-stdout:
+ <i=x j=a b k=c
+ d\xA0e€f>
+ s="u=x v='a b' w=$'c\nd\xA0e\u20ACf'"
+ typeset u=x
+ typeset v='a b'
+ typeset w=$'c\nd\xA0e\u20ACf'
+---
name: varexpand-null-1
description:
Ensure empty strings expand emptily
@@ -8445,6 +9640,7 @@
Ensure concatenating behaviour matches other shells
stdin:
showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
+ showargs 0 ""$@
x=; showargs 1 "$x"$@
set A; showargs 2 "${@:+}"
n() { echo "$#"; }
@@ -8464,6 +9660,7 @@
n "$@"
n "$@""$e"
expected-stdout:
+ <0> <> .
<1> <> .
<2> <> .
2
@@ -8479,13 +9676,64 @@
0
1
---
+name: varexpand-funny-chars
+description:
+ Check some characters
+ XXX \uEF80 is asymmetric, possibly buggy so we don’t check this
+stdin:
+ x=$'<\x00>'; typeset -p x
+ x=$'<\x01>'; typeset -p x
+ x=$'<\u0000>'; typeset -p x
+ x=$'<\u0001>'; typeset -p x
+expected-stdout:
+ typeset x='<'
+ typeset x=$'<\001>'
+ typeset x='<'
+ typeset x=$'<\001>'
+---
name: print-funny-chars
description:
Check print builtin's capability to output designated characters
stdin:
- print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
+ {
+ print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
+ print '<\x00>'
+ print '<\x01>'
+ print '<\u0000>'
+ print '<\u0001>'
+ } | {
+ # integer-base-one-3Ar
+ typeset -Uui16 -Z11 pos=0
+ typeset -Uui16 -Z5 hv=2147483647
+ dasc=
+ if read -arN -1 line; then
+ typeset -i1 line
+ i=0
+ while (( i < ${#line[*]} )); do
+ hv=${line[i++]}
+ if (( (pos & 15) == 0 )); then
+ (( pos )) && print -r -- "$dasc|"
+ print -n "${pos#16#} "
+ dasc=' |'
+ fi
+ print -n "${hv#16#} "
+ if (( (hv < 32) || (hv > 126) )); then
+ dasc=$dasc.
+ else
+ dasc=$dasc${line[i-1]#1#}
+ fi
+ (( (pos++ & 15) == 7 )) && print -n -- '- '
+ done
+ fi
+ while (( pos & 15 )); do
+ print -n ' '
+ (( (pos++ & 15) == 7 )) && print -n -- '- '
+ done
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
+ }
expected-stdout:
- <d\xE4\xDBÛ€Û@>
+ 00000000 3C 64 E4 DB C3 9B E2 82 - AC C3 9B 40 3E 0A 3C 00 |<d.........@>.<.|
+ 00000010 3E 0A 3C 01 3E 0A 3C 00 - 3E 0A 3C 01 3E 0A |>.<.>.<.>.<.>.|
---
name: print-bksl-c
description:
@@ -8523,6 +9771,84 @@
{220 Who do you wanna pretend to be today?
}
---
+name: print-crlf
+description:
+ Check that CR+LF is shown and read as-is
+category: shell:textmode-no
+stdin:
+ cat >foo <<-'EOF'
+ x='bar
+ ' #
+ echo .${#x} #
+ if test x"$KSH_VERSION" = x""; then #
+ printf '<%s>' "$x" #
+ else #
+ print -nr -- "<$x>" #
+ fi #
+ EOF
+ echo "[$("$__progname" foo)]"
+ "$__progname" foo | while IFS= read -r line; do
+ print -r -- "{$line}"
+ done
+expected-stdout:
+ [.5
+ <bar
+ >]
+ {.5}
+ {<bar
+}
+---
+name: print-crlf-textmode
+description:
+ Check that CR+LF is treated as newline
+category: shell:textmode-yes
+stdin:
+ cat >foo <<-'EOF'
+ x='bar
+ ' #
+ echo .${#x} #
+ if test x"$KSH_VERSION" = x""; then #
+ printf '<%s>' "$x" #
+ else #
+ print -nr -- "<$x>" #
+ fi #
+ EOF
+ echo "[$("$__progname" foo)]"
+ "$__progname" foo | while IFS= read -r line; do
+ print -r -- "{$line}"
+ done
+expected-stdout:
+ [.4
+ <bar
+ >]
+ {.4}
+ {<bar}
+---
+name: print-lf
+description:
+ Check that LF-only is shown and read as-is
+stdin:
+ cat >foo <<-'EOF'
+ x='bar
+ ' #
+ echo .${#x} #
+ if test x"$KSH_VERSION" = x""; then #
+ printf '<%s>' "$x" #
+ else #
+ print -nr -- "<$x>" #
+ fi #
+ EOF
+ echo "[$("$__progname" foo)]"
+ "$__progname" foo | while IFS= read -r line; do
+ print -r -- "{$line}"
+ done
+expected-stdout:
+ [.4
+ <bar
+ >]
+ {.4}
+ {<bar}
+---
name: print-nul-chars
description:
Check handling of NUL characters for print and COMSUB
@@ -8533,6 +9859,16 @@
expected-stdout-pattern:
/^4 3 2 <> <\0>$/
---
+name: print-array
+description:
+ Check that print -A works as expected
+stdin:
+ print -An 0x20AC 0xC3 0xBC 8#101
+ set -U
+ print -A 0x20AC 0xC3 0xBC 8#102
+expected-stdout:
+ \xACüA€Ã¼B
+---
name: print-escapes
description:
Check backslash expansion by the print builtin
@@ -8553,7 +9889,7 @@
while [[ -n $line ]]; do
hv=1#${line::1}
if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
+ (( pos )) && print -r -- "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
@@ -8571,7 +9907,7 @@
print -n ' '
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
- (( hv == 2147483647 )) || print "$dasc|"
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
}
expected-stdout:
00000000 5C 20 5C 21 5C 22 5C 23 - 5C 24 5C 25 5C 26 5C 27 |\ \!\"\#\$\%\&\'|
@@ -8580,13 +9916,13 @@
00000030 20 5C 39 5C 3A 5C 3B 5C - 3C 5C 3D 5C 3E 5C 3F 5C | \9\:\;\<\=\>\?\|
00000040 40 5C 41 5C 42 5C 43 5C - 44 1B 5C 46 5C 47 5C 48 |@\A\B\C\D.\F\G\H|
00000050 5C 49 5C 4A 5C 4B 5C 4C - 5C 4D 5C 4E 5C 4F 5C 50 |\I\J\K\L\M\N\O\P|
- 00000060 5C 51 5C 52 5C 53 5C 54 - 20 5C 56 5C 57 5C 58 5C |\Q\R\S\T \V\W\X\|
- 00000070 59 5C 5A 5C 5B 5C 5C 5D - 5C 5E 5C 5F 5C 60 07 08 |Y\Z\[\]\^\_\`..|
- 00000080 20 20 5C 64 1B 0C 5C 67 - 5C 68 5C 69 5C 6A 5C 6B | \d..\g\h\i\j\k|
- 00000090 5C 6C 5C 6D 0A 5C 6F 5C - 70 20 5C 71 0D 5C 73 09 |\l\m.\o\p \q.\s.|
- 000000A0 0B 5C 77 5C 79 5C 7A 5C - 7B 5C 7C 5C 7D 5C 7E 20 |.\w\y\z\{\|\}\~ |
- 000000B0 E2 82 AC 64 20 EF BF BD - 20 12 33 20 78 20 53 20 |...d ... .3 x S |
- 000000C0 53 34 0A - |S4.|
+ 00000060 5C 51 5C 52 5C 53 5C 54 - 20 5C 55 5C 56 5C 57 5C |\Q\R\S\T \U\V\W\|
+ 00000070 58 5C 59 5C 5A 5C 5B 5C - 5C 5D 5C 5E 5C 5F 5C 60 |X\Y\Z\[\\]\^\_\`|
+ 00000080 07 08 20 20 5C 64 1B 0C - 5C 67 5C 68 5C 69 5C 6A |.. \d..\g\h\i\j|
+ 00000090 5C 6B 5C 6C 5C 6D 0A 5C - 6F 5C 70 20 5C 71 0D 5C |\k\l\m.\o\p \q.\|
+ 000000A0 73 09 5C 75 0B 5C 77 5C - 78 5C 79 5C 7A 5C 7B 5C |s.\u.\w\x\y\z\{\|
+ 000000B0 7C 5C 7D 5C 7E 20 E2 82 - AC 64 20 EF BF BD 20 12 ||\}\~ ...d ... .|
+ 000000C0 33 20 78 20 53 20 53 34 - 0A |3 x S S4.|
---
name: dollar-doublequoted-strings
description:
@@ -8615,7 +9951,7 @@
$'\J\K\L\M\N\O\P\Q\R\S\T\U1\V\W\X\Y\Z\[\\\]\^\_\`\a\b\d\e' \
$'\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u1\v\w\x1\y\z\{\|\}\~ $x' \
$'\u20acd' $'\U20acd' $'\x123' $'fn\x0rd' $'\0234' $'\234' \
- $'\2345' $'\ca' $'\c!' $'\c?' $'\c€' $'a\
+ $'\2345' $'\ca' $'\c!' $'\c?' $'\c…' $'a\
b' | {
# integer-base-one-3As
typeset -Uui16 -Z11 pos=0
@@ -8628,7 +9964,7 @@
while [[ -n $line ]]; do
hv=1#${line::1}
if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
+ (( pos )) && print -r -- "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
@@ -8646,7 +9982,7 @@
print -n ' '
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
- (( hv == 2147483647 )) || print "$dasc|"
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
}
expected-stdout:
00000000 20 21 22 23 24 25 26 27 - 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./|
@@ -8657,7 +9993,7 @@
00000050 68 69 6A 6B 6C 6D 0A 6F - 70 71 0D 73 09 01 0B 77 |hijklm.opq.s...w|
00000060 01 79 7A 7B 7C 7D 7E 20 - 24 78 0A E2 82 AC 64 0A |.yz{|}~ $x....d.|
00000070 EF BF BD 0A C4 A3 0A 66 - 6E 0A 13 34 0A 9C 0A 9C |.......fn..4....|
- 00000080 35 0A 01 0A 01 0A 7F 0A - 02 82 AC 0A 61 0A 62 0A |5...........a.b.|
+ 00000080 35 0A 01 0A 01 0A 7F 0A - 82 80 A6 0A 61 0A 62 0A |5...........a.b.|
---
name: dollar-quotes-in-heredocs-strings
description:
@@ -8717,30 +10053,57 @@
"$__progname" -c source
expected-exit: e != 0
expected-stderr-pattern:
- /\.: missing argument.*\n.*\.: missing argument/
+ /\.: missing argument.*\n.*source: missing argument/
---
+name: dot-errorlevel
+description:
+ Ensure dot resets $?
+stdin:
+ :>dotfile
+ (exit 42)
+ . ./dotfile
+ echo 1 $? .
+expected-stdout:
+ 1 0 .
+---
name: alias-function-no-conflict
description:
- make aliases not conflict with functions
- note: for ksh-like functions, the order of preference is
- different; bash outputs baz instead of bar in line 2 below
+ make aliases not conflict with function definitions
stdin:
+ # POSIX function can be defined, but alias overrides it
alias foo='echo bar'
+ foo
foo() {
echo baz
}
+ foo
+ unset -f foo
+ foo 2>/dev/null || echo rab
+ # alias overrides ksh function
alias korn='echo bar'
+ korn
function korn {
echo baz
}
- foo
korn
- unset -f foo
- foo 2>/dev/null || echo rab
+ # alias temporarily overrides POSIX function
+ bla() {
+ echo bfn
+ }
+ bla
+ alias bla='echo bal'
+ bla
+ unalias bla
+ bla
expected-stdout:
- baz
bar
- rab
+ bar
+ bar
+ bar
+ bar
+ bfn
+ bal
+ bfn
---
name: bash-function-parens
description:
@@ -8752,12 +10115,11 @@
echo "$1 {"
echo ' echo "bar='\''$0'\'\"
echo '}'
- echo ${2:-foo}
+ print -r -- "${2:-foo}"
}
mk 'function foo' >f-korn
mk 'foo ()' >f-dash
mk 'function foo ()' >f-bash
- mk 'function stop ()' stop >f-stop
print '#!'"$__progname"'\nprint -r -- "${0%/f-argh}"' >f-argh
chmod +x f-*
u=$(./f-argh)
@@ -8764,12 +10126,10 @@
x="korn: $(./f-korn)"; echo "${x/@("$u")/.}"
x="dash: $(./f-dash)"; echo "${x/@("$u")/.}"
x="bash: $(./f-bash)"; echo "${x/@("$u")/.}"
- x="stop: $(./f-stop)"; echo "${x/@("$u")/.}"
expected-stdout:
korn: bar='foo'
dash: bar='./f-dash'
bash: bar='./f-bash'
- stop: bar='./f-stop'
---
name: integer-base-one-1
description:
@@ -8932,7 +10292,7 @@
while [[ -n $line ]]; do
hv=1#${line::1}
if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
+ (( pos )) && print -r -- "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
@@ -8950,7 +10310,7 @@
print -n ' '
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
- (( hv == 2147483647 )) || print "$dasc|"
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
}
expected-stdout:
00000000 48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3 |Hello, World!\..|
@@ -9020,7 +10380,7 @@
dasc=$dasc$dch
dch=
elif (( (pos & 7) == 0 )); then
- (( pos )) && print "$dasc|"
+ (( pos )) && print -r -- "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
@@ -9035,7 +10395,7 @@
print -n ' '
(( (pos++ & 7) == 3 )) && print -n -- '- '
done
- (( hv == 2147483647 )) || print "$dasc|"
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
}
expected-stdout:
00000000 0048 0065 006C 006C - 006F 002C 0020 0057 |Hello, W|
@@ -9101,7 +10461,7 @@
while (( i < ${#line[*]} )); do
hv=${line[i++]}
if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
+ (( pos )) && print -r -- "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
@@ -9118,7 +10478,7 @@
print -n ' '
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
- (( hv == 2147483647 )) || print "$dasc|"
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
}
expected-stdout:
00000000 48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3 |Hello, World!\..|
@@ -9184,7 +10544,7 @@
dasc=$dasc$dch
dch=
elif (( (pos & 7) == 0 )); then
- (( pos )) && print "$dasc|"
+ (( pos )) && print -r -- "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
@@ -9198,7 +10558,7 @@
print -n ' '
(( (pos++ & 7) == 3 )) && print -n -- '- '
done
- (( hv == 2147483647 )) || print "$dasc|"
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
}
expected-stdout:
00000000 0048 0065 006C 006C - 006F 002C 0020 0057 |Hello, W|
@@ -9264,6 +10624,7 @@
name: integer-base-one-5A
description:
Check to see that we’re NUL and Unicode safe
+category: !shell:ebcdic-yes
stdin:
set +U
print 'a\0b\xfdz' >x
@@ -9274,6 +10635,20 @@
expected-stdout:
16#61 16#0 16#62 16#FD 16#7A .
---
+name: integer-base-one-5E
+description:
+ Check to see that we’re NUL and Unicode safe
+category: !shell:ebcdic-no
+stdin:
+ set +U
+ print 'a\0b\xfdz' >x
+ read -a y <x
+ set -U
+ typeset -Uui16 y
+ print ${y[*]} .
+expected-stdout:
+ 16#81 16#0 16#82 16#FD 16#A9 .
+---
name: integer-base-one-5W
description:
Check to see that we’re NUL and Unicode safe
@@ -9289,8 +10664,15 @@
---
name: ulimit-1
description:
+ Check that ulimit as used in dot.mksh works or is stubbed
+stdin:
+ ulimit -c 0
+---
+name: ulimit-2
+description:
Check if we can use a specific syntax idiom for ulimit
-category: !os:syllable
+ XXX Haiku works, but only for -n and -V
+category: !os:haiku,!os:syllable
stdin:
if ! x=$(ulimit -d) || [[ $x = unknown ]]; then
#echo expected to fail on this OS
@@ -9335,7 +10717,6 @@
description:
Check if GNU bash-like I/O redirection works
Part 1: this is also supported by GNU bash
-category: shell:legacy-no
stdin:
exec 3>&1
function threeout {
@@ -9356,7 +10737,6 @@
description:
Check if GNU bash-like I/O redirection works
Part 2: this is *not* supported by GNU bash
-category: shell:legacy-no
stdin:
exec 3>&1
function threeout {
@@ -9377,7 +10757,6 @@
description:
Check if GNU bash-like I/O redirection works
Part 2: this is *not* supported by GNU bash
-category: shell:legacy-no
stdin:
exec 3>&1
function threeout {
@@ -9398,7 +10777,6 @@
description:
Check if GNU bash-like I/O redirection works
Part 2: this is supported by GNU bash 4 only
-category: shell:legacy-no
stdin:
echo mir >foo
set -o noclobber
@@ -9422,7 +10800,6 @@
description:
Check if GNU bash-like I/O redirection fails correctly
Part 1: this is also supported by GNU bash
-category: shell:legacy-no
stdin:
echo mir >foo
set -o noclobber
@@ -9444,7 +10821,6 @@
description:
Check if GNU bash-like I/O redirection fails correctly
Part 2: this is *not* supported by GNU bash
-category: shell:legacy-no
stdin:
echo mir >foo
set -o noclobber
@@ -9468,7 +10844,6 @@
Check if GNU bash-like I/O redirection works
Part 4: this is also supported by GNU bash,
but failed in some mksh versions
-category: shell:legacy-no
stdin:
exec 3>&1
function threeout {
@@ -9490,11 +10865,10 @@
ras
dwa
---
-name: bashiop-5-normal
+name: bashiop-5
description:
Check if GNU bash-like I/O redirection is only supported
in !POSIX !sh mode as it breaks existing scripts' syntax
-category: shell:legacy-no
stdin:
:>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
:>x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
@@ -9504,43 +10878,6 @@
2 = bar .
3 = bar .
---
-name: bashiop-5-legacy
-description:
- Check if GNU bash-like I/O redirection is not parsed
- in lksh as it breaks existing scripts' syntax
-category: shell:legacy-yes
-stdin:
- :>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
- :>x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
- :>x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-expected-stdout:
- 1 = bar .
- 2 = bar .
- 3 = bar .
----
-name: mkshiop-1
-description:
- Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
- read -u10 foo 10<<< bar
- echo x$foo
-expected-stdout:
- xbar
----
-name: mkshiop-2
-description:
- Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
- exec 12>foo
- print -u12 bar
- echo baz >&12
- cat foo
-expected-stdout:
- bar
- baz
----
name: oksh-eval
description:
Check expansions.
@@ -9714,7 +11051,7 @@
stdin:
print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
- done >env; chmod +x env; PATH=.:$PATH
+ done >env; chmod +x env; PATH=.$PATHSEP$PATH
function k {
if [ x$FOO != xbar ]; then
echo 1
@@ -9775,41 +11112,40 @@
description:
Verify that file descriptors > 2 are private for Korn shells
AT&T ksh93 does this still, which means we must keep it as well
-category: shell:legacy-no
-file-setup: file 644 "test.sh"
- echo >&3 Fowl
+ XXX fails on some old Perl installations
+need-pass: no
stdin:
- exec 3>&1
- "$__progname" test.sh
+ cat >cld <<-EOF
+ #!$__perlname
+ open(my \$fh, ">&", 9) or die "E: open \$!";
+ syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!";
+ EOF
+ chmod +x cld
+ exec 9>&1
+ ./cld
expected-exit: e != 0
expected-stderr-pattern:
- /bad file descriptor/
+ /E: open /
---
name: fd-cloexec-2
description:
Verify that file descriptors > 2 are not private for POSIX shells
See Debian Bug #154540, Closes: #499139
-file-setup: file 644 "test.sh"
- echo >&3 Fowl
+ XXX fails on some old Perl installations
+need-pass: no
stdin:
- test -n "$POSH_VERSION" || set -o sh
- exec 3>&1
- "$__progname" test.sh
+ cat >cld <<-EOF
+ #!$__perlname
+ open(my \$fh, ">&", 9) or die "E: open \$!";
+ syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!";
+ EOF
+ chmod +x cld
+ test -n "$POSH_VERSION" || set -o posix
+ exec 9>&1
+ ./cld
expected-stdout:
Fowl
---
-name: fd-cloexec-3
-description:
- Verify that file descriptors > 2 are not private for LEGACY KSH
-category: shell:legacy-yes
-file-setup: file 644 "test.sh"
- echo >&3 Fowl
-stdin:
- exec 3>&1
- "$__progname" test.sh
-expected-stdout:
- Fowl
----
name: comsub-1a
description:
COMSUB are now parsed recursively, so this works
@@ -9874,7 +11210,7 @@
is a must (a non-recursive parser cannot pass all three of
these test cases, especially the ‘#’ is difficult)
stdin:
- print '#!'"$__progname"'\necho 1234' >id; chmod +x id; PATH=.:$PATH
+ print '#!'"$__progname"'\necho 1234' >id; chmod +x id; PATH=.$PATHSEP$PATH
echo $(typeset -i10 x=16#20; echo $x)
echo $(typeset -Uui16 x=16#$(id -u)
) .
@@ -9898,10 +11234,10 @@
x() {
case $1 in
(u)
- echo x
+ \echo x
;|
(*)
- echo $1
+ \echo $1
;;
esac
}
@@ -9909,20 +11245,36 @@
name: comsub-5
description:
Check COMSUB works with aliases (does not expand them twice)
+ and reentrancy safety
stdin:
print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
chmod +x pfn
alias echo='echo a'
foo() {
+ echo moo
./pfn "$(echo foo)"
}
./pfn "$(echo b)"
+ typeset -f foo >x
+ cat x
+ foo
+ . ./x
typeset -f foo
+ foo
expected-stdout:
a b
foo() {
- ./pfn "$(echo foo )"
+ \echo a moo
+ ./pfn "$(\echo a foo )"
}
+ a moo
+ a foo
+ foo() {
+ \echo a moo
+ ./pfn "$(\echo a foo )"
+ }
+ a moo
+ a foo
---
name: comsub-torture
description:
@@ -10033,56 +11385,56 @@
vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4"
}
inline_TCOM() {
- vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4"
+ vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4"
}
function comsub_TCOM { x=$(
vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4"
); }
function comsub_TCOM {
- x=$(vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" )
+ x=$(vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" )
}
function reread_TCOM { x=$((
vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4"
)|tr u x); }
function reread_TCOM {
- x=$(( vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" ) | tr u x )
+ x=$( ( vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" ) | \tr u x )
}
inline_TPAREN_TPIPE_TLIST() {
(echo $foo | tr -dc 0-9; echo)
}
inline_TPAREN_TPIPE_TLIST() {
- ( echo $foo | tr -dc 0-9
- echo )
+ ( \echo $foo | \tr -dc 0-9
+ \echo )
}
function comsub_TPAREN_TPIPE_TLIST { x=$(
(echo $foo | tr -dc 0-9; echo)
); }
function comsub_TPAREN_TPIPE_TLIST {
- x=$(( echo $foo | tr -dc 0-9 ; echo ) )
+ x=$( ( \echo $foo | \tr -dc 0-9 ; \echo ) )
}
function reread_TPAREN_TPIPE_TLIST { x=$((
(echo $foo | tr -dc 0-9; echo)
)|tr u x); }
function reread_TPAREN_TPIPE_TLIST {
- x=$(( ( echo $foo | tr -dc 0-9 ; echo ) ) | tr u x )
+ x=$( ( ( \echo $foo | \tr -dc 0-9 ; \echo ) ) | \tr u x )
}
inline_TAND_TOR() {
cmd && echo ja || echo nein
}
inline_TAND_TOR() {
- cmd && echo ja || echo nein
+ \cmd && \echo ja || \echo nein
}
function comsub_TAND_TOR { x=$(
cmd && echo ja || echo nein
); }
function comsub_TAND_TOR {
- x=$(cmd && echo ja || echo nein )
+ x=$(\cmd && \echo ja || \echo nein )
}
function reread_TAND_TOR { x=$((
cmd && echo ja || echo nein
)|tr u x); }
function reread_TAND_TOR {
- x=$(( cmd && echo ja || echo nein ) | tr u x )
+ x=$( ( \cmd && \echo ja || \echo nein ) | \tr u x )
}
inline_TSELECT() {
select file in *; do echo "<$file>" ; break ; done
@@ -10090,8 +11442,8 @@
inline_TSELECT() {
select file in *
do
- echo "<$file>"
- break
+ \echo "<$file>"
+ \break
done
}
function comsub_TSELECT { x=$(
@@ -10098,13 +11450,13 @@
select file in *; do echo "<$file>" ; break ; done
); }
function comsub_TSELECT {
- x=$(select file in * ; do echo "<$file>" ; break ; done )
+ x=$(select file in * ; do \echo "<$file>" ; \break ; done )
}
function reread_TSELECT { x=$((
select file in *; do echo "<$file>" ; break ; done
)|tr u x); }
function reread_TSELECT {
- x=$(( select file in * ; do echo "<$file>" ; break ; done ) | tr u x )
+ x=$( ( select file in * ; do \echo "<$file>" ; \break ; done ) | \tr u x )
}
inline_TFOR_TTIME() {
time for i in {1,2,3} ; do echo $i ; done
@@ -10112,7 +11464,7 @@
inline_TFOR_TTIME() {
time for i in {1,2,3}
do
- echo $i
+ \echo $i
done
}
function comsub_TFOR_TTIME { x=$(
@@ -10119,13 +11471,13 @@
time for i in {1,2,3} ; do echo $i ; done
); }
function comsub_TFOR_TTIME {
- x=$(time for i in {1,2,3} ; do echo $i ; done )
+ x=$(time for i in {1,2,3} ; do \echo $i ; done )
}
function reread_TFOR_TTIME { x=$((
time for i in {1,2,3} ; do echo $i ; done
)|tr u x); }
function reread_TFOR_TTIME {
- x=$(( time for i in {1,2,3} ; do echo $i ; done ) | tr u x )
+ x=$( ( time for i in {1,2,3} ; do \echo $i ; done ) | \tr u x )
}
inline_TCASE() {
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
@@ -10133,13 +11485,13 @@
inline_TCASE() {
case $foo in
(1)
- echo eins
+ \echo eins
;&
(2)
- echo zwei
+ \echo zwei
;|
(*)
- echo kann net bis drei zählen
+ \echo kann net bis drei zählen
;;
esac
}
@@ -10147,13 +11499,13 @@
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
); }
function comsub_TCASE {
- x=$(case $foo in (1) echo eins ;& (2) echo zwei ;| (*) echo kann net bis drei zählen ;; esac )
+ x=$(case $foo in (1) \echo eins ;& (2) \echo zwei ;| (*) \echo kann net bis drei zählen ;; esac )
}
function reread_TCASE { x=$((
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
)|tr u x); }
function reread_TCASE {
- x=$(( case $foo in (1) echo eins ;& (2) echo zwei ;| (*) echo kann net bis drei zählen ;; esac ) | tr u x )
+ x=$( ( case $foo in (1) \echo eins ;& (2) \echo zwei ;| (*) \echo kann net bis drei zählen ;; esac ) | \tr u x )
}
inline_TIF_TBANG_TDBRACKET_TELIF() {
if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi
@@ -10161,12 +11513,12 @@
inline_TIF_TBANG_TDBRACKET_TELIF() {
if ! [[ 1 = 1 ]]
then
- echo eins
+ \echo eins
elif [[ 1 = 2 ]]
then
- echo zwei
+ \echo zwei
else
- echo drei
+ \echo drei
fi
}
function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
@@ -10173,13 +11525,13 @@
if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi
); }
function comsub_TIF_TBANG_TDBRACKET_TELIF {
- x=$(if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi )
+ x=$(if ! [[ 1 = 1 ]] ; then \echo eins ; elif [[ 1 = 2 ]] ; then \echo zwei ; else \echo drei ; fi )
}
function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi
)|tr u x); }
function reread_TIF_TBANG_TDBRACKET_TELIF {
- x=$(( if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) | tr u x )
+ x=$( ( if ! [[ 1 = 1 ]] ; then \echo eins ; elif [[ 1 = 2 ]] ; then \echo zwei ; else \echo drei ; fi ) | \tr u x )
}
inline_TWHILE() {
i=1; while (( i < 10 )); do echo $i; let ++i; done
@@ -10186,10 +11538,12 @@
}
inline_TWHILE() {
i=1
- while let] " i < 10 "
+ while {
+ \\builtin let " i < 10 "
+ }
do
- echo $i
- let ++i
+ \echo $i
+ \let ++i
done
}
function comsub_TWHILE { x=$(
@@ -10196,13 +11550,13 @@
i=1; while (( i < 10 )); do echo $i; let ++i; done
); }
function comsub_TWHILE {
- x=$(i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done )
+ x=$(i=1 ; while { \\builtin let " i < 10 " ; } ; do \echo $i ; \let ++i ; done )
}
function reread_TWHILE { x=$((
i=1; while (( i < 10 )); do echo $i; let ++i; done
)|tr u x); }
function reread_TWHILE {
- x=$(( i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) | tr u x )
+ x=$( ( i=1 ; while { \\builtin let " i < 10 " ; } ; do \echo $i ; \let ++i ; done ) | \tr u x )
}
inline_TUNTIL() {
i=10; until (( !--i )) ; do echo $i; done
@@ -10209,9 +11563,11 @@
}
inline_TUNTIL() {
i=10
- until let] " !--i "
+ until {
+ \\builtin let " !--i "
+ }
do
- echo $i
+ \echo $i
done
}
function comsub_TUNTIL { x=$(
@@ -10218,32 +11574,32 @@
i=10; until (( !--i )) ; do echo $i; done
); }
function comsub_TUNTIL {
- x=$(i=10 ; until let] " !--i " ; do echo $i ; done )
+ x=$(i=10 ; until { \\builtin let " !--i " ; } ; do \echo $i ; done )
}
function reread_TUNTIL { x=$((
i=10; until (( !--i )) ; do echo $i; done
)|tr u x); }
function reread_TUNTIL {
- x=$(( i=10 ; until let] " !--i " ; do echo $i ; done ) | tr u x )
+ x=$( ( i=10 ; until { \\builtin let " !--i " ; } ; do \echo $i ; done ) | \tr u x )
}
inline_TCOPROC() {
cat * |& ls
}
inline_TCOPROC() {
- cat * |&
- ls
+ \cat * |&
+ \ls
}
function comsub_TCOPROC { x=$(
cat * |& ls
); }
function comsub_TCOPROC {
- x=$(cat * |& ls )
+ x=$(\cat * |& \ls )
}
function reread_TCOPROC { x=$((
cat * |& ls
)|tr u x); }
function reread_TCOPROC {
- x=$(( cat * |& ls ) | tr u x )
+ x=$( ( \cat * |& \ls ) | \tr u x )
}
inline_TFUNCT_TBRACE_TASYNC() {
function korn { echo eins; echo zwei ; }
@@ -10251,11 +11607,11 @@
}
inline_TFUNCT_TBRACE_TASYNC() {
function korn {
- echo eins
- echo zwei
+ \echo eins
+ \echo zwei
}
bourne() {
- logger * &
+ \logger * &
}
}
function comsub_TFUNCT_TBRACE_TASYNC { x=$(
@@ -10263,7 +11619,7 @@
bourne () { logger * & }
); }
function comsub_TFUNCT_TBRACE_TASYNC {
- x=$(function korn { echo eins ; echo zwei ; } ; bourne() { logger * & } )
+ x=$(function korn { \echo eins ; \echo zwei ; } ; bourne() { \logger * & } )
}
function reread_TFUNCT_TBRACE_TASYNC { x=$((
function korn { echo eins; echo zwei ; }
@@ -10270,25 +11626,25 @@
bourne () { logger * & }
)|tr u x); }
function reread_TFUNCT_TBRACE_TASYNC {
- x=$(( function korn { echo eins ; echo zwei ; } ; bourne() { logger * & } ) | tr u x )
+ x=$( ( function korn { \echo eins ; \echo zwei ; } ; bourne() { \logger * & } ) | \tr u x )
}
inline_IOREAD_IOCAT() {
tr x u 0<foo >>bar
}
inline_IOREAD_IOCAT() {
- tr x u <foo >>bar
+ \tr x u <foo >>bar
}
function comsub_IOREAD_IOCAT { x=$(
tr x u 0<foo >>bar
); }
function comsub_IOREAD_IOCAT {
- x=$(tr x u <foo >>bar )
+ x=$(\tr x u <foo >>bar )
}
function reread_IOREAD_IOCAT { x=$((
tr x u 0<foo >>bar
)|tr u x); }
function reread_IOREAD_IOCAT {
- x=$(( tr x u <foo >>bar ) | tr u x )
+ x=$( ( \tr x u <foo >>bar ) | \tr u x )
}
inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
cat >|bar <<'EOFN'
@@ -10296,7 +11652,7 @@
EOFN
}
inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
- cat >|bar <<"EOFN"
+ \cat >|bar <<"EOFN"
foo
EOFN
@@ -10307,7 +11663,7 @@
EOFN
); }
function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
- x=$(cat >|bar <<"EOFN"
+ x=$(\cat >|bar <<"EOFN"
foo
EOFN
)
@@ -10318,10 +11674,10 @@
EOFN
)|tr u x); }
function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
- x=$(( cat >|bar <<"EOFN"
+ x=$( ( \cat >|bar <<"EOFN"
foo
EOFN
- ) | tr u x )
+ ) | \tr u x )
}
inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
cat 1>bar <<-EOFI
@@ -10329,7 +11685,7 @@
EOFI
}
inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
- cat >bar <<-EOFI
+ \cat >bar <<-EOFI
foo
EOFI
@@ -10340,7 +11696,7 @@
EOFI
); }
function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
- x=$(cat >bar <<-EOFI
+ x=$(\cat >bar <<-EOFI
foo
EOFI
)
@@ -10351,46 +11707,46 @@
EOFI
)|tr u x); }
function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
- x=$(( cat >bar <<-EOFI
+ x=$( ( \cat >bar <<-EOFI
foo
EOFI
- ) | tr u x )
+ ) | \tr u x )
}
inline_IORDWR_IODUP() {
sh 1<>/dev/console 0<&1 2>&1
}
inline_IORDWR_IODUP() {
- sh 1<>/dev/console <&1 2>&1
+ \sh 1<>/dev/console <&1 2>&1
}
function comsub_IORDWR_IODUP { x=$(
sh 1<>/dev/console 0<&1 2>&1
); }
function comsub_IORDWR_IODUP {
- x=$(sh 1<>/dev/console <&1 2>&1 )
+ x=$(\sh 1<>/dev/console <&1 2>&1 )
}
function reread_IORDWR_IODUP { x=$((
sh 1<>/dev/console 0<&1 2>&1
)|tr u x); }
function reread_IORDWR_IODUP {
- x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x )
+ x=$( ( \sh 1<>/dev/console <&1 2>&1 ) | \tr u x )
}
inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
}
inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
- echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;}
+ \echo $(\true ) $((1+ 2)) ${ \: ;} ${|REPLY=x ;}
}
function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$(
echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
); }
function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB {
- x=$(echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} )
+ x=$(\echo $(\true ) $((1+ 2)) ${ \: ;} ${|REPLY=x ;} )
}
function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$((
echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;}
)|tr u x); }
function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB {
- x=$(( echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | tr u x )
+ x=$( ( \echo $(\true ) $((1+ 2)) ${ \: ;} ${|REPLY=x ;} ) | \tr u x )
}
inline_QCHAR_OQUOTE_CQUOTE() {
echo fo\ob\"a\`r\'b\$az
@@ -10398,9 +11754,9 @@
echo 'fo\ob\"a\`r'\''b\$az'
}
inline_QCHAR_OQUOTE_CQUOTE() {
- echo fo\ob\"a\`r\'b\$az
- echo "fo\ob\"a\`r\'b\$az"
- echo "fo\\ob\\\"a\\\`r"\'"b\\\$az"
+ \echo fo\ob\"a\`r\'b\$az
+ \echo "fo\ob\"a\`r\'b\$az"
+ \echo "fo\\ob\\\"a\\\`r"\'"b\\\$az"
}
function comsub_QCHAR_OQUOTE_CQUOTE { x=$(
echo fo\ob\"a\`r\'b\$az
@@ -10408,7 +11764,7 @@
echo 'fo\ob\"a\`r'\''b\$az'
); }
function comsub_QCHAR_OQUOTE_CQUOTE {
- x=$(echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" )
+ x=$(\echo fo\ob\"a\`r\'b\$az ; \echo "fo\ob\"a\`r\'b\$az" ; \echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" )
}
function reread_QCHAR_OQUOTE_CQUOTE { x=$((
echo fo\ob\"a\`r\'b\$az
@@ -10416,7 +11772,7 @@
echo 'fo\ob\"a\`r'\''b\$az'
)|tr u x); }
function reread_QCHAR_OQUOTE_CQUOTE {
- x=$(( echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) | tr u x )
+ x=$( ( \echo fo\ob\"a\`r\'b\$az ; \echo "fo\ob\"a\`r\'b\$az" ; \echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) | \tr u x )
}
inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
@@ -10434,7 +11790,7 @@
[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
)|tr u x); }
function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT {
- x=$(( [[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) | tr u x )
+ x=$( ( [[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) | \tr u x )
}
inline_heredoc_closed() {
x=$(cat <<EOFN
@@ -10442,11 +11798,11 @@
EOFN); echo $x
}
inline_heredoc_closed() {
- x=$(cat <<EOFN
+ x=$(\cat <<EOFN
note there must be no space between EOFN and )
EOFN
)
- echo $x
+ \echo $x
}
function comsub_heredoc_closed { x=$(
x=$(cat <<EOFN
@@ -10454,10 +11810,10 @@
EOFN); echo $x
); }
function comsub_heredoc_closed {
- x=$(x=$(cat <<EOFN
+ x=$(x=$(\cat <<EOFN
note there must be no space between EOFN and )
EOFN
- ) ; echo $x )
+ ) ; \echo $x )
}
function reread_heredoc_closed { x=$((
x=$(cat <<EOFN
@@ -10465,10 +11821,10 @@
EOFN); echo $x
)|tr u x); }
function reread_heredoc_closed {
- x=$(( x=$(cat <<EOFN
+ x=$( ( x=$(\cat <<EOFN
note there must be no space between EOFN and )
EOFN
- ) ; echo $x ) | tr u x )
+ ) ; \echo $x ) | \tr u x )
}
inline_heredoc_space() {
x=$(cat <<EOFN\
@@ -10476,11 +11832,11 @@
EOFN ); echo $x
}
inline_heredoc_space() {
- x=$(cat <<EOFN\
+ x=$(\cat <<EOFN\
note the space between EOFN and ) is actually part of the here document marker
EOFN
)
- echo $x
+ \echo $x
}
function comsub_heredoc_space { x=$(
x=$(cat <<EOFN\
@@ -10488,10 +11844,10 @@
EOFN ); echo $x
); }
function comsub_heredoc_space {
- x=$(x=$(cat <<EOFN\
+ x=$(x=$(\cat <<EOFN\
note the space between EOFN and ) is actually part of the here document marker
EOFN
- ) ; echo $x )
+ ) ; \echo $x )
}
function reread_heredoc_space { x=$((
x=$(cat <<EOFN\
@@ -10499,10 +11855,10 @@
EOFN ); echo $x
)|tr u x); }
function reread_heredoc_space {
- x=$(( x=$(cat <<EOFN\
+ x=$( ( x=$(\cat <<EOFN\
note the space between EOFN and ) is actually part of the here document marker
EOFN
- ) ; echo $x ) | tr u x )
+ ) ; \echo $x ) | \tr u x )
}
inline_patch_motd() {
x=$(sysctl -n kern.version | sed 1q)
@@ -10521,8 +11877,8 @@
fi
}
inline_patch_motd() {
- x=$(sysctl -n kern.version | sed 1q )
- [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
+ x=$(\sysctl -n kern.version | \sed 1q )
+ [[ -s /etc/motd && "$([[ "$(\head -1 /etc/motd )" != $x ]] && \ed -s /etc/motd 2>&1 <<-EOF
1,/^\$/d
0a
$x
@@ -10530,11 +11886,11 @@
.
wq
EOF
- )" = @(?) ]] && rm -f /etc/motd
+ )" = @(?) ]] && \rm -f /etc/motd
if [[ ! -s /etc/motd ]]
then
- install -c -o root -g wheel -m 664 /dev/null /etc/motd
- print -- "$x\n" >/etc/motd
+ \install -c -o root -g wheel -m 664 /dev/null /etc/motd
+ \print -- "$x\n" >/etc/motd
fi
}
function comsub_patch_motd { x=$(
@@ -10554,7 +11910,7 @@
fi
); }
function comsub_patch_motd {
- x=$(x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
+ x=$(x=$(\sysctl -n kern.version | \sed 1q ) ; [[ -s /etc/motd && "$([[ "$(\head -1 /etc/motd )" != $x ]] && \ed -s /etc/motd 2>&1 <<-EOF
1,/^\$/d
0a
$x
@@ -10562,7 +11918,7 @@
.
wq
EOF
- )" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi )
+ )" = @(?) ]] && \rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then \install -c -o root -g wheel -m 664 /dev/null /etc/motd ; \print -- "$x\n" >/etc/motd ; fi )
}
function reread_patch_motd { x=$((
x=$(sysctl -n kern.version | sed 1q)
@@ -10581,7 +11937,7 @@
fi
)|tr u x); }
function reread_patch_motd {
- x=$(( x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
+ x=$( ( x=$(\sysctl -n kern.version | \sed 1q ) ; [[ -s /etc/motd && "$([[ "$(\head -1 /etc/motd )" != $x ]] && \ed -s /etc/motd 2>&1 <<-EOF
1,/^\$/d
0a
$x
@@ -10589,7 +11945,7 @@
.
wq
EOF
- )" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) | tr u x )
+ )" = @(?) ]] && \rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then \install -c -o root -g wheel -m 664 /dev/null /etc/motd ; \print -- "$x\n" >/etc/motd ; fi ) | \tr u x )
}
inline_wdarrassign() {
case x in
@@ -10600,7 +11956,7 @@
case x in
(x)
a+=b
- set -A c+ -- d e
+ \\builtin set -A c+ -- d e
;;
esac
}
@@ -10610,7 +11966,7 @@
esac
); }
function comsub_wdarrassign {
- x=$(case x in (x) a+=b ; set -A c+ -- d e ;; esac )
+ x=$(case x in (x) a+=b ; \\builtin set -A c+ -- d e ;; esac )
}
function reread_wdarrassign { x=$((
case x in
@@ -10618,7 +11974,7 @@
esac
)|tr u x); }
function reread_wdarrassign {
- x=$(( case x in (x) a+=b ; set -A c+ -- d e ;; esac ) | tr u x )
+ x=$( ( case x in (x) a+=b ; \\builtin set -A c+ -- d e ;; esac ) | \tr u x )
}
---
name: comsub-torture-io
@@ -10685,56 +12041,56 @@
vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" >&3
}
inline_TCOM() {
- vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" >&3
+ vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" >&3
}
function comsub_TCOM { x=$(
vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" >&3
); }
function comsub_TCOM {
- x=$(vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" >&3 )
+ x=$(vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" >&3 )
}
function reread_TCOM { x=$((
vara=1 varb='2 3' cmd arg1 $arg2 "$arg3 4" >&3
)|tr u x); }
function reread_TCOM {
- x=$(( vara=1 varb="2 3" cmd arg1 $arg2 "$arg3 4" >&3 ) | tr u x )
+ x=$( ( vara=1 varb="2 3" \cmd arg1 $arg2 "$arg3 4" >&3 ) | \tr u x )
}
inline_TPAREN_TPIPE_TLIST() {
(echo $foo | tr -dc 0-9 >&3; echo >&3) >&3
}
inline_TPAREN_TPIPE_TLIST() {
- ( echo $foo | tr -dc 0-9 >&3
- echo >&3 ) >&3
+ ( \echo $foo | \tr -dc 0-9 >&3
+ \echo >&3 ) >&3
}
function comsub_TPAREN_TPIPE_TLIST { x=$(
(echo $foo | tr -dc 0-9 >&3; echo >&3) >&3
); }
function comsub_TPAREN_TPIPE_TLIST {
- x=$(( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 )
+ x=$( ( \echo $foo | \tr -dc 0-9 >&3 ; \echo >&3 ) >&3 )
}
function reread_TPAREN_TPIPE_TLIST { x=$((
(echo $foo | tr -dc 0-9 >&3; echo >&3) >&3
)|tr u x); }
function reread_TPAREN_TPIPE_TLIST {
- x=$(( ( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) | tr u x )
+ x=$( ( ( \echo $foo | \tr -dc 0-9 >&3 ; \echo >&3 ) >&3 ) | \tr u x )
}
inline_TAND_TOR() {
cmd >&3 && >&3 echo ja || echo >&3 nein
}
inline_TAND_TOR() {
- cmd >&3 && echo ja >&3 || echo nein >&3
+ \cmd >&3 && \echo ja >&3 || \echo nein >&3
}
function comsub_TAND_TOR { x=$(
cmd >&3 && >&3 echo ja || echo >&3 nein
); }
function comsub_TAND_TOR {
- x=$(cmd >&3 && echo ja >&3 || echo nein >&3 )
+ x=$(\cmd >&3 && \echo ja >&3 || \echo nein >&3 )
}
function reread_TAND_TOR { x=$((
cmd >&3 && >&3 echo ja || echo >&3 nein
)|tr u x); }
function reread_TAND_TOR {
- x=$(( cmd >&3 && echo ja >&3 || echo nein >&3 ) | tr u x )
+ x=$( ( \cmd >&3 && \echo ja >&3 || \echo nein >&3 ) | \tr u x )
}
inline_TSELECT() {
select file in *; do echo "<$file>" ; break >&3 ; done >&3
@@ -10742,8 +12098,8 @@
inline_TSELECT() {
select file in *
do
- echo "<$file>"
- break >&3
+ \echo "<$file>"
+ \break >&3
done >&3
}
function comsub_TSELECT { x=$(
@@ -10750,13 +12106,13 @@
select file in *; do echo "<$file>" ; break >&3 ; done >&3
); }
function comsub_TSELECT {
- x=$(select file in * ; do echo "<$file>" ; break >&3 ; done >&3 )
+ x=$(select file in * ; do \echo "<$file>" ; \break >&3 ; done >&3 )
}
function reread_TSELECT { x=$((
select file in *; do echo "<$file>" ; break >&3 ; done >&3
)|tr u x); }
function reread_TSELECT {
- x=$(( select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) | tr u x )
+ x=$( ( select file in * ; do \echo "<$file>" ; \break >&3 ; done >&3 ) | \tr u x )
}
inline_TFOR_TTIME() {
for i in {1,2,3} ; do time >&3 echo $i ; done >&3
@@ -10764,7 +12120,7 @@
inline_TFOR_TTIME() {
for i in {1,2,3}
do
- time echo $i >&3
+ time \echo $i >&3
done >&3
}
function comsub_TFOR_TTIME { x=$(
@@ -10771,13 +12127,13 @@
for i in {1,2,3} ; do time >&3 echo $i ; done >&3
); }
function comsub_TFOR_TTIME {
- x=$(for i in {1,2,3} ; do time echo $i >&3 ; done >&3 )
+ x=$(for i in {1,2,3} ; do time \echo $i >&3 ; done >&3 )
}
function reread_TFOR_TTIME { x=$((
for i in {1,2,3} ; do time >&3 echo $i ; done >&3
)|tr u x); }
function reread_TFOR_TTIME {
- x=$(( for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) | tr u x )
+ x=$( ( for i in {1,2,3} ; do time \echo $i >&3 ; done >&3 ) | \tr u x )
}
inline_TCASE() {
case $foo in 1) echo eins >&3;& 2) echo zwei >&3 ;| *) echo kann net bis drei zählen >&3;; esac >&3
@@ -10785,13 +12141,13 @@
inline_TCASE() {
case $foo in
(1)
- echo eins >&3
+ \echo eins >&3
;&
(2)
- echo zwei >&3
+ \echo zwei >&3
;|
(*)
- echo kann net bis drei zählen >&3
+ \echo kann net bis drei zählen >&3
;;
esac >&3
}
@@ -10799,13 +12155,13 @@
case $foo in 1) echo eins >&3;& 2) echo zwei >&3 ;| *) echo kann net bis drei zählen >&3;; esac >&3
); }
function comsub_TCASE {
- x=$(case $foo in (1) echo eins >&3 ;& (2) echo zwei >&3 ;| (*) echo kann net bis drei zählen >&3 ;; esac >&3 )
+ x=$(case $foo in (1) \echo eins >&3 ;& (2) \echo zwei >&3 ;| (*) \echo kann net bis drei zählen >&3 ;; esac >&3 )
}
function reread_TCASE { x=$((
case $foo in 1) echo eins >&3;& 2) echo zwei >&3 ;| *) echo kann net bis drei zählen >&3;; esac >&3
)|tr u x); }
function reread_TCASE {
- x=$(( case $foo in (1) echo eins >&3 ;& (2) echo zwei >&3 ;| (*) echo kann net bis drei zählen >&3 ;; esac >&3 ) | tr u x )
+ x=$( ( case $foo in (1) \echo eins >&3 ;& (2) \echo zwei >&3 ;| (*) \echo kann net bis drei zählen >&3 ;; esac >&3 ) | \tr u x )
}
inline_TIF_TBANG_TDBRACKET_TELIF() {
if ! [[ 1 = 1 ]] >&3 ; then echo eins; elif [[ 1 = 2 ]] >&3; then echo zwei ;else echo drei; fi >&3
@@ -10813,12 +12169,12 @@
inline_TIF_TBANG_TDBRACKET_TELIF() {
if ! [[ 1 = 1 ]] >&3
then
- echo eins
+ \echo eins
elif [[ 1 = 2 ]] >&3
then
- echo zwei
+ \echo zwei
else
- echo drei
+ \echo drei
fi >&3
}
function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
@@ -10825,13 +12181,13 @@
if ! [[ 1 = 1 ]] >&3 ; then echo eins; elif [[ 1 = 2 ]] >&3; then echo zwei ;else echo drei; fi >&3
); }
function comsub_TIF_TBANG_TDBRACKET_TELIF {
- x=$(if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 )
+ x=$(if ! [[ 1 = 1 ]] >&3 ; then \echo eins ; elif [[ 1 = 2 ]] >&3 ; then \echo zwei ; else \echo drei ; fi >&3 )
}
function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
if ! [[ 1 = 1 ]] >&3 ; then echo eins; elif [[ 1 = 2 ]] >&3; then echo zwei ;else echo drei; fi >&3
)|tr u x); }
function reread_TIF_TBANG_TDBRACKET_TELIF {
- x=$(( if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) | tr u x )
+ x=$( ( if ! [[ 1 = 1 ]] >&3 ; then \echo eins ; elif [[ 1 = 2 ]] >&3 ; then \echo zwei ; else \echo drei ; fi >&3 ) | \tr u x )
}
inline_TWHILE() {
i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
@@ -10838,10 +12194,12 @@
}
inline_TWHILE() {
i=1
- while let] " i < 10 " >&3
+ while {
+ \\builtin let " i < 10 "
+ } >&3
do
- echo $i
- let ++i
+ \echo $i
+ \let ++i
done >&3
}
function comsub_TWHILE { x=$(
@@ -10848,13 +12206,13 @@
i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
); }
function comsub_TWHILE {
- x=$(i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 )
+ x=$(i=1 ; while { \\builtin let " i < 10 " ; } >&3 ; do \echo $i ; \let ++i ; done >&3 )
}
function reread_TWHILE { x=$((
i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
)|tr u x); }
function reread_TWHILE {
- x=$(( i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) | tr u x )
+ x=$( ( i=1 ; while { \\builtin let " i < 10 " ; } >&3 ; do \echo $i ; \let ++i ; done >&3 ) | \tr u x )
}
inline_TUNTIL() {
i=10; until (( !--i )) >&3 ; do echo $i; done >&3
@@ -10861,9 +12219,11 @@
}
inline_TUNTIL() {
i=10
- until let] " !--i " >&3
+ until {
+ \\builtin let " !--i "
+ } >&3
do
- echo $i
+ \echo $i
done >&3
}
function comsub_TUNTIL { x=$(
@@ -10870,32 +12230,32 @@
i=10; until (( !--i )) >&3 ; do echo $i; done >&3
); }
function comsub_TUNTIL {
- x=$(i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 )
+ x=$(i=10 ; until { \\builtin let " !--i " ; } >&3 ; do \echo $i ; done >&3 )
}
function reread_TUNTIL { x=$((
i=10; until (( !--i )) >&3 ; do echo $i; done >&3
)|tr u x); }
function reread_TUNTIL {
- x=$(( i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) | tr u x )
+ x=$( ( i=10 ; until { \\builtin let " !--i " ; } >&3 ; do \echo $i ; done >&3 ) | \tr u x )
}
inline_TCOPROC() {
cat * >&3 |& >&3 ls
}
inline_TCOPROC() {
- cat * >&3 |&
- ls >&3
+ \cat * >&3 |&
+ \ls >&3
}
function comsub_TCOPROC { x=$(
cat * >&3 |& >&3 ls
); }
function comsub_TCOPROC {
- x=$(cat * >&3 |& ls >&3 )
+ x=$(\cat * >&3 |& \ls >&3 )
}
function reread_TCOPROC { x=$((
cat * >&3 |& >&3 ls
)|tr u x); }
function reread_TCOPROC {
- x=$(( cat * >&3 |& ls >&3 ) | tr u x )
+ x=$( ( \cat * >&3 |& \ls >&3 ) | \tr u x )
}
inline_TFUNCT_TBRACE_TASYNC() {
function korn { echo eins; echo >&3 zwei ; }
@@ -10903,11 +12263,11 @@
}
inline_TFUNCT_TBRACE_TASYNC() {
function korn {
- echo eins
- echo zwei >&3
+ \echo eins
+ \echo zwei >&3
}
bourne() {
- logger * >&3 &
+ \logger * >&3 &
}
}
function comsub_TFUNCT_TBRACE_TASYNC { x=$(
@@ -10915,7 +12275,7 @@
bourne () { logger * >&3 & }
); }
function comsub_TFUNCT_TBRACE_TASYNC {
- x=$(function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 & } )
+ x=$(function korn { \echo eins ; \echo zwei >&3 ; } ; bourne() { \logger * >&3 & } )
}
function reread_TFUNCT_TBRACE_TASYNC { x=$((
function korn { echo eins; echo >&3 zwei ; }
@@ -10922,25 +12282,25 @@
bourne () { logger * >&3 & }
)|tr u x); }
function reread_TFUNCT_TBRACE_TASYNC {
- x=$(( function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 & } ) | tr u x )
+ x=$( ( function korn { \echo eins ; \echo zwei >&3 ; } ; bourne() { \logger * >&3 & } ) | \tr u x )
}
inline_COMSUB_EXPRSUB() {
echo $(true >&3) $((1+ 2))
}
inline_COMSUB_EXPRSUB() {
- echo $(true >&3 ) $((1+ 2))
+ \echo $(\true >&3 ) $((1+ 2))
}
function comsub_COMSUB_EXPRSUB { x=$(
echo $(true >&3) $((1+ 2))
); }
function comsub_COMSUB_EXPRSUB {
- x=$(echo $(true >&3 ) $((1+ 2)) )
+ x=$(\echo $(\true >&3 ) $((1+ 2)) )
}
function reread_COMSUB_EXPRSUB { x=$((
echo $(true >&3) $((1+ 2))
)|tr u x); }
function reread_COMSUB_EXPRSUB {
- x=$(( echo $(true >&3 ) $((1+ 2)) ) | tr u x )
+ x=$( ( \echo $(\true >&3 ) $((1+ 2)) ) | \tr u x )
}
---
name: funsub-1
@@ -10987,7 +12347,7 @@
echo "before: x<$x> y<$y> z<$z> R<$REPLY>"
x=${|
local y
- echo "begin: x<$x> y<$y> z<$z> R<$REPLY>"
+ echo "start: x<$x> y<$y> z<$z> R<$REPLY>"
x=5
y=6
z=7
@@ -11002,102 +12362,12 @@
echo ${|true;}$(true).
expected-stdout:
before: x<1> y<2> z<3> R<4>
- begin: x<1> y<> z<3> R<>
+ start: x<1> y<> z<3> R<>
end: x<5> y<6> z<7> R<8>
after: x<8> y<2> z<7> R<4>
typeset t=$'foo\n\n'
this used to segfault.
---
-name: test-stnze-1
-description:
- Check that the short form [ $x ] works
-stdin:
- i=0
- [ -n $x ]
- rv=$?; echo $((++i)) $rv
- [ $x ]
- rv=$?; echo $((++i)) $rv
- [ -n "$x" ]
- rv=$?; echo $((++i)) $rv
- [ "$x" ]
- rv=$?; echo $((++i)) $rv
- x=0
- [ -n $x ]
- rv=$?; echo $((++i)) $rv
- [ $x ]
- rv=$?; echo $((++i)) $rv
- [ -n "$x" ]
- rv=$?; echo $((++i)) $rv
- [ "$x" ]
- rv=$?; echo $((++i)) $rv
- x='1 -a 1 = 2'
- [ -n $x ]
- rv=$?; echo $((++i)) $rv
- [ $x ]
- rv=$?; echo $((++i)) $rv
- [ -n "$x" ]
- rv=$?; echo $((++i)) $rv
- [ "$x" ]
- rv=$?; echo $((++i)) $rv
-expected-stdout:
- 1 0
- 2 1
- 3 1
- 4 1
- 5 0
- 6 0
- 7 0
- 8 0
- 9 1
- 10 1
- 11 0
- 12 0
----
-name: test-stnze-2
-description:
- Check that the short form [[ $x ]] works (ksh93 extension)
-stdin:
- i=0
- [[ -n $x ]]
- rv=$?; echo $((++i)) $rv
- [[ $x ]]
- rv=$?; echo $((++i)) $rv
- [[ -n "$x" ]]
- rv=$?; echo $((++i)) $rv
- [[ "$x" ]]
- rv=$?; echo $((++i)) $rv
- x=0
- [[ -n $x ]]
- rv=$?; echo $((++i)) $rv
- [[ $x ]]
- rv=$?; echo $((++i)) $rv
- [[ -n "$x" ]]
- rv=$?; echo $((++i)) $rv
- [[ "$x" ]]
- rv=$?; echo $((++i)) $rv
- x='1 -a 1 = 2'
- [[ -n $x ]]
- rv=$?; echo $((++i)) $rv
- [[ $x ]]
- rv=$?; echo $((++i)) $rv
- [[ -n "$x" ]]
- rv=$?; echo $((++i)) $rv
- [[ "$x" ]]
- rv=$?; echo $((++i)) $rv
-expected-stdout:
- 1 1
- 2 1
- 3 1
- 4 1
- 5 0
- 6 0
- 7 0
- 8 0
- 9 0
- 10 0
- 11 0
- 12 0
----
name: event-subst-3
description:
Check that '!' substitution in noninteractive mode is ignored
@@ -11109,7 +12379,7 @@
#! /bin/sh
echo si
stdin:
- export PATH=.:$PATH
+ export PATH=.$PATHSEP$PATH
falsetto
echo yeap
!false
@@ -11139,7 +12409,7 @@
#! /bin/sh
echo si
stdin:
- export PATH=.:$PATH
+ export PATH=.$PATHSEP$PATH
falsetto
echo yeap
!false
@@ -11463,9 +12733,20 @@
expected-stdout:
fxbar 0
---
+name: better-parens-5
+description:
+ Another corner case
+stdin:
+ ( (echo 'fo o$bar' "baz\$bla\"" m\$eh) | tr a A)
+ ((echo 'fo o$bar' "baz\$bla\"" m\$eh) | tr a A)
+expected-stdout:
+ fo o$bAr bAz$blA" m$eh
+ fo o$bAr bAz$blA" m$eh
+---
name: echo-test-1
description:
Test what the echo builtin does (mksh)
+category: !shell:ebcdic-yes
stdin:
echo -n 'foo\x40bar'
echo -e '\tbaz'
@@ -11472,6 +12753,16 @@
expected-stdout:
foo at bar baz
---
+name: echo-test-1-ebcdic
+description:
+ Test what the echo builtin does (mksh)
+category: !shell:ebcdic-no
+stdin:
+ echo -n 'foo\x7Cbar'
+ echo -e '\tbaz'
+expected-stdout:
+ foo at bar baz
+---
name: echo-test-2
description:
Test what the echo builtin does (POSIX)
@@ -11502,7 +12793,7 @@
name: echo-test-3-normal
description:
Test what the echo builtin does, and test a compatibility flag.
-category: !mnbsdash
+category: !mnbsdash,!shell:ebcdic-yes
stdin:
"$__progname" -c 'echo -n 1=\\x40$1; echo -e \\x2E' -- foo bar
"$__progname" -o posix -c 'echo -n 2=\\x40$1; echo -e \\x2E' -- foo bar
@@ -11512,6 +12803,19 @@
2=\x40foo-e \x2E
3=\x40foo-e \x2E
---
+name: echo-test-3-ebcdic
+description:
+ Test what the echo builtin does, and test a compatibility flag.
+category: !mnbsdash,!shell:ebcdic-no
+stdin:
+ "$__progname" -c 'echo -n 1=\\x7C$1; echo -e \\x4B' -- foo bar
+ "$__progname" -o posix -c 'echo -n 2=\\x7C$1; echo -e \\x4B' -- foo bar
+ "$__progname" -o sh -c 'echo -n 3=\\x7C$1; echo -e \\x4B' -- foo bar
+expected-stdout:
+ 1=@foo.
+ 2=\x7Cfoo-e \x4B
+ 3=\x7Cfoo-e \x4B
+---
name: utilities-getopts-1
description:
getopts sets OPTIND correctly for unparsed option
@@ -11542,6 +12846,17 @@
done
expected-stderr-pattern: /.*-x.*option/
---
+name: utilities-getopts-3
+description:
+ Check unsetting OPTARG
+stdin:
+ set -- -x arg -y
+ getopts x:y opt && echo "${OPTARG-unset}"
+ getopts x:y opt && echo "${OPTARG-unset}"
+expected-stdout:
+ arg
+ unset
+---
name: wcswidth-1
description:
Check the new wcswidth feature
@@ -11861,10 +13176,82 @@
after 0='swc' 1='二' 2=''
= done
---
+name: command-pvV-posix-priorities
+description:
+ For POSIX compatibility, command -v should find aliases and reserved
+ words, and command -p[vV] should find aliases, reserved words, and
+ builtins over external commands.
+stdin:
+ PATH=/bin:/usr/bin
+ alias foo="bar baz"
+ bar() { :; }
+ for word in 'if' 'foo' 'bar' 'set' 'true'; do
+ command -v "$word"
+ command -pv "$word"
+ command -V "$word"
+ command -pV "$word"
+ done
+expected-stdout:
+ if
+ if
+ if is a reserved word
+ if is a reserved word
+ alias foo='bar baz'
+ alias foo='bar baz'
+ foo is an alias for 'bar baz'
+ foo is an alias for 'bar baz'
+ bar
+ bar
+ bar is a function
+ bar is a function
+ set
+ set
+ set is a special shell builtin
+ set is a special shell builtin
+ true
+ true
+ true is a shell builtin
+ true is a shell builtin
+---
+name: whence-preserve-tradition
+description:
+ This regression test is to ensure that the POSIX compatibility
+ changes for 'command' (see previous test) do not affect traditional
+ 'whence' behaviour.
+category: os:mirbsd
+stdin:
+ PATH=/bin:/usr/bin
+ alias foo="bar baz"
+ bar() { :; }
+ for word in 'if' 'foo' 'bar' 'set' 'true'; do
+ whence "$word"
+ whence -p "$word"
+ whence -v "$word"
+ whence -pv "$word"
+ done
+expected-stdout:
+ if
+ if is a reserved word
+ if not found
+ 'bar baz'
+ foo is an alias for 'bar baz'
+ foo not found
+ bar
+ bar is a function
+ bar not found
+ set
+ set is a special shell builtin
+ set not found
+ true
+ /bin/true
+ true is a shell builtin
+ true is a tracked alias for /bin/true
+---
name: duffs-device
description:
Check that the compiler did not optimise-break them
(lex.c has got a similar one in SHEREDELIM)
+category: !shell:faux-ebcdic,!shell:ebcdic-yes
stdin:
set +U
s=
@@ -11877,6 +13264,38 @@
expected-stdout:
typeset s=$'\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\E\034\035\036\037 !"#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\u00A0\u20AC\uFFFD\357\277\276\357\277\277\360\220\200\200.'
---
+name: duffs-device-ebcdic
+description:
+ Check that the compiler did not optimise-break them
+category: !shell:ebcdic-no
+stdin:
+ set +U
+ s=
+ typeset -i1 i=0
+ while (( ++i < 256 )); do
+ s+=${i#1#}
+ done
+ #s+=$'\xC2\xA0\xE2\x82\xAC\xEF\xBF\xBD\xEF\xBF\xBE\xEF\xBF\xBF\xF0\x90\x80\x80.' #XXX
+ typeset -p s
+expected-stdout:
+ typeset s=$'\001\002\003\004\t\006\007\010\011\012\v\f\r\016\017\020\021\022\023\024\n\b\027\030\031\032\033\034\035\036\037\040\041\042\043\044\045\046\E\050\051\052\053\054\055\056\a\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077 \xA0\xE2\xE4\xE0\xE1\xE3\xE5\xE7\xF1\xA2.<(+|&\xE9\xEA\xEB\xE8\xED\xEE\xEF\xEC\xDF!$*);^-/\xC2\xC4\xC0\xC1\xC3\xC5\xC7Ѧ,%_>?\xF8\xC9\xCA\xCB\xC8\xCD\xCE\xCF\xCC`:#@\175="\xD8abcdefghi\xAB\xBB\xF0\xFD\xFE\xB1\xB0jklmnopqr\xAA\xBA\xE6\xB8Ƥ\xB5~stuvwxyz\xA1\xBF\xD0[ޮ\xAC\xA3\xA5\xB7\xA9\xA7\xB6\xBC\xBD\xBEݨ\xAF]\xB4\xD7{ABCDEFGHI\xAD\xF4\xF6\xF2\xF3\xF5}JKLMNOPQR\xB9\xFB\xFC\xF9\xFA\xFF\\\xF7STUVWXYZ\xB2\xD4\xD6\xD2\xD3\xD50123456789\xB3\xDB\xDC\xD9\xDA\377'
+---
+name: duffs-device-faux-EBCDIC
+description:
+ Check that the compiler did not optimise-break them
+category: shell:faux-ebcdic
+stdin:
+ set +U
+ s=
+ typeset -i1 i=0
+ while (( ++i < 256 )); do
+ s+=${i#1#}
+ done
+ s+=$'\xC2\xA0\xE2\x82\xAC\xEF\xBF\xBD\xEF\xBF\xBE\xEF\xBF\xBF\xF0\x90\x80\x80.'
+ typeset -p s
+expected-stdout:
+ typeset s=$'\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\E\034\035\036\037 !"#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF\u00A0\u20AC\uFFFD\xEF\xBF\xBE\xEF\xBF\xBF\xF0\220\200\200.'
+---
name: stateptr-underflow
description:
This check overflows an Xrestpos stored in a short in R40
@@ -11933,7 +13352,7 @@
Copyright (C) 2002 Free Software Foundation, Inc.'
EOF
chmod +x bash
- "$__progname" -xc 'foo=$(./bash --version 2>&1 | head -1); echo "=$foo="'
+ "$__progname" -xc 'foo=$(./bash --version 2>&1 | sed q); echo "=$foo="'
expected-stdout:
=GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)=
expected-stderr-pattern:
@@ -11962,3 +13381,62 @@
[(p:sh)(f1:sh)(f2:sh)] print '(o1:shx)'
[(p:sh)(f1:sh)(f2:sh)] set +x
---
+name: fksh-flags
+description:
+ Check that FKSH functions have their own shell flags
+category: shell:legacy-no
+stdin:
+ [[ $KSH_VERSION = Version* ]] && set +B
+ function foo {
+ set +f
+ set -e
+ echo 2 "${-/s}" .
+ }
+ set -fh
+ echo 1 "${-/s}" .
+ foo
+ echo 3 "${-/s}" .
+expected-stdout:
+ 1 fh .
+ 2 eh .
+ 3 fh .
+---
+name: fksh-flags-legacy
+description:
+ Check that even FKSH functions share the shell flags
+category: shell:legacy-yes
+stdin:
+ [[ $KSH_VERSION = Version* ]] && set +B
+ foo() {
+ set +f
+ set -e
+ echo 2 "${-/s}" .
+ }
+ set -fh
+ echo 1 "${-/s}" .
+ foo
+ echo 3 "${-/s}" .
+expected-stdout:
+ 1 fh .
+ 2 eh .
+ 3 eh .
+---
+name: fsh-flags
+description:
+ Check that !FKSH functions share the shell flags
+stdin:
+ [[ $KSH_VERSION = Version* ]] && set +B
+ foo() {
+ set +f
+ set -e
+ echo 2 "${-/s}" .
+ }
+ set -fh
+ echo 1 "${-/s}" .
+ foo
+ echo 3 "${-/s}" .
+expected-stdout:
+ 1 fh .
+ 2 eh .
+ 3 eh .
+---
Modified: trunk/contrib/mksh/dot.mkshrc
===================================================================
--- trunk/contrib/mksh/dot.mkshrc 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/dot.mkshrc 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
# $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.89.2.1 2015/01/11 22:39:44 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.121 2017/08/08 21:10:21 tg Exp $
#-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015
-# Thorsten Glaser <tg at mirbsd.org>
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017
+# mirabilos <m at mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
@@ -22,40 +22,87 @@
#-
# ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
-# catch non-mksh (including lksh) trying to shell this file
-case $KSH_VERSION in
-*MIRBSD\ KSH*) ;;
-*) return 0 ;;
+# catch non-mksh, non-lksh, trying to run this file
+case ${KSH_VERSION:-} in
+*LEGACY\ KSH*|*MIRBSD\ KSH*) ;;
+*) \return 0 ;;
esac
-PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \
- 2>/dev/null)} = *([ ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \
- 2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100}
-: ${MKSH:=$(whence -p mksh)}; PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
- local e=$?
+# give MidnightBSD's laffer1 a bit of csh feeling
+function setenv {
+ if (( $# )); then
+ \\builtin eval '\\builtin export "$1"="${2:-}"'
+ else
+ \\builtin typeset -x
+ fi
+}
+# pager (not control character safe)
+smores() (
+ \\builtin set +m
+ \\builtin cat "$@" |&
+ \\builtin trap "rv=\$?; \\\\builtin kill $! >/dev/null 2>&1; \\\\builtin exit \$rv" EXIT
+ while IFS= \\builtin read -pr line; do
+ llen=${%line}
+ (( llen == -1 )) && llen=${#line}
+ (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
+ if (( (curlin += llen) >= LINES )); then
+ \\builtin print -nr -- $'\e[7m--more--\e[0m'
+ \\builtin read -u1 || \\builtin exit $?
+ [[ $REPLY = [Qq]* ]] && \\builtin exit 0
+ curlin=$llen
+ fi
+ \\builtin print -r -- "$line"
+ done
+)
+
+# customise your favourite editor here; the first one found is used
+for EDITOR in "${EDITOR:-}" jupp jstar mcedit ed vi; do
+ EDITOR=$(\\builtin whence -p "$EDITOR") || EDITOR=
+ [[ -n $EDITOR && -x $EDITOR ]] && break
+ EDITOR=
+done
+
+\\builtin alias ls=ls l='ls -F' la='l -a' ll='l -l' lo='l -alo'
+\: "${HOSTNAME:=$(\\builtin ulimit -c 0; \\builtin print -r -- $(hostname \
+ 2>/dev/null))}${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit \
+ -c 0; id -un 2>/dev/null)}${USER:=?}"
+[[ $HOSTNAME = ?(?(ip6-)localhost?(6)) ]] && HOSTNAME=nil; \\builtin unalias ls
+\\builtin export EDITOR HOSTNAME TERM USER
+
+# minimal support for lksh users
+if [[ $KSH_VERSION = *LEGACY\ KSH* ]]; then
+ PS1='$USER@${HOSTNAME%%.*}:$PWD>'
+ \\builtin return 0
+fi
+
+# mksh-specific from here
+\: "${MKSH:=$(\\builtin whence -p mksh)}${MKSH:=/bin/mksh}"
+\\builtin export MKSH
+
+# prompts
+PS4='[$EPOCHREALTIME] '; PS1='#'; (( USER_ID )) && PS1='$'; PS1=$'\001\r''${|
+ \\builtin typeset e=$?
+
(( e )) && REPLY+="$e|"
- REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)}
- REPLY+=@${HOSTNAME%%.*}:
+ REPLY+=${USER}@${HOSTNAME%%.*}:
- local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~}
- local m=${%d} n p=...; (( m > 0 )) || m=${#d}
+ \\builtin typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
+ d=${d%/}; \\builtin typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
REPLY+=$p$d
- return $e
-} '"$PS1 "; export EDITOR HOSTNAME MKSH TERM USER
-alias ls=ls
-unalias ls
-alias l='ls -F'
-alias la='l -a'
-alias ll='l -l'
-alias lo='l -alo'
-alias doch='sudo mksh -c "$(fc -ln -1)"'
-whence -p rot13 >/dev/null || alias rot13='tr \
+ \\builtin return $e
+} '"$PS1 "
+
+# utilities
+\\builtin alias doch='sudo mksh -c "$(\\builtin fc -ln -1)"'
+\\builtin command -v rot13 >/dev/null || \\builtin alias rot13='tr \
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
-if whence -p hd >/dev/null; then :; elif whence -p hexdump >/dev/null; then
+if \\builtin command -v hd >/dev/null; then
+ \:
+elif \\builtin command -v hexdump >/dev/null; then
function hd {
hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \
-e '" |" "%_p"' -e '"|\n"' "$@"
@@ -62,227 +109,218 @@
}
else
function hd {
- local -Uui16 -Z11 pos=0
- local -Uui16 -Z5 hv=2147483647
- local dasc line i
-
- cat "$@" | { set +U; if read -arN -1 line; then
- typeset -i1 'line[*]'
- i=0
- while (( i < ${#line[*]} )); do
- hv=${line[i++]}
- if (( (pos & 15) == 0 )); then
- (( pos )) && print -r -- "$dasc|"
- print -n "${pos#16#} "
- dasc=' |'
- fi
- print -n "${hv#16#} "
- if (( (hv < 32) || (hv > 126) )); then
- dasc+=.
- else
- dasc+=${line[i-1]#1#}
- fi
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- done
- while (( pos & 15 )); do
- print -n ' '
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- done
- (( hv == 2147483647 )) || print -r -- "$dasc|"
- fi; }
+ \\builtin cat "$@" | hd_mksh "$@"
}
fi
+# NUL-safe and EBCDIC-safe hexdump (from stdin)
+function hd_mksh {
+ \\builtin typeset -Uui16 -Z11 pos=0
+ \\builtin typeset -Uui16 -Z5 hv=2147483647
+ \\builtin typeset dasc dn line i
+ \\builtin set +U
+
+ while \\builtin read -arn 512 line; do
+ \\builtin typeset -i1 'line[*]'
+ i=0
+ while (( i < ${#line[*]} )); do
+ dn=
+ (( (hv = line[i++]) != 0 )) && dn=${line[i-1]#1#}
+ if (( (pos & 15) == 0 )); then
+ (( pos )) && \
+ \\builtin print -r -- "$dasc|"
+ \\builtin print -nr "${pos#16#} "
+ dasc=' |'
+ fi
+ \\builtin print -nr "${hv#16#} "
+ if [[ $dn = [[:print:]] ]]; then
+ dasc+=$dn
+ else
+ dasc+=.
+ fi
+ (( (pos++ & 15) == 7 )) && \
+ \\builtin print -nr -- '- '
+ done
+ done
+ while (( pos & 15 )); do
+ \\builtin print -nr ' '
+ (( (pos++ & 15) == 7 )) && \
+ \\builtin print -nr -- '- '
+ done
+ (( hv == 2147483647 )) || \\builtin print -r -- "$dasc|"
+}
+
# Berkeley C shell compatible dirs, popd, and pushd functions
# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
-DIRSTACKBASE=$(realpath ~/. 2>/dev/null || print -nr -- "${HOME:-/}")
-set -A DIRSTACK
+DIRSTACKBASE=$(\\builtin realpath ~/. 2>/dev/null || \
+ \\builtin print -nr -- "${HOME:-/}")
+\\builtin set -A DIRSTACK
function chpwd {
- DIRSTACK[0]=$(realpath . 2>/dev/null || print -r -- "$PWD")
+ DIRSTACK[0]=$(\\builtin realpath . 2>/dev/null || \
+ \\builtin print -nr -- "$PWD")
[[ $DIRSTACKBASE = ?(*/) ]] || \
- DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/~}
- :
+ DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/\~}
+ \:
}
-chpwd .
-function cd {
- builtin cd "$@" || return $?
- chpwd "$@"
+\chpwd .
+cd() {
+ \\builtin cd "$@" || \\builtin return $?
+ \chpwd "$@"
}
function cd_csh {
- local d t=${1/#~/$DIRSTACKBASE}
+ \\builtin typeset d t=${1/#\~/$DIRSTACKBASE}
- if ! d=$(builtin cd "$t" 2>&1); then
- print -u2 "${1}: ${d##*cd: $t: }."
- return 1
+ if ! d=$(\\builtin cd "$t" 2>&1); then
+ \\builtin print -ru2 "${1}: ${d##*cd: $t: }."
+ \\builtin return 1
fi
- cd "$t"
+ \cd "$t"
}
function dirs {
- local d dwidth
- local -i fl=0 fv=0 fn=0 cpos=0
+ \\builtin typeset d dwidth
+ \\builtin typeset -i fl=0 fv=0 fn=0 cpos=0
- while getopts ":lvn" d; do
+ while \\builtin getopts ":lvn" d; do
case $d {
(l) fl=1 ;;
(v) fv=1 ;;
(n) fn=1 ;;
- (*) print -u2 'Usage: dirs [-lvn].'
- return 1 ;;
+ (*) \\builtin print -ru2 'Usage: dirs [-lvn].'
+ \\builtin return 1 ;;
}
done
- shift $((OPTIND - 1))
+ \\builtin shift $((OPTIND - 1))
if (( $# > 0 )); then
- print -u2 'Usage: dirs [-lvn].'
- return 1
+ \\builtin print -ru2 'Usage: dirs [-lvn].'
+ \\builtin return 1
fi
if (( fv )); then
fv=0
while (( fv < ${#DIRSTACK[*]} )); do
d=${DIRSTACK[fv]}
- (( fl )) && d=${d/#~/$DIRSTACKBASE}
- print -r -- "$fv $d"
- let fv++
+ (( fl )) && d=${d/#\~/$DIRSTACKBASE}
+ \\builtin print -r -- "$fv $d"
+ (( ++fv ))
done
else
fv=0
while (( fv < ${#DIRSTACK[*]} )); do
d=${DIRSTACK[fv]}
- (( fl )) && d=${d/#~/$DIRSTACKBASE}
+ (( fl )) && d=${d/#\~/$DIRSTACKBASE}
(( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
if (( fn && (cpos += dwidth + 1) >= 79 && \
dwidth < 80 )); then
- print
+ \\builtin print
(( cpos = dwidth + 1 ))
fi
- print -nr -- "$d "
- let fv++
+ \\builtin print -nr -- "$d "
+ (( ++fv ))
done
- print
+ \\builtin print
fi
- return 0
+ \\builtin return 0
}
function popd {
- local d fa
- local -i n=1
+ \\builtin typeset d fa
+ \\builtin typeset -i n=1
- while getopts ":0123456789lvn" d; do
+ while \\builtin getopts ":0123456789lvn" d; do
case $d {
(l|v|n) fa+=" -$d" ;;
(+*) n=2
- break ;;
- (*) print -u2 'Usage: popd [-lvn] [+<n>].'
- return 1 ;;
+ \\builtin break ;;
+ (*) \\builtin print -ru2 'Usage: popd [-lvn] [+<n>].'
+ \\builtin return 1 ;;
}
done
- shift $((OPTIND - n))
+ \\builtin shift $((OPTIND - n))
n=0
if (( $# > 1 )); then
- print -u2 popd: Too many arguments.
- return 1
+ \\builtin print -ru2 popd: Too many arguments.
+ \\builtin return 1
elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
- print -u2 popd: Directory stack not that deep.
- return 1
+ \\builtin print -ru2 popd: Directory stack not that deep.
+ \\builtin return 1
fi
elif [[ -n $1 ]]; then
- print -u2 popd: Bad directory.
- return 1
+ \\builtin print -ru2 popd: Bad directory.
+ \\builtin return 1
fi
if (( ${#DIRSTACK[*]} < 2 )); then
- print -u2 popd: Directory stack empty.
- return 1
+ \\builtin print -ru2 popd: Directory stack empty.
+ \\builtin return 1
fi
- unset DIRSTACK[n]
- set -A DIRSTACK -- "${DIRSTACK[@]}"
- cd_csh "${DIRSTACK[0]}" || return 1
- dirs $fa
+ \\builtin unset DIRSTACK[n]
+ \\builtin set -A DIRSTACK -- "${DIRSTACK[@]}"
+ \cd_csh "${DIRSTACK[0]}" || \\builtin return 1
+ \dirs $fa
}
function pushd {
- local d fa
- local -i n=1
+ \\builtin typeset d fa
+ \\builtin typeset -i n=1
- while getopts ":0123456789lvn" d; do
+ while \\builtin getopts ":0123456789lvn" d; do
case $d {
(l|v|n) fa+=" -$d" ;;
(+*) n=2
- break ;;
- (*) print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
- return 1 ;;
+ \\builtin break ;;
+ (*) \\builtin print -ru2 'Usage: pushd [-lvn] [<dir>|+<n>].'
+ \\builtin return 1 ;;
}
done
- shift $((OPTIND - n))
+ \\builtin shift $((OPTIND - n))
if (( $# == 0 )); then
if (( ${#DIRSTACK[*]} < 2 )); then
- print -u2 pushd: No other directory.
- return 1
+ \\builtin print -ru2 pushd: No other directory.
+ \\builtin return 1
fi
d=${DIRSTACK[1]}
DIRSTACK[1]=${DIRSTACK[0]}
- cd_csh "$d" || return 1
+ \cd_csh "$d" || \\builtin return 1
elif (( $# > 1 )); then
- print -u2 pushd: Too many arguments.
- return 1
+ \\builtin print -ru2 pushd: Too many arguments.
+ \\builtin return 1
elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
- print -u2 pushd: Directory stack not that deep.
- return 1
+ \\builtin print -ru2 pushd: Directory stack not that deep.
+ \\builtin return 1
fi
while (( n-- )); do
d=${DIRSTACK[0]}
- unset DIRSTACK[0]
- set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
+ \\builtin unset DIRSTACK[0]
+ \\builtin set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
done
- cd_csh "${DIRSTACK[0]}" || return 1
+ \cd_csh "${DIRSTACK[0]}" || \\builtin return 1
else
- set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
- cd_csh "$1" || return 1
+ \\builtin set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
+ \cd_csh "$1" || \\builtin return 1
fi
- dirs $fa
+ \dirs $fa
}
-# pager (not control character safe)
-function smores {
- (
- set +m
- cat "$@" |&
- trap "rv=\$?; kill $! >/dev/null 2>&1; exit \$rv" EXIT
- while IFS= read -pr line; do
- llen=${%line}
- (( llen == -1 )) && llen=${#line}
- (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
- if (( (curlin += llen) >= LINES )); then
- print -n -- '\033[7m--more--\033[0m'
- read -u1 || exit $?
- [[ $REPLY = [Qq]* ]] && exit 0
- curlin=$llen
- fi
- print -r -- "$line"
- done
- )
-}
-
-# base64 encoder and decoder, RFC compliant, NUL safe
+# base64 encoder and decoder, RFC compliant, NUL safe, not EBCDIC safe
function Lb64decode {
- [[ -o utf8-mode ]]; local u=$? c s="$*" t
- set +U
- [[ -n $s ]] || { s=$(cat; print x); s=${s%x}; }
- local -i i=0 j=0 n=${#s} p=0 v x
- local -i16 o
+ \\builtin set +U
+ \\builtin typeset c s="$*" t
+ [[ -n $s ]] || { s=$(\\builtin cat; \\builtin print x); s=${s%x}; }
+ \\builtin typeset -i i=0 j=0 n=${#s} p=0 v x
+ \\builtin typeset -i16 o
while (( i < n )); do
c=${s:(i++):1}
case $c {
- (=) break ;;
+ (=) \\builtin break ;;
([A-Z]) (( v = 1#$c - 65 )) ;;
([a-z]) (( v = 1#$c - 71 )) ;;
([0-9]) (( v = 1#$c + 4 )) ;;
(+) v=62 ;;
(/) v=63 ;;
- (*) continue ;;
+ (*) \\builtin continue ;;
}
(( x = (x << 6) | v ))
case $((p++)) {
- (0) continue ;;
+ (0) \\builtin continue ;;
(1) (( o = (x >> 4) & 255 )) ;;
(2) (( o = (x >> 2) & 255 )) ;;
(3) (( o = x & 255 ))
@@ -290,37 +328,33 @@
;;
}
t+=\\x${o#16#}
- (( ++j & 4095 )) && continue
- print -n $t
+ (( ++j & 4095 )) && \\builtin continue
+ \\builtin print -n $t
t=
done
- print -n $t
- (( u )) || set -U
+ \\builtin print -n $t
}
-
-set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
- a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
function Lb64encode {
- [[ -o utf8-mode ]]; local u=$? c s t
- set +U
+ \\builtin set +U
+ \\builtin typeset c s t table
+ \\builtin set -A table -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
+ a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
if (( $# )); then
- read -raN-1 s <<<"$*"
- unset s[${#s[*]}-1]
+ \\builtin read -raN-1 s <<<"$*"
+ \\builtin unset s[${#s[*]}-1]
else
- read -raN-1 s
+ \\builtin read -raN-1 s
fi
- local -i i=0 n=${#s[*]} j v
+ \\builtin typeset -i i=0 n=${#s[*]} v
while (( i < n )); do
(( v = s[i++] << 16 ))
- (( j = i < n ? s[i++] : 0 ))
- (( v |= j << 8 ))
- (( j = i < n ? s[i++] : 0 ))
- (( v |= j ))
- t+=${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]}
- c=${Lb64encode_code[v >> 6 & 63]}
+ (( v |= s[i++] << 8 ))
+ (( v |= s[i++] ))
+ t+=${table[v >> 18]}${table[v >> 12 & 63]}
+ c=${table[v >> 6 & 63]}
if (( i <= n )); then
- t+=$c${Lb64encode_code[v & 63]}
+ t+=$c${table[v & 63]}
elif (( i == n + 1 )); then
t+=$c=
else
@@ -327,76 +361,269 @@
t+===
fi
if (( ${#t} == 76 || i >= n )); then
- print $t
+ \\builtin print -r $t
t=
fi
done
- (( u )) || set -U
}
# Better Avalanche for the Jenkins Hash
-typeset -Z11 -Uui16 Lbafh_v
+\\builtin typeset -Z11 -Uui16 Lbafh_v
function Lbafh_init {
Lbafh_v=0
}
function Lbafh_add {
- [[ -o utf8-mode ]]; local u=$? s
- set +U
+ \\builtin set +U
+ \\builtin typeset s
if (( $# )); then
- read -raN-1 s <<<"$*"
- unset s[${#s[*]}-1]
+ \\builtin read -raN-1 s <<<"$*"
+ \\builtin unset s[${#s[*]}-1]
else
- read -raN-1 s
+ \\builtin read -raN-1 s
fi
- local -i i=0 n=${#s[*]}
+ \\builtin typeset -i i=0 n=${#s[*]}
while (( i < n )); do
((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 ))
((# Lbafh_v ^= Lbafh_v >> 6 ))
done
-
- (( u )) || set -U
}
function Lbafh_finish {
- local -Ui t
+ \\builtin typeset -Ui t
((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
((Lbafh_v << 1) & 0xFEFEFEFE) ))
- ((# Lbafh_v = t ^ (t >>> 8) ^ (Lbafh_v >>> 8) ^ \
- (Lbafh_v >>> 16) ^ (Lbafh_v >>> 24) ))
- :
+ ((# Lbafh_v = t ^ (t ^> 8) ^ (Lbafh_v ^> 8) ^ \
+ (Lbafh_v ^> 16) ^ (Lbafh_v ^> 24) ))
+ \:
}
# strip comments (and leading/trailing whitespace if IFS is set) from
# any file(s) given as argument, or stdin if none, and spew to stdout
function Lstripcom {
- cat "$@" | { set -o noglob; while read _line; do
+ \\builtin set -o noglob
+ \\builtin cat "$@" | while \\builtin read _line; do
_line=${_line%%#*}
- [[ -n $_line ]] && print -r -- $_line
- done; }
+ [[ -n $_line ]] && \\builtin print -r -- $_line
+ done
}
-# give MidnightBSD's laffer1 a bit of csh feeling
-function setenv {
- eval export "\"$1\""'="$2"'
+# toggle built-in aliases and utilities, and aliases and functions from mkshrc
+function enable {
+ \\builtin typeset doprnt=0 mode=1 x y z rv=0
+ \\builtin typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all
+ \\builtin set -A b_alias
+ \\builtin set -A i_alias
+ \\builtin set -A i_func
+
+ # accumulate mksh built-in aliases, in ASCIIbetical order
+ i_alias[nalias]=autoload; b_alias[nalias++]='\\builtin typeset -fu'
+ i_alias[nalias]=functions; b_alias[nalias++]='\\builtin typeset -f'
+ i_alias[nalias]=hash; b_alias[nalias++]='\\builtin alias -t'
+ i_alias[nalias]=history; b_alias[nalias++]='\\builtin fc -l'
+ i_alias[nalias]=integer; b_alias[nalias++]='\\builtin typeset -i'
+ i_alias[nalias]=local; b_alias[nalias++]='\\builtin typeset'
+ i_alias[nalias]=login; b_alias[nalias++]='\\builtin exec login'
+ i_alias[nalias]=nameref; b_alias[nalias++]='\\builtin typeset -n'
+ i_alias[nalias]=nohup; b_alias[nalias++]='nohup '
+ i_alias[nalias]=r; b_alias[nalias++]='\\builtin fc -e -'
+ i_alias[nalias]=type; b_alias[nalias++]='\\builtin whence -v'
+
+ # accumulate mksh built-in utilities, in definition order, even ifndef
+ i_func[nfunc++]=.
+ i_func[nfunc++]=:
+ i_func[nfunc++]='['
+ i_func[nfunc++]=alias
+ i_func[nfunc++]=break
+ # \\builtin cannot, by design, be overridden
+ i_func[nfunc++]=builtin
+ i_func[nfunc++]=cat
+ i_func[nfunc++]=cd
+ i_func[nfunc++]=chdir
+ i_func[nfunc++]=command
+ i_func[nfunc++]=continue
+ i_func[nfunc++]=echo
+ i_func[nfunc++]=eval
+ i_func[nfunc++]=exec
+ i_func[nfunc++]=exit
+ i_func[nfunc++]=export
+ 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
+ i_func[nfunc++]=print
+ i_func[nfunc++]=pwd
+ i_func[nfunc++]=read
+ i_func[nfunc++]=readonly
+ i_func[nfunc++]=realpath
+ i_func[nfunc++]=rename
+ i_func[nfunc++]=return
+ i_func[nfunc++]=set
+ i_func[nfunc++]=shift
+ i_func[nfunc++]=source
+ i_func[nfunc++]=suspend
+ i_func[nfunc++]=test
+ i_func[nfunc++]=times
+ i_func[nfunc++]=trap
+ i_func[nfunc++]=true
+ i_func[nfunc++]=typeset
+ i_func[nfunc++]=ulimit
+ i_func[nfunc++]=umask
+ i_func[nfunc++]=unalias
+ i_func[nfunc++]=unset
+ i_func[nfunc++]=wait
+ i_func[nfunc++]=whence
+ i_func[nfunc++]=bg
+ i_func[nfunc++]=fg
+ i_func[nfunc++]=bind
+ i_func[nfunc++]=mknod
+ i_func[nfunc++]=printf
+ i_func[nfunc++]=sleep
+ i_func[nfunc++]=domainname
+ i_func[nfunc++]=extproc
+
+ # accumulate aliases from dot.mkshrc, in definition order
+ i_alias[nalias]=l; b_alias[nalias++]='ls -F'
+ i_alias[nalias]=la; b_alias[nalias++]='l -a'
+ i_alias[nalias]=ll; b_alias[nalias++]='l -l'
+ i_alias[nalias]=lo; b_alias[nalias++]='l -alo'
+ i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\\builtin fc -ln -1)"'
+ i_alias[nalias]=rot13; b_alias[nalias++]='tr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
+ i_alias[nalias]=cls; b_alias[nalias++]='\\builtin print -n \\ec'
+
+ # accumulate functions from dot.mkshrc, in definition order
+ i_func[nfunc++]=setenv
+ i_func[nfunc++]=smores
+ i_func[nfunc++]=hd
+ i_func[nfunc++]=hd_mksh
+ i_func[nfunc++]=chpwd
+ i_func[nfunc++]=cd
+ i_func[nfunc++]=cd_csh
+ i_func[nfunc++]=dirs
+ i_func[nfunc++]=popd
+ i_func[nfunc++]=pushd
+ i_func[nfunc++]=Lb64decode
+ i_func[nfunc++]=Lb64encode
+ i_func[nfunc++]=Lbafh_init
+ i_func[nfunc++]=Lbafh_add
+ i_func[nfunc++]=Lbafh_finish
+ i_func[nfunc++]=Lstripcom
+ i_func[nfunc++]=enable
+
+ # collect all identifiers, sorted ASCIIbetically
+ \\builtin set -sA i_all -- "${i_alias[@]}" "${i_func[@]}"
+
+ # handle options, we don't do dynamic loading
+ while \\builtin getopts "adf:nps" x; do
+ case $x {
+ (a)
+ mode=-1
+ ;;
+ (d)
+ # deliberately causing an error, like bash-static
+ ;|
+ (f)
+ \\builtin print -ru2 enable: dynamic loading not available
+ \\builtin return 2
+ ;;
+ (n)
+ mode=0
+ ;;
+ (p)
+ doprnt=1
+ ;;
+ (s)
+ \\builtin set -sA i_all -- . : break continue eval \
+ exec exit export readonly return set shift times \
+ trap unset
+ ;;
+ (*)
+ \\builtin print -ru2 enable: usage: \
+ "enable [-adnps] [-f filename] [name ...]"
+ return 2
+ ;;
+ }
+ done
+ \\builtin shift $((OPTIND - 1))
+
+ # display builtins enabled/disabled/all/special?
+ if (( doprnt || ($# == 0) )); then
+ for x in "${i_all[@]}"; do
+ y=$(\\builtin alias "$x") || y=
+ [[ $y = "$x='\\\\builtin whence -p $x >/dev/null || (\\\\builtin print -r mksh: $x: not found; \\\\builtin exit 127) && \$(\\\\builtin whence -p $x)'" ]]; z=$?
+ case $mode:$z {
+ (-1:0|0:0)
+ \\builtin print -r -- "enable -n $x"
+ ;;
+ (-1:1|1:1)
+ \\builtin print -r -- "enable $x"
+ ;;
+ }
+ done
+ \\builtin return 0
+ fi
+
+ for x in "$@"; do
+ z=0
+ for y in "${i_alias[@]}" "${i_func[@]}"; do
+ [[ $x = "$y" ]] || \\builtin continue
+ z=1
+ \\builtin break
+ done
+ if (( !z )); then
+ \\builtin print -ru2 enable: "$x": not a shell builtin
+ rv=1
+ \\builtin continue
+ fi
+ if (( !mode )); then
+ # disable this
+ \\builtin alias "$x=\\\\builtin whence -p $x >/dev/null || (\\\\builtin print -r mksh: $x: not found; \\\\builtin exit 127) && \$(\\\\builtin whence -p $x)"
+ else
+ # find out if this is an alias or not, first
+ z=0
+ y=-1
+ while (( ++y < nalias )); do
+ [[ $x = "${i_alias[y]}" ]] || \\builtin continue
+ z=1
+ \\builtin break
+ done
+ if (( z )); then
+ # re-enable the original alias body
+ \\builtin alias "$x=${b_alias[y]}"
+ else
+ # re-enable the original utility/function
+ \\builtin unalias "$x"
+ fi
+ fi
+ done
+ \\builtin return $rv
}
-: place customisations below this line
+\: place customisations below this line
+# some defaults follow — you are supposed to adjust these 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
+
for p in ~/.etc/bin ~/bin; do
- [[ -d $p/. ]] || continue
- [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
+ [[ -d $p/. ]] || \\builtin continue
+ [[ $PATHSEP$PATH$PATHSEP = *"$PATHSEP$p$PATHSEP"* ]] || \
+ PATH=$p$PATHSEP$PATH
done
-export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
-alias cls='print -n \\033c'
+\\builtin export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
+\\builtin alias cls='\\builtin print -n \\ec'
-#unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
+#\\builtin unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
# LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
#p=en_GB.UTF-8
-#set -U
-#export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
+#\\builtin export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
+#\\builtin set -U
-unset p
+\\builtin unset p
-: place customisations above this line
+\: place customisations above this line
Modified: trunk/contrib/mksh/edit.c
===================================================================
--- trunk/contrib/mksh/edit.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/edit.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,12 +1,12 @@
-/* $OpenBSD: edit.c,v 1.39 2013/12/17 16:37:05 deraadt Exp $ */
+/* $OpenBSD: edit.c,v 1.41 2015/09/01 13:12:31 tedu Exp $ */
/* $OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $ */
-/* $OpenBSD: emacs.c,v 1.49 2015/02/16 01:44:41 tedu Exp $ */
-/* $OpenBSD: vi.c,v 1.28 2013/12/18 16:45:46 deraadt Exp $ */
+/* $OpenBSD: emacs.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */
+/* $OpenBSD: vi.c,v 1.30 2015/09/10 22:48:58 nicm Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -28,32 +28,39 @@
#ifndef MKSH_NO_CMDLINE_EDITING
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.276.2.3 2015/03/01 15:42:56 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.342 2018/01/14 00:03:00 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
* dependencies are problematic, this has not yet been decided on; another
- * good string is "\033c" except on hardware terminals like the DEC VT420
- * which do a full power cycle then...
+ * good string is KSH_ESC_STRING "c" except on hardware terminals like the
+ * DEC VT420 which do a full power cycle then...
*/
#ifndef MKSH_CLS_STRING
-#define MKSH_CLS_STRING "\033[;H\033[J"
+#define MKSH_CLS_STRING KSH_ESC_STRING "[;H" KSH_ESC_STRING "[J"
#endif
-#ifndef MKSH_CLRTOEOL_STRING
-#define MKSH_CLRTOEOL_STRING "\033[K"
-#endif
/* tty driver characters we are interested in */
-typedef struct {
- int erase;
- int kill;
- int werase;
- int intr;
- int quit;
- int eof;
-} X_chars;
+#define EDCHAR_DISABLED 0xFFFFU
+#define EDCHAR_INITIAL 0xFFFEU
+static struct {
+ unsigned short erase;
+ unsigned short kill;
+ unsigned short werase;
+ unsigned short intr;
+ unsigned short quit;
+ unsigned short eof;
+} edchars;
-static X_chars edchars;
+#define isched(x,e) ((unsigned short)(unsigned char)(x) == (e))
+#define isedchar(x) (!((x) & ~0xFF))
+#ifndef _POSIX_VDISABLE
+#define toedchar(x) ((unsigned short)(unsigned char)(x))
+#else
+#define toedchar(x) (((_POSIX_VDISABLE != -1) && ((x) == _POSIX_VDISABLE)) ? \
+ ((unsigned short)EDCHAR_DISABLED) : \
+ ((unsigned short)(unsigned char)(x)))
+#endif
/* x_cf_glob() flags */
#define XCF_COMMAND BIT(0) /* Do command completion */
@@ -68,6 +75,10 @@
static int modified; /* buffer has been "modified" */
static char *holdbufp; /* place to hold last edit buffer */
+/* 0=dumb 1=tmux (for now) */
+static uint8_t x_term_mode;
+
+static void x_adjust(void);
static int x_getc(void);
static void x_putcf(int);
static void x_modified(void);
@@ -78,7 +89,7 @@
static size_t x_longest_prefix(int, char * const *);
static void x_glob_hlp_add_qchar(char *);
static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
-static int x_basename(const char *, const char *);
+static size_t x_basename(const char *, const char *);
static void x_free_words(int, char **);
static int x_escape(const char *, size_t, int (*)(const char *, size_t));
static int x_emacs(char *);
@@ -86,6 +97,7 @@
#if !MKSH_S_NOVI
static int x_vi(char *);
#endif
+static void x_intr(int, int) MKSH_A_NORETURN;
#define x_flush() shf_flush(shl_out)
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
@@ -102,7 +114,6 @@
static int x_locate_word(const char *, int, int, int *, bool *);
static int x_e_getmbc(char *);
-static int x_e_rebuildline(const char *);
/* +++ generic editing functions +++ */
@@ -135,6 +146,9 @@
static int
x_getc(void)
{
+#ifdef __OS2__
+ return (_read_kbd(0, 1, 0));
+#else
char c;
ssize_t n;
@@ -149,7 +163,7 @@
/* redraw line in Emacs mode */
xx_cols = x_cols;
x_init_prompt(false);
- x_e_rebuildline(MKSH_CLRTOEOL_STRING);
+ x_adjust();
}
}
#endif
@@ -156,6 +170,7 @@
x_mode(true);
}
return ((n == 1) ? (int)(unsigned char)c : -1);
+#endif
}
static void
@@ -226,8 +241,9 @@
x_print_expansions(int nwords, char * const *words, bool is_command)
{
bool use_copy = false;
- int prefix_len;
+ size_t prefix_len;
XPtrV l = { NULL, 0, 0 };
+ struct columnise_opts co;
/*
* Check if all matches are in the same directory (in this
@@ -247,7 +263,8 @@
break;
/* All in same directory? */
if (i == nwords) {
- while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
+ while (prefix_len > 0 &&
+ !mksh_cdirsep(words[0][prefix_len - 1]))
prefix_len--;
use_copy = true;
XPinit(l, nwords + 1);
@@ -261,7 +278,11 @@
*/
x_putc('\r');
x_putc('\n');
- pr_list(use_copy ? (char **)XPptrv(l) : words);
+ co.shf = shl_out;
+ co.linesep = '\n';
+ co.do_last = true;
+ co.prefcol = false;
+ pr_list(&co, use_copy ? (char **)XPptrv(l) : words);
if (use_copy)
/* not x_free_words() */
@@ -289,14 +310,14 @@
* empirically made list of chars to escape
* for globbing as well as QCHAR itself
*/
- switch (ch) {
+ switch (ord(ch)) {
case QCHAR:
- case '$':
- case '*':
- case '?':
- case '[':
- case '\\':
- case '`':
+ case ORD('$'):
+ case ORD('*'):
+ case ORD('?'):
+ case ORD('['):
+ case ORD('\\'):
+ case ORD('`'):
*dp++ = QCHAR;
break;
}
@@ -323,7 +344,7 @@
* and if so, discern "~foo/bar" and "~/baz" from "~blah";
* if we have a directory part (the former), try to expand
*/
- if (*s == '~' && (cp = strchr(s, '/')) != NULL) {
+ if (*s == '~' && (cp = /* not sdirsep */ strchr(s, '/')) != NULL) {
/* ok, so split into "~foo"/"bar" or "~"/"baz" */
*cp++ = 0;
/* try to expand the tilde */
@@ -332,7 +353,7 @@
*--cp = '/';
} else {
/* ok, expand and replace */
- cp = shf_smprintf("%s/%s", dp, cp);
+ cp = shf_smprintf(Tf_sSs, dp, cp);
if (magic_flag)
afree(s, ATEMP);
s = cp;
@@ -381,7 +402,7 @@
source = s;
if (yylex(ONEWORD | LQCHAR) != LWORD) {
source = sold;
- internal_warningf("%s: %s", "fileglob", "bad substitution");
+ internal_warningf(Tfg_badsubst);
return (0);
}
source = sold;
@@ -435,8 +456,8 @@
/* Data structure used in x_command_glob() */
struct path_order_info {
char *word;
- int base;
- int path_order;
+ size_t base;
+ size_t path_order;
};
/* Compare routine used in x_command_glob() */
@@ -447,8 +468,13 @@
const struct path_order_info *b = (const struct path_order_info *)bb;
int t;
- t = strcmp(a->word + a->base, b->word + b->base);
- return (t ? t : a->path_order - b->path_order);
+ if ((t = ascstrcmp(a->word + a->base, b->word + b->base)))
+ return (t);
+ if (a->path_order > b->path_order)
+ return (1);
+ if (a->path_order < b->path_order)
+ return (-1);
+ return (0);
}
static int
@@ -471,7 +497,7 @@
glob_table(pat, &w, &l->funs);
glob_path(flags, pat, &w, path);
- if ((fpath = str_val(global("FPATH"))) != null)
+ if ((fpath = str_val(global(TFPATH))) != null)
glob_path(flags, pat, &w, fpath);
nwords = XPsize(w);
@@ -510,7 +536,7 @@
char **words = (char **)XPptrv(w);
size_t i, j;
- qsort(words, nwords, sizeof(void *), xstrcmp);
+ qsort(words, nwords, sizeof(void *), ascpstrcmp);
for (i = j = 0; i < nwords - 1; i++) {
if (strcmp(words[i], words[i + 1]))
words[j++] = words[i];
@@ -527,8 +553,7 @@
return (nwords);
}
-#define IS_WORDC(c) (!ctype(c, C_LEX1) && (c) != '\'' && (c) != '"' && \
- (c) != '`' && (c) != '=' && (c) != ':')
+#define IS_WORDC(c) (!ctype(c, C_EDNWC))
static int
x_locate_word(const char *buf, int buflen, int pos, int *startp,
@@ -563,9 +588,9 @@
int p = start - 1;
/* Figure out if this is a command */
- while (p >= 0 && ksh_isspace(buf[p]))
+ while (p >= 0 && ctype(buf[p], C_SPACE))
p--;
- iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
+ iscmd = p < 0 || ctype(buf[p], C_EDCMD);
if (iscmd) {
/*
* If command has a /, path, etc. is not searched;
@@ -573,7 +598,7 @@
* like file globbing.
*/
for (p = start; p < end; p++)
- if (buf[p] == '/')
+ if (mksh_cdirsep(buf[p]))
break;
iscmd = p == end;
}
@@ -624,11 +649,12 @@
for (s = toglob; *s; s++) {
if (*s == '\\' && s[1])
s++;
- else if (*s == '?' || *s == '*' || *s == '[' ||
- *s == '$' ||
+ else if (ctype(*s, C_QUEST | C_DOLAR) ||
+ ord(*s) == ORD('*') || ord(*s) == ORD('[') ||
/* ?() *() +() @() !() but two already checked */
- (s[1] == '(' /*)*/ &&
- (*s == '+' || *s == '@' || *s == '!'))) {
+ (ord(s[1]) == ORD('(' /*)*/) &&
+ (ord(*s) == ORD('+') || ord(*s) == ORD('@') ||
+ ord(*s) == ORD('!')))) {
/*
* just expand based on the extglob
* or parameter
@@ -637,7 +663,7 @@
}
}
- if (*toglob == '~' && !vstrchr(toglob, '/')) {
+ if (*toglob == '~' && /* not vdirsep */ !vstrchr(toglob, '/')) {
/* neither for '~foo' (but '~foo/bar') */
*flagsp |= XCF_IS_NOSPACE;
goto dont_add_glob;
@@ -689,8 +715,8 @@
break;
}
/* false for nwords==1 as 0 = words[0][prefix_len] then */
- if (UTFMODE && prefix_len && (words[0][prefix_len] & 0xC0) == 0x80)
- while (prefix_len && (words[0][prefix_len] & 0xC0) != 0xC0)
+ if (UTFMODE && prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) == 0x80)
+ while (prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) != 0xC0)
--prefix_len;
return (prefix_len);
}
@@ -716,23 +742,25 @@
* /// 2
* 0
*/
-static int
+static size_t
x_basename(const char *s, const char *se)
{
const char *p;
if (se == NULL)
- se = s + strlen(s);
+ se = strnul(s);
if (s == se)
return (0);
- /* Skip trailing slashes */
- for (p = se - 1; p > s && *p == '/'; p--)
- ;
- for (; p > s && *p != '/'; p--)
- ;
- if (*p == '/' && p + 1 < se)
- p++;
+ /* skip trailing directory separators */
+ p = se - 1;
+ while (p > s && mksh_cdirsep(*p))
+ --p;
+ /* drop last component */
+ while (p > s && !mksh_cdirsep(*p))
+ --p;
+ if (mksh_cdirsep(*p) && p + 1 < se)
+ ++p;
return (p - s);
}
@@ -771,8 +799,8 @@
Xinit(xs, xp, patlen + 128, ATEMP);
while (sp) {
xp = Xstring(xs, xp);
- if (!(p = cstrchr(sp, ':')))
- p = sp + strlen(sp);
+ if (!(p = cstrchr(sp, MKSH_PATHSEPC)))
+ p = strnul(sp);
pathlen = p - sp;
if (pathlen) {
/*
@@ -828,12 +856,10 @@
x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
{
size_t add = 0, wlen = len;
- const char *ifs = str_val(local("IFS", 0));
int rval = 0;
while (wlen - add > 0)
- if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
- vstrchr(ifs, s[add])) {
+ if (ctype(s[add], C_IFS | C_EDQ)) {
if (putbuf_func(s, add) != 0) {
rval = -1;
break;
@@ -882,12 +908,7 @@
#define XF_NOBIND 2 /* not allowed to bind to function */
#define XF_PREFIX 4 /* function sets prefix */
-/* Separator for completion */
-#define is_cfs(c) ((c) == ' ' || (c) == '\t' || (c) == '"' || (c) == '\'')
-/* Separator for motion */
-#define is_mfs(c) (!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80)))
-
-#define X_NTABS 3 /* normal, meta1, meta2 */
+#define X_NTABS 4 /* normal, meta1, meta2, pc */
#define X_TABSZ 256 /* size of keydef tables etc */
/*-
@@ -927,6 +948,7 @@
static char **x_histp; /* history position */
static int x_nextcmd; /* for newline-and-next */
static char **x_histncp; /* saved x_histp for " */
+static char **x_histmcp; /* saved x_histp for " */
static char *xmp; /* mark pointer */
static unsigned char x_last_command;
static unsigned char (*x_tab)[X_TABSZ]; /* key definition */
@@ -961,10 +983,10 @@
static void x_goto(char *);
static char *x_bs0(char *, char *) MKSH_A_PURE;
static void x_bs3(char **);
-static int x_size_str(char *);
static int x_size2(char *, char **);
static void x_zots(char *);
static void x_zotc3(char **);
+static void x_vi_zotc(int);
static void x_load_hist(char **);
static int x_search(char *, int, int);
#ifndef MKSH_SMALL
@@ -972,12 +994,11 @@
#endif
static int x_match(char *, char *);
static void x_redraw(int);
-static void x_push(int);
+static void x_push(size_t);
static char *x_mapin(const char *, Area *);
static char *x_mapout(int);
static void x_mapout2(int, char **);
static void x_print(int, int);
-static void x_adjust(void);
static void x_e_ungetc(int);
static int x_e_getc(void);
static void x_e_putc2(int);
@@ -987,6 +1008,7 @@
static int x_fold_case(int);
#endif
static char *x_lastcp(void);
+static void x_lastpos(void);
static void do_complete(int, Comp_type);
static size_t x_nb2nc(size_t) MKSH_A_PURE;
@@ -1010,56 +1032,56 @@
};
static struct x_defbindings const x_defbindings[] = {
- { XFUNC_del_back, 0, CTRL('?') },
- { XFUNC_del_bword, 1, CTRL('?') },
- { XFUNC_eot_del, 0, CTRL('D') },
- { XFUNC_del_back, 0, CTRL('H') },
- { XFUNC_del_bword, 1, CTRL('H') },
+ { XFUNC_del_back, 0, CTRL_QM },
+ { XFUNC_del_bword, 1, CTRL_QM },
+ { XFUNC_eot_del, 0, CTRL_D },
+ { XFUNC_del_back, 0, CTRL_H },
+ { XFUNC_del_bword, 1, CTRL_H },
{ XFUNC_del_bword, 1, 'h' },
{ XFUNC_mv_bword, 1, 'b' },
{ XFUNC_mv_fword, 1, 'f' },
{ XFUNC_del_fword, 1, 'd' },
- { XFUNC_mv_back, 0, CTRL('B') },
- { XFUNC_mv_forw, 0, CTRL('F') },
- { XFUNC_search_char_forw, 0, CTRL(']') },
- { XFUNC_search_char_back, 1, CTRL(']') },
- { XFUNC_newline, 0, CTRL('M') },
- { XFUNC_newline, 0, CTRL('J') },
- { XFUNC_end_of_text, 0, CTRL('_') },
- { XFUNC_abort, 0, CTRL('G') },
- { XFUNC_prev_com, 0, CTRL('P') },
- { XFUNC_next_com, 0, CTRL('N') },
- { XFUNC_nl_next_com, 0, CTRL('O') },
- { XFUNC_search_hist, 0, CTRL('R') },
+ { XFUNC_mv_back, 0, CTRL_B },
+ { XFUNC_mv_forw, 0, CTRL_F },
+ { XFUNC_search_char_forw, 0, CTRL_BC },
+ { XFUNC_search_char_back, 1, CTRL_BC },
+ { XFUNC_newline, 0, CTRL_M },
+ { XFUNC_newline, 0, CTRL_J },
+ { XFUNC_end_of_text, 0, CTRL_US },
+ { XFUNC_abort, 0, CTRL_G },
+ { XFUNC_prev_com, 0, CTRL_P },
+ { XFUNC_next_com, 0, CTRL_N },
+ { XFUNC_nl_next_com, 0, CTRL_O },
+ { XFUNC_search_hist, 0, CTRL_R },
{ XFUNC_beg_hist, 1, '<' },
{ XFUNC_end_hist, 1, '>' },
{ XFUNC_goto_hist, 1, 'g' },
- { XFUNC_mv_end, 0, CTRL('E') },
- { XFUNC_mv_begin, 0, CTRL('A') },
- { XFUNC_draw_line, 0, CTRL('L') },
- { XFUNC_cls, 1, CTRL('L') },
- { XFUNC_meta1, 0, CTRL('[') },
- { XFUNC_meta2, 0, CTRL('X') },
- { XFUNC_kill, 0, CTRL('K') },
- { XFUNC_yank, 0, CTRL('Y') },
+ { XFUNC_mv_end, 0, CTRL_E },
+ { XFUNC_mv_beg, 0, CTRL_A },
+ { XFUNC_draw_line, 0, CTRL_L },
+ { XFUNC_cls, 1, CTRL_L },
+ { XFUNC_meta1, 0, CTRL_BO },
+ { XFUNC_meta2, 0, CTRL_X },
+ { XFUNC_kill, 0, CTRL_K },
+ { XFUNC_yank, 0, CTRL_Y },
{ XFUNC_meta_yank, 1, 'y' },
- { XFUNC_literal, 0, CTRL('^') },
+ { XFUNC_literal, 0, CTRL_CA },
{ XFUNC_comment, 1, '#' },
- { XFUNC_transpose, 0, CTRL('T') },
- { XFUNC_complete, 1, CTRL('[') },
- { XFUNC_comp_list, 0, CTRL('I') },
+ { XFUNC_transpose, 0, CTRL_T },
+ { XFUNC_complete, 1, CTRL_BO },
+ { XFUNC_comp_list, 0, CTRL_I },
{ XFUNC_comp_list, 1, '=' },
{ XFUNC_enumerate, 1, '?' },
{ XFUNC_expand, 1, '*' },
- { XFUNC_comp_file, 1, CTRL('X') },
- { XFUNC_comp_comm, 2, CTRL('[') },
+ { XFUNC_comp_file, 1, CTRL_X },
+ { XFUNC_comp_comm, 2, CTRL_BO },
{ XFUNC_list_comm, 2, '?' },
- { XFUNC_list_file, 2, CTRL('Y') },
+ { XFUNC_list_file, 2, CTRL_Y },
{ XFUNC_set_mark, 1, ' ' },
- { XFUNC_kill_region, 0, CTRL('W') },
- { XFUNC_xchg_point_mark, 2, CTRL('X') },
- { XFUNC_literal, 0, CTRL('V') },
- { XFUNC_version, 1, CTRL('V') },
+ { XFUNC_kill_region, 0, CTRL_W },
+ { XFUNC_xchg_point_mark, 2, CTRL_X },
+ { XFUNC_literal, 0, CTRL_V },
+ { XFUNC_version, 1, CTRL_V },
{ XFUNC_prev_histword, 1, '.' },
{ XFUNC_prev_histword, 1, '_' },
{ XFUNC_set_arg, 1, '0' },
@@ -1093,15 +1115,36 @@
{ XFUNC_mv_back, 2, 'D' },
#ifndef MKSH_SMALL
{ XFUNC_vt_hack, 2, '1' },
- { XFUNC_mv_begin | 0x80, 2, '7' },
- { XFUNC_mv_begin, 2, 'H' },
+ { XFUNC_mv_beg | 0x80, 2, '7' },
+ { XFUNC_mv_beg, 2, 'H' },
{ XFUNC_mv_end | 0x80, 2, '4' },
{ XFUNC_mv_end | 0x80, 2, '8' },
{ XFUNC_mv_end, 2, 'F' },
{ XFUNC_del_char | 0x80, 2, '3' },
+ { XFUNC_del_char, 2, 'P' },
{ XFUNC_search_hist_up | 0x80, 2, '5' },
{ XFUNC_search_hist_dn | 0x80, 2, '6' },
+#endif
+ /* PC scancodes */
+#if !defined(MKSH_SMALL) || defined(__OS2__)
+ { XFUNC_meta3, 0, 0 },
+ { XFUNC_mv_beg, 3, 71 },
+ { XFUNC_prev_com, 3, 72 },
+#ifndef MKSH_SMALL
+ { XFUNC_search_hist_up, 3, 73 },
+#endif
+ { XFUNC_mv_back, 3, 75 },
+ { XFUNC_mv_forw, 3, 77 },
+ { XFUNC_mv_end, 3, 79 },
+ { XFUNC_next_com, 3, 80 },
+#ifndef MKSH_SMALL
+ { XFUNC_search_hist_dn, 3, 81 },
+#endif
+ { XFUNC_del_char, 3, 83 },
+#endif
+#ifndef MKSH_SMALL
/* more non-standard ones */
+ { XFUNC_eval_region, 1, CTRL_E },
{ XFUNC_edit_line, 2, 'e' }
#endif
};
@@ -1121,6 +1164,7 @@
x_modified(void)
{
if (!modified) {
+ x_histmcp = x_histp;
x_histp = histptr + 1;
modified = 1;
}
@@ -1143,17 +1187,19 @@
if (c == -1)
return (-1);
if (UTFMODE) {
- if ((buf[0] >= 0xC2) && (buf[0] < 0xF0)) {
+ if ((rtt2asc(buf[0]) >= (unsigned char)0xC2) &&
+ (rtt2asc(buf[0]) < (unsigned char)0xF0)) {
c = x_e_getc();
if (c == -1)
return (-1);
- if ((c & 0xC0) != 0x80) {
+ if ((rtt2asc(c) & 0xC0) != 0x80) {
x_e_ungetc(c);
return (1);
}
buf[pos++] = c;
}
- if ((buf[0] >= 0xE0) && (buf[0] < 0xF0)) {
+ if ((rtt2asc(buf[0]) >= (unsigned char)0xE0) &&
+ (rtt2asc(buf[0]) < (unsigned char)0xF0)) {
/* XXX x_e_ungetc is one-octet only */
buf[pos++] = c = x_e_getc();
if (c == -1)
@@ -1163,6 +1209,12 @@
return (pos);
}
+/*
+ * minimum required space to work with on a line - if the prompt
+ * leaves less space than this on a line, the prompt is truncated
+ */
+#define MIN_EDIT_SPACE 7
+
static void
x_init_prompt(bool doprint)
{
@@ -1191,7 +1243,7 @@
xlp_valid = true;
xmp = NULL;
x_curprefix = 0;
- x_histp = histptr + 1;
+ x_histmcp = x_histp = histptr + 1;
x_last_command = XFUNC_error;
x_init_prompt(true);
@@ -1245,9 +1297,7 @@
return (i);
case KINTR:
/* special case for interrupt */
- trapsig(SIGINT);
- x_mode(false);
- unwind(LSHELL);
+ x_intr(SIGINT, c);
}
/* ad-hoc hack for fixing the cursor position */
x_goto(xcp);
@@ -1266,11 +1316,11 @@
if (c == 0) {
invmbs:
left = 0;
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
if (UTFMODE) {
- if (((c & 0xC0) == 0x80) && left) {
+ if (((rtt2asc(c) & 0xC0) == 0x80) && left) {
str[pos++] = c;
if (!--left) {
str[pos] = '\0';
@@ -1328,7 +1378,7 @@
x_do_ins(const char *cp, size_t len)
{
if (xep + len >= xend) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (-1);
}
memmove(xcp + len, xcp, xep - xcp + 1);
@@ -1355,15 +1405,9 @@
x_lastcp();
x_adj_ok = tobool(xcp >= xlp);
x_zots(cp);
- /* has x_adjust() been called? */
- if (adj == x_adj_done) {
- /* no */
- cp = xlp;
- while (cp > xcp)
- x_bs3(&cp);
- }
- if (xlp == xep - 1)
- x_redraw(xx_cols);
+ if (adj == x_adj_done)
+ /* x_adjust() has not been called */
+ x_lastpos();
x_adj_ok = true;
return (0);
}
@@ -1374,7 +1418,7 @@
ssize_t i = 0;
if (xcp == xbuf) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
do {
@@ -1400,7 +1444,7 @@
}
if (!i) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
x_delete(i, false);
@@ -1470,10 +1514,7 @@
/*x_goto(xcp);*/
x_adj_ok = true;
xlp_valid = false;
- cp = x_lastcp();
- while (cp > xcp)
- x_bs3(&cp);
-
+ x_lastpos();
x_modified();
return;
}
@@ -1513,15 +1554,15 @@
char *cp = xcp;
if (cp == xbuf) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (0);
}
while (x_arg--) {
- while (cp != xbuf && is_mfs(cp[-1])) {
+ while (cp != xbuf && ctype(cp[-1], C_MFS)) {
cp--;
nb++;
}
- while (cp != xbuf && !is_mfs(cp[-1])) {
+ while (cp != xbuf && !ctype(cp[-1], C_MFS)) {
cp--;
nb++;
}
@@ -1537,13 +1578,13 @@
char *cp = xcp;
if (cp == xep) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (0);
}
while (x_arg--) {
- while (cp != xep && is_mfs(*cp))
+ while (cp != xep && ctype(*cp, C_MFS))
cp++;
- while (cp != xep && !is_mfs(*cp))
+ while (cp != xep && !ctype(*cp, C_MFS))
cp++;
}
nc = x_nb2nc(cp - xcp);
@@ -1556,7 +1597,7 @@
x_goto(char *cp)
{
cp = cp >= xep ? xep : x_bs0(cp, xbuf);
- if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
+ if (cp < xbp || cp >= utf_skipcols(xbp, x_displen, NULL)) {
/* we are heading off screen */
xcp = cp;
x_adjust();
@@ -1576,7 +1617,7 @@
{
if (UTFMODE)
while ((!lower_bound || (cp > lower_bound)) &&
- ((*(unsigned char *)cp & 0xC0) == 0x80))
+ ((rtt2asc(*cp) & 0xC0) == 0x80))
--cp;
return (cp);
}
@@ -1593,20 +1634,11 @@
}
static int
-x_size_str(char *cp)
-{
- int size = 0;
- while (*cp)
- size += x_size2(cp, &cp);
- return (size);
-}
-
-static int
x_size2(char *cp, char **dcp)
{
uint8_t c = *(unsigned char *)cp;
- if (UTFMODE && (c > 0x7F))
+ if (UTFMODE && (rtt2asc(c) > 0x7F))
return (utf_widthadj(cp, (const char **)dcp));
if (dcp)
*dcp = cp + 1;
@@ -1613,7 +1645,7 @@
if (c == '\t')
/* Kludge, tabs are always four spaces. */
return (4);
- if (ISCTRL(c) && /* but not C1 */ c < 0x80)
+ if (ksh_isctrl(c))
/* control unsigned char */
return (2);
return (1);
@@ -1636,11 +1668,11 @@
if (c == '\t') {
/* Kludge, tabs are always four spaces. */
- x_e_puts(" ");
+ x_e_puts(T4spaces);
(*cp)++;
- } else if (ISCTRL(c) && /* but not C1 */ c < 0x80) {
+ } else if (ksh_isctrl(c)) {
x_e_putc2('^');
- x_e_putc2(UNCTRL(c));
+ x_e_putc2(ksh_unctrl(c));
(*cp)++;
} else
x_e_putc3((const char **)cp);
@@ -1650,7 +1682,7 @@
x_mv_back(int c MKSH_A_UNUSED)
{
if (xcp == xbuf) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
while (x_arg--) {
@@ -1667,7 +1699,7 @@
char *cp = xcp, *cp2;
if (xcp == xep) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
while (x_arg--) {
@@ -1688,13 +1720,13 @@
*xep = '\0';
if (x_e_getmbc(tmp) < 0) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
while (x_arg--) {
if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
(cp = strstr(xbuf, tmp)) == NULL) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
}
@@ -1709,7 +1741,7 @@
bool b;
if (x_e_getmbc(tmp) < 0) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
for (; x_arg--; cp = p)
@@ -1717,7 +1749,7 @@
if (p-- == xbuf)
p = xep;
if (p == cp) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
if ((tmp[1] && ((p+1) > xep)) ||
@@ -1750,10 +1782,11 @@
static int
x_end_of_text(int c MKSH_A_UNUSED)
{
- char tmp = edchars.eof;
- char *cp = &tmp;
+ unsigned char tmp[1], *cp = tmp;
- x_zotc3(&cp);
+ *tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
+ (unsigned char)CTRL_D;
+ x_zotc3((char **)&cp);
x_putc('\r');
x_putc('\n');
x_flush();
@@ -1806,7 +1839,6 @@
static void
x_load_hist(char **hp)
{
- int oldsize;
char *sp = NULL;
if (hp == histptr + 1) {
@@ -1813,23 +1845,18 @@
sp = holdbufp;
modified = 0;
} else if (hp < history || hp > histptr) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return;
}
if (sp == NULL)
sp = *hp;
x_histp = hp;
- oldsize = x_size_str(xbuf);
if (modified)
strlcpy(holdbufp, xbuf, LINE);
strlcpy(xbuf, sp, xend - xbuf);
xbp = xbuf;
- xep = xcp = xbuf + strlen(xbuf);
- xlp_valid = false;
- if (xep <= x_lastcp()) {
- x_redraw(oldsize);
- }
- x_goto(xep);
+ xep = xcp = strnul(xbuf);
+ x_adjust();
modified = 0;
}
@@ -1836,9 +1863,11 @@
static int
x_nl_next_com(int c MKSH_A_UNUSED)
{
- if (!x_histncp || (x_histp != x_histncp && x_histp != histptr + 1))
+ if (!modified)
+ x_histmcp = x_histp;
+ if (!x_histncp || (x_histmcp != x_histncp && x_histmcp != histptr + 1))
/* fresh start of ^O */
- x_histncp = x_histp;
+ x_histncp = x_histmcp;
x_nextcmd = source->line - (histptr - x_histncp) + 1;
return (x_newline('\n'));
}
@@ -1871,13 +1900,13 @@
if ((c = x_e_getc()) < 0)
return (KSTD);
f = x_tab[0][c];
- if (c == CTRL('[')) {
+ if (c == CTRL_BO) {
if ((f & 0x7F) == XFUNC_meta1) {
if ((c = x_e_getc()) < 0)
return (KSTD);
f = x_tab[1][c] & 0x7F;
if (f == XFUNC_meta1 || f == XFUNC_meta2)
- x_meta1(CTRL('['));
+ x_meta1(CTRL_BO);
x_e_ungetc(c);
}
break;
@@ -1896,8 +1925,10 @@
offset = -1;
break;
}
- if (p > pat)
- *--p = '\0';
+ if (p > pat) {
+ p = x_bs0(p - 1, pat);
+ *p = '\0';
+ }
if (p == pat)
offset = -1;
else
@@ -1907,7 +1938,7 @@
/* add char to pattern */
/* overflow check... */
if ((size_t)(p - pat) >= sizeof(pat) - 1) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
continue;
}
*p++ = c, *p = '\0';
@@ -1932,7 +1963,7 @@
}
}
if (offset < 0)
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
@@ -1953,7 +1984,7 @@
return (i);
}
}
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
x_histp = histptr;
return (-1);
}
@@ -2007,18 +2038,13 @@
static int
x_del_line(int c MKSH_A_UNUSED)
{
- int i, j;
-
*xep = 0;
- i = xep - xbuf;
- j = x_size_str(xbuf);
- xcp = xbuf;
- x_push(i);
+ x_push(xep - (xcp = xbuf));
xlp = xbp = xep = xbuf;
xlp_valid = true;
*xcp = 0;
xmp = NULL;
- x_redraw(j);
+ x_redraw('\r');
x_modified();
return (KSTD);
}
@@ -2031,7 +2057,7 @@
}
static int
-x_mv_begin(int c MKSH_A_UNUSED)
+x_mv_beg(int c MKSH_A_UNUSED)
{
x_goto(xbuf);
return (KSTD);
@@ -2040,89 +2066,82 @@
static int
x_draw_line(int c MKSH_A_UNUSED)
{
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
static int
-x_e_rebuildline(const char *clrstr)
+x_cls(int c MKSH_A_UNUSED)
{
- shf_puts(clrstr, shl_out);
- x_adjust();
+ shf_puts(MKSH_CLS_STRING, shl_out);
+ x_redraw(0);
return (KSTD);
}
-static int
-x_cls(int c MKSH_A_UNUSED)
+/*
+ * clear line from x_col (current cursor position) to xx_cols - 2,
+ * then output lastch, then go back to x_col; if lastch is space,
+ * clear with termcap instead of spaces, or not if line_was_cleared;
+ * lastch MUST be an ASCII character with wcwidth(lastch) == 1
+ */
+static void
+x_clrtoeol(int lastch, bool line_was_cleared)
{
- return (x_e_rebuildline(MKSH_CLS_STRING));
+ int col;
+
+ if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
+ shf_puts(KSH_ESC_STRING "[K", shl_out);
+ line_was_cleared = true;
+ }
+ if (lastch == ' ' && line_was_cleared)
+ return;
+
+ col = x_col;
+ while (col < (xx_cols - 2)) {
+ x_putc(' ');
+ ++col;
+ }
+ x_putc(lastch);
+ ++col;
+ while (col > x_col) {
+ x_putc('\b');
+ --col;
+ }
}
-/*
- * Redraw (part of) the line. If limit is < 0, the everything is redrawn
- * on a NEW line, otherwise limit is the screen column up to which needs
- * redrawing.
- */
+/* output the prompt, assuming a line has just been started */
static void
-x_redraw(int limit)
+x_pprompt(void)
{
- int i, j;
- char *cp;
+ if (prompt_trunc != -1)
+ pprompt(prompt, prompt_trunc);
+ x_col = pwidth;
+}
+/* output CR, then redraw the line, clearing to EOL if needed (cr ≠ 0, LF) */
+static void
+x_redraw(int cr)
+{
+ int lch;
+
x_adj_ok = false;
- if (limit == -1)
- x_e_putc2('\n');
- else
- x_e_putc2('\r');
+ /* clear the line */
+ x_e_putc2(cr ? cr : '\r');
x_flush();
- if (xbp == xbuf) {
- if (prompt_trunc != -1)
- pprompt(prompt, prompt_trunc);
- x_col = pwidth;
- }
+ /* display the prompt */
+ if (xbp == xbuf)
+ x_pprompt();
x_displen = xx_cols - 2 - x_col;
+ /* display the line content */
xlp_valid = false;
x_zots(xbp);
- if (xbp != xbuf || xep > xlp)
- limit = xx_cols;
- if (limit >= 0) {
- if (xep > xlp)
- /* we fill the line */
- i = 0;
- else {
- char *cpl = xbp;
-
- i = limit;
- while (cpl < xlp)
- i -= x_size2(cpl, &cpl);
- }
-
- j = 0;
- while ((j < i) || (x_col < (xx_cols - 2))) {
- if (!(x_col < (xx_cols - 2)))
- break;
- x_e_putc2(' ');
- j++;
- }
- i = ' ';
- if (xep > xlp) {
- /* more off screen */
- if (xbp > xbuf)
- i = '*';
- else
- i = '>';
- } else if (xbp > xbuf)
- i = '<';
- x_e_putc2(i);
- j++;
- while (j--)
- x_e_putc2('\b');
- }
- cp = xlp;
- while (cp > xcp)
- x_bs3(&cp);
+ /* check whether there is more off-screen */
+ lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' ';
+ /* clear the rest of the line */
+ x_clrtoeol(lch, !cr || cr == '\n');
+ /* go back to actual cursor position */
+ x_lastpos();
x_adj_ok = true;
- return;
}
static int
@@ -2145,11 +2164,11 @@
* to the one they want.
*/
if (xcp == xbuf) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
} else if (xcp == xep || Flag(FGMACS)) {
if (xcp - xbuf == 1) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
/*
@@ -2158,12 +2177,12 @@
*/
x_bs3(&xcp);
if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
x_bs3(&xcp);
if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
utf_wctomb(xcp, tmpa);
@@ -2176,12 +2195,12 @@
* cursor, move cursor position along one.
*/
if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
x_bs3(&xcp);
if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
utf_wctomb(xcp, tmpa);
@@ -2215,6 +2234,13 @@
}
static int
+x_meta3(int c MKSH_A_UNUSED)
+{
+ x_curprefix = 3;
+ return (KSTD);
+}
+
+static int
x_kill(int c MKSH_A_UNUSED)
{
size_t col = xcp - xbuf;
@@ -2233,14 +2259,10 @@
}
static void
-x_push(int nchars)
+x_push(size_t nchars)
{
- char *cp;
-
- strndupx(cp, xcp, nchars, AEDIT);
- if (killstack[killsp])
- afree(killstack[killsp], AEDIT);
- killstack[killsp] = cp;
+ afree(killstack[killsp], AEDIT);
+ strndupx(killstack[killsp], xcp, nchars, AEDIT);
killsp = (killsp + 1) % KILLSIZE;
}
@@ -2254,7 +2276,7 @@
killtp--;
if (killstack[killtp] == 0) {
x_e_puts("\nnothing to yank");
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
xmp = xcp;
@@ -2271,7 +2293,7 @@
killstack[killtp] == 0) {
killtp = killsp;
x_e_puts("\nyank something first");
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
len = strlen(killstack[killtp]);
@@ -2287,14 +2309,28 @@
return (KSTD);
}
-static int
-x_abort(int c MKSH_A_UNUSED)
+/* fake receiving an interrupt */
+static void
+x_intr(int signo, int c)
{
- /* x_zotc(c); */
+ x_vi_zotc(c);
+ *xep = '\0';
+ strip_nuls(xbuf, xep - xbuf);
+ if (*xbuf)
+ histsave(&source->line, xbuf, HIST_STORE, true);
xlp = xep = xcp = xbp = xbuf;
xlp_valid = true;
*xcp = 0;
x_modified();
+ x_flush();
+ trapsig(signo);
+ x_mode(false);
+ unwind(LSHELL);
+}
+
+static int
+x_abort(int c MKSH_A_UNUSED)
+{
return (KINTR);
}
@@ -2301,7 +2337,7 @@
static int
x_error(int c MKSH_A_UNUSED)
{
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
@@ -2319,7 +2355,7 @@
case '~':
x_arg = 1;
x_arg_defaulted = true;
- return (x_mv_begin(0));
+ return (x_mv_beg(0));
case ';':
/* "interesting" sequence detected */
break;
@@ -2361,19 +2397,18 @@
strdupx(news, cp, ap);
op = news;
while (*cp) {
- /* XXX -- should handle \^ escape? */
- if (*cp == '^') {
+ switch (*cp) {
+ case '^':
cp++;
- /*XXX or ^^ escape? this is ugly. */
- if (*cp >= '?')
- /* includes '?'; ASCII */
- *op++ = CTRL(*cp);
- else {
- *op++ = '^';
- cp--;
- }
- } else
+ *op++ = ksh_toctrl(*cp);
+ break;
+ case '\\':
+ if (cp[1] == '\\' || cp[1] == '^')
+ ++cp;
+ /* FALLTHROUGH */
+ default:
*op++ = *cp;
+ }
cp++;
}
*op = '\0';
@@ -2386,9 +2421,9 @@
{
char *p = *buf;
- if (ISCTRL(c)) {
+ if (ksh_isctrl(c)) {
*p++ = '^';
- *p++ = UNCTRL(c);
+ *p++ = ksh_unctrl(c);
} else
*p++ = c;
*p = 0;
@@ -2411,9 +2446,9 @@
int f = x_tab[prefix][key];
if (prefix)
- /* prefix == 1 || prefix == 2 */
- shf_puts(x_mapout(prefix == 1 ?
- CTRL('[') : CTRL('X')), shl_stdout);
+ /* 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
@@ -2420,7 +2455,7 @@
shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
if (XFUNC_VALUE(f) != XFUNC_ins_string)
#endif
- shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
+ shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
#ifndef MKSH_SMALL
else
shprintf("'%s'\n", x_atab[prefix][key]);
@@ -2452,7 +2487,7 @@
if (list) {
for (f = 0; f < NELEM(x_ftab); f++)
if (!(x_ftab[f].xf_flags & XF_NOBIND))
- shprintf("%s\n", x_ftab[f].xf_name);
+ shprintf(Tf_sN, x_ftab[f].xf_name);
return (0);
}
if (a1 == NULL) {
@@ -2478,6 +2513,8 @@
prefix = 1;
else if (f == XFUNC_meta2)
prefix = 2;
+ else if (f == XFUNC_meta3)
+ prefix = 3;
else
break;
}
@@ -2491,7 +2528,7 @@
m1 = msg;
while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
x_mapout2(*c++, &m1);
- bi_errorf("%s: %s", "too long key sequence", msg);
+ bi_errorf("too long key sequence: %s", msg);
return (1);
}
#ifndef MKSH_SMALL
@@ -2515,7 +2552,7 @@
if (!strcmp(x_ftab[f].xf_name, a2))
break;
if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
- bi_errorf("%s: %s %s", a2, "no such", Tfunction);
+ bi_errorf("%s: no such function", a2);
return (1);
}
}
@@ -2575,7 +2612,7 @@
char *xr;
if (xmp == NULL) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
if (xmp > xcp) {
@@ -2597,7 +2634,7 @@
char *tmp;
if (xmp == NULL) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
tmp = xmp;
@@ -2675,7 +2712,7 @@
&start, &end, &words);
if (nwords == 0) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
x_goto(xbuf + start);
@@ -2684,8 +2721,8 @@
i = 0;
while (i < nwords) {
if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
- (++i < nwords && x_ins(" ") < 0)) {
- x_e_putc2(7);
+ (++i < nwords && x_ins(T1space) < 0)) {
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
}
@@ -2709,7 +2746,7 @@
&start, &end, &words);
/* no match */
if (nwords == 0) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return;
}
if (type == CT_LIST) {
@@ -2767,9 +2804,9 @@
* append a space if this is a single non-directory match
* and not a parameter or homedir substitution
*/
- if (nwords == 1 && words[0][nlen - 1] != '/' &&
+ if (nwords == 1 && !mksh_cdirsep(words[0][nlen - 1]) &&
!(flags & XCF_IS_NOSPACE)) {
- x_ins(" ");
+ x_ins(T1space);
}
x_free_words(nwords, words);
@@ -2829,7 +2866,7 @@
x_adjust_out:
xlp_valid = false;
- x_redraw(xx_cols);
+ x_redraw('\r');
x_flush();
}
@@ -2866,9 +2903,10 @@
{
int width = 1;
- if (c == '\r' || c == '\n')
+ if (ctype(c, C_CR | C_LF))
x_col = 0;
if (x_col < xx_cols) {
+#ifndef MKSH_EBCDIC
if (UTFMODE && (c > 0x7F)) {
char utf_tmp[3];
size_t x;
@@ -2883,9 +2921,10 @@
x_putc(utf_tmp[2]);
width = utf_wcwidth(c);
} else
+#endif
x_putc(c);
switch (c) {
- case 7:
+ case KSH_BEL:
break;
case '\r':
case '\n':
@@ -2907,7 +2946,7 @@
{
int width = 1, c = **(const unsigned char **)cp;
- if (c == '\r' || c == '\n')
+ if (ctype(c, C_CR | C_LF))
x_col = 0;
if (x_col < xx_cols) {
if (UTFMODE && (c > 0x7F)) {
@@ -2914,14 +2953,24 @@
char *cp2;
width = utf_widthadj(*cp, (const char **)&cp2);
- while (*cp < cp2)
- x_putcf(*(*cp)++);
+ if (cp2 == *cp + 1) {
+ (*cp)++;
+#ifdef MKSH_EBCDIC
+ x_putc(asc2rtt(0xEF));
+ x_putc(asc2rtt(0xBF));
+ x_putc(asc2rtt(0xBD));
+#else
+ shf_puts("\xEF\xBF\xBD", shl_out);
+#endif
+ } else
+ while (*cp < cp2)
+ x_putcf(*(*cp)++);
} else {
(*cp)++;
x_putc(c);
}
switch (c) {
- case 7:
+ case KSH_BEL:
break;
case '\r':
case '\n':
@@ -2965,8 +3014,8 @@
/* strip command prefix */
c &= 255;
- while (c >= 0 && ksh_isdigit(c)) {
- n = n * 10 + (c - '0');
+ while (c >= 0 && ctype(c, C_DIGIT)) {
+ n = n * 10 + ksh_numdig(c);
if (n > LINE)
/* upper bound for repeat */
goto x_set_arg_too_big;
@@ -2975,7 +3024,7 @@
}
if (c < 0 || first) {
x_set_arg_too_big:
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
x_arg = 1;
x_arg_defaulted = true;
} else {
@@ -2990,18 +3039,17 @@
static int
x_comment(int c MKSH_A_UNUSED)
{
- int oldsize = x_size_str(xbuf);
ssize_t len = xep - xbuf;
int ret = x_do_comment(xbuf, xend - xbuf, &len);
if (ret < 0)
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
else {
x_modified();
xep = xbuf + len;
*xep = '\0';
xcp = xbp = xbuf;
- x_redraw(oldsize);
+ x_redraw('\r');
if (ret > 0)
return (x_newline('\n'));
}
@@ -3013,15 +3061,13 @@
{
char *o_xbuf = xbuf, *o_xend = xend;
char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
- int lim = x_lastcp() - xbp;
- size_t vlen;
char *v;
strdupx(v, KSH_VERSION, ATEMP);
xbuf = xbp = xcp = v;
- xend = xep = v + (vlen = strlen(v));
- x_redraw(lim);
+ xend = xep = strnul(v);
+ x_redraw('\r');
x_flush();
c = x_e_getc();
@@ -3030,7 +3076,7 @@
xbp = o_xbp;
xep = o_xep;
xcp = o_xcp;
- x_redraw((int)vlen);
+ x_redraw('\r');
if (c < 0)
return (KSTD);
@@ -3048,22 +3094,22 @@
{
if (x_arg_defaulted) {
if (xep == xbuf) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
if (modified) {
*xep = '\0';
- histsave(&source->line, xbuf, true, true);
+ histsave(&source->line, xbuf, HIST_STORE, true);
x_arg = 0;
} else
x_arg = source->line - (histptr - x_histp);
}
if (x_arg)
- shf_snprintf(xbuf, xend - xbuf, "%s %d",
+ shf_snprintf(xbuf, xend - xbuf, Tf_sd,
"fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
else
strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
- xep = xbuf + strlen(xbuf);
+ xep = strnul(xbuf);
return (x_newline('\n'));
}
#endif
@@ -3103,7 +3149,7 @@
last_arg = x_arg_defaulted ? -1 : x_arg;
xhp = histptr - (m - 1);
if ((xhp < history) || !(cp = *xhp)) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
x_modified();
return (KSTD);
}
@@ -3115,11 +3161,11 @@
/*
* ignore white-space after the last word
*/
- while (rcp > cp && is_cfs(*rcp))
+ while (rcp > cp && ctype(*rcp, C_CFS))
rcp--;
- while (rcp > cp && !is_cfs(*rcp))
+ while (rcp > cp && !ctype(*rcp, C_CFS))
rcp--;
- if (is_cfs(*rcp))
+ if (ctype(*rcp, C_CFS))
rcp++;
x_ins(rcp);
} else {
@@ -3130,16 +3176,16 @@
/*
* ignore white-space at start of line
*/
- while (*rcp && is_cfs(*rcp))
+ while (*rcp && ctype(*rcp, C_CFS))
rcp++;
while (x_arg-- > 0) {
- while (*rcp && !is_cfs(*rcp))
+ while (*rcp && !ctype(*rcp, C_CFS))
rcp++;
- while (*rcp && is_cfs(*rcp))
+ while (*rcp && ctype(*rcp, C_CFS))
rcp++;
}
cp = rcp;
- while (*rcp && !is_cfs(*rcp))
+ while (*rcp && !ctype(*rcp, C_CFS))
rcp++;
ch = *rcp;
*rcp = '\0';
@@ -3146,6 +3192,8 @@
x_ins(cp);
*rcp = ch;
}
+ if (!modified)
+ x_histmcp = x_histp;
modified = m + 1;
return (KSTD);
}
@@ -3189,7 +3237,7 @@
char *cp = xcp;
if (cp == xep) {
- x_e_putc2(7);
+ x_e_putc2(KSH_BEL);
return (KSTD);
}
while (x_arg--) {
@@ -3196,7 +3244,7 @@
/*
* first skip over any white-space
*/
- while (cp != xep && is_mfs(*cp))
+ while (cp != xep && ctype(*cp, C_MFS))
cp++;
/*
* do the first char on its own since it may be
@@ -3214,7 +3262,7 @@
/*
* now for the rest of the word
*/
- while (cp != xep && !is_mfs(*cp)) {
+ while (cp != xep && !ctype(*cp, C_MFS)) {
if (c == 'U')
/* uppercase */
*cp = ksh_toupper(*cp);
@@ -3234,22 +3282,10 @@
* NAME:
* x_lastcp - last visible char
*
- * SYNOPSIS:
- * x_lastcp()
- *
* DESCRIPTION:
* This function returns a pointer to that char in the
* edit buffer that will be the last displayed on the
- * screen. The sequence:
- *
- * cp = x_lastcp();
- * while (cp > xcp)
- * x_bs3(&cp);
- *
- * Will position the cursor correctly on the screen.
- *
- * RETURN VALUE:
- * cp or NULL
+ * screen.
*/
static char *
x_lastcp(void)
@@ -3271,7 +3307,17 @@
return (xlp);
}
+/* correctly position the cursor on the screen from end of visible area */
static void
+x_lastpos(void)
+{
+ char *cp = x_lastcp();
+
+ while (cp > xcp)
+ x_bs3(&cp);
+}
+
+static void
x_mode(bool onoff)
{
static bool x_cur_mode;
@@ -3283,42 +3329,41 @@
if (onoff) {
x_mkraw(tty_fd, NULL, false);
- edchars.erase = tty_state.c_cc[VERASE];
- edchars.kill = tty_state.c_cc[VKILL];
- edchars.intr = tty_state.c_cc[VINTR];
- edchars.quit = tty_state.c_cc[VQUIT];
- edchars.eof = tty_state.c_cc[VEOF];
+ edchars.erase = toedchar(tty_state.c_cc[VERASE]);
+ edchars.kill = toedchar(tty_state.c_cc[VKILL]);
+ edchars.intr = toedchar(tty_state.c_cc[VINTR]);
+ edchars.quit = toedchar(tty_state.c_cc[VQUIT]);
+ edchars.eof = toedchar(tty_state.c_cc[VEOF]);
#ifdef VWERASE
- edchars.werase = tty_state.c_cc[VWERASE];
+ edchars.werase = toedchar(tty_state.c_cc[VWERASE]);
+#else
+ edchars.werase = 0;
#endif
-#ifdef _POSIX_VDISABLE
- /* Convert unset values to internal 'unset' value */
- if (edchars.erase == _POSIX_VDISABLE)
- edchars.erase = -1;
- if (edchars.kill == _POSIX_VDISABLE)
- edchars.kill = -1;
- if (edchars.intr == _POSIX_VDISABLE)
- edchars.intr = -1;
- if (edchars.quit == _POSIX_VDISABLE)
- edchars.quit = -1;
- if (edchars.eof == _POSIX_VDISABLE)
- edchars.eof = -1;
- if (edchars.werase == _POSIX_VDISABLE)
- edchars.werase = -1;
-#endif
+ if (!edchars.erase)
+ edchars.erase = CTRL_H;
+ if (!edchars.kill)
+ edchars.kill = CTRL_U;
+ if (!edchars.intr)
+ edchars.intr = CTRL_C;
+ if (!edchars.quit)
+ edchars.quit = CTRL_BK;
+ if (!edchars.eof)
+ edchars.eof = CTRL_D;
+ if (!edchars.werase)
+ edchars.werase = CTRL_W;
- if (edchars.erase >= 0) {
+ if (isedchar(edchars.erase)) {
bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
}
- if (edchars.kill >= 0)
+ if (isedchar(edchars.kill))
bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
- if (edchars.werase >= 0)
+ if (isedchar(edchars.werase))
bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
- if (edchars.intr >= 0)
+ if (isedchar(edchars.intr))
bind_if_not_bound(0, edchars.intr, XFUNC_abort);
- if (edchars.quit >= 0)
+ if (isedchar(edchars.quit))
bind_if_not_bound(0, edchars.quit, XFUNC_noop);
} else
mksh_tcset(tty_fd, &tty_state);
@@ -3340,6 +3385,7 @@
static int vi_insert(int);
static int vi_cmd(int, const char *);
static int domove(int, const char *, int);
+static int domovebeg(void);
static int redo_insert(int);
static void yank_range(int, int);
static int bracktype(int);
@@ -3366,12 +3412,10 @@
static int expand_word(int);
static int complete_word(int, int);
static int print_expansions(struct edstate *, int);
-#define char_len(c) ((ISCTRL((unsigned char)c) && \
- /* but not C1 */ (unsigned char)c < 0x80) ? 2 : 1)
-static void x_vi_zotc(int);
static void vi_error(void);
static void vi_macro_reset(void);
static int x_vi_putbuf(const char *, size_t);
+#define char_len(c) (ksh_isctrl(c) ? 2 : 1)
#define vC 0x01 /* a valid command that isn't a vM, vE, vU */
#define vM 0x02 /* movement command (h, l, etc.) */
@@ -3382,14 +3426,14 @@
#define vZ 0x40 /* repeat count defaults to 0 (not 1) */
#define vS 0x80 /* search (/, ?) */
-#define is_bad(c) (classify[(c)&0x7f]&vB)
-#define is_cmd(c) (classify[(c)&0x7f]&(vM|vE|vC|vU))
-#define is_move(c) (classify[(c)&0x7f]&vM)
-#define is_extend(c) (classify[(c)&0x7f]&vE)
-#define is_long(c) (classify[(c)&0x7f]&vX)
-#define is_undoable(c) (!(classify[(c)&0x7f]&vU))
-#define is_srch(c) (classify[(c)&0x7f]&vS)
-#define is_zerocount(c) (classify[(c)&0x7f]&vZ)
+#define is_bad(c) (classify[rtt2asc(c) & 0x7F] & vB)
+#define is_cmd(c) (classify[rtt2asc(c) & 0x7F] & (vM | vE | vC | vU))
+#define is_move(c) (classify[rtt2asc(c) & 0x7F] & vM)
+#define is_extend(c) (classify[rtt2asc(c) & 0x7F] & vE)
+#define is_long(c) (classify[rtt2asc(c) & 0x7F] & vX)
+#define is_undoable(c) (!(classify[rtt2asc(c) & 0x7F] & vU))
+#define is_srch(c) (classify[rtt2asc(c) & 0x7F] & vS)
+#define is_zerocount(c) (classify[rtt2asc(c) & 0x7F] & vZ)
static const unsigned char classify[128] = {
/* 0 1 2 3 4 5 6 7 */
@@ -3453,7 +3497,7 @@
static struct edstate ebuf;
static struct edstate undobuf;
-static struct edstate *es; /* current editor state */
+static struct edstate *vs; /* current Vi editing mode state */
static struct edstate *undo;
static char *ibuf; /* input buffer */
@@ -3517,7 +3561,7 @@
undobuf.linelen = ebuf.linelen = 0;
undobuf.cursor = ebuf.cursor = 0;
undobuf.winleft = ebuf.winleft = 0;
- es = &ebuf;
+ vs = &ebuf;
undo = &undobuf;
x_init_prompt(true);
@@ -3557,16 +3601,20 @@
if (c == -1)
break;
if (state != VLIT) {
- if (c == edchars.intr || c == edchars.quit) {
+ if (isched(c, edchars.intr) ||
+ isched(c, edchars.quit)) {
+ /* shove input buffer away */
+ xbuf = ebuf.cbuf;
+ xep = xbuf;
+ if (ebuf.linelen > 0)
+ xep += ebuf.linelen;
/* pretend we got an interrupt */
- x_vi_zotc(c);
- x_flush();
- trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
- x_mode(false);
- unwind(LSHELL);
- } else if (c == edchars.eof && state != VVERSION) {
- if (es->linelen == 0) {
- x_vi_zotc(edchars.eof);
+ x_intr(isched(c, edchars.intr) ?
+ SIGINT : SIGQUIT, c);
+ } else if (isched(c, edchars.eof) &&
+ state != VVERSION) {
+ if (vs->linelen == 0) {
+ x_vi_zotc(c);
c = -1;
break;
}
@@ -3582,15 +3630,15 @@
x_putc('\n');
x_flush();
- if (c == -1 || (ssize_t)LINE <= es->linelen)
+ if (c == -1 || (ssize_t)LINE <= vs->linelen)
return (-1);
- if (es->cbuf != buf)
- memcpy(buf, es->cbuf, es->linelen);
+ if (vs->cbuf != buf)
+ memcpy(buf, vs->cbuf, vs->linelen);
- buf[es->linelen++] = '\n';
+ buf[vs->linelen++] = '\n';
- return (es->linelen);
+ return (vs->linelen);
}
static int
@@ -3602,19 +3650,32 @@
switch (state) {
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 (insert != 0) {
- if (ch == CTRL('v')) {
+ if (ch == CTRL_V) {
state = VLIT;
ch = '^';
}
switch (vi_insert(ch)) {
case -1:
+ vi_insert_failed:
vi_error();
state = VNORMAL;
break;
case 0:
if (state == VLIT) {
- es->cursor--;
+ vs->cursor--;
refresh(0);
} else
refresh(insert != 0);
@@ -3623,20 +3684,21 @@
return (1);
}
} else {
- if (ch == '\r' || ch == '\n')
+ if (ctype(ch, C_CR | C_LF))
return (1);
cmdlen = 0;
argc1 = 0;
- if (ch >= '1' && ch <= '9') {
- argc1 = ch - '0';
+ if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) {
+ argc1 = ksh_numdig(ch);
state = VARG1;
} else {
+ pseudo_vi_command:
curcmd[cmdlen++] = ch;
state = nextstate(ch);
if (state == VSEARCH) {
save_cbuf();
- es->cursor = 0;
- es->linelen = 0;
+ vs->cursor = 0;
+ vs->linelen = 0;
if (putbuf(ch == '/' ? "/" : "?", 1,
false) != 0)
return (-1);
@@ -3644,8 +3706,8 @@
}
if (state == VVERSION) {
save_cbuf();
- es->cursor = 0;
- es->linelen = 0;
+ vs->cursor = 0;
+ vs->linelen = 0;
putbuf(KSH_VERSION,
strlen(KSH_VERSION), false);
refresh(0);
@@ -3656,10 +3718,10 @@
case VLIT:
if (is_bad(ch)) {
- del_range(es->cursor, es->cursor + 1);
+ del_range(vs->cursor, vs->cursor + 1);
vi_error();
} else
- es->cbuf[es->cursor++] = ch;
+ vs->cbuf[vs->cursor++] = ch;
refresh(1);
state = VNORMAL;
break;
@@ -3671,8 +3733,8 @@
break;
case VARG1:
- if (ksh_isdigit(ch))
- argc1 = argc1 * 10 + ch - '0';
+ if (ctype(ch, C_DIGIT))
+ argc1 = argc1 * 10 + ksh_numdig(ch);
else {
curcmd[cmdlen++] = ch;
state = nextstate(ch);
@@ -3681,8 +3743,8 @@
case VEXTCMD:
argc2 = 0;
- if (ch >= '1' && ch <= '9') {
- argc2 = ch - '0';
+ if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) {
+ argc2 = ksh_numdig(ch);
state = VARG2;
return (0);
} else {
@@ -3697,8 +3759,8 @@
break;
case VARG2:
- if (ksh_isdigit(ch))
- argc2 = argc2 * 10 + ch - '0';
+ if (ctype(ch, C_DIGIT))
+ argc2 = argc2 * 10 + ksh_numdig(ch);
else {
if (argc1 == 0)
argc1 = argc2;
@@ -3715,7 +3777,7 @@
break;
case VXCH:
- if (ch == CTRL('['))
+ if (ch == CTRL_BO)
state = VNORMAL;
else {
curcmd[cmdlen++] = ch;
@@ -3724,7 +3786,7 @@
break;
case VSEARCH:
- if (ch == '\r' || ch == '\n' /*|| ch == CTRL('[')*/ ) {
+ if (ctype(ch, C_CR | C_LF) /* || ch == CTRL_BO */ ) {
restore_cbuf();
/* Repeat last search? */
if (srchlen == 0) {
@@ -3739,11 +3801,11 @@
memcpy(srchpat, locpat, srchlen + 1);
}
state = VCMD;
- } else if (ch == edchars.erase || ch == CTRL('h')) {
+ } else if (isched(ch, edchars.erase) || ch == CTRL_H) {
if (srchlen != 0) {
srchlen--;
- es->linelen -= char_len(locpat[srchlen]);
- es->cursor = es->linelen;
+ vs->linelen -= char_len(locpat[srchlen]);
+ vs->cursor = vs->linelen;
refresh(0);
return (0);
}
@@ -3750,13 +3812,13 @@
restore_cbuf();
state = VNORMAL;
refresh(0);
- } else if (ch == edchars.kill) {
+ } else if (isched(ch, edchars.kill)) {
srchlen = 0;
- es->linelen = 1;
- es->cursor = 1;
+ vs->linelen = 1;
+ vs->cursor = 1;
refresh(0);
return (0);
- } else if (ch == edchars.werase) {
+ } else if (isched(ch, edchars.werase)) {
unsigned int i, n;
struct edstate new_es, *save_es;
@@ -3763,16 +3825,16 @@
new_es.cursor = srchlen;
new_es.cbuf = locpat;
- save_es = es;
- es = &new_es;
+ save_es = vs;
+ vs = &new_es;
n = backword(1);
- es = save_es;
+ vs = save_es;
i = (unsigned)srchlen;
while (--i >= n)
- es->linelen -= char_len(locpat[i]);
+ vs->linelen -= char_len(locpat[i]);
srchlen = (int)n;
- es->cursor = es->linelen;
+ vs->cursor = vs->linelen;
refresh(0);
return (0);
} else {
@@ -3780,18 +3842,18 @@
vi_error();
else {
locpat[srchlen++] = ch;
- if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
- if ((size_t)es->linelen + 2 >
- (size_t)es->cbufsize)
+ if (ksh_isctrl(ch)) {
+ if ((size_t)vs->linelen + 2 >
+ (size_t)vs->cbufsize)
vi_error();
- es->cbuf[es->linelen++] = '^';
- es->cbuf[es->linelen++] = UNCTRL(ch);
+ vs->cbuf[vs->linelen++] = '^';
+ vs->cbuf[vs->linelen++] = ksh_unctrl(ch);
} else {
- if (es->linelen >= es->cbufsize)
+ if (vs->linelen >= vs->cbufsize)
vi_error();
- es->cbuf[es->linelen++] = ch;
+ vs->cbuf[vs->linelen++] = ch;
}
- es->cursor = es->linelen;
+ vs->cursor = vs->linelen;
refresh(0);
}
return (0);
@@ -3799,22 +3861,23 @@
break;
case VPREFIX2:
+ vi_xfunc_search_up:
state = VFAIL;
switch (ch) {
case 'A':
/* the cursor may not be at the BOL */
- if (!es->cursor)
+ if (!vs->cursor)
break;
/* nor further in the line than we can search for */
- if ((size_t)es->cursor >= sizeof(srchpat) - 1)
- es->cursor = sizeof(srchpat) - 2;
+ if ((size_t)vs->cursor >= sizeof(srchpat) - 1)
+ vs->cursor = sizeof(srchpat) - 2;
/* anchor the search pattern */
srchpat[0] = '^';
/* take the current line up to the cursor */
- memmove(srchpat + 1, es->cbuf, es->cursor);
- srchpat[es->cursor + 1] = '\0';
+ memmove(srchpat + 1, vs->cbuf, vs->cursor);
+ srchpat[vs->cursor + 1] = '\0';
/* set a magic flag */
- argc1 = 2 + (int)es->cursor;
+ argc1 = 2 + (int)vs->cursor;
/* and emulate a backwards history search */
lastsearch = '/';
*curcmd = 'n';
@@ -3857,8 +3920,8 @@
break;
case 0:
if (insert != 0) {
- if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
- lastcmd[0] == 'C') {
+ if (lastcmd[0] == 's' ||
+ ksh_eq(lastcmd[0], 'C', 'c')) {
if (redo_insert(1) != 0)
vi_error();
} else {
@@ -3896,7 +3959,7 @@
return (VXCH);
else if (ch == '.')
return (VREDO);
- else if (ch == CTRL('v'))
+ else if (ch == CTRL_V)
return (VVERSION);
else if (is_cmd(ch))
return (VCMD);
@@ -3909,54 +3972,54 @@
{
int tcursor;
- if (ch == edchars.erase || ch == CTRL('h')) {
+ if (isched(ch, edchars.erase) || ch == CTRL_H) {
if (insert == REPLACE) {
- if (es->cursor == undo->cursor) {
+ if (vs->cursor == undo->cursor) {
vi_error();
return (0);
}
if (inslen > 0)
inslen--;
- es->cursor--;
- if (es->cursor >= undo->linelen)
- es->linelen--;
+ vs->cursor--;
+ if (vs->cursor >= undo->linelen)
+ vs->linelen--;
else
- es->cbuf[es->cursor] = undo->cbuf[es->cursor];
+ vs->cbuf[vs->cursor] = undo->cbuf[vs->cursor];
} else {
- if (es->cursor == 0)
+ if (vs->cursor == 0)
return (0);
if (inslen > 0)
inslen--;
- es->cursor--;
- es->linelen--;
- memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1],
- es->linelen - es->cursor + 1);
+ vs->cursor--;
+ vs->linelen--;
+ memmove(&vs->cbuf[vs->cursor], &vs->cbuf[vs->cursor + 1],
+ vs->linelen - vs->cursor + 1);
}
expanded = NONE;
return (0);
}
- if (ch == edchars.kill) {
- if (es->cursor != 0) {
+ if (isched(ch, edchars.kill)) {
+ if (vs->cursor != 0) {
inslen = 0;
- memmove(es->cbuf, &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen -= es->cursor;
- es->cursor = 0;
+ memmove(vs->cbuf, &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen -= vs->cursor;
+ vs->cursor = 0;
}
expanded = NONE;
return (0);
}
- if (ch == edchars.werase) {
- if (es->cursor != 0) {
+ if (isched(ch, edchars.werase)) {
+ if (vs->cursor != 0) {
tcursor = backword(1);
- memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen -= es->cursor - tcursor;
- if (inslen < es->cursor - tcursor)
+ memmove(&vs->cbuf[tcursor], &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen -= vs->cursor - tcursor;
+ if (inslen < vs->cursor - tcursor)
inslen = 0;
else
- inslen -= es->cursor - tcursor;
- es->cursor = tcursor;
+ inslen -= vs->cursor - tcursor;
+ vs->cursor = tcursor;
}
expanded = NONE;
return (0);
@@ -3966,7 +4029,7 @@
* buffer (if user inserts & deletes char, ibuf gets trashed and
* we don't want to use it)
*/
- if (first_insert && ch != CTRL('['))
+ if (first_insert && ch != CTRL_BO)
saved_inslen = 0;
switch (ch) {
case '\0':
@@ -3976,7 +4039,7 @@
case '\n':
return (1);
- case CTRL('['):
+ case CTRL_BO:
expanded = NONE;
if (first_insert) {
first_insert = false;
@@ -3987,45 +4050,44 @@
lastcmd[0] = 'a';
lastac = 1;
}
- if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
- lastcmd[0] == 'C')
+ if (lastcmd[0] == 's' || ksh_eq(lastcmd[0], 'C', 'c'))
return (redo_insert(0));
else
return (redo_insert(lastac - 1));
- /* { Begin nonstandard vi commands */
- case CTRL('x'):
+ /* { start nonstandard vi commands */
+ case CTRL_X:
expand_word(0);
break;
- case CTRL('f'):
+ case CTRL_F:
complete_word(0, 0);
break;
- case CTRL('e'):
- print_expansions(es, 0);
+ case CTRL_E:
+ print_expansions(vs, 0);
break;
- case CTRL('i'):
+ case CTRL_I:
if (Flag(FVITABCOMPLETE)) {
complete_word(0, 0);
break;
}
/* FALLTHROUGH */
- /* End nonstandard vi commands } */
+ /* end nonstandard vi commands } */
default:
- if (es->linelen >= es->cbufsize - 1)
+ if (vs->linelen >= vs->cbufsize - 1)
return (-1);
ibuf[inslen++] = ch;
if (insert == INSERT) {
- memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen++;
+ memmove(&vs->cbuf[vs->cursor + 1], &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen++;
}
- es->cbuf[es->cursor++] = ch;
- if (insert == REPLACE && es->cursor > es->linelen)
- es->linelen++;
+ vs->cbuf[vs->cursor++] = ch;
+ if (insert == REPLACE && vs->cursor > vs->linelen)
+ vs->linelen++;
expanded = NONE;
}
return (0);
@@ -4044,29 +4106,29 @@
if (is_move(*cmd)) {
if ((cur = domove(argcnt, cmd, 0)) >= 0) {
- if (cur == es->linelen && cur != 0)
+ if (cur == vs->linelen && cur != 0)
cur--;
- es->cursor = cur;
+ vs->cursor = cur;
} else
return (-1);
} else {
/* Don't save state in middle of macro.. */
if (is_undoable(*cmd) && !macro.p) {
- undo->winleft = es->winleft;
- memmove(undo->cbuf, es->cbuf, es->linelen);
- undo->linelen = es->linelen;
- undo->cursor = es->cursor;
+ undo->winleft = vs->winleft;
+ memmove(undo->cbuf, vs->cbuf, vs->linelen);
+ undo->linelen = vs->linelen;
+ undo->cursor = vs->cursor;
lastac = argcnt;
memmove(lastcmd, cmd, MAXVICMD);
}
- switch (*cmd) {
+ switch (ord(*cmd)) {
- case CTRL('l'):
- case CTRL('r'):
+ case CTRL_L:
+ case CTRL_R:
redraw_line(true);
break;
- case '@':
+ case ORD('@'):
{
static char alias[] = "_\0";
struct tbl *ap;
@@ -4107,58 +4169,59 @@
}
break;
- case 'a':
+ case ORD('a'):
modified = 1;
hnum = hlast;
- if (es->linelen != 0)
- es->cursor++;
+ if (vs->linelen != 0)
+ vs->cursor++;
insert = INSERT;
break;
- case 'A':
+ case ORD('A'):
modified = 1;
hnum = hlast;
del_range(0, 0);
- es->cursor = es->linelen;
+ vs->cursor = vs->linelen;
insert = INSERT;
break;
- case 'S':
- es->cursor = domove(1, "^", 1);
- del_range(es->cursor, es->linelen);
+ case ORD('S'):
+ vs->cursor = domovebeg();
+ del_range(vs->cursor, vs->linelen);
modified = 1;
hnum = hlast;
insert = INSERT;
break;
- case 'Y':
+ case ORD('Y'):
cmd = "y$";
/* ahhhhhh... */
- case 'c':
- case 'd':
- case 'y':
+
+ /* FALLTHROUGH */
+ case ORD('c'):
+ case ORD('d'):
+ case ORD('y'):
if (*cmd == cmd[1]) {
- c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
- c2 = es->linelen;
+ c1 = *cmd == 'c' ? domovebeg() : 0;
+ c2 = vs->linelen;
} else if (!is_move(cmd[1]))
return (-1);
else {
if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
return (-1);
- if (*cmd == 'c' &&
- (cmd[1] == 'w' || cmd[1] == 'W') &&
- !ksh_isspace(es->cbuf[es->cursor])) {
+ if (*cmd == 'c' && ksh_eq(cmd[1], 'W', 'w') &&
+ !ctype(vs->cbuf[vs->cursor], C_SPACE)) {
do {
--ncursor;
- } while (ksh_isspace(es->cbuf[ncursor]));
+ } while (ctype(vs->cbuf[ncursor], C_SPACE));
ncursor++;
}
- if (ncursor > es->cursor) {
- c1 = es->cursor;
+ if (ncursor > vs->cursor) {
+ c1 = vs->cursor;
c2 = ncursor;
} else {
c1 = ncursor;
- c2 = es->cursor;
+ c2 = vs->cursor;
if (cmd[1] == '%')
c2++;
}
@@ -4167,7 +4230,7 @@
yank_range(c1, c2);
if (*cmd != 'y') {
del_range(c1, c2);
- es->cursor = c1;
+ vs->cursor = c1;
}
if (*cmd == 'c') {
modified = 1;
@@ -4176,21 +4239,21 @@
}
break;
- case 'p':
+ case ORD('p'):
modified = 1;
hnum = hlast;
- if (es->linelen != 0)
- es->cursor++;
+ if (vs->linelen != 0)
+ vs->cursor++;
while (putbuf(ybuf, yanklen, false) == 0 &&
--argcnt > 0)
;
- if (es->cursor != 0)
- es->cursor--;
+ if (vs->cursor != 0)
+ vs->cursor--;
if (argcnt != 0)
return (-1);
break;
- case 'P':
+ case ORD('P'):
modified = 1;
hnum = hlast;
any = 0;
@@ -4197,31 +4260,31 @@
while (putbuf(ybuf, yanklen, false) == 0 &&
--argcnt > 0)
any = 1;
- if (any && es->cursor != 0)
- es->cursor--;
+ if (any && vs->cursor != 0)
+ vs->cursor--;
if (argcnt != 0)
return (-1);
break;
- case 'C':
+ case ORD('C'):
modified = 1;
hnum = hlast;
- del_range(es->cursor, es->linelen);
+ del_range(vs->cursor, vs->linelen);
insert = INSERT;
break;
- case 'D':
- yank_range(es->cursor, es->linelen);
- del_range(es->cursor, es->linelen);
- if (es->cursor != 0)
- es->cursor--;
+ case ORD('D'):
+ yank_range(vs->cursor, vs->linelen);
+ del_range(vs->cursor, vs->linelen);
+ if (vs->cursor != 0)
+ vs->cursor--;
break;
- case 'g':
+ case ORD('g'):
if (!argcnt)
argcnt = hlast;
/* FALLTHROUGH */
- case 'G':
+ case ORD('G'):
if (!argcnt)
argcnt = 1;
else
@@ -4234,22 +4297,22 @@
}
break;
- case 'i':
+ case ORD('i'):
modified = 1;
hnum = hlast;
insert = INSERT;
break;
- case 'I':
+ case ORD('I'):
modified = 1;
hnum = hlast;
- es->cursor = domove(1, "^", 1);
+ vs->cursor = domovebeg();
insert = INSERT;
break;
- case 'j':
- case '+':
- case CTRL('n'):
+ case ORD('j'):
+ case ORD('+'):
+ case CTRL_N:
if (grabhist(modified, hnum + argcnt) < 0)
return (-1);
else {
@@ -4258,9 +4321,9 @@
}
break;
- case 'k':
- case '-':
- case CTRL('p'):
+ case ORD('k'):
+ case ORD('-'):
+ case CTRL_P:
if (grabhist(modified, hnum - argcnt) < 0)
return (-1);
else {
@@ -4269,8 +4332,8 @@
}
break;
- case 'r':
- if (es->linelen == 0)
+ case ORD('r'):
+ if (vs->linelen == 0)
return (-1);
modified = 1;
hnum = hlast;
@@ -4279,85 +4342,85 @@
else {
int n;
- if (es->cursor + argcnt > es->linelen)
+ if (vs->cursor + argcnt > vs->linelen)
return (-1);
for (n = 0; n < argcnt; ++n)
- es->cbuf[es->cursor + n] = cmd[1];
- es->cursor += n - 1;
+ vs->cbuf[vs->cursor + n] = cmd[1];
+ vs->cursor += n - 1;
}
break;
- case 'R':
+ case ORD('R'):
modified = 1;
hnum = hlast;
insert = REPLACE;
break;
- case 's':
- if (es->linelen == 0)
+ case ORD('s'):
+ if (vs->linelen == 0)
return (-1);
modified = 1;
hnum = hlast;
- if (es->cursor + argcnt > es->linelen)
- argcnt = es->linelen - es->cursor;
- del_range(es->cursor, es->cursor + argcnt);
+ if (vs->cursor + argcnt > vs->linelen)
+ argcnt = vs->linelen - vs->cursor;
+ del_range(vs->cursor, vs->cursor + argcnt);
insert = INSERT;
break;
- case 'v':
+ case ORD('v'):
if (!argcnt) {
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
if (modified) {
- es->cbuf[es->linelen] = '\0';
- histsave(&source->line, es->cbuf, true,
- true);
+ vs->cbuf[vs->linelen] = '\0';
+ histsave(&source->line, vs->cbuf,
+ HIST_STORE, true);
} else
argcnt = source->line + 1 -
(hlast - hnum);
}
if (argcnt)
- shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
+ shf_snprintf(vs->cbuf, vs->cbufsize, Tf_sd,
"fc -e ${VISUAL:-${EDITOR:-vi}} --",
argcnt);
else
- strlcpy(es->cbuf,
+ strlcpy(vs->cbuf,
"fc -e ${VISUAL:-${EDITOR:-vi}} --",
- es->cbufsize);
- es->linelen = strlen(es->cbuf);
+ vs->cbufsize);
+ vs->linelen = strlen(vs->cbuf);
return (2);
- case 'x':
- if (es->linelen == 0)
+ case ORD('x'):
+ if (vs->linelen == 0)
return (-1);
modified = 1;
hnum = hlast;
- if (es->cursor + argcnt > es->linelen)
- argcnt = es->linelen - es->cursor;
- yank_range(es->cursor, es->cursor + argcnt);
- del_range(es->cursor, es->cursor + argcnt);
+ if (vs->cursor + argcnt > vs->linelen)
+ argcnt = vs->linelen - vs->cursor;
+ yank_range(vs->cursor, vs->cursor + argcnt);
+ del_range(vs->cursor, vs->cursor + argcnt);
break;
- case 'X':
- if (es->cursor > 0) {
+ case ORD('X'):
+ if (vs->cursor > 0) {
modified = 1;
hnum = hlast;
- if (es->cursor < argcnt)
- argcnt = es->cursor;
- yank_range(es->cursor - argcnt, es->cursor);
- del_range(es->cursor - argcnt, es->cursor);
- es->cursor -= argcnt;
+ if (vs->cursor < argcnt)
+ argcnt = vs->cursor;
+ yank_range(vs->cursor - argcnt, vs->cursor);
+ del_range(vs->cursor - argcnt, vs->cursor);
+ vs->cursor -= argcnt;
} else
return (-1);
break;
- case 'u':
- t = es;
- es = undo;
+ case ORD('u'):
+ t = vs;
+ vs = undo;
undo = t;
break;
- case 'U':
+ case ORD('U'):
if (!modified)
return (-1);
if (grabhist(modified, ohnum) < 0)
@@ -4366,17 +4429,19 @@
hnum = ohnum;
break;
- case '?':
+ case ORD('?'):
if (hnum == hlast)
hnum = -1;
/* ahhh */
- case '/':
+
+ /* FALLTHROUGH */
+ case ORD('/'):
c3 = 1;
srchlen = 0;
lastsearch = *cmd;
/* FALLTHROUGH */
- case 'n':
- case 'N':
+ case ORD('n'):
+ case ORD('N'):
if (lastsearch == ' ')
return (-1);
if (lastsearch == '?')
@@ -4399,11 +4464,11 @@
}
if (argcnt >= 2) {
/* flag from cursor-up command */
- es->cursor = argcnt - 2;
+ vs->cursor = argcnt - 2;
return (0);
}
break;
- case '_':
+ case ORD('_'):
{
bool inspace;
char *p, *sp;
@@ -4411,14 +4476,13 @@
if (histnum(-1) < 0)
return (-1);
p = *histpos();
-#define issp(c) (ksh_isspace(c) || (c) == '\n')
if (argcnt) {
- while (*p && issp(*p))
+ while (ctype(*p, C_SPACE))
p++;
while (*p && --argcnt) {
- while (*p && !issp(*p))
+ while (*p && !ctype(*p, C_SPACE))
p++;
- while (*p && issp(*p))
+ while (ctype(*p, C_SPACE))
p++;
}
if (!*p)
@@ -4428,7 +4492,7 @@
sp = p;
inspace = false;
while (*p) {
- if (issp(*p))
+ if (ctype(*p, C_SPACE))
inspace = true;
else if (inspace) {
inspace = false;
@@ -4440,16 +4504,16 @@
}
modified = 1;
hnum = hlast;
- if (es->cursor != es->linelen)
- es->cursor++;
- while (*p && !issp(*p)) {
+ if (vs->cursor != vs->linelen)
+ vs->cursor++;
+ while (*p && !ctype(*p, C_SPACE)) {
argcnt++;
p++;
}
- if (putbuf(" ", 1, false) != 0 ||
+ if (putbuf(T1space, 1, false) != 0 ||
putbuf(sp, argcnt, false) != 0) {
- if (es->cursor != 0)
- es->cursor--;
+ if (vs->cursor != 0)
+ vs->cursor--;
return (-1);
}
insert = INSERT;
@@ -4456,49 +4520,49 @@
}
break;
- case '~':
+ case ORD('~'):
{
char *p;
int i;
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
for (i = 0; i < argcnt; i++) {
- p = &es->cbuf[es->cursor];
- if (ksh_islower(*p)) {
+ p = &vs->cbuf[vs->cursor];
+ if (ctype(*p, C_LOWER)) {
modified = 1;
hnum = hlast;
*p = ksh_toupper(*p);
- } else if (ksh_isupper(*p)) {
+ } else if (ctype(*p, C_UPPER)) {
modified = 1;
hnum = hlast;
*p = ksh_tolower(*p);
}
- if (es->cursor < es->linelen - 1)
- es->cursor++;
+ if (vs->cursor < vs->linelen - 1)
+ vs->cursor++;
}
break;
}
- case '#':
+ case ORD('#'):
{
- int ret = x_do_comment(es->cbuf, es->cbufsize,
- &es->linelen);
+ int ret = x_do_comment(vs->cbuf, vs->cbufsize,
+ &vs->linelen);
if (ret >= 0)
- es->cursor = 0;
+ vs->cursor = 0;
return (ret);
}
/* AT&T ksh */
- case '=':
+ case ORD('='):
/* Nonstandard vi/ksh */
- case CTRL('e'):
- print_expansions(es, 1);
+ case CTRL_E:
+ print_expansions(vs, 1);
break;
/* Nonstandard vi/ksh */
- case CTRL('i'):
+ case CTRL_I:
if (!Flag(FVITABCOMPLETE))
return (-1);
complete_word(1, argcnt);
@@ -4505,36 +4569,37 @@
break;
/* some annoying AT&T kshs */
- case CTRL('['):
+ case CTRL_BO:
if (!Flag(FVIESCCOMPLETE))
return (-1);
+ /* FALLTHROUGH */
/* AT&T ksh */
- case '\\':
+ case ORD('\\'):
/* Nonstandard vi/ksh */
- case CTRL('f'):
+ case CTRL_F:
complete_word(1, argcnt);
break;
/* AT&T ksh */
- case '*':
+ case ORD('*'):
/* Nonstandard vi/ksh */
- case CTRL('x'):
+ case CTRL_X:
expand_word(1);
break;
/* mksh: cursor movement */
- case '[':
- case 'O':
+ case ORD('['):
+ case ORD('O'):
state = VPREFIX2;
- if (es->linelen != 0)
- es->cursor++;
+ if (vs->linelen != 0)
+ vs->cursor++;
insert = INSERT;
return (0);
}
- if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
- es->cursor--;
+ if (insert == 0 && vs->cursor != 0 && vs->cursor >= vs->linelen)
+ vs->cursor--;
}
return (0);
}
@@ -4542,51 +4607,50 @@
static int
domove(int argcnt, const char *cmd, int sub)
{
- int bcount, i = 0, t;
- int ncursor = 0;
+ int ncursor = 0, i = 0, t;
+ unsigned int bcount;
- switch (*cmd) {
- case 'b':
- if (!sub && es->cursor == 0)
+ switch (ord(*cmd)) {
+ case ORD('b'):
+ if (!sub && vs->cursor == 0)
return (-1);
ncursor = backword(argcnt);
break;
- case 'B':
- if (!sub && es->cursor == 0)
+ case ORD('B'):
+ if (!sub && vs->cursor == 0)
return (-1);
ncursor = Backword(argcnt);
break;
- case 'e':
- if (!sub && es->cursor + 1 >= es->linelen)
+ case ORD('e'):
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = endword(argcnt);
- if (sub && ncursor < es->linelen)
+ if (sub && ncursor < vs->linelen)
ncursor++;
break;
- case 'E':
- if (!sub && es->cursor + 1 >= es->linelen)
+ case ORD('E'):
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = Endword(argcnt);
- if (sub && ncursor < es->linelen)
+ if (sub && ncursor < vs->linelen)
ncursor++;
break;
- case 'f':
- case 'F':
- case 't':
- case 'T':
+ case ORD('f'):
+ case ORD('F'):
+ case ORD('t'):
+ case ORD('T'):
fsavecmd = *cmd;
fsavech = cmd[1];
- /* drop through */
-
- case ',':
- case ';':
+ /* FALLTHROUGH */
+ case ORD(','):
+ case ORD(';'):
if (fsavecmd == ' ')
return (-1);
- i = fsavecmd == 'f' || fsavecmd == 'F';
+ i = ksh_eq(fsavecmd, 'F', 'f');
t = fsavecmd > 'a';
if (*cmd == ',')
t = !t;
@@ -4597,81 +4661,78 @@
ncursor++;
break;
- case 'h':
- case CTRL('h'):
- if (!sub && es->cursor == 0)
+ case ORD('h'):
+ case CTRL_H:
+ if (!sub && vs->cursor == 0)
return (-1);
- ncursor = es->cursor - argcnt;
+ ncursor = vs->cursor - argcnt;
if (ncursor < 0)
ncursor = 0;
break;
- case ' ':
- case 'l':
- if (!sub && es->cursor + 1 >= es->linelen)
+ case ORD(' '):
+ case ORD('l'):
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
- if (es->linelen != 0) {
- ncursor = es->cursor + argcnt;
- if (ncursor > es->linelen)
- ncursor = es->linelen;
+ if (vs->linelen != 0) {
+ ncursor = vs->cursor + argcnt;
+ if (ncursor > vs->linelen)
+ ncursor = vs->linelen;
}
break;
- case 'w':
- if (!sub && es->cursor + 1 >= es->linelen)
+ case ORD('w'):
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = forwword(argcnt);
break;
- case 'W':
- if (!sub && es->cursor + 1 >= es->linelen)
+ case ORD('W'):
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = Forwword(argcnt);
break;
- case '0':
+ case ORD('0'):
ncursor = 0;
break;
- case '^':
- ncursor = 0;
- while (ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
- ncursor++;
+ case ORD('^'):
+ ncursor = domovebeg();
break;
- case '|':
+ case ORD('|'):
ncursor = argcnt;
- if (ncursor > es->linelen)
- ncursor = es->linelen;
+ if (ncursor > vs->linelen)
+ ncursor = vs->linelen;
if (ncursor)
ncursor--;
break;
- case '$':
- if (es->linelen != 0)
- ncursor = es->linelen;
+ case ORD('$'):
+ if (vs->linelen != 0)
+ ncursor = vs->linelen;
else
ncursor = 0;
break;
- case '%':
- ncursor = es->cursor;
- while (ncursor < es->linelen &&
- (i = bracktype(es->cbuf[ncursor])) == 0)
+ case ORD('%'):
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen &&
+ (i = bracktype(vs->cbuf[ncursor])) == 0)
ncursor++;
- if (ncursor == es->linelen)
+ if (ncursor == vs->linelen)
return (-1);
bcount = 1;
do {
if (i > 0) {
- if (++ncursor >= es->linelen)
+ if (++ncursor >= vs->linelen)
return (-1);
} else {
if (--ncursor < 0)
return (-1);
}
- t = bracktype(es->cbuf[ncursor]);
+ t = bracktype(vs->cbuf[ncursor]);
if (t == i)
bcount++;
else if (t == -i)
@@ -4688,13 +4749,24 @@
}
static int
+domovebeg(void)
+{
+ int ncursor = 0;
+
+ while (ncursor < vs->linelen - 1 &&
+ ctype(vs->cbuf[ncursor], C_SPACE))
+ ncursor++;
+ return (ncursor);
+}
+
+static int
redo_insert(int count)
{
while (count-- > 0)
if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0)
return (-1);
- if (es->cursor > 0)
- es->cursor--;
+ if (vs->cursor > 0)
+ vs->cursor--;
insert = 0;
return (0);
}
@@ -4704,30 +4776,30 @@
{
yanklen = b - a;
if (yanklen != 0)
- memmove(ybuf, &es->cbuf[a], yanklen);
+ memmove(ybuf, &vs->cbuf[a], yanklen);
}
static int
bracktype(int ch)
{
- switch (ch) {
+ switch (ord(ch)) {
- case '(':
+ case ORD('('):
return (1);
- case '[':
+ case ORD('['):
return (2);
- case '{':
+ case ORD('{'):
return (3);
- case ')':
+ case ORD(')'):
return (-1);
- case ']':
+ case ORD(']'):
return (-2);
- case '}':
+ case ORD('}'):
return (-3);
default:
@@ -4742,8 +4814,8 @@
static void
save_cbuf(void)
{
- memmove(holdbufp, es->cbuf, es->linelen);
- holdlen = es->linelen;
+ memmove(holdbufp, vs->cbuf, vs->linelen);
+ holdlen = vs->linelen;
holdbufp[holdlen] = '\0';
}
@@ -4750,9 +4822,9 @@
static void
restore_cbuf(void)
{
- es->cursor = 0;
- es->linelen = holdlen;
- memmove(es->cbuf, holdbufp, holdlen);
+ vs->cursor = 0;
+ vs->linelen = holdlen;
+ memmove(vs->cbuf, holdbufp, holdlen);
}
/* return a new edstate */
@@ -4803,19 +4875,19 @@
if (len == 0)
return (0);
if (repl) {
- if (es->cursor + len >= es->cbufsize)
+ if (vs->cursor + len >= vs->cbufsize)
return (-1);
- if (es->cursor + len > es->linelen)
- es->linelen = es->cursor + len;
+ if (vs->cursor + len > vs->linelen)
+ vs->linelen = vs->cursor + len;
} else {
- if (es->linelen + len >= es->cbufsize)
+ if (vs->linelen + len >= vs->cbufsize)
return (-1);
- memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen += len;
+ memmove(&vs->cbuf[vs->cursor + len], &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen += len;
}
- memmove(&es->cbuf[es->cursor], buf, len);
- es->cursor += len;
+ memmove(&vs->cbuf[vs->cursor], buf, len);
+ vs->cursor += len;
return (0);
}
@@ -4822,9 +4894,9 @@
static void
del_range(int a, int b)
{
- if (es->linelen != b)
- memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
- es->linelen -= b - a;
+ if (vs->linelen != b)
+ memmove(&vs->cbuf[a], &vs->cbuf[b], vs->linelen - b);
+ vs->linelen -= b - a;
}
static int
@@ -4832,19 +4904,19 @@
{
int ncursor;
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
- ncursor = es->cursor;
+ ncursor = vs->cursor;
while (cnt--) {
do {
if (forw) {
- if (++ncursor == es->linelen)
+ if (++ncursor == vs->linelen)
return (-1);
} else {
if (--ncursor < 0)
return (-1);
}
- } while (es->cbuf[ncursor] != ch);
+ } while (vs->cbuf[ncursor] != ch);
}
if (!incl) {
if (forw)
@@ -4860,19 +4932,18 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (ksh_isalnux(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen && argcnt--) {
+ if (ctype(vs->cbuf[ncursor], C_ALNUX))
+ while (ncursor < vs->linelen &&
+ ctype(vs->cbuf[ncursor], C_ALNUX))
ncursor++;
- else if (!ksh_isspace(es->cbuf[ncursor]))
- while (!ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ else if (!ctype(vs->cbuf[ncursor], C_SPACE))
+ while (ncursor < vs->linelen &&
+ !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
ncursor++;
- while (ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < vs->linelen &&
+ ctype(vs->cbuf[ncursor], C_SPACE))
ncursor++;
}
return (ncursor);
@@ -4883,19 +4954,18 @@
{
int ncursor;
- ncursor = es->cursor;
+ ncursor = vs->cursor;
while (ncursor > 0 && argcnt--) {
- while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor]))
+ while (--ncursor > 0 && ctype(vs->cbuf[ncursor], C_SPACE))
;
if (ncursor > 0) {
- if (ksh_isalnux(es->cbuf[ncursor]))
+ if (ctype(vs->cbuf[ncursor], C_ALNUX))
while (--ncursor >= 0 &&
- ksh_isalnux(es->cbuf[ncursor]))
+ ctype(vs->cbuf[ncursor], C_ALNUX))
;
else
while (--ncursor >= 0 &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
+ !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
;
ncursor++;
}
@@ -4908,20 +4978,19 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- while (++ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen && argcnt--) {
+ while (++ncursor < vs->linelen - 1 &&
+ ctype(vs->cbuf[ncursor], C_SPACE))
;
- if (ncursor < es->linelen - 1) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (++ncursor < es->linelen &&
- ksh_isalnux(es->cbuf[ncursor]))
+ if (ncursor < vs->linelen - 1) {
+ if (ctype(vs->cbuf[ncursor], C_ALNUX))
+ while (++ncursor < vs->linelen &&
+ ctype(vs->cbuf[ncursor], C_ALNUX))
;
else
- while (++ncursor < es->linelen &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
+ while (++ncursor < vs->linelen &&
+ !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
;
ncursor--;
}
@@ -4934,13 +5003,13 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- while (!ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen && argcnt--) {
+ while (ncursor < vs->linelen &&
+ !ctype(vs->cbuf[ncursor], C_SPACE))
ncursor++;
- while (ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < vs->linelen &&
+ ctype(vs->cbuf[ncursor], C_SPACE))
ncursor++;
}
return (ncursor);
@@ -4951,11 +5020,11 @@
{
int ncursor;
- ncursor = es->cursor;
+ ncursor = vs->cursor;
while (ncursor > 0 && argcnt--) {
- while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor]))
+ while (--ncursor >= 0 && ctype(vs->cbuf[ncursor], C_SPACE))
;
- while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor]))
+ while (ncursor >= 0 && !ctype(vs->cbuf[ncursor], C_SPACE))
ncursor--;
ncursor++;
}
@@ -4967,14 +5036,14 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen - 1 && argcnt--) {
- while (++ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen - 1 && argcnt--) {
+ while (++ncursor < vs->linelen - 1 &&
+ ctype(vs->cbuf[ncursor], C_SPACE))
;
- if (ncursor < es->linelen - 1) {
- while (++ncursor < es->linelen &&
- !ksh_isspace(es->cbuf[ncursor]))
+ if (ncursor < vs->linelen - 1) {
+ while (++ncursor < vs->linelen &&
+ !ctype(vs->cbuf[ncursor], C_SPACE))
;
ncursor--;
}
@@ -4996,15 +5065,15 @@
}
(void)histnum(n);
if ((hptr = *histpos()) == NULL) {
- internal_warningf("%s: %s", "grabhist", "bad history array");
+ internal_warningf("grabhist: bad history array");
return (-1);
}
if (save)
save_cbuf();
- if ((es->linelen = strlen(hptr)) >= es->cbufsize)
- es->linelen = es->cbufsize - 1;
- memmove(es->cbuf, hptr, es->linelen);
- es->cursor = 0;
+ if ((vs->linelen = strlen(hptr)) >= vs->cbufsize)
+ vs->linelen = vs->cbufsize - 1;
+ memmove(vs->cbuf, hptr, vs->linelen);
+ vs->cursor = 0;
ohnum = n;
return (0);
}
@@ -5035,10 +5104,10 @@
save_cbuf();
histnum(hist);
hptr = *histpos();
- if ((es->linelen = strlen(hptr)) >= es->cbufsize)
- es->linelen = es->cbufsize - 1;
- memmove(es->cbuf, hptr, es->linelen);
- es->cursor = 0;
+ if ((vs->linelen = strlen(hptr)) >= vs->cbufsize)
+ vs->linelen = vs->cbufsize - 1;
+ memmove(vs->cbuf, hptr, vs->linelen);
+ vs->cursor = 0;
return (hist);
}
@@ -5051,9 +5120,7 @@
x_putc('\r');
x_putc('\n');
}
- if (prompt_trunc != -1)
- pprompt(prompt, prompt_trunc);
- x_col = pwidth;
+ x_pprompt();
morec = ' ';
}
@@ -5075,12 +5142,12 @@
{
int cur, col;
- if (es->cursor < es->winleft)
+ if (vs->cursor < vs->winleft)
return (1);
col = 0;
- cur = es->winleft;
- while (cur < es->cursor)
- col = newcol((unsigned char)es->cbuf[cur++], col);
+ cur = vs->winleft;
+ while (cur < vs->cursor)
+ col = newcol((unsigned char)vs->cbuf[cur++], col);
if (col >= winwidth)
return (1);
return (0);
@@ -5095,7 +5162,7 @@
holdcur1 = holdcur2 = tcur = 0;
holdcol1 = holdcol2 = tcol = 0;
- while (tcur < es->cursor) {
+ while (tcur < vs->cursor) {
if (tcol - holdcol2 > winwidth / 2) {
holdcur1 = holdcur2;
holdcol1 = holdcol2;
@@ -5102,12 +5169,12 @@
holdcur2 = tcur;
holdcol2 = tcol;
}
- tcol = newcol((unsigned char)es->cbuf[tcur++], tcol);
+ tcol = newcol((unsigned char)vs->cbuf[tcur++], tcol);
}
while (tcol - holdcol1 > winwidth / 2)
- holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++],
+ holdcol1 = newcol((unsigned char)vs->cbuf[holdcur1++],
holdcol1);
- es->winleft = holdcur1;
+ vs->winleft = holdcur1;
}
static int
@@ -5128,21 +5195,21 @@
int moreright;
col = 0;
- cur = es->winleft;
+ cur = vs->winleft;
moreright = 0;
twb1 = wb1;
- while (col < winwidth && cur < es->linelen) {
- if (cur == es->cursor && leftside)
+ while (col < winwidth && cur < vs->linelen) {
+ if (cur == vs->cursor && leftside)
ncol = col + pwidth;
- if ((ch = es->cbuf[cur]) == '\t')
+ if ((ch = vs->cbuf[cur]) == '\t')
do {
*twb1++ = ' ';
} while (++col < winwidth && (col & 7) != 0);
else if (col < winwidth) {
- if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
+ if (ksh_isctrl(ch)) {
*twb1++ = '^';
if (++col < winwidth) {
- *twb1++ = UNCTRL(ch);
+ *twb1++ = ksh_unctrl(ch);
col++;
}
} else {
@@ -5150,11 +5217,11 @@
col++;
}
}
- if (cur == es->cursor && !leftside)
+ if (cur == vs->cursor && !leftside)
ncol = col + pwidth - 1;
cur++;
}
- if (cur == es->cursor)
+ if (cur == vs->cursor)
ncol = col + pwidth;
if (col < winwidth) {
while (col < winwidth) {
@@ -5180,13 +5247,13 @@
twb2++;
col++;
}
- if (es->winleft > 0 && moreright)
+ if (vs->winleft > 0 && moreright)
/*
* POSIX says to use * for this but that is a globbing
* character and may confuse people; + is more innocuous
*/
mc = '+';
- else if (es->winleft > 0)
+ else if (vs->winleft > 0)
mc = '<';
else if (moreright)
mc = '>';
@@ -5208,9 +5275,7 @@
if (col < x_col) {
if (col + 1 < x_col - col) {
x_putc('\r');
- if (prompt_trunc != -1)
- pprompt(prompt, prompt_trunc);
- x_col = pwidth;
+ x_pprompt();
while (x_col++ < col)
x_putcf(*wb++);
} else {
@@ -5236,7 +5301,7 @@
/* Undo previous expansion */
if (cmd == 0 && expanded == EXPAND && buf) {
- restore_edstate(es, buf);
+ restore_edstate(vs, buf);
buf = 0;
expanded = NONE;
return (0);
@@ -5247,7 +5312,7 @@
}
i = XCF_COMMAND_FILE | XCF_FULLPATH;
- nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor,
+ nwords = x_cf_glob(&i, vs->cbuf, vs->linelen, vs->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
@@ -5254,10 +5319,10 @@
return (-1);
}
- buf = save_edstate(es);
+ buf = save_edstate(vs);
expanded = EXPAND;
del_range(start, end);
- es->cursor = start;
+ vs->cursor = start;
i = 0;
while (i < nwords) {
if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
@@ -5264,7 +5329,7 @@
rval = -1;
break;
}
- if (++i < nwords && putbuf(" ", 1, false) != 0) {
+ if (++i < nwords && putbuf(T1space, 1, false) != 0) {
rval = -1;
break;
}
@@ -5271,7 +5336,7 @@
}
i = buf->cursor - end;
if (rval == 0 && i > 0)
- es->cursor += i;
+ vs->cursor += i;
modified = 1;
hnum = hlast;
insert = INSERT;
@@ -5297,7 +5362,7 @@
return (0);
}
if (cmd == 0 && expanded == PRINT && buf) {
- restore_edstate(es, buf);
+ restore_edstate(vs, buf);
buf = 0;
expanded = NONE;
return (0);
@@ -5314,7 +5379,7 @@
flags = XCF_COMMAND_FILE;
if (count)
flags |= XCF_FULLPATH;
- nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor,
+ nwords = x_cf_glob(&flags, vs->cbuf, vs->linelen, vs->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
@@ -5359,9 +5424,9 @@
is_unique = nwords == 1;
}
- buf = save_edstate(es);
+ buf = save_edstate(vs);
del_range(start, end);
- es->cursor = start;
+ vs->cursor = start;
/*
* escape all shell-sensitive characters and put the result into
@@ -5380,9 +5445,9 @@
* append a space if this is a non-directory match
* and not a parameter or homedir substitution
*/
- if (match_len > 0 && match[match_len - 1] != '/' &&
+ if (match_len > 0 && !mksh_cdirsep(match[match_len - 1]) &&
!(flags & XCF_IS_NOSPACE))
- rval = putbuf(" ", 1, false);
+ rval = putbuf(T1space, 1, false);
}
x_free_words(nwords, words);
@@ -5414,24 +5479,26 @@
redraw_line(false);
return (0);
}
+#endif /* !MKSH_S_NOVI */
/* Similar to x_zotc(emacs.c), but no tab weirdness */
static void
x_vi_zotc(int c)
{
- if (ISCTRL(c)) {
+ if (ksh_isctrl(c)) {
x_putc('^');
- c = UNCTRL(c);
+ c = ksh_unctrl(c);
}
x_putc(c);
}
+#if !MKSH_S_NOVI
static void
vi_error(void)
{
/* Beem out of any macros as soon as an error occurs */
vi_macro_reset();
- x_putc(7);
+ x_putc(KSH_BEL);
x_flush();
}
@@ -5452,12 +5519,11 @@
int i, j;
/*
- * Set edchars to -2 to force initial binding, except
- * we need default values for some deficient systems…
+ * set edchars to force initial binding, except we need
+ * default values for ^W for some deficient systems…
*/
edchars.erase = edchars.kill = edchars.intr = edchars.quit =
- edchars.eof = -2;
- /* ^W */
+ edchars.eof = EDCHAR_INITIAL;
edchars.werase = 027;
/* command line editing specific memory allocation */
@@ -5493,4 +5559,90 @@
afreeall(AEDIT);
}
#endif
+
+void
+x_initterm(const char *termtype)
+{
+ /* default must be 0 (bss) */
+ x_term_mode = 0;
+ /* this is what tmux uses, don't ask me about it */
+ if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7))
+ x_term_mode = 1;
+}
+
+#ifndef MKSH_SMALL
+static char *
+x_eval_region_helper(const char *cmd, size_t len)
+{
+ char * volatile cp;
+ newenv(E_ERRH);
+
+ if (!kshsetjmp(e->jbuf)) {
+ char *wds = alloc(len + 3, ATEMP);
+
+ wds[0] = FUNASUB;
+ memcpy(wds + 1, cmd, len);
+ wds[len + 1] = '\0';
+ wds[len + 2] = EOS;
+
+ cp = evalstr(wds, DOSCALAR);
+ afree(wds, ATEMP);
+ strdupx(cp, cp, AEDIT);
+ } else
+ cp = NULL;
+ quitenv(NULL);
+ return (cp);
+}
+
+static int
+x_eval_region(int c MKSH_A_UNUSED)
+{
+ char *evbeg, *evend, *cp;
+ size_t newlen;
+ /* only for LINE overflow checking */
+ size_t restlen;
+
+ if (xmp == NULL) {
+ evbeg = xbuf;
+ evend = xep;
+ } else if (xmp < xcp) {
+ evbeg = xmp;
+ evend = xcp;
+ } else {
+ evbeg = xcp;
+ evend = xmp;
+ }
+
+ x_e_putc2('\r');
+ x_clrtoeol(' ', false);
+ x_flush();
+ x_mode(false);
+ cp = x_eval_region_helper(evbeg, evend - evbeg);
+ x_mode(true);
+
+ if (cp == NULL) {
+ /* command cannot be parsed */
+ x_eval_region_err:
+ x_e_putc2(KSH_BEL);
+ x_redraw('\r');
+ return (KSTD);
+ }
+
+ newlen = strlen(cp);
+ restlen = xep - evend;
+ /* check for LINE overflow, until this is dynamically allocated */
+ if (evbeg + newlen + restlen >= xend)
+ goto x_eval_region_err;
+
+ xmp = evbeg;
+ xcp = evbeg + newlen;
+ xep = xcp + restlen;
+ memmove(xcp, evend, restlen + /* NUL */ 1);
+ memcpy(xmp, cp, newlen);
+ afree(cp, AEDIT);
+ x_adjust();
+ x_modified();
+ return (KSTD);
+}
+#endif /* !MKSH_SMALL */
#endif /* !MKSH_NO_CMDLINE_EDITING */
Modified: trunk/contrib/mksh/emacsfn.h
===================================================================
--- trunk/contrib/mksh/emacsfn.h 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/emacsfn.h 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,5 +1,25 @@
+/*-
+ * Copyright (c) 2009, 2010, 2015, 2016
+ * mirabilos <m at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
#if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.5 2010/07/17 22:09:33 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.10 2016/09/01 12:59:09 tg Exp $");
#define FN(cname,sname,flags) static int x_##cname(int);
#elif defined(EMACSFN_ENUMS)
#define FN(cname,sname,flags) XFUNC_##cname,
@@ -34,6 +54,9 @@
FN(enumerate, "list", 0)
FN(eot_del, "eot-or-delete", XF_ARG)
FN(error, "error", 0)
+#ifndef MKSH_SMALL
+FN(eval_region, "evaluate-region", 0)
+#endif
FN(expand, "expand-file", 0)
#ifndef MKSH_SMALL
FN(fold_capitalise, "capitalize-word", XF_ARG)
@@ -52,9 +75,10 @@
FN(literal, "quote", 0)
FN(meta1, "prefix-1", XF_PREFIX)
FN(meta2, "prefix-2", XF_PREFIX)
+FN(meta3, "prefix-3", XF_PREFIX)
FN(meta_yank, "yank-pop", 0)
FN(mv_back, "backward-char", XF_ARG)
-FN(mv_begin, "beginning-of-line", 0)
+FN(mv_beg, "beginning-of-line", 0)
FN(mv_bword, "backward-word", XF_ARG)
FN(mv_end, "end-of-line", 0)
FN(mv_forw, "forward-char", XF_ARG)
Modified: trunk/contrib/mksh/eval.c
===================================================================
--- trunk/contrib/mksh/eval.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/eval.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -2,8 +2,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.158.2.4 2015/03/01 15:42:58 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.219 2018/01/14 01:29:47 tg Exp $");
/*
* string expansion
@@ -62,9 +62,15 @@
#define IFS_WORD 0 /* word has chars (or quotes except "$@") */
#define IFS_WS 1 /* have seen IFS white-space */
#define IFS_NWS 2 /* have seen IFS non-white-space */
-#define IFS_IWS 3 /* begin of word, ignore IFS WS */
+#define IFS_IWS 3 /* beginning of word, ignore IFS WS */
#define IFS_QUOTE 4 /* beg.w/quote, become IFS_WORD unless "$@" */
+#define STYPE_CHAR 0xFF
+#define STYPE_DBL 0x100
+#define STYPE_AT 0x200
+#define STYPE_SINGLE 0x2FF
+#define STYPE_MASK 0x300
+
static int varsub(Expand *, const char *, const char *, int *, int *);
static int comsub(Expand *, const char *, int);
static char *valsub(struct op *, Area *);
@@ -120,7 +126,7 @@
s->start = s->str = cp;
source = s;
if (yylex(ONEWORD) != LWORD)
- internal_errorf("bad substitution");
+ internal_errorf(Tbadsubst);
source = sold;
afree(s, ATEMP);
return (evalstr(yylval.cp, f));
@@ -186,7 +192,7 @@
rv = (char *) *XPptrv(w);
break;
default:
- rv = evalstr(cp, f&~DOGLOB);
+ rv = evalstr(cp, f & ~DOGLOB);
break;
}
XPfree(w);
@@ -198,7 +204,7 @@
struct tbl *var; /* variable for ${var..} */
struct SubType *prev; /* old type */
struct SubType *next; /* poped type (to avoid re-allocating) */
- size_t base; /* begin position of expanded word */
+ size_t base; /* start position of expanded word */
short stype; /* [=+-?%#] action after expanded word */
short f; /* saved value of f (DOPAT, etc) */
uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */
@@ -277,38 +283,33 @@
switch (type) {
case XBASE:
/* original prefixed string */
- c = *sp++;
+ c = ord(*sp++);
switch (c) {
case EOS:
c = 0;
break;
case CHAR:
- c = *sp++;
+ c = ord(*sp++);
break;
case QCHAR:
/* temporary quote */
quote |= 2;
- c = *sp++;
+ c = ord(*sp++);
break;
case OQUOTE:
- switch (word) {
- case IFS_QUOTE:
- /* """something */
- word = IFS_WORD;
- break;
- case IFS_WORD:
- break;
- default:
+ if (word != IFS_WORD)
word = IFS_QUOTE;
- break;
- }
tilde_ok = 0;
quote = 1;
continue;
case CQUOTE:
+ if (word == IFS_QUOTE)
+ word = IFS_WORD;
quote = st->quotew;
continue;
+ case COMASUB:
case COMSUB:
+ case FUNASUB:
case FUNSUB:
case VALSUB:
tilde_ok = 0;
@@ -315,18 +316,27 @@
if (f & DONTRUNCOMMAND) {
word = IFS_WORD;
*dp++ = '$';
- *dp++ = c == COMSUB ? '(' : '{';
- if (c != COMSUB)
- *dp++ = c == FUNSUB ? ' ' : '|';
+ switch (c) {
+ case COMASUB:
+ case COMSUB:
+ *dp++ = '(';
+ c = ORD(')');
+ break;
+ case FUNASUB:
+ case FUNSUB:
+ case VALSUB:
+ *dp++ = '{';
+ *dp++ = c == VALSUB ? '|' : ' ';
+ c = ORD('}');
+ break;
+ }
while (*sp != '\0') {
Xcheck(ds, dp);
*dp++ = *sp++;
}
- if (c != COMSUB) {
+ if ((unsigned int)c == ORD('}'))
*dp++ = ';';
- *dp++ = '}';
- } else
- *dp++ = ')';
+ *dp++ = c;
} else {
type = comsub(&x, sp, c);
if (type != XBASE && (f & DOBLANK))
@@ -382,14 +392,15 @@
unwind_substsyn:
/* restore sp */
sp = varname - 2;
- end = (beg = wdcopy(sp, ATEMP)) +
- (wdscan(sp, CSUBST) - sp);
+ beg = wdcopy(sp, ATEMP);
+ end = (wdscan(cstrchr(sp, '\0') + 1,
+ CSUBST) - sp) + beg;
/* ({) the } or x is already skipped */
if (end < wdscan(beg, EOS))
*end = EOS;
- str = snptreef(NULL, 64, "%S", beg);
+ str = snptreef(NULL, 64, Tf_S, beg);
afree(beg, ATEMP);
- errorf("%s: %s", str, "bad substitution");
+ errorf(Tf_sD_s, str, Tbadsubst);
}
if (f & DOBLANK)
doblank++;
@@ -410,8 +421,8 @@
st->stype = stype;
st->base = Xsavepos(ds, dp);
st->f = f;
- if (x.var == &vtemp) {
- st->var = tempvar();
+ if (x.var == vtemp) {
+ st->var = tempvar(vtemp->name);
st->var->flag &= ~INTEGER;
/* can't fail here */
setstr(st->var,
@@ -424,12 +435,12 @@
/* skip qualifier(s) */
if (stype)
sp += slen;
- switch (stype & 0x17F) {
- case 0x100 | '#':
+ switch (stype & STYPE_SINGLE) {
+ case ORD('#') | STYPE_AT:
x.str = shf_smprintf("%08X",
(unsigned int)hash(str_val(st->var)));
break;
- case 0x100 | 'Q': {
+ case ORD('Q') | STYPE_AT: {
struct shf shf;
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
@@ -437,7 +448,7 @@
x.str = shf_sclose(&shf);
break;
}
- case '0': {
+ case ORD('0'): {
char *beg, *mid, *end, *stg;
mksh_ari_t from = 0, num = -1, flen, finc = 0;
@@ -444,18 +455,15 @@
beg = wdcopy(sp, ATEMP);
mid = beg + (wdscan(sp, ADELIM) - sp);
stg = beg + (wdscan(sp, CSUBST) - sp);
- if (mid >= stg)
- goto unwind_substsyn;
mid[-2] = EOS;
- if (mid[-1] == /*{*/'}') {
+ if (ord(mid[-1]) == ORD(/*{*/ '}')) {
sp += mid - beg - 1;
end = NULL;
} else {
end = mid +
(wdscan(mid, ADELIM) - mid);
- if (end >= stg ||
- /* more than max delimiters */
- end[-1] != /*{*/ '}')
+ if (ord(end[-1]) != ORD(/*{*/ '}'))
+ /* more than max delimiters */
goto unwind_substsyn;
end[-2] = EOS;
sp += end - beg - 1;
@@ -487,96 +495,66 @@
strndupx(x.str, beg, num, ATEMP);
goto do_CSUBST;
}
- case '/': {
+ case ORD('/') | STYPE_AT:
+ case ORD('/'): {
char *s, *p, *d, *sbeg, *end;
- char *pat, *rrep;
- char *tpat0, *tpat1, *tpat2;
+ char *pat = NULL, *rrep = null;
+ char fpat = 0, *tpat1, *tpat2;
+ char *ws, *wpat, *wrep;
- s = wdcopy(sp, ATEMP);
+ s = ws = wdcopy(sp, ATEMP);
p = s + (wdscan(sp, ADELIM) - sp);
d = s + (wdscan(sp, CSUBST) - sp);
- if (p >= d)
- goto unwind_substsyn;
p[-2] = EOS;
- if (p[-1] == /*{*/'}')
+ if (ord(p[-1]) == ORD(/*{*/ '}'))
d = NULL;
else
d[-2] = EOS;
sp += (d ? d : p) - s - 1;
- tpat0 = wdstrip(s,
- WDS_KEEPQ | WDS_MAGIC);
- pat = substitute(tpat0, 0);
- if (d) {
- d = wdstrip(p, WDS_KEEPQ);
- rrep = substitute(d, 0);
- afree(d, ATEMP);
- } else
- rrep = null;
- afree(s, ATEMP);
- s = d = pat;
- while (*s)
- if (*s != '\\' ||
- s[1] == '%' ||
- s[1] == '#' ||
- s[1] == '\0' ||
- /* XXX really? */ s[1] == '\\' ||
- s[1] == '/')
- *d++ = *s++;
- else
- s++;
- *d = '\0';
- afree(tpat0, ATEMP);
+ if (!(stype & STYPE_MASK) &&
+ s[0] == CHAR &&
+ 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;
+ }
+ /* prepare string on which to work */
+ strdupx(s, str_val(st->var), ATEMP);
+ sbeg = s;
+ again_search:
+ pat = evalstr(wpat,
+ DOTILDE | DOSCALAR | DOPAT);
/* check for special cases */
- d = str_val(st->var);
- switch (*pat) {
- case '#':
- /* anchor at begin */
- tpat0 = pat + 1;
- tpat1 = rrep;
- tpat2 = d;
- break;
- case '%':
- /* anchor at end */
- tpat0 = pat + 1;
- tpat1 = d;
- tpat2 = rrep;
- break;
- case '\0':
- /* empty pattern */
+ if (!*pat && !fpat) {
+ /*
+ * empty unanchored
+ * pattern => reject
+ */
goto no_repl;
- default:
- tpat0 = pat;
- /* silence gcc */
- tpat1 = tpat2 = NULL;
}
- if (gmatchx(null, tpat0, false)) {
+ if ((stype & STYPE_MASK) &&
+ gmatchx(null, pat, false)) {
/*
- * pattern matches
- * the empty string
+ * pattern matches empty
+ * string => don't loop
*/
- if (tpat0 == pat)
- goto no_repl;
- /* but is anchored */
- s = shf_smprintf("%s%s",
- tpat1, tpat2);
- goto do_repl;
+ stype &= ~STYPE_MASK;
}
- /* prepare string on which to work */
- strdupx(s, d, ATEMP);
- sbeg = s;
-
/* first see if we have any match at all */
- tpat0 = pat;
- if (*pat == '#') {
+ if (ord(fpat) == ORD('#')) {
/* anchor at the beginning */
- tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC);
+ tpat1 = shf_smprintf("%s%c*", pat, MAGIC);
tpat2 = tpat1;
- } else if (*pat == '%') {
+ } else if (ord(fpat) == ORD('%')) {
/* anchor at the end */
- tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0);
- tpat2 = tpat0;
+ tpat1 = shf_smprintf("%c*%s", MAGIC, pat);
+ tpat2 = pat;
} else {
/* float */
tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC);
@@ -591,7 +569,7 @@
goto end_repl;
end = strnul(s);
/* now anchor the beginning of the match */
- if (*pat != '#')
+ if (ord(fpat) != ORD('#'))
while (sbeg <= end) {
if (gmatchx(sbeg, tpat2, false))
break;
@@ -600,41 +578,57 @@
}
/* now anchor the end of the match */
p = end;
- if (*pat != '%')
+ if (ord(fpat) != ORD('%'))
while (p >= sbeg) {
bool gotmatch;
- c = *p;
+ c = ord(*p);
*p = '\0';
- gotmatch = tobool(gmatchx(sbeg, tpat0, false));
+ gotmatch = tobool(gmatchx(sbeg, pat, false));
*p = c;
if (gotmatch)
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;
+ }
strndupx(end, s, sbeg - s, ATEMP);
- d = shf_smprintf("%s%s%s", end, rrep, p);
+ d = shf_smprintf(Tf_sss, end, rrep, p);
afree(end, ATEMP);
sbeg = d + (sbeg - s) + strlen(rrep);
afree(s, ATEMP);
s = d;
- if (stype & 0x80)
+ if (stype & STYPE_AT) {
+ afree(tpat1, ATEMP);
+ afree(pat, ATEMP);
+ goto again_search;
+ } else if (stype & STYPE_DBL)
goto again_repl;
end_repl:
afree(tpat1, ATEMP);
- do_repl:
x.str = s;
no_repl:
afree(pat, ATEMP);
if (rrep != null)
afree(rrep, ATEMP);
+ afree(ws, ATEMP);
goto do_CSUBST;
}
- case '#':
- case '%':
- /* ! DOBLANK,DOBRACE,DOTILDE */
+ case ORD('#'):
+ case ORD('%'):
+ /* ! DOBLANK,DOBRACE */
f = (f & DONTRUNCOMMAND) |
- DOPAT | DOTEMP | DOSCALAR;
+ DOPAT | DOTILDE |
+ DOTEMP | DOSCALAR;
+ tilde_ok = 1;
st->quotew = quote = 0;
/*
* Prepend open pattern (so |
@@ -643,18 +637,17 @@
*/
if (!Flag(FSH)) {
*dp++ = MAGIC;
- *dp++ = 0x80 | '@';
+ *dp++ = ORD(0x80 | '@');
}
break;
- case '=':
+ case ORD('='):
/*
- * Enabling tilde expansion
- * after :s here is
- * non-standard ksh, but is
- * consistent with rules for
- * other assignments. Not
- * sure what POSIX thinks of
- * this.
+ * Tilde expansion for string
+ * variables in POSIX mode is
+ * governed by Austinbug 351.
+ * In non-POSIX mode historic
+ * ksh behaviour (enable it!)
+ * us followed.
* Not doing tilde expansion
* for integer variables is a
* non-POSIX thing - makes
@@ -663,7 +656,7 @@
*/
if (!(x.var->flag & INTEGER))
f |= DOASNTILDE | DOTILDE;
- f |= DOTEMP;
+ f |= DOTEMP | DOSCALAR;
/*
* These will be done after the
* value has been assigned.
@@ -671,7 +664,10 @@
f &= ~(DOBLANK|DOGLOB|DOBRACE);
tilde_ok = 1;
break;
- case '?':
+ case ORD('?'):
+ if (*sp == CSUBST)
+ errorf("%s: parameter null or not set",
+ st->var->name);
f &= ~DOBLANK;
f |= DOTEMP;
/* FALLTHROUGH */
@@ -702,9 +698,9 @@
f = st->f;
if (f & DOBLANK)
doblank--;
- switch (st->stype & 0x17F) {
- case '#':
- case '%':
+ switch (st->stype & STYPE_SINGLE) {
+ case ORD('#'):
+ case ORD('%'):
if (!Flag(FSH)) {
/* Append end-pattern */
*dp++ = MAGIC;
@@ -734,7 +730,7 @@
doblank++;
st = st->prev;
continue;
- case '=':
+ case ORD('='):
/*
* Restore our position and substitute
* the value of st->var (may not be
@@ -767,18 +763,17 @@
st = st->prev;
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
continue;
- case '?': {
- char *s = Xrestpos(ds, dp, st->base);
+ case ORD('?'):
+ dp = Xrestpos(ds, dp, st->base);
- errorf("%s: %s", st->var->name,
- dp == s ?
- "parameter null or not set" :
- (debunk(s, s, strlen(s) + 1), s));
- }
- case '0':
- case '/':
- case 0x100 | '#':
- case 0x100 | 'Q':
+ errorf(Tf_sD_s, st->var->name,
+ debunk(dp, dp, strlen(dp) + 1));
+ break;
+ case ORD('0'):
+ case ORD('/') | STYPE_AT:
+ case ORD('/'):
+ case ORD('#') | STYPE_AT:
+ case ORD('Q') | STYPE_AT:
dp = Xrestpos(ds, dp, st->base);
type = XSUB;
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
@@ -796,19 +791,19 @@
/* open pattern: *(foo|bar) */
/* Next char is the type of pattern */
make_magic = true;
- c = *sp++ | 0x80;
+ c = ord(*sp++) | 0x80U;
break;
case SPAT:
/* pattern separator (|) */
make_magic = true;
- c = '|';
+ c = ORD('|');
break;
case CPAT:
/* close pattern */
make_magic = true;
- c = /*(*/ ')';
+ c = ORD(/*(*/ ')');
break;
}
break;
@@ -829,7 +824,7 @@
case XSUB:
case XSUBMID:
- if ((c = *x.str++) == 0) {
+ if ((c = ord(*x.str++)) == 0) {
type = XBASE;
if (f & DOBLANK)
doblank--;
@@ -842,7 +837,7 @@
quote = 1;
/* FALLTHROUGH */
case XARG:
- if ((c = *x.str++) == '\0') {
+ if ((c = ord(*x.str++)) == '\0') {
/*
* force null words to be created so
* set -- "" 2 ""; echo "$@" will do
@@ -856,17 +851,17 @@
doblank--;
continue;
}
- c = ifs0;
+ c = ord(ifs0);
if ((f & DOHEREDOC)) {
/* pseudo-field-split reliably */
if (c == 0)
- c = ' ';
+ c = ORD(' ');
break;
}
if ((f & DOSCALAR)) {
/* do not field-split */
if (x.split) {
- c = ' ';
+ c = ORD(' ');
break;
}
if (c == 0)
@@ -878,7 +873,7 @@
if (!quote && word == IFS_WS)
continue;
/* this is so we don't terminate */
- c = ' ';
+ c = ORD(' ');
/* now force-emit a word */
goto emit_word;
}
@@ -898,16 +893,33 @@
c = -1;
} else if (newlines) {
/* spit out saved NLs */
- c = '\n';
+ c = ORD('\n');
--newlines;
} else {
- while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
- if (c == '\n')
+ while ((c = shf_getc(x.u.shf)) == 0 ||
+ cinttype(c, C_NL)) {
+#ifdef MKSH_WITH_TEXTMODE
+ if (c == ORD('\r')) {
+ c = shf_getc(x.u.shf);
+ switch (c) {
+ case ORD('\n'):
+ break;
+ default:
+ shf_ungetc(c, x.u.shf);
+ /* FALLTHROUGH */
+ case -1:
+ c = ORD('\r');
+ break;
+ }
+ }
+#endif
+ if (c == ORD('\n'))
/* save newlines */
newlines++;
+ }
if (newlines && c != -1) {
shf_ungetc(c, x.u.shf);
- c = '\n';
+ c = ORD('\n');
--newlines;
}
}
@@ -943,6 +955,8 @@
(word == IFS_IWS || word == IFS_NWS) &&
!ctype(c, C_IFSWS))) {
emit_word:
+ if (f & DOHERESTR)
+ *dp++ = '\n';
*dp++ = '\0';
cp = Xclose(ds, dp);
if (fdo & DOBRACE)
@@ -988,11 +1002,11 @@
tilde_ok <<= 1;
/* mark any special second pass chars */
if (!quote)
- switch (c) {
- case '[':
- case '!':
- case '-':
- case ']':
+ switch (ord(c)) {
+ case ORD('['):
+ case ORD('!'):
+ case ORD('-'):
+ case ORD(']'):
/*
* For character classes - doesn't hurt
* to have magic !,-,]s outside of
@@ -1000,43 +1014,43 @@
*/
if (f & (DOPAT | DOGLOB)) {
fdo |= DOMAGIC;
- if (c == '[')
+ if ((unsigned int)c == ORD('['))
fdo |= f & DOGLOB;
*dp++ = MAGIC;
}
break;
- case '*':
- case '?':
+ case ORD('*'):
+ case ORD('?'):
if (f & (DOPAT | DOGLOB)) {
fdo |= DOMAGIC | (f & DOGLOB);
*dp++ = MAGIC;
}
break;
- case '{':
- case '}':
- case ',':
- if ((f & DOBRACE) && (c == '{' /*}*/ ||
+ case ORD('{'):
+ case ORD('}'):
+ case ORD(','):
+ if ((f & DOBRACE) &&
+ (ord(c) == ORD('{' /*}*/) ||
(fdo & DOBRACE))) {
fdo |= DOBRACE|DOMAGIC;
*dp++ = MAGIC;
}
break;
- case '=':
+ case ORD('='):
/* Note first unquoted = for ~ */
- if (!(f & DOTEMP) && !saw_eq &&
- (Flag(FBRACEEXPAND) ||
- (f & DOASNTILDE))) {
+ if (!(f & DOTEMP) && (!Flag(FPOSIX) ||
+ (f & DOASNTILDE)) && !saw_eq) {
saw_eq = true;
tilde_ok = 1;
}
break;
- case ':':
+ case ORD(':'):
/* : */
/* Note unquoted : for ~ */
if (!(f & DOTEMP) && (f & DOASNTILDE))
tilde_ok = 1;
break;
- case '~':
+ case ORD('~'):
/*
* tilde_ok is reset whenever
* any of ' " $( $(( ${ } are seen.
@@ -1081,6 +1095,17 @@
}
}
+static bool
+hasnonempty(const char **strv)
+{
+ size_t i = 0;
+
+ while (strv[i])
+ if (*strv[i++])
+ return (true);
+ return (false);
+}
+
/*
* Prepare to generate the string returned by ${} substitution.
*/
@@ -1092,12 +1117,12 @@
int c;
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
int stype; /* substitution type */
- int slen;
+ int slen = 0;
const char *p;
struct tbl *vp;
bool zero_ok = false;
- if ((stype = sp[0]) == '\0')
+ if ((stype = ord(sp[0])) == '\0')
/* Bad variable name */
return (-1);
@@ -1107,20 +1132,20 @@
* ${#var}, string length (-U: characters, +U: octets) or array size
* ${%var}, string width (-U: screen columns, +U: octets)
*/
- c = sp[1];
- if (stype == '%' && c == '\0')
+ c = ord(sp[1]);
+ if ((unsigned int)stype == ORD('%') && c == '\0')
return (-1);
- if ((stype == '#' || stype == '%') && c != '\0') {
+ if (ctype(stype, C_SUB2) && c != '\0') {
/* Can't have any modifiers for ${#...} or ${%...} */
if (*word != CSUBST)
return (-1);
sp++;
/* Check for size of array */
- if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
- p[2] == ']') {
+ if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
+ ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
int n = 0;
- if (stype != '#')
+ if ((unsigned int)stype != ORD('#'))
return (-1);
vp = global(arrayname(sp));
if (vp->flag & (ISSET|ARRAY))
@@ -1129,14 +1154,15 @@
if (vp->flag & ISSET)
n++;
c = n;
- } else if (c == '*' || c == '@') {
- if (stype != '#')
+ } else if ((unsigned int)c == ORD('*') ||
+ (unsigned int)c == ORD('@')) {
+ if ((unsigned int)stype != ORD('#'))
return (-1);
c = e->loc->argc;
} else {
p = str_val(global(sp));
zero_ok = p != null;
- if (stype == '#')
+ if ((unsigned int)stype == ORD('#'))
c = utflen(p);
else {
/* partial utf_mbswidth reimplementation */
@@ -1150,7 +1176,7 @@
if (!UTFMODE || (len = utf_mbtowc(&wc,
s)) == (size_t)-1)
/* not UTFMODE or not UTF-8 */
- wc = (unsigned char)(*s++);
+ wc = rtt2asc(*s++);
else
/* UTFMODE and UTF-8 */
s += len;
@@ -1165,159 +1191,175 @@
}
}
if (Flag(FNOUNSET) && c == 0 && !zero_ok)
- errorf("%s: %s", sp, "parameter not set");
+ errorf(Tf_parm, sp);
/* unqualified variable/string substitution */
*stypep = 0;
- xp->str = shf_smprintf("%d", c);
+ xp->str = shf_smprintf(Tf_d, c);
return (XSUB);
}
+ if ((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);
+ }
/* Check for qualifiers in word part */
stype = 0;
- c = word[slen = 0] == CHAR ? word[1] : 0;
- if (c == ':') {
+ c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
+ if ((unsigned int)c == ORD(':')) {
slen += 2;
- stype = 0x80;
- c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
+ stype = STYPE_DBL;
+ c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
}
- if (!stype && c == '/') {
+ if (!stype && (unsigned int)c == ORD('/')) {
slen += 2;
stype = c;
- if (word[slen] == ADELIM) {
+ if (word[slen] == ADELIM &&
+ ord(word[slen + 1]) == (unsigned int)c) {
slen += 2;
- stype |= 0x80;
+ stype |= STYPE_DBL;
}
- } else if (stype == 0x80 && (c == ' ' || c == '0')) {
- stype |= '0';
- } else if (ctype(c, C_SUBOP1)) {
+ } else if (stype == STYPE_DBL && ((unsigned int)c == ORD(' ') ||
+ (unsigned int)c == ORD('0'))) {
+ stype |= ORD('0');
+ } else if (ctype(c, C_SUB1)) {
slen += 2;
stype |= c;
- } else if (ctype(c, C_SUBOP2)) {
+ } else if (ctype(c, C_SUB2)) {
/* Note: ksh88 allows :%, :%%, etc */
slen += 2;
stype = c;
- if (word[slen + 0] == CHAR && c == word[slen + 1]) {
- stype |= 0x80;
+ if (word[slen + 0] == CHAR &&
+ ord(word[slen + 1]) == (unsigned int)c) {
+ stype |= STYPE_DBL;
slen += 2;
}
- } else if (c == '@') {
+ } else if ((unsigned int)c == ORD('@')) {
/* @x where x is command char */
- slen += 2;
- stype |= 0x100;
- if (word[slen] == CHAR) {
- stype |= word[slen + 1];
- slen += 2;
+ switch (c = ord(word[slen + 2]) == CHAR ?
+ ord(word[slen + 3]) : 0) {
+ case ORD('#'):
+ case ORD('/'):
+ case ORD('Q'):
+ break;
+ default:
+ return (-1);
}
+ stype |= STYPE_AT | c;
+ slen += 4;
} else if (stype)
/* : is not ok */
return (-1);
if (!stype && *word != CSUBST)
return (-1);
- *stypep = stype;
- *slenp = slen;
- c = sp[0];
- if (c == '*' || c == '@') {
- switch (stype & 0x17F) {
+ c = ord(sp[0]);
+ if ((unsigned int)c == ORD('*') || (unsigned int)c == ORD('@')) {
+ switch (stype & STYPE_SINGLE) {
/* can't assign to a vector */
- case '=':
+ case ORD('='):
/* can't trim a vector (yet) */
- case '%':
- case '#':
- case '0':
- case '/':
- case 0x100 | '#':
- case 0x100 | 'Q':
+ case ORD('%'):
+ case ORD('#'):
+ case ORD('?'):
+ case ORD('0'):
+ case ORD('/') | STYPE_AT:
+ case ORD('/'):
+ case ORD('#') | STYPE_AT:
+ case ORD('Q') | STYPE_AT:
return (-1);
}
if (e->loc->argc == 0) {
xp->str = null;
xp->var = global(sp);
- state = c == '@' ? XNULLSUB : XSUB;
+ 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(c == '@');
+ xp->split = tobool((unsigned int)c == ORD('@'));
state = XARG;
}
/* POSIX 2009? */
zero_ok = true;
- } else {
- if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
- p[2] == ']') {
- XPtrV wv;
+ } else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
+ ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
+ XPtrV wv;
- switch (stype & 0x17F) {
- /* can't assign to a vector */
- case '=':
- /* can't trim a vector (yet) */
- case '%':
- case '#':
- case '?':
- case '0':
- case '/':
- case 0x100 | '#':
- case 0x100 | 'Q':
- return (-1);
- }
- XPinit(wv, 32);
- if ((c = sp[0]) == '!')
- ++sp;
- vp = global(arrayname(sp));
- for (; vp; vp = vp->u.array) {
- if (!(vp->flag&ISSET))
- continue;
- XPput(wv, c == '!' ? shf_smprintf("%lu",
- arrayindex(vp)) :
- str_val(vp));
- }
- if (XPsize(wv) == 0) {
- xp->str = null;
- state = p[1] == '@' ? XNULLSUB : XSUB;
- XPfree(wv);
- } else {
- XPput(wv, 0);
- xp->u.strv = (const char **)XPptrv(wv);
- xp->str = *xp->u.strv++;
- /* ${foo[@]} */
- xp->split = tobool(p[1] == '@');
- state = XARG;
- }
+ 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:
+ return (-1);
+ }
+ c = 0;
+ arraynames:
+ XPinit(wv, 32);
+ vp = global(arrayname(sp));
+ for (; vp; vp = vp->u.array) {
+ if (!(vp->flag&ISSET))
+ continue;
+ XPput(wv, (unsigned int)c == ORD('!') ?
+ shf_smprintf(Tf_lu, arrayindex(vp)) :
+ str_val(vp));
+ }
+ if (XPsize(wv) == 0) {
+ xp->str = null;
+ state = ord(p[1]) == ORD('@') ? XNULLSUB : XSUB;
+ XPfree(wv);
} else {
- /* Can't assign things like $! or $1 */
- if ((stype & 0x17F) == '=' &&
- ctype(*sp, C_VAR1 | C_DIGIT))
- return (-1);
- if (*sp == '!' && sp[1]) {
- ++sp;
- xp->var = global(sp);
- if (vstrchr(sp, '['))
- xp->str = shf_smprintf("%s[%lu]",
- xp->var->name,
- arrayindex(xp->var));
- else
- xp->str = xp->var->name;
- } else {
- xp->var = global(sp);
- xp->str = str_val(xp->var);
- }
- state = XSUB;
+ XPput(wv, 0);
+ xp->u.strv = (const char **)XPptrv(wv);
+ xp->str = *xp->u.strv++;
+ /* ${foo[@]} */
+ xp->split = tobool(ord(p[1]) == ORD('@'));
+ 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 & 0x7F;
+ c = stype & STYPE_CHAR;
/* test the compiler's code generator */
- if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
- (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
- c == '=' || c == '-' || c == '?' : c == '+'))) ||
- stype == (0x80 | '0') || stype == (0x100 | '#') ||
- stype == (0x100 | 'Q'))
+ if ((!(stype & STYPE_AT) && (ctype(c, C_SUB2) ||
+ (((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('/'))
/* expand word instead of variable value */
state = XBASE;
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
- (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
- errorf("%s: %s", sp, "parameter not set");
+ (ctype(c, C_SUB2) || (state != XBASE && (unsigned int)c != ORD('+'))))
+ errorf(Tf_parm, sp);
+ *stypep = stype;
+ *slenp = slen;
return (state);
}
@@ -1325,17 +1367,28 @@
* Run the command in $(...) and read its output.
*/
static int
-comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
+comsub(Expand *xp, const char *cp, int fn)
{
Source *s, *sold;
struct op *t;
struct shf *shf;
+ bool doalias = false;
uint8_t old_utfmode = UTFMODE;
+ switch (fn) {
+ case COMASUB:
+ fn = COMSUB;
+ if (0)
+ /* FALLTHROUGH */
+ case FUNASUB:
+ fn = FUNSUB;
+ doalias = true;
+ }
+
s = pushs(SSTRING, ATEMP);
s->start = s->str = cp;
sold = source;
- t = compile(s, true);
+ t = compile(s, true, doalias);
afree(s, ATEMP);
source = sold;
@@ -1353,14 +1406,31 @@
struct ioword *io = *t->ioact;
char *name;
- if ((io->flag & IOTYPE) != IOREAD)
- errorf("%s: %s", "funny $() command",
- snptreef(NULL, 32, "%R", io));
- shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
- SHF_MAPHI|SHF_CLEXEC);
- if (shf == NULL)
- warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
- "can't open", "$(<...) input", cstrerror(errno));
+ switch (io->ioflag & IOTYPE) {
+ case IOREAD:
+ shf = shf_open(name = evalstr(io->ioname, DOTILDE),
+ O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
+ if (shf == NULL)
+ warningf(!Flag(FTALKING), Tf_sD_s_sD_s,
+ name, Tcant_open, "$(<...) input",
+ cstrerror(errno));
+ break;
+ case IOHERE:
+ if (!herein(io, &name)) {
+ xp->str = name;
+ /* as $(…) requires, trim trailing newlines */
+ name = strnul(name);
+ while (name > xp->str && name[-1] == '\n')
+ --name;
+ *name = '\0';
+ return (XSUB);
+ }
+ shf = NULL;
+ break;
+ default:
+ errorf(Tf_sD_s, T_funny_command,
+ snptreef(NULL, 32, Tft_R, io));
+ }
} else if (fn == FUNSUB) {
int ofd1;
struct temp *tf = NULL;
@@ -1371,8 +1441,8 @@
*/
maketemp(ATEMP, TT_FUNSUB, &tf);
if (!tf->shf) {
- errorf("can't %s temporary file %s: %s",
- "create", tf->tffn, cstrerror(errno));
+ errorf(Tf_temp,
+ Tcreate, tf->tffn, cstrerror(errno));
}
/* extract shf from temporary file, unlink and free it */
shf = tf->shf;
@@ -1424,12 +1494,13 @@
char *end = strnul(str);
char *p, c;
- switch (how & 0xFF) {
- case '#':
+ switch (how & (STYPE_CHAR | STYPE_DBL)) {
+ case ORD('#'):
/* shortest match at beginning */
for (p = str; p <= end; p += utf_ptradj(p)) {
c = *p; *p = '\0';
if (gmatchx(str, pat, false)) {
+ record_match(str);
*p = c;
return (p);
}
@@ -1436,11 +1507,12 @@
*p = c;
}
break;
- case '#'|0x80:
+ case ORD('#') | STYPE_DBL:
/* longest match at beginning */
for (p = end; p >= str; p--) {
c = *p; *p = '\0';
if (gmatchx(str, pat, false)) {
+ record_match(str);
*p = c;
return (p);
}
@@ -1447,7 +1519,7 @@
*p = c;
}
break;
- case '%':
+ case ORD('%'):
/* shortest match at end */
p = end;
while (p >= str) {
@@ -1455,7 +1527,7 @@
goto trimsub_match;
if (UTFMODE) {
char *op = p;
- while ((p-- > str) && ((*p & 0xC0) == 0x80))
+ while ((p-- > str) && ((rtt2asc(*p) & 0xC0) == 0x80))
;
if ((p < str) || (p + utf_ptradj(p) != op))
p = op - 1;
@@ -1463,11 +1535,12 @@
--p;
}
break;
- case '%'|0x80:
+ case ORD('%') | STYPE_DBL:
/* longest match at end */
for (p = str; p <= end; p++)
if (gmatchx(p, pat, false)) {
trimsub_match:
+ record_match(p);
strndupx(end, str, p - str, ATEMP);
return (end);
}
@@ -1493,7 +1566,7 @@
XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
else
qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize,
- sizeof(void *), xstrcmp);
+ sizeof(void *), ascpstrcmp);
}
#define GF_NONE 0
@@ -1559,7 +1632,7 @@
* SunOS 4.1.3 does this...
*/
if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
- xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&
+ mksh_cdirsep(xp[-1]) && !S_ISDIR(lstatb.st_mode) &&
(!S_ISLNK(lstatb.st_mode) ||
stat_check() < 0 || !S_ISDIR(statb.st_mode)))
return;
@@ -1569,7 +1642,7 @@
* directory
*/
if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
- xp > Xstring(*xs, xp) && xp[-1] != '/' &&
+ xp > Xstring(*xs, xp) && !mksh_cdirsep(xp[-1]) &&
(S_ISDIR(lstatb.st_mode) ||
(S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
S_ISDIR(statb.st_mode)))) {
@@ -1584,11 +1657,11 @@
if (xp > Xstring(*xs, xp))
*xp++ = '/';
- while (*sp == '/') {
+ while (mksh_cdirsep(*sp)) {
Xcheck(*xs, xp);
*xp++ = *sp++;
}
- np = strchr(sp, '/');
+ np = mksh_sdirsep(sp);
if (np != NULL) {
se = np;
/* don't assume '/', can be multiple kinds */
@@ -1596,7 +1669,7 @@
*np++ = '\0';
} else {
odirsep = '\0'; /* keep gcc quiet */
- se = sp + strlen(sp);
+ se = strnul(sp);
}
@@ -1607,10 +1680,10 @@
* directory isn't readable - if no globbing is needed, only execute
* permission should be required (as per POSIX)).
*/
- if (!has_globbing(sp, se)) {
+ if (!has_globbing(sp)) {
XcheckN(*xs, xp, se - sp + 1);
debunk(xp, sp, Xnleft(*xs, xp));
- xp += strlen(xp);
+ xp = strnul(xp);
*xpp = xp;
globit(xs, xpp, np, wp, check);
} else {
@@ -1622,7 +1695,7 @@
/* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
*xp = '\0';
prefix_len = Xlength(*xs, xp);
- dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
+ dirp = opendir(prefix_len ? Xstring(*xs, xp) : Tdot);
if (dirp == NULL)
goto Nodir;
while ((d = readdir(dirp)) != NULL) {
@@ -1639,9 +1712,8 @@
XcheckN(*xs, xp, len);
memcpy(xp, name, len);
*xpp = xp + len - 1;
- globit(xs, xpp, np, wp,
- (check & GF_MARKDIR) | GF_GLOBBED
- | (np ? GF_EXCHECK : GF_NONE));
+ globit(xs, xpp, np, wp, (check & GF_MARKDIR) |
+ GF_GLOBBED | (np ? GF_EXCHECK : GF_NONE));
xp = Xstring(*xs, xp) + prefix_len;
}
closedir(dirp);
@@ -1666,7 +1738,7 @@
memmove(dp, sp, s - sp);
for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++)
if (!ISMAGIC(*s) || !(*++s & 0x80) ||
- !vstrchr("*+?@! ", *s & 0x7f))
+ !ctype(*s & 0x7F, C_PATMO | C_SPC))
*d++ = *s;
else {
/* extended pattern operators: *+?@! */
@@ -1696,8 +1768,8 @@
Xinit(ts, tp, 16, ATEMP);
/* : only for DOASNTILDE form */
- while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))
- {
+ while (p[0] == CHAR && /* not cdirsep */ p[1] != '/' &&
+ (!isassign || p[1] != ':')) {
Xcheck(ts, tp);
*tp++ = p[1];
p += 2;
@@ -1724,24 +1796,40 @@
*
* based on a version by Arnold Robbins
*/
-
char *
do_tilde(char *cp)
{
char *dp = null;
+#ifndef MKSH_NOPWNAM
+ bool do_simplify = true;
+#endif
if (cp[0] == '\0')
dp = str_val(global("HOME"));
else if (cp[0] == '+' && cp[1] == '\0')
- dp = str_val(global("PWD"));
- else if (cp[0] == '-' && cp[1] == '\0')
- dp = str_val(global("OLDPWD"));
+ dp = str_val(global(TPWD));
+ else if (ksh_isdash(cp))
+ dp = str_val(global(TOLDPWD));
#ifndef MKSH_NOPWNAM
- else
+ else {
dp = homedir(cp);
+ do_simplify = false;
+ }
#endif
- /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
- return (dp == null ? NULL : dp);
+
+ /* if parameters aren't set, don't expand ~ */
+ if (dp == NULL || dp == null)
+ return (NULL);
+
+ /* simplify parameters as if cwd upon entry */
+#ifndef MKSH_NOPWNAM
+ if (do_simplify)
+#endif
+ {
+ strdupx(dp, dp, ATEMP);
+ simplify_path(dp);
+ }
+ return (dp);
}
#ifndef MKSH_NOPWNAM
@@ -1779,7 +1867,7 @@
char *p = exp_start;
/* search for open brace */
- while ((p = strchr(p, MAGIC)) && p[1] != '{' /*}*/)
+ while ((p = strchr(p, MAGIC)) && ord(p[1]) != ORD('{' /*}*/))
p += 2;
brace_start = p;
@@ -1790,9 +1878,9 @@
p += 2;
while (*p && count) {
if (ISMAGIC(*p++)) {
- if (*p == '{' /*}*/)
+ if (ord(*p) == ORD('{' /*}*/))
++count;
- else if (*p == /*{*/ '}')
+ else if (ord(*p) == ORD(/*{*/ '}'))
--count;
else if (*p == ',' && count == 1)
comma = p;
@@ -1824,9 +1912,9 @@
count = 1;
for (p = brace_start + 2; p != brace_end; p++) {
if (ISMAGIC(*p)) {
- if (*++p == '{' /*}*/)
+ if (ord(*++p) == ORD('{' /*}*/))
++count;
- else if ((*p == /*{*/ '}' && --count == 0) ||
+ else if ((ord(*p) == ORD(/*{*/ '}') && --count == 0) ||
(*p == ',' && count == 1)) {
char *news;
int l1, l2, l3;
Modified: trunk/contrib/mksh/exec.c
===================================================================
--- trunk/contrib/mksh/exec.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/exec.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-/* $OpenBSD: exec.c,v 1.50 2013/06/10 21:09:27 millert Exp $ */
+/* $OpenBSD: exec.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,10 +23,10 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.137.2.2 2015/03/01 15:42:59 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.201 2017/10/11 21:09:24 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
-#define MKSH_DEFAULT_EXECSHELL "/bin/sh"
+#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
#endif
static int comexec(struct op *, struct tbl * volatile, const char **,
@@ -34,15 +34,13 @@
static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
static int call_builtin(struct tbl *, const char **, const char *, bool);
static int iosetup(struct ioword *, struct tbl *);
-static int herein(struct ioword *, char **);
static const char *do_selectargs(const char **, bool);
static Test_op dbteste_isa(Test_env *, Test_meta);
static const char *dbteste_getopnd(Test_env *, Test_op, bool);
static void dbteste_error(Test_env *, int, const char *);
-static int search_access(const char *, int);
/* XXX: horrible kludge to fit within the framework */
-static char *plain_fmt_entry(char *, size_t, unsigned int, const void *);
-static char *select_fmt_entry(char *, size_t, unsigned int, const void *);
+static void plain_fmt_entry(char *, size_t, unsigned int, const void *);
+static void select_fmt_entry(char *, size_t, unsigned int, const void *);
/*
* execute command tree
@@ -61,7 +59,6 @@
const char *s, *ccp;
struct ioword **iowp;
struct tbl *tp = NULL;
- char *cp;
if (t == NULL)
return (0);
@@ -80,11 +77,18 @@
/* we want to run an executable, do some variance checks */
if (t->type == TCOM) {
+ /*
+ * Clear subst_exstat before argument expansion. Used by
+ * null commands (see comexec() and c_eval()) and by c_set().
+ */
+ subst_exstat = 0;
+
+ /* for $LINENO */
+ current_lineno = t->lineno;
+
/* check if this is 'var=<<EOF' */
- /*XXX this is broken, don’t use! */
- /*XXX https://bugs.launchpad.net/mksh/+bug/1380389 */
if (
- /* we have zero arguments, i.e. no programme to run */
+ /* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
@@ -92,49 +96,27 @@
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
- (t->ioact[0]->flag & IOTYPE) == IOHERE &&
+ (t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
/* the variable assignment begins with a valid varname */
(ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
/* and has no right-hand side (i.e. "varname=") */
ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
/* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
- ccp[3] == '=' && ccp[4] == EOS)) &&
- /* plus we can have a here document content */
- herein(t->ioact[0], &cp) == 0 && cp && *cp) {
- char *sp = cp, *dp;
- size_t n = ccp - t->vars[0] + (ccp[1] == '+' ? 4 : 2);
- size_t z;
+ ccp[3] == '=' && ccp[4] == EOS))) {
+ char *cp, *dp;
- /* drop redirection (will be garbage collected) */
- t->ioact = NULL;
-
- /* set variable to its expanded value */
- z = strlen(cp);
- if (notoktomul(z, 2) || notoktoadd(z * 2, n + 1))
- internal_errorf(Toomem, (size_t)-1);
- dp = alloc(z * 2 + n + 1, APERM);
- memcpy(dp, t->vars[0], n);
- t->vars[0] = dp;
- dp += n;
- while (*sp) {
- *dp++ = QCHAR;
- *dp++ = *sp++;
- }
- *dp = EOS;
+ if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
+ cp = NULL;
+ dp = shf_smprintf(Tf_ss, evalstr(t->vars[0],
+ DOASNTILDE | DOSCALAR), rv ? null : cp);
+ typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
/* free the expanded value */
afree(cp, APERM);
+ afree(dp, ATEMP);
+ goto Break;
}
/*
- * Clear subst_exstat before argument expansion. Used by
- * null commands (see comexec() and c_eval()) and by c_set().
- */
- subst_exstat = 0;
-
- /* for $LINENO */
- current_lineno = t->lineno;
-
- /*
* POSIX says expand command words first, then redirections,
* and assignments last..
*/
@@ -300,7 +282,7 @@
case TOR:
case TAND:
- rv = execute(t->left, XERROK, xerrok);
+ rv = execute(t->left, XERROK, NULL);
if ((rv == 0) == (t->type == TAND))
rv = execute(t->right, flags & XERROK, xerrok);
else {
@@ -357,16 +339,14 @@
rv = execute(t->left, flags & XERROK, xerrok);
}
} else {
- /* TSELECT */
- for (;;) {
- if (!(ccp = do_selectargs(ap, is_first))) {
- rv = 1;
- break;
- }
+ do_TSELECT:
+ if ((ccp = do_selectargs(ap, is_first))) {
is_first = false;
setstr(global(t->str), ccp, KSH_UNWIND_ERROR);
execute(t->left, flags & XERROK, xerrok);
+ goto do_TSELECT;
}
+ rv = 1;
}
break;
}
@@ -396,9 +376,8 @@
if (t->right == NULL)
/* should be error */
break;
- rv = execute(t->left, XERROK, NULL) == 0 ?
- execute(t->right->left, flags & XERROK, xerrok) :
- execute(t->right->right, flags & XERROK, xerrok);
+ rv = execute(execute(t->left, XERROK, NULL) == 0 ?
+ t->right->left : t->right->right, flags & XERROK, xerrok);
break;
case TCASE:
@@ -408,6 +387,7 @@
for (ap = (const char **)t->vars; *ap; ap++) {
if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
gmatchx(ccp, s, false))) {
+ record_match(ccp);
rv = execute(t->left, flags & XERROK,
xerrok);
i = 0;
@@ -459,7 +439,7 @@
if (rv == ENOEXEC)
scriptexec(t, (const char **)up);
else
- errorf("%s: %s", t->str, cstrerror(rv));
+ errorf(Tf_sD_s, t->str, cstrerror(rv));
}
Break:
exstat = rv & 0xFF;
@@ -550,7 +530,9 @@
break;
}
if ((tp = findcom(cp, FC_BI)) == NULL)
- errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin");
+ errorf(Tf_sD_sD_s, Tbuiltin, cp, Tnot_found);
+ if (tp->type == CSHELL && (tp->flag & LOW_BI))
+ break;
continue;
} else if (tp->val.f == c_exec) {
if (ap[1] == NULL)
@@ -572,6 +554,9 @@
}
ap += builtin_opt.optind;
flags |= XEXEC;
+ /* POSuX demands ksh88-like behaviour here */
+ if (Flag(FPOSIX))
+ fcflags = FC_PATH;
} else if (tp->val.f == c_command) {
bool saw_p = false;
@@ -589,7 +574,7 @@
fcflags = FC_BI|FC_PATH;
if (saw_p) {
if (Flag(FRESTRICTED)) {
- warningf(true, "%s: %s",
+ warningf(true, Tf_sD_s,
"command -p", "restricted");
rv = 1;
goto Leave;
@@ -607,25 +592,21 @@
subst_exstat = 0;
break;
}
-#ifndef MKSH_NO_EXTERNAL_CAT
- } else if (tp->val.f == c_cat) {
- /*
- * if we have any flags, do not use the builtin
- * in theory, we could allow -u, but that would
- * mean to use ksh_getopt here and possibly ad-
- * ded complexity and more code and isn't worth
- * additional hassle (and the builtin must call
- * ksh_getopt already but can't come back here)
- */
- if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
+ } else if (tp->flag & LOW_BI) {
+ /* if we have any flags, do not use the builtin */
+ if ((ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
/* argument, begins with -, is not - or -- */
- (ap[1][1] != '-' || ap[1][2] != '\0'))
- /* don't look for builtins or functions */
- fcflags = FC_PATH;
- else
- /* go on, use the builtin */
- break;
-#endif
+ (ap[1][1] != '-' || ap[1][2] != '\0')) ||
+ /* always prefer the external utility */
+ (tp->flag & LOWER_BI)) {
+ struct tbl *ext_cmd;
+
+ ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC);
+ if (ext_cmd && (ext_cmd->type != CTALIAS ||
+ (ext_cmd->flag & ISSET)))
+ tp = ext_cmd;
+ }
+ break;
} else if (tp->val.f == c_trap) {
t->u.evalflags &= ~DOTCOMEXEC;
break;
@@ -693,8 +674,8 @@
rv = subst_exstat;
goto Leave;
} else if (!tp) {
- if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
- warningf(true, "%s: %s", cp, "restricted");
+ if (Flag(FRESTRICTED) && mksh_vdirsep(cp)) {
+ warningf(true, Tf_sD_s, cp, "restricted");
rv = 1;
goto Leave;
}
@@ -705,6 +686,7 @@
/* shell built-in */
case CSHELL:
+ do_call_builtin:
rv = call_builtin(tp, (const char **)ap, null, resetspec);
if (resetspec && tp->val.f == c_shift) {
l_expand->argc = l_assign->argc;
@@ -714,30 +696,38 @@
/* function call */
case CFUNC: {
- volatile unsigned char old_xflag;
volatile uint32_t old_inuse;
const char * volatile old_kshname;
+ volatile uint8_t old_flags[FNFLAGS];
if (!(tp->flag & ISSET)) {
struct tbl *ftp;
if (!tp->u.fpath) {
+ fpath_error:
rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
- warningf(true, "%s: %s %s: %s", cp,
- "can't find", "function definition file",
+ warningf(true, Tf_sD_s_sD_s, cp,
+ Tcant_find, Tfile_fd,
cstrerror(tp->u2.errnov));
break;
}
- if (include(tp->u.fpath, 0, NULL, false) < 0) {
- warningf(true, "%s: %s %s %s: %s", cp,
- "can't open", "function definition file",
- tp->u.fpath, cstrerror(errno));
- rv = 127;
- break;
- }
- if (!(ftp = findfunc(cp, hash(cp), false)) ||
+ errno = 0;
+ if (include(tp->u.fpath, 0, NULL, false) < 0 ||
+ !(ftp = findfunc(cp, hash(cp), false)) ||
!(ftp->flag & ISSET)) {
- warningf(true, "%s: %s %s", cp,
+ rv = errno;
+ if ((ftp = findcom(cp, FC_BI)) &&
+ (ftp->type == CSHELL) &&
+ (ftp->flag & LOW_BI)) {
+ tp = ftp;
+ goto do_call_builtin;
+ }
+ if (rv) {
+ tp->u2.errnov = rv;
+ cp = tp->u.fpath;
+ goto fpath_error;
+ }
+ warningf(true, Tf_sD_s_s, cp,
"function not defined by", tp->u.fpath);
rv = 127;
break;
@@ -768,8 +758,9 @@
getopts_reset(1);
}
- old_xflag = Flag(FXTRACE) ? 1 : 0;
- change_xtrace((Flag(FXTRACEREC) ? old_xflag : 0) |
+ for (type_flags = 0; type_flags < FNFLAGS; ++type_flags)
+ old_flags[type_flags] = shell_flags[type_flags];
+ change_xtrace((Flag(FXTRACEREC) ? Flag(FXTRACE) : 0) |
((tp->flag & TRACE) ? 1 : 0), false);
old_inuse = tp->flag & FINUSE;
tp->flag |= FINUSE;
@@ -781,13 +772,20 @@
}
kshname = old_kshname;
- change_xtrace(old_xflag, false);
+ change_xtrace(old_flags[(int)FXTRACE], false);
+#ifndef MKSH_LEGACY_MODE
+ if (tp->flag & FKSH) {
+ /* Korn style functions restore Flags on return */
+ old_flags[(int)FXTRACE] = Flag(FXTRACE);
+ for (type_flags = 0; type_flags < FNFLAGS; ++type_flags)
+ shell_flags[type_flags] = old_flags[type_flags];
+ }
+#endif
tp->flag = (tp->flag & ~FINUSE) | old_inuse;
/*
* Were we deleted while executing? If so, free the
- * execution tree. TODO: Unfortunately, the table entry
- * is never re-used until the lookup table is expanded.
+ * execution tree.
*/
if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
if (tp->flag & ALLOC) {
@@ -810,7 +808,7 @@
/* NOTREACHED */
default:
quitenv(NULL);
- internal_errorf("%s %d", "CFUNC", i);
+ internal_errorf(Tunexpected_type, Tunwind, Tfunction, i);
}
break;
}
@@ -822,10 +820,10 @@
if (!(tp->flag&ISSET)) {
if (tp->u2.errnov == ENOENT) {
rv = 127;
- warningf(true, "%s: %s", cp, "not found");
+ warningf(true, Tf_sD_s, cp, Tnot_found);
} else {
rv = 126;
- warningf(true, "%s: %s: %s", cp, "can't execute",
+ warningf(true, Tf_sD_sD_s, cp, "can't execute",
cstrerror(tp->u2.errnov));
}
break;
@@ -879,7 +877,7 @@
#endif
union mksh_ccphack args, cap;
- sh = str_val(global("EXECSHELL"));
+ sh = str_val(global(TEXECSHELL));
if (sh && *sh)
sh = search_path(sh, path, X_OK, NULL);
if (!sh || !*sh)
@@ -888,11 +886,16 @@
*tp->args-- = tp->str;
#ifndef MKSH_SMALL
- if ((fd = open(tp->str, O_RDONLY | O_BINARY)) >= 0) {
+ if ((fd = binopen2(tp->str, O_RDONLY)) >= 0) {
unsigned char *cp;
+#ifndef MKSH_EBCDIC
unsigned short m;
+#endif
ssize_t n;
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ setmode(fd, O_TEXT);
+#endif
/* read first couple of octets from file */
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
@@ -907,7 +910,7 @@
(buf[2] == 0xBF)) ? 3 : 0);
/* scan for newline or NUL (end of buffer) */
- while (*cp && *cp != '\n')
+ while (!ctype(*cp, C_NL | C_NUL))
++cp;
/* if the shebang line is longer than MAXINTERP, bail out */
if (!*cp)
@@ -915,15 +918,20 @@
/* replace newline by NUL */
*cp = '\0';
- /* restore begin of shebang position (buf+0 or buf+3) */
+ /* restore start of shebang position (buf+0 or buf+3) */
cp = buf + n;
/* bail out if no shebang magic found */
- if ((cp[0] != '#') || (cp[1] != '!'))
+ if (cp[0] == '#' && cp[1] == '!')
+ cp += 2;
+#ifdef __OS2__
+ else if (!strncmp(cp, Textproc, 7) &&
+ ctype(cp[7], C_BLANK))
+ cp += 8;
+#endif
+ else
goto noshebang;
-
- cp += 2;
/* skip whitespace before shell name */
- while (*cp == ' ' || *cp == '\t')
+ while (ctype(*cp, C_BLANK))
++cp;
/* just whitespace on the line? */
if (*cp == '\0')
@@ -931,20 +939,36 @@
/* no, we actually found an interpreter name */
sh = (char *)cp;
/* look for end of shell/interpreter name */
- while (*cp != ' ' && *cp != '\t' && *cp != '\0')
+ while (!ctype(*cp, C_BLANK | C_NUL))
++cp;
/* any arguments? */
if (*cp) {
*cp++ = '\0';
/* skip spaces before arguments */
- while (*cp == ' ' || *cp == '\t')
+ while (ctype(*cp, C_BLANK))
++cp;
/* pass it all in ONE argument (historic reasons) */
if (*cp)
*tp->args-- = (char *)cp;
}
+#ifdef __OS2__
+ /*
+ * On OS/2, the directory structure differs from normal
+ * Unix, which can make many scripts whose shebang
+ * hardcodes the path to an interpreter fail (and there
+ * might be no /usr/bin/env); for user convenience, if
+ * the specified interpreter is not usable, do a PATH
+ * search to find it.
+ */
+ if (mksh_vdirsep(sh) && !search_path(sh, path, X_OK, NULL)) {
+ cp = search_path(_getname(sh), path, X_OK, NULL);
+ if (cp)
+ sh = cp;
+ }
+#endif
goto nomagic;
noshebang:
+#ifndef MKSH_EBCDIC
m = buf[0] << 8 | buf[1];
if (m == 0x7F45 && buf[2] == 'L' && buf[3] == 'F')
errorf("%s: not executable: %d-bit ELF file", tp->str,
@@ -956,9 +980,27 @@
(m == /* ECOFF_I386 */ 0x4C01) ||
(m == /* ECOFF_M68K */ 0x0150 || m == 0x5001) ||
(m == /* ECOFF_SH */ 0x0500 || m == 0x0005) ||
- (m == /* "MZ" */ 0x4D5A) ||
+ (m == /* bzip */ 0x425A) || (m == /* "MZ" */ 0x4D5A) ||
+ (m == /* "NE" */ 0x4E45) || (m == /* "LX" */ 0x4C58) ||
+ (m == /* ksh93 */ 0x0B13) || (m == /* LZIP */ 0x4C5A) ||
+ (m == /* xz */ 0xFD37 && buf[2] == 'z' && buf[3] == 'X' &&
+ buf[4] == 'Z') || (m == /* 7zip */ 0x377A) ||
(m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D))
errorf("%s: not executable: magic %04X", tp->str, m);
+#endif
+#ifdef __OS2__
+ cp = _getext(tp->str);
+ if (cp && (!stricmp(cp, ".cmd") || !stricmp(cp, ".bat"))) {
+ /* execute .cmd and .bat with OS2_SHELL, usually CMD.EXE */
+ sh = str_val(global("OS2_SHELL"));
+ *tp->args-- = "/c";
+ /* convert slahes to backslashes */
+ for (cp = tp->str; *cp; cp++) {
+ if (*cp == '/')
+ *cp = '\\';
+ }
+ }
+#endif
nomagic:
;
}
@@ -970,16 +1012,20 @@
execve(args.rw[0], args.rw, cap.rw);
/* report both the programme that was run and the bogus interpreter */
- errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
+ errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno));
}
+/* actual 'builtin' built-in utility call is handled in comexec() */
int
-shcomexec(const char **wp)
+c_builtin(const char **wp)
{
- struct tbl *tp;
+ return (call_builtin(get_builtin(*wp), wp, Tbuiltin, false));
+}
- tp = ktsearch(&builtins, *wp, hash(*wp));
- return (call_builtin(tp, wp, "shcomexec", false));
+struct tbl *
+get_builtin(const char *s)
+{
+ return (s && *s ? ktsearch(&builtins, s, hash(s)) : NULL);
}
/*
@@ -1020,12 +1066,6 @@
nhash = hash(name);
- if (t != NULL && !tobool(t->u.ksh_func)) {
- /* drop same-name aliases for POSIX functions */
- if ((tp = ktsearch(&aliases, name, nhash)))
- ktdelete(tp);
- }
-
while (/* CONSTCOND */ 1) {
tp = findfunc(name, nhash, true);
@@ -1045,7 +1085,7 @@
}
if (tp->flag & ALLOC) {
- tp->flag &= ~(ISSET|ALLOC);
+ tp->flag &= ~(ISSET|ALLOC|FKSH);
tfree(tp->val.t, tp->areap);
}
@@ -1073,23 +1113,46 @@
uint32_t flag = DEFINED;
/* see if any flags should be set for this builtin */
- while (1) {
- if (*name == '=')
- /* command does variable assignment */
- flag |= KEEPASN;
- else if (*name == '*')
- /* POSIX special builtin */
- flag |= SPEC_BI;
- else
- break;
- name++;
+ flags_loop:
+ switch (*name) {
+ case '=':
+ /* command does variable assignment */
+ flag |= KEEPASN;
+ break;
+ case '*':
+ /* POSIX special builtin */
+ flag |= SPEC_BI;
+ break;
+ case '~':
+ /* external utility overrides built-in utility, always */
+ flag |= LOWER_BI;
+ /* FALLTHROUGH */
+ case '!':
+ /* external utility overrides built-in utility, with flags */
+ flag |= LOW_BI;
+ break;
+ case '-':
+ /* is declaration utility if argv[1] is one (POSIX: command) */
+ flag |= DECL_FWDR;
+ break;
+ case '^':
+ /* is declaration utility (POSIX: export, readonly) */
+ flag |= DECL_UTIL;
+ break;
+ default:
+ goto flags_seen;
}
+ ++name;
+ goto flags_loop;
+ flags_seen:
+ /* enter into the builtins hash table */
tp = ktenter(&builtins, name, hash(name));
tp->flag = flag;
tp->type = CSHELL;
tp->val.f = func;
+ /* return name, for direct builtin call check in main.c */
return (name);
}
@@ -1109,7 +1172,7 @@
char *fpath;
union mksh_cchack npath;
- if (vstrchr(name, '/')) {
+ if (mksh_vdirsep(name)) {
insert = 0;
/* prevent FPATH search below */
flags &= ~FC_FUNC;
@@ -1125,7 +1188,7 @@
if (!tp && (flags & FC_FUNC)) {
tp = findfunc(name, h, false);
if (tp && !(tp->flag & ISSET)) {
- if ((fpath = str_val(global("FPATH"))) == null) {
+ if ((fpath = str_val(global(TFPATH))) == null) {
tp->u.fpath = NULL;
tp->u2.errnov = ENOENT;
} else
@@ -1170,7 +1233,7 @@
afree(npath.rw, ATEMP);
tp->flag |= ISSET|ALLOC;
} else if ((flags & FC_FUNC) &&
- (fpath = str_val(global("FPATH"))) != null &&
+ (fpath = str_val(global(TFPATH))) != null &&
(npath.ro = search_path(name, fpath, R_OK,
&tp->u2.errnov)) != NULL) {
/*
@@ -1200,7 +1263,7 @@
struct tstate ts;
for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; )
- if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) {
+ if ((tp->flag&ISSET) && (all || !mksh_abspath(tp->val.s))) {
if (tp->flag&ALLOC) {
tp->flag &= ~(ALLOC|ISSET);
afree(tp->val.s, APERM);
@@ -1210,7 +1273,7 @@
}
/* check if path is something we want to find */
-static int
+int
search_access(const char *fn, int mode)
{
struct stat sb;
@@ -1219,16 +1282,31 @@
/* file does not exist */
return (ENOENT);
/* LINTED use of access */
- if (access(fn, mode) < 0)
+ if (access(fn, mode) < 0) {
/* file exists, but we can't access it */
- return (errno);
+ int eno;
+
+ eno = errno;
+ return (eno ? eno : EACCES);
+ }
+#ifdef __OS2__
+ /* treat all files as executable on OS/2 */
+ sb.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+#endif
if (mode == X_OK && (!S_ISREG(sb.st_mode) ||
- !(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))))
+ !(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
/* access(2) may say root can execute everything */
return (S_ISDIR(sb.st_mode) ? EISDIR : EACCES);
return (0);
}
+#ifdef __OS2__
+/* check if path is something we want to find, adding executable extensions */
+#define search_access(fn, mode) access_ex((search_access), (fn), (mode))
+#else
+#define search_access(fn, mode) (search_access)((fn), (mode))
+#endif
+
/*
* search for command with PATH
*/
@@ -1245,12 +1323,16 @@
size_t namelen;
int ec = 0, ev;
- if (vstrchr(name, '/')) {
+ if (mksh_vdirsep(name)) {
if ((ec = search_access(name, mode)) == 0) {
search_path_ok:
if (errnop)
*errnop = 0;
+#ifndef __OS2__
return (name);
+#else
+ return (real_exec_name(name));
+#endif
}
goto search_path_err;
}
@@ -1261,12 +1343,16 @@
sp = lpath;
while (sp != NULL) {
xp = Xstring(xs, xp);
- if (!(p = cstrchr(sp, ':')))
- p = sp + strlen(sp);
+ if (!(p = cstrchr(sp, MKSH_PATHSEPC)))
+ p = strnul(sp);
if (p != sp) {
XcheckN(xs, xp, p - sp);
memcpy(xp, sp, p - sp);
xp += p - sp;
+#ifdef __OS2__
+ if (xp > Xstring(xs, xp) && mksh_cdirsep(xp[-1]))
+ xp--;
+#endif
*xp++ = '/';
}
sp = p;
@@ -1295,7 +1381,7 @@
int rv;
if (!tp)
- internal_errorf("%s: %s", where, wp[0]);
+ internal_errorf(Tf_sD_s, where, wp[0]);
builtin_argv0 = wp[0];
builtin_spec = tobool(!resetspec && (tp->flag & SPEC_BI));
shf_reopen(1, SHF_WR, shl_stdout);
@@ -1316,9 +1402,9 @@
iosetup(struct ioword *iop, struct tbl *tp)
{
int u = -1;
- char *cp = iop->name;
- int iotype = iop->flag & IOTYPE;
- bool do_open = true, do_close = false;
+ char *cp = iop->ioname;
+ int iotype = iop->ioflag & IOTYPE;
+ bool do_open = true, do_close = false, do_fstat = false;
int flags = 0;
struct ioword iotmp;
struct stat statb;
@@ -1328,12 +1414,12 @@
/* Used for tracing and error messages to print expanded cp */
iotmp = *iop;
- iotmp.name = (iotype == IOHERE) ? NULL : cp;
- iotmp.flag |= IONAMEXP;
+ iotmp.ioname = (iotype == IOHERE) ? NULL : cp;
+ iotmp.ioflag |= IONAMEXP;
if (Flag(FXTRACE)) {
change_xtrace(2, false);
- fptreef(shl_xtrace, 0, "%R", &iotmp);
+ fptreef(shl_xtrace, 0, Tft_R, &iotmp);
change_xtrace(1, false);
}
@@ -1347,14 +1433,27 @@
break;
case IOWRITE:
- flags = O_WRONLY | O_CREAT | O_TRUNC;
- /*
- * The stat() is here to allow redirections to
- * things like /dev/null without error.
- */
- if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
- (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
- flags |= O_EXCL;
+ if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB)) {
+ /* >file under set -C */
+ if (stat(cp, &statb)) {
+ /* nonexistent file */
+ flags = O_WRONLY | O_CREAT | O_EXCL;
+ } else if (S_ISREG(statb.st_mode)) {
+ /* regular file, refuse clobbering */
+ goto clobber_refused;
+ } else {
+ /*
+ * allow redirections to things
+ * like /dev/null without error
+ */
+ flags = O_WRONLY;
+ /* but check again after opening */
+ do_fstat = true;
+ }
+ } else {
+ /* >|file or set +C */
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ }
break;
case IORDWR:
@@ -1372,21 +1471,21 @@
const char *emsg;
do_open = false;
- if (*cp == '-' && !cp[1]) {
+ if (ksh_isdash(cp)) {
/* prevent error return below */
u = 1009;
do_close = true;
} else if ((u = check_fd(cp,
- X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
+ X_OK | ((iop->ioflag & IORDUP) ? R_OK : W_OK),
&emsg)) < 0) {
char *sp;
- warningf(true, "%s: %s",
- (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg);
+ warningf(true, Tf_sD_s,
+ (sp = snptreef(NULL, 32, Tft_R, &iotmp)), emsg);
afree(sp, ATEMP);
return (-1);
}
- if (u == iop->unit)
+ if (u == (int)iop->unit)
/* "dup from" == "dup to" */
return (0);
break;
@@ -1395,19 +1494,31 @@
if (do_open) {
if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
- warningf(true, "%s: %s", cp, "restricted");
+ warningf(true, Tf_sD_s, cp, "restricted");
return (-1);
}
- u = open(cp, flags | O_BINARY, 0666);
+ u = binopen3(cp, flags, 0666);
+ if (do_fstat && u >= 0) {
+ /* prevent race conditions */
+ if (fstat(u, &statb) || S_ISREG(statb.st_mode)) {
+ close(u);
+ clobber_refused:
+ u = -1;
+ errno = EEXIST;
+ }
+ }
}
if (u < 0) {
/* herein() may already have printed message */
if (u == -1) {
u = errno;
- warningf(true, "can't %s %s: %s",
+ warningf(true, Tf_cant_ss_s,
+#if 0
+ /* can't happen */
iotype == IODUP ? "dup" :
+#endif
(iotype == IOREAD || iotype == IOHERE) ?
- "open" : "create", cp, cstrerror(u));
+ Topen : Tcreate, cp, cstrerror(u));
}
return (-1);
}
@@ -1414,7 +1525,7 @@
/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
if (e->savefd[iop->unit] == 0) {
/* If these are the same, it means unit was previously closed */
- if (u == iop->unit)
+ if (u == (int)iop->unit)
e->savefd[iop->unit] = -1;
else
/*
@@ -1429,15 +1540,14 @@
if (do_close)
close(iop->unit);
- else if (u != iop->unit) {
+ else if (u != (int)iop->unit) {
if (ksh_dup2(u, iop->unit, true) < 0) {
int eno;
char *sp;
eno = errno;
- warningf(true, "%s %s %s",
- "can't finish (dup) redirection",
- (sp = snptreef(NULL, 32, "%R", &iotmp)),
+ warningf(true, Tf_s_sD_s, Tredirection_dup,
+ (sp = snptreef(NULL, 32, Tft_R, &iotmp)),
cstrerror(eno));
afree(sp, ATEMP);
if (iotype != IODUP)
@@ -1451,7 +1561,7 @@
* causes the shell to close its copies
*/
else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
- if (iop->flag & IORDUP)
+ if (iop->ioflag & IORDUP)
/* possible exec <&p */
coproc_read_close(u);
else
@@ -1471,9 +1581,9 @@
* unquoted, the string is expanded first.
*/
static int
-hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
+hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
{
- const char * volatile ccp = content;
+ const char * volatile ccp = iop->heredoc;
struct source *s, *osource;
osource = source;
@@ -1484,13 +1594,15 @@
/* special to iosetup(): don't print error */
return (-2);
}
- if (sub) {
+ if (iop->ioflag & IOHERESTR) {
+ ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC);
+ } else if (sub) {
/* do substitutions on the content of heredoc */
s = pushs(SSTRING, ATEMP);
s->start = s->str = ccp;
source = s;
if (yylex(sub) != LWORD)
- internal_errorf("%s: %s", "herein", "yylex");
+ internal_errorf("herein: yylex");
source = osource;
ccp = evalstr(yylval.cp, DOSCALAR | DOHEREDOC);
}
@@ -1504,7 +1616,7 @@
return (0);
}
-static int
+int
herein(struct ioword *iop, char **resbuf)
{
int fd = -1;
@@ -1512,19 +1624,12 @@
struct temp *h;
int i;
- /* ksh -c 'cat <<EOF' can cause this... */
- if (iop->heredoc == NULL) {
- warningf(true, "%s missing", "here document");
- /* special to iosetup(): don't print error */
- return (-2);
- }
-
/* lexer substitution flags */
- i = (iop->flag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
+ i = (iop->ioflag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
/* skip all the fd setup if we just want the value */
if (resbuf != NULL)
- return (hereinval(iop->heredoc, i, resbuf, NULL));
+ return (hereinval(iop, i, resbuf, NULL));
/*
* Create temp file to hold content (done before newenv
@@ -1531,10 +1636,10 @@
* so temp doesn't get removed too soon).
*/
h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
- if (!(shf = h->shf) || (fd = open(h->tffn, O_RDONLY | O_BINARY, 0)) < 0) {
+ if (!(shf = h->shf) || (fd = binopen3(h->tffn, O_RDONLY, 0)) < 0) {
i = errno;
- warningf(true, "can't %s temporary file %s: %s",
- !shf ? "create" : "open", h->tffn, cstrerror(i));
+ warningf(true, Tf_temp,
+ !shf ? Tcreate : Topen, h->tffn, cstrerror(i));
if (shf)
shf_close(shf);
/* special to iosetup(): don't print error */
@@ -1541,7 +1646,7 @@
return (-2);
}
- if (hereinval(iop->heredoc, i, NULL, shf) == -2) {
+ if (hereinval(iop, i, NULL, shf) == -2) {
close(fd);
/* special to iosetup(): don't print error */
return (-2);
@@ -1550,8 +1655,8 @@
if (shf_close(shf) == -1) {
i = errno;
close(fd);
- warningf(true, "can't %s temporary file %s: %s",
- "write", h->tffn, cstrerror(i));
+ warningf(true, Tf_temp,
+ Twrite, h->tffn, cstrerror(i));
/* special to iosetup(): don't print error */
return (-2);
}
@@ -1567,7 +1672,7 @@
do_selectargs(const char **ap, bool print_menu)
{
static const char *read_args[] = {
- "read", "-r", "REPLY", NULL
+ Tread, "-r", "REPLY", NULL
};
char *s;
int i, argct;
@@ -1583,13 +1688,13 @@
*/
if (print_menu || !*str_val(global("REPLY")))
pr_menu(ap);
- shellf("%s", str_val(global("PS3")));
- if (call_builtin(findcom("read", FC_BI), read_args, Tselect,
+ shellf(Tf_s, str_val(global("PS3")));
+ if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect,
false))
return (NULL);
- s = str_val(global("REPLY"));
- if (*s && getn(s, &i))
- return ((i >= 1 && i <= argct) ? ap[i - 1] : null);
+ if (*(s = str_val(global("REPLY"))))
+ return ((getn(s, &i) && i >= 1 && i <= argct) ?
+ ap[i - 1] : null);
print_menu = true;
}
}
@@ -1600,7 +1705,7 @@
};
/* format a single select menu item */
-static char *
+static void
select_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
{
const struct select_menu_info *smi =
@@ -1608,7 +1713,6 @@
shf_snprintf(buf, buflen, "%*u) %s",
smi->num_width, i + 1, smi->args[i]);
- return (buf);
}
/*
@@ -1621,6 +1725,7 @@
const char * const *pp;
size_t acols = 0, aocts = 0, i;
unsigned int n;
+ struct columnise_opts co;
/*
* width/column calculations were done once and saved, but this
@@ -1649,20 +1754,21 @@
smi.num_width++;
smi.args = ap;
- print_columns(shl_out, n, select_fmt_entry, (void *)&smi,
- smi.num_width + 2 + aocts, smi.num_width + 2 + acols,
- true);
+ co.shf = shl_out;
+ co.linesep = '\n';
+ co.prefcol = co.do_last = true;
+ print_columns(&co, n, select_fmt_entry, (void *)&smi,
+ smi.num_width + 2 + aocts, smi.num_width + 2 + acols);
}
-static char *
+static void
plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
{
strlcpy(buf, ((const char * const *)arg)[i], buflen);
- return (buf);
}
void
-pr_list(char * const *ap)
+pr_list(struct columnise_opts *cop, char * const *ap)
{
size_t acols = 0, aocts = 0, i;
unsigned int n;
@@ -1677,8 +1783,8 @@
acols = i;
}
- print_columns(shl_out, n, plain_fmt_entry, (const void *)ap,
- aocts, acols, false);
+ print_columns(cop, n, plain_fmt_entry, (const void *)ap,
+ aocts, acols);
}
/*
Modified: trunk/contrib/mksh/expr.c
===================================================================
--- trunk/contrib/mksh/expr.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/expr.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -2,8 +2,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,50 +23,16 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.76.2.1 2015/01/25 15:44:05 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.103 2018/01/14 01:29:47 tg Exp $");
-/* the order of these enums is constrained by the order of opinfo[] */
-enum token {
- /* some (long) unary operators */
- O_PLUSPLUS = 0, O_MINUSMINUS,
- /* binary operators */
- O_EQ, O_NE,
- /* assignments are assumed to be in range O_ASN .. O_BORASN */
- O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
-#ifndef MKSH_LEGACY_MODE
- O_ROLASN, O_RORASN,
-#endif
- O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
- /* binary non-assignment operators */
-#ifndef MKSH_LEGACY_MODE
- O_ROL, O_ROR,
-#endif
- O_LSHIFT, O_RSHIFT,
- O_LE, O_GE, O_LT, O_GT,
- O_LAND,
- O_LOR,
- O_TIMES, O_DIV, O_MOD,
- O_PLUS, O_MINUS,
- O_BAND,
- O_BXOR,
- O_BOR,
- O_TERN,
- O_COMMA,
- /* things after this aren't used as binary operators */
- /* unary that are not also binaries */
- O_BNOT, O_LNOT,
- /* misc */
- OPEN_PAREN, CLOSE_PAREN, CTERN,
- /* things that don't appear in the opinfo[] table */
- VAR, LIT, END, BAD
-};
-#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+#define EXPRTOK_DEFNS
+#include "exprtok.h"
/* precisions; used to be enum prec but we do arithmetics on it */
#define P_PRIMARY 0 /* VAR, LIT, (), ! ~ ++ -- */
#define P_MULT 1 /* * / % */
#define P_ADD 2 /* + - */
-#define P_SHIFT 3 /* <<< >>> << >> */
+#define P_SHIFT 3 /* ^< ^> << >> */
#define P_RELATION 4 /* < <= > >= */
#define P_EQUALITY 5 /* == != */
#define P_BAND 6 /* & */
@@ -75,74 +41,31 @@
#define P_LAND 9 /* && */
#define P_LOR 10 /* || */
#define P_TERN 11 /* ?: */
- /* = += -= *= /= %= <<<= >>>= <<= >>= &= ^= |= */
+ /* = += -= *= /= %= ^<= ^>= <<= >>= &= ^= |= */
#define P_ASSIGN 12
#define P_COMMA 13 /* , */
#define MAX_PREC P_COMMA
-struct opinfo {
- char name[5];
- /* name length */
- uint8_t len;
- /* precedence: lower is higher */
- uint8_t prec;
+enum token {
+#define EXPRTOK_ENUM
+#include "exprtok.h"
};
-/*
- * Tokens in this table must be ordered so the longest are first
- * (eg, += before +). If you change something, change the order
- * of enum token too.
- */
-static const struct opinfo opinfo[] = {
- { "++", 2, P_PRIMARY }, /* before + */
- { "--", 2, P_PRIMARY }, /* before - */
- { "==", 2, P_EQUALITY }, /* before = */
- { "!=", 2, P_EQUALITY }, /* before ! */
- { "=", 1, P_ASSIGN }, /* keep assigns in a block */
- { "*=", 2, P_ASSIGN },
- { "/=", 2, P_ASSIGN },
- { "%=", 2, P_ASSIGN },
- { "+=", 2, P_ASSIGN },
- { "-=", 2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
- { "<<<=", 4, P_ASSIGN }, /* before <<< */
- { ">>>=", 4, P_ASSIGN }, /* before >>> */
-#endif
- { "<<=", 3, P_ASSIGN },
- { ">>=", 3, P_ASSIGN },
- { "&=", 2, P_ASSIGN },
- { "^=", 2, P_ASSIGN },
- { "|=", 2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
- { "<<<", 3, P_SHIFT }, /* before << */
- { ">>>", 3, P_SHIFT }, /* before >> */
-#endif
- { "<<", 2, P_SHIFT },
- { ">>", 2, P_SHIFT },
- { "<=", 2, P_RELATION },
- { ">=", 2, P_RELATION },
- { "<", 1, P_RELATION },
- { ">", 1, P_RELATION },
- { "&&", 2, P_LAND },
- { "||", 2, P_LOR },
- { "*", 1, P_MULT },
- { "/", 1, P_MULT },
- { "%", 1, P_MULT },
- { "+", 1, P_ADD },
- { "-", 1, P_ADD },
- { "&", 1, P_BAND },
- { "^", 1, P_BXOR },
- { "|", 1, P_BOR },
- { "?", 1, P_TERN },
- { ",", 1, P_COMMA },
- { "~", 1, P_PRIMARY },
- { "!", 1, P_PRIMARY },
- { "(", 1, P_PRIMARY },
- { ")", 1, P_PRIMARY },
- { ":", 1, P_PRIMARY },
- { "", 0, P_PRIMARY }
+static const char opname[][4] = {
+#define EXPRTOK_NAME
+#include "exprtok.h"
};
+static const uint8_t oplen[] = {
+#define EXPRTOK_LEN
+#include "exprtok.h"
+};
+
+static const uint8_t opprec[] = {
+#define EXPRTOK_PREC
+#include "exprtok.h"
+};
+
typedef struct expr_state {
/* expression being evaluated */
const char *expression;
@@ -227,7 +150,7 @@
exprtoken(es);
if (es->tok == END) {
es->tok = LIT;
- es->val = tempvar();
+ es->val = tempvar("");
}
v = intvar(es, evalexpr(es, MAX_PREC));
@@ -272,35 +195,35 @@
s = tbuf;
break;
default:
- s = opinfo[(int)es->tok].name;
+ s = opname[(int)es->tok];
}
- warningf(true, "%s: %s '%s'", es->expression,
- "unexpected", s);
+ warningf(true, Tf_sD_s_qs, es->expression,
+ Tunexpected, s);
break;
case ET_BADLIT:
- warningf(true, "%s: %s '%s'", es->expression,
- "bad number", str);
+ warningf(true, Tf_sD_s_qs, es->expression,
+ Tbadnum, str);
break;
case ET_RECURSIVE:
- warningf(true, "%s: %s '%s'", es->expression,
+ warningf(true, Tf_sD_s_qs, es->expression,
"expression recurses on parameter", str);
break;
case ET_LVALUE:
- warningf(true, "%s: %s %s",
+ warningf(true, Tf_sD_s_s,
es->expression, str, "requires lvalue");
break;
case ET_RDONLY:
- warningf(true, "%s: %s %s",
+ warningf(true, Tf_sD_s_s,
es->expression, str, "applied to read-only variable");
break;
default: /* keep gcc happy */
case ET_STR:
- warningf(true, "%s: %s", es->expression, str);
+ warningf(true, Tf_sD_s, es->expression, str);
break;
}
unwind(LAEXPR);
@@ -402,8 +325,16 @@
vl = evalexpr(es, prec - 1);
while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
- opinfo[(int)op].prec == prec) {
- exprtoken(es);
+ opprec[(int)op] == prec) {
+ switch ((int)op) {
+ case O_TERN:
+ case O_LAND:
+ case O_LOR:
+ break;
+ default:
+ exprtoken(es);
+ }
+
vasn = vl;
if (op != O_ASN)
/* vl may not have a value yet */
@@ -417,14 +348,15 @@
if (!ev)
es->noassign++;
+ exprtoken(es);
vl = evalexpr(es, MAX_PREC);
if (!ev)
es->noassign--;
if (es->tok != CTERN)
evalerr(es, ET_STR, "missing :");
- exprtoken(es);
if (ev)
es->noassign++;
+ exprtoken(es);
vr = evalexpr(es, P_TERN);
if (ev)
es->noassign--;
@@ -579,6 +511,7 @@
case O_LAND:
if (!t1)
es->noassign++;
+ exprtoken(es);
vr = intvar(es, evalexpr(es, prec - 1));
res = t1 && vr->val.u;
if (!t1)
@@ -587,6 +520,7 @@
case O_LOR:
if (t1)
es->noassign++;
+ exprtoken(es);
vr = intvar(es, evalexpr(es, prec - 1));
res = t1 || vr->val.u;
if (t1)
@@ -624,9 +558,11 @@
/* skip whitespace */
skip_spaces:
- while ((c = *cp), ksh_isspace(c))
- ++cp;
- if (es->tokp == es->expression && c == '#') {
+ --cp;
+ do {
+ c = ord(*++cp);
+ } while (ctype(c, C_SPACE));
+ if (es->tokp == es->expression && (unsigned int)c == ORD('#')) {
/* expression begins with # */
/* switch to unsigned */
es->natural = true;
@@ -637,10 +573,11 @@
if (c == '\0')
es->tok = END;
- else if (ksh_isalphx(c)) {
- for (; ksh_isalnux(c); c = *cp)
- cp++;
- if (c == '[') {
+ else if (ctype(c, C_ALPHX)) {
+ do {
+ c = ord(*++cp);
+ } while (ctype(c, C_ALNUX));
+ if ((unsigned int)c == ORD('[')) {
size_t len;
len = array_ref_len(cp);
@@ -649,7 +586,7 @@
cp += len;
}
if (es->noassign) {
- es->val = tempvar();
+ es->val = tempvar("");
es->val->flag |= EXPRLVALUE;
} else {
strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
@@ -659,12 +596,16 @@
es->tok = VAR;
} else if (c == '1' && cp[1] == '#') {
cp += 2;
- cp += utf_ptradj(cp);
+ if (*cp)
+ cp += utf_ptradj(cp);
strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
goto process_tvar;
#ifndef MKSH_SMALL
} else if (c == '\'') {
- ++cp;
+ if (*++cp == '\0') {
+ es->tok = END;
+ evalerr(es, ET_UNEXPECTED, NULL);
+ }
cp += utf_ptradj(cp);
if (*cp++ != '\'')
evalerr(es, ET_STR,
@@ -678,12 +619,12 @@
tvar[c] = '\0';
goto process_tvar;
#endif
- } else if (ksh_isdigit(c)) {
- while (c != '_' && (ksh_isalnux(c) || c == '#'))
- c = *cp++;
+ } else if (ctype(c, C_DIGIT)) {
+ while (ctype(c, C_ALNUM | C_HASH))
+ c = ord(*cp++);
strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP);
process_tvar:
- es->val = tempvar();
+ es->val = tempvar("");
es->val->flag &= ~INTEGER;
es->val->type = 0;
es->val->val.s = tvar;
@@ -694,11 +635,11 @@
} else {
int i, n0;
- for (i = 0; (n0 = opinfo[i].name[0]); i++)
- if (c == n0 && strncmp(cp, opinfo[i].name,
- (size_t)opinfo[i].len) == 0) {
+ for (i = 0; (n0 = ord(opname[i][0])); i++)
+ if (c == n0 && strncmp(cp, opname[i],
+ (size_t)oplen[i]) == 0) {
es->tok = (enum token)i;
- cp += opinfo[i].len;
+ cp += oplen[i];
break;
}
if (!n0)
@@ -712,23 +653,25 @@
{
if (es->tok == END || !vasn ||
(vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
- evalerr(es, ET_LVALUE, opinfo[(int)op].name);
+ evalerr(es, ET_LVALUE, opname[(int)op]);
else if (vasn->flag & RDONLY)
- evalerr(es, ET_RDONLY, opinfo[(int)op].name);
+ evalerr(es, ET_RDONLY, opname[(int)op]);
}
struct tbl *
-tempvar(void)
+tempvar(const char *vname)
{
struct tbl *vp;
+ size_t vsize;
- vp = alloc(sizeof(struct tbl), ATEMP);
+ vsize = strlen(vname) + 1;
+ vp = alloc(offsetof(struct tbl, name[0]) + vsize, ATEMP);
+ memcpy(vp->name, vname, vsize);
vp->flag = ISSET|INTEGER;
vp->type = 0;
vp->areap = ATEMP;
vp->ua.hval = 0;
vp->val.i = 0;
- vp->name[0] = '\0';
return (vp);
}
@@ -743,7 +686,7 @@
(vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
return (vp);
- vq = tempvar();
+ vq = tempvar("");
if (setint_v(vq, vp, es->arith) == NULL) {
if (vp->flag & EXPRINEVAL)
evalerr(es, ET_RECURSIVE, vp->name);
@@ -803,15 +746,26 @@
}
const char *
-utf_skipcols(const char *p, int cols)
+utf_skipcols(const char *p, int cols, int *colp)
{
int c = 0;
+ const char *q;
while (c < cols) {
- if (!*p)
- return (p + cols - c);
+ if (!*p) {
+ /* end of input; special handling for edit.c */
+ if (!colp)
+ return (p + cols - c);
+ *colp = c;
+ return (p);
+ }
c += utf_widthadj(p, &p);
}
+ if (UTFMODE)
+ while (utf_widthadj(p, &q) == 0)
+ p = q;
+ if (colp)
+ *colp = c;
return (p);
}
@@ -820,8 +774,7 @@
{
register size_t n;
- if (!UTFMODE ||
- *(const unsigned char *)(src) < 0xC2 ||
+ if (!UTFMODE || rtt2asc(*src) < 0xC2 ||
(n = utf_mbtowc(NULL, src)) == (size_t)-1)
n = 1;
return (n);
@@ -839,7 +792,7 @@
const unsigned char *s = (const unsigned char *)src;
unsigned int c, wc;
- if ((wc = *s++) < 0x80) {
+ if ((wc = ord(rtt2asc(*s++))) < 0x80) {
out:
if (dst != NULL)
*dst = wc;
@@ -853,7 +806,7 @@
if (wc < 0xE0) {
wc = (wc & 0x1F) << 6;
- if (((c = *s++) & 0xC0) != 0x80)
+ if (((c = ord(rtt2asc(*s++))) & 0xC0) != 0x80)
goto ilseq;
wc |= c & 0x3F;
goto out;
@@ -861,11 +814,11 @@
wc = (wc & 0x0F) << 12;
- if (((c = *s++) & 0xC0) != 0x80)
+ if (((c = ord(rtt2asc(*s++))) & 0xC0) != 0x80)
goto ilseq;
wc |= (c & 0x3F) << 6;
- if (((c = *s++) & 0xC0) != 0x80)
+ if (((c = ord(rtt2asc(*s++))) & 0xC0) != 0x80)
goto ilseq;
wc |= c & 0x3F;
@@ -882,18 +835,18 @@
unsigned char *d;
if (wc < 0x80) {
- *dst = wc;
+ *dst = asc2rtt(wc);
return (1);
}
d = (unsigned char *)dst;
if (wc < 0x0800)
- *d++ = (wc >> 6) | 0xC0;
+ *d++ = asc2rtt((wc >> 6) | 0xC0);
else {
- *d++ = ((wc = wc > 0xFFFD ? 0xFFFD : wc) >> 12) | 0xE0;
- *d++ = ((wc >> 6) & 0x3F) | 0x80;
+ *d++ = asc2rtt(((wc = wc > 0xFFFD ? 0xFFFD : wc) >> 12) | 0xE0);
+ *d++ = asc2rtt(((wc >> 6) & 0x3F) | 0x80);
}
- *d++ = (wc & 0x3F) | 0x80;
+ *d++ = asc2rtt((wc & 0x3F) | 0x80);
return ((char *)d - dst);
}
@@ -905,6 +858,9 @@
int
ksh_access(const char *fn, int mode)
{
+#ifdef __OS2__
+ return (access_ex(access, fn, mode));
+#else
int rv;
struct stat sb;
@@ -914,9 +870,11 @@
rv = -1;
return (rv);
+#endif
}
-/* From: X11/xc/programs/xterm/wcwidth.c,v 1.8 2014/06/24 19:53:53 tg Exp $ */
+#ifndef MIRBSD_BOOTFLOPPY
+/* From: X11/xc/programs/xterm/wcwidth.c,v 1.10 */
struct mb_ucsrange {
unsigned short beg;
@@ -927,8 +885,8 @@
unsigned int val) MKSH_A_PURE;
/*
- * Generated by MirOS: contrib/code/Snippets/eawparse,v 1.2 2013/11/30 13:45:17 tg Exp $
- * from the Unicode Character Database, Version 7.0.0
+ * Generated from the Unicode Character Database, Version 10.0.0, by
+ * MirOS: contrib/code/Snippets/eawparse,v 1.12 2017/09/06 16:05:45 tg Exp $
*/
static const struct mb_ucsrange mb_ucs_combining[] = {
@@ -939,16 +897,14 @@
{ 0x05C1, 0x05C2 },
{ 0x05C4, 0x05C5 },
{ 0x05C7, 0x05C7 },
- { 0x0600, 0x0605 },
{ 0x0610, 0x061A },
{ 0x061C, 0x061C },
{ 0x064B, 0x065F },
{ 0x0670, 0x0670 },
- { 0x06D6, 0x06DD },
+ { 0x06D6, 0x06DC },
{ 0x06DF, 0x06E4 },
{ 0x06E7, 0x06E8 },
{ 0x06EA, 0x06ED },
- { 0x070F, 0x070F },
{ 0x0711, 0x0711 },
{ 0x0730, 0x074A },
{ 0x07A6, 0x07B0 },
@@ -958,7 +914,8 @@
{ 0x0825, 0x0827 },
{ 0x0829, 0x082D },
{ 0x0859, 0x085B },
- { 0x08E4, 0x0902 },
+ { 0x08D4, 0x08E1 },
+ { 0x08E3, 0x0902 },
{ 0x093A, 0x093A },
{ 0x093C, 0x093C },
{ 0x0941, 0x0948 },
@@ -984,6 +941,7 @@
{ 0x0AC7, 0x0AC8 },
{ 0x0ACD, 0x0ACD },
{ 0x0AE2, 0x0AE3 },
+ { 0x0AFA, 0x0AFF },
{ 0x0B01, 0x0B01 },
{ 0x0B3C, 0x0B3C },
{ 0x0B3F, 0x0B3F },
@@ -1006,7 +964,8 @@
{ 0x0CC6, 0x0CC6 },
{ 0x0CCC, 0x0CCD },
{ 0x0CE2, 0x0CE3 },
- { 0x0D01, 0x0D01 },
+ { 0x0D00, 0x0D01 },
+ { 0x0D3B, 0x0D3C },
{ 0x0D41, 0x0D44 },
{ 0x0D4D, 0x0D4D },
{ 0x0D62, 0x0D63 },
@@ -1053,6 +1012,7 @@
{ 0x17C9, 0x17D3 },
{ 0x17DD, 0x17DD },
{ 0x180B, 0x180E },
+ { 0x1885, 0x1886 },
{ 0x18A9, 0x18A9 },
{ 0x1920, 0x1922 },
{ 0x1927, 0x1928 },
@@ -1090,8 +1050,8 @@
{ 0x1CED, 0x1CED },
{ 0x1CF4, 0x1CF4 },
{ 0x1CF8, 0x1CF9 },
- { 0x1DC0, 0x1DF5 },
- { 0x1DFC, 0x1DFF },
+ { 0x1DC0, 0x1DF9 },
+ { 0x1DFB, 0x1DFF },
{ 0x200B, 0x200F },
{ 0x202A, 0x202E },
{ 0x2060, 0x2064 },
@@ -1104,13 +1064,13 @@
{ 0x3099, 0x309A },
{ 0xA66F, 0xA672 },
{ 0xA674, 0xA67D },
- { 0xA69F, 0xA69F },
+ { 0xA69E, 0xA69F },
{ 0xA6F0, 0xA6F1 },
{ 0xA802, 0xA802 },
{ 0xA806, 0xA806 },
{ 0xA80B, 0xA80B },
{ 0xA825, 0xA826 },
- { 0xA8C4, 0xA8C4 },
+ { 0xA8C4, 0xA8C5 },
{ 0xA8E0, 0xA8F1 },
{ 0xA926, 0xA92D },
{ 0xA947, 0xA951 },
@@ -1137,7 +1097,7 @@
{ 0xABED, 0xABED },
{ 0xFB1E, 0xFB1E },
{ 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE2D },
+ { 0xFE20, 0xFE2F },
{ 0xFEFF, 0xFEFF },
{ 0xFFF9, 0xFFFB }
};
@@ -1144,15 +1104,50 @@
static const struct mb_ucsrange mb_ucs_fullwidth[] = {
{ 0x1100, 0x115F },
+ { 0x231A, 0x231B },
{ 0x2329, 0x232A },
- { 0x2E80, 0x303E },
- { 0x3040, 0xA4CF },
+ { 0x23E9, 0x23EC },
+ { 0x23F0, 0x23F0 },
+ { 0x23F3, 0x23F3 },
+ { 0x25FD, 0x25FE },
+ { 0x2614, 0x2615 },
+ { 0x2648, 0x2653 },
+ { 0x267F, 0x267F },
+ { 0x2693, 0x2693 },
+ { 0x26A1, 0x26A1 },
+ { 0x26AA, 0x26AB },
+ { 0x26BD, 0x26BE },
+ { 0x26C4, 0x26C5 },
+ { 0x26CE, 0x26CE },
+ { 0x26D4, 0x26D4 },
+ { 0x26EA, 0x26EA },
+ { 0x26F2, 0x26F3 },
+ { 0x26F5, 0x26F5 },
+ { 0x26FA, 0x26FA },
+ { 0x26FD, 0x26FD },
+ { 0x2705, 0x2705 },
+ { 0x270A, 0x270B },
+ { 0x2728, 0x2728 },
+ { 0x274C, 0x274C },
+ { 0x274E, 0x274E },
+ { 0x2753, 0x2755 },
+ { 0x2757, 0x2757 },
+ { 0x2795, 0x2797 },
+ { 0x27B0, 0x27B0 },
+ { 0x27BF, 0x27BF },
+ { 0x2B1B, 0x2B1C },
+ { 0x2B50, 0x2B50 },
+ { 0x2B55, 0x2B55 },
+ { 0x2E80, 0x3029 },
+ { 0x302E, 0x303E },
+ { 0x3040, 0x3098 },
+ { 0x309B, 0xA4CF },
{ 0xA960, 0xA97F },
{ 0xAC00, 0xD7A3 },
{ 0xF900, 0xFAFF },
{ 0xFE10, 0xFE19 },
{ 0xFE30, 0xFE6F },
- { 0xFF00, 0xFF60 },
+ { 0xFF01, 0xFF60 },
{ 0xFFE0, 0xFFE6 }
};
@@ -1195,3 +1190,4 @@
return (2);
return (1);
}
+#endif
Copied: trunk/contrib/mksh/exprtok.h (from rev 12140, vendor/MirOS/mksh/dist/exprtok.h)
===================================================================
--- trunk/contrib/mksh/exprtok.h (rev 0)
+++ trunk/contrib/mksh/exprtok.h 2019-01-18 21:15:37 UTC (rev 12141)
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2016
+ * mirabilos <m at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#if defined(EXPRTOK_DEFNS)
+__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
+/* see range comment below */
+#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+#define FN(name, len, prec, enum) /* nothing */
+#define F1(enum) /* nothing */
+#elif defined(EXPRTOK_ENUM)
+#define F0(name, len, prec, enum) enum = 0,
+#define FN(name, len, prec, enum) enum,
+#define F1(enum) enum,
+#define F2(enum) enum,
+#define F9(enum) enum
+#elif defined(EXPRTOK_NAME)
+#define FN(name, len, prec, enum) name,
+#define F1(enum) ""
+#elif defined(EXPRTOK_LEN)
+#define FN(name, len, prec, enum) len,
+#define F1(enum) 0
+#elif defined(EXPRTOK_PREC)
+#define FN(name, len, prec, enum) prec,
+#define F1(enum) P_PRIMARY
+#endif
+
+#ifndef F0
+#define F0 FN
+#endif
+
+#ifndef F2
+#define F2(enum) /* nothing */
+#define F9(enum) /* nothing */
+#endif
+
+/* tokens must be ordered so the longest are first (e.g. += before +) */
+
+/* some (long) unary operators */
+FN("++", 2, P_PRIMARY, O_PLUSPLUS = 0) /* before + */
+FN("--", 2, P_PRIMARY, O_MINUSMINUS) /* before - */
+/* binary operators */
+FN("==", 2, P_EQUALITY, O_EQ) /* before = */
+FN("!=", 2, P_EQUALITY, O_NE) /* before ! */
+/* assignments are assumed to be in range O_ASN .. O_BORASN */
+FN("=", 1, P_ASSIGN, O_ASN)
+FN("*=", 2, P_ASSIGN, O_TIMESASN)
+FN("/=", 2, P_ASSIGN, O_DIVASN)
+FN("%=", 2, P_ASSIGN, O_MODASN)
+FN("+=", 2, P_ASSIGN, O_PLUSASN)
+FN("-=", 2, P_ASSIGN, O_MINUSASN)
+#ifndef MKSH_LEGACY_MODE
+FN("^<=", 3, P_ASSIGN, O_ROLASN) /* before ^< */
+FN("^>=", 3, P_ASSIGN, O_RORASN) /* before ^> */
+#endif
+FN("<<=", 3, P_ASSIGN, O_LSHIFTASN)
+FN(">>=", 3, P_ASSIGN, O_RSHIFTASN)
+FN("&=", 2, P_ASSIGN, O_BANDASN)
+FN("^=", 2, P_ASSIGN, O_BXORASN)
+FN("|=", 2, P_ASSIGN, O_BORASN)
+/* binary non-assignment operators */
+#ifndef MKSH_LEGACY_MODE
+FN("^<", 2, P_SHIFT, O_ROL) /* before ^ */
+FN("^>", 2, P_SHIFT, O_ROR) /* before ^ */
+#endif
+FN("<<", 2, P_SHIFT, O_LSHIFT)
+FN(">>", 2, P_SHIFT, O_RSHIFT)
+FN("<=", 2, P_RELATION, O_LE)
+FN(">=", 2, P_RELATION, O_GE)
+FN("<", 1, P_RELATION, O_LT)
+FN(">", 1, P_RELATION, O_GT)
+FN("&&", 2, P_LAND, O_LAND)
+FN("||", 2, P_LOR, O_LOR)
+FN("*", 1, P_MULT, O_TIMES)
+FN("/", 1, P_MULT, O_DIV)
+FN("%", 1, P_MULT, O_MOD)
+FN("+", 1, P_ADD, O_PLUS)
+FN("-", 1, P_ADD, O_MINUS)
+FN("&", 1, P_BAND, O_BAND)
+FN("^", 1, P_BXOR, O_BXOR)
+FN("|", 1, P_BOR, O_BOR)
+FN("?", 1, P_TERN, O_TERN)
+FN(",", 1, P_COMMA, O_COMMA)
+/* things after this aren't used as binary operators */
+/* unary that are not also binaries */
+FN("~", 1, P_PRIMARY, O_BNOT)
+FN("!", 1, P_PRIMARY, O_LNOT)
+/* misc */
+FN("(", 1, P_PRIMARY, OPEN_PAREN)
+FN(")", 1, P_PRIMARY, CLOSE_PAREN)
+FN(":", 1, P_PRIMARY, CTERN)
+/* things that don't appear in the opinfo[] table */
+F1(VAR) /*XXX should be F2 */
+F2(LIT)
+F2(END)
+F9(BAD)
+
+#undef FN
+#undef F0
+#undef F1
+#undef F2
+#undef F9
+#undef EXPRTOK_DEFNS
+#undef EXPRTOK_ENUM
+#undef EXPRTOK_NAME
+#undef EXPRTOK_LEN
+#undef EXPRTOK_PREC
Modified: trunk/contrib/mksh/funcs.c
===================================================================
--- trunk/contrib/mksh/funcs.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/funcs.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,12 +1,12 @@
-/* $OpenBSD: c_ksh.c,v 1.34 2013/12/17 16:37:05 deraadt Exp $ */
-/* $OpenBSD: c_sh.c,v 1.45 2014/08/27 08:26:04 jmc Exp $ */
+/* $OpenBSD: c_ksh.c,v 1.37 2015/09/10 22:48:58 nicm Exp $ */
+/* $OpenBSD: c_sh.c,v 1.46 2015/07/20 20:46:24 guenther Exp $ */
/* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */
/* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */
/*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- * 2010, 2011, 2012, 2013, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -38,7 +38,7 @@
#endif
#endif
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.259.2.2 2015/01/25 15:35:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.353 2018/01/14 01:26:49 tg Exp $");
#if HAVE_KILLPG
/*
@@ -64,6 +64,8 @@
static int c_suspend(const char **);
#endif
+static int do_whence(const char **, int, bool, bool);
+
/* getn() that prints error */
static int
bi_getn(const char *as, int *ai)
@@ -71,7 +73,7 @@
int rv;
if (!(rv = getn(as, ai)))
- bi_errorf("%s: %s", as, "bad number");
+ bi_errorf(Tf_sD_s, Tbadnum, as);
return (rv);
}
@@ -90,60 +92,63 @@
/*
* A leading = means assignments before command are kept.
* A leading * means a POSIX special builtin.
+ * A leading ^ means declaration utility, - forwarder.
*/
const struct builtin mkshbuiltins[] = {
- {"*=.", c_dot},
+ {Tsgdot, c_dot},
{"*=:", c_true},
- {"[", c_test},
+ {Tbracket, c_test},
/* no =: AT&T manual wrong */
{Talias, c_alias},
- {"*=break", c_brkcont},
- {Tgbuiltin, c_builtin},
- {"cat", c_cat},
- {"cd", c_cd},
+ {Tsgbreak, c_brkcont},
+ {T__builtin, c_builtin},
+ {Tbuiltin, c_builtin},
+ {Tbcat, c_cat},
+ {Tcd, c_cd},
/* dash compatibility hack */
{"chdir", c_cd},
- {"command", c_command},
- {"*=continue", c_brkcont},
+ {T_command, c_command},
+ {Tsgcontinue, c_brkcont},
{"echo", c_print},
{"*=eval", c_eval},
{"*=exec", c_exec},
{"*=exit", c_exitreturn},
- {Tsgexport, c_typeset},
- {"false", c_false},
+ {Tdsgexport, c_typeset},
+ {Tfalse, c_false},
{"fc", c_fc},
- {"getopts", c_getopts},
- {"=global", c_typeset},
- {"jobs", c_jobs},
+ {Tgetopts, c_getopts},
+ /* deprecated, replaced by typeset -g */
+ {"^=global", c_typeset},
+ {Tjobs, c_jobs},
{"kill", c_kill},
{"let", c_let},
- {"let]", c_let},
{"print", c_print},
{"pwd", c_pwd},
- {"read", c_read},
- {Tsgreadonly, c_typeset},
- {"realpath", c_realpath},
- {"rename", c_rename},
+ {Tread, c_read},
+ {Tdsgreadonly, c_typeset},
+ {"!realpath", c_realpath},
+ {"~rename", c_rename},
{"*=return", c_exitreturn},
{Tsgset, c_set},
{"*=shift", c_shift},
+ {Tgsource, c_dot},
#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
- {"suspend", c_suspend},
+ {Tsuspend, c_suspend},
#endif
{"test", c_test},
{"*=times", c_times},
{"*=trap", c_trap},
- {"true", c_true},
- {T_typeset, c_typeset},
+ {Ttrue, c_true},
+ {Tdgtypeset, c_typeset},
{"ulimit", c_ulimit},
{"umask", c_umask},
{Tunalias, c_unalias},
- {Tsgunset, c_unset},
- {"=wait", c_wait},
+ {"*=unset", c_unset},
+ {"wait", c_wait},
{"whence", c_whence},
#ifndef MKSH_UNEMPLOYED
- {"bg", c_fgbg},
- {"fg", c_fgbg},
+ {Tbg, c_fgbg},
+ {Tfg, c_fgbg},
#endif
#ifndef MKSH_NO_CMDLINE_EDITING
{"bind", c_bind},
@@ -152,7 +157,7 @@
{"mknod", c_mknod},
#endif
#ifdef MKSH_PRINTF_BUILTIN
- {"printf", c_printf},
+ {"~printf", c_printf},
#endif
#if HAVE_SELECT
{"sleep", c_sleep},
@@ -161,6 +166,9 @@
/* alias to "true" for historical reasons */
{"domainname", c_true},
#endif
+#ifdef __OS2__
+ {Textproc, c_true},
+#endif
{NULL, (int (*)(const char **))NULL}
};
@@ -181,8 +189,8 @@
{"-f", TO_FILREG },
{"-G", TO_FILGID },
{"-g", TO_FILSETG },
+ {"-H", TO_FILCDF },
{"-h", TO_FILSYM },
- {"-H", TO_FILCDF },
{"-k", TO_FILSTCK },
{"-L", TO_FILSYM },
{"-n", TO_STNZE },
@@ -190,10 +198,11 @@
{"-o", TO_OPTION },
{"-p", TO_FILFIFO },
{"-r", TO_FILRD },
+ {"-S", TO_FILSOCK },
{"-s", TO_FILGZ },
- {"-S", TO_FILSOCK },
{"-t", TO_FILTT },
{"-u", TO_FILSETU },
+ {"-v", TO_ISSET },
{"-w", TO_FILWR },
{"-x", TO_FILEX },
{"-z", TO_STZER },
@@ -224,7 +233,7 @@
static Test_op ptest_isa(Test_env *, Test_meta);
static const char *ptest_getopnd(Test_env *, Test_op, bool);
static void ptest_error(Test_env *, int, const char *);
-static char *kill_fmt_entry(char *, size_t, unsigned int, const void *);
+static void kill_fmt_entry(char *, size_t, unsigned int, const void *);
static void p_time(struct shf *, bool, long, int, int,
const char *, const char *);
@@ -249,7 +258,7 @@
wp += builtin_opt.optind;
if (wp[0]) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
@@ -258,11 +267,11 @@
if (p && access(p, R_OK) < 0)
p = NULL;
if (!p && !(p = allocd = ksh_get_wd())) {
- bi_errorf("%s: %s", "can't determine current directory",
+ bi_errorf(Tf_sD_s, "can't determine current directory",
cstrerror(errno));
return (1);
}
- shprintf("%s\n", p);
+ shprintf(Tf_sN, p);
afree(allocd, ATEMP);
return (0);
}
@@ -274,113 +283,148 @@
int
c_print(const char **wp)
{
-#define PO_NL BIT(0) /* print newline */
-#define PO_EXPAND BIT(1) /* expand backslash sequences */
-#define PO_PMINUSMINUS BIT(2) /* print a -- argument */
-#define PO_HIST BIT(3) /* print to history instead of stdout */
-#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
- int fd = 1, c;
- int flags = PO_EXPAND | PO_NL;
- const char *s, *emsg;
+ int c;
+ const char *s;
+ char *xp;
XString xs;
- char *xp;
+ struct {
+ /* storage for columnisation */
+ XPtrV words;
+ /* temporary storage for a wide character */
+ mksh_ari_t wc;
+ /* output file descriptor (if any) */
+ int fd;
+ /* temporary storage for a multibyte character */
+ char ts[4];
+ /* output word separator */
+ char ws;
+ /* output line separator */
+ char ls;
+ /* output a trailing line separator? */
+ bool nl;
+ /* expand backslash sequences? */
+ bool exp;
+ /* columnise output? */
+ bool col;
+ /* print to history instead of file descriptor / stdout? */
+ bool hist;
+ /* print words as wide characters? */
+ bool chars;
+ /* writing to a coprocess (SIGPIPE blocked)? */
+ bool coproc;
+ bool copipe;
+ } po;
+ memset(&po, 0, sizeof(po));
+ po.fd = 1;
+ po.ws = ' ';
+ po.ls = '\n';
+ po.nl = true;
+
if (wp[0][0] == 'e') {
- /* echo builtin */
- wp++;
-#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
- if (Flag(FSH)) {
- /*
- * MidnightBSD /bin/sh needs a BSD echo, that is,
- * one that supports -e but does not enable it by
- * default
- */
- flags = PO_NL;
- }
-#endif
+ /* "echo" builtin */
if (Flag(FPOSIX) ||
#ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
Flag(FSH) ||
#endif
Flag(FAS_BUILTIN)) {
- /* Debian Policy 10.4 compliant "echo" builtin */
+ /* BSD "echo" cmd, Debian Policy 10.4 compliant */
+ ++wp;
+ bsd_echo:
if (*wp && !strcmp(*wp, "-n")) {
- /* we recognise "-n" only as the first arg */
- flags = 0;
- wp++;
- } else
- /* otherwise, we print everything as-is */
- flags = PO_NL;
+ po.nl = false;
+ ++wp;
+ }
+ po.exp = false;
} else {
- int nflags = flags;
+ bool new_exp, new_nl = true;
- /**
- * a compromise between sysV and BSD echo commands:
- * escape sequences are enabled by default, and -n,
- * -e and -E are recognised if they appear in argu-
- * ments with no illegal options (ie, echo -nq will
- * print -nq).
- * Different from sysV echo since options are reco-
- * gnised, different from BSD echo since escape se-
- * quences are enabled by default.
+ /*-
+ * compromise between various historic echos: only
+ * recognise -Een if they appear in arguments with
+ * no illegal options; e.g. echo -nq outputs '-nq'
*/
+#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
+ /* MidnightBSD /bin/sh needs -e supported but off */
+ if (Flag(FSH))
+ new_exp = false;
+ else
+#endif
+ /* otherwise compromise on -e enabled by default */
+ new_exp = true;
+ goto print_tradparse_beg;
- while ((s = *wp) && *s == '-' && s[1]) {
- while (*++s)
- if (*s == 'n')
- nflags &= ~PO_NL;
- else if (*s == 'e')
- nflags |= PO_EXPAND;
- else if (*s == 'E')
- nflags &= ~PO_EXPAND;
- else
- /*
- * bad option: don't use
- * nflags, print argument
- */
- break;
-
- if (*s)
- break;
- wp++;
- flags = nflags;
+ print_tradparse_arg:
+ if ((s = *wp) && *s++ == '-' && *s) {
+ print_tradparse_ch:
+ switch ((c = *s++)) {
+ case 'E':
+ new_exp = false;
+ goto print_tradparse_ch;
+ case 'e':
+ new_exp = true;
+ goto print_tradparse_ch;
+ case 'n':
+ new_nl = false;
+ goto print_tradparse_ch;
+ case '\0':
+ print_tradparse_beg:
+ po.exp = new_exp;
+ po.nl = new_nl;
+ ++wp;
+ goto print_tradparse_arg;
+ }
}
}
} else {
- int optc;
- const char *opts = "Rnprsu,";
+ /* "print" builtin */
+ const char *opts = "AcelNnpRrsu,";
+ const char *emsg;
- while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
- switch (optc) {
- case 'R':
- /* fake BSD echo command */
- flags |= PO_PMINUSMINUS;
- flags &= ~PO_EXPAND;
- opts = "ne";
+ po.exp = true;
+
+ while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
+ switch (c) {
+ case 'A':
+ po.chars = true;
break;
+ case 'c':
+ po.col = true;
+ break;
case 'e':
- flags |= PO_EXPAND;
+ po.exp = true;
break;
+ case 'l':
+ po.ws = '\n';
+ break;
+ case 'N':
+ po.ws = '\0';
+ po.ls = '\0';
+ break;
case 'n':
- flags &= ~PO_NL;
+ po.nl = false;
break;
case 'p':
- if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
- bi_errorf("%s: %s", "-p", emsg);
+ if ((po.fd = coproc_getfd(W_OK, &emsg)) < 0) {
+ bi_errorf(Tf_coproc, emsg);
return (1);
}
break;
+ case 'R':
+ /* fake BSD echo but don't reset other flags */
+ wp += builtin_opt.optind;
+ goto bsd_echo;
case 'r':
- flags &= ~PO_EXPAND;
+ po.exp = false;
break;
case 's':
- flags |= PO_HIST;
+ po.hist = true;
break;
case 'u':
if (!*(s = builtin_opt.optarg))
- fd = 0;
- else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
- bi_errorf("%s: %s: %s", "-u", s, emsg);
+ po.fd = 0;
+ else if ((po.fd = check_fd(s, W_OK, &emsg)) < 0) {
+ bi_errorf("-u%s: %s", s, emsg);
return (1);
}
break;
@@ -389,22 +433,48 @@
}
if (!(builtin_opt.info & GI_MINUSMINUS)) {
- /* treat a lone - like -- */
+ /* treat a lone "-" like "--" */
if (wp[builtin_opt.optind] &&
ksh_isdash(wp[builtin_opt.optind]))
builtin_opt.optind++;
- } else if (flags & PO_PMINUSMINUS)
- builtin_opt.optind--;
+ }
wp += builtin_opt.optind;
}
+ if (po.col) {
+ if (*wp == NULL)
+ return (0);
+
+ XPinit(po.words, 16);
+ }
+
Xinit(xs, xp, 128, ATEMP);
- while (*wp != NULL) {
- s = *wp;
+ if (*wp == NULL)
+ goto print_no_arg;
+ print_read_arg:
+ if (po.chars) {
+ while (*wp != NULL) {
+ s = *wp++;
+ if (*s == '\0')
+ break;
+ if (!evaluate(s, &po.wc, KSH_RETURN_ERROR, true))
+ return (1);
+ Xcheck(xs, xp);
+ if (UTFMODE) {
+ po.ts[utf_wctomb(po.ts, po.wc)] = 0;
+ c = 0;
+ do {
+ Xput(xs, xp, po.ts[c]);
+ } while (po.ts[++c]);
+ } else
+ Xput(xs, xp, po.wc & 0xFF);
+ }
+ } else {
+ s = *wp++;
while ((c = *s++) != '\0') {
Xcheck(xs, xp);
- if ((flags & PO_EXPAND) && c == '\\') {
+ if (po.exp && c == '\\') {
s_ptr = s;
c = unbksl(false, s_get, s_put);
s = s_ptr;
@@ -412,11 +482,11 @@
/* rejected by generic function */
switch ((c = *s++)) {
case 'c':
- flags &= ~PO_NL;
+ po.nl = false;
/* AT&T brain damage */
continue;
case '\0':
- s--;
+ --s;
c = '\\';
break;
default:
@@ -424,29 +494,55 @@
}
} else if ((unsigned int)c > 0xFF) {
/* generic function returned Unicode */
- char ts[4];
-
- ts[utf_wctomb(ts, c - 0x100)] = 0;
- for (c = 0; ts[c]; ++c)
- Xput(xs, xp, ts[c]);
+ po.ts[utf_wctomb(po.ts, c - 0x100)] = 0;
+ c = 0;
+ do {
+ Xput(xs, xp, po.ts[c]);
+ } while (po.ts[++c]);
continue;
}
}
Xput(xs, xp, c);
}
- if (*++wp != NULL)
- Xput(xs, xp, ' ');
}
- if (flags & PO_NL)
- Xput(xs, xp, '\n');
+ if (po.col) {
+ Xput(xs, xp, '\0');
+ XPput(po.words, Xclose(xs, xp));
+ Xinit(xs, xp, 128, ATEMP);
+ }
+ if (*wp != NULL) {
+ if (!po.col)
+ Xput(xs, xp, po.ws);
+ goto print_read_arg;
+ }
+ if (po.col) {
+ size_t w = XPsize(po.words);
+ struct columnise_opts co;
- if (flags & PO_HIST) {
+ XPput(po.words, NULL);
+ co.shf = shf_sopen(NULL, 128, SHF_WR | SHF_DYNAMIC, NULL);
+ co.linesep = po.ls;
+ co.prefcol = co.do_last = false;
+ pr_list(&co, (char **)XPptrv(po.words));
+ while (w--)
+ afree(XPptrv(po.words)[w], ATEMP);
+ XPfree(po.words);
+ w = co.shf->wp - co.shf->buf;
+ XcheckN(xs, xp, w);
+ memcpy(xp, co.shf->buf, w);
+ xp += w;
+ shf_sclose(co.shf);
+ }
+ print_no_arg:
+ if (po.nl)
+ Xput(xs, xp, po.ls);
+
+ c = 0;
+ if (po.hist) {
Xput(xs, xp, '\0');
- histsave(&source->line, Xstring(xs, xp), true, false);
- Xfree(xs, xp);
+ histsave(&source->line, Xstring(xs, xp), HIST_STORE, false);
} else {
- int len = Xlength(xs, xp);
- int opipe = 0;
+ size_t len = Xlength(xs, xp);
/*
* Ensure we aren't killed by a SIGPIPE while writing to
@@ -454,37 +550,45 @@
* to just check that the co-process is alive which is
* not enough).
*/
- if (coproc.write >= 0 && coproc.write == fd) {
- flags |= PO_COPROC;
- opipe = block_pipe();
- }
- for (s = Xstring(xs, xp); len > 0; ) {
- if ((c = write(fd, s, len)) < 0) {
- if (flags & PO_COPROC)
- restore_pipe(opipe);
+ if (coproc.write >= 0 && coproc.write == po.fd) {
+ po.coproc = true;
+ po.copipe = block_pipe();
+ } else
+ po.coproc = po.copipe = false;
+
+ s = Xstring(xs, xp);
+ while (len > 0) {
+ ssize_t nwritten;
+
+ if ((nwritten = write(po.fd, s, len)) < 0) {
if (errno == EINTR) {
- /* allow user to ^C out */
+ if (po.copipe)
+ restore_pipe();
+ /* give the user a chance to ^C out */
intrcheck();
- if (flags & PO_COPROC)
- opipe = block_pipe();
+ /* interrupted, try again */
+ if (po.coproc)
+ po.copipe = block_pipe();
continue;
}
- return (1);
+ c = 1;
+ break;
}
- s += c;
- len -= c;
+ s += nwritten;
+ len -= nwritten;
}
- if (flags & PO_COPROC)
- restore_pipe(opipe);
+ if (po.copipe)
+ restore_pipe();
}
+ Xfree(xs, xp);
- return (0);
+ return (c);
}
static int
s_get(void)
{
- return (*s_ptr++);
+ return (ord(*s_ptr++));
}
static void
@@ -496,14 +600,10 @@
int
c_whence(const char **wp)
{
- struct tbl *tp;
- const char *id;
- bool pflag = false, vflag = false, Vflag = false;
- int rv = 0, optc, fcflags;
- bool iam_whence = wp[0][0] == 'w';
- const char *opts = iam_whence ? "pv" : "pvV";
+ int optc;
+ bool pflag = false, vflag = false;
- while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
+ while ((optc = ksh_getopt(wp, &builtin_opt, Tpv)) != -1)
switch (optc) {
case 'p':
pflag = true;
@@ -511,80 +611,81 @@
case 'v':
vflag = true;
break;
+ case '?':
+ return (1);
+ }
+ wp += builtin_opt.optind;
+
+ return (do_whence(wp, pflag ? FC_PATH :
+ FC_BI | FC_FUNC | FC_PATH | FC_WHENCE, vflag, false));
+}
+
+/* note: command without -vV is dealt with in comexec() */
+int
+c_command(const char **wp)
+{
+ int optc, fcflags = FC_BI | FC_FUNC | FC_PATH | FC_WHENCE;
+ bool vflag = false;
+
+ while ((optc = ksh_getopt(wp, &builtin_opt, TpVv)) != -1)
+ switch (optc) {
+ case 'p':
+ fcflags |= FC_DEFPATH;
+ break;
case 'V':
- Vflag = true;
+ vflag = true;
break;
+ case 'v':
+ vflag = false;
+ break;
case '?':
return (1);
}
wp += builtin_opt.optind;
- fcflags = FC_BI | FC_PATH | FC_FUNC;
- if (!iam_whence) {
- /* Note that -p on its own is deal with in comexec() */
- if (pflag)
- fcflags |= FC_DEFPATH;
- /*
- * Convert command options to whence options - note that
- * command -pV uses a different path search than whence -v
- * or whence -pv. This should be considered a feature.
- */
- vflag = Vflag;
- }
- if (pflag)
- fcflags &= ~(FC_BI | FC_FUNC);
+ return (do_whence(wp, fcflags, vflag, true));
+}
+static int
+do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
+{
+ uint32_t h;
+ int rv = 0;
+ struct tbl *tp;
+ const char *id;
+
while ((vflag || rv == 0) && (id = *wp++) != NULL) {
- uint32_t h = 0;
+ h = hash(id);
+ tp = NULL;
- tp = NULL;
- if ((iam_whence || vflag) && !pflag)
- tp = ktsearch(&keywords, id, h = hash(id));
- if (!tp && !pflag) {
- tp = ktsearch(&aliases, id, h ? h : hash(id));
+ if (fcflags & FC_WHENCE)
+ tp = ktsearch(&keywords, id, h);
+ if (!tp && (fcflags & FC_WHENCE)) {
+ tp = ktsearch(&aliases, id, h);
if (tp && !(tp->flag & ISSET))
tp = NULL;
}
if (!tp)
tp = findcom(id, fcflags);
- if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
- tp->type != CTALIAS))
+
+ switch (tp->type) {
+ case CSHELL:
+ case CFUNC:
+ case CKEYWD:
shf_puts(id, shl_stdout);
- if (vflag) {
- switch (tp->type) {
- case CKEYWD:
- case CALIAS:
- case CFUNC:
- case CSHELL:
- shf_puts(" is a", shl_stdout);
- break;
- }
- switch (tp->type) {
- case CKEYWD:
- case CSHELL:
- case CTALIAS:
- case CEXEC:
- shf_putc(' ', shl_stdout);
- break;
- }
+ break;
}
switch (tp->type) {
- case CKEYWD:
+ case CSHELL:
if (vflag)
- shf_puts("reserved word", shl_stdout);
+ shprintf(" is a %sshell %s",
+ (tp->flag & SPEC_BI) ? "special " : "",
+ Tbuiltin);
break;
- case CALIAS:
- if (vflag)
- shprintf("n %s%s for ",
- (tp->flag & EXPORT) ? "exported " : null,
- Talias);
- if (!iam_whence && !vflag)
- shprintf("%s %s=", Talias, id);
- print_value_quoted(shl_stdout, tp->val.s);
- break;
case CFUNC:
if (vflag) {
+ shf_puts(" is a", shl_stdout);
if (tp->flag & EXPORT)
shf_puts("n exported", shl_stdout);
if (tp->flag & TRACE)
@@ -598,34 +699,42 @@
shf_puts(T_function, shl_stdout);
}
break;
- case CSHELL:
- if (vflag) {
- if (tp->flag & SPEC_BI)
- shf_puts("special ", shl_stdout);
- shprintf("%s %s", "shell", Tbuiltin);
- }
- break;
+ case CEXEC:
case CTALIAS:
- case CEXEC:
if (tp->flag & ISSET) {
if (vflag) {
- shf_puts("is ", shl_stdout);
+ shprintf("%s is ", id);
if (tp->type == CTALIAS)
shprintf("a tracked %s%s for ",
(tp->flag & EXPORT) ?
- "exported " : null,
+ "exported " : "",
Talias);
}
shf_puts(tp->val.s, shl_stdout);
} else {
if (vflag)
- shf_puts("not found", shl_stdout);
+ shprintf(Tnot_found_s, id);
rv = 1;
}
break;
+ case CALIAS:
+ if (vflag) {
+ shprintf("%s is an %s%s for ", id,
+ (tp->flag & EXPORT) ? "exported " : "",
+ Talias);
+ } else if (iscommand)
+ shprintf("%s %s=", Talias, id);
+ print_value_quoted(shl_stdout, tp->val.s);
+ break;
+ case CKEYWD:
+ if (vflag)
+ shf_puts(" is a reserved word", shl_stdout);
+ break;
+#ifndef MKSH_SMALL
default:
- shf_puts(" is *GOK*", shl_stdout);
- break;
+ bi_errorf(Tunexpected_type, id, Tcommand, tp->type);
+ return (1);
+#endif
}
if (vflag || !rv)
shf_putc('\n', shl_stdout);
@@ -633,387 +742,19 @@
return (rv);
}
-/* Deal with command -vV - command -p dealt with in comexec() */
-int
-c_command(const char **wp)
+bool
+valid_alias_name(const char *cp)
{
- /*
- * Let c_whence do the work. Note that c_command() must be
- * a distinct function from c_whence() (tested in comexec()).
- */
- return (c_whence(wp));
-}
-
-/* typeset, global, export, and readonly */
-static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool);
-static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
- bool);
-int
-c_typeset(const char **wp)
-{
- struct tbl *vp, **p;
- uint32_t fset = 0, fclr = 0, flag;
- int thing = 0, field = 0, base = 0, i;
- struct block *l;
- const char *opts;
- const char *fieldstr = NULL, *basestr = NULL;
- bool localv = false, func = false, pflag = false, istset = true;
- enum namerefflag new_refflag = SRF_NOP;
-
- switch (**wp) {
-
- /* export */
- case 'e':
- fset |= EXPORT;
- istset = false;
- break;
-
- /* readonly */
- case 'r':
- fset |= RDONLY;
- istset = false;
- break;
-
- /* set */
- case 's':
- /* called with 'typeset -' */
- break;
-
- /* typeset */
- case 't':
- localv = true;
- break;
- }
-
- /* see comment below regarding possible opions */
- opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
-
- builtin_opt.flags |= GF_PLUSOPT;
- /*
- * AT&T ksh seems to have 0-9 as options which are multiplied
- * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
- * sets right justify in a field of 12). This allows options
- * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
- * does not allow the number to be specified as a separate argument
- * Here, the number must follow the RLZi option, but is optional
- * (see the # kludge in ksh_getopt()).
- */
- while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
- flag = 0;
- switch (i) {
- case 'L':
- flag = LJUST;
- fieldstr = builtin_opt.optarg;
- break;
- case 'R':
- flag = RJUST;
- fieldstr = builtin_opt.optarg;
- break;
- case 'U':
- /*
- * AT&T ksh uses u, but this conflicts with
- * upper/lower case. If this option is changed,
- * need to change the -U below as well
- */
- flag = INT_U;
- break;
- case 'Z':
- flag = ZEROFIL;
- fieldstr = builtin_opt.optarg;
- break;
- case 'a':
- /*
- * this is supposed to set (-a) or unset (+a) the
- * indexed array attribute; it does nothing on an
- * existing regular string or indexed array though
- */
- break;
- case 'f':
- func = true;
- break;
- case 'i':
- flag = INTEGER;
- basestr = builtin_opt.optarg;
- break;
- case 'l':
- flag = LCASEV;
- break;
- case 'n':
- new_refflag = (builtin_opt.info & GI_PLUS) ?
- SRF_DISABLE : SRF_ENABLE;
- break;
- /* export, readonly: POSIX -p flag */
- case 'p':
- /* typeset: show values as well */
- pflag = true;
- if (istset)
- continue;
- break;
- case 'r':
- flag = RDONLY;
- break;
- case 't':
- flag = TRACE;
- break;
- case 'u':
- /* upper case / autoload */
- flag = UCASEV_AL;
- break;
- case 'x':
- flag = EXPORT;
- break;
- case '?':
- return (1);
- }
- if (builtin_opt.info & GI_PLUS) {
- fclr |= flag;
- fset &= ~flag;
- thing = '+';
- } else {
- fset |= flag;
- fclr &= ~flag;
- thing = '-';
- }
- }
-
- if (fieldstr && !bi_getn(fieldstr, &field))
- return (1);
- if (basestr && (!bi_getn(basestr, &base) || base < 1 || base > 36)) {
- bi_errorf("%s: %s", "bad integer base", basestr);
- return (1);
- }
-
- if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
- (wp[builtin_opt.optind][0] == '-' ||
- wp[builtin_opt.optind][0] == '+') &&
- wp[builtin_opt.optind][1] == '\0') {
- thing = wp[builtin_opt.optind][0];
- builtin_opt.optind++;
- }
-
- if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
- new_refflag != SRF_NOP)) {
- bi_errorf("only -t, -u and -x options may be used with -f");
- return (1);
- }
- if (wp[builtin_opt.optind]) {
- /*
- * Take care of exclusions.
- * At this point, flags in fset are cleared in fclr and vice
- * versa. This property should be preserved.
- */
- if (fset & LCASEV)
- /* LCASEV has priority over UCASEV_AL */
- fset &= ~UCASEV_AL;
- if (fset & LJUST)
- /* LJUST has priority over RJUST */
- fset &= ~RJUST;
- if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
- /* -Z implies -ZR */
- fset |= RJUST;
- fclr &= ~RJUST;
- }
- /*
- * Setting these attributes clears the others, unless they
- * are also set in this command
- */
- if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
- INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
- fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
- LCASEV | INTEGER | INT_U | INT_L);
- }
- if (new_refflag != SRF_NOP) {
- fclr &= ~(ARRAY | ASSOC);
- fset &= ~(ARRAY | ASSOC);
- fclr |= EXPORT;
- fset |= ASSOC;
- if (new_refflag == SRF_DISABLE)
- fclr |= ASSOC;
- }
-
- /* set variables and attributes */
- if (wp[builtin_opt.optind] &&
- /* not "typeset -p varname" */
- !(!func && pflag && !(fset | fclr))) {
- int rv = 0;
- struct tbl *f;
-
- if (localv && !func)
- fset |= LOCAL;
- for (i = builtin_opt.optind; wp[i]; i++) {
- if (func) {
- f = findfunc(wp[i], hash(wp[i]),
- tobool(fset & UCASEV_AL));
- if (!f) {
- /* AT&T ksh does ++rv: bogus */
- rv = 1;
- continue;
- }
- if (fset | fclr) {
- f->flag |= fset;
- f->flag &= ~fclr;
- } else {
- fpFUNCTf(shl_stdout, 0,
- tobool(f->flag & FKSH),
- wp[i], f->val.t);
- shf_putc('\n', shl_stdout);
- }
- } else if (!typeset(wp[i], fset, fclr, field, base)) {
- bi_errorf("%s: %s", wp[i], "is not an identifier");
- return (1);
- }
- }
- return (rv);
- }
-
- /* list variables and attributes */
-
- /* no difference at this point.. */
- flag = fset | fclr;
- if (func) {
- for (l = e->loc; l; l = l->next) {
- for (p = ktsort(&l->funs); (vp = *p++); ) {
- if (flag && (vp->flag & flag) == 0)
- continue;
- if (thing == '-')
- fpFUNCTf(shl_stdout, 0,
- tobool(vp->flag & FKSH),
- vp->name, vp->val.t);
- else
- shf_puts(vp->name, shl_stdout);
- shf_putc('\n', shl_stdout);
- }
- }
- } else if (wp[builtin_opt.optind]) {
- for (i = builtin_opt.optind; wp[i]; i++) {
- varsearch(e->loc, &vp, wp[i], hash(wp[i]));
- c_typeset_vardump(vp, flag, thing, pflag, istset);
- }
- } else
- c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
- return (0);
-}
-
-static void
-c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
- bool pflag, bool istset)
-{
- struct tbl **blockvars, *vp;
-
- if (l->next)
- c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
- blockvars = ktsort(&l->vars);
- while ((vp = *blockvars++))
- c_typeset_vardump(vp, flag, thing, pflag, istset);
- /*XXX doesn’t this leak? */
-}
-
-static void
-c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
- bool istset)
-{
- struct tbl *tvp;
- int any_set = 0;
- char *s;
-
- if (!vp)
- return;
-
- /*
- * See if the parameter is set (for arrays, if any
- * element is set).
- */
- for (tvp = vp; tvp; tvp = tvp->u.array)
- if (tvp->flag & ISSET) {
- any_set = 1;
- break;
- }
-
- /*
- * Check attributes - note that all array elements
- * have (should have?) the same attributes, so checking
- * the first is sufficient.
- *
- * Report an unset param only if the user has
- * explicitly given it some attribute (like export);
- * otherwise, after "echo $FOO", we would report FOO...
- */
- if (!any_set && !(vp->flag & USERATTRIB))
- return;
- if (flag && (vp->flag & flag) == 0)
- return;
- if (!(vp->flag & ARRAY))
- /* optimise later conditionals */
- any_set = 0;
- do {
- /*
- * Ignore array elements that aren't set unless there
- * are no set elements, in which case the first is
- * reported on
- */
- if (any_set && !(vp->flag & ISSET))
- continue;
- /* no arguments */
- if (!thing && !flag) {
- if (any_set == 1) {
- shprintf("%s %s %s\n", Tset, "-A", vp->name);
- any_set = 2;
- }
- /*
- * AT&T ksh prints things like export, integer,
- * leftadj, zerofill, etc., but POSIX says must
- * be suitable for re-entry...
- */
- shprintf("%s %s", Ttypeset, "");
- if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
- shprintf("%s ", "-n");
- if ((vp->flag & INTEGER))
- shprintf("%s ", "-i");
- if ((vp->flag & EXPORT))
- shprintf("%s ", "-x");
- if ((vp->flag & RDONLY))
- shprintf("%s ", "-r");
- if ((vp->flag & TRACE))
- shprintf("%s ", "-t");
- if ((vp->flag & LJUST))
- shprintf("-L%d ", vp->u2.field);
- if ((vp->flag & RJUST))
- shprintf("-R%d ", vp->u2.field);
- if ((vp->flag & ZEROFIL))
- shprintf("%s ", "-Z");
- if ((vp->flag & LCASEV))
- shprintf("%s ", "-l");
- if ((vp->flag & UCASEV_AL))
- shprintf("%s ", "-u");
- if ((vp->flag & INT_U))
- shprintf("%s ", "-U");
- } else if (pflag) {
- shprintf("%s %s", istset ? Ttypeset :
- (flag & EXPORT) ? Texport : Treadonly, "");
- }
- if (any_set)
- shprintf("%s[%lu]", vp->name, arrayindex(vp));
+ if (ord(*cp) == ORD('-'))
+ return (false);
+ if (ord(cp[0]) == ORD('[') && ord(cp[1]) == ORD('[') && !cp[2])
+ return (false);
+ while (*cp)
+ if (ctype(*cp, C_ALIAS))
+ ++cp;
else
- shf_puts(vp->name, shl_stdout);
- if ((!thing && !flag && pflag) ||
- (thing == '-' && (vp->flag & ISSET))) {
- s = str_val(vp);
- shf_putc('=', shl_stdout);
- /* AT&T ksh can't have justified integers... */
- if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
- shf_puts(s, shl_stdout);
- else
- print_value_quoted(shl_stdout, s);
- }
- shf_putc('\n', shl_stdout);
-
- /*
- * Only report first 'element' of an array with
- * no set elements.
- */
- if (!any_set)
- return;
- } while ((vp = vp->u.array));
+ return (false);
+ return (true);
}
int
@@ -1021,7 +762,7 @@
{
struct table *t = &aliases;
int rv = 0, prefix = 0;
- bool rflag = false, tflag, Uflag = false, pflag = false;
+ bool rflag = false, tflag, Uflag = false, pflag = false, chkalias;
uint32_t xflag = 0;
int optc;
@@ -1066,12 +807,13 @@
wp += builtin_opt.optind;
if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
- (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
+ ctype(wp[0][0], C_MINUS | C_PLUS) && wp[0][1] == '\0') {
prefix = wp[0][0];
wp++;
}
tflag = t == &taliases;
+ chkalias = t == &aliases;
/* "hash -r" means reset all the tracked aliases.. */
if (rflag) {
@@ -1094,7 +836,7 @@
for (p = ktsort(t); (ap = *p++) != NULL; )
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
if (pflag)
- shprintf("%s ", Talias);
+ shprintf(Tf_s_, Talias);
shf_puts(ap->name, shl_stdout);
if (prefix != '+') {
shf_putc('=', shl_stdout);
@@ -1114,12 +856,17 @@
strndupx(xalias, alias, val++ - alias, ATEMP);
alias = xalias;
}
+ if (chkalias && !valid_alias_name(alias)) {
+ bi_errorf(Tinvname, alias, Talias);
+ afree(xalias, ATEMP);
+ return (1);
+ }
h = hash(alias);
if (val == NULL && !tflag && !xflag) {
ap = ktsearch(t, alias, h);
if (ap != NULL && (ap->flag&ISSET)) {
if (pflag)
- shprintf("%s ", Talias);
+ shprintf(Tf_s_, Talias);
shf_puts(ap->name, shl_stdout);
if (prefix != '+') {
shf_putc('=', shl_stdout);
@@ -1127,8 +874,7 @@
}
shf_putc('\n', shl_stdout);
} else {
- shprintf("%s %s %s\n", alias, Talias,
- "not found");
+ shprintf(Tf_s_s_sN, alias, Talias, Tnot_found);
rv = 1;
}
continue;
@@ -1232,7 +978,7 @@
if (wp[1] == NULL)
/* AT&T ksh does this */
- bi_errorf("no arguments");
+ bi_errorf(Tno_args);
else
for (wp++; *wp; wp++)
if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
@@ -1283,7 +1029,7 @@
int
c_fgbg(const char **wp)
{
- bool bg = strcmp(*wp, "bg") == 0;
+ bool bg = strcmp(*wp, Tbg) == 0;
int rv = 0;
if (!Flag(FMONITOR)) {
@@ -1298,12 +1044,13 @@
rv = j_resume(*wp, bg);
else
rv = j_resume("%%", bg);
- return (bg ? 0 : rv);
+ /* fg returns $? of the job unless POSIX */
+ return ((bg | Flag(FPOSIX)) ? 0 : rv);
}
#endif
/* format a single kill item */
-static char *
+static void
kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
{
const struct kill_info *ki = (const struct kill_info *)arg;
@@ -1313,7 +1060,6 @@
ki->num_width, i,
ki->name_width, sigtraps[i].name,
sigtraps[i].mess);
- return (buf);
}
int
@@ -1325,10 +1071,9 @@
int i, n, rv, sig;
/* assume old style options if -digits or -UPPERCASE */
- if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
- ksh_isupper(p[1]))) {
- if (!(t = gettrap(p + 1, false))) {
- bi_errorf("bad signal '%s'", p + 1);
+ if ((p = wp[1]) && *p == '-' && ctype(p[1], C_DIGIT | C_UPPER)) {
+ if (!(t = gettrap(p + 1, false, false))) {
+ bi_errorf(Tbad_sig_s, p + 1);
return (1);
}
i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
@@ -1341,8 +1086,9 @@
lflag = true;
break;
case 's':
- if (!(t = gettrap(builtin_opt.optarg, true))) {
- bi_errorf("bad signal '%s'",
+ if (!(t = gettrap(builtin_opt.optarg,
+ true, false))) {
+ bi_errorf(Tbad_sig_s,
builtin_opt.optarg);
return (1);
}
@@ -1367,24 +1113,33 @@
for (; wp[i]; i++) {
if (!bi_getn(wp[i], &n))
return (1);
-#if (NSIG < 128)
- if (n > 128 && n < 128 + NSIG)
+#if (ksh_NSIG <= 128)
+ if (n > 128 && n < 128 + ksh_NSIG)
n -= 128;
#endif
- if (n > 0 && n < NSIG)
- shprintf("%s\n", sigtraps[n].name);
+ if (n > 0 && n < ksh_NSIG)
+ shprintf(Tf_sN, sigtraps[n].name);
else
- shprintf("%d\n", n);
+ shprintf(Tf_dN, n);
}
+ } else if (Flag(FPOSIX)) {
+ n = 1;
+ while (n < ksh_NSIG) {
+ shf_puts(sigtraps[n].name, shl_stdout);
+ shf_putc(++n == ksh_NSIG ? '\n' : ' ',
+ shl_stdout);
+ }
} else {
- ssize_t w, mess_cols, mess_octs;
- int j;
- struct kill_info ki;
+ ssize_t w, mess_cols = 0, mess_octs = 0;
+ int j = ksh_NSIG - 1;
+ struct kill_info ki = { 0, 0 };
+ struct columnise_opts co;
- for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
+ do {
ki.num_width++;
- ki.name_width = mess_cols = mess_octs = 0;
- for (j = 0; j < NSIG; j++) {
+ } while ((j /= 10));
+
+ for (j = 1; j < ksh_NSIG; j++) {
w = strlen(sigtraps[j].name);
if (w > ki.name_width)
ki.name_width = w;
@@ -1396,11 +1151,14 @@
mess_cols = w;
}
- print_columns(shl_stdout, (unsigned int)(NSIG - 1),
+ co.shf = shl_stdout;
+ co.linesep = '\n';
+ co.prefcol = co.do_last = true;
+
+ print_columns(&co, (unsigned int)(ksh_NSIG - 1),
kill_fmt_entry, (void *)&ki,
ki.num_width + 1 + ki.name_width + 1 + mess_octs,
- ki.num_width + 1 + ki.name_width + 1 + mess_cols,
- true);
+ ki.num_width + 1 + ki.name_width + 1 + mess_cols);
}
return (0);
}
@@ -1411,12 +1169,12 @@
if (j_kill(p, sig))
rv = 1;
} else if (!getn(p, &n)) {
- bi_errorf("%s: %s", p,
+ bi_errorf(Tf_sD_s, p,
"arguments must be jobs or process IDs");
rv = 1;
} else {
if (mksh_kill(n, sig) < 0) {
- bi_errorf("%s: %s", p, cstrerror(errno));
+ bi_errorf(Tf_sD_s, p, cstrerror(errno));
rv = 1;
}
}
@@ -1428,7 +1186,8 @@
getopts_reset(int val)
{
if (val >= 1) {
- ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
+ ksh_getopt_reset(&user_opt, GF_NONAME |
+ (Flag(FPOSIX) ? 0 : GF_PLUSOPT));
user_opt.optind = user_opt.uoptind = val;
}
}
@@ -1447,22 +1206,22 @@
opts = *wp++;
if (!opts) {
- bi_errorf("missing %s argument", "options");
+ bi_errorf(Tf_sD_s, "options", Tno_args);
return (1);
}
var = *wp++;
if (!var) {
- bi_errorf("missing %s argument", "name");
+ bi_errorf(Tf_sD_s, Tname, Tno_args);
return (1);
}
if (!*var || *skip_varname(var, true)) {
- bi_errorf("%s: %s", var, "is not an identifier");
+ bi_errorf(Tf_sD_s, var, Tnot_ident);
return (1);
}
if (e->loc->next == NULL) {
- internal_warningf("%s: %s", "c_getopts", "no argv");
+ internal_warningf(Tf_sD_s, Tgetopts, Tno_args);
return (1);
}
/* Which arguments are we parsing... */
@@ -1509,7 +1268,7 @@
if (user_opt.optarg == NULL)
unset(voptarg, 1);
else
- /* This can't fail (have cleared readonly/integer) */
+ /* this can't fail (haing cleared readonly/integer) */
setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
rv = 0;
@@ -1607,7 +1366,7 @@
/* nothing to do */
return (0);
} else if (n < 0) {
- bi_errorf("%s: %s", arg, "bad number");
+ bi_errorf(Tf_sD_s, Tbadnum, arg);
return (1);
}
if (l->argc < n) {
@@ -1647,7 +1406,7 @@
old_umask = ~old_umask;
p = buf;
for (i = 0; i < 3; i++) {
- *p++ = "ugo"[i];
+ *p++ = Tugo[i];
*p++ = '=';
for (j = 0; j < 3; j++)
if (old_umask & (1 << (8 - (3*i + j))))
@@ -1655,17 +1414,20 @@
*p++ = ',';
}
p[-1] = '\0';
- shprintf("%s\n", buf);
+ shprintf(Tf_sN, buf);
} else
shprintf("%#3.3o\n", (unsigned int)old_umask);
} else {
mode_t new_umask;
- if (ksh_isdigit(*cp)) {
- for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
- new_umask = new_umask * 8 + (*cp - '0');
+ if (ctype(*cp, C_DIGIT)) {
+ new_umask = 0;
+ while (ctype(*cp, C_OCTAL)) {
+ new_umask = new_umask * 8 + ksh_numdig(*cp);
+ ++cp;
+ }
if (*cp) {
- bi_errorf("bad number");
+ bi_errorf(Tbadnum);
return (1);
}
} else {
@@ -1680,7 +1442,7 @@
new_umask = old_umask;
positions = 0;
while (*cp) {
- while (*cp && vstrchr("augo", *cp))
+ while (*cp && vstrchr(Taugo, *cp))
switch (*cp++) {
case 'a':
positions |= 0111;
@@ -1698,7 +1460,7 @@
if (!positions)
/* default is a */
positions = 0111;
- if (!vstrchr("=+-", op = *cp))
+ if (!ctype((op = *cp), C_EQUAL | C_MINUS | C_PLUS))
break;
cp++;
new_val = 0;
@@ -1739,7 +1501,7 @@
if (*cp == ',') {
positions = 0;
cp++;
- } else if (!vstrchr("=+-", *cp))
+ } else if (!ctype(*cp, C_EQUAL | C_MINUS | C_PLUS))
break;
}
if (*cp) {
@@ -1757,17 +1519,21 @@
c_dot(const char **wp)
{
const char *file, *cp, **argv;
- int argc, i, errcode;
+ int argc, rv, errcode;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return (1);
if ((cp = wp[builtin_opt.optind]) == NULL) {
- bi_errorf("missing argument");
+ bi_errorf(Tno_args);
return (1);
}
- if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) {
- bi_errorf("%s: %s", cp, cstrerror(errcode));
+ file = search_path(cp, path, R_OK, &errcode);
+ if (!file && errcode == ENOENT && wp[0][0] == 's' &&
+ search_access(cp, R_OK) == 0)
+ file = cp;
+ if (!file) {
+ bi_errorf(Tf_sD_s, cp, cstrerror(errcode));
return (1);
}
@@ -1782,12 +1548,17 @@
argc = 0;
argv = NULL;
}
- if ((i = include(file, argc, argv, false)) < 0) {
+ /* SUSv4: OR with a high value never written otherwise */
+ exstat |= 0x4000;
+ if ((rv = include(file, argc, argv, false)) < 0) {
/* should not happen */
- bi_errorf("%s: %s", cp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errno));
return (1);
}
- return (i);
+ if (exstat & 0x4000)
+ /* detect old exstat, use 0 in that case */
+ rv = 0;
+ return (rv);
}
int
@@ -1812,18 +1583,19 @@
return (rv);
}
-static char REPLY[] = "REPLY";
+static const char REPLY[] = "REPLY";
int
c_read(const char **wp)
{
#define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
- int c, fd = 0, rv = 0, lastparm = 0;
+ int c, fd = 0, rv = 0;
bool savehist = false, intoarray = false, aschars = false;
bool rawmode = false, expanding = false;
+ bool lastparmmode = false, lastparmused = false;
enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
char delim = '\n';
size_t bytesleft = 128, bytesread;
- struct tbl *vp /* FU gcc */ = NULL, *vq;
+ struct tbl *vp /* FU gcc */ = NULL, *vq = NULL;
char *cp, *allocd = NULL, *xp;
const char *ccp;
XString xs;
@@ -1830,6 +1602,8 @@
size_t xsave = 0;
mksh_ttyst tios;
bool restore_tios = false;
+ /* to catch read -aN2 foo[i] */
+ bool subarray = false;
#if HAVE_SELECT
bool hastimeout = false;
struct timeval tv, tvlim;
@@ -1837,6 +1611,10 @@
#else
#define c_read_opts "Aad:N:n:prsu,"
#endif
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ int saved_mode;
+ int saved_errno;
+#endif
while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
switch (c) {
@@ -1855,7 +1633,7 @@
if (!bi_getn(builtin_opt.optarg, &c))
return (2);
if (c == -1) {
- readmode = READALL;
+ readmode = readmode == BYTES ? READALL : UPTO;
bytesleft = 1024;
} else
bytesleft = (unsigned int)c;
@@ -1862,7 +1640,7 @@
break;
case 'p':
if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
- bi_errorf("%s: %s", "-p", ccp);
+ bi_errorf(Tf_coproc, ccp);
return (2);
}
break;
@@ -1875,7 +1653,7 @@
#if HAVE_SELECT
case 't':
if (parse_usec(builtin_opt.optarg, &tv)) {
- bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
+ bi_errorf(Tf_sD_s_qs, Tsynerr, cstrerror(errno),
builtin_opt.optarg);
return (2);
}
@@ -1886,7 +1664,7 @@
if (!builtin_opt.optarg[0])
fd = 0;
else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
- bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
+ bi_errorf(Tf_sD_sD_s, "-u", builtin_opt.optarg, ccp);
return (2);
}
break;
@@ -1898,7 +1676,7 @@
*--wp = REPLY;
if (intoarray && wp[1] != NULL) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (2);
}
@@ -1945,7 +1723,7 @@
timersub(&tvlim, &tv, &tv);
if (tv.tv_sec < 0) {
/* timeout expired globally */
- rv = 1;
+ rv = 3;
goto c_read_out;
}
@@ -1954,10 +1732,11 @@
break;
case 0:
/* timeout expired for this call */
- rv = 1;
- goto c_read_out;
+ bytesread = 0;
+ rv = 3;
+ goto c_read_readdone;
default:
- bi_errorf("%s: %s", Tselect, cstrerror(errno));
+ bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
rv = 2;
goto c_read_out;
}
@@ -1964,21 +1743,32 @@
}
#endif
- bytesread = blocking_read(fd, xp, bytesleft);
- if (bytesread == (size_t)-1) {
- /* interrupted */
- if (errno == EINTR && fatal_trap_check()) {
- /*
- * Was the offending signal one that would
- * normally kill a process? If so, pretend
- * the read was killed.
- */
- rv = 2;
- goto c_read_out;
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ saved_mode = setmode(fd, O_TEXT);
+#endif
+ if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) {
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ saved_errno = errno;
+ setmode(fd, saved_mode);
+ errno = saved_errno;
+#endif
+ if (errno == EINTR) {
+ /* check whether the signal would normally kill */
+ if (!fatal_trap_check()) {
+ /* no, just ignore the signal */
+ goto c_read_readloop;
+ }
+ /* pretend the read was killed */
+ } else {
+ /* unexpected error */
+ bi_errorf(Tf_s, cstrerror(errno));
}
- /* just ignore the signal */
- goto c_read_readloop;
+ rv = 2;
+ goto c_read_out;
}
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ setmode(fd, saved_mode);
+#endif
switch (readmode) {
case READALL:
@@ -2002,7 +1792,7 @@
if (bytesread == 0) {
/* end of file reached */
rv = 1;
- xp = Xstring(xs, xp);
+ /* may be partial read: $? = 1, but content */
goto c_read_readdone;
}
xp += bytesread;
@@ -2053,19 +1843,19 @@
/*-
* state: we finished reading the input and NUL terminated it
* Xstring(xs, xp) -> xp-1 = input string without trailing delim
- * rv = 1 if EOF, 0 otherwise (errors handled already)
+ * rv = 3 if timeout, 1 if EOF, 0 otherwise (errors handled already)
*/
- if (rv == 1) {
- /* clean up coprocess if needed, on EOF */
+ if (rv) {
+ /* clean up coprocess if needed, on EOF/error/timeout */
coproc_read_close(fd);
- if (readmode == READALL)
+ if (readmode == READALL && (rv == 1 || (rv == 3 && bytesread)))
/* EOF is no error here */
rv = 0;
}
if (savehist)
- histsave(&source->line, Xstring(xs, xp), true, false);
+ histsave(&source->line, Xstring(xs, xp), HIST_STORE, false);
ccp = cp = Xclose(xs, xp);
expanding = false;
@@ -2072,18 +1862,19 @@
XinitN(xs, 128, ATEMP);
if (intoarray) {
vp = global(*wp);
+ subarray = last_lookup_was_array;
if (vp->flag & RDONLY) {
c_read_splitro:
- bi_errorf("read-only: %s", *wp);
+ bi_errorf(Tf_ro, *wp);
c_read_spliterr:
rv = 2;
afree(cp, ATEMP);
goto c_read_out;
}
+ /* counter for array index */
+ c = subarray ? arrayindex(vp) : 0;
/* exporting an array is currently pointless */
- unset(vp, 1);
- /* counter for array index */
- c = 0;
+ unset(vp, subarray ? 0 : 1);
}
if (!aschars) {
/* skip initial IFS whitespace */
@@ -2123,7 +1914,7 @@
}
if (!intoarray && wp[1] == NULL)
- lastparm = 1;
+ lastparmmode = true;
c_read_splitlast:
/* copy until IFS character */
@@ -2148,6 +1939,7 @@
}
xsave = Xsavepos(xs, xp);
/* copy word delimiter: IFSWS+IFS,IFSWS */
+ expanding = false;
while (bytesread) {
char ch;
@@ -2154,10 +1946,16 @@
ch = *ccp;
if (!ctype(ch, C_IFS))
break;
- Xcheck(xs, xp);
- Xput(xs, xp, ch);
+ if (lastparmmode && !expanding && !rawmode && ch == '\\') {
+ expanding = true;
+ } else {
+ Xcheck(xs, xp);
+ Xput(xs, xp, ch);
+ }
++ccp;
--bytesread;
+ if (expanding)
+ continue;
if (!ctype(ch, C_IFSWS))
break;
}
@@ -2168,17 +1966,28 @@
--bytesread;
}
/* if no more parameters, rinse and repeat */
- if (lastparm && bytesread) {
- ++lastparm;
+ if (lastparmmode && bytesread) {
+ lastparmused = true;
goto c_read_splitlast;
}
/* get rid of the delimiter unless we pack the rest */
- if (lastparm < 2)
+ if (!lastparmused)
xp = Xrestpos(xs, xp, xsave);
c_read_gotword:
Xput(xs, xp, '\0');
if (intoarray) {
- vq = arraysearch(vp, c++);
+ if (subarray) {
+ /* array element passed, accept first read */
+ if (vq) {
+ bi_errorf("nested arrays not yet supported");
+ goto c_read_spliterr;
+ }
+ vq = vp;
+ if (c)
+ /* [0] doesn't */
+ vq->flag |= AINDEX;
+ } else
+ vq = arraysearch(vp, c++);
} else {
vq = global(*wp);
/* must be checked before exporting */
@@ -2206,7 +2015,7 @@
Xfree(xs, xp);
if (restore_tios)
mksh_tcset(fd, &tios);
- return (rv);
+ return (rv == 3 ? ksh_sigmask(SIGALRM) : rv);
#undef is_ifsws
}
@@ -2221,6 +2030,7 @@
return (1);
s = pushs(SWORDS, ATEMP);
s->u.strv = wp + builtin_opt.optind;
+ s->line = current_lineno;
/*-
* The following code handles the case where the command is
@@ -2258,7 +2068,7 @@
savef = Flag(FERREXIT);
Flag(FERREXIT) |= 0x80;
- rv = shell(s, false);
+ rv = shell(s, 2);
Flag(FERREXIT) = savef;
source = saves;
afree(s, ATEMP);
@@ -2271,9 +2081,9 @@
int
c_trap(const char **wp)
{
- int i;
+ Trap *p = sigtraps;
+ int i = ksh_NSIG;
const char *s;
- Trap *p;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return (1);
@@ -2280,32 +2090,34 @@
wp += builtin_opt.optind;
if (*wp == NULL) {
- for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
- if (p->trap != NULL) {
+ do {
+ if (p->trap) {
shf_puts("trap -- ", shl_stdout);
print_value_quoted(shl_stdout, p->trap);
- shprintf(" %s\n", p->name);
+ shprintf(Tf__sN, p->name);
}
+ ++p;
+ } while (i--);
return (0);
}
- /*
- * Use case sensitive lookup for first arg so the
- * command 'exit' isn't confused with the pseudo-signal
- * 'EXIT'.
- */
- /* get command */
- s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
- if (s != NULL && s[0] == '-' && s[1] == '\0')
+ if (getn(*wp, &i)) {
+ /* first argument is a signal number, reset them all */
s = NULL;
+ } else {
+ /* first argument must be a command, then */
+ s = *wp++;
+ /* reset traps? */
+ if (ksh_isdash(s))
+ s = NULL;
+ }
- /* set/clear traps */
+ /* set/clear the traps */
i = 0;
- while (*wp != NULL)
- if ((p = gettrap(*wp++, true)) == NULL) {
- warningf(true, "%s: %s '%s'", builtin_argv0,
- "bad signal", wp[-1]);
- ++i;
+ while (*wp)
+ if (!(p = gettrap(*wp++, true, true))) {
+ warningf(true, Tbad_sig_ss, builtin_argv0, wp[-1]);
+ i = 1;
} else
settrap(p, s);
return (i);
@@ -2315,20 +2127,14 @@
c_exitreturn(const char **wp)
{
int n, how = LEXIT;
- const char *arg;
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- goto c_exitreturn_err;
- arg = wp[builtin_opt.optind];
-
- if (arg) {
- if (!getn(arg, &n)) {
- exstat = 1;
- warningf(true, "%s: %s", arg, "bad number");
- } else
- exstat = n & 0xFF;
+ if (wp[1]) {
+ if (wp[2])
+ goto c_exitreturn_err;
+ exstat = bi_getn(wp[1], &n) ? (n & 0xFF) : 1;
} else if (trap_exstat != -1)
exstat = trap_exstat;
+
if (wp[0][0] == 'r') {
/* return */
struct env *ep;
@@ -2349,12 +2155,13 @@
how = LSHELL;
}
- /* get rid of any i/o redirections */
+ /* get rid of any I/O redirections */
quitenv(NULL);
unwind(how);
/* NOTREACHED */
c_exitreturn_err:
+ bi_errorf(Ttoo_many_args);
return (1);
}
@@ -2376,7 +2183,7 @@
goto c_brkcont_err;
if (n <= 0) {
/* AT&T ksh does this for non-interactive shells only - weird */
- bi_errorf("%s: %s", arg, "bad value");
+ bi_errorf("%s: bad value", arg);
goto c_brkcont_err;
}
quit = (unsigned int)n;
@@ -2397,7 +2204,7 @@
* scripts, but don't generate an error (ie, keep going).
*/
if ((unsigned int)n == quit) {
- warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
+ warningf(true, Tf_cant_s, wp[0], wp[0]);
return (0);
}
/*
@@ -2491,8 +2298,9 @@
size_t n;
n = strlen(id);
- if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
- id[n-1] == ']') {
+ if (n > 3 && ord(id[n - 3]) == ORD('[') &&
+ ord(id[n - 2]) == ORD('*') &&
+ ord(id[n - 1]) == ORD(']')) {
strndupx(cp, id, n - 3, ATEMP);
id = cp;
optc = 3;
@@ -2503,7 +2311,7 @@
afree(cp, ATEMP);
if ((vp->flag&RDONLY)) {
- warningf(true, "read-only: %s", vp->name);
+ warningf(true, Tf_ro, vp->name);
rv = 1;
} else
unset(vp, optc);
@@ -2522,7 +2330,7 @@
shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
tv_sec, tv_usec, suffix);
else
- shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
+ shf_fprintf(shf, "%s%*ldm%02d.%02ds%s", prefix, width,
tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
}
@@ -2533,13 +2341,13 @@
getrusage(RUSAGE_SELF, &usage);
p_time(shl_stdout, false, usage.ru_utime.tv_sec,
- usage.ru_utime.tv_usec, 0, null, " ");
+ usage.ru_utime.tv_usec, 0, null, T1space);
p_time(shl_stdout, false, usage.ru_stime.tv_sec,
usage.ru_stime.tv_usec, 0, null, "\n");
getrusage(RUSAGE_CHILDREN, &usage);
p_time(shl_stdout, false, usage.ru_utime.tv_sec,
- usage.ru_utime.tv_usec, 0, null, " ");
+ usage.ru_utime.tv_usec, 0, null, T1space);
p_time(shl_stdout, false, usage.ru_stime.tv_sec,
usage.ru_stime.tv_usec, 0, null, "\n");
@@ -2598,17 +2406,17 @@
timersub(&tv1, &tv0, &tv1);
if (tf & TF_POSIX)
p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
- 5, "real ", "\n");
+ 5, Treal_sp1, "\n");
else
p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
- 5, null, " real ");
+ 5, null, Treal_sp2);
}
if (tf & TF_POSIX)
p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
- 5, "user ", "\n");
+ 5, Tuser_sp1, "\n");
else
p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
- 5, null, " user ");
+ 5, null, Tuser_sp2);
if (tf & TF_POSIX)
p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
5, "sys ", "\n");
@@ -2636,11 +2444,11 @@
t->str[0] |= TF_POSIX;
break;
case '?':
- errorf("time: -%s %s", opt.optarg,
- "unknown option");
+ errorf(Tf_optfoo, Ttime, Tcolsp,
+ opt.optarg[0], Tunknown_option);
case ':':
- errorf("time: -%s %s", opt.optarg,
- "requires an argument");
+ errorf(Tf_optfoo, Ttime, Tcolsp,
+ opt.optarg[0], Treq_arg);
}
/* Copy command words down over options. */
if (opt.optind != 0) {
@@ -2665,7 +2473,6 @@
for (i = 0; i < NUFILE; i++) {
if (e->savefd[i] > 0)
close(e->savefd[i]);
-#ifndef MKSH_LEGACY_MODE
/*
* keep all file descriptors > 2 private for ksh,
* but not for POSIX or legacy/kludge sh
@@ -2673,7 +2480,6 @@
if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
e->savefd[i])
fcntl(i, F_SETFD, FD_CLOEXEC);
-#endif
}
e->savefd = NULL;
}
@@ -2680,7 +2486,7 @@
return (0);
}
-#if HAVE_MKNOD
+#if HAVE_MKNOD && !defined(__OS2__)
int
c_mknod(const char **wp)
{
@@ -2730,21 +2536,21 @@
majnum = strtoul(argv[2], &c, 0);
if ((c == argv[2]) || (*c != '\0')) {
- bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
+ bi_errorf(Tf_nonnum, "device", "major", argv[2]);
goto c_mknod_err;
}
minnum = strtoul(argv[3], &c, 0);
if ((c == argv[3]) || (*c != '\0')) {
- bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
+ bi_errorf(Tf_nonnum, "device", "minor", argv[3]);
goto c_mknod_err;
}
dv = makedev(majnum, minnum);
if ((unsigned long)(major(dv)) != majnum) {
- bi_errorf("%s %s too large: %lu", "device", "major", majnum);
+ bi_errorf(Tf_toolarge, "device", "major", majnum);
goto c_mknod_err;
}
if ((unsigned long)(minor(dv)) != minnum) {
- bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
+ bi_errorf(Tf_toolarge, "device", "minor", minnum);
goto c_mknod_err;
}
if (mknod(argv[0], mode, dv))
@@ -2751,7 +2557,7 @@
goto c_mknod_failed;
} else if (mkfifo(argv[0], mode)) {
c_mknod_failed:
- bi_errorf("%s: %s", argv[0], cstrerror(errno));
+ bi_errorf(Tf_sD_s, argv[0], cstrerror(errno));
c_mknod_err:
rv = 1;
}
@@ -2760,8 +2566,8 @@
umask(oldmode);
return (rv);
c_mknod_usage:
- bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
- bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
+ bi_errorf("usage: mknod [-m mode] name %s", "b|c major minor");
+ bi_errorf("usage: mknod [-m mode] name %s", "p");
return (1);
}
#endif
@@ -2777,14 +2583,13 @@
| "(" oexpr ")"
;
- unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
- "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
- "-L"|"-h"|"-S"|"-H";
+ unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
+ "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
+ "-u"|"-v"|"-w"|"-x"|"-z";
- binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
- "-nt"|"-ot"|"-ef"|
- "<"|">" # rules used for [[ ... ]] expressions
- ;
+ binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
+ "-lt"|"-le"|"-ef"|"-nt"|"-ot";
+
operand ::= <anything>
*/
@@ -2797,6 +2602,7 @@
int argc, rv, invert = 0;
Test_env te;
Test_op op;
+ Test_meta tm;
const char *lhs, **swp;
te.flags = 0;
@@ -2808,7 +2614,7 @@
for (argc = 0; wp[argc]; argc++)
;
- if (strcmp(wp[0], "[") == 0) {
+ if (strcmp(wp[0], Tbracket) == 0) {
if (strcmp(wp[--argc], "]") != 0) {
bi_errorf("missing ]");
return (T_ERR_EXIT);
@@ -2820,7 +2626,7 @@
/*
* Attempt to conform to POSIX special cases. This is pretty
- * dumb code straight-forward from the 2008 spec, but unless
+ * dumb code straight-forward from the 2008 spec, but unlike
* the old pdksh code doesn't live from so many assumptions.
* It does, though, inline some calls to '(*te.funcname)()'.
*/
@@ -2841,6 +2647,8 @@
ptest_unary:
rv = test_eval(&te, op, *te.pos.wp++, NULL, true);
ptest_out:
+ if (te.flags & TEF_ERROR)
+ return (T_ERR_EXIT);
return ((invert & 1) ? rv : !rv);
}
/* let the parser deal with anything else */
@@ -2855,6 +2663,16 @@
rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
goto ptest_out;
}
+ if (ptest_isa(&te, tm = TM_AND) || ptest_isa(&te, tm = TM_OR)) {
+ /* XSI */
+ argc = test_eval(&te, TO_STNZE, lhs, NULL, true);
+ rv = test_eval(&te, TO_STNZE, *te.pos.wp++, NULL, true);
+ if (tm == TM_AND)
+ rv = argc && rv;
+ else
+ rv = argc || rv;
+ goto ptest_out;
+ }
/* back up to lhs */
te.pos.wp = swp;
if (ptest_isa(&te, TM_NOT)) {
@@ -2924,6 +2742,14 @@
return (TO_NONOP);
}
+#ifdef __OS2__
+#define test_access(name, mode) access_ex(access, (name), (mode))
+#define test_stat(name, buffer) stat_ex((name), (buffer))
+#else
+#define test_access(name, mode) access((name), (mode))
+#define test_stat(name, buffer) stat((name), (buffer))
+#endif
+
int
test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
bool do_eval)
@@ -2932,6 +2758,7 @@
size_t k;
struct stat b1, b2;
mksh_ari_t v1, v2;
+ struct tbl *vp;
if (!do_eval)
return (0);
@@ -2978,6 +2805,10 @@
case TO_STZER:
return (*opnd1 == '\0');
+ /* -v */
+ case TO_ISSET:
+ return ((vp = isglobal(opnd1, false)) && (vp->flag & ISSET));
+
/* -o */
case TO_OPTION:
if ((i = *opnd1) == '!' || i == '?')
@@ -2989,12 +2820,12 @@
/* -r */
case TO_FILRD:
/* LINTED use of access */
- return (access(opnd1, R_OK) == 0);
+ return (test_access(opnd1, R_OK) == 0);
/* -w */
case TO_FILWR:
/* LINTED use of access */
- return (access(opnd1, W_OK) == 0);
+ return (test_access(opnd1, W_OK) == 0);
/* -x */
case TO_FILEX:
@@ -3004,11 +2835,11 @@
case TO_FILAXST:
/* -e */
case TO_FILEXST:
- return (stat(opnd1, &b1) == 0);
+ return (test_stat(opnd1, &b1) == 0);
- /* -r */
+ /* -f */
case TO_FILREG:
- return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
/* -d */
case TO_FILID:
@@ -3107,16 +2938,22 @@
* Binary Operators
*/
- /* = */
+ /* =, == */
case TO_STEQL:
- if (te->flags & TEF_DBRACKET)
- return (gmatchx(opnd1, opnd2, false));
+ if (te->flags & TEF_DBRACKET) {
+ if ((i = gmatchx(opnd1, opnd2, false)))
+ record_match(opnd1);
+ return (i);
+ }
return (strcmp(opnd1, opnd2) == 0);
/* != */
case TO_STNEQ:
- if (te->flags & TEF_DBRACKET)
- return (!gmatchx(opnd1, opnd2, false));
+ if (te->flags & TEF_DBRACKET) {
+ if ((i = gmatchx(opnd1, opnd2, false)))
+ record_match(opnd1);
+ return (!i);
+ }
return (strcmp(opnd1, opnd2) != 0);
/* < */
@@ -3273,7 +3110,7 @@
/* unary expression */
opnd1 = (*te->getopnd)(te, op, do_eval);
if (!opnd1) {
- (*te->error)(te, -1, "missing argument");
+ (*te->error)(te, -1, Tno_args);
return (0);
}
@@ -3349,9 +3186,9 @@
te->flags |= TEF_ERROR;
if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
- bi_errorf("%s: %s", op, msg);
+ bi_errorf(Tf_sD_s, op, msg);
else
- bi_errorf("%s", msg);
+ bi_errorf(Tf_s, msg);
}
#ifndef MKSH_NO_LIMITS
@@ -3408,7 +3245,27 @@
# error nonsensical v ulimit
#endif
+struct limits {
+ /* limit resource */
+ int resource;
+ /* multiply by to get rlim_{cur,max} values */
+ unsigned int factor;
+ /* getopts char */
+ char optchar;
+ /* limit name */
+ char name[1];
+};
+
#define RLIMITS_DEFNS
+#define FN(lname,lid,lfac,lopt) \
+ static const struct { \
+ int resource; \
+ unsigned int factor; \
+ char optchar; \
+ char name[sizeof(lname)]; \
+ } rlimits_ ## lid = { \
+ lid, lfac, lopt, lname \
+ };
#include "rlimits.gen"
static void print_ulimit(const struct limits *, int);
@@ -3459,7 +3316,7 @@
found:
if (wp[builtin_opt.optind]) {
if (all || wp[builtin_opt.optind + 1]) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
@@ -3467,7 +3324,7 @@
if (!all)
print_ulimit(rlimits[i], how);
else for (i = 0; i < NELEM(rlimits); ++i) {
- shprintf("%-20s ", rlimits[i]->name);
+ shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name);
print_ulimit(rlimits[i], how);
}
return (0);
@@ -3492,7 +3349,7 @@
* If this causes problems, will have to add parameter to
* evaluate() to control if unset params are 0 or an error.
*/
- if (!rval && !ksh_isdigit(v[0])) {
+ if (!rval && !ctype(v[0], C_DIGIT)) {
bi_errorf("invalid %s limit: %s", l->name, v);
return (1);
}
@@ -3560,7 +3417,7 @@
bi_errorf(Tsynerr);
else if ((rv = rename(wp[0], wp[1])) != 0) {
rv = errno;
- bi_errorf("%s: %s", "failed", cstrerror(rv));
+ bi_errorf(Tf_sD_s, "failed", cstrerror(rv));
}
return (rv);
@@ -3583,11 +3440,11 @@
bi_errorf(Tsynerr);
else if ((buf = do_realpath(wp[0])) == NULL) {
rv = errno;
- bi_errorf("%s: %s", wp[0], cstrerror(rv));
+ bi_errorf(Tf_sD_s, wp[0], cstrerror(rv));
if ((unsigned int)rv > 255)
rv = 255;
} else {
- shprintf("%s\n", buf);
+ shprintf(Tf_sN, buf);
afree(buf, ATEMP);
rv = 0;
}
@@ -3598,10 +3455,11 @@
int
c_cat(const char **wp)
{
- int fd = STDIN_FILENO, rv, eno;
+ int fd = STDIN_FILENO, rv;
ssize_t n, w;
const char *fn = "<stdin>";
char *buf, *cp;
+ bool opipe;
#define MKSH_CAT_BUFSIZ 4096
/* parse options: POSIX demands we support "-u" as no-op */
@@ -3623,30 +3481,34 @@
return (1);
}
+ /* catch SIGPIPE */
+ opipe = block_pipe();
+
do {
if (*wp) {
fn = *wp++;
- if (fn[0] == '-' && fn[1] == '\0')
+ if (ksh_isdash(fn))
fd = STDIN_FILENO;
- else if ((fd = open(fn, O_RDONLY | O_BINARY)) < 0) {
- eno = errno;
- bi_errorf("%s: %s", fn, cstrerror(eno));
+ else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
+ bi_errorf(Tf_sD_s, fn, cstrerror(errno));
rv = 1;
continue;
}
}
while (/* CONSTCOND */ 1) {
- n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
- eno = errno;
- /* give the user a chance to ^C out */
- intrcheck();
- if (n == -1) {
- if (eno == EINTR) {
+ if ((n = blocking_read(fd, (cp = buf),
+ MKSH_CAT_BUFSIZ)) == -1) {
+ if (errno == EINTR) {
+ if (opipe)
+ restore_pipe();
+ /* give the user a chance to ^C out */
+ intrcheck();
/* interrupted, try again */
+ opipe = block_pipe();
continue;
}
/* an error occured during reading */
- bi_errorf("%s: %s", fn, cstrerror(eno));
+ bi_errorf(Tf_sD_s, fn, cstrerror(errno));
rv = 1;
break;
} else if (n == 0)
@@ -3653,24 +3515,35 @@
/* end of file reached */
break;
while (n) {
- w = write(STDOUT_FILENO, cp, n);
- eno = errno;
- /* give the user a chance to ^C out */
- intrcheck();
- if (w == -1) {
- if (eno == EINTR)
- /* interrupted, try again */
- continue;
+ if (intrsig)
+ goto has_intrsig;
+ if ((w = write(STDOUT_FILENO, cp, n)) != -1) {
+ n -= w;
+ cp += w;
+ continue;
+ }
+ if (errno == EINTR) {
+ has_intrsig:
+ if (opipe)
+ restore_pipe();
+ /* give the user a chance to ^C out */
+ intrcheck();
+ /* interrupted, try again */
+ opipe = block_pipe();
+ continue;
+ }
+ if (errno == EPIPE) {
+ /* fake receiving signal */
+ rv = ksh_sigmask(SIGPIPE);
+ } else {
/* an error occured during writing */
- bi_errorf("%s: %s", "<stdout>",
- cstrerror(eno));
+ bi_errorf(Tf_sD_s, "<stdout>",
+ cstrerror(errno));
rv = 1;
- if (fd != STDIN_FILENO)
- close(fd);
- goto out;
}
- n -= w;
- cp += w;
+ if (fd != STDIN_FILENO)
+ close(fd);
+ goto out;
}
}
if (fd != STDIN_FILENO)
@@ -3678,6 +3551,8 @@
} while (*wp);
out:
+ if (opipe)
+ restore_pipe();
free_osfunc(buf);
return (rv);
}
@@ -3698,7 +3573,7 @@
if (!wp[0] || wp[1])
bi_errorf(Tsynerr);
else if (parse_usec(wp[0], &tv))
- bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
+ bi_errorf(Tf_sD_s_qs, Tsynerr, cstrerror(errno), wp[0]);
else {
#ifndef MKSH_NOPROSPECTOFWORK
sigset_t omask, bmask;
@@ -3728,7 +3603,7 @@
*/
rv = 0;
else
- bi_errorf("%s: %s", Tselect, cstrerror(errno));
+ bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
#ifndef MKSH_NOPROSPECTOFWORK
/* this will re-schedule signal delivery */
sigprocmask(SIG_SETMASK, &omask, NULL);
@@ -3743,7 +3618,7 @@
c_suspend(const char **wp)
{
if (wp[1] != NULL) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
if (Flag(FLOGIN)) {
Modified: trunk/contrib/mksh/histrap.c
===================================================================
--- trunk/contrib/mksh/histrap.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/histrap.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,10 +1,10 @@
-/* $OpenBSD: history.c,v 1.40 2014/11/20 15:22:39 tedu Exp $ */
+/* $OpenBSD: history.c,v 1.41 2015/09/01 13:12:31 tedu Exp $ */
/* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2014, 2015, 2016, 2017
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -27,9 +27,9 @@
#include <sys/file.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.134.2.3 2015/03/01 15:43:00 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.166 2017/08/07 23:25:09 tg Exp $");
-Trap sigtraps[NSIG + 1];
+Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign;
#if HAVE_PERSISTENT_HISTORY
@@ -38,7 +38,7 @@
static void writehistfile(int, const char *);
#endif
-static int hist_execute(char *);
+static int hist_execute(char *, Area *);
static char **hist_get(const char *, bool, bool);
static char **hist_get_oldest(void);
@@ -69,12 +69,6 @@
static off_t histfsize;
#endif
-static const char Tnot_in_history[] = "not in history";
-#define Thistory (Tnot_in_history + 7)
-
-static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
-#define Tspdollaru (TFCEDIT_dollaru + 18)
-
/* HISTSIZE default: size of saved history, persistent or standard */
#ifdef MKSH_SMALL
#define MKSH_DEFHISTSIZE 255
@@ -84,6 +78,9 @@
/* maximum considered size of persistent history file */
#define MKSH_MAXHISTFSIZE ((off_t)1048576 * 96)
+/* hidden option */
+#define HIST_DISCARD 5
+
int
c_fc(const char **wp)
{
@@ -150,7 +147,7 @@
else if (!last)
last = p;
else {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
break;
@@ -180,7 +177,7 @@
if (!first && (first = *wp))
wp++;
if (last || *wp) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
@@ -214,7 +211,7 @@
xp += rep_len;
}
if (!any_subst) {
- bi_errorf("bad substitution");
+ bi_errorf(Tbadsubst);
return (1);
}
len = strlen(s) + 1;
@@ -223,7 +220,7 @@
xp += len;
line = Xclose(xs, xp);
}
- return (hist_execute(line));
+ return (hist_execute(line, ATEMP));
}
if (editor && (lflag || nflag)) {
@@ -236,7 +233,7 @@
if (!last && (last = *wp))
wp++;
if (*wp) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
if (!first) {
@@ -275,8 +272,9 @@
for (hp = rflag ? hlast : hfirst;
hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
if (!nflag)
- shf_fprintf(shl_stdout, "%d",
- hist_source->line - (int)(histptr - hp));
+ shf_fprintf(shl_stdout, Tf_lu,
+ (unsigned long)hist_source->line -
+ (unsigned long)(histptr - hp));
shf_putc('\t', shl_stdout);
/* print multi-line commands correctly */
s = *hp;
@@ -286,7 +284,7 @@
*t++ = '\n';
s = t;
}
- shf_fprintf(shl_stdout, "%s\n", s);
+ shf_fprintf(shl_stdout, Tf_sN, s);
}
shf_flush(shl_stdout);
return (0);
@@ -296,16 +294,14 @@
tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
if (!(shf = tf->shf)) {
- bi_errorf("can't %s temporary file %s: %s",
- "create", tf->tffn, cstrerror(errno));
+ bi_errorf(Tf_temp, Tcreate, tf->tffn, cstrerror(errno));
return (1);
}
for (hp = rflag ? hlast : hfirst;
hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
- shf_fprintf(shf, "%s\n", *hp);
+ shf_fprintf(shf, Tf_sN, *hp);
if (shf_close(shf) == -1) {
- bi_errorf("can't %s temporary file %s: %s",
- "write", tf->tffn, cstrerror(errno));
+ bi_errorf(Tf_temp, Twrite, tf->tffn, cstrerror(errno));
return (1);
}
@@ -312,17 +308,9 @@
/* Ignore setstr errors here (arbitrary) */
setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
- /* XXX: source should not get trashed by this.. */
- {
- Source *sold = source;
- int ret;
+ if ((optc = command(editor ? editor : TFCEDIT_dollaru, 0)))
+ return (optc);
- ret = command(editor ? editor : TFCEDIT_dollaru, 0);
- source = sold;
- if (ret)
- return (ret);
- }
-
{
struct stat statb;
XString xs;
@@ -330,8 +318,7 @@
ssize_t n;
if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
- bi_errorf("can't %s temporary file %s: %s",
- "open", tf->tffn, cstrerror(errno));
+ bi_errorf(Tf_temp, Topen, tf->tffn, cstrerror(errno));
return (1);
}
@@ -338,8 +325,8 @@
if (stat(tf->tffn, &statb) < 0)
n = 128;
else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
- bi_errorf("%s %s too large: %lu", Thistory,
- "file", (unsigned long)statb.st_size);
+ bi_errorf(Tf_toolarge, Thistory,
+ Tfile, (unsigned long)statb.st_size);
goto errout;
} else
n = (size_t)statb.st_size + 1;
@@ -350,8 +337,8 @@
XcheckN(xs, xp, Xlength(xs, xp));
}
if (n < 0) {
- bi_errorf("can't %s temporary file %s: %s",
- "read", tf->tffn, cstrerror(shf_errno(shf)));
+ bi_errorf(Tf_temp, Tread, tf->tffn,
+ cstrerror(shf_errno(shf)));
errout:
shf_close(shf);
return (1);
@@ -359,18 +346,15 @@
shf_close(shf);
*xp = '\0';
strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
- return (hist_execute(Xstring(xs, xp)));
+ return (hist_execute(Xstring(xs, xp), hist_source->areap));
}
}
-/* Save cmd in history, execute cmd (cmd gets trashed) */
+/* save cmd in history, execute cmd (cmd gets afree’d) */
static int
-hist_execute(char *cmd)
+hist_execute(char *cmd, Area *areap)
{
static int last_line = -1;
- Source *sold;
- int ret;
- char *p, *q;
/* Back up over last histsave */
if (histptr >= history && last_line != hist_source->line) {
@@ -380,23 +364,13 @@
last_line = hist_source->line;
}
- for (p = cmd; p; p = q) {
- if ((q = strchr(p, '\n'))) {
- /* kill the newline */
- *q++ = '\0';
- if (!*q)
- /* ignore trailing newline */
- q = NULL;
- }
- histsave(&hist_source->line, p, true, true);
+ histsave(&hist_source->line, cmd, HIST_STORE, true);
+ /* now *histptr == cmd without all trailing newlines */
+ afree(cmd, areap);
+ cmd = *histptr;
+ /* pdksh says POSIX doesn’t say this is done, testsuite needs it */
+ shellf(Tf_sN, cmd);
- /* POSIX doesn't say this is done... */
- shellf("%s\n", p);
- if (q)
- /* restore \n (trailing \n not restored) */
- q[-1] = '\n';
- }
-
/*-
* Commands are executed here instead of pushing them onto the
* input 'cause POSIX says the redirection and variable assignments
@@ -404,11 +378,7 @@
* X=y fc -e - 42 2> /dev/null
* are to effect the repeated commands environment.
*/
- /* XXX: source should not get trashed by this.. */
- sold = source;
- ret = command(cmd, 0);
- source = sold;
- return (ret);
+ return (command(cmd, 0));
}
/*
@@ -427,7 +397,7 @@
if (approx)
hp = hist_get_oldest();
else {
- bi_errorf("%s: %s", str, Tnot_in_history);
+ bi_errorf(Tf_sD_s, str, Tnot_in_history);
hp = NULL;
}
} else if ((size_t)hp > (size_t)histptr) {
@@ -434,11 +404,11 @@
if (approx)
hp = hist_get_newest(allow_cur);
else {
- bi_errorf("%s: %s", str, Tnot_in_history);
+ bi_errorf(Tf_sD_s, str, Tnot_in_history);
hp = NULL;
}
} else if (!allow_cur && hp == histptr) {
- bi_errorf("%s: %s", str, "invalid range");
+ bi_errorf(Tf_sD_s, str, "invalid range");
hp = NULL;
}
} else {
@@ -446,7 +416,7 @@
/* the -1 is to avoid the current fc command */
if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
- bi_errorf("%s: %s", str, Tnot_in_history);
+ bi_errorf(Tf_sD_s, str, Tnot_in_history);
else
hp = &history[n];
}
@@ -563,7 +533,7 @@
return;
/* if the name is the same as the name we have */
- if (hname && strcmp(hname, name) == 0)
+ if (hname && name && !strcmp(hname, name))
return;
/*
@@ -577,11 +547,13 @@
afree(hname, APERM);
hname = NULL;
/* let's reset the history */
+ histsave(NULL, NULL, HIST_DISCARD, true);
histptr = history - 1;
hist_source->line = 0;
}
- hist_init(hist_source);
+ if (name)
+ hist_init(hist_source);
}
#endif
@@ -610,6 +582,8 @@
{
bool changed = false;
+ /* called by histsave(), may not HIST_DISCARD, caller should flush */
+
if (histfd != -1) {
int lno = hist_source->line;
@@ -629,30 +603,76 @@
* save command in history
*/
void
-histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
+histsave(int *lnp, const char *cmd, int svmode, bool ignoredups)
{
- char **hp;
- char *c, *cp;
+ static char *enqueued = NULL;
+ char **hp, *c;
+ const char *ccp;
- strdupx(c, cmd, APERM);
- if ((cp = strchr(c, '\n')) != NULL)
- *cp = '\0';
+ if (svmode == HIST_DISCARD) {
+ afree(enqueued, APERM);
+ enqueued = NULL;
+ return;
+ }
- if (ignoredups && !strcmp(c, *histptr)
+ if (svmode == HIST_APPEND) {
+ if (!enqueued)
+ svmode = HIST_STORE;
+ } else if (enqueued) {
+ c = enqueued;
+ enqueued = NULL;
+ --*lnp;
+ histsave(lnp, c, HIST_STORE, true);
+ afree(c, APERM);
+ }
+
+ if (svmode == HIST_FLUSH)
+ return;
+
+ ccp = strnul(cmd);
+ while (ccp > cmd && ccp[-1] == '\n')
+ --ccp;
+ strndupx(c, cmd, ccp - cmd, APERM);
+
+ if (svmode != HIST_APPEND) {
+ if (ignoredups &&
+ histptr >= history &&
+ !strcmp(c, *histptr)
#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
- && !histsync()
+ && !histsync()
#endif
- ) {
- afree(c, APERM);
- return;
+ ) {
+ afree(c, APERM);
+ return;
+ }
+ ++*lnp;
}
- ++*lnp;
#if HAVE_PERSISTENT_HISTORY
- if (dowrite && histfd != -1)
+ if (svmode == HIST_STORE && histfd != -1)
writehistfile(*lnp, c);
#endif
+ if (svmode == HIST_QUEUE || svmode == HIST_APPEND) {
+ size_t nenq, ncmd;
+
+ if (!enqueued) {
+ if (*c)
+ enqueued = c;
+ else
+ afree(c, APERM);
+ return;
+ }
+
+ nenq = strlen(enqueued);
+ ncmd = strlen(c);
+ enqueued = aresize(enqueued, nenq + 1 + ncmd + 1, APERM);
+ enqueued[nenq] = '\n';
+ memcpy(enqueued + nenq + 1, c, ncmd + 1);
+ afree(c, APERM);
+ return;
+ }
+
hp = histptr;
if (++hp >= history + histsize) {
@@ -694,36 +714,79 @@
#if HAVE_PERSISTENT_HISTORY
static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 };
-#endif
-void
-hist_init(Source *s)
+static int
+hist_persist_back(int srcfd)
{
-#if HAVE_PERSISTENT_HISTORY
+ off_t tot, mis;
+ ssize_t n, w;
+ char *buf, *cp;
+ int rv = 0;
+#define MKSH_HS_BUFSIZ 4096
+
+ if ((tot = lseek(srcfd, (off_t)0, SEEK_END)) < 0 ||
+ lseek(srcfd, (off_t)0, SEEK_SET) < 0 ||
+ lseek(histfd, (off_t)0, SEEK_SET) < 0)
+ return (1);
+
+ if ((buf = malloc_osfunc(MKSH_HS_BUFSIZ)) == NULL)
+ return (1);
+
+ mis = tot;
+ while (mis > 0) {
+ if ((n = blocking_read(srcfd, (cp = buf),
+ MKSH_HS_BUFSIZ)) == -1) {
+ if (errno == EINTR) {
+ intrcheck();
+ continue;
+ }
+ goto copy_error;
+ }
+ mis -= n;
+ while (n) {
+ if (intrsig)
+ goto has_intrsig;
+ if ((w = write(histfd, cp, n)) != -1) {
+ n -= w;
+ cp += w;
+ continue;
+ }
+ if (errno == EINTR) {
+ has_intrsig:
+ intrcheck();
+ continue;
+ }
+ goto copy_error;
+ }
+ }
+ if (ftruncate(histfd, tot)) {
+ copy_error:
+ rv = 1;
+ }
+ free_osfunc(buf);
+ return (rv);
+}
+
+static void
+hist_persist_init(void)
+{
unsigned char *base;
int lines, fd;
- enum { hist_init_first, hist_init_retry, hist_init_restore } hs;
-#endif
+ enum { hist_init_first, hist_init_retry, hist_use_it } hs;
- if (Flag(FTALKING) == 0)
+ if (((hname = str_val(global("HISTFILE"))) == NULL) || !*hname) {
+ hname = NULL;
return;
-
- hstarted = true;
- hist_source = s;
-
-#if HAVE_PERSISTENT_HISTORY
- if ((hname = str_val(global("HISTFILE"))) == NULL)
- return;
+ }
strdupx(hname, hname, APERM);
hs = hist_init_first;
retry:
/* we have a file and are interactive */
- if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND | O_BINARY,
- 0600)) < 0)
+ if ((fd = binopen3(hname, O_RDWR | O_CREAT | O_APPEND, 0600)) < 0)
return;
-
- histfd = savefd(fd);
+ if ((histfd = savefd(fd)) < 0)
+ return;
if (histfd != fd)
close(fd);
@@ -730,9 +793,8 @@
mksh_lockfd(histfd);
histfsize = lseek(histfd, (off_t)0, SEEK_END);
- if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) {
+ if (histfsize > MKSH_MAXHISTFSIZE) {
/* we ignore too large files but still append to them */
- /* we also don't need to re-read after truncation */
goto hist_init_tail;
} else if (histfsize > 2) {
/* we have some data, check its validity */
@@ -755,9 +817,10 @@
/* create temporary file */
nhname = shf_smprintf("%s.%d", hname, (int)procpid);
- if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC |
- O_EXCL | O_BINARY, 0600)) < 0) {
+ if ((fd = binopen3(nhname, O_RDWR | O_CREAT | O_TRUNC |
+ O_EXCL, 0600)) < 0) {
/* just don't truncate then, meh. */
+ hs = hist_use_it;
goto hist_trunc_dont;
}
if (fstat(histfd, &sb) >= 0 &&
@@ -772,28 +835,26 @@
hp = history;
while (hp < histptr) {
if (!writehistline(fd,
- s->line - (histptr - hp), *hp))
+ hist_source->line - (histptr - hp), *hp))
goto hist_trunc_abort;
++hp;
}
- /* now unlock, close both, rename, rinse, repeat */
+ /* now transfer back */
+ if (!hist_persist_back(fd)) {
+ /* success! */
+ hs = hist_use_it;
+ }
+ hist_trunc_abort:
+ /* remove temporary file */
close(fd);
fd = -1;
- hist_finish();
- if (rename(nhname, hname) < 0) {
- hist_trunc_abort:
- if (fd != -1)
- close(fd);
- unlink(nhname);
- if (fd != -1)
- goto hist_trunc_dont;
- /* darn! restore histfd and pray */
- }
- hs = hist_init_restore;
+ unlink(nhname);
+ /* use whatever is in the file now */
hist_trunc_dont:
afree(nhname, ATEMP);
- if (hs == hist_init_restore)
- goto retry;
+ if (hs == hist_use_it)
+ goto hist_trunc_done;
+ goto hist_init_fail;
}
} else if (histfsize != 0) {
/* negative or too small... */
@@ -806,7 +867,7 @@
goto retry;
}
if (hs != hist_init_retry)
- bi_errorf("can't %s %s: %s",
+ bi_errorf(Tf_cant_ss_s,
"unlink HISTFILE", hname, cstrerror(errno));
histfsize = 0;
return;
@@ -817,10 +878,27 @@
return;
}
}
+ hist_trunc_done:
histfsize = lseek(histfd, (off_t)0, SEEK_END);
hist_init_tail:
mksh_unlkfd(histfd);
+}
#endif
+
+void
+hist_init(Source *s)
+{
+ histsave(NULL, NULL, HIST_DISCARD, true);
+
+ if (Flag(FTALKING) == 0)
+ return;
+
+ hstarted = true;
+ hist_source = s;
+
+#if HAVE_PERSISTENT_HISTORY
+ hist_persist_init();
+#endif
}
#if HAVE_PERSISTENT_HISTORY
@@ -854,13 +932,12 @@
if (lno >= s->line - (histptr - history) && lno <= s->line) {
hp = &histptr[lno - s->line];
- if (*hp)
- afree(*hp, APERM);
+ afree(*hp, APERM);
strdupx(*hp, (char *)(base + 4), APERM);
}
} else {
s->line = lno--;
- histsave(&lno, (char *)(base + 4), false, false);
+ histsave(&lno, (char *)(base + 4), HIST_NOTE, false);
}
/* advance base pointer past NUL */
bytes -= ++cp - base;
@@ -887,10 +964,11 @@
mksh_lockfd(histfd);
sizenow = lseek(histfd, (off_t)0, SEEK_END);
if (sizenow < histfsize) {
- /* the file has shrunk; give up */
- goto bad;
- }
- if (
+ /* the file has shrunk; trust it just appending the new data */
+ /* well, for now, anyway… since mksh strdups all into memory */
+ /* we can use a nicer approach some time later… */
+ ;
+ } else if (
/* ignore changes when the file is too large */
sizenow <= MKSH_MAXHISTFSIZE
&&
@@ -972,59 +1050,71 @@
{
int i;
const char *cs;
+#if !HAVE_SYS_SIGNAME
+ const struct mksh_sigpair *pair;
+#endif
trap_exstat = -1;
- /* Populate sigtraps based on sys_signame and sys_siglist. */
- /*XXX this is idiotic, use a multi-key/value hashtable! */
- for (i = 0; i <= NSIG; i++) {
+ /* populate sigtraps based on sys_signame and sys_siglist */
+ for (i = 1; i < ksh_NSIG; i++) {
sigtraps[i].signal = i;
- if (i == ksh_SIGERR) {
- sigtraps[i].name = "ERR";
- sigtraps[i].mess = "Error handler";
- } else {
#if HAVE_SYS_SIGNAME
- cs = sys_signame[i];
+ cs = sys_signame[i];
#else
- const struct mksh_sigpair *pair = mksh_sigpairs;
- while ((pair->nr != i) && (pair->name != NULL))
- ++pair;
- cs = pair->name;
+ pair = mksh_sigpairs;
+ while ((pair->nr != i) && (pair->name != NULL))
+ ++pair;
+ cs = pair->name;
#endif
- if ((cs == NULL) ||
- (cs[0] == '\0'))
- sigtraps[i].name = shf_smprintf("%d", i);
- else {
- char *s;
+ if ((cs == NULL) ||
+ (cs[0] == '\0'))
+ sigtraps[i].name = null;
+ else {
+ char *s;
- /* this is not optimal, what about SIGSIG1? */
- if ((cs[0] & 0xDF) == 'S' &&
- (cs[1] & 0xDF) == 'I' &&
- (cs[2] & 0xDF) == 'G' &&
- cs[3] != '\0') {
- /* skip leading "SIG" */
- cs += 3;
- }
- strdupx(s, cs, APERM);
- sigtraps[i].name = s;
- while ((*s = ksh_toupper(*s)))
- ++s;
+ /* this is not optimal, what about SIGSIG1? */
+ if (ksh_eq(cs[0], 'S', 's') &&
+ ksh_eq(cs[1], 'I', 'i') &&
+ ksh_eq(cs[2], 'G', 'g') &&
+ cs[3] != '\0') {
+ /* skip leading "SIG" */
+ cs += 3;
}
+ strdupx(s, cs, APERM);
+ sigtraps[i].name = s;
+ while ((*s = ksh_toupper(*s)))
+ ++s;
+ /* check for reserved names */
+ if (!strcmp(sigtraps[i].name, "EXIT") ||
+ !strcmp(sigtraps[i].name, "ERR")) {
+#ifndef MKSH_SMALL
+ internal_warningf(Tinvname, sigtraps[i].name,
+ "signal");
+#endif
+ sigtraps[i].name = null;
+ }
+ }
+ if (sigtraps[i].name == null)
+ sigtraps[i].name = shf_smprintf(Tf_d, i);
#if HAVE_SYS_SIGLIST
- sigtraps[i].mess = sys_siglist[i];
+ sigtraps[i].mess = sys_siglist[i];
#elif HAVE_STRSIGNAL
- sigtraps[i].mess = strsignal(i);
+ sigtraps[i].mess = strsignal(i);
#else
- sigtraps[i].mess = NULL;
+ sigtraps[i].mess = NULL;
#endif
- if ((sigtraps[i].mess == NULL) ||
- (sigtraps[i].mess[0] == '\0'))
- sigtraps[i].mess = shf_smprintf("%s %d",
- "Signal", i);
- }
+ if ((sigtraps[i].mess == NULL) ||
+ (sigtraps[i].mess[0] == '\0'))
+ sigtraps[i].mess = shf_smprintf(Tf_sd,
+ "Signal", i);
}
- /* our name for signal 0 */
+ sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT;
sigtraps[ksh_SIGEXIT].name = "EXIT";
+ sigtraps[ksh_SIGEXIT].mess = "Exit trap";
+ sigtraps[ksh_SIGERR].signal = ksh_SIGERR;
+ sigtraps[ksh_SIGERR].name = "ERR";
+ sigtraps[ksh_SIGERR].mess = "Error handler";
(void)sigemptyset(&Sigact_ign.sa_mask);
Sigact_ign.sa_flags = 0; /* interruptible */
@@ -1072,21 +1162,24 @@
}
Trap *
-gettrap(const char *cs, bool igncase)
+gettrap(const char *cs, bool igncase, bool allsigs)
{
int i;
Trap *p;
char *as;
- if (ksh_isdigit(*cs)) {
- return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
+ /* signal number (1..ksh_NSIG) or 0? */
+
+ if (ctype(*cs, C_DIGIT))
+ return ((getn(cs, &i) && 0 <= i && i < ksh_NSIG) ?
(&sigtraps[i]) : NULL);
- }
+ /* do a lookup by name then */
+
/* this breaks SIGSIG1, but we do that above anyway */
- if ((cs[0] & 0xDF) == 'S' &&
- (cs[1] & 0xDF) == 'I' &&
- (cs[2] & 0xDF) == 'G' &&
+ if (ksh_eq(cs[0], 'S', 's') &&
+ ksh_eq(cs[1], 'I', 'i') &&
+ ksh_eq(cs[2], 'G', 'g') &&
cs[3] != '\0') {
/* skip leading "SIG" */
cs += 3;
@@ -1101,14 +1194,24 @@
} else
as = NULL;
+ /* this is idiotic, we really want a hashtable here */
+
p = sigtraps;
- for (i = 0; i <= NSIG; i++) {
+ i = ksh_NSIG + 1;
+ do {
if (!strcmp(p->name, cs))
goto found;
++p;
+ } while (--i);
+ goto notfound;
+
+ found:
+ if (!allsigs) {
+ if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) {
+ notfound:
+ p = NULL;
+ }
}
- p = NULL;
- found:
afree(as, ATEMP);
return (p);
}
@@ -1152,14 +1255,16 @@
int
fatal_trap_check(void)
{
- int i;
- Trap *p;
+ Trap *p = sigtraps;
+ int i = ksh_NSIG + 1;
/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
+ do {
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
/* return value is used as an exit code */
- return (128 + p->signal);
+ return (ksh_sigmask(p->signal));
+ ++p;
+ } while (--i);
return (0);
}
@@ -1171,13 +1276,15 @@
int
trap_pending(void)
{
- int i;
- Trap *p;
+ Trap *p = sigtraps;
+ int i = ksh_NSIG + 1;
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
+ do {
if (p->set && ((p->trap && p->trap[0]) ||
((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
return (p->signal);
+ ++p;
+ } while (--i);
return (0);
}
@@ -1188,8 +1295,8 @@
void
runtraps(int flag)
{
- int i;
- Trap *p;
+ Trap *p = sigtraps;
+ int i = ksh_NSIG + 1;
if (ksh_tmout_state == TMOUT_LEAVING) {
ksh_tmout_state = TMOUT_EXECUTING;
@@ -1208,10 +1315,12 @@
if (flag & TF_FATAL)
fatal_trap = 0;
++trap_nested;
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
+ do {
if (p->set && (!flag ||
((p->flags & flag) && p->trap == NULL)))
runtrap(p, false);
+ ++p;
+ } while (--i);
if (!--trap_nested)
runtrap(NULL, true);
}
@@ -1230,16 +1339,17 @@
p->set = 0;
if (trapstr == NULL) {
/* SIG_DFL */
- if (p->flags & TF_FATAL) {
- /* eg, SIGHUP */
- exstat = (int)ksh_min(128U + (unsigned)i, 255U);
+ if (p->flags & (TF_FATAL | TF_DFL_INTR)) {
+ exstat = (int)(128U + (unsigned)i);
+ if ((unsigned)exstat > 255U)
+ exstat = 255;
+ }
+ /* e.g. SIGHUP */
+ if (p->flags & TF_FATAL)
unwind(LLEAVE);
- }
- if (p->flags & TF_DFL_INTR) {
- /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
- exstat = (int)ksh_min(128U + (unsigned)i, 255U);
+ /* e.g. SIGINT, SIGQUIT, SIGTERM, etc. */
+ if (p->flags & TF_DFL_INTR)
unwind(LINTR);
- }
goto donetrap;
}
if (trapstr[0] == '\0')
@@ -1279,17 +1389,19 @@
void
cleartraps(void)
{
- int i;
- Trap *p;
+ Trap *p = sigtraps;
+ int i = ksh_NSIG + 1;
trap = 0;
intrsig = 0;
fatal_trap = 0;
- for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
+
+ do {
p->set = 0;
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
settrap(p, NULL);
- }
+ ++p;
+ } while (--i);
}
/* restore signals just before an exec(2) */
@@ -1296,13 +1408,15 @@
void
restoresigs(void)
{
- int i;
- Trap *p;
+ Trap *p = sigtraps;
+ int i = ksh_NSIG + 1;
- for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
+ do {
if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
+ ++p;
+ } while (--i);
}
void
@@ -1310,8 +1424,7 @@
{
sig_t f;
- if (p->trap)
- afree(p->trap, APERM);
+ afree(p->trap, APERM);
/* handles s == NULL */
strdupx(p->trap, s, APERM);
p->flags |= TF_CHANGED;
@@ -1342,33 +1455,32 @@
}
/*
- * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
- * kill shell (unless user catches it and exits)
+ * called by c_print() when writing to a co-process to ensure
+ * SIGPIPE won't kill shell (unless user catches it and exits)
*/
-int
+bool
block_pipe(void)
{
- int restore_dfl = 0;
+ bool restore_dfl = false;
Trap *p = &sigtraps[SIGPIPE];
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
setsig(p, SIG_IGN, SS_RESTORE_CURR);
if (p->flags & TF_ORIG_DFL)
- restore_dfl = 1;
+ restore_dfl = true;
} else if (p->cursig == SIG_DFL) {
setsig(p, SIG_IGN, SS_RESTORE_CURR);
/* restore to SIG_DFL */
- restore_dfl = 1;
+ restore_dfl = true;
}
return (restore_dfl);
}
-/* Called by c_print() to undo whatever block_pipe() did */
+/* called by c_print() to undo whatever block_pipe() did */
void
-restore_pipe(int restore_dfl)
+restore_pipe(void)
{
- if (restore_dfl)
- setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
+ setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
}
/*
Copied: trunk/contrib/mksh/jehanne.c (from rev 12140, vendor/MirOS/mksh/dist/jehanne.c)
===================================================================
--- trunk/contrib/mksh/jehanne.c (rev 0)
+++ trunk/contrib/mksh/jehanne.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2017
+ * Giacomo Tesio <giacomo at tesio.it>
+ *
+ * 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.
+ *-
+ * Initialisation code for the Jehanne operating system (a Plan 9 de-
+ * rivative, using GCC)
+ */
+
+static const char __rcsid[] __attribute__((__used__)) =
+ "$MirOS: src/bin/mksh/jehanne.c,v 1.1 2017/12/22 16:30:00 tg Exp $";
+
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+
+void
+__application_newlib_init(int argc, char *argv[])
+{
+ rfork(RFFDG | RFREND | RFNOTEG);
+ libposix_emulate_SIGCHLD();
+}
Modified: trunk/contrib/mksh/jobs.c
===================================================================
--- trunk/contrib/mksh/jobs.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/jobs.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-/* $OpenBSD: jobs.c,v 1.40 2013/09/04 15:49:18 millert Exp $ */
+/* $OpenBSD: jobs.c,v 1.43 2015/09/10 22:48:58 nicm Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- * 2012, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2012, 2013, 2014, 2015, 2016, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.105.2.1 2015/01/25 15:44:06 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.125 2018/01/05 20:08:34 tg Exp $");
#if HAVE_KILLPG
#define mksh_killpg killpg
@@ -39,14 +39,27 @@
#define PSTOPPED 3
typedef struct proc Proc;
+/* to take alignment into consideration */
+struct proc_dummy {
+ Proc *next;
+ pid_t pid;
+ int state;
+ int status;
+ char command[128];
+};
+/* real structure */
struct proc {
- Proc *next; /* next process in pipeline (if any) */
- pid_t pid; /* process id */
+ /* next process in pipeline (if any) */
+ Proc *next;
+ /* process id of this Unix process in the job */
+ pid_t pid;
+ /* one of the four P… above */
int state;
- int status; /* wait status */
+ /* wait status */
+ int status;
/* process command string from vistree */
- char command[256 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) +
- 2 * sizeof(int))];
+ char command[256 - (ALLOC_OVERHEAD +
+ offsetof(struct proc_dummy, command[0]))];
};
/* Notify/print flag - j_print() argument */
@@ -86,7 +99,7 @@
int flags; /* see JF_* */
volatile int state; /* job state */
int status; /* exit status of last process */
- int32_t age; /* number of jobs started */
+ int age; /* number of jobs started */
Coproc_id coproc_id; /* 0 or id of coprocess output pipe */
#ifndef MKSH_UNEMPLOYED
mksh_ttyst ttystat; /* saved tty state for stopped jobs */
@@ -118,7 +131,7 @@
static pid_t async_pid;
static int nzombie; /* # of zombies owned by this process */
-static int32_t njobs; /* # of jobs started */
+static int njobs; /* # of jobs started */
#ifndef CHILD_MAX
#define CHILD_MAX 25
@@ -217,9 +230,9 @@
{
switch (p->state) {
case PEXITED:
- return (WEXITSTATUS(p->status));
+ return ((WEXITSTATUS(p->status)) & 255);
case PSIGNALLED:
- return (128 + WTERMSIG(p->status));
+ return (ksh_sigmask(WTERMSIG(p->status)));
default:
return (0);
}
@@ -238,11 +251,11 @@
mksh_tcset(tty_fd, &tty_state);
if (restore_ttypgrp >= 0) {
if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) {
- warningf(false, "%s: %s %s: %s", "j_suspend",
- "tcsetpgrp", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds,
+ Tj_suspend, "tcsetpgrp", cstrerror(errno));
} else if (setpgid(0, restore_ttypgrp) < 0) {
- warningf(false, "%s: %s %s: %s", "j_suspend",
- "setpgid", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds,
+ Tj_suspend, "setpgid", cstrerror(errno));
}
}
}
@@ -259,12 +272,12 @@
if (ttypgrp_ok) {
if (restore_ttypgrp >= 0) {
if (setpgid(0, kshpid) < 0) {
- warningf(false, "%s: %s %s: %s", "j_suspend",
- "setpgid", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds,
+ Tj_suspend, "setpgid", cstrerror(errno));
ttypgrp_ok = false;
} else if (tcsetpgrp(tty_fd, kshpid) < 0) {
- warningf(false, "%s: %s %s: %s", "j_suspend",
- "tcsetpgrp", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds,
+ Tj_suspend, "tcsetpgrp", cstrerror(errno));
ttypgrp_ok = false;
}
}
@@ -349,8 +362,8 @@
pid_t ttypgrp;
if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
- warningf(false, "%s: %s %s: %s",
- "j_init", "tcgetpgrp", "failed",
+ warningf(false, Tf_ssfaileds,
+ "j_init", "tcgetpgrp",
cstrerror(errno));
ttypgrp_ok = false;
break;
@@ -365,13 +378,13 @@
SS_RESTORE_DFL|SS_FORCE);
if (ttypgrp_ok && kshpgrp != kshpid) {
if (setpgid(0, kshpid) < 0) {
- warningf(false, "%s: %s %s: %s", "j_init",
- "setpgid", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds,
+ "j_init", "setpgid", cstrerror(errno));
ttypgrp_ok = false;
} else {
if (tcsetpgrp(tty_fd, kshpid) < 0) {
- warningf(false, "%s: %s %s: %s",
- "j_init", "tcsetpgrp", "failed",
+ warningf(false, Tf_ssfaileds,
+ "j_init", "tcsetpgrp",
cstrerror(errno));
ttypgrp_ok = false;
} else
@@ -381,7 +394,7 @@
}
#ifndef MKSH_DISABLE_TTY_WARNING
if (use_tty && !ttypgrp_ok)
- warningf(false, "%s: %s", "warning",
+ warningf(false, Tf_sD_s, "warning",
"won't have full job control");
#endif
} else {
@@ -415,7 +428,7 @@
errno = 0;
/* this is gonna annoy users; complain to your distro, people! */
if (nice(ness) == -1 && (eno = errno) != 0)
- warningf(false, "%s: %s", "bgnice", cstrerror(eno));
+ warningf(false, Tf_sD_s, "bgnice", cstrerror(eno));
#else
(void)nice(ness);
#endif
@@ -467,8 +480,7 @@
if (flags & XPIPEI) {
/* continuing with a pipe */
if (!last_job)
- internal_errorf("%s %d",
- "exchild: XPIPEI and no last_job - pid",
+ internal_errorf("exchild: XPIPEI and no last_job - pid %d",
(int)procpid);
j = last_job;
if (last_proc)
@@ -601,7 +613,7 @@
#ifndef MKSH_SMALL
if (t->type == TPIPE)
unwind(LLEAVE);
- internal_warningf("%s: %s", "exchild", "execute() returned");
+ internal_warningf("%s: execute() returned", "exchild");
fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
"exchild", t);
shf_flush(shl_out);
@@ -626,7 +638,7 @@
if (Flag(FTALKING)) {
shf_fprintf(shl_out, "[%d]", j->job);
for (p = j->proc_list; p; p = p->next)
- shf_fprintf(shl_out, " %d",
+ shf_fprintf(shl_out, Tf__d,
(int)p->pid);
shf_putchar('\n', shl_out);
shf_flush(shl_out);
@@ -678,9 +690,9 @@
j = last_job;
if (!j || !(j->flags & JF_STARTED)) {
if (!j)
- warningf(true, "%s: %s", "waitlast", "no last job");
+ warningf(true, Tf_sD_s, "waitlast", "no last job");
else
- internal_warningf("%s: %s", "waitlast", "not started");
+ internal_warningf(Tf_sD_s, "waitlast", Tnot_started);
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
@@ -740,7 +752,7 @@
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
if (ecode != JL_NOSUCH)
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (-1);
}
@@ -753,7 +765,7 @@
if (rv < 0)
/* we were interrupted */
- *sigp = 128 + -rv;
+ *sigp = ksh_sigmask(-rv);
return (rv);
}
@@ -774,7 +786,7 @@
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (1);
}
@@ -781,7 +793,7 @@
if (j->pgrp == 0) {
/* started when !Flag(FMONITOR) */
if (kill_job(j, sig) < 0) {
- bi_errorf("%s: %s", cp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errno));
rv = 1;
}
} else {
@@ -790,7 +802,7 @@
mksh_killpg(j->pgrp, SIGCONT);
#endif
if (mksh_killpg(j->pgrp, sig) < 0) {
- bi_errorf("%s: %s", cp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errno));
rv = 1;
}
}
@@ -817,7 +829,7 @@
if ((j = j_lookup(cp, &ecode)) == NULL) {
sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (1);
}
@@ -862,10 +874,10 @@
if (j->flags & JF_SAVEDTTY)
mksh_tcset(tty_fd, &tty_state);
sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s %s(%d, %ld) %s: %s",
- "1st", "tcsetpgrp", tty_fd,
+ bi_errorf(Tf_ldfailed,
+ "fg: 1st", "tcsetpgrp", tty_fd,
(long)((j->flags & JF_SAVEDTTYPGRP) ?
- j->saved_ttypgrp : j->pgrp), "failed",
+ j->saved_ttypgrp : j->pgrp),
cstrerror(rv));
return (1);
}
@@ -884,12 +896,12 @@
if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
mksh_tcset(tty_fd, &tty_state);
if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
- warningf(true, "%s %s(%d, %ld) %s: %s",
+ warningf(true, Tf_ldfailed,
"fg: 2nd", "tcsetpgrp", tty_fd,
- (long)kshpgrp, "failed", cstrerror(errno));
+ (long)kshpgrp, cstrerror(errno));
}
sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s %s %s", "can't continue job",
+ bi_errorf(Tf_s_sD_s, "can't continue job",
cp, cstrerror(eno));
return (1);
}
@@ -958,7 +970,7 @@
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (1);
}
} else
@@ -978,7 +990,7 @@
for (j = job_list; j; j = tmp) {
tmp = j->next;
if (j->flags & JF_REMOVE)
- remove_job(j, "jobs");
+ remove_job(j, Tjobs);
}
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
@@ -1010,8 +1022,14 @@
}
for (j = job_list; j; j = tmp) {
tmp = j->next;
- if (j->flags & JF_REMOVE)
- remove_job(j, "notify");
+ if (j->flags & JF_REMOVE) {
+ if (j == async_job || (j->flags & JF_KNOWN)) {
+ j->flags = (j->flags & ~JF_REMOVE) | JF_ZOMBIE;
+ j->job = -1;
+ nzombie++;
+ } else
+ remove_job(j, "notify");
+ }
}
shf_flush(shl_out);
#ifndef MKSH_NOPROSPECTOFWORK
@@ -1052,7 +1070,7 @@
if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
remove_job(async_job, "async");
if (!(j->flags & JF_STARTED)) {
- internal_warningf("%s: %s", "j_async", "job not started");
+ internal_warningf(Tf_sD_s, "j_async", Tjob_not_started);
return;
}
async_job = j;
@@ -1111,6 +1129,7 @@
int flags,
const char *where)
{
+ Proc *p;
int rv;
#ifdef MKSH_NO_SIGSUSPEND
sigset_t omask;
@@ -1175,9 +1194,9 @@
(j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
j->flags |= JF_SAVEDTTYPGRP;
if (tcsetpgrp(tty_fd, kshpgrp) < 0)
- warningf(true, "%s %s(%d, %ld) %s: %s",
+ warningf(true, Tf_ldfailed,
"j_waitj:", "tcsetpgrp", tty_fd,
- (long)kshpgrp, "failed", cstrerror(errno));
+ (long)kshpgrp, cstrerror(errno));
if (j->state == PSTOPPED) {
j->flags |= JF_SAVEDTTY;
mksh_tcget(tty_fd, &j->ttystat);
@@ -1222,14 +1241,14 @@
* even when not monitoring, but this doesn't make sense since
* a tty generated ^C goes to the whole process group)
*/
- {
- int status;
+ if (Flag(FMONITOR) && j->state == PSIGNALLED &&
+ WIFSIGNALED(j->last_proc->status)) {
+ int termsig;
- status = j->last_proc->status;
- if (Flag(FMONITOR) && j->state == PSIGNALLED &&
- WIFSIGNALED(status) &&
- (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))
- trapsig(WTERMSIG(status));
+ if ((termsig = WTERMSIG(j->last_proc->status)) > 0 &&
+ termsig < ksh_NSIG &&
+ (sigtraps[termsig].flags & TF_TTY_INTR))
+ trapsig(termsig);
}
#endif
}
@@ -1238,9 +1257,10 @@
j_systime = j->systime;
rv = j->status;
- if ((flags & JW_PIPEST) && (j->proc_list != NULL)) {
+ if (!(p = j->proc_list)) {
+ ; /* nothing */
+ } else if (flags & JW_PIPEST) {
uint32_t num = 0;
- Proc *p = j->proc_list;
struct tbl *vp;
unset(vp_pipest, 1);
@@ -1270,15 +1290,13 @@
rv = vp->val.i;
p = p->next;
}
- } else if (Flag(FPIPEFAIL) && (j->proc_list != NULL)) {
- Proc *p = j->proc_list;
- int i;
+ } else if (Flag(FPIPEFAIL)) {
+ do {
+ const int i = proc_errorlevel(p);
- while (p != NULL) {
- if ((i = proc_errorlevel(p)))
+ if (i)
rv = i;
- p = p->next;
- }
+ } while ((p = p->next));
}
if (!(flags & JW_ASYNCNOTIFY)
@@ -1426,8 +1444,8 @@
/* XXX debugging (nasty - interrupt routine using shl_out) */
if (!(j->flags & JF_STARTED)) {
- internal_warningf("check_job: job started (flags 0x%x)",
- j->flags);
+ internal_warningf("check_job: job started (flags 0x%X)",
+ (unsigned int)j->flags);
return;
}
@@ -1527,7 +1545,9 @@
Proc *p;
int state;
int status;
- int coredumped;
+#ifdef WCOREDUMP
+ bool coredumped;
+#endif
char jobchar = ' ';
char buf[64];
const char *filler;
@@ -1539,7 +1559,7 @@
* group leader (ie, !FMONITOR). We arbitrarily return
* last pid (which is what $! returns).
*/
- shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
+ shf_fprintf(shf, Tf_dN, (int)(j->pgrp ? j->pgrp :
(j->last_proc ? j->last_proc->pid : 0)));
return;
}
@@ -1551,41 +1571,54 @@
jobchar = '-';
for (p = j->proc_list; p != NULL;) {
- coredumped = 0;
+#ifdef WCOREDUMP
+ coredumped = false;
+#endif
switch (p->state) {
case PRUNNING:
memcpy(buf, "Running", 8);
break;
- case PSTOPPED:
- strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess,
- sizeof(buf));
+ case PSTOPPED: {
+ int stopsig = WSTOPSIG(p->status);
+
+ strlcpy(buf, stopsig > 0 && stopsig < ksh_NSIG ?
+ sigtraps[stopsig].mess : "Stopped", sizeof(buf));
break;
- case PEXITED:
+ }
+ case PEXITED: {
+ int exitstatus = (WEXITSTATUS(p->status)) & 255;
+
if (how == JP_SHORT)
buf[0] = '\0';
- else if (WEXITSTATUS(p->status) == 0)
+ else if (exitstatus == 0)
memcpy(buf, "Done", 5);
else
shf_snprintf(buf, sizeof(buf), "Done (%d)",
- WEXITSTATUS(p->status));
+ exitstatus);
break;
- case PSIGNALLED:
+ }
+ case PSIGNALLED: {
+ int termsig = WTERMSIG(p->status);
#ifdef WCOREDUMP
if (WCOREDUMP(p->status))
- coredumped = 1;
+ coredumped = true;
#endif
/*
* kludge for not reporting 'normal termination
* signals' (i.e. SIGINT, SIGPIPE)
*/
- if (how == JP_SHORT && !coredumped &&
- (WTERMSIG(p->status) == SIGINT ||
- WTERMSIG(p->status) == SIGPIPE)) {
+ if (how == JP_SHORT &&
+#ifdef WCOREDUMP
+ !coredumped &&
+#endif
+ (termsig == SIGINT || termsig == SIGPIPE)) {
buf[0] = '\0';
} else
- strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,
+ strlcpy(buf, termsig > 0 && termsig < ksh_NSIG ?
+ sigtraps[termsig].mess : "Signalled",
sizeof(buf));
break;
+ }
default:
buf[0] = '\0';
}
@@ -1603,14 +1636,22 @@
if (how == JP_SHORT) {
if (buf[0]) {
output = 1;
+#ifdef WCOREDUMP
shf_fprintf(shf, "%s%s ",
buf, coredumped ? " (core dumped)" : null);
+#else
+ shf_puts(buf, shf);
+ shf_putchar(' ', shf);
+#endif
}
} else {
output = 1;
shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
p->next ? "|" : null,
- coredumped ? " (core dumped)" : null);
+#ifdef WCOREDUMP
+ coredumped ? " (core dumped)" :
+#endif
+ null);
}
state = p->state;
@@ -1619,10 +1660,10 @@
while (p && p->state == state && p->status == status) {
if (how == JP_LONG)
shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
- (int)p->pid, " ", p->command,
+ (int)p->pid, T1space, p->command,
p->next ? "|" : null);
else if (how == JP_MEDIUM)
- shf_fprintf(shf, " %s%s", p->command,
+ shf_fprintf(shf, Tf__ss, p->command,
p->next ? "|" : null);
p = p->next;
}
@@ -1644,8 +1685,7 @@
size_t len;
int job = 0;
- if (ksh_isdigit(*cp)) {
- getn(cp, &job);
+ if (ctype(*cp, C_DIGIT) && getn(cp, &job)) {
/* Look for last_proc->pid (what $! returns) first... */
for (j = job_list; j != NULL; j = j->next)
if (j->last_proc && j->last_proc->pid == job)
@@ -1657,11 +1697,10 @@
for (j = job_list; j != NULL; j = j->next)
if (j->pgrp && j->pgrp == job)
return (j);
- if (ecodep)
- *ecodep = JL_NOSUCH;
- return (NULL);
+ goto j_lookup_nosuch;
}
if (*cp != '%') {
+ j_lookup_invalid:
if (ecodep)
*ecodep = JL_INVALID;
return (NULL);
@@ -1681,7 +1720,8 @@
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- getn(cp, &job);
+ if (!getn(cp, &job))
+ goto j_lookup_invalid;
for (j = job_list; j != NULL; j = j->next)
if (j->job == job)
return (j);
@@ -1721,6 +1761,7 @@
return (last_match);
break;
}
+ j_lookup_nosuch:
if (ecodep)
*ecodep = JL_NOSUCH;
return (NULL);
@@ -1796,7 +1837,7 @@
curr = *prev;
}
if (curr != j) {
- internal_warningf("remove_job: job %s (%s)", "not found", where);
+ internal_warningf("remove_job: job %s (%s)", Tnot_found, where);
return;
}
*prev = curr->next;
@@ -1885,22 +1926,22 @@
break;
case 1:
#ifndef MKSH_DISABLE_TTY_WARNING
- warningf(false, "%s: %s %s: %s",
- "No controlling tty", "open", "/dev/tty",
- cstrerror(errno));
+ warningf(false, Tf_sD_s_sD_s,
+ "No controlling tty", Topen, T_devtty, cstrerror(errno));
#endif
break;
case 2:
#ifndef MKSH_DISABLE_TTY_WARNING
- warningf(false, "%s: %s", "can't find tty fd", cstrerror(errno));
+ warningf(false, Tf_sD_s_s, Tcant_find, Ttty_fd,
+ cstrerror(errno));
#endif
break;
case 3:
- warningf(false, "%s: %s %s: %s", "j_ttyinit",
- "dup of tty fd", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds, "j_ttyinit",
+ Ttty_fd_dupof, cstrerror(errno));
break;
case 4:
- warningf(false, "%s: %s: %s", "j_ttyinit",
+ warningf(false, Tf_sD_sD_s, "j_ttyinit",
"can't set close-on-exec flag", cstrerror(errno));
break;
}
Modified: trunk/contrib/mksh/lalloc.c
===================================================================
--- trunk/contrib/mksh/lalloc.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/lalloc.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,6 +1,6 @@
/*-
- * Copyright (c) 2009, 2010, 2011, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * Copyright (c) 2009, 2010, 2011, 2013, 2014, 2016
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -19,8 +19,11 @@
*/
#include "sh.h"
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+#include <err.h>
+#endif
-__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.20.2.1 2015/01/25 15:35:47 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.26 2016/02/26 21:53:36 tg Exp $");
/* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */
#if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0)
@@ -29,19 +32,72 @@
#define remalloc(p,n) realloc_osi((p), (n))
#endif
-#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % ALLOC_SIZE)
-static ALLOC_ITEM *findptr(ALLOC_ITEM **, char *, Area *);
+static struct lalloc_common *findptr(struct lalloc_common **, char *, Area *);
+#ifndef MKSH_ALLOC_CATCH_UNDERRUNS
+#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % sizeof(struct lalloc_common))
+#else
+#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) & 4095)
+#undef remalloc
+#undef free_osimalloc
+
+static void
+free_osimalloc(void *ptr)
+{
+ struct lalloc_item *lp = ptr;
+
+ if (munmap(lp, lp->len))
+ err(1, "free_osimalloc");
+}
+
+static void *
+remalloc(void *ptr, size_t size)
+{
+ struct lalloc_item *lp, *lold = ptr;
+
+ size = (size + 4095) & ~(size_t)4095;
+
+ if (lold && lold->len >= size)
+ return (ptr);
+
+ if ((lp = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, (off_t)0)) == MAP_FAILED)
+ err(1, "remalloc: mmap(%zu)", size);
+ if (ALLOC_ISUNALIGNED(lp))
+ errx(1, "remalloc: unaligned(%p)", lp);
+ if (mprotect(((char *)lp) + 4096, 4096, PROT_NONE))
+ err(1, "remalloc: mprotect");
+ lp->len = size;
+
+ if (lold) {
+ memcpy(((char *)lp) + 8192, ((char *)lold) + 8192,
+ lold->len - 8192);
+ if (munmap(lold, lold->len))
+ err(1, "remalloc: munmap");
+ }
+
+ return (lp);
+}
+#endif
+
void
ainit(Area *ap)
{
- /* area pointer is an ALLOC_ITEM, just the head of the list */
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+ if (sysconf(_SC_PAGESIZE) != 4096) {
+ fprintf(stderr, "mksh: fatal: pagesize %lu not 4096!\n",
+ sysconf(_SC_PAGESIZE));
+ fflush(stderr);
+ abort();
+ }
+#endif
+ /* area pointer and items share struct lalloc_common */
ap->next = NULL;
}
-static ALLOC_ITEM *
-findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap)
+static struct lalloc_common *
+findptr(struct lalloc_common **lpp, char *ptr, Area *ap)
{
void *lp;
@@ -51,10 +107,10 @@
#endif
/* get address of ALLOC_ITEM from user item */
/*
- * note: the alignment of "ptr" to ALLOC_SIZE is checked
+ * note: the alignment of "ptr" to ALLOC_ITEM is checked
* above; the "void *" gets us rid of a gcc 2.95 warning
*/
- *lpp = (lp = ptr - ALLOC_SIZE);
+ *lpp = (lp = ptr - sizeof(ALLOC_ITEM));
/* search for allocation item in group list */
while (ap->next != lp)
if ((ap = ap->next) == NULL) {
@@ -84,28 +140,28 @@
void *
aresize(void *ptr, size_t numb, Area *ap)
{
- ALLOC_ITEM *lp = NULL;
+ struct lalloc_common *lp = NULL;
/* resizing (true) or newly allocating? */
if (ptr != NULL) {
- ALLOC_ITEM *pp;
+ struct lalloc_common *pp;
pp = findptr(&lp, ptr, ap);
pp->next = lp->next;
}
- if (notoktoadd(numb, ALLOC_SIZE) ||
- (lp = remalloc(lp, numb + ALLOC_SIZE)) == NULL
+ if (notoktoadd(numb, sizeof(ALLOC_ITEM)) ||
+ (lp = remalloc(lp, numb + sizeof(ALLOC_ITEM))) == NULL
#ifndef MKSH_SMALL
|| ALLOC_ISUNALIGNED(lp)
#endif
)
internal_errorf(Toomem, numb);
- /* this only works because Area is an ALLOC_ITEM */
+ /* area pointer and items share struct lalloc_common */
lp->next = ap->next;
ap->next = lp;
/* return user item address */
- return ((char *)lp + ALLOC_SIZE);
+ return ((char *)lp + sizeof(ALLOC_ITEM));
}
void
@@ -112,7 +168,7 @@
afree(void *ptr, Area *ap)
{
if (ptr != NULL) {
- ALLOC_ITEM *lp, *pp;
+ struct lalloc_common *lp, *pp;
pp = findptr(&lp, ptr, ap);
/* unhook */
@@ -125,7 +181,7 @@
void
afreeall(Area *ap)
{
- ALLOC_ITEM *lp;
+ struct lalloc_common *lp;
/* traverse group (linked list) */
while ((lp = ap->next) != NULL) {
Modified: trunk/contrib/mksh/lex.c
===================================================================
--- trunk/contrib/mksh/lex.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/lex.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-/* $OpenBSD: lex.c,v 1.49 2013/12/17 16:37:06 deraadt Exp $ */
+/* $OpenBSD: lex.c,v 1.51 2015/09/10 22:48:58 nicm Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193.2.1 2015/01/11 22:39:50 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.247 2018/01/14 01:44:01 tg Exp $");
/*
* states while lexing word
@@ -38,8 +38,8 @@
#define SQBRACE 7 /* inside "${}" */
#define SBQUOTE 8 /* inside `` */
#define SASPAREN 9 /* inside $(( )) */
-#define SHEREDELIM 10 /* parsing <<,<<-,<<< delimiter */
-#define SHEREDQUOTE 11 /* parsing " in <<,<<-,<<< delimiter */
+#define SHEREDELIM 10 /* parsing << or <<- delimiter */
+#define SHEREDQUOTE 11 /* parsing " in << or <<- delimiter */
#define SPATTERN 12 /* parsing *(...|...) pattern (*+?@!) */
#define SADELIM 13 /* like SBASE, looking for delimiter */
#define STBRACEKORN 14 /* parsing ${...[#%]...} !FSH */
@@ -61,7 +61,7 @@
/* point to the next state block */
struct lex_state *base;
/* marks start of state output in output string */
- int start;
+ size_t start;
/* SBQUOTE: true if in double quotes: "`...`" */
/* SEQUOTE: got NUL, ignore rest of string */
bool abool;
@@ -94,11 +94,10 @@
static int getsc_uu(void);
static void getsc_line(Source *);
static int getsc_bn(void);
-static int s_get(void);
-static void s_put(int);
+static int getsc_i(void);
static char *get_brace_var(XString *, char *);
static bool arraysub(char **);
-static void gethere(bool);
+static void gethere(void);
static Lex_state *push_state_i(State_info *, Lex_state *);
static Lex_state *pop_state_i(State_info *, Lex_state *);
@@ -112,7 +111,7 @@
#define o_getsc_u() ((*source->str != '\0') ? *source->str++ : getsc_uu())
/* retrace helper */
-#define o_getsc_r(carg) { \
+#define o_getsc_r(carg) \
int cev = (carg); \
struct sretrace_info *rp = retrace_info; \
\
@@ -122,17 +121,17 @@
rp = rp->next; \
} \
\
- return (cev); \
-}
+ return (cev);
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-static int getsc(void);
-
+/* callback */
static int
-getsc(void)
+getsc_i(void)
{
- o_getsc_r(o_getsc());
+ o_getsc_r((unsigned int)(unsigned char)o_getsc());
}
+
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#define getsc() getsc_i()
#else
static int getsc_r(int);
@@ -142,7 +141,7 @@
o_getsc_r(c);
}
-#define getsc() getsc_r(o_getsc())
+#define getsc() getsc_r((unsigned int)(unsigned char)o_getsc())
#endif
#define STATE_BSIZE 8
@@ -221,12 +220,14 @@
} else {
/* normal lexing */
state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
- while ((c = getsc()) == ' ' || c == '\t')
- ;
+ do {
+ c = getsc();
+ } while (ctype(c, C_BLANK));
if (c == '#') {
ignore_backslash_newline++;
- while ((c = getsc()) != '\0' && c != '\n')
- ;
+ do {
+ c = getsc();
+ } while (!ctype(c, C_NUL | C_LF));
ignore_backslash_newline--;
}
ungetsc(c);
@@ -234,55 +235,44 @@
if (source->flags & SF_ALIAS) {
/* trailing ' ' in alias definition */
source->flags &= ~SF_ALIAS;
- cf |= ALIAS;
+ /* POSIX: trailing space only counts if parsing simple cmd */
+ if (!Flag(FPOSIX) || (cf & CMDWORD))
+ cf |= ALIAS;
}
/* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */
statep->type = state;
- /* check for here string */
- if (state == SHEREDELIM) {
- c = getsc();
- if (c == '<') {
- state = SHEREDELIM;
- while ((c = getsc()) == ' ' || c == '\t')
- ;
- ungetsc(c);
- c = '<';
- goto accept_nonword;
- }
- ungetsc(c);
- }
-
/* collect non-special or quoted characters to form word */
while (!((c = getsc()) == 0 ||
((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
if (state == SBASE &&
- subshell_nesting_type == /*{*/ '}' &&
- c == /*{*/ '}')
+ subshell_nesting_type == ORD(/*{*/ '}') &&
+ (unsigned int)c == ORD(/*{*/ '}'))
/* possibly end ${ :;} */
break;
- accept_nonword:
Xcheck(ws, wp);
switch (state) {
case SADELIM:
- if (c == '(')
+ if ((unsigned int)c == ORD('('))
statep->nparen++;
- else if (c == ')')
+ else if ((unsigned int)c == ORD(')'))
statep->nparen--;
- else if (statep->nparen == 0 && (c == /*{*/ '}' ||
+ else if (statep->nparen == 0 &&
+ ((unsigned int)c == ORD(/*{*/ '}') ||
c == (int)statep->ls_adelim.delimiter)) {
*wp++ = ADELIM;
*wp++ = c;
- if (c == /*{*/ '}' || --statep->ls_adelim.num == 0)
+ if ((unsigned int)c == ORD(/*{*/ '}') ||
+ --statep->ls_adelim.num == 0)
POP_STATE();
- if (c == /*{*/ '}')
+ if ((unsigned int)c == ORD(/*{*/ '}'))
POP_STATE();
break;
}
/* FALLTHROUGH */
case SBASE:
- if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
+ if ((unsigned int)c == ORD('[') && (cf & CMDASN)) {
/* temporary */
*wp = EOS;
if (is_wdvarname(Xstring(ws, wp), false)) {
@@ -298,15 +288,6 @@
}
afree(tmp, ATEMP);
break;
- } else {
- Source *s;
-
- s = pushs(SREREAD,
- source->areap);
- s->start = s->str =
- s->u.freeme = tmp;
- s->next = source;
- source = s;
}
}
*wp++ = CHAR;
@@ -315,10 +296,9 @@
}
/* FALLTHROUGH */
Sbase1: /* includes *(...|...) pattern (*+?@!) */
- if (c == '*' || c == '@' || c == '+' || c == '?' ||
- c == '!') {
+ if (ctype(c, C_PATMO)) {
c2 = getsc();
- if (c2 == '(' /*)*/ ) {
+ if ((unsigned int)c2 == ORD('(' /*)*/)) {
*wp++ = OPAT;
*wp++ = c;
PUSH_STATE(SPATTERN);
@@ -329,7 +309,7 @@
/* FALLTHROUGH */
Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */
switch (c) {
- case '\\':
+ case ORD('\\'):
getsc_qchar:
if ((c = getsc())) {
/* trailing \ is lost */
@@ -337,7 +317,7 @@
*wp++ = c;
}
break;
- case '\'':
+ case ORD('\''):
open_ssquote_unless_heredoc:
if ((cf & HEREDOC))
goto store_char;
@@ -345,12 +325,12 @@
ignore_backslash_newline++;
PUSH_STATE(SSQUOTE);
break;
- case '"':
+ case ORD('"'):
open_sdquote:
*wp++ = OQUOTE;
PUSH_STATE(SDQUOTE);
break;
- case '$':
+ case ORD('$'):
/*
* processing of dollar sign belongs into
* Subst, except for those which can open
@@ -359,9 +339,9 @@
subst_dollar_ex:
c = getsc();
switch (c) {
- case '"':
+ case ORD('"'):
goto open_sdquote;
- case '\'':
+ case ORD('\''):
goto open_sequote;
default:
goto SubstS;
@@ -373,15 +353,16 @@
Subst:
switch (c) {
- case '\\':
+ case ORD('\\'):
c = getsc();
switch (c) {
- case '"':
+ case ORD('"'):
if ((cf & HEREDOC))
goto heredocquote;
/* FALLTHROUGH */
- case '\\':
- case '$': case '`':
+ case ORD('\\'):
+ case ORD('$'):
+ case ORD('`'):
store_qchar:
*wp++ = QCHAR;
*wp++ = c;
@@ -399,12 +380,12 @@
break;
}
break;
- case '$':
+ case ORD('$'):
c = getsc();
SubstS:
- if (c == '(') /*)*/ {
+ if ((unsigned int)c == ORD('(' /*)*/)) {
c = getsc();
- if (c == '(') /*)*/ {
+ if ((unsigned int)c == ORD('(' /*)*/)) {
*wp++ = EXPRSUB;
PUSH_SRETRACE(SASPAREN);
statep->nparen = 2;
@@ -421,8 +402,8 @@
memcpy(wp, sp, cz);
wp += cz;
}
- } else if (c == '{') /*}*/ {
- if ((c = getsc()) == '|') {
+ } else if ((unsigned int)c == ORD('{' /*}*/)) {
+ if ((unsigned int)(c = getsc()) == ORD('|')) {
/*
* non-subenvironment
* value substitution
@@ -439,15 +420,15 @@
}
ungetsc(c);
*wp++ = OSUBST;
- *wp++ = '{'; /*}*/
+ *wp++ = '{' /*}*/;
wp = get_brace_var(&ws, wp);
c = getsc();
/* allow :# and :% (ksh88 compat) */
- if (c == ':') {
+ if ((unsigned int)c == ORD(':')) {
*wp++ = CHAR;
*wp++ = c;
c = getsc();
- if (c == ':') {
+ if ((unsigned int)c == ORD(':')) {
*wp++ = CHAR;
*wp++ = '0';
*wp++ = ADELIM;
@@ -458,10 +439,9 @@
statep->ls_adelim.num = 1;
statep->nparen = 0;
break;
- } else if (ksh_isdigit(c) ||
- c == '('/*)*/ || c == ' ' ||
+ } else if (ctype(c, C_DIGIT | C_DOLAR | C_SPC) ||
/*XXX what else? */
- c == '$') {
+ c == '(' /*)*/) {
/* substring subst. */
if (c != ' ') {
*wp++ = CHAR;
@@ -476,10 +456,12 @@
break;
}
} else if (c == '/') {
+ c2 = ADELIM;
+ parse_adelim_slash:
*wp++ = CHAR;
*wp++ = c;
- if ((c = getsc()) == '/') {
- *wp++ = ADELIM;
+ if ((unsigned int)(c = getsc()) == ORD('/')) {
+ *wp++ = c2;
*wp++ = c;
} else
ungetsc(c);
@@ -489,12 +471,19 @@
statep->ls_adelim.num = 1;
statep->nparen = 0;
break;
+ } else if (c == '@') {
+ c2 = getsc();
+ ungetsc(c2);
+ if ((unsigned int)c2 == ORD('/')) {
+ c2 = CHAR;
+ goto parse_adelim_slash;
+ }
}
/*
* If this is a trim operation,
* treat (,|,) specially in STBRACE.
*/
- if (ctype(c, C_SUBOP2)) {
+ if (ctype(c, C_SUB2)) {
ungetsc(c);
if (Flag(FSH))
PUSH_STATE(STBRACEBOURNE);
@@ -508,7 +497,7 @@
else
PUSH_STATE(SBRACE);
}
- } else if (ksh_isalphx(c)) {
+ } else if (ctype(c, C_ALPHX)) {
*wp++ = OSUBST;
*wp++ = 'X';
do {
@@ -515,7 +504,7 @@
Xcheck(ws, wp);
*wp++ = c;
c = getsc();
- } while (ksh_isalnux(c));
+ } while (ctype(c, C_ALNUX));
*wp++ = '\0';
*wp++ = CSUBST;
*wp++ = 'X';
@@ -534,30 +523,17 @@
ungetsc(c);
}
break;
- case '`':
+ case ORD('`'):
subst_gravis:
PUSH_STATE(SBQUOTE);
- *wp++ = COMSUB;
+ *wp++ = COMASUB;
/*
- * Need to know if we are inside double quotes
- * since sh/AT&T-ksh translate the \" to " in
- * "`...\"...`".
- * This is not done in POSIX mode (section
- * 3.2.3, Double Quotes: "The backquote shall
- * retain its special meaning introducing the
- * other form of command substitution (see
- * 3.6.3). The portion of the quoted string
- * from the initial backquote and the
- * characters up to the next backquote that
- * is not preceded by a backslash (having
- * escape characters removed) defines that
- * command whose output replaces `...` when
- * the word is expanded."
- * Section 3.6.3, Command Substitution:
- * "Within the backquoted style of command
- * substitution, backslash shall retain its
- * literal meaning, except when followed by
- * $ ` \.").
+ * We need to know whether we are within double
+ * quotes in order to translate \" to " within
+ * "…`…\"…`…" because, unlike for COMSUBs, the
+ * outer double quoteing changes the backslash
+ * meaning for the inside. For more details:
+ * http://austingroupbugs.net/view.php?id=1015
*/
statep->ls_bool = false;
s2 = statep;
@@ -591,13 +567,13 @@
break;
case SEQUOTE:
- if (c == '\'') {
+ if ((unsigned int)c == ORD('\'')) {
POP_STATE();
*wp++ = CQUOTE;
ignore_backslash_newline--;
- } else if (c == '\\') {
- if ((c2 = unbksl(true, s_get, s_put)) == -1)
- c2 = s_get();
+ } else if ((unsigned int)c == ORD('\\')) {
+ if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1)
+ c2 = getsc();
if (c2 == 0)
statep->ls_bool = true;
if (!statep->ls_bool) {
@@ -609,10 +585,11 @@
} else {
cz = utf_wctomb(ts, c2 - 0x100);
ts[cz] = 0;
- for (cz = 0; ts[cz]; ++cz) {
+ cz = 0;
+ do {
*wp++ = QCHAR;
*wp++ = ts[cz];
- }
+ } while (ts[++cz]);
}
}
} else if (!statep->ls_bool) {
@@ -622,7 +599,7 @@
break;
case SSQUOTE:
- if (c == '\'') {
+ if ((unsigned int)c == ORD('\'')) {
POP_STATE();
if ((cf & HEREDOC) || state == SQBRACE)
goto store_char;
@@ -635,7 +612,7 @@
break;
case SDQUOTE:
- if (c == '"') {
+ if ((unsigned int)c == ORD('"')) {
POP_STATE();
*wp++ = CQUOTE;
} else
@@ -644,15 +621,15 @@
/* $(( ... )) */
case SASPAREN:
- if (c == '(')
+ if ((unsigned int)c == ORD('('))
statep->nparen++;
- else if (c == ')') {
+ else if ((unsigned int)c == ORD(')')) {
statep->nparen--;
if (statep->nparen == 1) {
/* end of EXPRSUB */
POP_SRETRACE();
- if ((c2 = getsc()) == /*(*/ ')') {
+ if ((unsigned int)(c2 = getsc()) == ORD(/*(*/ ')')) {
cz = strlen(sp) - 2;
XcheckN(ws, wp, cz);
memcpy(wp, sp + 1, cz);
@@ -684,7 +661,7 @@
goto Sbase2;
case SQBRACE:
- if (c == '\\') {
+ if ((unsigned int)c == ORD('\\')) {
/*
* perform POSIX "quote removal" if the back-
* slash is "special", i.e. same cases as the
@@ -693,8 +670,10 @@
* write QCHAR+c, otherwise CHAR+\+CHAR+c are
* emitted (in heredocquote:)
*/
- if ((c = getsc()) == '"' || c == '\\' ||
- c == '$' || c == '`' || c == /*{*/'}')
+ if ((unsigned int)(c = getsc()) == ORD('"') ||
+ (unsigned int)c == ORD('\\') ||
+ ctype(c, C_DOLAR | C_GRAVE) ||
+ (unsigned int)c == ORD(/*{*/ '}'))
goto store_qchar;
goto heredocquote;
}
@@ -701,18 +680,18 @@
goto common_SQBRACE;
case SBRACE:
- if (c == '\'')
+ if ((unsigned int)c == ORD('\''))
goto open_ssquote_unless_heredoc;
- else if (c == '\\')
+ else if ((unsigned int)c == ORD('\\'))
goto getsc_qchar;
common_SQBRACE:
- if (c == '"')
+ if ((unsigned int)c == ORD('"'))
goto open_sdquote;
- else if (c == '$')
+ else if ((unsigned int)c == ORD('$'))
goto subst_dollar_ex;
- else if (c == '`')
+ else if ((unsigned int)c == ORD('`'))
goto subst_gravis;
- else if (c != /*{*/ '}')
+ else if ((unsigned int)c != ORD(/*{*/ '}'))
goto store_char;
POP_STATE();
*wp++ = CSUBST;
@@ -721,9 +700,9 @@
/* Same as SBASE, except (,|,) treated specially */
case STBRACEKORN:
- if (c == '|')
+ if ((unsigned int)c == ORD('|'))
*wp++ = SPAT;
- else if (c == '(') {
+ else if ((unsigned int)c == ORD('(')) {
*wp++ = OPAT;
/* simile for @ */
*wp++ = ' ';
@@ -730,7 +709,7 @@
PUSH_STATE(SPATTERN);
} else /* FALLTHROUGH */
case STBRACEBOURNE:
- if (c == /*{*/ '}') {
+ if ((unsigned int)c == ORD(/*{*/ '}')) {
POP_STATE();
*wp++ = CSUBST;
*wp++ = /*{*/ '}';
@@ -739,19 +718,20 @@
break;
case SBQUOTE:
- if (c == '`') {
+ if ((unsigned int)c == ORD('`')) {
*wp++ = 0;
POP_STATE();
- } else if (c == '\\') {
+ } else if ((unsigned int)c == ORD('\\')) {
switch (c = getsc()) {
case 0:
/* trailing \ is lost */
break;
- case '\\':
- case '$': case '`':
+ case ORD('$'):
+ case ORD('`'):
+ case ORD('\\'):
*wp++ = c;
break;
- case '"':
+ case ORD('"'):
if (statep->ls_bool) {
*wp++ = c;
break;
@@ -772,10 +752,10 @@
/* LETEXPR: (( ... )) */
case SLETPAREN:
- if (c == /*(*/ ')') {
+ if ((unsigned int)c == ORD(/*(*/ ')')) {
if (statep->nparen > 0)
--statep->nparen;
- else if ((c2 = getsc()) == /*(*/ ')') {
+ else if ((unsigned int)(c2 = getsc()) == ORD(/*(*/ ')')) {
c = 0;
*wp++ = CQUOTE;
goto Done;
@@ -783,6 +763,7 @@
Source *s;
ungetsc(c2);
+ ungetsc(c);
/*
* mismatched parenthesis -
* assume we were really
@@ -790,14 +771,15 @@
*/
*wp = EOS;
sp = Xstring(ws, wp);
- dp = wdstrip(sp, WDS_KEEPQ);
+ dp = wdstrip(sp + 1, WDS_TPUTS);
s = pushs(SREREAD, source->areap);
s->start = s->str = s->u.freeme = dp;
s->next = source;
source = s;
- return ('('/*)*/);
+ ungetsc('(' /*)*/);
+ return (ORD('(' /*)*/));
}
- } else if (c == '(')
+ } else if ((unsigned int)c == ORD('('))
/*
* parentheses inside quotes and
* backslashes are lost, but AT&T ksh
@@ -806,7 +788,7 @@
++statep->nparen;
goto Sbase2;
- /* <<, <<-, <<< delimiter */
+ /* << or <<- delimiter */
case SHEREDELIM:
/*
* here delimiters need a special case since
@@ -813,7 +795,7 @@
* $ and `...` are not to be treated specially
*/
switch (c) {
- case '\\':
+ case ORD('\\'):
if ((c = getsc())) {
/* trailing \ is lost */
*wp++ = QCHAR;
@@ -820,10 +802,10 @@
*wp++ = c;
}
break;
- case '\'':
+ case ORD('\''):
goto open_ssquote_unless_heredoc;
- case '$':
- if ((c2 = getsc()) == '\'') {
+ case ORD('$'):
+ if ((unsigned int)(c2 = getsc()) == ORD('\'')) {
open_sequote:
*wp++ = OQUOTE;
ignore_backslash_newline++;
@@ -830,9 +812,9 @@
PUSH_STATE(SEQUOTE);
statep->ls_bool = false;
break;
- } else if (c2 == '"') {
+ } else if ((unsigned int)c2 == ORD('"')) {
/* FALLTHROUGH */
- case '"':
+ case ORD('"'):
PUSH_SRETRACE(SHEREDQUOTE);
break;
}
@@ -844,9 +826,9 @@
}
break;
- /* " in <<, <<-, <<< delimiter */
+ /* " in << or <<- delimiter */
case SHEREDQUOTE:
- if (c != '"')
+ if ((unsigned int)c != ORD('"'))
goto Subst;
POP_SRETRACE();
dp = strnul(sp) - 1;
@@ -859,10 +841,10 @@
while ((c = *dp++)) {
if (c == '\\') {
switch ((c = *dp++)) {
- case '\\':
- case '"':
- case '$':
- case '`':
+ case ORD('\\'):
+ case ORD('"'):
+ case ORD('$'):
+ case ORD('`'):
break;
default:
*wp++ = CHAR;
@@ -880,12 +862,12 @@
/* in *(...|...) pattern (*+?@!) */
case SPATTERN:
- if (c == /*(*/ ')') {
+ if ((unsigned int)c == ORD(/*(*/ ')')) {
*wp++ = CPAT;
POP_STATE();
- } else if (c == '|') {
+ } else if ((unsigned int)c == ORD('|')) {
*wp++ = SPAT;
- } else if (c == '(') {
+ } else if ((unsigned int)c == ORD('(')) {
*wp++ = OPAT;
/* simile for @ */
*wp++ = ' ';
@@ -899,7 +881,7 @@
Xcheck(ws, wp);
if (statep != &states[1])
/* XXX figure out what is missing */
- yyerror("no closing quote\n");
+ yyerror("no closing quote");
/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
if (state == SHEREDELIM)
@@ -907,61 +889,48 @@
dp = Xstring(ws, wp);
if (state == SBASE && (
-#ifndef MKSH_LEGACY_MODE
(c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
-#endif
- c == '<' || c == '>')) {
+ ctype(c, C_ANGLE)) && ((c2 = Xlength(ws, wp)) == 0 ||
+ (c2 == 2 && dp[0] == CHAR && ctype(dp[1], C_DIGIT)))) {
struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
- if (Xlength(ws, wp) == 0)
- iop->unit = c == '<' ? 0 : 1;
- else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) {
- if (dp[c2] != CHAR)
- goto no_iop;
- if (!ksh_isdigit(dp[c2 + 1]))
- goto no_iop;
- iop->unit = (iop->unit * 10) + dp[c2 + 1] - '0';
- }
+ iop->unit = c2 == 2 ? ksh_numdig(dp[1]) : c == '<' ? 0 : 1;
- if (iop->unit >= FDBASE)
- goto no_iop;
-
if (c == '&') {
- if ((c2 = getsc()) != '>') {
+ if ((unsigned int)(c2 = getsc()) != ORD('>')) {
ungetsc(c2);
goto no_iop;
}
c = c2;
- iop->flag = IOBASH;
+ iop->ioflag = IOBASH;
} else
- iop->flag = 0;
+ iop->ioflag = 0;
c2 = getsc();
/* <<, >>, <> are ok, >< is not */
- if (c == c2 || (c == '<' && c2 == '>')) {
- iop->flag |= c == c2 ?
- (c == '>' ? IOCAT : IOHERE) : IORDWR;
- if (iop->flag == IOHERE) {
- if ((c2 = getsc()) == '-') {
- iop->flag |= IOSKIP;
- c2 = getsc();
- } else if (c2 == '<')
- iop->flag |= IOHERESTR;
- ungetsc(c2);
- if (c2 == '\n')
- iop->flag |= IONDELIM;
+ if (c == c2 || ((unsigned int)c == ORD('<') &&
+ (unsigned int)c2 == ORD('>'))) {
+ iop->ioflag |= c == c2 ?
+ ((unsigned int)c == ORD('>') ? IOCAT : IOHERE) : IORDWR;
+ if (iop->ioflag == IOHERE) {
+ if ((unsigned int)(c2 = getsc()) == ORD('-'))
+ iop->ioflag |= IOSKIP;
+ else if ((unsigned int)c2 == ORD('<'))
+ iop->ioflag |= IOHERESTR;
+ else
+ ungetsc(c2);
}
- } else if (c2 == '&')
- iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
+ } else if ((unsigned int)c2 == ORD('&'))
+ iop->ioflag |= IODUP | ((unsigned int)c == ORD('<') ? IORDUP : 0);
else {
- iop->flag |= c == '>' ? IOWRITE : IOREAD;
- if (c == '>' && c2 == '|')
- iop->flag |= IOCLOB;
+ iop->ioflag |= (unsigned int)c == ORD('>') ? IOWRITE : IOREAD;
+ if ((unsigned int)c == ORD('>') && (unsigned int)c2 == ORD('|'))
+ iop->ioflag |= IOCLOB;
else
ungetsc(c2);
}
- iop->name = NULL;
+ iop->ioname = NULL;
iop->delim = NULL;
iop->heredoc = NULL;
/* free word */
@@ -976,35 +945,50 @@
/* free word */
Xfree(ws, wp);
/* no word, process LEX1 character */
- if ((c == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) {
+ if (((unsigned int)c == ORD('|')) ||
+ ((unsigned int)c == ORD('&')) ||
+ ((unsigned int)c == ORD(';')) ||
+ ((unsigned int)c == ORD('(' /*)*/))) {
if ((c2 = getsc()) == c)
- c = (c == ';') ? BREAK :
- (c == '|') ? LOGOR :
- (c == '&') ? LOGAND :
- /* c == '(' ) */ MDPAREN;
- else if (c == '|' && c2 == '&')
+ c = ((unsigned int)c == ORD(';')) ? BREAK :
+ ((unsigned int)c == ORD('|')) ? LOGOR :
+ ((unsigned int)c == ORD('&')) ? LOGAND :
+ /* (unsigned int)c == ORD('(' )) */ MDPAREN;
+ else if ((unsigned int)c == ORD('|') && (unsigned int)c2 == ORD('&'))
c = COPROC;
- else if (c == ';' && c2 == '|')
+ else if ((unsigned int)c == ORD(';') && (unsigned int)c2 == ORD('|'))
c = BRKEV;
- else if (c == ';' && c2 == '&')
+ else if ((unsigned int)c == ORD(';') && (unsigned int)c2 == ORD('&'))
c = BRKFT;
else
ungetsc(c2);
#ifndef MKSH_SMALL
if (c == BREAK) {
- if ((c2 = getsc()) == '&')
+ if ((unsigned int)(c2 = getsc()) == ORD('&'))
c = BRKEV;
else
ungetsc(c2);
}
#endif
- } else if (c == '\n') {
- gethere(false);
- if (cf & CONTIN)
- goto Again;
- } else if (c == '\0')
- /* need here strings at EOF */
- gethere(true);
+ } else if ((unsigned int)c == ORD('\n')) {
+ if (cf & HEREDELIM)
+ ungetsc(c);
+ else {
+ gethere();
+ if (cf & CONTIN)
+ goto Again;
+ }
+ } else if (c == '\0' && !(cf & HEREDELIM)) {
+ struct ioword **p = heres;
+
+ while (p < herep)
+ if ((*p)->ioflag & IOHERESTR)
+ ++p;
+ else
+ /* ksh -c 'cat <<EOF' can cause this */
+ yyerror(Tf_heredoc,
+ evalstr((*p)->delim, 0));
+ }
return (c);
}
@@ -1027,28 +1011,13 @@
/* copy word to unprefixed string ident */
sp = yylval.cp;
dp = ident;
- if ((cf & HEREDELIM) && (sp[1] == '<')) {
- herestringloop:
- switch ((c = *sp++)) {
- case CHAR:
- ++sp;
- /* FALLTHROUGH */
- case OQUOTE:
- case CQUOTE:
- goto herestringloop;
- default:
- break;
- }
- /* dummy value */
- *dp++ = 'x';
- } else
- while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
- *dp++ = *sp++;
+ while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
+ *dp++ = *sp++;
+ if (c != EOS)
+ /* word is not unquoted, or space ran out */
+ dp = ident;
/* make sure the ident array stays NUL padded */
memset(dp, 0, (ident + IDENT) - dp + 1);
- if (c != EOS)
- /* word is not unquoted */
- *ident = '\0';
if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) {
struct tbl *p;
@@ -1056,7 +1025,7 @@
if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
(!(cf & ESACONLY) || p->val.i == ESAC ||
- p->val.i == /*{*/ '}')) {
+ (unsigned int)p->val.i == ORD(/*{*/ '}'))) {
afree(yylval.cp, ATEMP);
return (p->val.i);
}
@@ -1069,7 +1038,7 @@
const char *cp = source->str;
/* prefer POSIX but not Korn functions over aliases */
- while (*cp == ' ' || *cp == '\t')
+ while (ctype(*cp, C_BLANK))
/*
* this is like getsc() without skipping
* over Source boundaries (including not
@@ -1077,8 +1046,7 @@
* pushed into an SREREAD) which is what
* we want here anyway: find out whether
* the alias name is followed by a POSIX
- * function definition (only the opening
- * parenthesis is checked though)
+ * function definition
*/
++cp;
/* prefer functions over aliases */
@@ -1095,6 +1063,7 @@
s->start = s->str = p->val.s;
s->u.tblp = p;
s->flags |= SF_HASALIAS;
+ s->line = source->line;
s->next = source;
if (source->type == SEOF) {
/* prevent infinite recursion at EOS */
@@ -1106,6 +1075,14 @@
goto Again;
}
}
+ } else if (*ident == '\0') {
+ /* retain typeset et al. even when quoted */
+ struct tbl *tt = get_builtin((dp = wdstrip(yylval.cp, 0)));
+ uint32_t flag = tt ? tt->flag : 0;
+
+ if (flag & (DECL_UTIL | DECL_FWDR))
+ strlcpy(ident, dp, sizeof(ident));
+ afree(dp, ATEMP);
}
return (LWORD);
@@ -1112,15 +1089,12 @@
}
static void
-gethere(bool iseof)
+gethere(void)
{
struct ioword **p;
for (p = heres; p < herep; p++)
- if (iseof && !((*p)->flag & IOHERESTR))
- /* only here strings at EOF */
- return;
- else
+ if (!((*p)->ioflag & IOHERESTR))
readhere(*p);
herep = heres;
}
@@ -1136,20 +1110,11 @@
const char *eof, *eofp;
XString xs;
char *xp;
- int xpos;
+ size_t xpos;
- if (iop->flag & IOHERESTR) {
- /* process the here string */
- iop->heredoc = xp = evalstr(iop->delim, DOBLANK);
- xpos = strlen(xp) - 1;
- memmove(xp, xp + 1, xpos);
- xp[xpos] = '\n';
- return;
- }
+ eof = evalstr(iop->delim, 0);
- eof = iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0);
-
- if (!(iop->flag & IOEVAL))
+ if (!(iop->ioflag & IOEVAL))
ignore_backslash_newline++;
Xinit(xs, xp, 256, ATEMP);
@@ -1158,10 +1123,10 @@
/* beginning of line */
eofp = eof;
xpos = Xsavepos(xs, xp);
- if (iop->flag & IOSKIP) {
+ if (iop->ioflag & IOSKIP) {
/* skip over leading tabs */
while ((c = getsc()) == '\t')
- /* nothing */;
+ ; /* nothing */
goto heredoc_parse_char;
}
heredoc_read_char:
@@ -1171,7 +1136,7 @@
if (!*eofp) {
/* end of here document marker, what to do? */
switch (c) {
- case /*(*/ ')':
+ case ORD(/*(*/ ')'):
if (!subshell_nesting_type)
/*-
* not allowed outside $(...) or (...)
@@ -1186,7 +1151,7 @@
* Allow EOF here to commands without trailing
* newlines (mksh -c '...') will work as well.
*/
- case '\n':
+ case ORD('\n'):
/* Newline terminates here document marker */
goto heredoc_found_terminator;
}
@@ -1197,7 +1162,7 @@
while (c != '\n') {
if (!c)
/* oops, reached EOF */
- yyerror("%s '%s' unclosed\n", "here document", eof);
+ yyerror(Tf_heredoc, eof);
/* store character */
Xcheck(xs, xp);
Xput(xs, xp, c);
@@ -1220,7 +1185,7 @@
Xput(xs, xp, '\0');
iop->heredoc = Xclose(xs, xp);
- if (!(iop->flag & IOEVAL))
+ if (!(iop->ioflag & IOEVAL))
ignore_backslash_newline--;
}
@@ -1238,6 +1203,7 @@
error_prefix(true);
va_start(va, fmt);
shf_vfprintf(shl_out, fmt, va);
+ shf_putc('\n', shl_out);
va_end(va);
errorfz();
}
@@ -1267,7 +1233,7 @@
Source *s = source;
int c;
- while ((c = *s->str++) == 0) {
+ while ((c = ord(*s->str++)) == 0) {
/* return 0 for EOF by default */
s->str = NULL;
switch (s->type) {
@@ -1297,7 +1263,7 @@
s->start = s->str = "\n";
s->type = SEOF;
} else {
- s->start = s->str = " ";
+ s->start = s->str = T1space;
s->type = SWORDS;
}
break;
@@ -1309,7 +1275,7 @@
source->flags |= s->flags & SF_ALIAS;
s = source;
} else if (*s->u.tblp->val.s &&
- (c = strnul(s->u.tblp->val.s)[-1], ksh_isspace(c))) {
+ ctype((c = strnul(s->u.tblp->val.s)[-1]), C_SPACE)) {
/* pop source stack */
source = s = s->next;
/*
@@ -1383,8 +1349,11 @@
ksh_tmout_state = TMOUT_READING;
alarm(ksh_tmout);
}
- if (interactive)
+ if (interactive) {
+ if (cur_prompt == PS1)
+ histsave(&s->line, NULL, HIST_FLUSH, true);
change_winsz();
+ }
#ifndef MKSH_NO_CMDLINE_EDITING
if (have_tty && (
#if !MKSH_S_NOVI
@@ -1455,16 +1424,23 @@
if (s->type == SFILE)
shf_fdclose(s->u.shf);
s->str = NULL;
- } else if (interactive && *s->str &&
- (cur_prompt != PS1 || !ctype(*s->str, C_IFS | C_IFSWS))) {
- histsave(&s->line, s->str, true, true);
+ } else if (interactive && *s->str) {
+ if (cur_prompt != PS1)
+ histsave(&s->line, s->str, HIST_APPEND, true);
+ else if (!ctype(*s->str, C_IFS | C_IFSWS))
+ histsave(&s->line, s->str, HIST_QUEUE, true);
#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
+ else
+ goto check_for_sole_return;
} else if (interactive && cur_prompt == PS1) {
+ check_for_sole_return:
cp = Xstring(s->xs, xp);
- while (*cp && ctype(*cp, C_IFSWS))
+ while (ctype(*cp, C_IFSWS))
++cp;
- if (!*cp)
+ if (!*cp) {
+ histsave(&s->line, NULL, HIST_FLUSH, true);
histsync();
+ }
#endif
}
if (interactive)
@@ -1474,7 +1450,7 @@
void
set_prompt(int to, Source *s)
{
- cur_prompt = to;
+ cur_prompt = (uint8_t)to;
switch (to) {
/* command */
@@ -1489,6 +1465,7 @@
struct shf *shf;
char * volatile ps1;
Area *saved_atemp;
+ int saved_lineno;
ps1 = str_val(global("PS1"));
shf = shf_sopen(NULL, strlen(ps1) * 2,
@@ -1497,9 +1474,12 @@
if (*ps1 != '!' || *++ps1 == '!')
shf_putchar(*ps1++, shf);
else
- shf_fprintf(shf, "%d",
- s ? s->line + 1 : 0);
+ shf_fprintf(shf, Tf_lu, s ?
+ (unsigned long)s->line + 1 : 0UL);
ps1 = shf_sclose(shf);
+ saved_lineno = current_lineno;
+ if (s)
+ current_lineno = s->line + 1;
saved_atemp = ATEMP;
newenv(E_ERRH);
if (kshsetjmp(e->jbuf)) {
@@ -1515,6 +1495,7 @@
char *cp = substitute(ps1, 0);
strdupx(prompt, cp, saved_atemp);
}
+ current_lineno = saved_lineno;
quitenv(NULL);
}
break;
@@ -1547,7 +1528,7 @@
for (; *cp; cp++) {
if (indelimit && *cp != delimiter)
;
- else if (*cp == '\n' || *cp == '\r') {
+ else if (ctype(*cp, C_CR | C_LF)) {
lines += columns / x_cols + ((*cp == '\n') ? 1 : 0);
columns = 0;
} else if (*cp == '\t') {
@@ -1557,7 +1538,7 @@
columns--;
} else if (*cp == delimiter)
indelimit = !indelimit;
- else if (UTFMODE && ((unsigned char)*cp > 0x7F)) {
+ else if (UTFMODE && (rtt2asc(*cp) > 0x7F)) {
const char *cp2;
columns += utf_widthadj(cp, &cp2);
if (doprint && (indelimit ||
@@ -1585,8 +1566,8 @@
{
char c;
enum parse_state {
- PS_INITIAL, PS_SAW_HASH, PS_IDENT,
- PS_NUMBER, PS_VAR1
+ PS_INITIAL, PS_SAW_PERCENT, PS_SAW_HASH, PS_SAW_BANG,
+ PS_IDENT, PS_NUMBER, PS_VAR1
} state = PS_INITIAL;
while (/* CONSTCOND */ 1) {
@@ -1593,43 +1574,61 @@
c = getsc();
/* State machine to figure out where the variable part ends. */
switch (state) {
+ case PS_SAW_HASH:
+ if (ctype(c, C_VAR1)) {
+ char c2;
+
+ c2 = getsc();
+ ungetsc(c2);
+ if (ord(c2) != ORD(/*{*/ '}')) {
+ ungetsc(c);
+ goto out;
+ }
+ }
+ goto ps_common;
+ case PS_SAW_BANG:
+ switch (ord(c)) {
+ case ORD('@'):
+ case ORD('#'):
+ case ORD('-'):
+ case ORD('?'):
+ goto out;
+ }
+ goto ps_common;
case PS_INITIAL:
- if (c == '#' || c == '!' || c == '%') {
+ switch (ord(c)) {
+ case ORD('%'):
+ state = PS_SAW_PERCENT;
+ goto next;
+ case ORD('#'):
state = PS_SAW_HASH;
- break;
+ goto next;
+ case ORD('!'):
+ state = PS_SAW_BANG;
+ goto next;
}
/* FALLTHROUGH */
- case PS_SAW_HASH:
- if (ksh_isalphx(c))
+ case PS_SAW_PERCENT:
+ ps_common:
+ if (ctype(c, C_ALPHX))
state = PS_IDENT;
- else if (ksh_isdigit(c))
+ else if (ctype(c, C_DIGIT))
state = PS_NUMBER;
- else if (c == '#') {
- if (state == PS_SAW_HASH) {
- char c2;
-
- c2 = getsc();
- ungetsc(c2);
- if (c2 != /*{*/ '}') {
- ungetsc(c);
- goto out;
- }
- }
+ else if (ctype(c, C_VAR1))
state = PS_VAR1;
- } else if (ctype(c, C_VAR1))
- state = PS_VAR1;
else
goto out;
break;
case PS_IDENT:
- if (!ksh_isalnux(c)) {
- if (c == '[') {
+ if (!ctype(c, C_ALNUX)) {
+ if (ord(c) == ORD('[')) {
char *tmp, *p;
if (!arraysub(&tmp))
- yyerror("missing ]\n");
+ yyerror("missing ]");
*wp++ = c;
- for (p = tmp; *p; ) {
+ p = tmp;
+ while (*p) {
Xcheck(*wsp, wp);
*wp++ = *p++;
}
@@ -1639,9 +1638,10 @@
}
goto out;
}
+ next:
break;
case PS_NUMBER:
- if (!ksh_isdigit(c))
+ if (!ctype(c, C_DIGIT))
goto out;
break;
case PS_VAR1:
@@ -1676,9 +1676,9 @@
c = getsc();
Xcheck(ws, wp);
*wp++ = c;
- if (c == '[')
+ if (ord(c) == ORD('['))
depth++;
- else if (c == ']')
+ else if (ord(c) == ORD(']'))
depth--;
} while (depth > 0 && c && c != '\n');
@@ -1757,19 +1757,19 @@
{
int c;
- if ((unsigned char)(c = o_getsc_u()) != 0xEF) {
+ if (rtt2asc((c = o_getsc_u())) != 0xEF) {
ungetsc_i(c);
return;
}
- if ((unsigned char)(c = o_getsc_u()) != 0xBB) {
+ if (rtt2asc((c = o_getsc_u())) != 0xBB) {
ungetsc_i(c);
- ungetsc_i(0xEF);
+ ungetsc_i(asc2rtt(0xEF));
return;
}
- if ((unsigned char)(c = o_getsc_u()) != 0xBF) {
+ if (rtt2asc((c = o_getsc_u())) != 0xBF) {
ungetsc_i(c);
- ungetsc_i(0xBB);
- ungetsc_i(0xEF);
+ ungetsc_i(asc2rtt(0xBB));
+ ungetsc_i(asc2rtt(0xEF));
return;
}
UTFMODE |= 8;
@@ -1798,15 +1798,3 @@
return (si->base + STATE_BSIZE - 1);
}
-
-static int
-s_get(void)
-{
- return (getsc());
-}
-
-static void
-s_put(int c)
-{
- ungetsc(c);
-}
Modified: trunk/contrib/mksh/lksh.1
===================================================================
--- trunk/contrib/mksh/lksh.1 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/lksh.1 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,7 +1,7 @@
-.\" $MirOS: src/bin/mksh/lksh.1,v 1.5 2013/05/22 18:18:06 tg Exp $
+.\" $MirOS: src/bin/mksh/lksh.1,v 1.23 2017/04/02 13:38:02 tg Exp $
.\"-
-.\" Copyright (c) 2008, 2009, 2010, 2012, 2013
-.\" Thorsten “mirabilos” Glaser <tg at mirbsd.org>
+.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017
+.\" mirabilos <m at mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
.\" are retained or reproduced in an accompanying document, permission
@@ -27,7 +27,9 @@
.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
.\" * \(en does not work in nroff, so use \*(en
.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
-.\" Also make sure to use \& especially with two-letter words.
+.\" Also make sure to use \& *before* a punctuation char that is to not
+.\" be interpreted as punctuation, and especially with two-letter words
+.\" but also (after) a period that does not end a sentence (“e.g.\&”).
.\" The section after the "doc" macropackage has been loaded contains
.\" additional code to convene between the UCB mdoc macropackage (and
.\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
@@ -72,7 +74,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: May 22 2013 $
+.Dd $Mdocdate: April 2 2017 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
@@ -171,48 +173,70 @@
refer to its manual page for details on the scripting language.
It is recommended to port scripts to
.Nm mksh
-instead of relying on legacy or idiotic POSIX-mandated behaviour,
+instead of relying on legacy or objectionable POSIX-mandated behaviour,
since the MirBSD Korn Shell scripting language is much more consistent.
+.Pp
+Do not use
+.Nm
+as an interactive or login shell; use
+.Nm mksh
+instead.
+.Pp
+Note that it's strongly recommended to invoke
+.Nm
+with
+.Fl o Ic posix
+to fully enjoy better compatibility to the
+.Tn POSIX
+standard (which is probably why you use
+.Nm
+over
+.Nm mksh
+in the first place);
+.Fl o Ic sh
+(possibly additionally to the above) may be needed for some legacy scripts.
.Sh LEGACY MODE
.Nm
-has the following differences from
+currently has the following differences from
.Nm mksh :
.Bl -bullet
.It
-There is no explicit support for interactive use,
-nor any command line editing or history code.
-Hence,
-.Nm
-is not suitable as a user's login shell, either; use
-.Nm mksh
-instead.
-.It
The
.Ev KSH_VERSION
string identifies
.Nm
as
-.Dq LEGACY KSH
+.Dq Li LEGACY KSH
instead of
-.Dq MIRBSD KSH .
+.Dq Li MIRBSD KSH .
+Note that the rest of the version string is identical between
+the two shell flavours, and the behaviour and differences can
+change between versions; see the accompanying manual page
+.Xr mksh 1
+for the versions this document applies to.
.It
.Nm
-only offers the traditional ten file descriptors to scripts.
-.It
-.Nm
uses
.Tn POSIX
-arithmetics, which has quite a few implications:
-The data type for arithmetics is the host ISO C
+arithmetic, which has quite a few implications:
+The data type for arithmetic operations is the host
+.Tn ISO
+C
.Vt long
data type.
-Signed integer wraparound is Undefined Behaviour.
+Signed integer wraparound is Undefined Behaviour; this means that...
+.Bd -literal -offset indent
+$ echo $((2147483647 + 1))
+.Ed
+.Pp
+\&... is permitted to, e.g. delete all files on your system
+(the figure differs for non-32-bit systems, the rule doesn't).
The sign of the result of a modulo operation with at least one
negative operand is unspecified.
Shift operations on negative numbers are unspecified.
Division of the largest negative number by \-1 is Undefined Behaviour.
The compiler is permitted to delete all data and crash the system
-if Undefined Behaviour occurs.
+if Undefined Behaviour occurs (see above for an example).
.It
The rotation arithmetic operators are not available.
.It
@@ -219,17 +243,6 @@
The shift arithmetic operators take all bits of the second operand into
account; if they exceed permitted precision, the result is unspecified.
.It
-The
-.Tn GNU
-.Nm bash
-extension &\*(Gt to redirect stdout and stderr in one go is not parsed.
-.It
-The
-.Nm mksh
-command line option
-.Fl T
-is not available.
-.It
Unless
.Ic set -o posix
is active,
@@ -245,18 +258,18 @@
.Xr getopt 1
command.
.It
-.Nm lksh ,
-unlike
-.At
-.Nm ksh ,
-does not keep file descriptors \*(Gt 2 private.
+Functions defined with the
+.Ic function
+reserved word share the shell options
+.Pq Ic set -o
+instead of locally scoping them.
.El
.Sh SEE ALSO
.Xr mksh 1
.Pp
-.Pa https://www.mirbsd.org/mksh.htm
+.Pa http://www.mirbsd.org/mksh.htm
.Pp
-.Pa https://www.mirbsd.org/ksh\-chan.htm
+.Pa http://www.mirbsd.org/ksh\-chan.htm
.Sh CAVEATS
To use
.Nm
@@ -264,8 +277,25 @@
.Pa /bin/sh ,
compilation to enable
.Ic set -o posix
-by default is highly recommended for better standards compliance.
+by default if called as
+.Nm sh
+.Pq adding Dv \-DMKSH_BINSHPOSIX to Dv CPPFLAGS
+is highly recommended for better standards compliance.
.Pp
+For better compatibility with legacy scripts, such as many
+.Tn Debian
+maintainer scripts, Upstart and SYSV init scripts, and other
+unfixed scripts, also adding the
+.Dv \-DMKSH_BINSHREDUCED
+compile-time option to enable
+.Em both
+.Ic set -o posix -o sh
+when the shell is run as
+.Nm sh ,
+as well as integrating the optional disrecommended
+.Xr printf 1
+builtin, might be necessary.
+.Pp
.Nm
tries to make a cross between a legacy bourne/posix compatibl-ish
shell and a legacy pdksh-alike but
@@ -272,14 +302,6 @@
.Dq legacy
is not exactly specified.
.Pp
-The
-.Ic set
-built-in command does not have all options one would expect
-from a full-blown
-.Nm mksh
-or
-.Nm pdksh .
-.Pp
Talk to the
.Mx
development team using the mailing list at
Modified: trunk/contrib/mksh/main.c
===================================================================
--- trunk/contrib/mksh/main.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/main.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,12 +1,12 @@
-/* $OpenBSD: main.c,v 1.55 2015/02/09 09:09:30 jsg Exp $ */
+/* $OpenBSD: main.c,v 1.57 2015/09/10 22:48:58 nicm Exp $ */
/* $OpenBSD: tty.c,v 1.10 2014/08/10 02:44:26 guenther Exp $ */
-/* $OpenBSD: io.c,v 1.25 2014/08/11 20:28:47 guenther Exp $ */
-/* $OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $ */
+/* $OpenBSD: io.c,v 1.26 2015/09/11 08:00:27 guenther Exp $ */
+/* $OpenBSD: table.c,v 1.16 2015/09/01 13:12:31 tedu Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -34,16 +34,14 @@
#include <locale.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.285.2.2 2015/03/01 15:43:01 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.347 2018/01/13 21:45:07 tg Exp $");
-extern char **environ;
-
#ifndef MKSHRC_PATH
#define MKSHRC_PATH "~/.mkshrc"
#endif
#ifndef MKSH_DEFAULT_TMPDIR
-#define MKSH_DEFAULT_TMPDIR "/tmp"
+#define MKSH_DEFAULT_TMPDIR MKSH_UNIXROOT "/tmp"
#endif
static uint8_t isuc(const char *);
@@ -52,48 +50,46 @@
static void reclaim(void);
static void remove_temps(struct temp *);
static mksh_uari_t rndsetup(void);
+static void init_environ(void);
#ifdef SIGWINCH
static void x_sigwinch(int);
#endif
-static const char initifs[] = "IFS= \t\n";
-
static const char initsubs[] =
- "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0} ${EPOCHREALTIME=}";
+ "${PS2=> }"
+ "${PS3=#? }"
+ "${PS4=+ }"
+ "${SECONDS=0}"
+ "${TMOUT=0}"
+ "${EPOCHREALTIME=}";
static const char *initcoms[] = {
Ttypeset, "-r", initvsn, NULL,
- Ttypeset, "-x", "HOME", "PATH", "SHELL", NULL,
+ Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias,
- "integer=typeset -i",
- Tlocal_typeset,
+ "integer=\\\\builtin typeset -i",
+ "local=\\\\builtin typeset",
/* not "alias -t --": hash -r needs to work */
- "hash=alias -t",
- "type=whence -v",
-#if !defined(ANDROID) && !defined(MKSH_UNEMPLOYED)
- /* not in Android for political reasons */
- /* not in ARGE mksh due to no job control */
- "stop=kill -STOP",
-#endif
- "autoload=typeset -fu",
- "functions=typeset -f",
- "history=fc -l",
- "nameref=typeset -n",
+ "hash=\\\\builtin alias -t",
+ "type=\\\\builtin whence -v",
+ "autoload=\\\\builtin typeset -fu",
+ "functions=\\\\builtin typeset -f",
+ "history=\\\\builtin fc -l",
+ "nameref=\\\\builtin typeset -n",
"nohup=nohup ",
- Tr_fc_e_dash,
- "source=PATH=$PATH:. command .",
- "login=exec login",
+ "r=\\\\builtin fc -e -",
+ "login=\\\\builtin exec login",
NULL,
/* this is what AT&T ksh seems to track, with the addition of emacs */
Talias, "-tU",
- "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
- "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
+ Tcat, "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
+ "make", "mv", "pr", "rm", "sed", Tsh, "vi", "who", NULL,
NULL
};
static const char *restr_com[] = {
- Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
+ Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
};
static bool initio_done;
@@ -102,6 +98,51 @@
static struct env env;
struct env *e = &env;
+/* compile-time assertions */
+#define cta(name, expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
+
+/* this one should be defined by the standard */
+cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
+ (sizeof(unsigned char) == 1));
+cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) &&
+ ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF));
+/* the next assertion is probably not really needed */
+cta(short_is_2_char, sizeof(short) == 2);
+cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short));
+/* the next assertion is probably not really needed */
+cta(int_is_4_char, sizeof(int) == 4);
+cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int));
+
+cta(long_ge_int, sizeof(long) >= sizeof(int));
+cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long));
+
+#ifndef MKSH_LEGACY_MODE
+/* the next assertion is probably not really needed */
+cta(ari_is_4_char, sizeof(mksh_ari_t) == 4);
+/* but this is */
+cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1));
+/* the next assertion is probably not really needed */
+cta(uari_is_4_char, sizeof(mksh_uari_t) == 4);
+/* but the next three are; we REQUIRE unsigned integer wraparound */
+cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1));
+cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3));
+cta(uari_wrap_32_bit,
+ (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) >
+ (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4));
+#endif
+/* these are always required */
+cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0);
+cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0);
+/* we require these to have the precisely same size and assume 2s complement */
+cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t));
+
+cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t));
+cta(sizet_voidptr_same_size, sizeof(size_t) == sizeof(void *));
+cta(sizet_funcptr_same_size, sizeof(size_t) == sizeof(void (*)(void)));
+/* our formatting routines assume this */
+cta(ptr_fits_in_long, sizeof(size_t) <= sizeof(long));
+cta(ari_fits_in_long, sizeof(mksh_ari_t) <= sizeof(long));
+
static mksh_uari_t
rndsetup(void)
{
@@ -116,13 +157,11 @@
} *bufptr;
char *cp;
- cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM);
-#ifdef DEBUG
- /* clear the allocated space, for valgrind */
- memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE);
-#endif
+ cp = alloc(sizeof(*bufptr) - sizeof(ALLOC_ITEM), APERM);
+ /* clear the allocated space, for valgrind and to avoid UB */
+ memset(cp, 0, sizeof(*bufptr) - sizeof(ALLOC_ITEM));
/* undo what alloc() did to the malloc result address */
- bufptr = (void *)(cp - ALLOC_SIZE);
+ bufptr = (void *)(cp - sizeof(ALLOC_ITEM));
/* PIE or something similar provides us with deltas here */
bufptr->dataptr = &rndsetupstate;
/* ASLR in at least Windows, Linux, some BSDs */
@@ -136,6 +175,9 @@
/* introduce variation (and yes, second arg MBZ for portability) */
mksh_TIME(bufptr->tv);
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+ mprotect(((char *)bufptr) + 4096, 4096, PROT_READ | PROT_WRITE);
+#endif
h = chvt_rndsetup(bufptr, sizeof(*bufptr));
afree(cp, APERM);
@@ -152,7 +194,7 @@
}
static const char *empty_argv[] = {
- "mksh", NULL
+ Tmksh, NULL
};
static uint8_t
@@ -193,10 +235,19 @@
ssize_t k;
#endif
+#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
+ ebcdic_init();
+#endif
+ set_ifs(TC_IFSWS);
+
+#ifdef __OS2__
+ os2_init(&argc, &argv);
+#endif
+
/* do things like getpgrp() et al. */
chvt_reinit();
- /* make sure argv[] is sane */
+ /* make sure argv[] is sane, for weird OSes */
if (!*argv) {
argv = empty_argv;
argc = 1;
@@ -205,6 +256,8 @@
/* initialise permanent Area */
ainit(&aperm);
+ /* max. name length: -2147483648 = 11 (+ NUL) */
+ vtemp = alloc(offsetof(struct tbl, name[0]) + 12, APERM);
/* set up base environment */
env.type = E_NONE;
@@ -217,11 +270,11 @@
/* determine the basename (without '-' or path) of the executable */
ccp = kshname;
- goto begin_parse_kshname;
+ goto begin_parsing_kshname;
while ((i = ccp[argi++])) {
- if (i == '/') {
+ if (mksh_cdirsep(i)) {
ccp += argi;
- begin_parse_kshname:
+ begin_parsing_kshname:
argi = 0;
if (*ccp == '-')
++ccp;
@@ -249,7 +302,7 @@
/* define built-in commands and see if we were called as one */
ktinit(APERM, &builtins,
- /* currently up to 51 builtins: 75% of 128 = 2^7 */
+ /* currently up to 54 builtins: 75% of 128 = 2^7 */
7);
for (i = 0; mkshbuiltins[i].name != NULL; i++)
if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
@@ -264,7 +317,7 @@
#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
/* are we called as -sh or /bin/sh or so? */
- if (!strcmp(ccp, "sh")) {
+ if (!strcmp(ccp, "sh" MKSH_EXE_EXT)) {
/* either also turns off braceexpand */
#ifdef MKSH_BINSHPOSIX
/* enable better POSIX conformance */
@@ -280,8 +333,6 @@
initvar();
- initctypes();
-
inittraps();
coproc_init();
@@ -312,13 +363,16 @@
#endif
/*
* this is uniform across all OSes unless it
- * breaks somewhere; don't try to optimise,
+ * breaks somewhere hard; don't try to optimise,
* e.g. add stuff for Interix or remove /usr
* for HURD, because e.g. Debian GNU/HURD is
* "keeping a regular /usr"; this is supposed
* to be a sane 'basic' default PATH
*/
- def_path = "/bin:/usr/bin:/sbin:/usr/sbin";
+ def_path = MKSH_UNIXROOT "/bin" MKSH_PATHSEPS
+ MKSH_UNIXROOT "/usr/bin" MKSH_PATHSEPS
+ MKSH_UNIXROOT "/sbin" MKSH_PATHSEPS
+ MKSH_UNIXROOT "/usr/sbin";
#endif
/*
@@ -325,7 +379,7 @@
* Set PATH to def_path (will set the path global variable).
* (import of environment below will probably change this setting).
*/
- vp = global("PATH");
+ vp = global(TPATH);
/* setstr can't fail here */
setstr(vp, def_path, KSH_RETURN_ERROR);
@@ -342,27 +396,21 @@
#endif
/* import environment */
- if (environ != NULL) {
- wp = (const char **)environ;
- while (*wp != NULL) {
- rndpush(*wp);
- typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
- ++wp;
- }
- }
+ init_environ();
/* for security */
- typeset(initifs, 0, 0, 0, 0);
+ typeset(TinitIFS, 0, 0, 0, 0);
/* assign default shell variable values */
+ typeset("PATHSEP=" MKSH_PATHSEPS, 0, 0, 0, 0);
substitute(initsubs, 0);
/* Figure out the current working directory and set $PWD */
- vp = global("PWD");
+ vp = global(TPWD);
cp = str_val(vp);
/* Try to use existing $PWD if it is valid */
- set_current_wd((cp[0] == '/' && test_eval(NULL, TO_FILEQ, cp, ".",
- true)) ? cp : NULL);
+ set_current_wd((mksh_abspath(cp) && test_eval(NULL, TO_FILEQ, cp,
+ Tdot, true)) ? cp : NULL);
if (current_wd[0])
simplify_path(current_wd);
/* Only set pwd if we know where we are or if it had a bogus value */
@@ -371,7 +419,7 @@
setstr(vp, current_wd, KSH_RETURN_ERROR);
for (wp = initcoms; *wp != NULL; wp++) {
- shcomexec(wp);
+ c_builtin(wp);
while (*wp != NULL)
wp++;
}
@@ -407,11 +455,7 @@
setint_n((vp_pipest = global("PIPESTATUS")), 0, 10);
/* Set this before parsing arguments */
- Flag(FPRIVILEGED) = (
-#if HAVE_ISSETUGID
- issetugid() ||
-#endif
- kshuid != ksheuid || kshgid != kshegid) ? 2 : 0;
+ Flag(FPRIVILEGED) = (kshuid != ksheuid || kshgid != kshegid) ? 2 : 0;
/* this to note if monitor is set on command line (see below) */
#ifndef MKSH_UNEMPLOYED
@@ -436,9 +480,9 @@
} else if (Flag(FCOMMAND)) {
s = pushs(SSTRINGCMDLINE, ATEMP);
if (!(s->start = s->str = argv[argi++]))
- errorf("%s %s", "-c", "requires an argument");
+ errorf(Tf_optfoo, "", "", 'c', Treq_arg);
while (*s->str) {
- if (*s->str != ' ' && ctype(*s->str, C_QUOTE))
+ if (ctype(*s->str, C_QUOTE))
break;
s->str++;
}
@@ -454,12 +498,26 @@
kshname = argv[argi++];
} else if (argi < argc && !Flag(FSTDIN)) {
s = pushs(SFILE, ATEMP);
+#ifdef __OS2__
+ /*
+ * A bug in OS/2 extproc (like shebang) handling makes
+ * it not pass the full pathname of a script, so we need
+ * to search for it. This changes the behaviour of a
+ * simple "mksh foo", but can't be helped.
+ */
s->file = argv[argi++];
+ if (search_access(s->file, X_OK) != 0)
+ s->file = search_path(s->file, path, X_OK, NULL);
+ if (!s->file || !*s->file)
+ s->file = argv[argi - 1];
+#else
+ s->file = argv[argi++];
+#endif
s->u.shf = shf_open(s->file, O_RDONLY, 0,
SHF_MAPHI | SHF_CLEXEC);
if (s->u.shf == NULL) {
shl_stdout_ok = false;
- warningf(true, "%s: %s", s->file, cstrerror(errno));
+ warningf(true, Tf_sD_s, s->file, cstrerror(errno));
/* mandated by SUSv4 */
exstat = 127;
unwind(LERROR);
@@ -547,8 +605,8 @@
#endif
if (!isuc(ccp))
ccp = null;
+#endif
/* FALLTHROUGH */
-#endif
/* auto-detect from environment */
case 3:
@@ -595,8 +653,7 @@
if (Flag(FLOGIN))
include(substitute("$HOME/.profile", 0), 0, NULL, true);
if (Flag(FTALKING)) {
- cp = substitute(substitute("${ENV:-" MKSHRC_PATH "}",
- 0), DOTILDE);
+ cp = substitute("${ENV:-" MKSHRC_PATH "}", DOTILDE);
if (cp[0] != '\0')
include(cp, 0, NULL, true);
}
@@ -608,7 +665,7 @@
}
if (restricted_shell) {
- shcomexec(restr_com);
+ c_builtin(restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 1;
}
@@ -638,9 +695,9 @@
if ((rv = main_init(argc, argv, &s, &l)) == 0) {
if (Flag(FAS_BUILTIN)) {
- rv = shcomexec(l->argv);
+ rv = c_builtin(l->argv);
} else {
- shell(s, true);
+ shell(s, 0);
/* NOTREACHED */
}
}
@@ -693,7 +750,7 @@
unwind(i);
/* NOTREACHED */
default:
- internal_errorf("%s %d", "include", i);
+ internal_errorf(Tunexpected_type, Tunwind, Tsource, i);
/* NOTREACHED */
}
}
@@ -704,7 +761,7 @@
s = pushs(SFILE, ATEMP);
s->u.shf = shf;
strdupx(s->file, name, ATEMP);
- i = shell(s, false);
+ i = shell(s, 1);
quitenv(s->u.shf);
if (old_argv) {
e->loc->argv = old_argv;
@@ -718,12 +775,15 @@
int
command(const char *comm, int line)
{
- Source *s;
+ Source *s, *sold = source;
+ int rv;
s = pushs(SSTRING, ATEMP);
s->start = s->str = comm;
s->line = line;
- return (shell(s, false));
+ rv = shell(s, 1);
+ source = sold;
+ return (rv);
}
/*
@@ -730,22 +790,33 @@
* run the commands from the input source, returning status.
*/
int
-shell(Source * volatile s, volatile bool toplevel)
+shell(Source * volatile s, volatile int level)
{
struct op *t;
volatile bool wastty = tobool(s->flags & SF_TTY);
volatile uint8_t attempts = 13;
- volatile bool interactive = Flag(FTALKING) && toplevel;
+ volatile bool interactive = (level == 0) && Flag(FTALKING);
volatile bool sfirst = true;
Source *volatile old_source = source;
int i;
- newenv(E_PARSE);
+ newenv(level == 2 ? E_EVAL : E_PARSE);
if (interactive)
really_exit = false;
switch ((i = kshsetjmp(e->jbuf))) {
case 0:
break;
+ case LBREAK:
+ case LCONTIN:
+ if (level != 2) {
+ source = old_source;
+ quitenv(NULL);
+ internal_errorf(Tf_cant_s, Tshell,
+ i == LBREAK ? Tbreak : Tcontinue);
+ /* NOTREACHED */
+ }
+ /* assert: interactive == false */
+ /* FALLTHROUGH */
case LINTR:
/* we get here if SIGINT not caught or ignored */
case LERROR:
@@ -785,7 +856,7 @@
default:
source = old_source;
quitenv(NULL);
- internal_errorf("%s %d", "shell", i);
+ internal_errorf(Tunexpected_type, Tunwind, Tshell, i);
/* NOTREACHED */
}
while (/* CONSTCOND */ 1) {
@@ -802,7 +873,9 @@
j_notify();
set_prompt(PS1, s);
}
- t = compile(s, sfirst);
+ t = compile(s, sfirst, true);
+ if (interactive)
+ histsave(&s->line, NULL, HIST_FLUSH, true);
sfirst = false;
if (!t)
goto source_no_tree;
@@ -821,7 +894,7 @@
* immediately after the last command
* executed.
*/
- if (toplevel)
+ if (level == 0)
unwind(LEXIT);
break;
}
@@ -884,6 +957,7 @@
case E_INCL:
case E_LOOP:
case E_ERRH:
+ case E_EVAL:
kshlongjmp(e->jbuf, i);
/* NOTREACHED */
case E_NONE:
@@ -892,13 +966,6 @@
/* FALLTHROUGH */
default:
quitenv(NULL);
- /*
- * quitenv() may have reclaimed the memory
- * used by source which will end badly when
- * we jump to a function that expects it to
- * be valid
- */
- source = NULL;
}
}
}
@@ -913,9 +980,9 @@
* struct env includes ALLOC_ITEM for alignment constraints
* so first get the actually used memory, then assign it
*/
- cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
+ cp = alloc(sizeof(struct env) - sizeof(ALLOC_ITEM), ATEMP);
/* undo what alloc() did to the malloc result address */
- ep = (void *)(cp - ALLOC_SIZE);
+ ep = (void *)(cp - sizeof(ALLOC_ITEM));
/* initialise public members of struct env (not the ALLOC_ITEM) */
ainit(&ep->area);
ep->oenv = e;
@@ -1011,7 +1078,7 @@
/* free the struct env - tricky due to the ALLOC_ITEM inside */
cp = (void *)ep;
- afree(cp + ALLOC_SIZE, ATEMP);
+ afree(cp + sizeof(ALLOC_ITEM), ATEMP);
}
/* Called after a fork to cleanup stuff left over from parents environment */
@@ -1069,6 +1136,14 @@
remove_temps(e->temps);
e->temps = NULL;
+
+ /*
+ * if the memory backing source is reclaimed, things
+ * will end up badly when a function expecting it to
+ * be valid is run; a NULL pointer is easily debugged
+ */
+ if (source && source->areap == &e->area)
+ source = NULL;
afreeall(&e->area);
}
@@ -1075,9 +1150,11 @@
static void
remove_temps(struct temp *tp)
{
- for (; tp != NULL; tp = tp->next)
+ while (tp) {
if (tp->pid == procpid)
unlink(tp->tffn);
+ tp = tp->next;
+ }
}
/*
@@ -1111,7 +1188,7 @@
goto got_fd;
}
#endif
- if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
+ if ((fd = open(T_devtty, O_RDWR, 0)) >= 0) {
do_close = true;
goto got_fd;
}
@@ -1168,13 +1245,13 @@
{
if (fmt) {
if (flags & VWARNINGF_INTERNAL)
- shf_fprintf(shl_out, "internal error: ");
+ shf_fprintf(shl_out, Tf_sD_, "internal error");
if (flags & VWARNINGF_ERRORPREFIX)
error_prefix(tobool(flags & VWARNINGF_FILELINE));
if ((flags & VWARNINGF_BUILTIN) &&
/* not set when main() calls parse_args() */
builtin_argv0 && builtin_argv0 != kshname)
- shf_fprintf(shl_out, "%s: ", builtin_argv0);
+ shf_fprintf(shl_out, Tf_sD_, builtin_argv0);
shf_vfprintf(shl_out, fmt, ap);
shf_putchar('\n', shl_out);
}
@@ -1244,13 +1321,10 @@
VWARNINGF_BUILTIN, fmt, va);
va_end(va);
- /*
- * POSIX special builtins and ksh special builtins cause
- * non-interactive shells to exit.
- * XXX odd use of KEEPASN; also may not want LERROR here
- */
+ /* POSIX special builtins cause non-interactive shells to exit */
if (builtin_spec) {
builtin_argv0 = NULL;
+ /* may not want to use LERROR here */
unwind(LERROR);
}
}
@@ -1284,10 +1358,11 @@
/* Avoid foo: foo[2]: ... */
if (!fileline || !source || !source->file ||
strcmp(source->file, kshname) != 0)
- shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
+ shf_fprintf(shl_out, Tf_sD_, kshname + (*kshname == '-'));
if (fileline && source && source->file != NULL) {
- shf_fprintf(shl_out, "%s[%d]: ", source->file,
- source->errline > 0 ? source->errline : source->line);
+ shf_fprintf(shl_out, "%s[%lu]: ", source->file,
+ (unsigned long)(source->errline ?
+ source->errline : source->line));
source->errline = 0;
}
}
@@ -1351,13 +1426,13 @@
shf_fdopen(2, SHF_WR, shl_xtrace);
#ifdef DF
if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
- if ((lfp = getenv("HOME")) == NULL || *lfp != '/')
- errorf("cannot get home directory");
- lfp = shf_smprintf("%s/mksh-dbg.txt", lfp);
+ if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
+ errorf("can't get home directory");
+ lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
}
if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
- errorf("cannot open debug output file %s", lfp);
+ errorf("can't open debug output file %s", lfp);
if (shl_dbg_fd < FDBASE) {
int nfd;
@@ -1364,7 +1439,7 @@
nfd = fcntl(shl_dbg_fd, F_DUPFD, FDBASE);
close(shl_dbg_fd);
if ((shl_dbg_fd = nfd) == -1)
- errorf("cannot dup debug output file");
+ errorf("can't dup debug output file");
}
fcntl(shl_dbg_fd, F_SETFD, FD_CLOEXEC);
shf_fdopen(shl_dbg_fd, SHF_WR, shl_dbg);
@@ -1380,7 +1455,7 @@
int rv;
if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF))
- errorf("too many files open in shell");
+ errorf(Ttoo_many_files);
#ifdef __ultrix
/*XXX imake style */
@@ -1401,10 +1476,10 @@
int nfd = fd;
if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 &&
- errno == EBADF)
+ (errno == EBADF || errno == EPERM))
return (-1);
if (nfd < 0 || nfd > SHRT_MAX)
- errorf("too many files open in shell");
+ errorf(Ttoo_many_files);
fcntl(nfd, F_SETFD, FD_CLOEXEC);
return ((short)nfd);
}
@@ -1437,6 +1512,10 @@
pv[1] = savefd(lpv[1]);
if (pv[1] != lpv[1])
close(lpv[1]);
+#ifdef __OS2__
+ setmode(pv[0], O_BINARY);
+ setmode(pv[1], O_BINARY);
+#endif
}
void
@@ -1455,16 +1534,18 @@
{
int fd, fl;
- if (name[0] == 'p' && !name[1])
+ if (!name[0] || name[1])
+ goto illegal_fd_name;
+ if (name[0] == 'p')
return (coproc_getfd(mode, emsgp));
- for (fd = 0; ksh_isdigit(*name); ++name)
- fd = (fd * 10) + *name - '0';
- if (*name || fd >= FDBASE) {
+ if (!ctype(name[0], C_DIGIT)) {
+ illegal_fd_name:
if (emsgp)
*emsgp = "illegal file descriptor name";
return (-1);
}
- if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
+
+ if ((fl = fcntl((fd = ksh_numdig(name[0])), F_GETFL, 0)) < 0) {
if (emsgp)
*emsgp = "bad file descriptor";
return (-1);
@@ -1605,28 +1686,20 @@
memcpy(cp, "/shXXXXXX.tmp", 14);
/* point to the first of six Xes */
cp += 3;
- /* generate random part of filename */
- len = -1;
+
+ /* cyclically attempt to open a temporary file */
do {
- i = rndget() % 36;
- cp[++len] = i < 26 ? 'a' + i : '0' + i - 26;
- } while (len < 5);
+ /* generate random part of filename */
+ len = 0;
+ do {
+ cp[len++] = digits_lc[rndget() % 36];
+ } while (len < 6);
- /* cyclically attempt to open a temporary file */
- while ((i = open(tp->tffn, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
- 0600)) < 0) {
- if (errno != EEXIST)
+ /* check if this one works */
+ if ((i = binopen3(tp->tffn, O_CREAT | O_EXCL | O_RDWR,
+ 0600)) < 0 && errno != EEXIST)
goto maketemp_out;
- /* count down from z to a then from 9 to 0 */
- while (cp[len] == '0')
- if (!len--)
- goto maketemp_out;
- if (cp[len] == 'a')
- cp[len] = '9';
- else
- --cp[len];
- /* do another cycle */
- }
+ } while (i < 0);
if (type == TT_FUNSUB) {
/* map us high and mark as close-on-exec */
@@ -1804,7 +1877,7 @@
const struct tbl *a = *((const struct tbl * const *)p1);
const struct tbl *b = *((const struct tbl * const *)p2);
- return (strcmp(a->name, b->name));
+ return (ascstrcmp(a->name, b->name));
}
struct tbl **
@@ -1895,3 +1968,111 @@
mksh_tcset(fd, &cb);
}
+
+#ifdef MKSH_ENVDIR
+static void
+init_environ(void)
+{
+ char *xp;
+ ssize_t n;
+ XString xs;
+ struct shf *shf;
+ DIR *dirp;
+ struct dirent *dent;
+
+ if ((dirp = opendir(MKSH_ENVDIR)) == NULL) {
+ warningf(false, "cannot read environment from %s: %s",
+ MKSH_ENVDIR, cstrerror(errno));
+ return;
+ }
+ XinitN(xs, 256, ATEMP);
+ read_envfile:
+ 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);
+ if (!(shf = shf_open(xp, O_RDONLY, 0, 0))) {
+ warningf(false,
+ "cannot read environment %s from %s: %s",
+ dent->d_name, MKSH_ENVDIR,
+ cstrerror(errno));
+ goto read_envfile;
+ }
+ afree(xp, ATEMP);
+ n = strlen(dent->d_name);
+ xp = Xstring(xs, xp);
+ XcheckN(xs, xp, n + 32);
+ memcpy(xp, dent->d_name, n);
+ xp += n;
+ *xp++ = '=';
+ while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
+ xp += n;
+ if (Xnleft(xs, xp) <= 0)
+ XcheckN(xs, xp, Xlength(xs, xp));
+ }
+ if (n < 0) {
+ warningf(false,
+ "cannot read environment %s from %s: %s",
+ dent->d_name, MKSH_ENVDIR,
+ cstrerror(shf_errno(shf)));
+ } else {
+ *xp = '\0';
+ xp = Xstring(xs, xp);
+ rndpush(xp);
+ typeset(xp, IMPORT | EXPORT, 0, 0, 0);
+ }
+ shf_close(shf);
+ }
+ goto read_envfile;
+ } else if (errno)
+ warningf(false, "cannot read environment from %s: %s",
+ MKSH_ENVDIR, cstrerror(errno));
+ closedir(dirp);
+ Xfree(xs, xp);
+}
+#else
+extern char **environ;
+
+static void
+init_environ(void)
+{
+ const char **wp;
+
+ if (environ == NULL)
+ return;
+
+ wp = (const char **)environ;
+ while (*wp != NULL) {
+ rndpush(*wp);
+ typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
+ ++wp;
+ }
+}
+#endif
+
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+void
+recheck_ctype(void)
+{
+ const char *ccp;
+
+ ccp = str_val(global("LC_ALL"));
+ if (ccp == null)
+ ccp = str_val(global("LC_CTYPE"));
+ if (ccp == null)
+ ccp = str_val(global("LANG"));
+ UTFMODE = isuc(ccp);
+#if HAVE_SETLOCALE_CTYPE
+ ccp = setlocale(LC_CTYPE, ccp);
+#if HAVE_LANGINFO_CODESET
+ if (!isuc(ccp))
+ ccp = nl_langinfo(CODESET);
+#endif
+ if (isuc(ccp))
+ UTFMODE = 1;
+#endif
+
+ if (Flag(FPOSIX))
+ warningf(true, "early locale tracking enabled UTF-8 mode while in POSIX mode, you are now noncompliant");
+}
+#endif
Modified: trunk/contrib/mksh/mirhash.h
===================================================================
--- trunk/contrib/mksh/mirhash.h 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/mirhash.h 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,6 +1,6 @@
/*-
- * Copyright © 2011, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * Copyright © 2011, 2014, 2015
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -44,7 +44,7 @@
#include <sys/types.h>
-__RCSID("$MirOS: src/bin/mksh/mirhash.h,v 1.3 2014/10/02 19:34:06 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/mirhash.h,v 1.6 2015/11/29 17:05:02 tg Exp $");
/*-
* BAFH itself is defined by the following primitives:
@@ -61,7 +61,8 @@
* the context is (still) zero, adding a NUL byte is not ignored.
*
* • BAFHror(eax,cl) evaluates to the unsigned 32-bit integer “eax”,
- * rotated right by “cl” ∈ [0;31]; no casting, be careful!
+ * rotated right by “cl” ∈ [0; 31] (no casting, be careful!) where
+ * “eax” must be uint32_t and “cl” an in-range integer.
*
* • BAFHFinish(ctx) avalanches the context around so every sub-byte
* depends on all input octets; afterwards, the context variable’s
@@ -88,7 +89,7 @@
* • BAFHHostStr(ctx,buf) does the same for C strings.
*
* All macros may use ctx multiple times in their expansion, but all
- * other arguments are always evaluated at most once.
+ * other arguments are always evaluated at most once except BAFHror.
*
* To stay portable, never use the BAFHHost*() macros (these are for
* host-local entropy shuffling), and encode numbers using ULEB128.
@@ -206,6 +207,7 @@
} BAFHHost_v; \
\
BAFHUpdate_s = (const void *)(s); \
+ BAFHHost_v.as_u32 = 0; \
if ((BAFHHost_v.as_u8[0] = *BAFHUpdate_s) != 0) \
++BAFHUpdate_s; \
if ((BAFHHost_v.as_u8[1] = *BAFHUpdate_s) != 0) \
Modified: trunk/contrib/mksh/misc.c
===================================================================
--- trunk/contrib/mksh/misc.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/misc.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,10 +1,12 @@
-/* $OpenBSD: misc.c,v 1.39 2015/01/16 06:39:32 deraadt Exp $ */
-/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */
+/* $OpenBSD: misc.c,v 1.41 2015/09/10 22:48:58 nicm Exp $ */
+/* $OpenBSD: path.c,v 1.13 2015/09/05 09:47:08 jsg Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ * mirabilos <m at mirbsd.org>
+ * Copyright (c) 2015
+ * Daniel Richard G. <skunk at iSKUNK.ORG>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -30,7 +32,7 @@
#include <grp.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.219.2.2 2015/03/01 15:43:02 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.291 2018/01/14 00:03:03 tg Exp $");
#define KSH_CHVT_FLAG
#ifdef MKSH_SMALL
@@ -40,10 +42,6 @@
#define KSH_CHVT_CODE
#define KSH_CHVT_FLAG
#endif
-#ifdef MKSH_LEGACY_MODE
-#undef KSH_CHVT_CODE
-#undef KSH_CHVT_FLAG
-#endif
/* type bits for unsigned char */
unsigned char chtypes[UCHAR_MAX + 1];
@@ -51,8 +49,9 @@
static const unsigned char *pat_scan(const unsigned char *,
const unsigned char *, bool) MKSH_A_PURE;
static int do_gmatch(const unsigned char *, const unsigned char *,
- const unsigned char *, const unsigned char *) MKSH_A_PURE;
-static const unsigned char *cclass(const unsigned char *, unsigned char)
+ const unsigned char *, const unsigned char *,
+ const unsigned char *) MKSH_A_PURE;
+static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
MKSH_A_PURE;
#ifdef KSH_CHVT_CODE
static void chvt(const Getopt *);
@@ -72,43 +71,7 @@
#define DO_SETUID(func, argvec) func argvec
#endif
-/*
- * Fast character classes
- */
-void
-setctypes(const char *s, int t)
-{
- unsigned int i;
- if (t & C_IFS) {
- for (i = 0; i < UCHAR_MAX + 1; i++)
- chtypes[i] &= ~C_IFS;
- /* include \0 in C_IFS */
- chtypes[0] |= C_IFS;
- }
- while (*s != 0)
- chtypes[(unsigned char)*s++] |= t;
-}
-
-void
-initctypes(void)
-{
- int c;
-
- for (c = 'a'; c <= 'z'; c++)
- chtypes[c] |= C_ALPHA;
- for (c = 'A'; c <= 'Z'; c++)
- chtypes[c] |= C_ALPHA;
- chtypes['_'] |= C_ALPHA;
- setctypes("0123456789", C_DIGIT);
- /* \0 added automatically */
- setctypes(TC_LEX1, C_LEX1);
- setctypes("*@#!$-?", C_VAR1);
- setctypes(TC_IFSWS, C_IFSWS);
- setctypes("=-+?", C_SUBOP1);
- setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE);
-}
-
/* called from XcheckN() to grow buffer */
char *
Xcheck_grow(XString *xsp, const char *xp, size_t more)
@@ -126,6 +89,17 @@
#define SHFLAGS_DEFNS
+#define FN(sname,cname,flags,ochar) \
+ static const struct { \
+ /* character flag (if any) */ \
+ char c; \
+ /* OF_* */ \
+ unsigned char optflags; \
+ /* long name of option */ \
+ char name[sizeof(sname)]; \
+ } shoptione_ ## cname = { \
+ ochar, flags, sname \
+ };
#include "sh_flags.gen"
#define OFC(i) (options[i][-2])
@@ -145,7 +119,7 @@
{
size_t i = 0;
- if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2])
+ if (ctype(n[0], C_MINUS | C_PLUS) && n[1] && !n[2])
while (i < NELEM(options)) {
if (OFC(i) == n[1])
return (i);
@@ -166,11 +140,11 @@
int opts[NELEM(options)];
};
-static char *options_fmt_entry(char *, size_t, unsigned int, const void *);
+static void options_fmt_entry(char *, size_t, unsigned int, const void *);
static void printoptions(bool);
/* format a single select menu item */
-static char *
+static void
options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
{
const struct options_info *oi = (const struct options_info *)arg;
@@ -178,7 +152,6 @@
shf_snprintf(buf, buflen, "%-*s %s",
oi->opt_width, OFN(oi->opts[i]),
Flag(oi->opts[i]) ? "on" : "off");
- return (buf);
}
static void
@@ -189,6 +162,7 @@
if (verbose) {
size_t n = 0, len, octs = 0;
struct options_info oi;
+ struct columnise_opts co;
/* verbose version */
shf_puts("Current option settings\n", shl_stdout);
@@ -205,8 +179,11 @@
}
++i;
}
- print_columns(shl_stdout, n, options_fmt_entry, &oi,
- octs + 4, oi.opt_width + 4, true);
+ co.shf = shl_stdout;
+ co.linesep = '\n';
+ co.prefcol = co.do_last = true;
+ print_columns(&co, n, options_fmt_entry, &oi,
+ octs + 4, oi.opt_width + 4);
} else {
/* short version like AT&T ksh93 */
shf_puts(Tset, shl_stdout);
@@ -294,6 +271,11 @@
} else if ((f == FPOSIX || f == FSH) && newval) {
/* Turning on -o posix or -o sh? */
Flag(FBRACEEXPAND) = 0;
+ /* Turning on -o posix? */
+ if (f == FPOSIX) {
+ /* C locale required for compliance */
+ UTFMODE = 0;
+ }
} else if (f == FTALKING) {
/* Changing interactive flag? */
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
@@ -348,7 +330,7 @@
*/
int
parse_args(const char **argv,
- /* OF_CMDLINE or OF_SET */
+ /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
int what,
bool *setargsp)
{
@@ -381,7 +363,7 @@
*/
if (*p != '-')
for (q = p; *q; )
- if (*q++ == '/')
+ if (mksh_cdirsep(*q++))
p = q;
Flag(FLOGIN) = (*p == '-');
opts = cmd_opts;
@@ -437,7 +419,8 @@
else if ((i != (size_t)-1) && (OFF(i) & what))
change_flag((enum sh_flag)i, what, set);
else {
- bi_errorf("%s: %s", go.optarg, "bad option");
+ bi_errorf(Tf_sD_s, go.optarg,
+ Tunknown_option);
return (-1);
}
break;
@@ -477,7 +460,7 @@
}
}
if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
- (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
+ ctype(argv[go.optind][0], C_MINUS | C_PLUS) &&
argv[go.optind][1] == '\0') {
/* lone - clears -v and -x flags */
if (argv[go.optind][0] == '-') {
@@ -495,10 +478,10 @@
if (arrayset) {
const char *ccp = NULL;
- if (*array)
+ if (array && *array)
ccp = skip_varname(array, false);
if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
- bi_errorf("%s: %s", array, "is not an identifier");
+ bi_errorf(Tf_sD_s, array, Tnot_ident);
return (-1);
}
}
@@ -506,7 +489,7 @@
for (i = go.optind; argv[i]; i++)
;
qsort(&argv[go.optind], i - go.optind, sizeof(void *),
- xstrcmp);
+ ascpstrcmp);
}
if (arrayset)
go.optind += set_array(array, tobool(arrayset > 0),
@@ -527,7 +510,7 @@
do {
c = *s++;
- } while (ksh_isspace(c));
+ } while (ctype(c, C_SPACE));
switch (c) {
case '-':
@@ -539,13 +522,13 @@
}
do {
- if (!ksh_isdigit(c))
+ if (!ctype(c, C_DIGIT))
/* not numeric */
return (0);
if (num.u > 214748364U)
/* overflow on multiplication */
return (0);
- num.u = num.u * 10U + (unsigned int)(c - '0');
+ num.u = num.u * 10U + (unsigned int)ksh_numdig(c);
/* now: num.u <= 2147483649U */
} while ((c = *s++));
@@ -579,7 +562,7 @@
sp = cp;
simplify_gmatch_pat1a:
dp = cp;
- se = sp + strlen((const void *)sp);
+ se = strnul(sp);
while ((c = *sp++)) {
if (!ISMAGIC(c)) {
*dp++ = c;
@@ -651,13 +634,12 @@
if (s == NULL || p == NULL)
return (0);
- se = s + strlen(s);
- pe = p + strlen(p);
+ pe = strnul(p);
/*
* isfile is false iff no syntax check has been done on
- * the pattern. If check fails, just to a strcmp().
+ * the pattern. If check fails, just do a strcmp().
*/
- if (!isfile && !has_globbing(p, pe)) {
+ if (!isfile && !has_globbing(p)) {
size_t len = pe - p + 1;
char tbuf[64];
char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
@@ -664,6 +646,7 @@
debunk(t, p, len);
return (!strcmp(t, s));
}
+ se = strnul(s);
/*
* since the do_gmatch() engine sucks so much, we must do some
@@ -670,10 +653,11 @@
* pattern simplifications
*/
pnew = simplify_gmatch_pattern((const unsigned char *)p);
- pe = pnew + strlen(pnew);
+ pe = strnul(pnew);
rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
- (const unsigned char *)pnew, (const unsigned char *)pe);
+ (const unsigned char *)pnew, (const unsigned char *)pe,
+ (const unsigned char *)s);
afree(pnew, ATEMP);
return (rv);
}
@@ -684,7 +668,7 @@
* Syntax errors are:
* - [ with no closing ]
* - imbalanced $(...) expression
- * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
+ * - [...] and *(...) not nested (eg, @(a[b|)c], *(a[b|c]d))
*/
/*XXX
* - if no magic,
@@ -695,76 +679,101 @@
* return ?
* - return ?
*/
-int
-has_globbing(const char *xp, const char *xpe)
+bool
+has_globbing(const char *pat)
{
- const unsigned char *p = (const unsigned char *) xp;
- const unsigned char *pe = (const unsigned char *) xpe;
- int c;
- int nest = 0, bnest = 0;
+ unsigned char c, subc;
bool saw_glob = false;
- /* inside [...] */
- bool in_bracket = false;
+ unsigned int nest = 0;
+ const unsigned char *p = (const unsigned char *)pat;
+ const unsigned char *s;
- for (; p < pe; p++) {
- if (!ISMAGIC(*p))
+ while ((c = *p++)) {
+ /* regular character? ok. */
+ if (!ISMAGIC(c))
continue;
- if ((c = *++p) == '*' || c == '?')
+ /* MAGIC + NUL? abort. */
+ if (!(c = *p++))
+ return (false);
+ /* some specials */
+ if (ord(c) == ORD('*') || ord(c) == ORD('?')) {
+ /* easy glob, accept */
saw_glob = true;
- else if (c == '[') {
- if (!in_bracket) {
- saw_glob = true;
- in_bracket = true;
- if (ISMAGIC(p[1]) && p[2] == '!')
- p += 2;
- if (ISMAGIC(p[1]) && p[2] == ']')
- p += 2;
+ } else if (ord(c) == ORD('[')) {
+ /* bracket expression; eat negation and initial ] */
+ if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!'))
+ p += 2;
+ if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
+ p += 2;
+ /* check next string part */
+ s = p;
+ while ((c = *s++)) {
+ /* regular chars are ok */
+ if (!ISMAGIC(c))
+ continue;
+ /* MAGIC + NUL cannot happen */
+ if (!(c = *s++))
+ return (false);
+ /* terminating bracket? */
+ if (ord(c) == ORD(']')) {
+ /* accept and continue */
+ p = s;
+ saw_glob = true;
+ break;
+ }
+ /* sub-bracket expressions */
+ if (ord(c) == ORD('[') && (
+ /* collating element? */
+ ord(*s) == ORD('.') ||
+ /* equivalence class? */
+ ord(*s) == ORD('=') ||
+ /* character class? */
+ ord(*s) == ORD(':'))) {
+ /* must stop with exactly the same c */
+ subc = *s++;
+ /* arbitrarily many chars in betwixt */
+ while ((c = *s++))
+ /* but only this sequence... */
+ if (c == subc && ISMAGIC(*s) &&
+ ord(s[1]) == ORD(']')) {
+ /* accept, terminate */
+ s += 2;
+ break;
+ }
+ /* EOS without: reject bracket expr */
+ if (!c)
+ break;
+ /* continue; */
+ }
+ /* anything else just goes on */
}
- /*XXX Do we need to check ranges here? POSIX Q */
- } else if (c == ']') {
- if (in_bracket) {
- if (bnest)
- /* [a*(b]) */
- return (0);
- in_bracket = false;
- }
- } else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) {
+ } else if ((c & 0x80) && ctype(c & 0x7F, C_PATMO | C_SPC)) {
+ /* opening pattern */
saw_glob = true;
- if (in_bracket)
- bnest++;
- else
- nest++;
- } else if (c == '|') {
- if (in_bracket && !bnest)
- /* *(a[foo|bar]) */
- return (0);
- } else if (c == /*(*/ ')') {
- if (in_bracket) {
- if (!bnest--)
- /* *(a[b)c] */
- return (0);
- } else if (nest)
- nest--;
+ ++nest;
+ } else if (ord(c) == ORD(/*(*/ ')')) {
+ /* closing pattern */
+ if (nest)
+ --nest;
}
- /*
- * else must be a MAGIC-MAGIC, or MAGIC-!,
- * MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-}
- */
}
- return (saw_glob && !in_bracket && !nest);
+ return (saw_glob && !nest);
}
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
static int
do_gmatch(const unsigned char *s, const unsigned char *se,
- const unsigned char *p, const unsigned char *pe)
+ const unsigned char *p, const unsigned char *pe,
+ const unsigned char *smin)
{
- unsigned char sc, pc;
+ unsigned char sc, pc, sl = 0;
const unsigned char *prest, *psub, *pnext;
const unsigned char *srest;
if (s == NULL || p == NULL)
return (0);
+ if (s > smin && s <= se)
+ sl = s[-1];
while (p < pe) {
pc = *p++;
sc = s < se ? *s : '\0';
@@ -772,15 +781,39 @@
if (!ISMAGIC(pc)) {
if (sc != pc)
return (0);
+ sl = sc;
continue;
}
- switch (*p++) {
- case '[':
- if (sc == 0 || (p = cclass(p, sc)) == NULL)
+ switch (ord(*p++)) {
+ case ORD('['):
+ /* BSD cclass extension? */
+ if (ISMAGIC(p[0]) && ord(p[1]) == ORD('[') &&
+ ord(p[2]) == ORD(':') &&
+ ctype((pc = p[3]), C_ANGLE) &&
+ ord(p[4]) == ORD(':') &&
+ ISMAGIC(p[5]) && ord(p[6]) == ORD(']') &&
+ ISMAGIC(p[7]) && ord(p[8]) == ORD(']')) {
+ /* zero-length match */
+ --s;
+ p += 9;
+ /* word begin? */
+ if (ord(pc) == ORD('<') &&
+ !ctype(sl, C_ALNUX) &&
+ ctype(sc, C_ALNUX))
+ break;
+ /* word end? */
+ if (ord(pc) == ORD('>') &&
+ ctype(sl, C_ALNUX) &&
+ !ctype(sc, C_ALNUX))
+ break;
+ /* neither */
return (0);
+ }
+ if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL)
+ return (0);
break;
- case '?':
+ case ORD('?'):
if (sc == 0)
return (0);
if (UTFMODE) {
@@ -789,39 +822,39 @@
}
break;
- case '*':
+ case ORD('*'):
if (p == pe)
return (1);
s--;
do {
- if (do_gmatch(s, se, p, pe))
+ if (do_gmatch(s, se, p, pe, smin))
return (1);
} while (s++ < se);
return (0);
/**
- * [*+?@!](pattern|pattern|..)
+ * [+*?@!](pattern|pattern|..)
* This is also needed for ${..%..}, etc.
*/
/* matches one or more times */
- case 0x80|'+':
+ case ORD('+') | 0x80:
/* matches zero or more times */
- case 0x80|'*':
+ case ORD('*') | 0x80:
if (!(prest = pat_scan(p, pe, false)))
return (0);
s--;
/* take care of zero matches */
- if (p[-1] == (0x80 | '*') &&
- do_gmatch(s, se, prest, pe))
+ if (ord(p[-1]) == (0x80 | ORD('*')) &&
+ do_gmatch(s, se, prest, pe, smin))
return (1);
for (psub = p; ; psub = pnext) {
pnext = pat_scan(psub, pe, true);
for (srest = s; srest <= se; srest++) {
- if (do_gmatch(s, srest, psub, pnext - 2) &&
- (do_gmatch(srest, se, prest, pe) ||
- (s != srest && do_gmatch(srest,
- se, p - 2, pe))))
+ if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
+ (do_gmatch(srest, se, prest, pe, smin) ||
+ (s != srest &&
+ do_gmatch(srest, se, p - 2, pe, smin))))
return (1);
}
if (pnext == prest)
@@ -830,24 +863,24 @@
return (0);
/* matches zero or once */
- case 0x80|'?':
+ case ORD('?') | 0x80:
/* matches one of the patterns */
- case 0x80|'@':
+ case ORD('@') | 0x80:
/* simile for @ */
- case 0x80|' ':
+ case ORD(' ') | 0x80:
if (!(prest = pat_scan(p, pe, false)))
return (0);
s--;
/* Take care of zero matches */
- if (p[-1] == (0x80 | '?') &&
- do_gmatch(s, se, prest, pe))
+ if (ord(p[-1]) == (0x80 | ORD('?')) &&
+ do_gmatch(s, se, prest, pe, smin))
return (1);
for (psub = p; ; psub = pnext) {
pnext = pat_scan(psub, pe, true);
srest = prest == pe ? se : s;
for (; srest <= se; srest++) {
- if (do_gmatch(s, srest, psub, pnext - 2) &&
- do_gmatch(srest, se, prest, pe))
+ if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
+ do_gmatch(srest, se, prest, pe, smin))
return (1);
}
if (pnext == prest)
@@ -856,7 +889,7 @@
return (0);
/* matches none of the patterns */
- case 0x80|'!':
+ case ORD('!') | 0x80:
if (!(prest = pat_scan(p, pe, false)))
return (0);
s--;
@@ -866,7 +899,7 @@
for (psub = p; ; psub = pnext) {
pnext = pat_scan(psub, pe, true);
if (do_gmatch(s, srest, psub,
- pnext - 2)) {
+ pnext - 2, smin)) {
matched = 1;
break;
}
@@ -874,7 +907,7 @@
break;
}
if (!matched &&
- do_gmatch(srest, se, prest, pe))
+ do_gmatch(srest, se, prest, pe, smin))
return (1);
}
return (0);
@@ -884,55 +917,245 @@
return (0);
break;
}
+ sl = sc;
}
return (s == se);
}
+/*XXX this is a prime example for bsearch or a const hashtable */
+static const struct cclass {
+ const char *name;
+ uint32_t value;
+} cclasses[] = {
+ /* POSIX */
+ { "alnum", C_ALNUM },
+ { "alpha", C_ALPHA },
+ { "blank", C_BLANK },
+ { "cntrl", C_CNTRL },
+ { "digit", C_DIGIT },
+ { "graph", C_GRAPH },
+ { "lower", C_LOWER },
+ { "print", C_PRINT },
+ { "punct", C_PUNCT },
+ { "space", C_SPACE },
+ { "upper", C_UPPER },
+ { "xdigit", C_SEDEC },
+ /* BSD */
+ /* "<" and ">" are handled inline */
+ /* GNU bash */
+ { "ascii", C_ASCII },
+ { "word", C_ALNUX },
+ /* mksh */
+ { "sh_alias", C_ALIAS },
+ { "sh_edq", C_EDQ },
+ { "sh_ifs", C_IFS },
+ { "sh_ifsws", C_IFSWS },
+ { "sh_nl", C_NL },
+ { "sh_quote", C_QUOTE },
+ /* sentinel */
+ { NULL, 0 }
+};
+
static const unsigned char *
-cclass(const unsigned char *p, unsigned char sub)
+gmatch_cclass(const unsigned char *pat, unsigned char sc)
{
- unsigned char c, d;
- bool notp, found = false;
- const unsigned char *orig_p = p;
+ unsigned char c, subc, lc;
+ const unsigned char *p = pat, *s;
+ bool found = false;
+ bool negated = false;
+ char *subp;
- if ((notp = tobool(ISMAGIC(*p) && *++p == '!')))
- p++;
- do {
- c = *p++;
+ /* check for negation */
+ if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!')) {
+ p += 2;
+ negated = true;
+ }
+ /* make initial ] non-MAGIC */
+ if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
+ ++p;
+ /* iterate over bracket expression, debunk()ing on the fly */
+ while ((c = *p++)) {
+ nextc:
+ /* non-regular character? */
if (ISMAGIC(c)) {
- c = *p++;
- if ((c & 0x80) && !ISMAGIC(c)) {
- /* extended pattern matching: *+?@! */
+ /* MAGIC + NUL cannot happen */
+ if (!(c = *p++))
+ break;
+ /* terminating bracket? */
+ if (ord(c) == ORD(']')) {
+ /* accept and return */
+ return (found != negated ? p : NULL);
+ }
+ /* sub-bracket expressions */
+ if (ord(c) == ORD('[') && (
+ /* collating element? */
+ ord(*p) == ORD('.') ||
+ /* equivalence class? */
+ ord(*p) == ORD('=') ||
+ /* character class? */
+ ord(*p) == ORD(':'))) {
+ /* must stop with exactly the same c */
+ subc = *p++;
+ /* save away start of substring */
+ s = p;
+ /* arbitrarily many chars in betwixt */
+ while ((c = *p++))
+ /* but only this sequence... */
+ if (c == subc && ISMAGIC(*p) &&
+ ord(p[1]) == ORD(']')) {
+ /* accept, terminate */
+ p += 2;
+ break;
+ }
+ /* EOS without: reject bracket expr */
+ if (!c)
+ break;
+ /* debunk substring */
+ strndupx(subp, s, p - s - 3, ATEMP);
+ debunk(subp, subp, p - s - 3 + 1);
+ cclass_common:
+ /* whither subexpression */
+ if (ord(subc) == ORD(':')) {
+ const struct cclass *cls = cclasses;
+
+ /* search for name in cclass list */
+ while (cls->name)
+ if (!strcmp(subp, cls->name)) {
+ /* found, match? */
+ if (ctype(sc,
+ cls->value))
+ found = true;
+ /* break either way */
+ break;
+ } else
+ ++cls;
+ /* that's all here */
+ afree(subp, ATEMP);
+ continue;
+ }
+ /* collating element or equivalence class */
+ /* Note: latter are treated as former */
+ if (ctype(subp[0], C_ASCII) && !subp[1])
+ /* [.a.] where a is one ASCII char */
+ c = subp[0];
+ else
+ /* force no match */
+ c = 0;
+ /* no longer needed */
+ afree(subp, ATEMP);
+ } else if (!ISMAGIC(c) && (c & 0x80)) {
+ /* 0x80|' ' is plain (...) */
+ if ((c &= 0x7F) != ' ') {
+ /* check single match NOW */
+ if (sc == c)
+ found = true;
+ /* next character is (...) */
+ }
+ c = '(' /*)*/;
+ }
+ }
+ /* range expression? */
+ if (!(ISMAGIC(p[0]) && ord(p[1]) == ORD('-') &&
+ /* not terminating bracket? */
+ (!ISMAGIC(p[2]) || ord(p[3]) != ORD(']')))) {
+ /* no, check single match */
+ if (sc == c)
+ /* note: sc is never NUL */
+ found = true;
+ /* do the next "first" character */
+ continue;
+ }
+ /* save lower range bound */
+ lc = c;
+ /* skip over the range operator */
+ p += 2;
+ /* do the same shit as above... almost */
+ subc = 0;
+ if (!(c = *p++))
+ break;
+ /* non-regular character? */
+ if (ISMAGIC(c)) {
+ /* MAGIC + NUL cannot happen */
+ if (!(c = *p++))
+ break;
+ /* sub-bracket expressions */
+ if (ord(c) == ORD('[') && (
+ /* collating element? */
+ ord(*p) == ORD('.') ||
+ /* equivalence class? */
+ ord(*p) == ORD('=') ||
+ /* character class? */
+ ord(*p) == ORD(':'))) {
+ /* must stop with exactly the same c */
+ subc = *p++;
+ /* save away start of substring */
+ s = p;
+ /* arbitrarily many chars in betwixt */
+ while ((c = *p++))
+ /* but only this sequence... */
+ if (c == subc && ISMAGIC(*p) &&
+ ord(p[1]) == ORD(']')) {
+ /* accept, terminate */
+ p += 2;
+ break;
+ }
+ /* EOS without: reject bracket expr */
+ if (!c)
+ break;
+ /* debunk substring */
+ strndupx(subp, s, p - s - 3, ATEMP);
+ debunk(subp, subp, p - s - 3 + 1);
+ /* whither subexpression */
+ if (ord(subc) == ORD(':')) {
+ /* oops, not a range */
+
+ /* match single previous char */
+ if (lc && (sc == lc))
+ found = true;
+ /* match hyphen-minus */
+ if (ord(sc) == ORD('-'))
+ found = true;
+ /* handle cclass common part */
+ goto cclass_common;
+ }
+ /* collating element or equivalence class */
+ /* Note: latter are treated as former */
+ if (ctype(subp[0], C_ASCII) && !subp[1])
+ /* [.a.] where a is one ASCII char */
+ c = subp[0];
+ else
+ /* force no match */
+ c = 0;
+ /* no longer needed */
+ afree(subp, ATEMP);
+ /* other meaning below */
+ subc = 0;
+ } else if (c == (0x80 | ' ')) {
+ /* 0x80|' ' is plain (...) */
+ c = '(' /*)*/;
+ } else if (!ISMAGIC(c) && (c & 0x80)) {
c &= 0x7F;
- /* XXX the ( char isn't handled as part of [] */
- if (c == ' ')
- /* simile for @: plain (..) */
- c = '(' /*)*/;
+ subc = '(' /*)*/;
}
}
- if (c == '\0')
- /* No closing ] - act as if the opening [ was quoted */
- return (sub == '[' ? orig_p : NULL);
- if (ISMAGIC(p[0]) && p[1] == '-' &&
- (!ISMAGIC(p[2]) || p[3] != ']')) {
- /* MAGIC- */
- p += 2;
- d = *p++;
- if (ISMAGIC(d)) {
- d = *p++;
- if ((d & 0x80) && !ISMAGIC(d))
- d &= 0x7f;
- }
- /* POSIX says this is an invalid expression */
- if (c > d)
- return (NULL);
- } else
- d = c;
- if (c == sub || (c <= sub && sub <= d))
+ /* now do the actual range match check */
+ if (lc != 0 /* && c != 0 */ &&
+ asciibetical(lc) <= asciibetical(sc) &&
+ asciibetical(sc) <= asciibetical(c))
found = true;
- } while (!(ISMAGIC(p[0]) && p[1] == ']'));
-
- return ((found != notp) ? p+2 : NULL);
+ /* forced next character? */
+ if (subc) {
+ c = subc;
+ goto nextc;
+ }
+ /* otherwise, just go on with the pattern string */
+ }
+ /* if we broke here, the bracket expression was invalid */
+ if (ord(sc) == ORD('['))
+ /* initial opening bracket as literal match */
+ return (pat);
+ /* or rather no match */
+ return (NULL);
}
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
@@ -947,7 +1170,7 @@
if ((*++p == /*(*/ ')' && nest-- == 0) ||
(*p == '|' && match_sep && nest == 0))
return (p + 1);
- if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f))
+ if ((*p & 0x80) && ctype(*p & 0x7F, C_PATMO | C_SPC))
nest++;
}
return (NULL);
@@ -954,11 +1177,25 @@
}
int
-xstrcmp(const void *p1, const void *p2)
+ascstrcmp(const void *s1, const void *s2)
{
- return (strcmp(*(const char * const *)p1, *(const char * const *)p2));
+ const uint8_t *cp1 = s1, *cp2 = s2;
+
+ while (*cp1 == *cp2) {
+ if (*cp1++ == '\0')
+ return (0);
+ ++cp2;
+ }
+ return ((int)asciibetical(*cp1) - (int)asciibetical(*cp2));
}
+int
+ascpstrcmp(const void *pstr1, const void *pstr2)
+{
+ return (ascstrcmp(*(const char * const *)pstr1,
+ *(const char * const *)pstr2));
+}
+
/* Initialise a Getopt structure */
void
ksh_getopt_reset(Getopt *go, int flags)
@@ -1007,7 +1244,7 @@
const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
go->p = 1;
- if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
+ if (flag == '-' && ksh_isdash(arg + 1)) {
go->optind++;
go->p = 0;
go->info |= GI_MINUSMINUS;
@@ -1026,16 +1263,16 @@
go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
}
go->p++;
- if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
+ if (ctype(c, C_QUEST | C_COLON | C_HASH) || c == ';' || c == ',' ||
!(o = cstrchr(optionsp, c))) {
if (optionsp[0] == ':') {
go->buf[0] = c;
go->optarg = go->buf;
} else {
- warningf(true, "%s%s-%c: %s",
+ warningf(true, Tf_optfoo,
(go->flags & GF_NONAME) ? "" : argv[0],
- (go->flags & GF_NONAME) ? "" : ": ", c,
- "unknown option");
+ (go->flags & GF_NONAME) ? "" : Tcolsp,
+ c, Tunknown_option);
if (go->flags & GF_ERROR)
bi_errorfz();
}
@@ -1060,10 +1297,10 @@
go->optarg = go->buf;
return (':');
}
- warningf(true, "%s%s-%c: %s",
+ warningf(true, Tf_optfoo,
(go->flags & GF_NONAME) ? "" : argv[0],
- (go->flags & GF_NONAME) ? "" : ": ", c,
- "requires an argument");
+ (go->flags & GF_NONAME) ? "" : Tcolsp,
+ c, Treq_arg);
if (go->flags & GF_ERROR)
bi_errorfz();
return ('?');
@@ -1080,13 +1317,14 @@
* argument is missing.
*/
if (argv[go->optind - 1][go->p]) {
- if (ksh_isdigit(argv[go->optind - 1][go->p])) {
+ if (ctype(argv[go->optind - 1][go->p], C_DIGIT)) {
go->optarg = argv[go->optind - 1] + go->p;
go->p = 0;
} else
go->optarg = NULL;
} else {
- if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
+ if (argv[go->optind] &&
+ ctype(argv[go->optind][0], C_DIGIT)) {
go->optarg = argv[go->optind++];
go->p = 0;
} else
@@ -1109,8 +1347,8 @@
bool inquote = true;
/* first, check whether any quotes are needed */
- while ((c = *p++) >= 32)
- if (ctype(c, C_QUOTE))
+ while (rtt2asc(c = *p++) >= 32)
+ if (ctype(c, C_QUOTE | C_SPC))
inquote = false;
p = (const unsigned char *)s;
@@ -1148,6 +1386,7 @@
shf_putc('$', shf);
shf_putc('\'', shf);
while ((c = *p) != 0) {
+#ifndef MKSH_EBCDIC
if (c >= 0xC2) {
n = utf_mbtowc(&wc, (const char *)p);
if (n != (size_t)-1) {
@@ -1156,10 +1395,11 @@
continue;
}
}
+#endif
++p;
switch (c) {
/* see unbksl() in this file for comments */
- case 7:
+ case KSH_BEL:
c = 'a';
if (0)
/* FALLTHROUGH */
@@ -1183,11 +1423,11 @@
c = 't';
if (0)
/* FALLTHROUGH */
- case 11:
+ case KSH_VTAB:
c = 'v';
if (0)
/* FALLTHROUGH */
- case '\033':
+ case KSH_ESC:
/* take E not e because \e is \ in *roff */
c = 'E';
/* FALLTHROUGH */
@@ -1197,7 +1437,12 @@
if (0)
/* FALLTHROUGH */
default:
- if (c < 32 || c > 0x7E) {
+#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
+ if (ksh_isctrl(c))
+#else
+ if (!ctype(c, C_PRINT))
+#endif
+ {
/* FALLTHROUGH */
case '\'':
shf_fprintf(shf, "\\%03o", c);
@@ -1219,11 +1464,11 @@
* the i-th element
*/
void
-print_columns(struct shf *shf, unsigned int n,
- char *(*func)(char *, size_t, unsigned int, const void *),
- const void *arg, size_t max_oct, size_t max_colz, bool prefcol)
+print_columns(struct columnise_opts *opts, unsigned int n,
+ void (*func)(char *, size_t, unsigned int, const void *),
+ const void *arg, size_t max_oct, size_t max_colz)
{
- unsigned int i, r, c, rows, cols, nspace, max_col;
+ unsigned int i, r = 0, c, rows, cols, nspace, max_col;
char *str;
if (!n)
@@ -1249,72 +1494,78 @@
str = alloc(max_oct, ATEMP);
/*
- * We use (max_col + 1) to consider the space separator.
- * Note that no space is printed after the last column
- * to avoid problems with terminals that have auto-wrap.
+ * We use (max_col + 2) to consider the separator space.
+ * Note that no spaces are printed after the last column
+ * to avoid problems with terminals that have auto-wrap,
+ * but we need to also take this into account in x_cols.
*/
- cols = x_cols / (max_col + 1);
+ cols = (x_cols + 1) / (max_col + 2);
/* if we can only print one column anyway, skip the goo */
if (cols < 2) {
- for (i = 0; i < n; ++i)
- shf_fprintf(shf, "%s\n",
- (*func)(str, max_oct, i, arg));
+ goto prcols_easy;
+ while (r < n) {
+ shf_putc(opts->linesep, opts->shf);
+ prcols_easy:
+ (*func)(str, max_oct, r++, arg);
+ shf_puts(str, opts->shf);
+ }
goto out;
}
rows = (n + cols - 1) / cols;
- if (prefcol && cols > rows) {
+ if (opts->prefcol && cols > rows) {
cols = rows;
rows = (n + cols - 1) / cols;
}
nspace = (x_cols - max_col * cols) / cols;
+ if (nspace < 2)
+ nspace = 2;
max_col = -max_col;
- if (nspace <= 0)
- nspace = 1;
- for (r = 0; r < rows; r++) {
+ goto prcols_hard;
+ while (r < rows) {
+ shf_putchar(opts->linesep, opts->shf);
+ prcols_hard:
for (c = 0; c < cols; c++) {
- i = c * rows + r;
- if (i < n) {
- shf_fprintf(shf, "%*s", max_col,
- (*func)(str, max_oct, i, arg));
- if (c + 1 < cols)
- shf_fprintf(shf, "%*s", nspace, null);
- }
+ if ((i = c * rows + r) >= n)
+ break;
+ (*func)(str, max_oct, i, arg);
+ if (i + rows >= n)
+ shf_puts(str, opts->shf);
+ else
+ shf_fprintf(opts->shf, "%*s%*s",
+ (int)max_col, str, (int)nspace, null);
}
- shf_putchar('\n', shf);
+ ++r;
}
out:
+ if (opts->do_last)
+ shf_putchar(opts->linesep, opts->shf);
afree(str, ATEMP);
}
-/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
+/* strip all NUL bytes from buf; output is NUL-terminated if stripped */
void
-strip_nuls(char *buf, int nbytes)
+strip_nuls(char *buf, size_t len)
{
- char *dst;
+ char *cp, *dp, *ep;
- /*
- * nbytes check because some systems (older FreeBSDs) have a
- * buggy memchr()
- */
- if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
- char *end = buf + nbytes;
- char *p, *q;
+ if (!len || !(dp = memchr(buf, '\0', len)))
+ return;
- for (p = dst; p < end; p = q) {
- /* skip a block of nulls */
- while (++p < end && *p == '\0')
- ;
- /* find end of non-null block */
- if (!(q = memchr(p, '\0', end - p)))
- q = end;
- memmove(dst, p, q - p);
- dst += q - p;
- }
- *dst = '\0';
- }
+ ep = buf + len;
+ cp = dp;
+
+ cp_has_nul_byte:
+ while (cp++ < ep && *cp == '\0')
+ ; /* nothing */
+ while (cp < ep && *cp != '\0')
+ *dp++ = *cp++;
+ if (cp < ep)
+ goto cp_has_nul_byte;
+
+ *dp = '\0';
}
/*
@@ -1407,14 +1658,23 @@
/* max. recursion depth */
int symlinks = 32;
- if (upath[0] == '/') {
+ if (mksh_abspath(upath)) {
/* upath is an absolute pathname */
strdupx(ipath, upath, ATEMP);
+#ifdef MKSH_DOSPATH
+ } else if (mksh_drvltr(upath)) {
+ /* upath is a drive-relative pathname */
+ 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);
+#endif
} else {
/* upath is a relative pathname, prepend cwd */
- if ((tp = ksh_get_wd()) == NULL || tp[0] != '/')
+ if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
return (NULL);
- ipath = shf_smprintf("%s%s%s", tp, "/", upath);
+ ipath = shf_smprintf(Tf_sss, tp, "/", upath);
afree(tp, ATEMP);
}
@@ -1426,7 +1686,7 @@
while (*ip) {
/* skip slashes in input */
- while (*ip == '/')
+ while (mksh_cdirsep(*ip))
++ip;
if (!*ip)
break;
@@ -1433,7 +1693,7 @@
/* get next pathname component from input */
tp = ip;
- while (*ip && *ip != '/')
+ while (*ip && !mksh_cdirsep(*ip))
++ip;
len = ip - tp;
@@ -1444,8 +1704,9 @@
continue;
else if (len == 2 && tp[1] == '.') {
/* strip off last pathname component */
+ /*XXX consider a rooted pathname */
while (xp > Xstring(xs, xp))
- if (*--xp == '/')
+ if (mksh_cdirsep(*--xp))
break;
/* then continue with the next one */
continue;
@@ -1468,7 +1729,7 @@
/* lstat failed */
if (errno == ENOENT) {
/* because the pathname does not exist */
- while (*ip == '/')
+ while (mksh_cdirsep(*ip))
/* skip any trailing slashes */
++ip;
/* no more components left? */
@@ -1511,11 +1772,23 @@
* restart if symlink target is an absolute path,
* otherwise continue with currently resolved prefix
*/
+#ifdef MKSH_DOSPATH
+ assemble_symlink:
+#endif
/* append rest of current input path to link target */
- tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
+ tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
afree(ipath, ATEMP);
ip = ipath = tp;
- if (ldest[0] != '/') {
+ if (!mksh_abspath(ipath)) {
+#ifdef MKSH_DOSPATH
+ /* symlink target might be drive-relative */
+ if (mksh_drvltr(ipath)) {
+ if (getdrvwd(&ldest, ord(*ipath)))
+ goto notfound;
+ ip += 2;
+ goto assemble_symlink;
+ }
+#endif
/* symlink target is a relative path */
xp = Xrestpos(xs, xp, pos);
} else
@@ -1524,14 +1797,22 @@
/* symlink target is an absolute path */
xp = Xstring(xs, xp);
beginning_of_a_pathname:
- /* assert: (ip == ipath)[0] == '/' */
+ /* assert: mksh_abspath(ip == ipath) */
/* assert: xp == xs.beg => start of path */
/* exactly two leading slashes? (SUSv4 3.266) */
- if (ip[1] == '/' && ip[2] != '/') {
+ if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
/* keep them, e.g. for UNC pathnames */
Xput(xs, xp, '/');
}
+#ifdef MKSH_DOSPATH
+ /* drive letter? */
+ if (mksh_drvltr(ip)) {
+ /* keep it */
+ Xput(xs, xp, *ip++);
+ Xput(xs, xp, *ip++);
+ }
+#endif
}
}
/* otherwise (no symlink) merely go on */
@@ -1553,7 +1834,7 @@
* if source path had a trailing slash, check if target path
* is not a non-directory existing file
*/
- if (ip > ipath && ip[-1] == '/') {
+ if (ip > ipath && mksh_cdirsep(ip[-1])) {
if (stat(Xstring(xs, xp), &sb)) {
if (errno != ENOENT)
goto notfound;
@@ -1565,8 +1846,7 @@
}
/* return target path */
- if (ldest != NULL)
- afree(ldest, ATEMP);
+ afree(ldest, ATEMP);
afree(ipath, ATEMP);
return (Xclose(xs, xp));
@@ -1573,8 +1853,7 @@
notfound:
/* save; freeing memory might trash it */
llen = errno;
- if (ldest != NULL)
- afree(ldest, ATEMP);
+ afree(ldest, ATEMP);
afree(ipath, ATEMP);
Xfree(xs, xp);
errno = llen;
@@ -1615,7 +1894,7 @@
if (!file)
file = null;
- if (file[0] == '/') {
+ if (mksh_abspath(file)) {
*phys_pathp = 0;
use_cdpath = false;
} else {
@@ -1624,7 +1903,7 @@
if (c == '.')
c = file[2];
- if (c == '/' || c == '\0')
+ if (mksh_cdirsep(c) || c == '\0')
use_cdpath = false;
}
@@ -1632,21 +1911,21 @@
if (!plist)
use_cdpath = false;
else if (use_cdpath) {
- char *pend;
+ char *pend = plist;
- for (pend = plist; *pend && *pend != ':'; pend++)
- ;
+ while (*pend && *pend != MKSH_PATHSEPC)
+ ++pend;
plen = pend - plist;
*cdpathp = *pend ? pend + 1 : NULL;
}
- if ((!use_cdpath || !plen || plist[0] != '/') &&
+ if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
(cwd && *cwd)) {
len = strlen(cwd);
XcheckN(*xsp, xp, len);
memcpy(xp, cwd, len);
xp += len;
- if (cwd[len - 1] != '/')
+ if (!mksh_cdirsep(cwd[len - 1]))
Xput(*xsp, xp, '/');
}
*phys_pathp = Xlength(*xsp, xp);
@@ -1654,7 +1933,7 @@
XcheckN(*xsp, xp, plen);
memcpy(xp, plist, plen);
xp += plen;
- if (plist[plen - 1] != '/')
+ if (!mksh_cdirsep(plist[plen - 1]))
Xput(*xsp, xp, '/');
rval = 1;
}
@@ -1683,6 +1962,15 @@
* .. ..
* ./foo foo
* foo/../../../bar ../../bar
+ * C:/foo/../.. C:/
+ * C:. C:
+ * C:.. C:..
+ * C:foo/../../blah C:../blah
+ *
+ * XXX consider a rooted pathname: we cannot really 'cd ..' for
+ * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
+ * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
+ * we need to honour this throughout the shell
*/
void
simplify_path(char *p)
@@ -1690,13 +1978,27 @@
char *dp, *ip, *sp, *tp;
size_t len;
bool needslash;
+#ifdef MKSH_DOSPATH
+ bool needdot = true;
+ /* keep drive letter */
+ if (mksh_drvltr(p)) {
+ p += 2;
+ needdot = false;
+ }
+#else
+#define needdot true
+#endif
+
switch (*p) {
case 0:
return;
case '/':
+#ifdef MKSH_DOSPATH
+ case '\\':
+#endif
/* exactly two leading slashes? (SUSv4 3.266) */
- if (p[1] == '/' && p[2] != '/')
+ if (p[1] == p[0] && !mksh_cdirsep(p[2]))
/* keep them, e.g. for UNC pathnames */
++p;
needslash = true;
@@ -1708,7 +2010,7 @@
while (*ip) {
/* skip slashes in input */
- while (*ip == '/')
+ while (mksh_cdirsep(*ip))
++ip;
if (!*ip)
break;
@@ -1715,7 +2017,7 @@
/* get next pathname component from input */
tp = ip;
- while (*ip && *ip != '/')
+ while (*ip && !mksh_cdirsep(*ip))
++ip;
len = ip - tp;
@@ -1725,8 +2027,8 @@
/* just continue with the next one */
continue;
else if (len == 2 && tp[1] == '.') {
- /* parent level, but how? */
- if (*p == '/')
+ /* parent level, but how? (see above) */
+ if (mksh_abspath(p))
/* absolute path, only one way */
goto strip_last_component;
else if (dp > sp) {
@@ -1735,7 +2037,7 @@
strip_last_component:
/* strip off last pathname component */
while (dp > sp)
- if (*--dp == '/')
+ if (mksh_cdirsep(*--dp))
break;
} else {
/* relative path, at its beginning */
@@ -1764,10 +2066,15 @@
needslash = true;
/* try next component */
}
- if (dp == p)
- /* empty path -> dot */
- *dp++ = needslash ? '/' : '.';
+ if (dp == p) {
+ /* empty path -> dot (or slash, when absolute) */
+ if (needslash)
+ *dp++ = '/';
+ else if (needdot)
+ *dp++ = '.';
+ }
*dp = '\0';
+#undef needdot
}
void
@@ -1816,12 +2123,12 @@
wp += builtin_opt.optind;
if (Flag(FRESTRICTED)) {
- bi_errorf("restricted shell - can't cd");
+ bi_errorf(Tcant_cd);
return (2);
}
- pwd_s = global("PWD");
- oldpwd_s = global("OLDPWD");
+ pwd_s = global(TPWD);
+ oldpwd_s = global(TOLDPWD);
if (!wp[0]) {
/* No arguments - go home */
@@ -1837,7 +2144,7 @@
allocd = NULL;
dir = str_val(oldpwd_s);
if (dir == null) {
- bi_errorf("no OLDPWD");
+ bi_errorf(Tno_OLDPWD);
return (2);
}
printpath = true;
@@ -1858,7 +2165,7 @@
* we don't
*/
if ((cp = strstr(current_wd, wp[0])) == NULL) {
- bi_errorf("bad substitution");
+ bi_errorf(Tbadsubst);
return (2);
}
/*-
@@ -1877,10 +2184,22 @@
memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
printpath = true;
} else {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (2);
}
+#ifdef MKSH_DOSPATH
+ tryp = NULL;
+ if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
+ !getdrvwd(&tryp, ord(*dir))) {
+ dir = shf_smprintf(Tf_sss, tryp,
+ dir[2] ? "/" : "", dir + 2);
+ afree(tryp, ATEMP);
+ afree(allocd, ATEMP);
+ allocd = dir;
+ }
+#endif
+
#ifdef MKSH__NO_PATH_MAX
/* only a first guess; make_path will enlarge xs if necessary */
XinitN(xs, 1024, ATEMP);
@@ -1901,9 +2220,9 @@
if (rv < 0) {
if (cdnode)
- bi_errorf("%s: %s", dir, "bad directory");
+ bi_errorf(Tf_sD_s, dir, "bad directory");
else
- bi_errorf("%s: %s", tryp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
afree(allocd, ATEMP);
Xfree(xs, xp);
return (2);
@@ -1926,7 +2245,7 @@
/* Ignore failure (happens if readonly or integer) */
setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
- if (Xstring(xs, xp)[0] != '/') {
+ if (!mksh_abspath(Xstring(xs, xp))) {
pwd = NULL;
} else if (!physical) {
goto norealpath_PWD;
@@ -1952,7 +2271,7 @@
rv = 1;
}
if (printpath || cdnode)
- shprintf("%s\n", pwd);
+ shprintf(Tf_sN, pwd);
afree(allocd, ATEMP);
Xfree(xs, xp);
@@ -1987,33 +2306,33 @@
memmove(cp + 1, cp, /* /dev/tty */ 8);
dv = cp + 1;
if (stat(dv, &sb)) {
- errorf("%s: %s: %s", "chvt",
+ errorf(Tf_sD_sD_s, "chvt",
"can't find tty", go->optarg);
}
}
}
if (!(sb.st_mode & S_IFCHR))
- errorf("%s: %s: %s", "chvt", "not a char device", dv);
+ errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
#ifndef MKSH_DISABLE_REVOKE_WARNING
#if HAVE_REVOKE
if (revoke(dv))
#endif
- warningf(false, "%s: %s %s", "chvt",
+ warningf(false, Tf_sD_s_s, "chvt",
"new shell is potentially insecure, can't revoke",
dv);
#endif
}
}
- if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) {
+ if ((fd = binopen2(dv, O_RDWR)) < 0) {
sleep(1);
- if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) {
- errorf("%s: %s %s", "chvt", "can't open", dv);
+ if ((fd = binopen2(dv, O_RDWR)) < 0) {
+ errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
}
}
if (go->optarg[0] != '!') {
switch (fork()) {
case -1:
- errorf("%s: %s %s", "chvt", "fork", "failed");
+ errorf(Tf_sD_s_s, "chvt", "fork", "failed");
case 0:
break;
default:
@@ -2021,12 +2340,12 @@
}
}
if (setsid() == -1)
- errorf("%s: %s %s", "chvt", "setsid", "failed");
+ errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
if (go->optarg[0] != '-') {
if (ioctl(fd, TIOCSCTTY, NULL) == -1)
- errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed");
+ errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
if (tcflush(fd, TCIOFLUSH))
- errorf("%s: %s %s", "chvt", "TCIOFLUSH", "failed");
+ errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
}
ksh_dup2(fd, 0, false);
ksh_dup2(fd, 1, false);
@@ -2136,18 +2455,12 @@
int
unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
{
- int wc, i, c, fc;
+ int wc, i, c, fc, n;
fc = (*fg)();
switch (fc) {
case 'a':
- /*
- * according to the comments in pdksh, \007 seems
- * to be more portable than \a (due to HP-UX cc,
- * Ultrix cc, old pcc, etc.) so we avoid the escape
- * sequence altogether in mksh and assume ASCII
- */
- wc = 7;
+ wc = KSH_BEL;
break;
case 'b':
wc = '\b';
@@ -2156,11 +2469,11 @@
if (!cstyle)
goto unknown_escape;
c = (*fg)();
- wc = CTRL(c);
+ wc = ksh_toctrl(c);
break;
case 'E':
case 'e':
- wc = 033;
+ wc = KSH_ESC;
break;
case 'f':
wc = '\f';
@@ -2175,8 +2488,7 @@
wc = '\t';
break;
case 'v':
- /* assume ASCII here as well */
- wc = 11;
+ wc = KSH_VTAB;
break;
case '1':
case '2':
@@ -2199,8 +2511,8 @@
wc = 0;
i = 3;
while (i--)
- if ((c = (*fg)()) >= '0' && c <= '7')
- wc = (wc << 3) + (c - '0');
+ if (ctype((c = (*fg)()), C_OCTAL))
+ wc = (wc << 3) + ksh_numdig(c);
else {
(*fp)(c);
break;
@@ -2209,13 +2521,13 @@
case 'U':
i = 8;
if (/* CONSTCOND */ 0)
- /* FALLTHROUGH */
+ /* FALLTHROUGH */
case 'u':
- i = 4;
+ i = 4;
if (/* CONSTCOND */ 0)
- /* FALLTHROUGH */
+ /* FALLTHROUGH */
case 'x':
- i = cstyle ? -1 : 2;
+ i = cstyle ? -1 : 2;
/**
* x: look for a hexadecimal number with up to
* two (C style: arbitrary) digits; convert
@@ -2224,20 +2536,24 @@
* four (U: eight) digits; convert to Unicode
*/
wc = 0;
- while (i--) {
+ n = 0;
+ while (n < i || i == -1) {
wc <<= 4;
- if ((c = (*fg)()) >= '0' && c <= '9')
- wc += c - '0';
- else if (c >= 'A' && c <= 'F')
- wc += c - 'A' + 10;
- else if (c >= 'a' && c <= 'f')
- wc += c - 'a' + 10;
- else {
+ if (!ctype((c = (*fg)()), C_SEDEC)) {
wc >>= 4;
(*fp)(c);
break;
}
+ if (ctype(c, C_DIGIT))
+ wc += ksh_numdig(c);
+ else if (ctype(c, C_UPPER))
+ wc += ksh_numuc(c) + 10;
+ else
+ wc += ksh_numlc(c) + 10;
+ ++n;
}
+ if (!n)
+ goto unknown_escape;
if ((cstyle && wc > 0xFF) || fc != 'x')
/* Unicode marker */
wc += 0x100;
Modified: trunk/contrib/mksh/mksh.1
===================================================================
--- trunk/contrib/mksh/mksh.1 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/mksh.1 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.344.2.3 2015/03/01 15:43:03 tg Exp $
-.\" $OpenBSD: ksh.1,v 1.156 2015/01/16 15:32:32 schwarze Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.451 2017/08/16 21:40:14 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
-.\" Thorsten “mirabilos” Glaser <tg at mirbsd.org>
+.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
+.\" mirabilos <m at mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
.\" are retained or reproduced in an accompanying document, permission
@@ -29,7 +29,9 @@
.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
.\" * \(en does not work in nroff, so use \*(en
.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
-.\" Also make sure to use \& especially with two-letter words.
+.\" Also make sure to use \& *before* a punctuation char that is to not
+.\" be interpreted as punctuation, and especially with two-letter words
+.\" but also (after) a period that does not end a sentence (“e.g.\&”).
.\" The section after the "doc" macropackage has been loaded contains
.\" additional code to convene between the UCB mdoc macropackage (and
.\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
@@ -74,7 +76,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: March 1 2015 $
+.Dd $Mdocdate: August 16 2017 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
@@ -179,23 +181,13 @@
Its command language is a superset of the
.Xr sh C
shell language and largely compatible to the original Korn shell.
-.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.
+At times, this manual page may give scripting advice; while it
+sometimes does take portable shell scripting or various standards
+into account all information is first and foremost presented with
+.Nm
+in mind and should be taken as such.
+.Ss I use Android, OS/2, etc. so what...?
+Please see the FAQ at the end of this document.
.Ss Invocation
Most builtins can be called directly, for example if a link points from its
name to the shell; not all make sense, have been tested or work at all though.
@@ -216,7 +208,7 @@
.Xr tty 4 .
An interactive shell has job control enabled, ignores the
.Dv SIGINT ,
-.Dv SIGQUIT ,
+.Dv SIGQUIT
and
.Dv SIGTERM
signals, and prints prompts before reading input (see the
@@ -279,7 +271,7 @@
.It
The
.Ev SHELL ,
-.Ev ENV ,
+.Ev ENV
and
.Ev PATH
parameters cannot be changed.
@@ -293,10 +285,10 @@
can't be used.
.It
Redirections that create files can't be used (i.e.\&
-.Ql \*(Gt ,
-.Ql \*(Gt\*(Ba ,
-.Ql \*(Gt\*(Gt ,
-.Ql \*(Lt\*(Gt ) .
+.Dq Li \*(Gt ,
+.Dq Li \*(Gt\*(Ba ,
+.Dq Li \*(Gt\*(Gt ,
+.Dq Li \*(Lt\*(Gt ) .
.El
.It Fl s
The shell reads commands from standard input; all non-option arguments
@@ -316,12 +308,12 @@
Unless
.Ar name
begins with an exclamation mark
-.Pq Sq \&! ,
+.Pq Ql \&! ,
this is done in a subshell and returns immediately.
If
.Ar name
is a dash
-.Pq Sq \&\- ,
+.Pq Ql \&\- ,
detach from controlling terminal (daemonise) instead.
.El
.Pp
@@ -353,7 +345,7 @@
command line could not be opened, or non-zero if a fatal syntax error
occurred during the execution of a script.
In the absence of fatal errors,
-the exit status is that of the last command executed, or zero, if no
+the exit status is that of the last command executed, or zero if no
command is executed.
.Ss Startup files
For the actual location of these files, see
@@ -364,7 +356,7 @@
A non-privileged interactive shell checks the value of the
.Ev ENV
parameter after subjecting it to parameter, command, arithmetic and tilde
-.Pq Sq \*(TI
+.Pq Ql \*(TI
substitution; if unset or empty, the user mkshrc profile is processed;
otherwise, if a file whose name is the substitution result exists,
it is processed; non-existence is silently ignored.
@@ -376,7 +368,7 @@
combinations, then breaking it into
.Em words .
Words (which are sequences of characters) are delimited by unquoted whitespace
-characters (space, tab, and newline) or meta-characters
+characters (space, tab and newline) or meta-characters
.Po
.Ql \*(Lt ,
.Ql \*(Gt ,
@@ -383,7 +375,7 @@
.Ql \*(Ba ,
.Ql \&; ,
.Ql \&( ,
-.Ql \&) ,
+.Ql \&)
and
.Ql &
.Pc .
@@ -391,56 +383,56 @@
usually delimit commands.
The meta-characters are used in building the following
.Em tokens :
-.Ql \*(Lt ,
-.Ql \*(Lt& ,
-.Ql \*(Lt\*(Lt ,
-.Ql \*(Lt\*(Lt\*(Lt ,
-.Ql \*(Gt ,
-.Ql \*(Gt& ,
-.Ql \*(Gt\*(Gt ,
-.Ql &\*(Gt ,
+.Dq Li \*(Lt ,
+.Dq Li \*(Lt& ,
+.Dq Li \*(Lt\*(Lt ,
+.Dq Li \*(Lt\*(Lt\*(Lt ,
+.Dq Li \*(Gt ,
+.Dq Li \*(Gt& ,
+.Dq Li \*(Gt\*(Gt ,
+.Dq Li &\*(Gt ,
etc. are used to specify redirections (see
.Sx Input/output redirection
below);
-.Ql \*(Ba
+.Dq Li \*(Ba
is used to create pipelines;
-.Ql \*(Ba&
+.Dq Li \*(Ba&
is used to create co-processes (see
.Sx Co-processes
below);
-.Ql \&;
+.Dq Li \&;
is used to separate commands;
-.Ql &
+.Dq Li &
is used to create asynchronous pipelines;
-.Ql &&
+.Dq Li &&
and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
are used to specify conditional execution;
-.Ql ;; ,
-.Ql ;&\&
+.Dq Li \&;; ,
+.Dq Li \&;&
and
-.Ql ;\*(Ba\&
+.Dq Li \&;\*(Ba
are used in
.Ic case
statements;
-.Ql \&(( .. ))
+.Dq Li \&(( ... \&))
is used in arithmetic expressions;
and lastly,
-.Ql \&( .. )\&
+.Dq Li \&( ... \&)
is used to create subshells.
.Pp
Whitespace and meta-characters can be quoted individually using a backslash
-.Pq Sq \e ,
+.Pq Ql \e ,
or in groups using double
-.Pq Sq \&"
+.Pq Ql \&"
or single
-.Pq Sq \*(aq
+.Pq Dq Li \*(aq
quotes.
Note that the following characters are also treated specially by the
shell and must be quoted if they are to represent themselves:
.Ql \e ,
.Ql \&" ,
-.Ql \*(aq ,
+.Dq Li \*(aq ,
.Ql # ,
.Ql $ ,
.Ql \` ,
@@ -448,7 +440,7 @@
.Ql { ,
.Ql } ,
.Ql * ,
-.Ql \&? ,
+.Ql \&?
and
.Ql \&[ .
The first three of these are the above mentioned quoting characters (see
@@ -460,7 +452,7 @@
.Ql #
up to the nearest newline is ignored;
.Ql $
-is used to introduce parameter, command, and arithmetic substitutions (see
+is used to introduce parameter, command and arithmetic substitutions (see
.Sx Substitution
below);
.Ql \`
@@ -476,12 +468,12 @@
.Ql }
delimit
.Xr csh 1 Ns -style
-alterations (see
+alternations (see
.Sx Brace expansion
below);
and finally,
.Ql * ,
-.Ql \&? ,
+.Ql \&?
and
.Ql \&[
are used in file name generation (see
@@ -497,7 +489,7 @@
.Ic for
and
.Ic if
-statements, grouping constructs, and function definitions.
+statements, grouping constructs and function definitions.
.Pp
A simple-command consists of some combination of parameter assignments
(see
@@ -505,12 +497,12 @@
below),
input/output redirections (see
.Sx Input/output redirections
-below),
+below)
and command words; the only restriction is that parameter assignments come
before any command words.
The command words, if any, define the command
that is to be executed and its arguments.
-The command may be a shell built-in command, a function,
+The command may be a shell built-in command, a function
or an external command
(i.e. a separate executable file that is located using the
.Ev PATH
@@ -530,7 +522,7 @@
parameter assignment or 0 if there were no command substitutions.
.Pp
Commands can be chained together using the
-.Ql \*(Ba
+.Dq Li \*(Ba
token to form pipelines, in which the standard output of each command but the
last is piped (see
.Xr pipe 2 )
@@ -546,7 +538,7 @@
.Ic read
builtin's description for implications and workarounds.
A pipeline may be prefixed by the
-.Ql \&!
+.Dq Li \&!
reserved word which causes the exit status of the pipeline to be logically
complemented: if the original status was 0, the complemented status will be 1;
if the original status was not 0, the complemented status will be 0.
@@ -554,12 +546,12 @@
.Em Lists
of commands can be created by separating pipelines by any of the following
tokens:
-.Ql && ,
-.Ql \*(Ba\*(Ba ,
-.Ql & ,
-.Ql \*(Ba& ,
+.Dq Li && ,
+.Dq Li \*(Ba\*(Ba ,
+.Dq Li & ,
+.Dq Li \*(Ba&
and
-.Ql \&; .
+.Dq Li \&; .
The first two are for conditional execution:
.Dq Ar cmd1 No && Ar cmd2
executes
@@ -567,25 +559,25 @@
only if the exit status of
.Ar cmd1
is zero;
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
is the opposite \*(en
.Ar cmd2
is executed only if the exit status of
.Ar cmd1
is non-zero.
-.Ql &&
+.Dq Li &&
and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
have equal precedence which is higher than that of
-.Ql & ,
-.Ql \*(Ba& ,
+.Dq Li & ,
+.Dq Li \*(Ba&
and
-.Ql \&; ,
+.Dq Li \&; ,
which also have equal precedence.
Note that the
-.Ql &&
+.Dq Li &&
and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
operators are
.Qq left-associative .
For example, both of these commands will print only
@@ -596,7 +588,7 @@
.Ed
.Pp
The
-.Ql &
+.Dq Li &
token causes the preceding command to be executed asynchronously; that is,
the shell starts the command but does not wait for it to complete (the shell
does keep track of the status of asynchronous commands; see
@@ -611,20 +603,20 @@
.Pa /dev/null
(however, redirections specified in the asynchronous command have precedence).
The
-.Ql \*(Ba&
+.Dq Li \*(Ba&
operator starts a co-process which is a special kind of asynchronous process
(see
.Sx Co-processes
below).
Note that a command must follow the
-.Ql &&
+.Dq Li &&
and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
operators, while it need not follow
-.Ql & ,
-.Ql \*(Ba& ,
+.Dq Li & ,
+.Dq Li \*(Ba&
or
-.Ql \&; .
+.Dq Li \&; .
The exit status of a list is that of the last command executed, with the
exception of asynchronous lists, for which the exit status is 0.
.Pp
@@ -642,7 +634,7 @@
.Pp
In the following compound command descriptions, command lists (denoted as
.Em list )
-that are followed by reserved words must end with a semicolon, a newline, or
+that are followed by reserved words must end with a semicolon, a newline or
a (syntactically correct) reserved word.
For example, the following are all valid:
.Bd -literal -offset indent
@@ -666,9 +658,9 @@
.Ar list
is executed, but not in a subshell.
Note that
-.Ql {
+.Dq Li {
and
-.Ql }
+.Dq Li }
are reserved words, not meta-characters.
.It Xo case Ar word No in
.Oo Op \&(
@@ -699,7 +691,7 @@
Note that any unquoted space before and after a pattern is
stripped; any space within a pattern must be quoted.
Both the word and the
-patterns are subject to parameter, command, and arithmetic substitution, as
+patterns are subject to parameter, command and arithmetic substitution, as
well as tilde substitution.
.Pp
For historical reasons, open and close braces may be used instead of
@@ -713,11 +705,11 @@
.Ic terminator Ns s
are:
.Bl -tag -width 4n
-.It Ql ;;
+.It Dq Li ;;
Terminate after the list.
-.It Ql ;&\&
+.It Dq Li \&;&
Fall through into the next list.
-.It Ql ;\*(Ba\&
+.It Dq Li \&;\*(Ba
Evaluate the remaining pattern-list tuples.
.El
.Pp
@@ -741,9 +733,8 @@
is executed.
If
.Ic in
-is not used to specify a word list, the positional parameters
-($1, $2, etc.)\&
-are used instead.
+is not used to specify a word list, the positional parameters ($1, $2,
+etc.) are used instead.
For historical reasons, open and close braces may be used instead of
.Ic do
and
@@ -802,8 +793,9 @@
.Ar word Ns (s)
is printed on standard error, followed by a prompt
.Po
-.Ev PS3: normally
-.Sq #?\ \&
+.Ev PS3 :
+normally
+.Dq Li #?\ \&
.Pc .
A number corresponding to one of the enumerated words is then read from
standard input,
@@ -822,15 +814,13 @@
.Ar list
completes, the enumerated list is printed if
.Ev REPLY
-is
-.Dv NULL ,
-the prompt is printed, and so on.
+is empty, the prompt is printed, and so on.
This process continues until an end-of-file
is read, an interrupt is received, or a
.Ic break
statement is executed inside the loop.
If
-.Dq in word ...
+.Dq in Ar word ...
is omitted, the positional parameters are used
(i.e. $1, $2, etc.).
For historical reasons, open and close braces may be used instead of
@@ -909,12 +899,12 @@
The arithmetic expression
.Ar expression
is evaluated; equivalent to
-.Dq let expression
+.Dq Li let \&" Ns Ar expression Ns \&"
(see
.Sx Arithmetic expressions
and the
.Ic let
-command, below).
+command, below) in a compound construct.
.It Bq Bq Ar \ \&expression\ \&
Similar to the
.Ic test
@@ -932,22 +922,22 @@
.Fl o
.Pq OR
operators are replaced with
-.Ql &&
+.Dq Li &&
and
-.Ql \*(Ba\*(Ba ,
+.Dq Li \*(Ba\*(Ba ,
respectively.
.It
Operators (e.g.\&
-.Sq Fl f ,
-.Sq = ,
-.Sq \&! )
+.Dq Li \-f ,
+.Dq Li = ,
+.Dq Li \&! )
must be unquoted.
.It
-Parameter, command, and arithmetic substitutions are performed as expressions
+Parameter, command and arithmetic substitutions are performed as expressions
are evaluated and lazy expression evaluation is used for the
-.Ql &&
+.Dq Li &&
and
-.Ql \*(Ba\*(Ba
+.Dq Li \*(Ba\*(Ba
operators.
This means that in the following statement,
.Ic $(\*(Ltfoo)
@@ -959,9 +949,9 @@
.Ed
.It
The second operand of the
-.Sq !=
+.Dq Li !=
and
-.Sq =
+.Dq Li =
expressions are a subset of patterns (e.g. the comparison
.Ic \&[[ foobar = f*r ]]
succeeds).
@@ -988,33 +978,36 @@
.Ql \e
and the newline are stripped.
Second, a single quote
-.Pq Sq \*(aq
+.Pq Dq Li \*(aq
quotes everything up to the next single quote (this may span lines).
Third, a double quote
-.Pq Sq \&"
+.Pq Ql \&"
quotes all characters, except
.Ql $ ,
-.Ql \`
+.Ql \e
and
-.Ql \e ,
-up to the next unquoted double quote.
+.Ql \` ,
+up to the next unescaped double quote.
.Ql $
and
.Ql \`
-inside double quotes have their usual meaning (i.e. parameter, command, or
-arithmetic substitution) except no field splitting is carried out on the
-results of double-quoted substitutions.
+inside double quotes have their usual meaning (i.e. parameter, arithmetic
+or command substitution) except no field splitting is carried out on the
+results of double-quoted substitutions, and the old-style form of command
+substitution has backslash-quoting for double quotes enabled.
If a
.Ql \e
inside a double-quoted string is followed by
-.Ql \e ,
+.Ql \&" ,
.Ql $ ,
+.Ql \e
+or
.Ql \` ,
-or
-.Ql \&" ,
-it is replaced by the second character; if it is followed by a newline, both
-the
+only the
.Ql \e
+is removed, i.e. the combination is replaced by the second character;
+if it is followed by a newline, both the
+.Ql \e
and the newline are stripped; otherwise, both the
.Ql \e
and the character following are unchanged.
@@ -1026,7 +1019,9 @@
the expanded result is treated as any other single-quoted string.
If a double-quoted string is preceded by an unquoted
.Ql $ ,
-the latter is ignored.
+the
+.Ql $
+is simply ignored.
.Ss Backslash expansion
In places where backslashes are expanded, certain C and
.At
@@ -1035,68 +1030,68 @@
.Nm bash
style escapes are translated.
These include
-.Ql \ea ,
-.Ql \eb ,
-.Ql \ef ,
-.Ql \en ,
-.Ql \er ,
-.Ql \et ,
-.Ql \eU######## ,
-.Ql \eu#### ,
+.Dq Li \ea ,
+.Dq Li \eb ,
+.Dq Li \ef ,
+.Dq Li \en ,
+.Dq Li \er ,
+.Dq Li \et ,
+.Dq Li \eU######## ,
+.Dq Li \eu####
and
-.Ql \ev .
+.Dq Li \ev .
For
-.Ql \eU########
+.Dq Li \eU########
and
-.Ql \eu#### ,
+.Dq Li \eu#### ,
.Dq #
-means a hexadecimal digit, of thich there may be none up to four or eight;
+means a hexadecimal digit, of which there may be none up to four or eight;
these escapes translate a Unicode codepoint to UTF-8.
Furthermore,
-.Ql \eE
+.Dq Li \eE
and
-.Ql \ee
+.Dq Li \ee
expand to the escape character.
.Pp
In the
.Ic print
builtin mode,
-.Ql \e" ,
-.Ql \e\*(aq ,
+.Dq Li \e" ,
+.Dq Li \e\*(aq
and
-.Ql \e?
+.Dq Li \e?
are explicitly excluded;
octal sequences must have the none up to three octal digits
.Dq #
prefixed with the digit zero
-.Pq Ql \e0### ;
+.Pq Dq Li \e0### ;
hexadecimal sequences
-.Ql \ex##
+.Dq Li \ex##
are limited to none up to two hexadecimal digits
.Dq # ;
both octal and hexadecimal sequences convert to raw octets;
-.Ql \e# ,
+.Dq Li \e# ,
where # is none of the above, translates to \e# (backslashes are retained).
.Pp
Backslash expansion in the C style mode slightly differs: octal sequences
-.Ql \e###
+.Dq Li \e###
must have no digit zero prefixing the one up to three octal digits
.Dq #
and yield raw octets; hexadecimal sequences
-.Ql \ex#*
+.Dq Li \ex#*
greedily eat up as many hexadecimal digits
.Dq #
as they can and terminate with the first non-hexadecimal digit;
these translate a Unicode codepoint to UTF-8.
The sequence
-.Ql \ec# ,
+.Dq Li \ec# ,
where
.Dq #
is any octet, translates to Ctrl-# (which basically means,
-.Ql \ec?
+.Dq Li \ec?
becomes DEL, everything else is bitwise ANDed with 0x1F).
Finally,
-.Ql \e# ,
+.Dq Li \e# ,
where # is none of the above, translates to # (has the backslash trimmed),
even if it is a newline.
.Ss Aliases
@@ -1120,18 +1115,17 @@
.Pp
The following command aliases are defined automatically by the shell:
.Bd -literal -offset indent
-autoload=\*(aqtypeset \-fu\*(aq
-functions=\*(aqtypeset \-f\*(aq
-hash=\*(aqalias \-t\*(aq
-history=\*(aqfc \-l\*(aq
-integer=\*(aqtypeset \-i\*(aq
-local=\*(aqtypeset\*(aq
-login=\*(aqexec login\*(aq
-nameref=\*(aqtypeset \-n\*(aq
+autoload=\*(aq\e\ebuiltin typeset \-fu\*(aq
+functions=\*(aq\e\ebuiltin typeset \-f\*(aq
+hash=\*(aq\e\ebuiltin alias \-t\*(aq
+history=\*(aq\e\ebuiltin fc \-l\*(aq
+integer=\*(aq\e\ebuiltin typeset \-i\*(aq
+local=\*(aq\e\ebuiltin typeset\*(aq
+login=\*(aq\e\ebuiltin exec login\*(aq
+nameref=\*(aq\e\ebuiltin typeset \-n\*(aq
nohup=\*(aqnohup \*(aq
-r=\*(aqfc \-e \-\*(aq
-stop=\*(aqkill \-STOP\*(aq
-type=\*(aqwhence \-v\*(aq
+r=\*(aq\e\ebuiltin fc \-e \-\*(aq
+type=\*(aq\e\ebuiltin whence \-v\*(aq
.Ed
.Pp
Tracked aliases allow the shell to remember where it found a particular
@@ -1142,7 +1136,7 @@
time the command is executed, the shell checks the saved path to see that it
is still valid, and if so, avoids repeating the path search.
Tracked aliases can be listed and created using
-.Ic alias \-t .
+.Ic alias Fl t .
Note that changing the
.Ev PATH
parameter clears the saved paths for all tracked aliases.
@@ -1149,9 +1143,9 @@
If the
.Ic trackall
option is set (i.e.\&
-.Ic set \-o Ic trackall
+.Ic set Fl o Ic trackall
or
-.Ic set \-h ) ,
+.Ic set Fl h ) ,
the shell tracks all commands.
This option is set automatically for non-interactive shells.
For interactive shells, only the following commands are
@@ -1171,7 +1165,7 @@
.Xr rm 1 ,
.Xr sed 1 ,
.Xr sh 1 ,
-.Xr vi 1 ,
+.Xr vi 1
and
.Xr who 1 .
.Ss Substitution
@@ -1178,7 +1172,7 @@
The first step the shell takes in executing a simple-command is to perform
substitutions on the words of the command.
There are three kinds of
-substitution: parameter, command, and arithmetic.
+substitution: parameter, command and arithmetic.
Parameter substitutions,
which are described in detail in the next section, take the form
.Pf $ Ns Ar name
@@ -1203,7 +1197,7 @@
.Ic return
work, and in that
.Ic exit
-terminates the parent shell.
+terminates the parent shell; shell options are shared.
.Pp
Another variant of substitution are the valsubs (value substitutions)
.Pf ${\*(Ba\& Ns Ar command Ns \&;}
@@ -1212,7 +1206,7 @@
the, initially empty, expression-local variable
.Ev REPLY
is set to within the
-.Ar command Ns No s .
+.Ar command Ns s .
.Pp
If a substitution appears outside of double quotes, the results of the
substitution are generally subject to word or field splitting according to
@@ -1222,7 +1216,7 @@
The
.Ev IFS
parameter specifies a list of octets which are used to break a string up
-into several words; any octets from the set space, tab, and newline that
+into several words; any octets from the set space, tab and newline that
appear in the
.Ev IFS
octets are called
@@ -1242,39 +1236,36 @@
Example: If
.Ev IFS
is set to
-.Dq \*(Ltspace\*(Gt: ,
+.Dq Li \*(Ltspace\*(Gt:
and VAR is set to
-.Dq \*(Ltspace\*(GtA\*(Ltspace\*(Gt:\*(Ltspace\*(Gt\*(Ltspace\*(GtB::D ,
+.Dq Li \*(Ltspace\*(GtA\*(Ltspace\*(Gt:\*(Ltspace\*(Gt\*(Ltspace\*(GtB::D ,
the substitution for $VAR results in four fields:
-.Sq A ,
-.Sq B ,
-.Sq
-(an empty field),
-and
-.Sq D .
+.Dq Li A ,
+.Dq Li B ,
+.Dq
+(an empty field) and
+.Dq Li D .
Note that if the
.Ev IFS
-parameter is set to the
-.Dv NULL
-string, no field splitting is done; if the parameter is unset, the default
-value of space, tab, and newline is used.
+parameter is set to the empty string, no field splitting is done;
+if it is unset, the default value of space, tab and newline is used.
.Pp
Also, note that the field splitting applies only to the immediate result of
the substitution.
Using the previous example, the substitution for $VAR:E
results in the fields:
-.Sq A ,
-.Sq B ,
-.Sq ,
+.Dq Li A ,
+.Dq Li B ,
+.Dq
and
-.Sq D:E ,
+.Dq Li D:E ,
not
-.Sq A ,
-.Sq B ,
-.Sq ,
-.Sq D ,
+.Dq Li A ,
+.Dq Li B ,
+.Dq ,
+.Dq Li D
and
-.Sq E .
+.Dq Li E .
This behavior is POSIX compliant, but incompatible with some other shell
implementations which do field splitting on the word which contained the
substitution or use
@@ -1289,6 +1280,8 @@
For
.Pf $( Ns Ar command Ns \&)
and
+.Pf ${\*(Ba\& Ns Ar command Ns \&;}
+and
.Pf ${\ \& Ar command Ns \&;}
substitutions, normal quoting rules are used when
.Ar command
@@ -1298,12 +1291,14 @@
.Ql \e
followed by any of
.Ql $ ,
-.Ql \` ,
+.Ql \`
or
.Ql \e
-is stripped (a
+is stripped (as is
+.Ql \&"
+when the substitution is part of a double-quoted string); a backslash
.Ql \e
-followed by any other character is unchanged).
+followed by any other character is unchanged.
As a special case in command substitutions, a command of the form
.Pf \*(Lt Ar file
is interpreted to mean substitute the contents of
@@ -1315,18 +1310,18 @@
.Pp
Note that some shells do not use a recursive parser for command substitutions,
leading to failure for certain constructs; to be portable, use as workaround
-.Ql x=$(cat) \*(Lt\*(Lt"EOF"
+.Dq Li x=$(cat) \*(Lt\*(Lt\eEOF
(or the newline-keeping
-.Ql x=\*(Lt\*(Lt"EOF"
+.Dq Li x=\*(Lt\*(Lt\eEOF
extension) instead to merely slurp the string.
.St -p1003.1
-recommends to use case statements of the form
-.Ql "x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac)"
+recommends using case statements of the form
+.Li "x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac)"
instead, which would work but not serve as example for this portability issue.
.Bd -literal -offset indent
x=$(case $foo in bar) echo $bar ;; *) echo $baz ;; esac)
# above fails to parse on old shells; below is the workaround
-x=$(eval $(cat)) \*(Lt\*(Lt"EOF"
+x=$(eval $(cat)) \*(Lt\*(Lt\eEOF
case $foo in bar) echo $bar ;; *) echo $baz ;; esac
EOF
.Ed
@@ -1361,7 +1356,7 @@
.Pp
Parameter substitutions take the form
.Pf $ Ns Ar name ,
-.Pf ${ Ns Ar name Ns } ,
+.Pf ${ Ns Ar name Ns }
or
.Sm off
.Pf ${ Ar name Oo Ar expr Oc }
@@ -1376,30 +1371,26 @@
works equivalent to $* and $@ for positional parameters.
If substitution is performed on a parameter
(or an array parameter element)
-that is not set, a null string is substituted unless the
+that is not set, an empty string is substituted unless the
.Ic nounset
option
-.Po
-.Ic set Fl o Ic nounset
-or
-.Ic set Fl u
-.Pc
+.Pq Ic set Fl u
is set, in which case an error occurs.
.Pp
Parameters can be assigned values in a number of ways.
First, the shell implicitly sets some parameters like
-.Ql # ,
-.Ql PWD ,
+.Dq Li # ,
+.Dq Li PWD
and
-.Ql $ ;
+.Dq Li $ ;
this is the only way the special single character parameters are set.
Second, parameters are imported from the shell's environment at startup.
Third, parameters can be assigned values on the command line: for example,
.Ic FOO=bar
sets the parameter
-.Dq FOO
+.Dq Li FOO
to
-.Dq bar ;
+.Dq Li bar ;
multiple parameter assignments can be given on a single command line and they
can be followed by a simple-command, in which case the assignments are in
effect only for the duration of the command (such assignments are also
@@ -1413,7 +1404,7 @@
The fourth way of setting a parameter is with the
.Ic export ,
.Ic global ,
-.Ic readonly ,
+.Ic readonly
and
.Ic typeset
commands; see their descriptions in the
@@ -1425,9 +1416,9 @@
.Ic select
loops set parameters as well as the
.Ic getopts ,
-.Ic read ,
+.Ic read
and
-.Ic set \-A
+.Ic set Fl A
commands.
Lastly, parameters can be assigned values using assignment operators
inside arithmetic expressions (see
@@ -1463,8 +1454,7 @@
.Sm on
If
.Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
it is substituted; otherwise,
.Ar word
is substituted.
@@ -1473,8 +1463,7 @@
.Sm on
If
.Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
.Ar word
is substituted; otherwise, nothing is substituted.
.Sm off
@@ -1482,8 +1471,7 @@
.Sm on
If
.Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
it is substituted; otherwise, it is assigned
.Ar word
and the resulting value of
@@ -1494,25 +1482,20 @@
.Sm on
If
.Ar name
-is set and not
-.Dv NULL ,
+is set and not empty,
it is substituted; otherwise,
.Ar word
is printed on standard error (preceded by
.Ar name : )
and an error occurs (normally causing termination of a shell script, function,
-or script sourced using the
-.Sq \&.
+or a script sourced using the
+.Dq Li \&.
built-in).
If
.Ar word
is omitted, the string
-.Dq parameter null or not set
+.Dq Li parameter null or not set
is used instead.
-Currently a bug, if
-.Ar word
-is a variable which expands to the null string, the
-error message is also printed.
.El
.Pp
Note that, for all of the above,
@@ -1521,7 +1504,7 @@
The parsing rules also differ on whether the expression is double-quoted:
.Ar word
then uses double-quoting rules, except for the double quote itself
-.Pq Sq \&"
+.Pq Ql \&"
and the closing brace, which, if backslash escaped, gets quote removal applied.
.Pp
In the above modifiers, the
@@ -1528,11 +1511,10 @@
.Ql \&:
can be omitted, in which case the conditions only depend on
.Ar name
-being set (as opposed to set and not
-.Dv NULL ) .
+being set (as opposed to set and not empty).
If
.Ar word
-is needed, parameter, command, arithmetic, and tilde substitution are performed
+is needed, parameter, command, arithmetic and tilde substitution are performed
on it; if
.Ar word
is not needed, it is not evaluated.
@@ -1539,7 +1521,9 @@
.Pp
The following forms of parameter substitution can also be used (if
.Ar name
-is an array, its element #0 will be substituted in a scalar context):
+is an array, the element with the key
+.Dq 0
+will be substituted in scalar context):
.Pp
.Bl -tag -width Ds -compact
.It Pf ${# Ns Ar name Ns \&}
@@ -1546,8 +1530,8 @@
The number of positional parameters if
.Ar name
is
-.Ql * ,
-.Ql @ ,
+.Dq Li * ,
+.Dq Li @
or not specified; otherwise the length
.Pq in characters
of the string value of parameter
@@ -1578,6 +1562,8 @@
.Ic nameref
command (which is an alias for
.Ic typeset Fl n ) .
+.Ar name
+cannot be one of most special parameters (see below).
.Pp
.It Pf ${! Ns Ar name Ns \&[*]}
.It Pf ${! Ns Ar name Ns \&[@]}
@@ -1616,7 +1602,7 @@
.Pf %% Ar pattern No }
.Xc
.Sm on
-Like ${..#..} substitution, but it deletes from the end of the value.
+Like ${...#...} substitution, but it deletes from the end of the value.
Cannot be applied to a vector.
.Pp
.Sm off
@@ -1626,36 +1612,64 @@
.Xc
.It Xo
.Pf ${ Ar name
+.Pf /# Ar pattern / Ar string No }
+.Xc
+.It Xo
+.Pf ${ Ar name
+.Pf /% Ar pattern / Ar string No }
+.Xc
+.It Xo
+.Pf ${ Ar name
.Pf // Ar pattern / Ar string No }
.Xc
.Sm on
-Like ${..#..} substitution, but it replaces the longest match of
-.Ar pattern ,
-anchored anywhere in the value, with
-.Ar string .
-If
+The longest match of
.Ar pattern
-begins with
-.Ql # ,
-it is anchored at the beginning of the value; if it begins with
-.Ql % ,
-it is anchored at the end.
-Patterns that are empty or consist only of wildcards are invalid.
-A single
-.Ql /
-replaces the first occurence of the search
-.Ar pattern ,
-and two of them replace all occurences.
-If
-.Pf / Ar string
-is omitted, the
+in the value of parameter
+.Ar name
+is replaced with
+.Ar string
+(deleted if
+.Ar string
+is empty; the trailing slash
+.Pq Ql /
+may be omitted in that case).
+A leading slash followed by
+.Ql #
+or
+.Ql %
+causes the pattern to be anchored at the beginning or end of
+the value, respectively; empty unanchored
+.Ar pattern Ns s
+cause no replacement; a single leading slash or use of a
.Ar pattern
-is replaced by the empty string, i.e. deleted.
+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.
.Pp
.Sm off
.It Xo
+.Pf ${ Ar name
+.Pf @/ Ar pattern / Ar string No }
+.Xc
+.Sm on
+The same as
+.Sm off
+.Xo
+.Pf ${ Ar name
+.Pf // Ar pattern / Ar string No } ,
+.Xc
+.Sm on
+except that both
+.Ar pattern
+and
+.Ar string
+are expanded anew for each iteration.
+.Pp
+.Sm off
+.It Xo
.Pf ${ Ar name : Ns Ar pos
.Pf : Ns Ar len Ns }
.Xc
@@ -1722,7 +1736,7 @@
.It Ev \&#
The number of positional parameters ($1, $2, etc.).
.It Ev \&$
-The PID of the shell, or the PID of the original shell if it is a subshell.
+The PID of the shell or, if it is a subshell, the PID of the original shell.
Do
.Em NOT
use this mechanism for generating temporary file names; see
@@ -1735,8 +1749,8 @@
.It Ev \&?
The exit status of the last non-asynchronous command executed.
If the last command was killed by a signal,
-.Ic $?\&
-is set to 128 plus the signal number.
+.Ic \&$?
+is set to 128 plus the signal number, but at most 255.
.It Ev 0
The name of the shell, determined as follows:
the first argument to
@@ -1756,7 +1770,7 @@
.It Ev 1 No .. Ev 9
The first nine positional parameters that were supplied to the shell, function,
or script sourced using the
-.Sq \&.
+.Dq Li \&.
built-in.
Further positional parameters may be accessed using
.Pf ${ Ar number Ns } .
@@ -1770,8 +1784,7 @@
.Ev IFS
parameter (or the empty string if
.Ev IFS
-is
-.Dv NULL ) .
+is unset.
.It Ev @
Same as
.Ic $* ,
@@ -1778,10 +1791,9 @@
unless it is used inside double quotes, in which case a separate word is
generated for each positional parameter.
If there are no positional parameters, no word is generated.
-.Ic $@
+.Ic \&"$@"
can be used to access arguments, verbatim, without losing
-.Dv NULL
-arguments or splitting arguments with spaces.
+empty arguments or splitting arguments with spaces (IFS, actually).
.El
.Pp
The following parameters are set and/or used by the shell:
@@ -1795,21 +1807,16 @@
.It Ev BASHPID
The PID of the shell or subshell.
.It Ev CDPATH
-Search path for the
+Like
+.Ev PATH ,
+but used to resolve the argument to the
.Ic cd
built-in command.
-It works the same way as
-.Ev PATH
-for those directories not beginning with
-.Ql /
-in
-.Ic cd
-commands.
Note that if
.Ev CDPATH
is set and does not contain
-.Sq \&.
-or contains an empty path, the current directory is not searched.
+.Dq Li \&.
+or an empty string element, the current directory is not searched.
Also, the
.Ic cd
built-in command will display the resulting directory when a match is found
@@ -1821,11 +1828,11 @@
.Xr stty 1
is non-zero and sane enough (minimum is 12x3); similar for
.Ev LINES .
-This parameter is used by the interactive line editing modes, and by the
+This parameter is used by the interactive line editing modes and by the
.Ic select ,
-.Ic set \-o ,
+.Ic set Fl o
and
-.Ic kill \-l
+.Ic kill Fl l
commands to format information columns.
Importing from the environment or unsetting this parameter removes the
binding to the actual terminal size in favour of the provided value.
@@ -1833,18 +1840,22 @@
If this parameter is found to be set after any profile files are executed, the
expanded value is used as a shell startup file.
It typically contains function and alias definitions.
-.It Ev ERRNO
-Integer value of the shell's
-.Va errno
-variable.
-It indicates the reason the last system call failed.
-Not yet implemented.
+.It Ev EPOCHREALTIME
+Time since the epoch, as returned by
+.Xr gettimeofday 2 ,
+formatted as decimal
+.Va tv_sec
+followed by a dot
+.Pq Ql \&.
+and
+.Va tv_usec
+padded to exactly six decimal digits.
.It Ev EXECSHELL
If set, this parameter is assumed to contain the shell that is to be used to
execute commands that
.Xr execve 2
fails to execute and which do not start with a
-.Dq #! Ns Ar shell
+.Dq Li #! Ns Ar shell
sequence.
.It Ev FCEDIT
The editor used by the
@@ -1862,7 +1873,9 @@
below for more information.
.It Ev HISTFILE
The name of the file used to store command history.
-When assigned to, history is loaded from the specified file.
+When assigned to or unset, the file is opened, history is truncated
+then loaded from the file; subsequent new commands (possibly consisting
+of several lines) are appended once they successfully compiled.
Also, several invocations of the shell will share history if their
.Ev HISTFILE
parameters all point to the same file.
@@ -1870,7 +1883,7 @@
.Sy Note :
If
.Ev HISTFILE
-isn't set, no history file is used.
+is unset or empty, no history file is used.
This is different from
.At
.Nm ksh .
@@ -1891,7 +1904,7 @@
.It Ev IFS
Internal field separator, used during substitution and by the
.Ic read
-command, to split values into distinct arguments; normally set to space, tab,
+command, to split values into distinct arguments; normally set to space, tab
and newline.
See
.Sx Substitution
@@ -1906,6 +1919,70 @@
The real group id of the shell.
.It Ev KSHUID
The real user id of the shell.
+.It Ev KSH_MATCH
+The last matched string.
+In a future version, this will be an indexed array,
+with indexes 1 and up capturing matching groups.
+Set by string comparisons (== and !=) in double-bracket test
+expressions when a match is found (when != returns false), by
+.Ic case
+when a match is encountered, and by the substitution operations
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf # Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf ## Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf % Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf %% Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf / Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf /# Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf /% Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf // Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+and
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf @/ Ar pat / Ar rpl No } .
+.Sm on
+.Xc
+See the end of the Emacs editing mode documentation for an example.
.It Ev KSH_VERSION
The name and version of the shell (read-only).
See also the version commands in
@@ -1921,21 +1998,11 @@
Always set, defaults to 24.
See
.Ev COLUMNS .
-.It Ev EPOCHREALTIME
-Time since the epoch, as returned by
-.Xr gettimeofday 2 ,
-formatted as decimal
-.Va tv_sec
-followed by a dot
-.Pq Sq .\&
-and
-.Va tv_usec
-padded to exactly six decimal digits.
.It Ev OLDPWD
The previous working directory.
Unset if
.Ic cd
-has not successfully changed directories since the shell started, or if the
+has not successfully changed directories since the shell started or if the
shell doesn't know where it is.
.It Ev OPTARG
When using
@@ -1948,14 +2015,16 @@
.Ic getopts
to process arguments from the beginning the next time it is invoked.
.It Ev PATH
-A colon separated list of directories that are searched when looking for
-commands and files sourced using the
-.Sq \&.
+A colon (semicolon on OS/2) separated list of directories that are
+searched when looking for commands and files sourced using the
+.Dq Li \&.
command (see below).
An empty string resulting from a leading or trailing
-colon, or two adjacent colons, is treated as a
-.Sq \&.
+(semi)colon, or two adjacent ones, is treated as a
+.Dq Li \&.
(the current directory).
+.It Ev PATHSEP
+A colon (semicolon on OS/2), for the user's convenience.
.It Ev PGRP
The process ID of the shell's process group leader.
.It Ev PIPESTATUS
@@ -1965,7 +2034,7 @@
The process ID of the shell's parent.
.It Ev PS1
The primary prompt for interactive shells.
-Parameter, command, and arithmetic
+Parameter, command and arithmetic
substitutions are performed, and
.Ql \&!
is replaced with the current command number (see the
@@ -1974,14 +2043,14 @@
A literal
.Ql \&!
can be put in the prompt by placing
-.Ql !!
+.Dq Li !!
in
.Ev PS1 .
.Pp
The default prompt is
-.Sq $\ \&
+.Dq Li $\ \&
for non-root users,
-.Sq #\ \&
+.Dq Li #\ \&
for root.
If
.Nm
@@ -1988,7 +2057,7 @@
is invoked by root and
.Ev PS1
does not contain a
-.Sq #
+.Ql #
character, the default value will be used even if
.Ev PS1
already exists in the environment.
@@ -2015,7 +2084,7 @@
sequences (such as escape codes) by prefixing your prompt with a
character (such as Ctrl-A) followed by a carriage return and then delimiting
the escape codes with this character.
-Any occurences of that character in the prompt are not printed.
+Any occurrences of that character in the prompt are not printed.
By the way, don't blame me for
this hack; it's derived from the original
.Xr ksh88 1 ,
@@ -2022,10 +2091,10 @@
which did print the delimiter character so you were out of luck
if you did not have any non-printing characters.
.Pp
-Since Backslashes and other special characters may be
+Since backslashes and other special characters may be
interpreted by the shell, to set
.Ev PS1
-either escape the backslash itself,
+either escape the backslash itself
or use double quotes.
The latter is more practical.
This is a more complex example,
@@ -2037,7 +2106,7 @@
.Pq colour would work, too ,
in the prompt string:
.Bd -literal -offset indent
-x=$(print \e\e001)
+x=$(print \e\e001) # otherwise unused char
PS1="$x$(print \e\er)$x$(tput so)$x\e$PWD$x$(tput se)$x\*(Gt "
.Ed
.Pp
@@ -2045,11 +2114,11 @@
.Nm
now also supports the following form:
.Bd -literal -offset indent
-PS1=$'\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt '
+PS1=$\*(aq\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt \*(aq
.Ed
.It Ev PS2
Secondary prompt string, by default
-.Sq \*(Gt\ \& ,
+.Dq Li \*(Gt\ \& ,
used when more input is needed to complete a command.
.It Ev PS3
Prompt used by the
@@ -2056,23 +2125,21 @@
.Ic select
statement when reading a menu selection.
The default is
-.Sq #?\ \& .
+.Dq Li #?\ \& .
.It Ev PS4
Used to prefix commands that are printed during execution tracing (see the
.Ic set Fl x
command below).
-Parameter, command, and arithmetic substitutions are performed
+Parameter, command and arithmetic substitutions are performed
before it is printed.
The default is
-.Sq +\ \& .
+.Dq Li +\ \& .
You may want to set it to
-.Sq \&[$EPOCHREALTIME]\ \&
+.Dq Li \&[$EPOCHREALTIME]\ \&
instead, to include timestamps.
.It Ev PWD
The current working directory.
-May be unset or
-.Dv NULL
-if the shell doesn't know where it is.
+May be unset or empty if the shell doesn't know where it is.
.It Ev RANDOM
Each time
.Ev RANDOM
@@ -2098,7 +2165,7 @@
.It Ev TMPDIR
The directory temporary shell files are created in.
If this parameter is not
-set, or does not contain the absolute path of a writable directory, temporary
+set or does not contain the absolute path of a writable directory, temporary
files are created in
.Pa /tmp .
.It Ev USER_ID
@@ -2105,19 +2172,28 @@
The effective user id of the shell.
.El
.Ss Tilde expansion
-Tilde expansion which is done in parallel with parameter substitution, is done
-on words starting with an unquoted
+Tilde expansion, which is done in parallel with parameter substitution,
+is applied to words starting with an unquoted
.Ql \*(TI .
+In parameter assignments (such as those preceding a simple-command or those
+occurring in the arguments of a declaration utility), tilde expansion is done
+after any assignment (i.e. after the equals sign) or after an unquoted colon
+.Pq Ql \&: ;
+login names are also delimited by colons.
+The Korn shell, except in POSIX mode, always expands tildes after unquoted
+equals signs, not just in assignment context (see below), and enables tab
+completion for tildes after all unquoted colons during command line editing.
+.Pp
The characters following the tilde, up to the first
.Ql / ,
if any, are assumed to be a login name.
If the login name is empty,
-.Ql + ,
+.Ql +
or
.Ql \- ,
-the value of the
+the simplified value of the
.Ev HOME ,
-.Ev PWD ,
+.Ev PWD
or
.Ev OLDPWD
parameter is substituted, respectively.
@@ -2128,27 +2204,12 @@
if any quoting or parameter substitution occurs in the login name, no
substitution is performed.
.Pp
-In parameter assignments
-(such as those preceding a simple-command or those occurring
-in the arguments of
-.Ic alias ,
-.Ic export ,
-.Ic global ,
-.Ic readonly ,
-and
-.Ic typeset ) ,
-tilde expansion is done after any assignment
-(i.e. after the equals sign)
-or after an unquoted colon
-.Pq Sq \&: ;
-login names are also delimited by colons.
-.Pp
The home directory of previously expanded login names are cached and re-used.
The
-.Ic alias \-d
-command may be used to list, change, and add to this cache (e.g.\&
+.Ic alias Fl d
+command may be used to list, change and add to this cache (e.g.\&
.Ic alias \-d fac=/usr/local/facilities; cd \*(TIfac/bin ) .
-.Ss Brace expansion (alteration)
+.Ss Brace expansion (alternation)
Brace expressions take the following form:
.Bd -unfilled -offset indent
.Sm off
@@ -2163,21 +2224,21 @@
.Ar N
words, each of which is the concatenation of
.Ar prefix ,
-.Ar str Ns i ,
+.Ar str Ns i
and
.Ar suffix
(e.g.\&
-.Dq a{c,b{X,Y},d}e
+.Dq Li a{c,b{X,Y},d}e
expands to four words:
-.Dq ace ,
-.Dq abXe ,
-.Dq abYe ,
+.Dq Li ace ,
+.Dq Li abXe ,
+.Dq Li abYe
and
-.Dq ade ) .
+.Dq Li ade ) .
As noted in the example, brace expressions can be nested and the resulting
words are not sorted.
Brace expressions must contain an unquoted comma
-.Pq Sq \&,
+.Pq Ql \&,
for expansion to occur (e.g.\&
.Ic {}
and
@@ -2190,11 +2251,11 @@
.Ql \&? ,
.Ql * ,
.Ql + ,
-.Ql @ ,
+.Ql @
or
.Ql \&!
characters or
-.Dq \&[..]
+.Dq Li \&[...]
sequences.
Once brace expansion has been performed, the shell replaces file
name patterns with the sorted names of all the files that match the pattern
@@ -2205,14 +2266,14 @@
Matches any single character.
.It \&*
Matches any sequence of octets.
-.It \&[..]
+.It \&[...]
Matches any of the octets inside the brackets.
Ranges of octets can be specified by separating two octets by a
.Ql \-
(e.g.\&
-.Dq \&[a0\-9]
+.Dq Li \&[a0\-9]
matches the letter
-.Sq a
+.Ql a
or any digit).
In order to represent itself, a
.Ql \-
@@ -2225,8 +2286,8 @@
.Ql \&!
appearing at the start of the list has special meaning (see below), so to
represent itself it must be quoted or appear later in the list.
-.It \&[!..]
-Like [..],
+.It \&[!...]
+Like [...],
except it matches any octet not inside the brackets.
.Sm off
.It *( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
@@ -2237,9 +2298,9 @@
.Ic *(foo\*(Babar)
matches the strings
.Dq ,
-.Dq foo ,
-.Dq bar ,
-.Dq foobarfoo ,
+.Dq Li foo ,
+.Dq Li bar ,
+.Dq Li foobarfoo ,
etc.
.Sm off
.It +( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
@@ -2249,9 +2310,9 @@
Example: The pattern
.Ic +(foo\*(Babar)
matches the strings
-.Dq foo ,
-.Dq bar ,
-.Dq foobar ,
+.Dq Li foo ,
+.Dq Li bar ,
+.Dq Li foobar ,
etc.
.Sm off
.It ?( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
@@ -2262,9 +2323,9 @@
.Ic ?(foo\*(Babar)
only matches the strings
.Dq ,
-.Dq foo ,
+.Dq Li foo
and
-.Dq bar .
+.Dq Li bar .
.Sm off
.It @( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
.Sm on
@@ -2272,9 +2333,9 @@
Example: The pattern
.Ic @(foo\*(Babar)
only matches the strings
-.Dq foo
+.Dq Li foo
and
-.Dq bar .
+.Dq Li bar .
.Sm off
.It !( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
.Sm on
@@ -2282,13 +2343,13 @@
Examples: The pattern
.Ic !(foo\*(Babar)
matches all strings except
-.Dq foo
+.Dq Li foo
and
-.Dq bar ;
+.Dq Li bar ;
the pattern
-.Ic !(*)
+.Ic \&!(*)
matches no strings; the pattern
-.Ic !(?)*\&
+.Ic \&!(?)*
matches all strings (think about it).
.El
.Pp
@@ -2299,28 +2360,28 @@
.Nm mksh
.Po and Nm pdksh Pc
never matches
-.Sq \&.
+.Dq Li \&.
and
-.Sq .. ,
+.Dq Li .. ,
but
.At
.Nm ksh ,
Bourne
-.Nm sh ,
+.Nm sh
and GNU
.Nm bash
do.
.Pp
Note that none of the above pattern elements match either a period
-.Pq Sq \&.
+.Pq Ql \&.
at the start of a file name or a slash
-.Pq Sq / ,
-even if they are explicitly used in a [..] sequence; also, the names
-.Sq \&.
+.Pq Ql / ,
+even if they are explicitly used in a [...] sequence; also, the names
+.Dq Li \&.
and
-.Sq ..
+.Dq Li ..
are never matched, even by the pattern
-.Sq .* .
+.Dq Li .* .
.Pp
If the
.Ic markdirs
@@ -2328,17 +2389,17 @@
with a trailing
.Ql / .
.Ss Input/output redirection
-When a command is executed, its standard input, standard output, and standard
-error (file descriptors 0, 1, and 2, respectively) are normally inherited from
+When a command is executed, its standard input, standard output and standard
+error (file descriptors 0, 1 and 2, respectively) are normally inherited from
the shell.
Three exceptions to this are commands in pipelines, for which
standard input and/or standard output are those set up by the pipeline,
asynchronous commands created when job control is disabled, for which standard
-input is initially set to be from
+input is initially set to
.Pa /dev/null ,
and commands for which any of the following redirections have been specified:
.Bl -tag -width XXxxmarker
-.It \*(Gt Ar file
+.It \*(Gt Ns Ar file
Standard output is redirected to
.Ar file .
If
@@ -2354,13 +2415,13 @@
.Ar cmd
gets a chance to actually read
.Ar foo .
-.It \*(Gt\*(Ba Ar file
+.It \*(Gt\*(Ba Ns Ar file
Same as
.Ic \*(Gt ,
except the file is truncated, even if the
.Ic noclobber
option is set.
-.It \*(Gt\*(Gt Ar file
+.It \*(Gt\*(Gt Ns Ar file
Same as
.Ic \*(Gt ,
except if
@@ -2369,15 +2430,15 @@
Also, the file is opened
in append mode, so writes always go to the end of the file (see
.Xr open 2 ) .
-.It \*(Lt Ar file
+.It \*(Lt Ns Ar file
Standard input is redirected from
.Ar file ,
which is opened for reading.
-.It \*(Lt\*(Gt Ar file
+.It \*(Lt\*(Gt Ns Ar file
Same as
.Ic \*(Lt ,
except the file is opened for reading and writing.
-.It \*(Lt\*(Lt Ar marker
+.It \*(Lt\*(Lt Ns Ar marker
After reading the command line containing this kind of redirection (called a
.Dq here document ) ,
the shell copies lines from the command source into a temporary file until a
@@ -2390,15 +2451,15 @@
.Ar marker
contains no quoted characters, the contents of the temporary file are processed
as if enclosed in double quotes each time the command is executed, so
-parameter, command, and arithmetic substitutions are performed, along with
+parameter, command and arithmetic substitutions are performed, along with
backslash
-.Pq Sq \e
+.Pq Ql \e
escapes for
.Ql $ ,
.Ql \` ,
-.Ql \e ,
+.Ql \e
and
-.Ql \enewline ,
+.Dq Li \enewline ,
but not for
.Ql \&" .
If multiple here documents are used on the same command line, they are saved in
@@ -2412,16 +2473,16 @@
If
.Ar marker
is only a set of either single
-.Dq \*(aq\*(aq
+.Dq Li \*(aq\*(aq
or double
-.Sq \&""
+.Ql \&""
quotes with nothing in between, the here document ends at the next empty line
and substitution will not be performed.
-.It \*(Lt\*(Lt\- Ar marker
+.It \*(Lt\*(Lt\- Ns Ar marker
Same as
.Ic \*(Lt\*(Lt ,
except leading tabs are stripped from lines in the here document.
-.It \*(Lt\*(Lt\*(Lt Ar word
+.It \*(Lt\*(Lt\*(Lt Ns Ar word
Same as
.Ic \*(Lt\*(Lt ,
except that
@@ -2429,11 +2490,11 @@
.Em is
the here document.
This is called a here string.
-.It \*(Lt& Ar fd
+.It \*(Lt& Ns Ar fd
Standard input is duplicated from file descriptor
.Ar fd .
.Ar fd
-can be a number, indicating the number of an existing file descriptor;
+can be a single digit, indicating the number of an existing file descriptor;
the letter
.Ql p ,
indicating the file descriptor associated with the output of the current
@@ -2440,45 +2501,35 @@
co-process; or the character
.Ql \- ,
indicating standard input is to be closed.
-Note that
-.Ar fd
-is limited to a single digit in most shell implementations.
-.It \*(Gt& Ar fd
+.It \*(Gt& Ns Ar fd
Same as
.Ic \*(Lt& ,
except the operation is done on standard output.
-.It &\*(Gt Ar file
+.It &\*(Gt Ns Ar file
Same as
-.Ic \*(Gt Ar file 2\*(Gt&1 .
-This is a GNU
+.Ic \*(Gt Ns Ar file 2\*(Gt&1 .
+This is a deprecated (legacy) GNU
.Nm bash
extension supported by
.Nm
-which also supports the preceding explicit fd number, for example,
-.Ic 3&\*(Gt Ar file
+which also supports the preceding explicit fd digit, for example,
+.Ic 3&\*(Gt Ns Ar file
is the same as
-.Ic 3\*(Gt Ar file 2\*(Gt&3
+.Ic 3\*(Gt Ns Ar file 2\*(Gt&3
in
.Nm
but a syntax error in GNU
.Nm bash .
-Setting the
-.Fl o Ar posix
-or
-.Fl o Ar sh
-shell options disable parsing of this redirection;
-it's a compatibility feature to legacy scripts, to
-not be used when writing new shell code.
.It Xo
-.No &\*(Gt\*(Ba Ar file ,
-.No &\*(Gt\*(Gt Ar file ,
-.No &\*(Gt& Ar fd
+.No &\*(Gt\*(Ba Ns Ar file ,
+.No &\*(Gt\*(Gt Ns Ar file ,
+.No &\*(Gt& Ns Ar fd
.Xc
Same as
-.Ic \*(Gt\*(Ba Ar file ,
-.Ic \*(Gt\*(Gt Ar file ,
+.Ic \*(Gt\*(Ba Ns Ar file ,
+.Ic \*(Gt\*(Gt Ns Ar file
or
-.Ic \*(Gt& Ar fd ,
+.Ic \*(Gt& Ns Ar fd ,
followed by
.Ic 2\*(Gt&1 ,
as above.
@@ -2490,12 +2541,12 @@
In any of the above redirections, the file descriptor that is redirected
(i.e. standard input or standard output)
can be explicitly given by preceding the
-redirection with a number (portably, only a single digit).
-Parameter, command, and arithmetic
-substitutions, tilde substitutions, and (if the shell is interactive)
+redirection with a single digit.
+Parameter, command and arithmetic
+substitutions, tilde substitutions, and, if the shell is interactive,
file name generation are all performed on the
.Ar file ,
-.Ar marker ,
+.Ar marker
and
.Ar fd
arguments of redirections.
@@ -2516,18 +2567,13 @@
pipelines are created and in the order they are given, so the following
will print an error with a line number prepended to it:
.Pp
-.D1 $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t
+.Dl $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t
.Pp
-File descriptors created by input/output redirections are private to the
-Korn shell, but passed to sub-processes if
-.Fl o Ic posix
-or
-.Fl o Ic sh
-is set.
+File descriptors created by I/O redirections are private to the shell.
.Ss Arithmetic expressions
Integer arithmetic expressions can be used with the
.Ic let
-command, inside $((..)) expressions, inside array references (e.g.\&
+command, inside $((...)) expressions, inside array references (e.g.\&
.Ar name Ns Bq Ar expr ) ,
as numeric arguments to the
.Ic test
@@ -2537,17 +2583,17 @@
.Ic let
command.
.Em Never
-use unchecked user input, e.g. from the environment, in arithmetics!
+use unchecked user input, e.g. from the environment, in an arithmetic context!
.Pp
Expressions are calculated using signed arithmetic and the
.Vt mksh_ari_t
type (a 32-bit signed integer), unless they begin with a sole
-.Sq #
+.Ql #
character, in which case they use
.Vt mksh_uari_t
.Po a 32-bit unsigned integer Pc .
.Pp
-Expressions may contain alpha-numeric parameter identifiers, array references,
+Expressions may contain alpha-numeric parameter identifiers, array references
and integer constants and may be combined with the following C operators
(listed and grouped in increasing order of precedence):
.Pp
@@ -2559,7 +2605,7 @@
Binary operators:
.Bd -literal -offset indent
,
-= += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+= += \-= *= /= %= \*(Lt\*(Lt= \*(Gt\*(Gt= \*(ha\*(Lt= \*(ha\*(Gt= &= \*(ha= \*(Ba=
\*(Ba\*(Ba
&&
\*(Ba
@@ -2567,7 +2613,7 @@
&
== !=
\*(Lt \*(Lt= \*(Gt \*(Gt=
-\*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt \*(Lt\*(Lt \*(Gt\*(Gt
+\*(Lt\*(Lt \*(Gt\*(Gt \*(ha\*(Lt \*(ha\*(Gt
+ \-
* / %
.Ed
@@ -2588,23 +2634,20 @@
.Ar base Ns # Ns Ar number ,
where
.Ar base
-is a decimal integer specifying the base, and
+is a decimal integer specifying the base (up to 36), and
.Ar number
is a number in the specified base.
Additionally, base-16 integers may be specified by prefixing them with
-.Sq 0x
+.Dq Li 0x
.Pq case-insensitive
in all forms of arithmetic expressions, except as numeric arguments to the
.Ic test
-built-in command.
+built-in utility.
Prefixing numbers with a sole digit zero
-.Pq Sq 0
-leads to the shell interpreting it as base-8 (octal) integer in
-.Ic posix
-mode
-.Em only ;
-historically, (pd)ksh has never done so either anyway,
-and it's unsafe to do that, but POSIX demands it nowadays.
+.Pq Dq Li 0
+does not cause interpretation as octal (except in POSIX mode,
+as required by the standard), as that's unsafe to do.
+.Pp
As a special
.Nm mksh
extension, numbers to the base of one are treated as either (8-bit
@@ -2615,9 +2658,9 @@
.At
.Nm ksh93
syntax of
-.Dq \*(aqx\*(aq
+.Dq Li \*(aqx\*(aq
instead of
-.Dq 1#x
+.Dq Li 1#x
is also supported.
Note that NUL bytes (integral value of zero) cannot be used.
An unset or empty parameter evaluates to 0 in integer context.
@@ -2628,7 +2671,9 @@
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.
+input is well-formed UTF-8 in the range of 0000..FFFD if you use this
+feature, as opposed to
+.Ic read Fl a .
.Pp
The operators are evaluated as follows:
.Bl -tag -width Ds -offset indent
@@ -2658,8 +2703,8 @@
.It =
Assignment; the variable on the left is set to the value on the right.
.It Xo
-.No += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt=
-.No \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+.No += \-= *= /= %= \*(Lt\*(Lt= \*(Gt\*(Gt=
+.No \*(ha\*(Lt= \*(ha\*(Gt= &= \*(ha= \*(Ba=
.Xc
Assignment operators.
.Sm off
@@ -2680,9 +2725,9 @@
.Aq Ar expr
preserved.
For example,
-.Dq var1 *= 5 + 3
+.Dq Li var1 *= 5 + 3
is the same as specifying
-.Dq var1 = var1 * (5 + 3) .
+.Dq Li var1 = var1 * (5 + 3) .
.It \*(Ba\*(Ba
Logical OR;
the result is 1 if either argument is non-zero, 0 if not.
@@ -2709,16 +2754,16 @@
Less than or equal, greater than, greater than or equal.
See
.Ic \*(Lt .
-.It \*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt
-Rotate left (right); the result is similar to shift (see
-.Ic \*(Lt\*(Lt )
+.It \*(Lt\*(Lt \*(Gt\*(Gt
+Shift left (right); the result is the left argument with its bits
+arithmetically (signed operation) or logically (unsigned expression)
+shifted left (right) by the amount given in the right argument.
+.It \*(ha\*(Lt \*(ha\*(Gt
+Rotate left (right); the result is similar to shift,
except that the bits shifted out at one end are shifted in
at the other end, instead of zero or sign bits.
-.It \*(Lt\*(Lt \*(Gt\*(Gt
-Shift left (right); the result is the left argument with its bits shifted left
-(right) by the amount given in the right argument.
.It + \- * /
-Addition, subtraction, multiplication, and division.
+Addition, subtraction, multiplication and division.
.It %
Remainder; the result is the symmetric remainder of the division of the left
argument by the right.
@@ -2746,11 +2791,11 @@
.El
.Ss Co-processes
A co-process (which is a pipeline created with the
-.Sq \*(Ba&
+.Dq Li \*(Ba&
operator) is an asynchronous process that the shell can both write to (using
-.Ic print \-p )
+.Ic print Fl p )
and read from (using
-.Ic read \-p ) .
+.Ic read Fl p ) .
The input and output of the co-process can also be manipulated using
.Ic \*(Gt&p
and
@@ -2788,13 +2833,13 @@
portion of the co-process output when the most recently started co-process
(instead of when all sharing co-processes) exits.
.It
-.Ic print \-p
+.Ic print Fl p
will ignore
.Dv SIGPIPE
signals during writes if the signal is not being trapped or ignored; the same
is true if the co-process input has been duplicated to another file descriptor
and
-.Ic print \-u Ns Ar n
+.Ic print Fl u Ns Ar n
is used.
.El
.Ss Functions
@@ -2806,7 +2851,7 @@
Functions are like
.Li .\(hyscripts
(i.e. scripts sourced using the
-.Sq \&.
+.Dq Li \&.
built-in)
in that they are executed in the current environment.
However, unlike
@@ -2823,11 +2868,11 @@
A list of functions can be obtained using
.Ic typeset +f
and the function definitions can be listed using
-.Ic typeset \-f .
+.Ic typeset Fl f .
The
.Ic autoload
command (which is an alias for
-.Ic typeset \-fu )
+.Ic typeset Fl fu )
may be used to create undefined functions: when an undefined function is
executed, the shell searches the path specified in the
.Ev FPATH
@@ -2848,9 +2893,9 @@
and
.Dq export ,
which can be set with
-.Ic typeset \-ft
+.Ic typeset Fl ft
and
-.Ic typeset \-fx ,
+.Ic typeset Fl fx ,
respectively.
When a traced function is executed, the shell's
.Ic xtrace
@@ -2882,6 +2927,12 @@
A function can be made to finish immediately using the
.Ic return
command; this may also be used to explicitly specify the exit status.
+Note that when called in a subshell,
+.Ic return
+will only exit that subshell and will not cause the original shell to exit
+a running function (see the
+.Ic while Ns Li \&... Ns Ic read
+loop FAQ below).
.Pp
Functions defined with the
.Ic function
@@ -2909,9 +2960,9 @@
.Ic getopts
outside the function).
.It
-Bourne-style function definitions take precedence over alias dereferences
-and remove alias definitions upon encounter, while aliases take precedence
-over Korn-style functions.
+Shell options
+.Pq Ic set Fl o
+have local scope, i.e. changes inside a function are reset upon its exit.
.El
.Pp
In the future, the following differences may also be added:
@@ -2925,15 +2976,11 @@
.It
The EXIT trap, if set in a function, will be executed after the function
returns.
-.It
-Shell options
-.Pq Ic set Fl o
-have local scope, i.e. changes inside a function are reset upon its exit.
.El
.Ss Command execution
-After evaluation of command-line arguments, redirections, and parameter
+After evaluation of command-line arguments, redirections and parameter
assignments, the type of command is determined: a special built-in command,
-a function, a normal builtin, or the name of a file to execute found using the
+a function, a normal builtin or the name of a file to execute found using the
.Ev PATH
parameter.
The checks are made in the above order.
@@ -2962,23 +3009,24 @@
.Nm
commands keeping assignments:
.Pp
-.Ic builtin , global , typeset , wait
+.Ic global , source , typeset
.Pp
Builtins that are not special:
.Pp
.Ic [ , alias , bg , bind ,
-.Ic cat , cd , command , echo ,
-.Ic false , fc , fg , getopts ,
-.Ic jobs , kill , let , mknod ,
+.Ic builtin , cat , cd , command ,
+.Ic echo , false , fc , fg ,
+.Ic getopts , jobs , kill , let ,
.Ic print , pwd , read , realpath ,
.Ic rename , sleep , suspend , test ,
.Ic true , ulimit , umask , unalias ,
-.Ic whence
+.Ic wait , whence
.Pp
Once the type of command has been determined, any command-line parameter
assignments are performed and exported for the duration of the command.
.Pp
-The following describes the special and regular built-in commands:
+The following describes the special and regular built-in commands and
+builtin-like reserved words:
.Pp
.Bl -tag -width false -compact
.It Ic \&. Ar file Op Ar arg ...
@@ -3018,9 +3066,13 @@
.Ic alias
lists all aliases.
For any name without a value, the existing alias is listed.
-Any name with a value defines an alias (see
+Any name with a value defines an alias; see
.Sx Aliases
-above).
+above.
+.Li \&[][A\-Za\-z0\-9_!%,.@:\-]
+are valid in names, except they may not begin with a hyphen-minus, and
+.Ic \&[[
+is not a valid alias name.
.Pp
When listing aliases, one of two formats is used.
Normally, aliases are listed as
@@ -3046,7 +3098,7 @@
If the
.Fl p
option is used, each alias is prefixed with the string
-.Dq alias\ \& .
+.Dq Li alias\ \& .
.Pp
The
.Fl t
@@ -3095,7 +3147,7 @@
.Ar string ,
which should consist of a control character
optionally preceded by one of the two prefix characters
-and optionally succeded by a tilde character.
+and optionally succeeded by a tilde character.
Future input of the
.Ar string
will cause the editing command to be immediately invoked.
@@ -3112,7 +3164,8 @@
.Pp
Control characters may be written using caret notation
i.e. \*(haX represents Ctrl-X.
-Note that although only two prefix characters (usually ESC and \*(haX)
+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)
are supported, some multi-character sequences can be supported.
.Pp
The following default bindings show how the arrow keys, the home, end and
@@ -3140,7 +3193,7 @@
inner-most
.Ic for ,
.Ic select ,
-.Ic until ,
+.Ic until
or
.Ic while
loop.
@@ -3156,6 +3209,18 @@
.Ar command .
.Pp
.It Xo
+.Ic \ebuiltin
+.Ar command Op Ar arg ...
+.Xc
+Same as
+.Ic builtin .
+Additionally acts as declaration utility forwarder, i.e. this is a
+declaration utility (see
+.Sx Tilde expansion )
+.No iff Ar command
+is a declaration utility.
+.Pp
+.It Xo
.Ic cat
.Op Fl u
.Op Ar
@@ -3165,17 +3230,15 @@
If a
.Ar file
is a single dash
-.Pq Sq -
+.Pq Dq Li \-
or absent, read from standard input.
-Unless compiled with
-.Dv MKSH_NO_EXTERNAL_CAT ,
-if any options are given, an external
-.Xr cat 1
-utility is invoked instead if called from the shell.
For direct builtin calls, the
.Tn POSIX
.Fl u
option is supported as a no-op.
+For calls from shell, if any options are given, an external
+.Xr cat 1
+utility is preferred over the builtin.
.Pp
.It Xo
.Ic cd
@@ -3198,16 +3261,13 @@
.Ev CDPATH
is set, it lists the search path for the directory containing
.Ar dir .
-A
-.Dv NULL
-path means the current directory.
+An unset or empty path means the current directory.
If
.Ar dir
is found in any component of the
.Ev CDPATH
-search path other than the
-.Dv NULL
-path, the name of the new working directory will be written to standard output.
+search path other than an unset or empty path,
+the name of the new working directory will be written to standard output.
If
.Ar dir
is missing, the home directory
@@ -3216,7 +3276,7 @@
If
.Ar dir
is
-.Ql \- ,
+.Dq Li \- ,
the previous working directory is used (see the
.Ev OLDPWD
parameter).
@@ -3228,7 +3288,7 @@
option isn't set (see the
.Ic set
command below), references to
-.Sq ..
+.Dq Li ..
in
.Ar dir
are relative to the path used to get to the directory.
@@ -3237,7 +3297,7 @@
option (physical path) is used or if the
.Ic physical
option is set,
-.Sq ..
+.Dq Li ..
is relative to the filesystem directory tree.
The
.Ev PWD
@@ -3247,7 +3307,7 @@
respectively.
If the
.Fl e
-option is set for physical filesystem traversal, and
+option is set for physical filesystem traversal and
.Ev PWD
could not be set, the exit code is 1; greater than 1 if an
error occurred, 0 otherwise.
@@ -3290,6 +3350,7 @@
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
@@ -3303,9 +3364,9 @@
.Ar cmd ,
information about what would be executed is given (and the same is done for
.Ar arg ... ) .
-For special and regular built-in commands and functions, their names are simply
-printed; for aliases, a command that defines them is printed; and for commands
-found by searching the
+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
.Ev PATH
parameter, the full path of the command is printed.
If no command is found
@@ -3324,7 +3385,7 @@
inner-most
.Ic for ,
.Ic select ,
-.Ic until ,
+.Ic until
or
.Ic while
loop.
@@ -3345,7 +3406,7 @@
standard output.
The newline is suppressed if any of the arguments contain the
backslash sequence
-.Ql \ec .
+.Dq Li \ec .
See the
.Ic print
command below for a list of other backslash sequences that are recognised.
@@ -3365,9 +3426,11 @@
.Ic posix
or
.Ic sh
-option is set or this is a direct builtin call, only the first argument
-is treated as an option, and only if it is exactly
-.Dq Fl n .
+option is set or this is a direct builtin call or
+.Ic print
+.Fl R ,
+only the first argument is treated as an option, and only if it is exactly
+.Dq Li \-n .
Backslash interpretation is disabled.
.Pp
.It Ic eval Ar command ...
@@ -3407,11 +3470,11 @@
it does pass these file descriptors on.
.Pp
.It Ic exit Op Ar status
-The shell exits with the specified exit status.
+The shell or subshell exits with the specified exit status.
If
.Ar status
is not specified, the exit status is the current value of the
-.Ic $?\&
+.Ic \&$?
parameter.
.Pp
.It Xo
@@ -3422,10 +3485,11 @@
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
-.Ql \-
+.Dq Li \-
with no option letter is specified, name=value pairs, or, with
.Fl p ,
.Ic export
@@ -3460,7 +3524,7 @@
.Fl l ,
the selected commands are edited by the editor specified with the
.Fl e
-option, or if no
+option or, if no
.Fl e
is specified, the editor specified by the
.Ev FCEDIT
@@ -3561,7 +3625,7 @@
argument) argument that does not start with a
.Ql \- ,
or when a
-.Ql \-\-
+.Dq Li \-\-
argument is encountered.
.Pp
Option parsing can be reset by setting
@@ -3571,14 +3635,27 @@
.Pp
Warning: Changing the value of the shell parameter
.Ev OPTIND
-to a value other than 1, or parsing different sets of arguments without
+to a value other than 1 or parsing different sets of arguments without
resetting
-.Ev OPTIND ,
+.Ev OPTIND
may lead to unexpected results.
.Pp
-.It global Ar ...
+.It Xo
+.Ic global
+.Op Ic +\-aglpnrtUux
+.Oo Fl L Ns Op Ar n
+.No \*(Ba Fl R Ns Op Ar n
+.No \*(Ba Fl Z Ns Op Ar n Oc
+.Op Fl i Ns Op Ar n
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
See
-.Ic typeset .
+.Ic typeset Fl g .
+.No Deprecated , Em will
+be removed from a future version of
+.Nm .
.Pp
.It Xo
.Ic hash
@@ -3625,7 +3702,7 @@
.No { Ar job \*(Ba pid \*(Ba pgrp No }
.Ar ...
.Xc
-Send the specified signal to the specified jobs, process IDs, or process
+Send the specified signal to the specified jobs, process IDs or process
groups.
If no signal is specified, the
.Dv TERM
@@ -3643,8 +3720,8 @@
.Xc
Print the signal name corresponding to
.Ar exit-status .
-If no arguments are specified, a list of all the signals, their numbers, and
-a short description of them are printed.
+If no arguments are specified, a list of all the signals with their numbers
+and a short description of each are printed.
.Pp
.It Ic let Op Ar expression ...
Each expression is evaluated (see
@@ -3656,13 +3733,9 @@
the parsing or evaluation of an expression, the exit status is greater than 1.
Since expressions may need to be quoted,
.No \&(( Ar expr No ))
-is syntactic sugar for
-.No let \&" Ns Ar expr Ns \&" .
+is syntactic sugar for:
+.Dl "{ \e\ebuiltin let \*(aq" Ns Ar expr Ns "\*(aq; }"
.Pp
-.It Ic let]
-Internally used alias for
-.Ic let .
-.Pp
.It Xo
.Ic mknod
.Op Fl m Ar mode
@@ -3681,7 +3754,7 @@
.Cm b
(block type device),
.Cm c
-(character type device),
+(character type device)
or
.Cm p
.Pq named pipe , Tn FIFO .
@@ -3695,77 +3768,77 @@
and
.Ar minor
(minor device number).
+This is not normally part of
+.Nm mksh ;
+however, distributors may have added this as builtin as a speed hack.
.Pp
-See
-.Xr mknod 8
-for further information.
-.Pp
.It Xo
.Ic print
-.Oo Fl nprsu Ns Oo Ar n Oc \*(Ba
-.Fl R Op Fl en Oc
+.Oo Fl AcelNnprsu Ns Oo Ar n Oc \*(Ba
+.Fl R Op Fl n Oc
.Op Ar argument ...
.Xc
-.Ic print
-prints its arguments on the standard output, separated by spaces and
-terminated with a newline.
-The
-.Fl n
-option suppresses the newline.
-By default, certain C escapes are translated.
-These include these mentioned in
+Print the specified argument(s) on the standard output,
+separated by spaces, terminated with a newline.
+The escapes mentioned in
.Sx Backslash expansion
above, as well as
-.Ql \ec ,
+.Dq Li \ec ,
which is equivalent to using the
.Fl n
-option.
-Backslash expansion may be inhibited with the
-.Fl r
-option.
-The
-.Fl s
-option prints to the history file instead of standard output; the
-.Fl u
-option prints to file descriptor
-.Ar n
-.Po
-.Ar n
-defaults to 1 if omitted
-.Pc ;
-and the
-.Fl p
-option prints to the co-process (see
+option, are interpreted.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl A
+Each
+.Ar argument
+is arithmetically evaluated; the character corresponding to the
+resulting value is printed.
+Empty
+.Ar argument Ns s
+separate input words.
+.It Fl c
+The output is printed columnised, line by line, similar to how the
+.Xr rs 1
+utility, tab completion, the
+.Ic kill Fl l
+built-in utility and the
+.Ic select
+statement do.
+.It Fl e
+Restore backslash expansion after a previous
+.Fl r .
+.It Fl l
+Change the output word separator to newline.
+.It Fl N
+Change the output word and line separator to ASCII NUL.
+.It Fl n
+Do not print the trailing line separator.
+.It Fl p
+Print to the co-process (see
.Sx Co-processes
above).
+.It Fl r
+Inhibit backslash expansion.
+.It Fl s
+Print to the history file instead of standard output.
+.It Fl u Ns Op Ar n
+Print to the file descriptor
+.Ar n Pq defaults to 1 if omitted
+instead of standard output.
+.El
.Pp
The
.Fl R
-option is used to emulate, to some degree, the
+option mostly emulates the
.Bx
.Xr echo 1
-command which does not process
-.Ql \e
-sequences unless the
-.Fl e
-option is given.
-As above, the
-.Fl n
-option suppresses the trailing newline.
+command which does not expand backslashes and interprets
+its first argument as option only if it is exactly
+.Dq Li \-n
+.Pq to suppress the trailing newline .
.Pp
-.It Ic printf Ar format Op Ar arguments ...
-Formatted output.
-Approximately the same as the
-.Xr printf 1 ,
-utility, except it uses the same
-.Sx Backslash expansion
-and I/O code and does hot handle floating point as the rest of
-.Nm mksh .
-This is not normally part of
-.Nm mksh ;
-however, distributors may have added this as builtin as a speed hack.
-Do not use in new code.
-.Pp
.It Ic pwd Op Fl LP
Print the present working directory.
If the
@@ -3782,7 +3855,7 @@
option (physical path) is used or if the
.Ic physical
option is set, the path determined from the filesystem (by following
-.Sq ..
+.Dq Li ..
directories to the root directory) is printed.
.Pp
.It Xo
@@ -3830,7 +3903,8 @@
.Ev REPLY )
as array of characters (wide characters if the
.Ic utf8\-mode
-option is enacted, octets otherwise).
+option is enacted, octets otherwise); the codepoints are
+encoded as decimal numbers by default.
.It Fl d Ar x
Use the first byte of
.Ar x ,
@@ -3839,7 +3913,11 @@
.It Fl N Ar z
Instead of reading till end-of-line, read exactly
.Ar z
-bytes; less if EOF or a timeout occurs.
+bytes.
+Upon EOF, a partial read is returned with exit status 1.
+After timeout, a partial read is returned with an exit status as if
+.Dv SIGALRM
+were caught.
.It Fl n Ar z
Instead of reading till end-of-line, read up to
.Ar z
@@ -3858,6 +3936,11 @@
Interrupt reading after
.Ar n
seconds (specified as positive decimal value with an optional fractional part).
+The exit status of
+.Nm read
+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;
@@ -3889,40 +3972,6 @@
.Ic read
exits with a non-zero status.
.Pp
-Another handy set of tricks:
-If
-.Ic read
-is run in a loop such as
-.Ic while read foo; do ...; done
-then leading whitespace will be removed (IFS) and backslashes processed.
-You might want to use
-.Ic while IFS= read \-r foo; do ...; done
-for pristine I/O.
-Similarily, when using the
-.Fl a
-option, use of the
-.Fl r
-option might be prudent; the same applies for:
-.Bd -literal -offset indent
-find . \-type f \-print0 \*(Ba \e
- while IFS= read \-d \*(aq\*(aq \-r filename; do
- print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
-done
-.Ed
-.Pp
-The inner loop will be executed in a subshell and variable changes
-cannot be propagated if executed in a pipeline:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba while read foo; do ...; done
-.Ed
-.Pp
-Use co-processes instead:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba&
-while read \-p foo; do ...; done
-exec 3\*(Gt&p; exec 3\*(Gt&\-
-.Ed
-.Pp
.It Xo
.Ic readonly
.Op Fl p
@@ -3931,6 +3980,7 @@
.Ar ... Oc
.Xc
Sets the read-only attribute of the named parameters.
+This is a declaration utility.
If values are given,
parameters are set to them before setting the attribute.
Once a parameter is
@@ -3954,11 +4004,14 @@
If
.Ar name
ends with a slash
-.Pq Sq / ,
+.Pq Ql / ,
it's also checked for existence and whether it is a directory; otherwise,
.Ic realpath
returns 0 if the pathname either exists or can be created immediately,
i.e. all but the last component exist and are directories.
+For calls from the shell, if any options are given, an external
+.Xr realpath 1
+utility is preferred over the builtin.
.Pp
.It Xo
.Ic rename
@@ -3970,14 +4023,15 @@
to
.Ar to .
Both must be complete pathnames and on the same device.
-This builtin is intended for emergency situations where
-.Pa /bin/mv
-becomes unusable, and directly calls
+An external utility is preferred over this builtin,
+which is intended for emergency situations
+.Pq where Pa /bin/mv No becomes unusable
+and directly calls
.Xr rename 2 .
.Pp
.It Ic return Op Ar status
Returns from a function or
-.Ic .\&
+.Ic \&.
script, with exit status
.Ar status .
If no
@@ -3984,7 +4038,7 @@
.Ar status
is given, the exit status of the last executed command is used.
If used outside of a function or
-.Ic .\&
+.Ic \&.
script, it has the same effect as
.Ic exit .
Note that
@@ -3992,9 +4046,9 @@
treats both profile and
.Ev ENV
files as
-.Ic .\&
+.Ic \&.
scripts, while the original Korn shell only treats profiles as
-.Ic .\&
+.Ic \&.
scripts.
.Pp
.It Xo
@@ -4045,23 +4099,6 @@
.Nm ksh93
is:
.Ic foo=(a b c); foo+=(d e)
-.Pp
-Another
-.At
-.Nm ksh93
-and
-.Tn GNU
-.Nm bash
-extension allows specifying the indices used for
-.Ar arg ...
-.Pq from the above example, Ic a b c
-like this:
-.Ic set \-A foo \-\- [0]=a [1]=b [2]=c
-or
-.Ic foo=([0]=a [1]=b [2]=c)
-which can also be written
-.Ic foo=([0]=a b c)
-because indices are incremented automatically.
.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
@@ -4087,9 +4124,9 @@
explicitly tested by a shell construct such as
.Ic if ,
.Ic until ,
-.Ic while ,
+.Ic while
or
-.Ic !\&
+.Ic \&!
statements.
For
.Ic &&
@@ -4155,7 +4192,7 @@
.Fn nl_langinfo CODESET ,
or the
.Ev LC_ALL ,
-.Ev LC_CTYPE ,
+.Ev LC_CTYPE
or
.Ev LANG
environment variables,
@@ -4166,14 +4203,20 @@
case-insensitively; for direct builtin calls depending on the
aforementioned environment variables; or for stdin or scripts,
if the input begins with a UTF-8 Byte Order Mark.
+.Pp
+In near future, locale tracking will be implemented, which means that
+.Ic set Fl +U
+is changed whenever one of the
+.Tn POSIX
+locale-related environment variables changes.
.It Fl u \*(Ba Fl o Ic nounset
Referencing of an unset parameter, other than
-.Dq $@
+.Dq Li $@
or
-.Dq $* ,
+.Dq Li $* ,
is treated as an error, unless one of the
.Ql \- ,
-.Ql + ,
+.Ql +
or
.Ql =
modifiers is used.
@@ -4192,7 +4235,6 @@
.It Fl o Ic braceexpand
Enable brace expansion (a.k.a. alternation).
This is enabled by default.
-If disabled, tilde expansion after an equals sign is disabled as a side effect.
.It Fl o Ic emacs
Enable BRL emacs-like command-line editing (interactive shells only); see
.Sx Emacs editing mode .
@@ -4236,11 +4278,11 @@
commands to use
.Dq physical
(i.e. the filesystem's)
-.Sq ..
+.Dq Li ..
directories instead of
.Dq logical
directories (i.e. the shell handles
-.Sq .. ,
+.Dq Li .. ,
which allows the user to be oblivious of symbolic links to directories).
Clear by default.
Note that setting this option does not affect the current value of the
@@ -4258,19 +4300,27 @@
Make the exit status of a pipeline (before logically complementing) the
rightmost non-zero errorlevel, or zero if all commands exited with zero.
.It Fl o Ic posix
-Enable a somewhat more
-.Px
-ish mode.
-As a side effect, setting this flag turns off
+Behave closer to the standards
+(see
+.Sx POSIX mode
+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 .
+As a side effect, setting this flag turns off the
.Ic braceexpand
-mode, which can be turned back on manually, and
+and
+.Ic utf8\-mode
+flags, which can be turned back on manually, and
.Ic sh
-mode.
+mode (unless both are enabled at the same time).
.It Fl o Ic sh
Enable
.Pa /bin/sh
.Pq kludge
-mode.
+mode (see
+.Sx SH mode ) .
Automatically enabled if the basename of the shell invocation begins with
.Dq sh
and this autodetection feature is compiled in
@@ -4279,7 +4329,7 @@
.Ic braceexpand
mode, which can be turned back on manually, and
.Ic posix
-mode.
+mode (unless both are enabled at the same time).
.It Fl o Ic vi
Enable
.Xr vi 1 Ns -like
@@ -4308,20 +4358,25 @@
These options can also be used upon invocation of the shell.
The current set of
options (with single letter names) can be found in the parameter
-.Sq $\- .
+.Dq Li $\- .
.Ic set Fl o
with no option name will list all the options and whether each is on or off;
.Ic set +o
will print the long names of all options that are currently on.
+In a future version,
+.Ic set +o
+will behave
+.Tn POSIX
+compliant and print commands to restore the current options instead.
.Pp
Remaining arguments, if any, are positional parameters and are assigned, in
order, to the positional parameters (i.e. $1, $2, etc.).
If options end with
-.Ql \-\-
+.Dq Li \-\-
and there are no remaining arguments, all positional parameters are cleared.
If no options or arguments are given, the values of all names are printed.
For unknown historical reasons, a lone
-.Ql \-
+.Dq Li \-
option is treated specially \*(en it clears both the
.Fl v
and
@@ -4332,10 +4387,7 @@
The positional parameters
.Ar number Ns +1 ,
.Ar number Ns +2 ,
-etc. are renamed to
-.Sq 1 ,
-.Sq 2 ,
-etc.
+etc. are renamed to 1, 2, etc.
.Ar number
defaults to 1.
.Pp
@@ -4349,16 +4401,9 @@
Like
.Ic \&. Po Do dot Dc Pc ,
except that the current working directory is appended to the
-.Ev PATH
-in GNU
+search path (GNU
.Nm bash
-and
-.Nm mksh .
-In
-.Nm ksh93
-and
-.Nm mksh ,
-this is implemented as a shell alias instead of a builtin.
+extension).
.Pp
.It Ic suspend
Stops the shell as if it had received the suspend character from
@@ -4432,34 +4477,6 @@
.It Fl O Ar file
.Ar file Ns 's
owner is the shell's effective user ID.
-.It Fl o Ar option
-Shell
-.Ar option
-is set (see the
-.Ic set
-command above for a list of options).
-As a non-standard extension, if the option starts with a
-.Ql \&! ,
-the test is negated; the test always fails if
-.Ar option
-doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option
-.Ar foo
-exists).
-The same can be achieved with [ \-o ?foo ] like in
-.At
-.Nm ksh93 .
-.Ar option
-can also be the short flag led by either
-.Ql \-
-or
-.Ql +
-.Pq no logical negation ,
-for example
-.Ql \-x
-or
-.Ql +x
-instead of
-.Ql xtrace .
.It Fl p Ar file
.Ar file
is a named pipe
@@ -4521,6 +4538,38 @@
.It Fl z Ar string
.Ar string
is empty.
+.It Fl v Ar name
+The shell parameter
+.Ar name
+is set.
+.It Fl o Ar option
+Shell
+.Ar option
+is set (see the
+.Ic set
+command above for a list of options).
+As a non-standard extension, if the option starts with a
+.Ql \&! ,
+the test is negated; the test always fails if
+.Ar option
+doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option
+.Ar foo
+exists).
+The same can be achieved with [ \-o ?foo ] like in
+.At
+.Nm ksh93 .
+.Ar option
+can also be the short flag led by either
+.Ql \-
+or
+.Ql +
+.Pq no logical negation ,
+for example
+.Dq Li \-x
+or
+.Dq Li +x
+instead of
+.Dq Li xtrace .
.It Ar string No = Ar string
Strings are equal.
.It Ar string No == Ar string
@@ -4568,7 +4617,7 @@
or inside the brackets
.Ic \&[ ... \&]
is less than five: if leading
-.Ql \&!
+.Dq Li \&!
arguments can be stripped such that only one to three arguments remain,
then the lowered comparison is executed; (thanks to XSI) parentheses
.Ic \e( ... \e)
@@ -4580,28 +4629,26 @@
.Pp
.Sy Note :
A common mistake is to use
-.Dq if \&[ $foo = bar \&]
+.Dq Li if \&[ $foo = bar \&]
which fails if parameter
.Dq foo
-is
-.Dv NULL
-or unset, if it has embedded spaces (i.e.\&
+is empty or unset, if it has embedded spaces (i.e.\&
.Ev IFS
-octets), or if it is a unary operator like
-.Sq \&!
+octets) or if it is a unary operator like
+.Dq Li \&!
or
-.Sq Fl n .
+.Dq Li \-n .
Use tests like
-.Dq if \&[ x\&"$foo\&" = x"bar" \&]
+.Dq Li if \&[ x\&"$foo\&" = x"bar" \&]
instead, or the double-bracket operator
-.Dq if \&[[ $foo = bar \&]]
+.Dq Li if \&[[ $foo = bar \&]]
or, to avoid pattern matching (see
.Ic \&[[
above):
-.Dq if \&[[ $foo = \&"$bar" \&]]
+.Dq Li if \&[[ $foo = \&"$bar" \&]]
.Pp
The
-.Ic \&[[ ... ]]
+.Ic \&[[ ... \&]]
construct is not only more secure to use but also often faster.
.Pp
.It Xo
@@ -4657,54 +4704,59 @@
0m0.00s 0m0.00s
.Ed
.Pp
+.It Ic trap Ar n Op Ar signal ...
+If the first operand is a decimal unsigned integer, this resets all
+specified signals to the default action, i.e. is the same as calling
+.Ic trap
+with a dash
+.Pq Dq Li \-
+as
+.Ar handler ,
+followed by the arguments
+.Pq Ar n Op Ar signal ... ,
+all of which are treated as signals.
+.Pp
.It Ic trap Op Ar handler signal ...
-Sets a trap handler that is to be executed when any of the specified signals are
-received.
+Sets a trap handler that is to be executed when any of the specified
+.Ar signal Ns s
+are received.
.Ar handler
-is either a
-.Dv NULL
-string, indicating the signals are to be ignored, a minus sign
-.Pq Sq \- ,
-indicating that the default action is to be taken for the signals (see
-.Xr signal 3 ) ,
-or a string containing shell commands to be evaluated and executed at the first
-opportunity (i.e. when the current command completes, or before printing the
-next
+is either an empty string, indicating the signals are to be ignored, a dash
+.Pq Dq Li \- ,
+indicating that the default action is to be taken for the signals
+.Pq see Xr signal 3 ,
+or a string containing shell commands to be executed at the first opportunity
+(i.e. when the current command completes or before printing the next
.Ev PS1
prompt) after receipt of one of the signals.
.Ar signal
-is the name of a signal (e.g.\&
-.Dv PIPE
-or
-.Dv ALRM )
+is the name of a signal
+.Pq e.g.\& Dv PIPE or Dv ALRM
or the number of the signal (see the
-.Ic kill \-l
+.Ic kill Fl l
command above).
.Pp
There are two special signals:
.Dv EXIT
-(also known as 0) which is executed when the shell is about to exit, and
+.Pq also known as 0 ,
+which is executed when the shell is about to exit, and
.Dv ERR ,
-which is executed after an error occurs (an error is something that would cause
-the shell to exit if the
-.Fl e
+which is executed after an error occurs; an error is something
+that would cause the shell to exit if the
+.Ic set Fl e
or
-.Ic errexit
-option were set \*(en see the
-.Ic set
-command above).
+.Ic set Fl o Ic errexit
+option were set.
.Dv EXIT
handlers are executed in the environment of the last executed command.
-Note
-that for non-interactive shells, the trap handler cannot be changed for signals
-that were ignored when the shell started.
.Pp
-With no arguments,
+Note that, for non-interactive shells, the trap handler cannot be changed
+for signals that were ignored when the shell started.
+.Pp
+With no arguments, the current state of the traps that have been set since
+the shell started is shown as a series of
.Ic trap
-lists, as a series of
-.Ic trap
-commands, the current state of the traps that have been set since the shell
-started.
+commands.
Note that the output of
.Ic trap
cannot be usefully piped to another process (an artifact of the fact that
@@ -4722,13 +4774,12 @@
A command that exits with a zero value.
.Pp
.It Xo
-.Ic global
-.Oo Op Ic +\-alpnrtUux
-.Op Fl L Ns Op Ar n
-.Op Fl R Ns Op Ar n
-.Op Fl Z Ns Op Ar n
+.Ic typeset
+.Op Ic +\-aglpnrtUux
+.Oo Fl L Ns Op Ar n
+.No \*(Ba Fl R Ns Op Ar n
+.No \*(Ba Fl Z Ns Op Ar n Oc
.Op Fl i Ns Op Ar n
-.No \*(Ba Fl f Op Fl tux Oc
.Oo Ar name
.Op Ns = Ns Ar value
.Ar ... Oc
@@ -4735,15 +4786,11 @@
.Xc
.It Xo
.Ic typeset
-.Oo Op Ic +\-alpnrtUux
-.Op Fl LRZ Ns Op Ar n
-.Op Fl i Ns Op Ar n
-.No \*(Ba Fl f Op Fl tux Oc
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
+.Fl f Op Fl tux
+.Op Ar name ...
.Xc
Display or set parameter attributes.
+This is a declaration utility.
With no
.Ar name
arguments, parameter attributes are displayed; if no options are used, the
@@ -4750,7 +4797,7 @@
current attributes of all parameters are printed as
.Ic typeset
commands; if an option is given (or
-.Ql \-
+.Dq Li \-
with no option letter), all parameters and their values with the specified
attributes are printed; if options are introduced with
.Ql + ,
@@ -4759,28 +4806,17 @@
If
.Ar name
arguments are given, the attributes of the named parameters are set
-.Pq Ic \-
+.Pq Ic \&\-
or cleared
-.Pq Ic + .
+.Pq Ic \&+ ;
+inside a function, this will cause the parameters to be created
+(with no value) in the local scope (but see
+.Fl g ) .
Values for parameters may optionally be specified.
For
.Ar name Ns \&[*] ,
-the change affects the entire array, and no value may be specified.
+the change affects all elements of the array, and no value may be specified.
.Pp
-If
-.Ic typeset
-is used inside a function, any parameters specified are localised.
-This is not done by the otherwise identical
-.Ic global .
-.Em Note :
-This means that
-.Nm No 's Ic global
-command is
-.Em not
-equivalent to other programming languages' as it does not allow a
-function called from another function to access a parameter at truly
-global scope, but only prevents putting an accessed one into local scope.
-.Pp
When
.Fl f
is used,
@@ -4799,6 +4835,9 @@
.It Fl f
Function mode.
Display or set functions and their attributes, instead of parameters.
+.It Fl g
+Do not cause named parameters to be created in
+the local scope when called inside a function.
.It Fl i Ns Op Ar n
Integer attribute.
.Ar n
@@ -4821,7 +4860,7 @@
to fit the field width.
.It Fl l
Lower case attribute.
-All upper case characters in values are converted to lower case.
+All upper case ASCII characters in values are converted to lower case.
(In the original Korn shell, this parameter meant
.Dq long integer
when used with the
@@ -4845,7 +4884,7 @@
.Ar name
is accessed.
This can be used by functions to access variables whose names are
-passed as parametres, instead of using
+passed as parameters, instead of using
.Ic eval .
.It Fl p
Print complete
@@ -4886,7 +4925,7 @@
This option is not in the original Korn shell.
.It Fl u
Upper case attribute.
-All lower case characters in values are converted to upper case.
+All lower case ASCII characters in values are converted to upper case.
(In the original Korn shell, this parameter meant
.Dq unsigned integer
when used with the
@@ -4925,7 +4964,7 @@
.Fl l ,
.Fl R ,
.Fl U ,
-.Fl u ,
+.Fl u
or
.Fl Z
options are changed, all others from this set are cleared,
@@ -4950,7 +4989,7 @@
Also note that the types of limits available are system
dependent \*(en some systems have only the
.Fl f
-limit.
+limit, or not even that, or can set only the soft limits
.Bl -tag -width 5n
.It Fl a
Display all limits; unless
@@ -5055,7 +5094,7 @@
.Op Fl S
.Op Ar mask
.Xc
-Display or set the file permission creation mask, or umask (see
+Display or set the file permission creation mask or umask (see
.Xr umask 2 ) .
If the
.Fl S
@@ -5067,11 +5106,11 @@
When used, they describe what permissions may be made available (as opposed to
octal masks in which a set bit means the corresponding bit is to be cleared).
For example,
-.Dq ug=rwx,o=
-sets the mask so files will not be readable, writable, or executable by
+.Dq Li ug=rwx,o=
+sets the mask so files will not be readable, writable or executable by
.Dq others ,
and is equivalent (on most systems) to the octal mask
-.Dq 007 .
+.Dq Li 007 .
.Pp
.It Xo
.Ic unalias
@@ -5114,8 +5153,8 @@
.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
-.Ic kill \-l Ar exit-status
-above); if the last specified job can't be found (because it never existed, or
+.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.
@@ -5124,9 +5163,9 @@
below for the format of
.Ar job .
.Ic wait
-will return if a signal for which a trap has been set is received, or if a
+will return if a signal for which a trap has been set is received or if a
.Dv SIGHUP ,
-.Dv SIGINT ,
+.Dv SIGINT
or
.Dv SIGQUIT
signal is received.
@@ -5143,38 +5182,20 @@
.Op Fl pv
.Op Ar name ...
.Xc
-For each
-.Ar name ,
-the type of command is listed (reserved word, built-in, alias,
-function, tracked alias, or executable).
-If the
-.Fl p
-option is used, a path search is performed even if
-.Ar name
-is a reserved word, alias, etc.
Without the
.Fl v
-option,
-.Ic whence
-is similar to
-.Ic command Fl v
-except that
-.Ic whence
-will find reserved words and won't print aliases as alias commands.
+option, it is the same as
+.Ic command Fl v ,
+except aliases are not printed as alias command.
With the
.Fl v
-option,
-.Ic whence
-is the same as
+option, it is exactly the same as
.Ic command Fl V .
-Note that for
+In either case, the
+.Fl p
+option differs: the search path is not affected in
.Ic whence ,
-the
-.Fl p
-option does not affect the search path used, as it does for
-.Ic command .
-If the type of one or more of the names could not be determined, the exit
-status is non-zero.
+but the search is restricted to the path.
.El
.Ss Job control
Job control refers to the shell's ability to monitor and control jobs which
@@ -5185,9 +5206,9 @@
.Ic jobs
commands.
If job control is fully enabled (using
-.Ic set \-m
+.Ic set Fl m
or
-.Ic set \-o monitor ) ,
+.Ic set Fl o Ic monitor ) ,
as it is for interactive shells, the processes of a job are placed in their
own process group.
Foreground jobs can be stopped by typing the suspend
@@ -5200,7 +5221,7 @@
job is stopped or restarted, respectively.
.Pp
Note that only commands that create processes (e.g. asynchronous commands,
-subshell commands, and non-built-in, non-function commands) can be stopped;
+subshell commands and non-built-in, non-function commands) can be stopped;
commands like
.Ic read
cannot be.
@@ -5207,7 +5228,7 @@
.Pp
When a job is created, it is assigned a job number.
For interactive shells, this number is printed inside
-.Dq \&[..] ,
+.Dq Li \&[...] ,
followed by the process IDs of the processes in the job when an asynchronous
command is run.
A job may be referred to in the
@@ -5214,19 +5235,18 @@
.Ic bg ,
.Ic fg ,
.Ic jobs ,
-.Ic kill ,
+.Ic kill
and
.Ic wait
commands either by the process ID of the last process in the command pipeline
(as stored in the
-.Ic $!\&
-parameter) or by prefixing the job number with a percent
-sign
-.Pq Sq % .
+.Ic \&$!
+parameter) or by prefixing the job number with a percent sign
+.Pq Ql % .
Other percent sequences can also be used to refer to jobs:
.Bl -tag -width "%+ x %% x %XX"
.It %+ \*(Ba %% \*(Ba %
-The most recently stopped job, or, if there are no stopped jobs, the oldest
+The most recently stopped job or, if there are no stopped jobs, the oldest
running job.
.It %\-
The job that would be the
@@ -5282,10 +5302,10 @@
.Dv SIGTSTP ) .
.It Ar signal-description Op Dq core dumped
The job was killed by a signal (e.g. memory fault, hangup); use
-.Ic kill \-l
+.Ic kill Fl l
for a list of signal descriptions.
The
-.Dq core dumped
+.Dq Li core dumped
message indicates the process created a core file.
.El
.It Ar command
@@ -5312,12 +5332,133 @@
is immediately made to exit the shell, the running jobs are sent a
.Dv SIGHUP
signal and the shell exits.
+.Ss POSIX mode
+Entering
+.Ic set Fl o Ic posix
+mode will cause
+.Nm
+to behave even more
+.Tn POSIX
+compliant in places where the defaults or opinions differ.
+Note that
+.Nm mksh
+will still operate with unsigned 32-bit arithmetic; use
+.Nm lksh
+if arithmetic on the host
+.Vt long
+data type, complete with ISO C Undefined Behaviour, is required;
+refer to the
+.Xr lksh 1
+manual page for details.
+Most other historic,
+.At
+.Nm ksh Ns -compatible
+or opinionated differences can be disabled by using this mode; these are:
+.Bl -bullet
+.It
+The incompatible GNU
+.Nm bash
+I/O redirection
+.Ic &\*(Gt Ns Ar file
+is not supported.
+.It
+File descriptors created by I/O redirections are inherited by
+child processes.
+.It
+Numbers with a leading digit zero are interpreted as octal.
+.It
+The
+.Nm echo
+builtin does not interpret backslashes and only supports the exact option
+.Fl n .
+.It
+Alias expansion with a trailing space only reruns on command words.
+.It
+Tilde expansion follows POSIX instead of Korn shell rules.
+.It
+The exit status of
+.Ic fg
+is always 0.
+.It
+.Ic kill
+.Fl l
+only lists signal names, all in one line.
+.It
+.Ic getopts
+does not accept options with a leading
+.Ql + .
+.It
+.Ic exec
+skips builtins, functions and other commands and uses a
+.Ev PATH
+search to determine the utility to execute.
+.El
+.Ss SH mode
+Compatibility mode; intended for use with legacy scripts that
+cannot easily be fixed; the changes are as follows:
+.Bl -bullet
+.It
+The incompatible GNU
+.Nm bash
+I/O redirection
+.Ic &\*(Gt Ns Ar file
+is not supported.
+.It
+File descriptors created by I/O redirections are inherited by
+child processes.
+.It
+The
+.Nm echo
+builtin does not interpret backslashes and only supports the exact option
+.Fl n ,
+unless built with
+.Ev \-DMKSH_MIDNIGHTBSD01ASH_COMPAT .
+.It
+The substitution operations
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf # Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf ## Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf % Ar pat No } ,
+.Sm on
+.Xc
+and
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf %% Ar pat No }
+.Sm on
+.Xc
+wrongly do not require a parenthesis to be escaped and do not parse extglobs.
+.It
+The getopt construct from
+.Xr lksh 1
+passes through the errorlevel.
+.It
+.Nm sh
+.Fl c
+eats a leading
+.Fl \-
+if built with
+.Ev \-DMKSH_MIDNIGHTBSD01ASH_COMPAT .
+.El
.Ss Interactive input line editing
The shell supports three modes of reading command lines from a
.Xr tty 4
in an interactive session, controlled by the
.Ic emacs ,
-.Ic gmacs ,
+.Ic gmacs
and
.Ic vi
options (at most one of these can be set at once).
@@ -5345,7 +5486,7 @@
parameter),
a
.Ql \*(Gt ,
-.Ql + ,
+.Ql +
or
.Ql \*(Lt
character is displayed in the last column indicating that there are more
@@ -5354,7 +5495,7 @@
The line is scrolled horizontally as necessary.
.Pp
Completed lines are pushed into the history, unless they begin with an
-IFS octet or IFS white space, or are the same as the previous line.
+IFS octet or IFS white space or are the same as the previous line.
.Ss Emacs editing mode
When the
.Ic emacs
@@ -5392,17 +5533,21 @@
command.
Furthermore, many editing commands are useful only on terminals with
a visible cursor.
-The default bindings were chosen to resemble corresponding
-Emacs key bindings.
The user's
.Xr tty 4
characters (e.g.\&
.Dv ERASE )
are bound to
-reasonable substitutes and override the default bindings.
+reasonable substitutes and override the default bindings;
+their customary values are shown in parentheses below.
+The default bindings were chosen to resemble corresponding
+Emacs key bindings:
.Bl -tag -width Ds
-.It abort: \*(haC, \*(haG
-Abort the current command, empty the line buffer and
+.It Xo abort:
+.No INTR Pq \*(haC ,
+.No \*(haG
+.Xc
+Abort the current command, save it to the history, empty the line buffer and
set the exit state to interrupted.
.It auto\-insert: Op Ar n
Simply causes the character to appear as literal input.
@@ -5409,7 +5554,7 @@
Most ordinary characters are bound to this.
.It Xo backward\-char:
.Op Ar n
-.No \*(haB , \*(haXD , ANSI-CurLeft
+.No \*(haB , \*(haXD , ANSI-CurLeft , PC-CurLeft
.Xc
Moves the cursor backward
.Ar n
@@ -5420,24 +5565,25 @@
.Xc
Moves the cursor backward to the beginning of the word; words consist of
alphanumerics, underscore
-.Pq Sq _ ,
+.Pq Ql _
and dollar sign
-.Pq Sq $
+.Pq Ql $
characters.
.It beginning\-of\-history: \*(ha[\*(Lt
Moves to the beginning of the history.
-.It beginning\-of\-line: \*(haA, ANSI-Home
+.It beginning\-of\-line: \*(haA, ANSI-Home, PC-Home
Moves the cursor to the beginning of the edited input line.
.It Xo capitalise\-word:
.Op Ar n
.No \*(ha[C , \*(ha[c
.Xc
-Uppercase the first character in the next
+Uppercase the first ASCII character in the next
.Ar n
words, leaving the cursor past the end of the last word.
.It clear\-screen: \*(ha[\*(haL
Prints a compile-time configurable sequence to clear the screen and home
-the cursor, redraws the entire prompt and the currently edited input line.
+the cursor, redraws the last line of the prompt string and the currently
+edited input line.
The default sequence works for almost all standard terminals.
.It comment: \*(ha[#
If the current line does not begin with a comment character, one is added at
@@ -5466,7 +5612,7 @@
.Ic complete
command described above.
.It complete\-list: \*(haI, \*(ha[=
-Complete as much as is possible of the current word,
+Complete as much as is possible of the current word
and list the possible completions for it.
If only one completion is possible,
match as in the
@@ -5475,7 +5621,8 @@
Note that \*(haI is usually generated by the TAB (tabulator) key.
.It Xo delete\-char\-backward:
.Op Ar n
-.No ERASE , \*(ha? , \*(haH
+.No ERASE Pq \*(haH ,
+.No \*(ha? , \*(haH
.Xc
Deletes
.Ar n
@@ -5482,7 +5629,7 @@
characters before the cursor.
.It Xo delete\-char\-forward:
.Op Ar n
-.No ANSI-Del
+.No ANSI-Del , PC-Del
.Xc
Deletes
.Ar n
@@ -5489,7 +5636,9 @@
characters after the cursor.
.It Xo delete\-word\-backward:
.Op Ar n
-.No WERASE , \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
+.No Pfx1+ERASE Pq \*(ha[\*(haH ,
+.No WERASE Pq \*(haW ,
+.No \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
.Xc
Deletes
.Ar n
@@ -5503,7 +5652,7 @@
words.
.It Xo down\-history:
.Op Ar n
-.No \*(haN , \*(haXB , ANSI-CurDown
+.No \*(haN , \*(haXB , ANSI-CurDown , PC-CurDown
.Xc
Scrolls the history buffer forward
.Ar n
@@ -5535,21 +5684,26 @@
.Ic fc \-e ${VISUAL:\-${EDITOR:\-vi}} Ar n .
.It end\-of\-history: \*(ha[\*(Gt
Moves to the end of the history.
-.It end\-of\-line: \*(haE, ANSI-End
+.It end\-of\-line: \*(haE, ANSI-End, PC-End
Moves the cursor to the end of the input line.
.It eot: \*(ha_
Acts as an end-of-file; this is useful because edit-mode input disables
-normal terminal input canonicalization.
+normal terminal input canonicalisation.
.It Xo eot\-or\-delete:
.Op Ar n
-.No \*(haD
+.No EOF Pq \*(haD
.Xc
-Acts as
-.Ic eot
-if alone on a line; otherwise acts as
+If alone on a line, same as
+.Ic eot ,
+otherwise,
.Ic delete\-char\-forward .
.It error: (not bound)
Error (ring the bell).
+.It evaluate\-region: \*(ha[\*(haE
+Evaluates the text between the mark and the cursor position
+.Pq the entire line if no mark is set
+as function substitution (if it cannot be parsed, the editing state is
+unchanged and the bell is rung to signal an error); $? is updated accordingly.
.It exchange\-point\-and\-mark: \*(haX\*(haX
Places the cursor where the mark is and sets the mark to where the cursor was.
.It expand\-file: \*(ha[*
@@ -5560,7 +5714,7 @@
If no files match the pattern, the bell is rung.
.It Xo forward\-char:
.Op Ar n
-.No \*(haF , \*(haXC , ANSI-CurRight
+.No \*(haF , \*(haXC , ANSI-CurRight , PC-CurRight
.Xc
Moves the cursor forward
.Ar n
@@ -5578,7 +5732,9 @@
.Xc
Goes to history number
.Ar n .
-.It kill\-line: KILL
+.It Xo kill\-line:
+.No KILL Pq \*(haU
+.Xc
Deletes the entire input line.
.It kill\-region: \*(haW
Deletes the input between the cursor and the mark.
@@ -5616,17 +5772,19 @@
.Ic search\-history
or
.Ic search\-history\-up .
-.It no\-op: QUIT
+.It Xo no\-op:
+.No QUIT Pq \*(ha\e
+.Xc
This does nothing.
.It prefix\-1: \*(ha[
Introduces a 2-character command sequence.
.It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
-Introduces a 2-character command sequence.
+Introduces a multi-character command sequence.
.It Xo prev\-hist\-word:
.Op Ar n
.No \*(ha[. , \*(ha[_
.Xc
-The last word, or, if given, the
+The last word or, if given, the
.Ar n Ns th
word (zero-based) of the previous (on repeated execution, second-last,
third-last, etc.) command is inserted at the cursor.
@@ -5674,12 +5832,12 @@
pattern.
The history buffer retains only a finite number of lines; the oldest
are discarded as necessary.
-.It search\-history\-up: ANSI-PgUp
+.It search\-history\-up: ANSI-PgUp, PC-PgUp
Search backwards through the history buffer for commands whose beginning match
the portion of the input line before the cursor.
When used on an empty line, this has the same effect as
.Ic up\-history .
-.It search\-history\-down: ANSI-PgDn
+.It search\-history\-down: ANSI-PgDn, PC-PgDn
Search forwards through the history buffer for commands whose beginning match
the portion of the input line before the cursor.
When used on an empty line, this has the same effect as
@@ -5692,7 +5850,7 @@
.It set\-mark\-command: \*(ha[ Ns Aq space
Set the mark at the cursor position.
.It transpose\-chars: \*(haT
-If at the end of line, or if the
+If at the end of line or, if the
.Ic gmacs
option is set, this exchanges the two previous characters; otherwise, it
exchanges the previous and current characters and moves the cursor one
@@ -5699,7 +5857,7 @@
character to the right.
.It Xo up\-history:
.Op Ar n
-.No \*(haP , \*(haXA , ANSI-CurUp
+.No \*(haP , \*(haXA , ANSI-CurUp , PC-CurUp
.Xc
Scrolls the history buffer backward
.Ar n
@@ -5723,6 +5881,11 @@
.Ic yank ,
replaces the inserted text string with the next previously killed text string.
.El
+.Pp
+The tab completion escapes characters the same way as the following code:
+.Bd -literal
+print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
+.Ed
.Ss Vi editing mode
.Em Note:
The vi command-line editing mode is orphaned, yet still functional.
@@ -5738,7 +5901,7 @@
You start out in insert mode.
.It
There are file name and command completion commands:
-=, \e, *, \*(haX, \*(haE, \*(haF, and, optionally,
+=, \e, *, \*(haX, \*(haE, \*(haF and, optionally,
.Aq tab
and
.Aq esc .
@@ -5783,7 +5946,7 @@
(see
.Xr stty 1 )
and have their usual meaning (normal values are in parentheses): kill (\*(haU),
-erase (\*(ha?), werase (\*(haW), eof (\*(haD), intr (\*(haC), and quit (\*(ha\e).
+erase (\*(ha?), werase (\*(haW), eof (\*(haD), intr (\*(haC) and quit (\*(ha\e).
In addition to
the above, the following characters are also treated specially in insert mode:
.Bl -tag -width XJXXXXM
@@ -5798,7 +5961,7 @@
Erases previous character.
.It \*(haJ \*(Ba \*(haM
End of line.
-The current line is read, parsed, and executed by the shell.
+The current line is read, parsed and executed by the shell.
.It \*(haV
Literal next.
The next character typed is not treated specially (can be used
@@ -5811,7 +5974,7 @@
Optional file name and command completion (see
.Ic \*(haF
above), enabled with
-.Ic set \-o vi\-tabcomplete .
+.Ic set Fl o Ic vi\-tabcomplete .
.El
.Pp
In command mode, each character is interpreted as a command.
@@ -5831,9 +5994,9 @@
cursor.
A
.Dq word
-is a sequence of letters, digits, and underscore characters or a sequence of
-non-letter, non-digit, non-underscore, and non-whitespace characters (e.g.\&
-.Dq ab2*&\*(ha
+is a sequence of letters, digits and underscore characters or a sequence of
+non-letter, non-digit, non-underscore and non-whitespace characters (e.g.\&
+.Dq Li ab2*&\*(ha
contains two words) and a
.Dq big-word
is a sequence of non-whitespace characters.
@@ -5856,7 +6019,7 @@
is not specified, the last word is inserted.
.It #
Insert the comment character
-.Pq Sq #
+.Pq Ql #
at the start of the current line and return the line to the shell (equivalent
to
.Ic I#\*(haJ ) .
@@ -5891,13 +6054,13 @@
.Ql \&; ,
.Ql \*(Ba ,
.Ql & ,
-.Ql \&( ,
+.Ql \&(
or
-.Ql \&) ,
+.Ql \&)
and does not contain a slash
-.Pq Sq / ,
+.Pq Ql / ,
then command expansion is done; otherwise file name expansion is done.
-Command expansion will match the big-word against all aliases, functions, and
+Command expansion will match the big-word against all aliases, functions and
built-in commands as well as any executable files found by searching the
directories in the
.Ev PATH
@@ -5924,7 +6087,7 @@
is only recognised if the
.Ic vi\-esccomplete
option is set (see
-.Ic set \-o ) .
+.Ic set Fl o ) .
If
.Ar n
is specified, the
@@ -6010,8 +6173,8 @@
big-words.
.It %
Find match.
-The editor looks forward for the nearest parenthesis, bracket, or
-brace and then moves the cursor to the matching parenthesis, bracket, or brace.
+The editor looks forward for the nearest parenthesis, bracket or
+brace and then moves the cursor to the matching parenthesis, bracket or brace.
.It Xo
.Oo Ar n Oc Ns f Ns Ar c
.Xc
@@ -6044,7 +6207,7 @@
.Oo Ar n Oc Ns \&;
.Xc
Repeats the last
-.Ic f , F , t ,
+.Ic f , F , t
or
.Ic T
command.
@@ -6052,7 +6215,7 @@
.Oo Ar n Oc Ns \&,
.Xc
Repeats the last
-.Ic f , F , t ,
+.Ic f , F , t
or
.Ic T
command, but moves in the opposite direction.
@@ -6127,7 +6290,7 @@
.Ar n Ns th
occurrence of the last search string;
the direction of the search is the opposite of the last search.
-.It Ar ANSI-CurUp
+.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
for lines beginning with this string; keep the cursor position.
@@ -6265,7 +6428,7 @@
Miscellaneous vi commands
.Bl -tag -width Ds
.It \*(haJ and \*(haM
-The current line is read, parsed, and executed by the shell.
+The current line is read, parsed and executed by the shell.
.It \*(haL and \*(haR
Redraw the current line.
.It Xo
@@ -6278,9 +6441,11 @@
Undo the last edit command.
.It U
Undo all changes that have been made to the current line.
+.It PC Home, End, Del and cursor keys
+They move as expected, both in insert and command mode.
.It Ar intr No and Ar quit
The interrupt and quit terminal characters cause the current line to be
-deleted and a new prompt to be printed.
+removed to the history and a new prompt to be printed.
.El
.Sh FILES
.Bl -tag -width XetcXsuid_profile -compact
@@ -6312,6 +6477,7 @@
.Xr cat 1 ,
.Xr ed 1 ,
.Xr getopt 1 ,
+.Xr lksh 1 ,
.Xr sed 1 ,
.Xr sh 1 ,
.Xr stty 1 ,
@@ -6337,9 +6503,7 @@
.Xr utf\-8 7 ,
.Xr mknod 8
.Pp
-.Pa http://docsrv.sco.com:507/en/man/html.C/sh.C.html
-.Pp
-.Pa https://www.mirbsd.org/ksh\-chan.htm
+.Pa http://www.mirbsd.org/ksh\-chan.htm
.Rs
.%A Morris Bolsky
.%B "The KornShell Command and Programming Language"
@@ -6405,20 +6569,20 @@
.An -nosplit
.Nm "The MirBSD Korn Shell"
is developed by
-.An Thorsten Glaser Aq tg at mirbsd.org
-and currently maintained as part of The MirOS Project.
+.An mirabilos Aq Mt m at mirbsd.org
+as part of The MirOS Project.
This shell is based on the public domain 7th edition Bourne shell clone by
.An Charles Forsyth ,
who kindly agreed to, in countries where the Public Domain status of the work
may not be valid, grant a copyright licence to the general public to deal in
-the work without restriction and permission to sublicence derivates under the
-terms of any (OSI approved) Open Source licence,
+the work without restriction and permission to sublicence derivatives under
+the terms of any (OSI approved) Open Source licence,
and parts of the BRL shell by
.An Doug A. Gwyn ,
.An Doug Kingston ,
.An Ron Natalie ,
.An Arnold Robbins ,
-.An Lou Salkind ,
+.An Lou Salkind
and others.
The first release of
.Nm pdksh
@@ -6425,23 +6589,36 @@
was created by
.An Eric Gisin ,
and it was subsequently maintained by
-.An John R. MacMillan Aq Mt change!john at sq.sq.com ,
-.An Simon J. Gerraty Aq Mt sjg at zen.void.oz.au ,
+.An John R. MacMillan ,
+.An Simon J. Gerraty
and
-.An Michael Rendell Aq Mt michael at cs.mun.ca .
+.An Michael Rendell .
The effort of several projects, such as Debian and OpenBSD, and other
contributors including our users, to improve the shell is appreciated.
-See the documentation, CVS, and web site for details.
+See the documentation, website and source code (CVS) for details.
.Pp
+.Nm mksh\-os2
+is developed by
+.An KO Myung-Hun Aq Mt komh at chollian.net .
+.Pp
+.Nm mksh\-w32
+is developed by
+.An Michael Langguth Aq Mt lan at scalaris.com .
+.Pp
+.Nm mksh Ns / Ns Tn z/OS
+is contributed by
+.An Daniel Richard G. Aq Mt skunk at iSKUNK.ORG .
+.Pp
The BSD daemon is Copyright \(co Marshall Kirk McKusick.
The complete legalese is at:
-.Pa https://www.mirbsd.org/TaC\-mksh.txt
+.Pa http://www.mirbsd.org/TaC\-mksh.txt
.\"
.\" This boils down to: feel free to use mksh.ico as application icon
-.\" or shortcut for mksh or mksh/Win32; distro patches are ok (but we
-.\" request they amend $KSH_VERSION when modifying mksh). Authors are
-.\" Marshall Kirk McKusick (UCB), Rick Collette (ekkoBSD), Thorsten
-.\" Glaser, Benny Siegert (MirBSD), Michael Langguth (mksh/Win32).
+.\" or shortcut for mksh or mksh/Win32 or OS/2; distro patches are ok
+.\" (but we request they amend $KSH_VERSION when modifying mksh).
+.\" Authors are Marshall Kirk McKusick (UCB), Rick Collette (ekkoBSD),
+.\" mirabilos, Benny Siegert (MirBSD), Michael Langguth (mksh/Win32),
+.\" KO Myung-Hun (mksh for OS/2).
.\"
.\" As far as MirBSD is concerned, the files themselves are free
.\" to modification and distribution under BSD/MirOS Licence, the
@@ -6449,51 +6626,58 @@
.\" to protect it or lose it, which McKusick almost did.
.\"
.Sh CAVEATS
-.Nm
-only supports the Unicode BMP (Basic Multilingual Plane).
+.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
-has a different scope model from
-.At
-.Nm ksh ,
-which leads to subtile differences in semantics for identical builtins.
-This can cause issues with a
-.Ic nameref
-to suddenly point to a local variable by accident; fixing this is hard.
+.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
-The parts of a pipeline, like below, are executed in subshells.
-Thus, variable assignments inside them are not visible in the
-surrounding execution environment.
-Use co-processes instead.
+For the purpose of
+.Tn POSIX ,
+.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 Unicode BMP (Basic Multilingual Plane) 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
-foo \*(Ba bar \*(Ba read baz # will not change $baz
-foo \*(Ba bar \*(Ba& read \-p baz # will, however, do so
+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, (Unicode) locale tracking will be implemented though.
.Pp
-.Nm mksh
-provides a consistent set of 32-bit integer arithmetics, both signed
-and unsigned, with defined wraparound and sign of the result of a
-remainder operation, even (defying POSIX) on 64-bit systems.
-If you require 64-bit integer arithmetics, use
-.Nm lksh Pq legacy mksh
-instead, but be aware that, in POSIX, it's legal for the OS to make
-.Li print $((2147483647 + 1))
-delete all files on your system, as it's Undefined Behaviour.
-.Pp
-.Nm mksh
-provides a consistent, clear interface normally.
-This may deviate from POSIX in optional or opinionated places, such
-as whether leading-digit-zero numbers should be interpreted as octal.
-.Ic set \-o posix
-will cause the shell (either
-.Nm mksh
-or
-.Nm lksh )
-to behave more like the standard expects.
+See also the FAQ below.
.Sh BUGS
Suspending (using \*(haZ) pipelines like the one below will only suspend
the currently running part of the pipeline; in this example,
-.Dq fubar
+.Dq Li fubar
is immediately printed on suspension (but not later after an
.Ic fg ) .
.Bd -literal -offset indent
@@ -6500,17 +6684,30 @@
$ /bin/sleep 666 && echo fubar
.Ed
.Pp
+The truncation process involved when changing
+.Ev HISTFILE
+does not free old history entries (leaks memory) and leaks
+old entries into the new history if their line numbers are
+not overwritten by same-number entries from the persistent
+history file; truncating the on-disc file to
+.Ev HISTSIZE
+lines has always been broken and prone to history file corruption
+when multiple shells are accessing the file; the rollover process
+for the in-memory portion of the history is slow, should use
+.Xr memmove 3 .
+.Pp
This document attempts to describe
-.Nm mksh\ R50e
+.Nm mksh\ R56
and up,
+.\" with vendor patches from insert-your-name-here,
compiled without any options impacting functionality, such as
.Dv MKSH_SMALL ,
when not called as
.Pa /bin/sh
which, on some systems only, enables
-.Ic set \-o posix
+.Ic set Fl o Ic posix
or
-.Ic set \-o sh
+.Ic set Fl o Ic sh
automatically (whose behaviour differs across targets),
for an operating environment supporting all of its advanced needs.
.Pp
@@ -6517,9 +6714,8 @@
Please report bugs in
.Nm
to the
-.Mx
-mailing list at
-.Aq miros\-mksh at mirbsd.org
+.Aq Mt miros\-mksh at mirbsd.org
+mailing list
or in the
.Li \&#\&!/bin/mksh
.Pq or Li \&#ksh
@@ -6528,3 +6724,183 @@
.Pq Port 6697 SSL, 6667 unencrypted ,
or at:
.Pa https://launchpad.net/mksh
+.Sh FREQUENTLY ASKED QUESTIONS
+This FAQ attempts to document some of the questions users of
+.Nm
+or readers of this manual page may encounter.
+.Ss I'm an Android user, so what's mksh?
+.Nm mksh
+is a
+.Ux
+shell / command interpreter, similar to
+.Nm COMMAND.COM
+or
+.Nm CMD.EXE ,
+which has been included with
+.Tn Android Open Source Project
+for a while now.
+Basically, it's a program that runs in a terminal (console window),
+takes user input and runs commands or scripts, which it can also
+be asked to do by other programs, even in the background.
+Any privilege pop-ups you might be encountering are thus not
+.Nm mksh
+issues but questions by some other program utilising it.
+.Ss "I'm an OS/2 user, what do I need to know?"
+Unlike the native command prompt, the current working directory is,
+for security reasons common on Unix systems which the shell is designed for,
+not in the search path at all; if you really need this, run the command
+.Li PATH=.$PATHSEP$PATH
+or add that to a suitable initialisation file.
+.Pp
+There are two different newline modes for mksh-os2: standard (Unix) mode,
+in which only LF (0A hex) is supported as line separator, and "textmode",
+which also accepts ASCII newlines (CR+LF), like most other tools on OS/2,
+but creating an incompatibility with standard
+.Nm .
+If you compiled mksh from source, you will get the standard Unix mode unless
+.Fl T
+is added during compilation; you will most likely have gotten this shell
+through komh's port on Hobbes, or from his OS/2 Factory on eComStation
+Korea, which uses "textmode", though.
+Most OS/2 users will want to use "textmode" unless they need absolute
+compatibility with Unix
+.Nm .
+.Ss "How do I start mksh on a specific terminal?"
+Normally:
+.Dl mksh \-T/dev/tty2
+.Pp
+However, if you want for it to return (e.g. for an embedded
+system rescue shell), use this on your real console device instead:
+.Dl mksh \-T!/dev/ttyACM0
+.Pp
+.Nm
+can also daemonise (send to the background):
+.Dl mksh \-T\- \-c \*(aqexec cdio lock\*(aq
+.Ss "POSIX says..."
+Run the shell in POSIX mode (and possibly
+.Nm lksh
+instead of
+.Nm mksh ) :
+.Dl set \-o posix
+.Ss "My prompt from <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.
Copied: trunk/contrib/mksh/mksh.ico (from rev 12140, vendor/MirOS/mksh/dist/mksh.ico)
===================================================================
(Binary files differ)
Copied: trunk/contrib/mksh/os2.c (from rev 12140, vendor/MirOS/mksh/dist/os2.c)
===================================================================
--- trunk/contrib/mksh/os2.c (rev 0)
+++ trunk/contrib/mksh/os2.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -0,0 +1,575 @@
+/*-
+ * Copyright (c) 2015, 2017
+ * KO Myung-Hun <komh at chollian.net>
+ * Copyright (c) 2017
+ * mirabilos <m at 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.
+ */
+
+#define INCL_DOS
+#include <os2.h>
+
+#include "sh.h"
+
+#include <klibc/startup.h>
+#include <errno.h>
+#include <io.h>
+#include <unistd.h>
+#include <process.h>
+
+__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.8 2017/12/22 16:41:42 tg Exp $");
+
+static char *remove_trailing_dots(char *);
+static int access_stat_ex(int (*)(), const char *, void *);
+static int test_exec_exist(const char *, char *);
+static void response(int *, const char ***);
+static char *make_response_file(char * const *);
+static void add_temp(const char *);
+static void cleanup_temps(void);
+static void cleanup(void);
+
+#define RPUT(x) do { \
+ if (new_argc >= new_alloc) { \
+ new_alloc += 20; \
+ if (!(new_argv = realloc(new_argv, \
+ new_alloc * sizeof(char *)))) \
+ goto exit_out_of_memory; \
+ } \
+ new_argv[new_argc++] = (x); \
+} while (/* CONSTCOND */ 0)
+
+#define KLIBC_ARG_RESPONSE_EXCLUDE \
+ (__KLIBC_ARG_DQUOTE | __KLIBC_ARG_WILDCARD | __KLIBC_ARG_SHELL)
+
+static void
+response(int *argcp, const char ***argvp)
+{
+ int i, old_argc, new_argc, new_alloc = 0;
+ const char **old_argv, **new_argv;
+ char *line, *l, *p;
+ FILE *f;
+
+ old_argc = *argcp;
+ old_argv = *argvp;
+ for (i = 1; i < old_argc; ++i)
+ if (old_argv[i] &&
+ !(old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) &&
+ old_argv[i][0] == '@')
+ break;
+
+ if (i >= old_argc)
+ /* do nothing */
+ return;
+
+ new_argv = NULL;
+ new_argc = 0;
+ for (i = 0; i < old_argc; ++i) {
+ if (i == 0 || !old_argv[i] ||
+ (old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) ||
+ old_argv[i][0] != '@' ||
+ !(f = fopen(old_argv[i] + 1, "rt")))
+ RPUT(old_argv[i]);
+ else {
+ long filesize;
+
+ fseek(f, 0, SEEK_END);
+ filesize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ line = malloc(filesize + /* type */ 1 + /* NUL */ 1);
+ if (!line) {
+ exit_out_of_memory:
+ fputs("Out of memory while reading response file\n", stderr);
+ exit(255);
+ }
+
+ line[0] = __KLIBC_ARG_NONZERO | __KLIBC_ARG_RESPONSE;
+ l = line + 1;
+ while (fgets(l, (filesize + 1) - (l - (line + 1)), f)) {
+ p = strchr(l, '\n');
+ if (p) {
+ /*
+ * if a line ends with a backslash,
+ * concatenate with the next line
+ */
+ if (p > l && p[-1] == '\\') {
+ char *p1;
+ int count = 0;
+
+ for (p1 = p - 1; p1 >= l &&
+ *p1 == '\\'; p1--)
+ count++;
+
+ if (count & 1) {
+ l = p + 1;
+
+ continue;
+ }
+ }
+
+ *p = 0;
+ }
+ p = strdup(line);
+ if (!p)
+ goto exit_out_of_memory;
+
+ RPUT(p + 1);
+
+ l = line + 1;
+ }
+
+ free(line);
+
+ if (ferror(f)) {
+ fputs("Cannot read response file\n", stderr);
+ exit(255);
+ }
+
+ fclose(f);
+ }
+ }
+
+ RPUT(NULL);
+ --new_argc;
+
+ *argcp = new_argc;
+ *argvp = new_argv;
+}
+
+static void
+init_extlibpath(void)
+{
+ const char *vars[] = {
+ "BEGINLIBPATH",
+ "ENDLIBPATH",
+ "LIBPATHSTRICT",
+ NULL
+ };
+ char val[512];
+ int flag;
+
+ for (flag = 0; vars[flag]; flag++) {
+ DosQueryExtLIBPATH(val, flag + 1);
+ if (val[0])
+ setenv(vars[flag], val, 1);
+ }
+}
+
+void
+os2_init(int *argcp, const char ***argvp)
+{
+ response(argcp, argvp);
+
+ init_extlibpath();
+
+ if (!isatty(STDIN_FILENO))
+ setmode(STDIN_FILENO, O_BINARY);
+ if (!isatty(STDOUT_FILENO))
+ setmode(STDOUT_FILENO, O_BINARY);
+ if (!isatty(STDERR_FILENO))
+ setmode(STDERR_FILENO, O_BINARY);
+
+ atexit(cleanup);
+}
+
+void
+setextlibpath(const char *name, const char *val)
+{
+ int flag;
+ char *p, *cp;
+
+ if (!strcmp(name, "BEGINLIBPATH"))
+ flag = BEGIN_LIBPATH;
+ else if (!strcmp(name, "ENDLIBPATH"))
+ flag = END_LIBPATH;
+ else if (!strcmp(name, "LIBPATHSTRICT"))
+ flag = LIBPATHSTRICT;
+ else
+ return;
+
+ /* convert slashes to backslashes */
+ strdupx(cp, val, ATEMP);
+ for (p = cp; *p; p++) {
+ if (*p == '/')
+ *p = '\\';
+ }
+
+ DosSetExtLIBPATH(cp, flag);
+
+ afree(cp, ATEMP);
+}
+
+/* remove trailing dots */
+static char *
+remove_trailing_dots(char *name)
+{
+ char *p = strnul(name);
+
+ while (--p > name && *p == '.')
+ /* nothing */;
+
+ if (*p != '.' && *p != '/' && *p != '\\' && *p != ':')
+ p[1] = '\0';
+
+ return (name);
+}
+
+#define REMOVE_TRAILING_DOTS(name) \
+ remove_trailing_dots(memcpy(alloca(strlen(name) + 1), name, strlen(name) + 1))
+
+/* alias of stat() */
+extern int _std_stat(const char *, struct stat *);
+
+/* replacement for stat() of kLIBC which fails if there are trailing dots */
+int
+stat(const char *name, struct stat *buffer)
+{
+ return (_std_stat(REMOVE_TRAILING_DOTS(name), buffer));
+}
+
+/* alias of access() */
+extern int _std_access(const char *, int);
+
+/* replacement for access() of kLIBC which fails if there are trailing dots */
+int
+access(const char *name, int mode)
+{
+ /*
+ * On OS/2 kLIBC, X_OK is set only for executable files.
+ * This prevents scripts from being executed.
+ */
+ if (mode & X_OK)
+ mode = (mode & ~X_OK) | R_OK;
+
+ return (_std_access(REMOVE_TRAILING_DOTS(name), mode));
+}
+
+#define MAX_X_SUFFIX_LEN 4
+
+static const char *x_suffix_list[] =
+ { "", ".ksh", ".exe", ".sh", ".cmd", ".com", ".bat", NULL };
+
+/* call fn() by appending executable extensions */
+static int
+access_stat_ex(int (*fn)(), const char *name, void *arg)
+{
+ char *x_name;
+ const char **x_suffix;
+ int rc = -1;
+ size_t x_namelen = strlen(name) + MAX_X_SUFFIX_LEN + 1;
+
+ /* otherwise, try to append executable suffixes */
+ x_name = alloc(x_namelen, ATEMP);
+
+ for (x_suffix = x_suffix_list; rc && *x_suffix; x_suffix++) {
+ strlcpy(x_name, name, x_namelen);
+ strlcat(x_name, *x_suffix, x_namelen);
+
+ rc = fn(x_name, arg);
+ }
+
+ afree(x_name, ATEMP);
+
+ return (rc);
+}
+
+/* access()/search_access() version */
+int
+access_ex(int (*fn)(const char *, int), const char *name, int mode)
+{
+ /*XXX this smells fishy --mirabilos */
+ return (access_stat_ex(fn, name, (void *)mode));
+}
+
+/* stat() version */
+int
+stat_ex(const char *name, struct stat *buffer)
+{
+ return (access_stat_ex(stat, name, buffer));
+}
+
+static int
+test_exec_exist(const char *name, char *real_name)
+{
+ struct stat sb;
+
+ if (stat(name, &sb) < 0 || !S_ISREG(sb.st_mode))
+ return (-1);
+
+ /* safe due to calculations in real_exec_name() */
+ memcpy(real_name, name, strlen(name) + 1);
+
+ return (0);
+}
+
+const char *
+real_exec_name(const char *name)
+{
+ char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1];
+ const char *real_name = name;
+
+ if (access_stat_ex(test_exec_exist, real_name, x_name) != -1)
+ /*XXX memory leak */
+ strdupx(real_name, x_name, ATEMP);
+
+ return (real_name);
+}
+
+/* make a response file to pass a very long command line */
+static char *
+make_response_file(char * const *argv)
+{
+ char rsp_name_arg[] = "@mksh-rsp-XXXXXX";
+ char *rsp_name = &rsp_name_arg[1];
+ int i;
+ int fd;
+ char *result;
+
+ if ((fd = mkstemp(rsp_name)) == -1)
+ return (NULL);
+
+ /* write all the arguments except a 0th program name */
+ for (i = 1; argv[i]; i++) {
+ write(fd, argv[i], strlen(argv[i]));
+ write(fd, "\n", 1);
+ }
+
+ close(fd);
+ add_temp(rsp_name);
+ strdupx(result, rsp_name_arg, ATEMP);
+
+ return (result);
+}
+
+/* alias of execve() */
+extern int _std_execve(const char *, char * const *, char * const *);
+
+/* replacement for execve() of kLIBC */
+int
+execve(const char *name, char * const *argv, char * const *envp)
+{
+ const char *exec_name;
+ FILE *fp;
+ char sign[2];
+ int pid;
+ int status;
+ int fd;
+ int rc;
+ int saved_mode;
+ int saved_errno;
+
+ /*
+ * #! /bin/sh : append .exe
+ * extproc sh : search sh.exe in PATH
+ */
+ exec_name = search_path(name, path, X_OK, NULL);
+ if (!exec_name) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ /*-
+ * kLIBC execve() has problems when executing scripts.
+ * 1. it fails to execute a script if a directory whose name
+ * is same as an interpreter exists in a current directory.
+ * 2. it fails to execute a script not starting with sharpbang.
+ * 3. it fails to execute a batch file if COMSPEC is set to a shell
+ * incompatible with cmd.exe, such as /bin/sh.
+ * And ksh process scripts more well, so let ksh process scripts.
+ */
+ errno = 0;
+ if (!(fp = fopen(exec_name, "rb")))
+ errno = ENOEXEC;
+
+ if (!errno && fread(sign, 1, sizeof(sign), fp) != sizeof(sign))
+ errno = ENOEXEC;
+
+ if (fp && fclose(fp))
+ errno = ENOEXEC;
+
+ if (!errno &&
+ !((sign[0] == 'M' && sign[1] == 'Z') ||
+ (sign[0] == 'N' && sign[1] == 'E') ||
+ (sign[0] == 'L' && sign[1] == 'X')))
+ errno = ENOEXEC;
+
+ if (errno == ENOEXEC)
+ return (-1);
+
+ /*
+ * Normal OS/2 programs expect that standard IOs, especially stdin,
+ * are opened in text mode at the startup. By the way, on OS/2 kLIBC
+ * child processes inherit a translation mode of a parent process.
+ * As a result, if stdin is set to binary mode in a parent process,
+ * stdin of child processes is opened in binary mode as well at the
+ * startup. In this case, some programs such as sed suffer from CR.
+ */
+ saved_mode = setmode(STDIN_FILENO, O_TEXT);
+
+ pid = spawnve(P_NOWAIT, exec_name, argv, envp);
+ saved_errno = errno;
+
+ /* arguments too long? */
+ if (pid == -1 && saved_errno == EINVAL) {
+ /* retry with a response file */
+ char *rsp_name_arg = make_response_file(argv);
+
+ if (rsp_name_arg) {
+ char *rsp_argv[3] = { argv[0], rsp_name_arg, NULL };
+
+ pid = spawnve(P_NOWAIT, exec_name, rsp_argv, envp);
+ saved_errno = errno;
+
+ afree(rsp_name_arg, ATEMP);
+ }
+ }
+
+ /* restore translation mode of stdin */
+ setmode(STDIN_FILENO, saved_mode);
+
+ if (pid == -1) {
+ cleanup_temps();
+
+ errno = saved_errno;
+ return (-1);
+ }
+
+ /* close all opened handles */
+ for (fd = 0; fd < NUFILE; fd++) {
+ if (fcntl(fd, F_GETFD) == -1)
+ continue;
+
+ close(fd);
+ }
+
+ while ((rc = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+ /* nothing */;
+
+ cleanup_temps();
+
+ /* Is this possible? And is this right? */
+ if (rc == -1)
+ return (-1);
+
+ if (WIFSIGNALED(status))
+ _exit(ksh_sigmask(WTERMSIG(status)));
+
+ _exit(WEXITSTATUS(status));
+}
+
+static struct temp *templist = NULL;
+
+static void
+add_temp(const char *name)
+{
+ struct temp *tp;
+
+ tp = alloc(offsetof(struct temp, tffn[0]) + strlen(name) + 1, APERM);
+ memcpy(tp->tffn, name, strlen(name) + 1);
+ tp->next = templist;
+ templist = tp;
+}
+
+/* alias of unlink() */
+extern int _std_unlink(const char *);
+
+/*
+ * Replacement for unlink() of kLIBC not supporting to remove files used by
+ * another processes.
+ */
+int
+unlink(const char *name)
+{
+ int rc;
+
+ rc = _std_unlink(name);
+ if (rc == -1 && errno != ENOENT)
+ add_temp(name);
+
+ return (rc);
+}
+
+static void
+cleanup_temps(void)
+{
+ struct temp *tp;
+ struct temp **tpnext;
+
+ for (tpnext = &templist, tp = templist; tp; tp = *tpnext) {
+ if (_std_unlink(tp->tffn) == 0 || errno == ENOENT) {
+ *tpnext = tp->next;
+ afree(tp, APERM);
+ } else {
+ tpnext = &tp->next;
+ }
+ }
+}
+
+static void
+cleanup(void)
+{
+ cleanup_temps();
+}
+
+int
+getdrvwd(char **cpp, unsigned int drvltr)
+{
+ PBYTE cp;
+ ULONG sz;
+ APIRET rc;
+ ULONG drvno;
+
+ if (DosQuerySysInfo(QSV_MAX_PATH_LENGTH, QSV_MAX_PATH_LENGTH,
+ &sz, sizeof(sz)) != 0) {
+ errno = EDOOFUS;
+ return (-1);
+ }
+
+ /* allocate 'X:/' plus sz plus NUL */
+ checkoktoadd((size_t)sz, (size_t)4);
+ cp = aresize(*cpp, (size_t)sz + (size_t)4, ATEMP);
+ cp[0] = ksh_toupper(drvltr);
+ cp[1] = ':';
+ cp[2] = '/';
+ drvno = ksh_numuc(cp[0]) + 1;
+ /* NUL is part of space within buffer passed */
+ ++sz;
+ if ((rc = DosQueryCurrentDir(drvno, cp + 3, &sz)) == 0) {
+ /* success! */
+ *cpp = cp;
+ return (0);
+ }
+ afree(cp, ATEMP);
+ *cpp = NULL;
+ switch (rc) {
+ case 15: /* invalid drive */
+ errno = ENOTBLK;
+ break;
+ case 26: /* not dos disk */
+ errno = ENODEV;
+ break;
+ case 108: /* drive locked */
+ errno = EDEADLK;
+ break;
+ case 111: /* buffer overflow */
+ errno = ENAMETOOLONG;
+ break;
+ default:
+ errno = EINVAL;
+ }
+ return (-1);
+}
Modified: trunk/contrib/mksh/rlimits.gen
===================================================================
--- trunk/contrib/mksh/rlimits.gen 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/rlimits.gen 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,19 +1,29 @@
+/* +++ GENERATED FILE +++ DO NOT EDIT +++ */
+/*-
+ * Copyright (c) 2013, 2015
+ * mirabilos <m at 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.
+ */
+
#ifndef RLIMITS_OPTCS
#if defined(RLIMITS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.1 2013/11/17 22:21:18 tg Exp $");
-struct limits {
- /* limit resource */
- int resource;
- /* multiply by to get rlim_{cur,max} values */
- unsigned int factor;
- /* getopts char */
- char optchar;
- /* limit name */
- char name[1];
-};
-#define FN(lname,lid,lfac,lopt) static const struct { int resource; unsigned int factor; char optchar; char name[sizeof(lname)]; } rlimits_ ## lid = { lid, lfac, lopt, lname };
+__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
#elif defined(RLIMITS_ITEMS)
-#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
+#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
#endif
#ifndef F0
#define F0 FN
Modified: trunk/contrib/mksh/rlimits.opt
===================================================================
--- trunk/contrib/mksh/rlimits.opt 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/rlimits.opt 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,18 +1,27 @@
+/*-
+ * Copyright (c) 2013, 2015
+ * mirabilos <m at 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.
+ */
+
@RLIMITS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.1 2013/11/17 22:21:18 tg Exp $");
-struct limits {
- /* limit resource */
- int resource;
- /* multiply by to get rlim_{cur,max} values */
- unsigned int factor;
- /* getopts char */
- char optchar;
- /* limit name */
- char name[1];
-};
-#define FN(lname,lid,lfac,lopt) static const struct { int resource; unsigned int factor; char optchar; char name[sizeof(lname)]; } rlimits_ ## lid = { lid, lfac, lopt, lname };
+__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
@RLIMITS_ITEMS
-#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
+#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
@@
/* generic options for the ulimit builtin */
Modified: trunk/contrib/mksh/sh.h
===================================================================
--- trunk/contrib/mksh/sh.h 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/sh.h 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,8 +1,8 @@
-/* $OpenBSD: sh.h,v 1.33 2013/12/18 13:53:12 millert Exp $ */
+/* $OpenBSD: sh.h,v 1.35 2015/09/10 22:48:58 nicm Exp $ */
/* $OpenBSD: shf.h,v 1.6 2005/12/11 18:53:51 deraadt Exp $ */
/* $OpenBSD: table.h,v 1.8 2012/02/19 07:52:30 otto Exp $ */
/* $OpenBSD: tree.h,v 1.10 2005/03/28 21:28:22 deraadt Exp $ */
-/* $OpenBSD: expand.h,v 1.6 2005/03/30 17:16:37 deraadt Exp $ */
+/* $OpenBSD: expand.h,v 1.7 2015/09/01 13:12:31 tedu Exp $ */
/* $OpenBSD: lex.h,v 1.13 2013/03/03 19:11:34 guenther Exp $ */
/* $OpenBSD: proto.h,v 1.35 2013/09/04 15:49:19 millert Exp $ */
/* $OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $ */
@@ -10,8 +10,8 @@
/*-
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -64,6 +64,9 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#if HAVE_IO_H
+#include <io.h>
+#endif
#if HAVE_LIBGEN_H
#include <libgen.h>
#endif
@@ -105,7 +108,17 @@
#if HAVE_VALUES_H
#include <values.h>
#endif
+#ifdef MIRBSD_BOOTFLOPPY
+#include <wchar.h>
+#endif
+/* monkey-patch known-bad offsetof versions to quell a warning */
+#if (defined(__KLIBC__) || defined(__dietlibc__)) && \
+ ((defined(__GNUC__) && (__GNUC__ > 3)) || defined(__NWCC__))
+#undef offsetof
+#define offsetof(s, e) __builtin_offsetof(s, e)
+#endif
+
#undef __attribute__
#if HAVE_ATTRIBUTE_BOUNDED
#define MKSH_A_BOUNDED(x,y,z) __attribute__((__bounded__(x, y, z)))
@@ -169,9 +182,9 @@
#endif
#ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.701.2.4 2015/03/01 15:43:05 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.858 2018/01/14 01:47:36 tg Exp $");
#endif
-#define MKSH_VERSION "R50 2015/03/01"
+#define MKSH_VERSION "R56 2018/01/14"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@@ -251,9 +264,27 @@
#ifndef MKSH_INCLUDES_ONLY
+/* EBCDIC fun */
+
+/* see the large comment in shf.c for an EBCDIC primer */
+
+#if defined(MKSH_FOR_Z_OS) && defined(__MVS__) && defined(__IBMC__) && defined(__CHARSET_LIB)
+# if !__CHARSET_LIB && !defined(MKSH_EBCDIC)
+# error "Please compile with Build.sh -E for EBCDIC!"
+# endif
+# if __CHARSET_LIB && defined(MKSH_EBCDIC)
+# error "Please compile without -E argument to Build.sh for ASCII!"
+# endif
+# if __CHARSET_LIB && !defined(_ENHANCED_ASCII_EXT)
+ /* go all-out on ASCII */
+# define _ENHANCED_ASCII_EXT 0xFFFFFFFF
+# endif
+#endif
+
/* extra types */
-#if !HAVE_GETRUSAGE
+/* getrusage does not exist on OS/2 kLIBC */
+#if !HAVE_GETRUSAGE && !defined(__OS2__)
#undef rusage
#undef RUSAGE_SELF
#undef RUSAGE_CHILDREN
@@ -298,16 +329,6 @@
} while (/* CONSTCOND */ 0)
#endif
-#define ksh_isdigit(c) (((c) >= '0') && ((c) <= '9'))
-#define ksh_islower(c) (((c) >= 'a') && ((c) <= 'z'))
-#define ksh_isupper(c) (((c) >= 'A') && ((c) <= 'Z'))
-#define ksh_tolower(c) (((c) >= 'A') && ((c) <= 'Z') ? (c) - 'A' + 'a' : (c))
-#define ksh_toupper(c) (((c) >= 'a') && ((c) <= 'z') ? (c) - 'a' + 'A' : (c))
-#define ksh_isdash(s) (((s)[0] == '-') && ((s)[1] == '\0'))
-#define ksh_isspace(c) ((((c) >= 0x09) && ((c) <= 0x0D)) || ((c) == 0x20))
-#define ksh_min(x,y) ((x) < (y) ? (x) : (y))
-#define ksh_max(x,y) ((x) > (y) ? (x) : (y))
-
#ifdef MKSH__NO_PATH_MAX
#undef PATH_MAX
#else
@@ -339,29 +360,80 @@
#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
#endif
-#ifndef NSIG
-#if defined(_NSIG)
-#define NSIG _NSIG
+
+/* determine ksh_NSIG: first, use the traditional definitions */
+#undef ksh_NSIG
+#if defined(NSIG)
+#define ksh_NSIG (NSIG)
+#elif defined(_NSIG)
+#define ksh_NSIG (_NSIG)
#elif defined(SIGMAX)
-#define NSIG (SIGMAX+1)
+#define ksh_NSIG (SIGMAX + 1)
#elif defined(_SIGMAX)
-#define NSIG (_SIGMAX+1)
+#define ksh_NSIG (_SIGMAX + 1)
+#elif defined(NSIG_MAX)
+#define ksh_NSIG (NSIG_MAX)
+#elif defined(MKSH_FOR_Z_OS)
+#define ksh_NSIG 40
#else
# error Please have your platform define NSIG.
-#define NSIG 64
#endif
-#endif
-
-/* get rid of this (and awk/printf(1) in Build.sh) later */
-#if (NSIG < 1)
+/* range-check them */
+#if (ksh_NSIG < 1)
# error Your NSIG value is not positive.
-#unset NSIG
-#define NSIG 64
+#undef ksh_NSIG
#endif
+/* second, see if the new POSIX definition is available */
+#ifdef NSIG_MAX
+#if (NSIG_MAX < 2)
+/* and usable */
+# error Your NSIG_MAX value is too small.
+#undef NSIG_MAX
+#elif (ksh_NSIG > NSIG_MAX)
+/* and realistic */
+# error Your NSIG value is larger than your NSIG_MAX value.
+#undef NSIG_MAX
+#else
+/* since it’s usable, prefer it */
+#undef ksh_NSIG
+#define ksh_NSIG (NSIG_MAX)
+#endif
+/* if NSIG_MAX is now still defined, use sysconf(_SC_NSIG) at runtime */
+#endif
+/* third, for cpp without the error directive, default */
+#ifndef ksh_NSIG
+#define ksh_NSIG 64
+#endif
+#define ksh_sigmask(sig) (((sig) < 1 || (sig) > 127) ? 255 : 128 + (sig))
+
/* OS-dependent additions (functions, variables, by OS) */
+#ifdef MKSH_EXE_EXT
+#undef MKSH_EXE_EXT
+#define MKSH_EXE_EXT ".exe"
+#else
+#define MKSH_EXE_EXT ""
+#endif
+
+#ifdef __OS2__
+#define MKSH_UNIXROOT "/@unixroot"
+#else
+#define MKSH_UNIXROOT ""
+#endif
+
+#ifdef MKSH_DOSPATH
+#ifndef __GNUC__
+# error GCC extensions needed later on
+#endif
+#define MKSH_PATHSEPS ";"
+#define MKSH_PATHSEPC ';'
+#else
+#define MKSH_PATHSEPS ":"
+#define MKSH_PATHSEPC ':'
+#endif
+
#if !HAVE_FLOCK_DECL
extern int flock(int, int);
#endif
@@ -441,7 +513,24 @@
#define ISTRIP 0
#endif
+#ifdef MKSH_EBCDIC
+#define KSH_BEL '\a'
+#define KSH_ESC 047
+#define KSH_ESC_STRING "\047"
+#define KSH_VTAB '\v'
+#else
+/*
+ * According to the comments in pdksh, \007 seems to be more portable
+ * than \a (HP-UX cc, Ultrix cc, old pcc, etc.) so we avoid the escape
+ * sequence if ASCII can be assumed.
+ */
+#define KSH_BEL 7
+#define KSH_ESC 033
+#define KSH_ESC_STRING "\033"
+#define KSH_VTAB 11
+#endif
+
/* some useful #defines */
#ifdef EXTERN
# define E_INIT(i) = i
@@ -452,7 +541,7 @@
#endif
/* define bit in flag */
-#define BIT(i) (1 << (i))
+#define BIT(i) (1U << (i))
#define NELEM(a) (sizeof(a) / sizeof((a)[0]))
/*
@@ -459,19 +548,39 @@
* Make MAGIC a char that might be printed to make bugs more obvious, but
* not a char that is used often. Also, can't use the high bit as it causes
* portability problems (calling strchr(x, 0x80 | 'x') is error prone).
+ *
+ * MAGIC can be followed by MAGIC (to escape the octet itself) or one of:
+ * ' !)*,-?[]{|}' 0x80|' !*+?@' (probably… hysteric raisins abound)
+ *
+ * The |0x80 is likely unsafe on EBCDIC :( though the listed chars are
+ * low-bit7 at least on cp1047 so YMMV
*/
-#define MAGIC (7) /* prefix for *?[!{,} during expand */
-#define ISMAGIC(c) ((unsigned char)(c) == MAGIC)
+#define MAGIC KSH_BEL /* prefix for *?[!{,} during expand */
+#define ISMAGIC(c) (ord(c) == ORD(MAGIC))
EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
#ifdef MKSH_LEGACY_MODE
-#define KSH_VERSIONNAME "LEGACY"
+#define KSH_VERSIONNAME_ISLEGACY "LEGACY"
#else
-#define KSH_VERSIONNAME "MIRBSD"
+#define KSH_VERSIONNAME_ISLEGACY "MIRBSD"
#endif
-EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME \
- " KSH " MKSH_VERSION);
+#ifdef MKSH_WITH_TEXTMODE
+#define KSH_VERSIONNAME_TEXTMODE " +TEXTMODE"
+#else
+#define KSH_VERSIONNAME_TEXTMODE ""
+#endif
+#ifdef MKSH_EBCDIC
+#define KSH_VERSIONNAME_EBCDIC " +EBCDIC"
+#else
+#define KSH_VERSIONNAME_EBCDIC ""
+#endif
+#ifndef KSH_VERSIONNAME_VENDOR_EXT
+#define KSH_VERSIONNAME_VENDOR_EXT ""
+#endif
+EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME_ISLEGACY \
+ " KSH " MKSH_VERSION KSH_VERSIONNAME_EBCDIC KSH_VERSIONNAME_TEXTMODE \
+ KSH_VERSIONNAME_VENDOR_EXT);
#define KSH_VERSION (initvsn + /* "KSH_VERSION=@(#)" */ 16)
EXTERN const char digits_uc[] E_INIT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
@@ -529,15 +638,12 @@
#endif
#if defined(DEBUG) || defined(__COVERITY__)
-#define mkssert(e) do { if (!(e)) exit(255); } while (/* CONSTCOND */ 0)
#ifndef DEBUG_LEAKS
#define DEBUG_LEAKS
#endif
-#else
-#define mkssert(e) do { } while (/* CONSTCOND */ 0)
#endif
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 505)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 563)
#error Must run Build.sh to compile this.
extern void thiswillneverbedefinedIhope(void);
int
@@ -549,7 +655,7 @@
#endif
/* use this ipv strchr(s, 0) but no side effects in s! */
-#define strnul(s) ((s) + strlen(s))
+#define strnul(s) ((s) + strlen((const void *)s))
#define utf_ptradjx(src, dst) do { \
(dst) = (src) + utf_ptradj(src); \
@@ -565,7 +671,7 @@
#else
/* be careful to evaluate arguments only once! */
#define strdupx(d, s, ap) do { \
- const char *strdup_src = (s); \
+ const char *strdup_src = (const void *)(s); \
char *strdup_dst = NULL; \
\
if (strdup_src != NULL) { \
@@ -576,7 +682,7 @@
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
#define strndupx(d, s, n, ap) do { \
- const char *strdup_src = (s); \
+ const char *strdup_src = (const void *)(s); \
char *strdup_dst = NULL; \
\
if (strdup_src != NULL) { \
@@ -589,21 +695,7 @@
} while (/* CONSTCOND */ 0)
#endif
-#ifdef MKSH_LEGACY_MODE
-#ifndef MKSH_NO_CMDLINE_EDITING
-#define MKSH_NO_CMDLINE_EDITING /* defined */
-#endif
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS /* defined */
-#endif
-#undef MKSH_S_NOVI
-#define MKSH_S_NOVI 1
-#endif
-
#ifdef MKSH_SMALL
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS /* defined */
-#endif
#ifndef MKSH_NOPWNAM
#define MKSH_NOPWNAM /* defined */
#endif
@@ -620,14 +712,8 @@
#define MKSH_UNEMPLOYED 1
#endif
-/* these shall be smaller than 100 */
-#ifdef MKSH_CONSERVATIVE_FDS
#define NUFILE 32 /* Number of user-accessible files */
#define FDBASE 10 /* First file usable by Shell */
-#else
-#define NUFILE 56 /* Number of user-accessible files */
-#define FDBASE 24 /* First file usable by Shell */
-#endif
/*
* simple grouping allocator
@@ -658,16 +744,29 @@
/* 1. internal structure */
-struct lalloc {
- struct lalloc *next;
+struct lalloc_common {
+ struct lalloc_common *next;
};
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+struct lalloc_item {
+ struct lalloc_common *next;
+ size_t len;
+ char dummy[8192 - sizeof(struct lalloc_common *) - sizeof(size_t)];
+};
+#endif
+
/* 2. sizes */
-#define ALLOC_ITEM struct lalloc
-#define ALLOC_SIZE (sizeof(ALLOC_ITEM))
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+#define ALLOC_ITEM struct lalloc_item
+#define ALLOC_OVERHEAD 0
+#else
+#define ALLOC_ITEM struct lalloc_common
+#define ALLOC_OVERHEAD (sizeof(ALLOC_ITEM))
+#endif
-/* 3. group structure (only the same for lalloc.c) */
-typedef struct lalloc Area;
+/* 3. group structure */
+typedef struct lalloc_common Area;
EXTERN Area aperm; /* permanent object space */
@@ -704,8 +803,8 @@
struct sretrace_info;
struct yyrecursive_state;
-EXTERN struct sretrace_info *retrace_info E_INIT(NULL);
-EXTERN int subshell_nesting_type E_INIT(0);
+EXTERN struct sretrace_info *retrace_info;
+EXTERN unsigned int subshell_nesting_type;
extern struct env {
ALLOC_ITEM alloc_INT; /* internal, do not touch */
@@ -730,6 +829,7 @@
#define E_LOOP 5 /* executing for/while # */
#define E_ERRH 6 /* general error handler # */
#define E_GONE 7 /* hidden in child */
+#define E_EVAL 8 /* running eval # */
/* # indicates env has valid jbuf (see unwind()) */
/* struct env.flag values */
@@ -792,38 +892,348 @@
/* null value for variable; comparison pointer for unset */
EXTERN char null[] E_INIT("");
-/* helpers for string pooling */
-EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
-EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
-#if defined(__GNUC__)
-/* trust this to have string pooling; -Wformat bitches otherwise */
-#define Tsynerr "syntax error"
+
+/* string pooling: do we rely on the compiler? */
+#ifndef HAVE_STRING_POOLING
+/* no, we use our own, saves quite some space */
+#elif HAVE_STRING_POOLING == 2
+/* “on demand” */
+#ifdef __GNUC__
+/* only for GCC 4 or later, older ones can get by without */
+#if __GNUC__ < 4
+#undef HAVE_STRING_POOLING
+#endif
#else
-EXTERN const char Tsynerr[] E_INIT("syntax error");
+/* not GCC, default to on */
#endif
+#elif HAVE_STRING_POOLING == 0
+/* default to on, unless explicitly set to 0 */
+#undef HAVE_STRING_POOLING
+#endif
+
+#ifndef HAVE_STRING_POOLING /* helpers for pooled strings */
+EXTERN const char T4spaces[] E_INIT(" ");
+#define T1space (Treal_sp2 + 5)
+#define Tcolsp (Tf_sD_ + 2)
+#define TC_IFSWS (TinitIFS + 4)
+EXTERN const char TinitIFS[] E_INIT("IFS= \t\n");
+EXTERN const char TFCEDIT_dollaru[] E_INIT("${FCEDIT:-/bin/ed} $_");
+#define Tspdollaru (TFCEDIT_dollaru + 18)
+EXTERN const char Tsgdot[] E_INIT("*=.");
+EXTERN const char Taugo[] E_INIT("augo");
+EXTERN const char Tbracket[] E_INIT("[");
+#define Tdot (Tsgdot + 2)
+#define Talias (Tunalias + 2)
+EXTERN const char Tbadnum[] E_INIT("bad number");
+#define Tbadsubst (Tfg_badsubst + 10)
+EXTERN const char Tbg[] E_INIT("bg");
+EXTERN const char Tbad_bsize[] E_INIT("bad shf/buf/bsize");
+#define Tbsize (Tbad_bsize + 12)
+EXTERN const char Tbad_sig_ss[] E_INIT("%s: bad signal '%s'");
+#define Tbad_sig_s (Tbad_sig_ss + 4)
+EXTERN const char Tsgbreak[] E_INIT("*=break");
+#define Tbreak (Tsgbreak + 2)
+EXTERN const char T__builtin[] E_INIT("-\\builtin");
+#define T_builtin (T__builtin + 1)
+#define Tbuiltin (T__builtin + 2)
+EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
+EXTERN const char Tcant_cd[] E_INIT("restricted shell - can't cd");
+EXTERN const char Tcant_find[] E_INIT("can't find");
+EXTERN const char Tcant_open[] E_INIT("can't open");
+#define Tbytes (Toomem + 24)
+EXTERN const char Tbcat[] E_INIT("!cat");
+#define Tcat (Tbcat + 1)
+#define Tcd (Tcant_cd + 25)
+#define T_command (T_funny_command + 9)
+#define Tcommand (T_funny_command + 10)
+EXTERN const char Tsgcontinue[] E_INIT("*=continue");
+#define Tcontinue (Tsgcontinue + 2)
+EXTERN const char Tcreate[] E_INIT("create");
+EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected");
+EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL");
+EXTERN const char Tdsgexport[] E_INIT("^*=export");
+#define Texport (Tdsgexport + 3)
+#ifdef __OS2__
+EXTERN const char Textproc[] E_INIT("extproc");
+#endif
+EXTERN const char Tfalse[] E_INIT("false");
+EXTERN const char Tfg[] E_INIT("fg");
+EXTERN const char Tfg_badsubst[] E_INIT("fileglob: bad substitution");
+#define Tfile (Tfile_fd + 20)
+EXTERN const char Tfile_fd[] E_INIT("function definition file");
+EXTERN const char TFPATH[] E_INIT("FPATH");
+EXTERN const char T_function[] E_INIT(" function");
+#define Tfunction (T_function + 1)
+EXTERN const char T_funny_command[] E_INIT("funny $()-command");
+EXTERN const char Tgetopts[] E_INIT("getopts");
+#define Thistory (Tnot_in_history + 7)
+EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
+EXTERN const char Tinvname[] E_INIT("%s: invalid %s name");
+EXTERN const char Tjobs[] E_INIT("jobs");
+EXTERN const char Tjob_not_started[] E_INIT("job not started");
+EXTERN const char Tmksh[] E_INIT("mksh");
+#define Tname (Tinvname + 15)
+EXTERN const char Tno_args[] E_INIT("missing argument");
+EXTERN const char Tno_OLDPWD[] E_INIT("no OLDPWD");
+EXTERN const char Tnot_ident[] E_INIT("is not an identifier");
+EXTERN const char Tnot_in_history[] E_INIT("not in history");
+EXTERN const char Tnot_found_s[] E_INIT("%s not found");
+#define Tnot_found (Tnot_found_s + 3)
+#define Tnot_started (Tjob_not_started + 4)
+#define TOLDPWD (Tno_OLDPWD + 3)
+#define Topen (Tcant_open + 6)
+#define TPATH (TFPATH + 1)
+#define Tpv (TpVv + 1)
+EXTERN const char TpVv[] E_INIT("Vpv");
+#define TPWD (Tno_OLDPWD + 6)
+#define Tread (Tshf_read + 4)
+EXTERN const char Tdsgreadonly[] E_INIT("^*=readonly");
+#define Treadonly (Tdsgreadonly + 3)
+EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection");
+#define Tredirection (Tredirection_dup + 19)
+#define Treal_sp1 (Treal_sp2 + 1)
+EXTERN const char Treal_sp2[] E_INIT(" real ");
+EXTERN const char Treq_arg[] E_INIT("requires an argument");
EXTERN const char Tselect[] E_INIT("select");
-EXTERN const char Tr_fc_e_dash[] E_INIT("r=fc -e -");
-#define Tfc_e_dash (Tr_fc_e_dash + 2) /* "fc -e -" */
-#define Zfc_e_dash 7 /* strlen(Tfc_e_dash) */
-EXTERN const char Tlocal_typeset[] E_INIT("local=typeset");
-#define T_typeset (Tlocal_typeset + 5) /* "=typeset" */
-#define Ttypeset (Tlocal_typeset + 6) /* "typeset" */
-EXTERN const char Talias[] E_INIT("alias");
+EXTERN const char Tsgset[] E_INIT("*=set");
+#define Tset (Tf_parm + 18)
+#define Tsh (Tmksh + 2)
+#define TSHELL (TEXECSHELL + 4)
+#define Tshell (Ttoo_many_files + 23)
+EXTERN const char Tshf_read[] E_INIT("shf_read");
+EXTERN const char Tshf_write[] E_INIT("shf_write");
+EXTERN const char Tgsource[] E_INIT("=source");
+#define Tsource (Tgsource + 1)
+EXTERN const char Tj_suspend[] E_INIT("j_suspend");
+#define Tsuspend (Tj_suspend + 2)
+EXTERN const char Tsynerr[] E_INIT("syntax error");
+EXTERN const char Ttime[] E_INIT("time");
+EXTERN const char Ttoo_many_args[] E_INIT("too many arguments");
+EXTERN const char Ttoo_many_files[] E_INIT("too many open files in shell");
+EXTERN const char Ttrue[] E_INIT("true");
+EXTERN const char Ttty_fd_dupof[] E_INIT("dup of tty fd");
+#define Ttty_fd (Ttty_fd_dupof + 7)
+EXTERN const char Tdgtypeset[] E_INIT("^=typeset");
+#define Ttypeset (Tdgtypeset + 2)
+#define Tugo (Taugo + 1)
EXTERN const char Tunalias[] E_INIT("unalias");
-EXTERN const char Tsgset[] E_INIT("*=set");
-#define Tset (Tsgset + 2) /* "set" */
-EXTERN const char Tsgunset[] E_INIT("*=unset");
-#define Tunset (Tsgunset + 2) /* "unset" */
-EXTERN const char Tsgexport[] E_INIT("*=export");
-#define Texport (Tsgexport + 2) /* "export" */
-EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
-#define Treadonly (Tsgreadonly + 2) /* "readonly" */
-EXTERN const char Tgbuiltin[] E_INIT("=builtin");
-#define Tbuiltin (Tgbuiltin + 1) /* "builtin" */
-EXTERN const char T_function[] E_INIT(" function");
-#define Tfunction (T_function + 1) /* "function" */
-EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
-#define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */
+#define Tunexpected (TELIF_unexpected + 6)
+EXTERN const char Tunexpected_type[] E_INIT("%s: unexpected %s type %d");
+EXTERN const char Tunknown_option[] E_INIT("unknown option");
+EXTERN const char Tunwind[] E_INIT("unwind");
+#define Tuser_sp1 (Tuser_sp2 + 1)
+EXTERN const char Tuser_sp2[] E_INIT(" user ");
+#define Twrite (Tshf_write + 4)
+EXTERN const char Tf__S[] E_INIT(" %S");
+#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 ");
+EXTERN const char Tf_s_T[] E_INIT("%s %T");
+EXTERN const char Tf_s_s_sN[] E_INIT("%s %s %s\n");
+#define Tf_s_s (Tf_sD_s_s + 4)
+#define Tf_s_sD_s (Tf_cant_ss_s + 6)
+EXTERN const char Tf_optfoo[] E_INIT("%s%s-%c: %s");
+EXTERN const char Tf_sD_[] E_INIT("%s: ");
+EXTERN const char Tf_szs[] E_INIT("%s: %zd %s");
+EXTERN const char Tf_parm[] E_INIT("%s: parameter not set");
+EXTERN const char Tf_coproc[] E_INIT("-p: %s");
+EXTERN const char Tf_cant_s[] E_INIT("%s: can't %s");
+EXTERN const char Tf_cant_ss_s[] E_INIT("can't %s %s: %s");
+EXTERN const char Tf_heredoc[] E_INIT("here document '%s' unclosed");
+#if HAVE_MKNOD
+EXTERN const char Tf_nonnum[] E_INIT("non-numeric %s %s '%s'");
+#endif
+EXTERN const char Tf_S_[] E_INIT("%S ");
+#define Tf_S (Tf__S + 1)
+#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");
+#define Tf_s (Tf_temp + 28)
+EXTERN const char Tft_end[] E_INIT("%;");
+EXTERN const char Tft_R[] E_INIT("%R");
+#define Tf_d (Tunexpected_type + 23)
+EXTERN const char Tf_sD_s_qs[] E_INIT("%s: %s '%s'");
+EXTERN const char Tf_ro[] E_INIT("read-only: %s");
+EXTERN const char Tf_flags[] E_INIT("%s: flags 0x%X");
+EXTERN const char Tf_temp[] E_INIT("can't %s temporary file %s: %s");
+EXTERN const char Tf_ssfaileds[] E_INIT("%s: %s failed: %s");
+EXTERN const char Tf_sD_sD_s[] E_INIT("%s: %s: %s");
+EXTERN const char Tf__c_[] E_INIT("-%c ");
+EXTERN const char Tf_sD_s_s[] E_INIT("%s: %s %s");
+#define Tf_sN (Tf_s_s_sN + 6)
+#define Tf_sD_s (Tf_temp + 24)
+EXTERN const char T_devtty[] E_INIT("/dev/tty");
+#else /* helpers for string pooling */
+#define T4spaces " "
+#define T1space " "
+#define Tcolsp ": "
+#define TC_IFSWS " \t\n"
+#define TinitIFS "IFS= \t\n"
+#define TFCEDIT_dollaru "${FCEDIT:-/bin/ed} $_"
+#define Tspdollaru " $_"
+#define Tsgdot "*=."
+#define Taugo "augo"
+#define Tbracket "["
+#define Tdot "."
+#define Talias "alias"
+#define Tbadnum "bad number"
+#define Tbadsubst "bad substitution"
+#define Tbg "bg"
+#define Tbad_bsize "bad shf/buf/bsize"
+#define Tbsize "bsize"
+#define Tbad_sig_ss "%s: bad signal '%s'"
+#define Tbad_sig_s "bad signal '%s'"
+#define Tsgbreak "*=break"
+#define Tbreak "break"
+#define T__builtin "-\\builtin"
+#define T_builtin "\\builtin"
+#define Tbuiltin "builtin"
+#define Toomem "can't allocate %zu data bytes"
+#define Tcant_cd "restricted shell - can't cd"
+#define Tcant_find "can't find"
+#define Tcant_open "can't open"
+#define Tbytes "bytes"
+#define Tbcat "!cat"
+#define Tcat "cat"
+#define Tcd "cd"
+#define T_command "-command"
+#define Tcommand "command"
+#define Tsgcontinue "*=continue"
+#define Tcontinue "continue"
+#define Tcreate "create"
+#define TELIF_unexpected "TELIF unexpected"
+#define TEXECSHELL "EXECSHELL"
+#define Tdsgexport "^*=export"
+#define Texport "export"
+#ifdef __OS2__
+#define Textproc "extproc"
+#endif
+#define Tfalse "false"
+#define Tfg "fg"
+#define Tfg_badsubst "fileglob: bad substitution"
+#define Tfile "file"
+#define Tfile_fd "function definition file"
+#define TFPATH "FPATH"
+#define T_function " function"
+#define Tfunction "function"
+#define T_funny_command "funny $()-command"
+#define Tgetopts "getopts"
+#define Thistory "history"
+#define Tintovfl "integer overflow %zu %c %zu prevented"
+#define Tinvname "%s: invalid %s name"
+#define Tjobs "jobs"
+#define Tjob_not_started "job not started"
+#define Tmksh "mksh"
+#define Tname "name"
+#define Tno_args "missing argument"
+#define Tno_OLDPWD "no OLDPWD"
+#define Tnot_ident "is not an identifier"
+#define Tnot_in_history "not in history"
+#define Tnot_found_s "%s not found"
+#define Tnot_found "not found"
+#define Tnot_started "not started"
+#define TOLDPWD "OLDPWD"
+#define Topen "open"
+#define TPATH "PATH"
+#define Tpv "pv"
+#define TpVv "Vpv"
+#define TPWD "PWD"
+#define Tread "read"
+#define Tdsgreadonly "^*=readonly"
+#define Treadonly "readonly"
+#define Tredirection_dup "can't finish (dup) redirection"
+#define Tredirection "redirection"
+#define Treal_sp1 "real "
+#define Treal_sp2 " real "
+#define Treq_arg "requires an argument"
+#define Tselect "select"
+#define Tsgset "*=set"
+#define Tset "set"
+#define Tsh "sh"
+#define TSHELL "SHELL"
+#define Tshell "shell"
+#define Tshf_read "shf_read"
+#define Tshf_write "shf_write"
+#define Tgsource "=source"
+#define Tsource "source"
+#define Tj_suspend "j_suspend"
+#define Tsuspend "suspend"
+#define Tsynerr "syntax error"
+#define Ttime "time"
+#define Ttoo_many_args "too many arguments"
+#define Ttoo_many_files "too many open files in shell"
+#define Ttrue "true"
+#define Ttty_fd_dupof "dup of tty fd"
+#define Ttty_fd "tty fd"
+#define Tdgtypeset "^=typeset"
+#define Ttypeset "typeset"
+#define Tugo "ugo"
+#define Tunalias "unalias"
+#define Tunexpected "unexpected"
+#define Tunexpected_type "%s: unexpected %s type %d"
+#define Tunknown_option "unknown option"
+#define Tunwind "unwind"
+#define Tuser_sp1 "user "
+#define Tuser_sp2 " user "
+#define Twrite "write"
+#define Tf__S " %S"
+#define Tf__d " %d"
+#define Tf__ss " %s%s"
+#define Tf__sN " %s\n"
+#define Tf_sSs "%s/%s"
+#define Tf_T "%T"
+#define Tf_dN "%d\n"
+#define Tf_s_ "%s "
+#define Tf_s_T "%s %T"
+#define Tf_s_s_sN "%s %s %s\n"
+#define Tf_s_s "%s %s"
+#define Tf_s_sD_s "%s %s: %s"
+#define Tf_optfoo "%s%s-%c: %s"
+#define Tf_sD_ "%s: "
+#define Tf_szs "%s: %zd %s"
+#define Tf_parm "%s: parameter not set"
+#define Tf_coproc "-p: %s"
+#define Tf_cant_s "%s: can't %s"
+#define Tf_cant_ss_s "can't %s %s: %s"
+#define Tf_heredoc "here document '%s' unclosed"
+#if HAVE_MKNOD
+#define Tf_nonnum "non-numeric %s %s '%s'"
+#endif
+#define Tf_S_ "%S "
+#define Tf_S "%S"
+#define Tf_lu "%lu"
+#define Tf_toolarge "%s %s too large: %lu"
+#define Tf_ldfailed "%s %s(%d, %ld) failed: %s"
+#define Tf_ss "%s%s"
+#define Tf_sss "%s%s%s"
+#define Tf_sD_s_sD_s "%s: %s %s: %s"
+#define Tf_toomany "too many %ss"
+#define Tf_sd "%s %d"
+#define Tf_s "%s"
+#define Tft_end "%;"
+#define Tft_R "%R"
+#define Tf_d "%d"
+#define Tf_sD_s_qs "%s: %s '%s'"
+#define Tf_ro "read-only: %s"
+#define Tf_flags "%s: flags 0x%X"
+#define Tf_temp "can't %s temporary file %s: %s"
+#define Tf_ssfaileds "%s: %s failed: %s"
+#define Tf_sD_sD_s "%s: %s: %s"
+#define Tf__c_ "-%c "
+#define Tf_sD_s_s "%s: %s %s"
+#define Tf_sN "%s\n"
+#define Tf_sD_s "%s: %s"
+#define T_devtty "/dev/tty"
+#endif /* end of string pooling */
typedef uint8_t Temp_type;
/* expanded heredoc */
@@ -892,13 +1302,13 @@
#define SS_USER BIT(4) /* user is doing the set (ie, trap command) */
#define SS_SHTRAP BIT(5) /* trap for internal use (ALRM, CHLD, WINCH) */
-#define ksh_SIGEXIT 0 /* for trap EXIT */
-#define ksh_SIGERR NSIG /* for trap ERR */
+#define ksh_SIGEXIT 0 /* for trap EXIT */
+#define ksh_SIGERR ksh_NSIG /* for trap ERR */
EXTERN volatile sig_atomic_t trap; /* traps pending? */
EXTERN volatile sig_atomic_t intrsig; /* pending trap interrupts command */
EXTERN volatile sig_atomic_t fatal_trap; /* received a fatal signal */
-extern Trap sigtraps[NSIG+1];
+extern Trap sigtraps[ksh_NSIG + 1];
/* got_winch = 1 when we need to re-adjust the window size */
#ifdef SIGWINCH
@@ -917,7 +1327,7 @@
TMOUT_LEAVING /* have timed out */
};
EXTERN unsigned int ksh_tmout;
-EXTERN enum tmout_enum ksh_tmout_state E_INIT(TMOUT_EXECUTING);
+EXTERN enum tmout_enum ksh_tmout_state;
/* For "You have stopped jobs" message */
EXTERN bool really_exit;
@@ -925,26 +1335,200 @@
/*
* fast character classes
*/
-#define C_ALPHA BIT(0) /* a-z_A-Z */
-#define C_DIGIT BIT(1) /* 0-9 */
-#define C_LEX1 BIT(2) /* \t \n\0|&;<>() */
-#define C_VAR1 BIT(3) /* *@#!$-? */
-#define C_IFSWS BIT(4) /* \t \n (IFS white space) */
-#define C_SUBOP1 BIT(5) /* "=-+?" */
-#define C_QUOTE BIT(6) /* \t\n "#$&'()*;<=>?[\]`| (needing quoting) */
-#define C_IFS BIT(7) /* $IFS */
-#define C_SUBOP2 BIT(8) /* "#%" (magic, see below) */
-extern unsigned char chtypes[];
+/* internal types, do not reference */
-#define ctype(c, t) tobool( ((t) == C_SUBOP2) ? \
- (((c) == '#' || (c) == '%') ? 1 : 0) : \
- (chtypes[(unsigned char)(c)] & (t)) )
-#define ksh_isalphx(c) ctype((c), C_ALPHA)
-#define ksh_isalnux(c) ctype((c), C_ALPHA | C_DIGIT)
+/* initially empty — filled at runtime from $IFS */
+#define CiIFS BIT(0)
+#define CiCNTRL BIT(1) /* \x01‥\x08\x0E‥\x1F\x7F */
+#define CiUPPER BIT(2) /* A‥Z */
+#define CiLOWER BIT(3) /* a‥z */
+#define CiHEXLT BIT(4) /* A‥Fa‥f */
+#define CiOCTAL BIT(5) /* 0‥7 */
+#define CiQCL BIT(6) /* &();| */
+#define CiALIAS BIT(7) /* !,.@ */
+#define CiQCX BIT(8) /* *[\\ */
+#define CiVAR1 BIT(9) /* !*@ */
+#define CiQCM BIT(10) /* /^~ */
+#define CiDIGIT BIT(11) /* 89 */
+#define CiQC BIT(12) /* "' */
+#define CiSPX BIT(13) /* \x0B\x0C */
+#define CiCURLY BIT(14) /* {} */
+#define CiANGLE BIT(15) /* <> */
+#define CiNUL BIT(16) /* \x00 */
+#define CiTAB BIT(17) /* \x09 */
+#define CiNL BIT(18) /* \x0A */
+#define CiCR BIT(19) /* \x0D */
+#define CiSP BIT(20) /* \x20 */
+#define CiHASH BIT(21) /* # */
+#define CiSS BIT(22) /* $ */
+#define CiPERCT BIT(23) /* % */
+#define CiPLUS BIT(24) /* + */
+#define CiMINUS BIT(25) /* - */
+#define CiCOLON BIT(26) /* : */
+#define CiEQUAL BIT(27) /* = */
+#define CiQUEST BIT(28) /* ? */
+#define CiBRACK BIT(29) /* ] */
+#define CiUNDER BIT(30) /* _ */
+#define CiGRAVE BIT(31) /* ` */
+/* out of space, but one for *@ would make sense, possibly others */
-EXTERN int ifs0 E_INIT(' '); /* for "$*" */
+/* compile-time initialised, ASCII only */
+extern const uint32_t tpl_ctypes[128];
+/* run-time, contains C_IFS as well, full 2⁸ octet range */
+EXTERN uint32_t ksh_ctypes[256];
+/* first octet of $IFS, for concatenating "$*" */
+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‥9A‥Za‥z alphanumerical */
+#define C_ALNUM (CiDIGIT | CiLOWER | CiOCTAL | CiUPPER)
+/* 0‥9A‥Z_a‥z alphanumerical plus underscore (“word character”) */
+#define C_ALNUX (CiDIGIT | CiLOWER | CiOCTAL | CiUNDER | CiUPPER)
+/* A‥Za‥z alphabetical (upper plus lower) */
+#define C_ALPHA (CiLOWER | CiUPPER)
+/* A‥Z_a‥z alphabetical plus underscore (identifier lead) */
+#define C_ALPHX (CiLOWER | CiUNDER | CiUPPER)
+/* \x01‥\x7F 7-bit ASCII except NUL */
+#define C_ASCII (CiALIAS | CiANGLE | CiBRACK | CiCNTRL | CiCOLON | CiCR | CiCURLY | CiDIGIT | CiEQUAL | CiGRAVE | CiHASH | CiLOWER | CiMINUS | CiNL | CiOCTAL | CiPERCT | CiPLUS | CiQC | CiQCL | CiQCM | CiQCX | CiQUEST | CiSP | CiSPX | CiSS | CiTAB | CiUNDER | CiUPPER)
+/* \x09\x20 tab and space */
+#define C_BLANK (CiSP | CiTAB)
+/* \x09\x20"' separator for completion */
+#define C_CFS (CiQC | CiSP | CiTAB)
+/* \x00‥\x1F\x7F POSIX control characters */
+#define C_CNTRL (CiCNTRL | CiCR | CiNL | CiNUL | CiSPX | CiTAB)
+/* 0‥9 decimal digits */
+#define C_DIGIT (CiDIGIT | CiOCTAL)
+/* &();`| editor x_locate_word() command */
+#define C_EDCMD (CiGRAVE | CiQCL)
+/* \x09\x0A\x20"&'():;<=>`| editor non-word characters */
+#define C_EDNWC (CiANGLE | CiCOLON | CiEQUAL | CiGRAVE | CiNL | CiQC | CiQCL | CiSP | CiTAB)
+/* "#$&'()*:;<=>?[\\`{|} editor quotes for tab completion */
+#define C_EDQ (CiANGLE | CiCOLON | CiCURLY | CiEQUAL | CiGRAVE | CiHASH | CiQC | CiQCL | CiQCX | CiQUEST | CiSS)
+/* !‥~ POSIX graphical (alphanumerical plus punctuation) */
+#define C_GRAPH (C_PUNCT | CiDIGIT | CiLOWER | CiOCTAL | CiUPPER)
+/* A‥Fa‥f hex letter */
+#define C_HEXLT CiHEXLT
+/* \x00 + $IFS IFS whitespace, IFS non-whitespace, NUL */
+#define C_IFS (CiIFS | CiNUL)
+/* \x09\x0A\x20 IFS whitespace */
+#define C_IFSWS (CiNL | CiSP | CiTAB)
+/* \x09\x0A\x20&();<>| (for the lexer) */
+#define C_LEX1 (CiANGLE | CiNL | CiQCL | CiSP | CiTAB)
+/* a‥z lowercase letters */
+#define C_LOWER CiLOWER
+/* not alnux or dollar separator for motion */
+#define C_MFS (CiALIAS | CiANGLE | CiBRACK | CiCNTRL | CiCOLON | CiCR | CiCURLY | CiEQUAL | CiGRAVE | CiHASH | CiMINUS | CiNL | CiNUL | CiPERCT | CiPLUS | CiQC | CiQCL | CiQCM | CiQCX | CiQUEST | CiSP | CiSPX | CiTAB)
+/* 0‥7 octal digit */
+#define C_OCTAL CiOCTAL
+/* !*+?@ pattern magical operator, except space */
+#define C_PATMO (CiPLUS | CiQUEST | CiVAR1)
+/* \x20‥~ POSIX printable characters (graph plus space) */
+#define C_PRINT (C_GRAPH | CiSP)
+/* !"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ POSIX punctuation */
+#define C_PUNCT (CiALIAS | CiANGLE | CiBRACK | CiCOLON | CiCURLY | CiEQUAL | CiGRAVE | CiHASH | CiMINUS | CiPERCT | CiPLUS | CiQC | CiQCL | CiQCM | CiQCX | CiQUEST | CiSS | CiUNDER)
+/* \x09\x0A"#$&'()*;<=>?[\\]`| characters requiring quoting, minus space */
+#define C_QUOTE (CiANGLE | CiBRACK | CiEQUAL | CiGRAVE | CiHASH | CiNL | CiQC | CiQCL | CiQCX | CiQUEST | CiSS | CiTAB)
+/* 0‥9A‥Fa‥f hexadecimal digit */
+#define C_SEDEC (CiDIGIT | CiHEXLT | CiOCTAL)
+/* \x09‥\x0D\x20 POSIX space class */
+#define C_SPACE (CiCR | CiNL | CiSP | CiSPX | CiTAB)
+/* +-=? substitution operations with word */
+#define C_SUB1 (CiEQUAL | CiMINUS | CiPLUS | CiQUEST)
+/* #% substitution operations with pattern */
+#define C_SUB2 (CiHASH | CiPERCT)
+/* A‥Z uppercase letters */
+#define C_UPPER CiUPPER
+/* !#$*-?@ substitution parameters, other than positional */
+#define C_VAR1 (CiHASH | CiMINUS | CiQUEST | CiSS | CiVAR1)
+
+/* individual chars you might like */
+#define C_ANGLE CiANGLE /* <> angle brackets */
+#define C_COLON CiCOLON /* : colon */
+#define C_CR CiCR /* \x0D ASCII carriage return */
+#define C_DOLAR CiSS /* $ dollar sign */
+#define C_EQUAL CiEQUAL /* = equals sign */
+#define C_GRAVE CiGRAVE /* ` accent gravis */
+#define C_HASH CiHASH /* # hash sign */
+#define C_LF CiNL /* \x0A ASCII line feed */
+#define C_MINUS CiMINUS /* - hyphen-minus */
+#ifdef MKSH_WITH_TEXTMODE
+#define C_NL (CiNL | CiCR) /* CR or LF under OS/2 TEXTMODE */
+#else
+#define C_NL CiNL /* LF only like under Unix */
+#endif
+#define C_NUL CiNUL /* \x00 ASCII NUL */
+#define C_PLUS CiPLUS /* + plus sign */
+#define C_QC CiQC /* "' quote characters */
+#define C_QUEST CiQUEST /* ? question mark */
+#define C_SPC CiSP /* \x20 ASCII space */
+#define C_TAB CiTAB /* \x09 ASCII horizontal tabulator */
+#define C_UNDER CiUNDER /* _ underscore */
+
+/* identity transform of octet */
+#if defined(DEBUG) && defined(__GNUC__) && !defined(__ICC) && \
+ !defined(__INTEL_COMPILER) && !defined(__SUNPRO_C)
+extern unsigned int eek_ord;
+#define ORD(c) ((size_t)(c) > 0xFF ? eek_ord : \
+ ((unsigned int)(unsigned char)(c)))
+#define ord(c) __builtin_choose_expr( \
+ __builtin_types_compatible_p(__typeof__(c), char) || \
+ __builtin_types_compatible_p(__typeof__(c), unsigned char), \
+ ((unsigned int)(unsigned char)(c)), ({ \
+ size_t ord_c = (c); \
+ \
+ if (ord_c > (size_t)0xFFU) \
+ internal_errorf("%s:%d:ord(%zX)", \
+ __FILE__, __LINE__, ord_c); \
+ ((unsigned int)(unsigned char)(ord_c)); \
+}))
+#else
+#define ord(c) ((unsigned int)(unsigned char)(c))
+#define ORD(c) ord(c) /* may evaluate arguments twice */
+#endif
+#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
+EXTERN unsigned short ebcdic_map[256];
+EXTERN unsigned char ebcdic_rtt_toascii[256];
+EXTERN unsigned char ebcdic_rtt_fromascii[256];
+extern void ebcdic_init(void);
+/* one-way to-ascii-or-high conversion, for POSIX locale ordering */
+#define asciibetical(c) ((unsigned int)ebcdic_map[(unsigned char)(c)])
+/* two-way round-trip conversion, for general use */
+#define rtt2asc(c) ebcdic_rtt_toascii[(unsigned char)(c)]
+#define asc2rtt(c) ebcdic_rtt_fromascii[(unsigned char)(c)]
+/* case-independent char comparison */
+#define ksh_eq(c,u,l) (ord(c) == ord(u) || ord(c) == ord(l))
+#else
+#define asciibetical(c) ord(c)
+#define rtt2asc(c) ((unsigned char)(c))
+#define asc2rtt(c) ((unsigned char)(c))
+#define ksh_eq(c,u,l) ((ord(c) | 0x20) == ord(l))
+#endif
+/* control character foo */
+#ifdef MKSH_EBCDIC
+#define ksh_isctrl(c) (ord(c) < 0x40 || ord(c) == 0xFF)
+#else
+#define ksh_isctrl(c) ((ord(c) & 0x7F) < 0x20 || ord(c) == 0x7F)
+#endif
+/* new fast character classes */
+#define ctype(c,t) tobool(ksh_ctypes[ord(c)] & (t))
+#define cinttype(c,t) ((c) >= 0 && (c) <= 0xFF ? \
+ tobool(ksh_ctypes[(unsigned char)(c)] & (t)) : false)
+/* helper functions */
+#define ksh_isdash(s) tobool(ord((s)[0]) == '-' && ord((s)[1]) == '\0')
+/* invariant distance even in EBCDIC */
+#define ksh_tolower(c) (ctype(c, C_UPPER) ? (c) - 'A' + 'a' : (c))
+#define ksh_toupper(c) (ctype(c, C_LOWER) ? (c) - 'a' + 'A' : (c))
+/* strictly speaking rtt2asc() here, but this works even in EBCDIC */
+#define ksh_numdig(c) (ord(c) - ORD('0'))
+#define ksh_numuc(c) (rtt2asc(c) - rtt2asc('A'))
+#define ksh_numlc(c) (rtt2asc(c) - rtt2asc('a'))
+#define ksh_toctrl(c) asc2rtt(ord(c) == ORD('?') ? 0x7F : rtt2asc(c) & 0x9F)
+#define ksh_unctrl(c) asc2rtt(rtt2asc(c) ^ 0x40U)
+
/* Argument parsing for built-in commands and getopts command */
/* Values for Getopt.flags */
@@ -977,7 +1561,7 @@
/* This for co-processes */
/* something that won't (realisticly) wrap */
-typedef int32_t Coproc_id;
+typedef int Coproc_id;
struct coproc {
void *job; /* 0 or job of co-process using input pipe */
@@ -996,7 +1580,7 @@
/* name of called builtin function (used by error functions) */
EXTERN const char *builtin_argv0;
-/* is called builtin SPEC_BI? */
+/* is called builtin a POSIX special builtin? (error functions only) */
EXTERN bool builtin_spec;
/* current working directory */
@@ -1003,30 +1587,21 @@
EXTERN char *current_wd;
/* input line size */
-#define LINE (4096 - ALLOC_SIZE)
-/*
- * Minimum required space to work with on a line - if the prompt leaves
- * less space than this on a line, the prompt is truncated.
- */
-#define MIN_EDIT_SPACE 7
-/*
- * Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
- */
-#define MIN_COLS (2 + MIN_EDIT_SPACE + 3)
-#define MIN_LINS 3
-EXTERN mksh_ari_t x_cols E_INIT(80); /* tty columns */
-EXTERN mksh_ari_t x_lins E_INIT(24); /* tty lines */
+#ifdef MKSH_SMALL
+#define LINE (4096 - ALLOC_OVERHEAD)
+#else
+#define LINE (16384 - ALLOC_OVERHEAD)
+#endif
+/* columns and lines of the tty */
+EXTERN mksh_ari_t x_cols E_INIT(80);
+EXTERN mksh_ari_t x_lins E_INIT(24);
/* Determine the location of the system (common) profile */
#ifndef MKSH_DEFAULT_PROFILEDIR
-#if defined(ANDROID)
-#define MKSH_DEFAULT_PROFILEDIR "/system/etc"
-#else
-#define MKSH_DEFAULT_PROFILEDIR "/etc"
+#define MKSH_DEFAULT_PROFILEDIR MKSH_UNIXROOT "/etc"
#endif
-#endif
#define MKSH_SYSTEM_PROFILE MKSH_DEFAULT_PROFILEDIR "/profile"
#define MKSH_SUID_PROFILE MKSH_DEFAULT_PROFILEDIR "/suid_profile"
@@ -1045,10 +1620,10 @@
#define shf_fileno(shf) ((shf)->fd)
#define shf_setfileno(shf,nfd) ((shf)->fd = (nfd))
#define shf_getc_i(shf) ((shf)->rnleft > 0 ? \
- (shf)->rnleft--, *(shf)->rp++ : \
+ (shf)->rnleft--, (int)ord(*(shf)->rp++) : \
shf_getchar(shf))
#define shf_putc_i(c, shf) ((shf)->wnleft == 0 ? \
- shf_putchar((c), (shf)) : \
+ shf_putchar((uint8_t)(c), (shf)) : \
((shf)->wnleft--, *(shf)->wp++ = (c)))
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
#define shf_error(shf) ((shf)->flags & SHF_ERROR)
@@ -1137,7 +1712,9 @@
char name[4];
};
-EXTERN struct tbl vtemp;
+EXTERN struct tbl *vtemp;
+/* set by isglobal(), global() and local() */
+EXTERN bool last_lookup_was_array;
/* common flag bits */
#define ALLOC BIT(0) /* val.s has been allocated */
@@ -1171,6 +1748,11 @@
#define FDELETE BIT(10) /* function deleted while it was executing */
#define FKSH BIT(11) /* function defined with function x (vs x()) */
#define SPEC_BI BIT(12) /* a POSIX special builtin */
+#define LOWER_BI BIT(13) /* (with LOW_BI) override even w/o flags */
+#define LOW_BI BIT(14) /* external utility overrides built-in one */
+#define DECL_UTIL BIT(15) /* is declaration utility */
+#define DECL_FWDR BIT(16) /* is declaration utility forwarder */
+
/*
* Attributes that can be set by the user (used to decide if an unset
* param should be repoted by set/typeset). Does not include ARRAY or
@@ -1204,8 +1786,8 @@
#define FC_BI (FC_SPECBI | FC_NORMBI)
#define FC_PATH BIT(3) /* do path search */
#define FC_DEFPATH BIT(4) /* use default path in path search */
+#define FC_WHENCE BIT(5) /* for use by command and whence */
-
#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
@@ -1270,7 +1852,7 @@
EXTERN const char *def_path; /* path to use if PATH not set */
EXTERN char *tmpdir; /* TMPDIR value */
EXTERN const char *prompt;
-EXTERN int cur_prompt; /* PS1 or PS2 */
+EXTERN uint8_t cur_prompt; /* PS1 or PS2 */
EXTERN int current_lineno; /* LINENO value */
/*
@@ -1342,16 +1924,18 @@
#define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */
#define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */
#define VALSUB 15 /* ${|foo;} substitution (NUL terminated) */
+#define COMASUB 16 /* `…` substitution (COMSUB but expand aliases) */
+#define FUNASUB 17 /* function substitution but expand aliases */
/*
* IO redirection
*/
struct ioword {
- int unit; /* unit affected */
- int flag; /* action (below) */
- char *name; /* file name (unused if heredoc) */
- char *delim; /* delimiter for <<,<<- */
- char *heredoc; /* content of heredoc */
+ char *ioname; /* filename (unused if heredoc) */
+ char *delim; /* delimiter for <<, <<- */
+ char *heredoc; /* content of heredoc */
+ unsigned short ioflag; /* action (below) */
+ short unit; /* unit (fd) affected */
};
/* ioword.flag - type of redirection */
@@ -1403,17 +1987,21 @@
#define DOTCOMEXEC BIT(11) /* not an eval flag, used by sh -c hack */
#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 X_EXTRA 20 /* this many extra bytes in X string */
typedef struct XString {
- char *end, *beg; /* end, begin of string */
- size_t len; /* length */
- Area *areap; /* area to allocate/free from */
+ /* beginning of string */
+ char *beg;
+ /* length of allocated area, minus safety margin */
+ size_t len;
+ /* end of string */
+ char *end;
+ /* memory area used */
+ Area *areap;
} XString;
-typedef char *XStringP;
-
/* initialise expandable string */
#define XinitN(xs, length, area) do { \
(xs).len = (length); \
@@ -1445,7 +2033,7 @@
/* close, return string */
#define Xclose(xs, xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
-/* begin of string */
+/* beginning of string */
#define Xstring(xs, xp) ((xs).beg)
#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
@@ -1461,7 +2049,7 @@
*/
typedef struct {
- /* begin of allocated area */
+ /* beginning of allocated area */
void **beg;
/* currently used number of entries */
size_t len;
@@ -1489,6 +2077,15 @@
#define XPclose(x) aresize2((x).beg, XPsize(x), sizeof(void *), ATEMP)
#define XPfree(x) afree((x).beg, ATEMP)
+/* for print_columns */
+
+struct columnise_opts {
+ struct shf *shf;
+ char linesep;
+ bool do_last;
+ bool prefcol;
+};
+
/*
* Lexer internals
*/
@@ -1495,24 +2092,39 @@
typedef struct source Source;
struct source {
- const char *str; /* input pointer */
- const char *start; /* start of current buffer */
+ /* input buffer */
+ XString xs;
+ /* memory area, also checked in reclaim() */
+ Area *areap;
+ /* stacked source */
+ Source *next;
+ /* input pointer */
+ const char *str;
+ /* start of current buffer */
+ const char *start;
+ /* input file name */
+ const char *file;
+ /* extra data */
union {
- const char **strv; /* string [] */
- struct shf *shf; /* shell file */
- struct tbl *tblp; /* alias (SF_HASALIAS) */
- char *freeme; /* also for SREREAD */
+ /* string[] */
+ const char **strv;
+ /* shell file */
+ struct shf *shf;
+ /* alias (SF_HASALIAS) */
+ struct tbl *tblp;
+ /* (also for SREREAD) */
+ char *freeme;
} u;
- const char *file; /* input file name */
- int type; /* input type */
- int line; /* line number */
- int errline; /* line the error occurred on (0 if not set) */
- int flags; /* SF_* */
- Area *areap;
- Source *next; /* stacked source */
- XString xs; /* input buffer */
- char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and
- * alias (SALIAS) */
+ /* flags */
+ int flags;
+ /* input type */
+ int type;
+ /* line number */
+ int line;
+ /* line the error occurred on (0 if not set) */
+ int errline;
+ /* buffer for ungetsc() (SREREAD) and alias (SALIAS) */
+ char ugbuf[2];
};
/* Source.type values */
@@ -1579,22 +2191,86 @@
#define ALIAS BIT(2) /* recognise alias */
#define KEYWORD BIT(3) /* recognise keywords */
#define LETEXPR BIT(4) /* get expression inside (( )) */
-#define VARASN BIT(5) /* check for var=word */
-#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */
+#define CMDASN BIT(5) /* parse x[1 & 2] as one word, for typeset */
+#define HEREDOC BIT(6) /* parsing a here document body */
#define ESACONLY BIT(7) /* only accept esac keyword */
#define CMDWORD BIT(8) /* parsing simple command (alias related) */
#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
#define LQCHAR BIT(10) /* source string contains QCHAR */
-#define HEREDOC BIT(11) /* parsing a here document body */
#define HERES 10 /* max number of << in line */
-#undef CTRL
-#define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */
-#define UNCTRL(x) ((x) ^ 0x40) /* ASCII */
-#define ISCTRL(x) (((signed char)((uint8_t)(x) + 1)) < 33)
+#ifdef MKSH_EBCDIC
+#define CTRL_AT (0x00U)
+#define CTRL_A (0x01U)
+#define CTRL_B (0x02U)
+#define CTRL_C (0x03U)
+#define CTRL_D (0x37U)
+#define CTRL_E (0x2DU)
+#define CTRL_F (0x2EU)
+#define CTRL_G (0x2FU)
+#define CTRL_H (0x16U)
+#define CTRL_I (0x05U)
+#define CTRL_J (0x15U)
+#define CTRL_K (0x0BU)
+#define CTRL_L (0x0CU)
+#define CTRL_M (0x0DU)
+#define CTRL_N (0x0EU)
+#define CTRL_O (0x0FU)
+#define CTRL_P (0x10U)
+#define CTRL_Q (0x11U)
+#define CTRL_R (0x12U)
+#define CTRL_S (0x13U)
+#define CTRL_T (0x3CU)
+#define CTRL_U (0x3DU)
+#define CTRL_V (0x32U)
+#define CTRL_W (0x26U)
+#define CTRL_X (0x18U)
+#define CTRL_Y (0x19U)
+#define CTRL_Z (0x3FU)
+#define CTRL_BO (0x27U)
+#define CTRL_BK (0x1CU)
+#define CTRL_BC (0x1DU)
+#define CTRL_CA (0x1EU)
+#define CTRL_US (0x1FU)
+#define CTRL_QM (0x07U)
+#else
+#define CTRL_AT (0x00U)
+#define CTRL_A (0x01U)
+#define CTRL_B (0x02U)
+#define CTRL_C (0x03U)
+#define CTRL_D (0x04U)
+#define CTRL_E (0x05U)
+#define CTRL_F (0x06U)
+#define CTRL_G (0x07U)
+#define CTRL_H (0x08U)
+#define CTRL_I (0x09U)
+#define CTRL_J (0x0AU)
+#define CTRL_K (0x0BU)
+#define CTRL_L (0x0CU)
+#define CTRL_M (0x0DU)
+#define CTRL_N (0x0EU)
+#define CTRL_O (0x0FU)
+#define CTRL_P (0x10U)
+#define CTRL_Q (0x11U)
+#define CTRL_R (0x12U)
+#define CTRL_S (0x13U)
+#define CTRL_T (0x14U)
+#define CTRL_U (0x15U)
+#define CTRL_V (0x16U)
+#define CTRL_W (0x17U)
+#define CTRL_X (0x18U)
+#define CTRL_Y (0x19U)
+#define CTRL_Z (0x1AU)
+#define CTRL_BO (0x1BU)
+#define CTRL_BK (0x1CU)
+#define CTRL_BC (0x1DU)
+#define CTRL_CA (0x1EU)
+#define CTRL_US (0x1FU)
+#define CTRL_QM (0x7FU)
+#endif
-#define IDENT 64
+#define IDENT 64
EXTERN Source *source; /* yyparse/yylex source */
EXTERN YYSTYPE yylval; /* result from yylex */
@@ -1605,6 +2281,13 @@
EXTERN char **histptr; /* last history item */
EXTERN mksh_ari_t histsize; /* history size */
+/* flags to histsave */
+#define HIST_FLUSH 0
+#define HIST_QUEUE 1
+#define HIST_APPEND 2
+#define HIST_STORE 3
+#define HIST_NOTE 4
+
/* user and system time of last j_waitjed job */
EXTERN struct timeval j_usrtime, j_systime;
@@ -1643,6 +2326,7 @@
int x_read(char *);
#endif
void x_mkraw(int, mksh_ttyst *, bool);
+void x_initterm(const char *);
/* eval.c */
char *substitute(const char *, int);
char **eval(const char **, int);
@@ -1654,15 +2338,18 @@
char *do_tilde(char *);
/* exec.c */
int execute(struct op * volatile, volatile int, volatile int * volatile);
-int shcomexec(const char **);
+int c_builtin(const char **);
+struct tbl *get_builtin(const char *);
struct tbl *findfunc(const char *, uint32_t, bool);
int define(const char *, struct op *);
const char *builtin(const char *, int (*)(const char **));
struct tbl *findcom(const char *, int);
void flushcom(bool);
+int search_access(const char *, int);
const char *search_path(const char *, const char *, int, int *);
void pr_menu(const char * const *);
-void pr_list(char * const *);
+void pr_list(struct columnise_opts *, char * const *);
+int herein(struct ioword *, char **);
/* expr.c */
int evaluate(const char *, mksh_ari_t *, int, bool);
int v_evaluate(struct tbl *, const char *, volatile int, bool);
@@ -1671,11 +2358,15 @@
size_t utf_wctomb(char *, unsigned int);
int utf_widthadj(const char *, const char **);
size_t utf_mbswidth(const char *) MKSH_A_PURE;
-const char *utf_skipcols(const char *, int) MKSH_A_PURE;
+const char *utf_skipcols(const char *, int, int *);
size_t utf_ptradj(const char *) MKSH_A_PURE;
+#ifdef MIRBSD_BOOTFLOPPY
+#define utf_wcwidth(i) wcwidth((wchar_t)(i))
+#else
int utf_wcwidth(unsigned int) MKSH_A_PURE;
+#endif
int ksh_access(const char *, int);
-struct tbl *tempvar(void);
+struct tbl *tempvar(const char *);
/* funcs.c */
int c_hash(const char **);
int c_pwd(const char **);
@@ -1686,6 +2377,7 @@
int c_whence(const char **);
int c_command(const char **);
int c_typeset(const char **);
+bool valid_alias_name(const char *);
int c_alias(const char **);
int c_unalias(const char **);
int c_let(const char **);
@@ -1715,8 +2407,6 @@
int timex(struct op *, int, volatile int *);
void timex_hook(struct op *, char ** volatile *);
int c_exec(const char **);
-/* dummy function (just need pointer value), special case in comexec() */
-#define c_builtin shcomexec
int c_test(const char **);
#if HAVE_MKNOD
int c_mknod(const char **);
@@ -1731,7 +2421,7 @@
#if HAVE_PERSISTENT_HISTORY
void hist_finish(void);
#endif
-void histsave(int *, const char *, bool, bool);
+void histsave(int *, const char *, int, bool);
#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
bool histsync(void);
#endif
@@ -1748,7 +2438,7 @@
char **hist_get_newest(bool);
void inittraps(void);
void alarm_init(void);
-Trap *gettrap(const char *, bool);
+Trap *gettrap(const char *, bool, bool);
void trapsig(int);
void intrcheck(void);
int fatal_trap_check(void);
@@ -1758,8 +2448,8 @@
void cleartraps(void);
void restoresigs(void);
void settrap(Trap *, const char *);
-int block_pipe(void);
-void restore_pipe(int);
+bool block_pipe(void);
+void restore_pipe(void);
int setsig(Trap *, sig_t, int);
void setexecsig(Trap *, int);
#if HAVE_FLOCK || HAVE_LOCK_FCNTL
@@ -1799,7 +2489,7 @@
/* main.c */
int include(const char *, int, const char **, bool);
int command(const char *, int);
-int shell(Source * volatile, volatile bool);
+int shell(Source * volatile, volatile int);
/* argument MUST NOT be 0 */
void unwind(int) MKSH_A_NORETURN;
void newenv(int);
@@ -1831,6 +2521,7 @@
MKSH_A_FORMAT(__printf__, 1, 2);
int can_seek(int);
void initio(void);
+void recheck_ctype(void);
int ksh_dup2(int, int, bool);
short savefd(int);
void restfd(int, int);
@@ -1858,8 +2549,6 @@
MKSH_A_FORMAT(__printf__, 1, 2);
#endif
/* misc.c */
-void setctypes(const char *, int);
-void initctypes(void);
size_t option(const char *) MKSH_A_PURE;
char *getoptions(void);
void change_flag(enum sh_flag, int, bool);
@@ -1867,16 +2556,18 @@
int parse_args(const char **, int, bool *);
int getn(const char *, int *);
int gmatchx(const char *, const char *, bool);
-int has_globbing(const char *, const char *) MKSH_A_PURE;
-int xstrcmp(const void *, const void *) MKSH_A_PURE;
+bool has_globbing(const char *) MKSH_A_PURE;
+int ascstrcmp(const void *, const void *) MKSH_A_PURE;
+int ascpstrcmp(const void *, const void *) MKSH_A_PURE;
void ksh_getopt_reset(Getopt *, int);
int ksh_getopt(const char **, Getopt *, const char *);
void print_value_quoted(struct shf *, const char *);
char *quote_value(const char *);
-void print_columns(struct shf *, unsigned int,
- char *(*)(char *, size_t, unsigned int, const void *),
- const void *, size_t, size_t, bool);
-void strip_nuls(char *, int);
+void print_columns(struct columnise_opts *, unsigned int,
+ void (*)(char *, size_t, unsigned int, const void *),
+ const void *, size_t, size_t);
+void strip_nuls(char *, size_t)
+ MKSH_A_BOUNDED(__string__, 1, 2);
ssize_t blocking_read(int, char *, size_t)
MKSH_A_BOUNDED(__buffer__, 2, 3);
int reset_nonblock(int);
@@ -1890,6 +2581,14 @@
char *strndup_i(const char *, size_t, Area *);
#endif
int unbksl(bool, int (*)(void), void (*)(int));
+#ifdef __OS2__
+/* os2.c */
+void os2_init(int *, const char ***);
+void setextlibpath(const char *, const char *);
+int access_ex(int (*)(const char *, int), const char *, int);
+int stat_ex(const char *, struct stat *);
+const char *real_exec_name(const char *);
+#endif
/* shf.c */
struct shf *shf_open(const char *, int, int, int);
struct shf *shf_fdopen(int, int, struct shf *);
@@ -1922,9 +2621,10 @@
MKSH_A_FORMAT(__printf__, 1, 2);
ssize_t shf_vfprintf(struct shf *, const char *, va_list)
MKSH_A_FORMAT(__printf__, 2, 0);
+void set_ifs(const char *);
/* syn.c */
void initkeywords(void);
-struct op *compile(Source *, bool);
+struct op *compile(Source *, bool, bool);
bool parse_usec(const char *, struct timeval *);
char *yyrecursive(int);
void yyrecursive_pop(bool);
@@ -1935,8 +2635,6 @@
char *wdcopy(const char *, Area *);
const char *wdscan(const char *, int);
#define WDS_TPUTS BIT(0) /* tputS (dumpwdvar) mode */
-#define WDS_KEEPQ BIT(1) /* keep quote characters */
-#define WDS_MAGIC BIT(2) /* make MAGIC */
char *wdstrip(const char *, int);
void tfree(struct op *, Area *);
void dumpchar(struct shf *, int);
@@ -1952,6 +2650,7 @@
void initvar(void);
struct block *varsearch(struct block *, struct tbl **, const char *, uint32_t);
struct tbl *global(const char *);
+struct tbl *isglobal(const char *, bool);
struct tbl *local(const char *, bool);
char *str_val(struct tbl *);
int setstr(struct tbl *, const char *, int);
@@ -1975,12 +2674,13 @@
mksh_ari_t rndget(void);
void rndset(unsigned long);
void rndpush(const void *);
+void record_match(const char *);
enum Test_op {
/* non-operator */
TO_NONOP = 0,
/* unary operators */
- TO_STNZE, TO_STZER, TO_OPTION,
+ TO_STNZE, TO_STZER, TO_ISSET, TO_OPTION,
TO_FILAXST,
TO_FILEXST,
TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
@@ -2037,6 +2737,55 @@
extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */
+#ifdef __OS2__
+#define binopen2(path,flags) __extension__({ \
+ int binopen2_fd = open((path), (flags) | O_BINARY); \
+ if (binopen2_fd >= 0) \
+ setmode(binopen2_fd, O_BINARY); \
+ (binopen2_fd); \
+})
+#define binopen3(path,flags,mode) __extension__({ \
+ int binopen3_fd = open((path), (flags) | O_BINARY, (mode)); \
+ if (binopen3_fd >= 0) \
+ setmode(binopen3_fd, O_BINARY); \
+ (binopen3_fd); \
+})
+#else
+#define binopen2(path,flags) open((path), (flags) | O_BINARY)
+#define binopen3(path,flags,mode) open((path), (flags) | O_BINARY, (mode))
+#endif
+
+#ifdef MKSH_DOSPATH
+#define mksh_drvltr(s) __extension__({ \
+ const char *mksh_drvltr_s = (s); \
+ (ctype(mksh_drvltr_s[0], C_ALPHA) && mksh_drvltr_s[1] == ':'); \
+})
+#define mksh_abspath(s) __extension__({ \
+ const char *mksh_abspath_s = (s); \
+ (mksh_cdirsep(mksh_abspath_s[0]) || \
+ (mksh_drvltr(mksh_abspath_s) && \
+ mksh_cdirsep(mksh_abspath_s[2]))); \
+})
+#define mksh_cdirsep(c) __extension__({ \
+ char mksh_cdirsep_c = (c); \
+ (mksh_cdirsep_c == '/' || mksh_cdirsep_c == '\\'); \
+})
+#define mksh_sdirsep(s) strpbrk((s), "/\\")
+#define mksh_vdirsep(s) __extension__({ \
+ const char *mksh_vdirsep_s = (s); \
+ (((mksh_drvltr(mksh_vdirsep_s) && \
+ !mksh_cdirsep(mksh_vdirsep_s[2])) ? (!0) : \
+ (mksh_sdirsep(mksh_vdirsep_s) != NULL)) && \
+ (strcmp(mksh_vdirsep_s, T_builtin) != 0)); \
+})
+int getdrvwd(char **, unsigned int);
+#else
+#define mksh_abspath(s) (ord((s)[0]) == ORD('/'))
+#define mksh_cdirsep(c) (ord(c) == ORD('/'))
+#define mksh_sdirsep(s) strchr((s), '/')
+#define mksh_vdirsep(s) vstrchr((s), '/')
+#endif
+
/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
Modified: trunk/contrib/mksh/sh_flags.gen
===================================================================
--- trunk/contrib/mksh/sh_flags.gen 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/sh_flags.gen 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,12 +1,32 @@
+/* +++ GENERATED FILE +++ DO NOT EDIT +++ */
+/*-
+ * Copyright (c) 2013, 2014, 2015, 2017
+ * mirabilos <m at 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.
+ */
+
#ifndef SHFLAGS_OPTCS
#if defined(SHFLAGS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.2 2014/06/09 12:28:19 tg Exp $");
-#define FN(sname,cname,flags,ochar) static const struct { /* character flag (if any) */ char c; /* OF_* */ unsigned char optflags; /* long name of option */ char name[sizeof(sname)]; } shoptione_ ## cname = { ochar, flags, sname };
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $");
#elif defined(SHFLAGS_ENUMS)
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
#elif defined(SHFLAGS_ITEMS)
-#define FN(sname,cname,flags,ochar) ((const char *)(&shoptione_ ## cname)) + 2,
+#define FN(sname,cname,flags,ochar) ((const char *)(&shoptione_ ## cname)) + 2,
#endif
#ifndef F0
#define F0 FN
@@ -16,11 +36,11 @@
FN("bgnice", FBGNICE, OF_ANY, 0)
#endif
FN("braceexpand", FBRACEEXPAND, OF_ANY, 0)
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("emacs", FEMACS, OF_ANY, 0)
#endif
FN("errexit", FERREXIT, OF_ANY, 'e')
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("gmacs", FGMACS, OF_ANY, 0)
#endif
FN("ignoreeof", FIGNOREEOF, OF_ANY, 0)
@@ -59,16 +79,16 @@
FN("trackall", FTRACKALL, OF_ANY, 'h')
FN("utf8-mode", FUNICODE, OF_ANY, 'U')
FN("verbose", FVERBOSE, OF_ANY, 'v')
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("vi", FVI, OF_ANY, 0)
#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY, 0)
#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY, 0)
#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("viraw", FVIRAW, OF_ANY, 0)
#endif
FN("xtrace", FXTRACE, OF_ANY, 'x')
Modified: trunk/contrib/mksh/sh_flags.opt
===================================================================
--- trunk/contrib/mksh/sh_flags.opt 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/sh_flags.opt 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,11 +1,30 @@
+/*-
+ * Copyright (c) 2013, 2014, 2015, 2017
+ * mirabilos <m at 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.
+ */
+
@SHFLAGS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.2 2014/06/09 12:28:19 tg Exp $");
-#define FN(sname,cname,flags,ochar) static const struct { /* character flag (if any) */ char c; /* OF_* */ unsigned char optflags; /* long name of option */ char name[sizeof(sname)]; } shoptione_ ## cname = { ochar, flags, sname };
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $");
@SHFLAGS_ENUMS
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
@SHFLAGS_ITEMS
-#define FN(sname,cname,flags,ochar) ((const char *)(&shoptione_ ## cname)) + 2,
+#define FN(sname,cname,flags,ochar) ((const char *)(&shoptione_ ## cname)) + 2,
@@
/* special cases */
@@ -33,7 +52,7 @@
FN("braceexpand", FBRACEEXPAND, OF_ANY
/* ./. Emacs command line editing mode */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("emacs", FEMACS, OF_ANY
/* -e quit on error */
@@ -41,7 +60,7 @@
FN("errexit", FERREXIT, OF_ANY
/* ./. Emacs command line editing mode, gmacs variant */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("gmacs", FGMACS, OF_ANY
/* ./. reading EOF does not exit */
@@ -141,19 +160,19 @@
FN("verbose", FVERBOSE, OF_ANY
/* ./. Vi command line editing mode */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("vi", FVI, OF_ANY
/* ./. enable ESC as file name completion character (non-standard) */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY
/* ./. enable Tab as file name completion character (non-standard) */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY
/* ./. always read in raw mode (no effect) */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("viraw", FVIRAW, OF_ANY
/* -x execution trace (display commands as they are run) */
Modified: trunk/contrib/mksh/shf.c
===================================================================
--- trunk/contrib/mksh/shf.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/shf.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -2,8 +2,10 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- * 2012, 2013, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2012, 2013, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
+ * Copyright (c) 2015
+ * Daniel Richard G. <skunk at iSKUNK.ORG>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -25,7 +27,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.62.2.2 2015/03/01 15:43:07 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.97 2018/01/14 01:28:16 tg Exp $");
/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
@@ -62,7 +64,7 @@
shf->flags = SHF_ALLOCS;
/* Rest filled in by reopen. */
- fd = open(name, oflags | O_BINARY, mode);
+ fd = binopen3(name, oflags, mode);
if (fd < 0) {
eno = errno;
afree(shf, shf->areap);
@@ -119,7 +121,7 @@
}
if (!(sflags & (SHF_RD | SHF_WR)))
- internal_errorf("%s: %s", where, "missing read/write");
+ internal_errorf(Tf_sD_s, where, "missing read/write");
}
/* Set up the shf structure for a file descriptor. Doesn't fail. */
@@ -167,7 +169,7 @@
shf_open_hlp(fd, &sflags, "shf_reopen");
if (!shf || !shf->buf || shf->bsize < bsize)
- internal_errorf("%s: %s", "shf_reopen", "bad shf/buf/bsize");
+ internal_errorf(Tf_sD_s, "shf_reopen", Tbad_bsize);
/* assumes shf->buf and shf->bsize already set up */
shf->fd = fd;
@@ -197,7 +199,8 @@
{
/* can't have a read+write string */
if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR)))
- internal_errorf("%s: flags 0x%X", "shf_sopen", sflags);
+ internal_errorf(Tf_flags, "shf_sopen",
+ (unsigned int)sflags);
if (!shf) {
shf = alloc(sizeof(struct shf), ATEMP);
@@ -288,29 +291,31 @@
int
shf_flush(struct shf *shf)
{
+ int rv = 0;
+
if (shf->flags & SHF_STRING)
- return ((shf->flags & SHF_WR) ? -1 : 0);
-
- if (shf->fd < 0)
- internal_errorf("%s: %s", "shf_flush", "no fd");
-
- if (shf->flags & SHF_ERROR) {
+ rv = (shf->flags & SHF_WR) ? -1 : 0;
+ else if (shf->fd < 0)
+ internal_errorf(Tf_sD_s, "shf_flush", "no fd");
+ else if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
- return (-1);
- }
-
- if (shf->flags & SHF_READING) {
+ rv = -1;
+ } else if (shf->flags & SHF_READING) {
shf->flags &= ~(SHF_EOF | SHF_READING);
if (shf->rnleft > 0) {
- lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR);
+ if (lseek(shf->fd, (off_t)-shf->rnleft,
+ SEEK_CUR) == -1) {
+ shf->flags |= SHF_ERROR;
+ shf->errnosv = errno;
+ rv = -1;
+ }
shf->rnleft = 0;
shf->rp = shf->buf;
}
- return (0);
} else if (shf->flags & SHF_WRITING)
- return (shf_emptybuf(shf, 0));
+ rv = shf_emptybuf(shf, 0);
- return (0);
+ return (rv);
}
/*
@@ -323,7 +328,7 @@
int ret = 0;
if (!(shf->flags & SHF_STRING) && shf->fd < 0)
- internal_errorf("%s: %s", "shf_emptybuf", "no fd");
+ internal_errorf(Tf_sD_s, "shf_emptybuf", "no fd");
if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
@@ -409,7 +414,7 @@
return (0);
if (shf->fd < 0)
- internal_errorf("%s: %s", "shf_fillbuf", "no fd");
+ internal_errorf(Tf_sD_s, "shf_fillbuf", "no fd");
if (shf->flags & (SHF_EOF | SHF_ERROR)) {
if (shf->flags & SHF_ERROR)
@@ -452,10 +457,11 @@
ssize_t ncopy, orig_bsize = bsize;
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_read", shf->flags);
+ internal_errorf(Tf_flags, Tshf_read,
+ (unsigned int)shf->flags);
if (bsize <= 0)
- internal_errorf("%s: %s %zd", "shf_write", "bsize", bsize);
+ internal_errorf(Tf_szs, Tshf_read, bsize, Tbsize);
while (bsize > 0) {
if (shf->rnleft == 0 &&
@@ -489,7 +495,8 @@
char *orig_buf = buf;
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_getse", shf->flags);
+ internal_errorf(Tf_flags, "shf_getse",
+ (unsigned int)shf->flags);
if (bsize <= 0)
return (NULL);
@@ -515,7 +522,23 @@
shf->rnleft -= ncopy;
buf += ncopy;
bsize -= ncopy;
+#ifdef MKSH_WITH_TEXTMODE
+ if (end && buf > orig_buf + 1 && buf[-2] == '\r') {
+ buf--;
+ bsize++;
+ buf[-1] = '\n';
+ }
+#endif
} while (!end && bsize);
+#ifdef MKSH_WITH_TEXTMODE
+ if (!bsize && buf[-1] == '\r') {
+ int c = shf_getc(shf);
+ if (c == '\n')
+ buf[-1] = '\n';
+ else if (c != -1)
+ shf_ungetc(c, shf);
+ }
+#endif
*buf = '\0';
return (buf);
}
@@ -525,12 +548,13 @@
shf_getchar(struct shf *shf)
{
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags);
+ internal_errorf(Tf_flags, "shf_getchar",
+ (unsigned int)shf->flags);
if (shf->rnleft == 0 && (shf_fillbuf(shf) == -1 || shf->rnleft == 0))
return (-1);
--shf->rnleft;
- return (*shf->rp++);
+ return (ord(*shf->rp++));
}
/*
@@ -541,7 +565,8 @@
shf_ungetc(int c, struct shf *shf)
{
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags);
+ internal_errorf(Tf_flags, "shf_ungetc",
+ (unsigned int)shf->flags);
if ((shf->flags & SHF_ERROR) || c == -1 ||
(shf->rp == shf->buf && shf->rnleft))
@@ -578,7 +603,8 @@
shf_putchar(int c, struct shf *shf)
{
if (!(shf->flags & SHF_WR))
- internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags);
+ internal_errorf(Tf_flags, "shf_putchar",
+ (unsigned int)shf->flags);
if (c == -1)
return (-1);
@@ -588,7 +614,7 @@
ssize_t n;
if (shf->fd < 0)
- internal_errorf("%s: %s", "shf_putchar", "no fd");
+ internal_errorf(Tf_sD_s, "shf_putchar", "no fd");
if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
return (-1);
@@ -633,10 +659,11 @@
ssize_t n, ncopy, orig_nbytes = nbytes;
if (!(shf->flags & SHF_WR))
- internal_errorf("%s: flags 0x%X", "shf_write", shf->flags);
+ internal_errorf(Tf_flags, Tshf_write,
+ (unsigned int)shf->flags);
if (nbytes < 0)
- internal_errorf("%s: %s %zd", "shf_write", "nbytes", nbytes);
+ internal_errorf(Tf_szs, Tshf_write, nbytes, Tbytes);
/* Don't buffer if buffer is empty and we're writting a large amount. */
if ((ncopy = shf->wnleft) &&
@@ -764,10 +791,10 @@
const char *s;
char c, *cp;
int tmp = 0, flags;
- ssize_t field, precision, len;
+ size_t field, precision, len;
unsigned long lnum;
/* %#o produces the longest output */
- char numbuf[(8 * sizeof(long) + 2) / 3 + 1];
+ char numbuf[(8 * sizeof(long) + 2) / 3 + 1 + /* NUL */ 1];
/* this stuff for dealing with the buffer */
ssize_t nwritten = 0;
@@ -791,7 +818,7 @@
*/
flags = 0;
field = precision = 0;
- for ( ; (c = *fmt++) ; ) {
+ while ((c = *fmt++)) {
switch (c) {
case '#':
flags |= FL_HASH;
@@ -821,12 +848,17 @@
case '*':
tmp = VA(int);
- if (flags & FL_DOT)
- precision = tmp;
- else if ((field = tmp) < 0) {
- field = -field;
- flags |= FL_RIGHT;
- }
+ if (tmp < 0) {
+ if (flags & FL_DOT)
+ precision = 0;
+ else {
+ field = (unsigned int)-tmp;
+ flags |= FL_RIGHT;
+ }
+ } else if (flags & FL_DOT)
+ precision = (unsigned int)tmp;
+ else
+ field = (unsigned int)tmp;
continue;
case 'l':
@@ -844,35 +876,32 @@
flags |= FL_SIZET;
continue;
}
- if (ksh_isdigit(c)) {
+ if (ctype(c, C_DIGIT)) {
bool overflowed = false;
- tmp = c - '0';
- while (c = *fmt++, ksh_isdigit(c)) {
+ tmp = ksh_numdig(c);
+ while (ctype((c = *fmt++), C_DIGIT))
if (notok2mul(2147483647, tmp, 10))
overflowed = true;
- tmp = tmp * 10 + c - '0';
- }
+ else
+ tmp = tmp * 10 + ksh_numdig(c);
--fmt;
if (overflowed)
tmp = 0;
if (flags & FL_DOT)
- precision = tmp;
+ precision = (unsigned int)tmp;
else
- field = tmp;
+ field = (unsigned int)tmp;
continue;
}
break;
}
- if (precision < 0)
- precision = 0;
-
if (!c)
/* nasty format */
break;
- if (c >= 'A' && c <= 'Z') {
+ if (ctype(c, C_UPPER)) {
flags |= FL_UPPER;
c = ksh_tolower(c);
}
@@ -905,6 +934,7 @@
integral:
flags |= FL_NUMBER;
cp = numbuf + sizeof(numbuf);
+ *--cp = '\0';
switch (c) {
case 'd':
@@ -917,7 +947,7 @@
/* FALLTHROUGH */
case 'u':
do {
- *--cp = lnum % 10 + '0';
+ *--cp = digits_lc[lnum % 10];
lnum /= 10;
} while (lnum);
@@ -933,7 +963,7 @@
case 'o':
do {
- *--cp = (lnum & 0x7) + '0';
+ *--cp = digits_lc[lnum & 0x7];
lnum >>= 3;
} while (lnum);
@@ -945,7 +975,7 @@
const char *digits = (flags & FL_UPPER) ?
digits_uc : digits_lc;
do {
- *--cp = digits[lnum & 0xf];
+ *--cp = digits[lnum & 0xF];
lnum >>= 4;
} while (lnum);
@@ -953,9 +983,9 @@
*--cp = (flags & FL_UPPER) ? 'X' : 'x';
*--cp = '0';
}
+ }
}
- }
- len = numbuf + sizeof(numbuf) - (s = cp);
+ len = numbuf + sizeof(numbuf) - 1 - (s = cp);
if (flags & FL_DOT) {
if (precision > len) {
field = precision;
@@ -999,11 +1029,9 @@
if (field > precision) {
field -= precision;
if (!(flags & FL_RIGHT)) {
- field = -field;
/* skip past sign or 0x when padding with 0 */
if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
- if (*s == '+' || *s == '-' ||
- *s == ' ') {
+ if (ctype(*s, C_SPC | C_PLUS | C_MINUS)) {
shf_putc(*s, shf);
s++;
precision--;
@@ -1012,8 +1040,8 @@
shf_putc(*s, shf);
s++;
nwritten++;
- if (--precision > 0 &&
- (*s | 0x20) == 'x') {
+ if (--precision &&
+ ksh_eq(*s, 'X', 'x')) {
shf_putc(*s, shf);
s++;
precision--;
@@ -1023,30 +1051,23 @@
c = '0';
} else
c = flags & FL_ZERO ? '0' : ' ';
- if (field < 0) {
- nwritten += -field;
- for ( ; field < 0 ; field++)
- shf_putc(c, shf);
- }
+ nwritten += field;
+ while (field--)
+ shf_putc(c, shf);
+ field = 0;
} else
c = ' ';
} else
field = 0;
- if (precision > 0) {
- const char *q;
+ nwritten += precision;
+ precision = utf_skipcols(s, precision, &tmp) - s;
+ while (precision--)
+ shf_putc(*s++, shf);
- nwritten += precision;
- q = utf_skipcols(s, precision);
- do {
- shf_putc(*s, shf);
- } while (++s < q);
- }
- if (field > 0) {
- nwritten += field;
- for ( ; field > 0 ; --field)
- shf_putc(c, shf);
- }
+ nwritten += field;
+ while (field--)
+ shf_putc(c, shf);
}
return (shf_error(shf) ? -1 : nwritten);
@@ -1097,14 +1118,10 @@
switch (errnum) {
case 0:
return ("Undefined error: 0");
-#ifdef EPERM
case EPERM:
return ("Operation not permitted");
-#endif
-#ifdef ENOENT
case ENOENT:
return ("No such file or directory");
-#endif
#ifdef ESRCH
case ESRCH:
return ("No such process");
@@ -1113,22 +1130,20 @@
case E2BIG:
return ("Argument list too long");
#endif
-#ifdef ENOEXEC
case ENOEXEC:
return ("Exec format error");
-#endif
+ case EBADF:
+ return ("Bad file descriptor");
#ifdef ENOMEM
case ENOMEM:
return ("Cannot allocate memory");
#endif
-#ifdef EACCES
case EACCES:
return ("Permission denied");
-#endif
-#ifdef ENOTDIR
+ case EEXIST:
+ return ("File exists");
case ENOTDIR:
return ("Not a directory");
-#endif
#ifdef EINVAL
case EINVAL:
return ("Invalid argument");
@@ -1144,3 +1159,163 @@
}
}
#endif
+
+/* fast character classes */
+const uint32_t tpl_ctypes[128] = {
+ /* 0x00 */
+ CiNUL, CiCNTRL, CiCNTRL, CiCNTRL,
+ CiCNTRL, CiCNTRL, CiCNTRL, CiCNTRL,
+ CiCNTRL, CiTAB, CiNL, CiSPX,
+ CiSPX, CiCR, CiCNTRL, CiCNTRL,
+ /* 0x10 */
+ CiCNTRL, CiCNTRL, CiCNTRL, CiCNTRL,
+ CiCNTRL, CiCNTRL, CiCNTRL, CiCNTRL,
+ CiCNTRL, CiCNTRL, CiCNTRL, CiCNTRL,
+ CiCNTRL, CiCNTRL, CiCNTRL, CiCNTRL,
+ /* 0x20 */
+ CiSP, CiALIAS | CiVAR1, CiQC, CiHASH,
+ CiSS, CiPERCT, CiQCL, CiQC,
+ CiQCL, CiQCL, CiQCX | CiVAR1, CiPLUS,
+ CiALIAS, CiMINUS, CiALIAS, CiQCM,
+ /* 0x30 */
+ CiOCTAL, CiOCTAL, CiOCTAL, CiOCTAL,
+ CiOCTAL, CiOCTAL, CiOCTAL, CiOCTAL,
+ CiDIGIT, CiDIGIT, CiCOLON, CiQCL,
+ CiANGLE, CiEQUAL, CiANGLE, CiQUEST,
+ /* 0x40 */
+ CiALIAS | CiVAR1, CiUPPER | CiHEXLT,
+ CiUPPER | CiHEXLT, CiUPPER | CiHEXLT,
+ CiUPPER | CiHEXLT, CiUPPER | CiHEXLT,
+ CiUPPER | CiHEXLT, CiUPPER,
+ CiUPPER, CiUPPER, CiUPPER, CiUPPER,
+ CiUPPER, CiUPPER, CiUPPER, CiUPPER,
+ /* 0x50 */
+ CiUPPER, CiUPPER, CiUPPER, CiUPPER,
+ CiUPPER, CiUPPER, CiUPPER, CiUPPER,
+ CiUPPER, CiUPPER, CiUPPER, CiQCX | CiBRACK,
+ CiQCX, CiBRACK, CiQCM, CiUNDER,
+ /* 0x60 */
+ CiGRAVE, CiLOWER | CiHEXLT,
+ CiLOWER | CiHEXLT, CiLOWER | CiHEXLT,
+ CiLOWER | CiHEXLT, CiLOWER | CiHEXLT,
+ CiLOWER | CiHEXLT, CiLOWER,
+ CiLOWER, CiLOWER, CiLOWER, CiLOWER,
+ CiLOWER, CiLOWER, CiLOWER, CiLOWER,
+ /* 0x70 */
+ CiLOWER, CiLOWER, CiLOWER, CiLOWER,
+ CiLOWER, CiLOWER, CiLOWER, CiLOWER,
+ CiLOWER, CiLOWER, CiLOWER, CiCURLY,
+ CiQCL, CiCURLY, CiQCM, CiCNTRL
+};
+
+void
+set_ifs(const char *s)
+{
+#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
+ int i = 256;
+
+ memset(ksh_ctypes, 0, sizeof(ksh_ctypes));
+ while (i--)
+ if (ebcdic_map[i] < 0x80U)
+ ksh_ctypes[i] = tpl_ctypes[ebcdic_map[i]];
+#else
+ memcpy(ksh_ctypes, tpl_ctypes, sizeof(tpl_ctypes));
+ memset((char *)ksh_ctypes + sizeof(tpl_ctypes), '\0',
+ sizeof(ksh_ctypes) - sizeof(tpl_ctypes));
+#endif
+ ifs0 = *s;
+ while (*s)
+ ksh_ctypes[ord(*s++)] |= CiIFS;
+}
+
+#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
+#include <locale.h>
+
+/*
+ * Many headaches with EBCDIC:
+ * 1. There are numerous EBCDIC variants, and it is not feasible for us
+ * to support them all. But we can support the EBCDIC code pages that
+ * contain all (most?) of the characters in ASCII, and these
+ * usually tend to agree on the code points assigned to the ASCII
+ * subset. If you need a representative example, look at EBCDIC 1047,
+ * which is first among equals in the IBM MVS development
+ * environment: https://en.wikipedia.org/wiki/EBCDIC_1047
+ * Unfortunately, the square brackets are not consistently mapped,
+ * and for certain reasons, we need an unambiguous bijective
+ * mapping between EBCDIC and "extended ASCII".
+ * 2. Character ranges that are contiguous in ASCII, like the letters
+ * in [A-Z], are broken up into segments (i.e. [A-IJ-RS-Z]), so we
+ * can't implement e.g. islower() as { return c >= 'a' && c <= 'z'; }
+ * because it will also return true for a handful of extraneous
+ * characters (like the plus-minus sign at 0x8F in EBCDIC 1047, a
+ * little after 'i'). But at least '_' is not one of these.
+ * 3. The normal [0-9A-Za-z] characters are at codepoints beyond 0x80.
+ * Not only do they require all 8 bits instead of 7, if chars are
+ * signed, they will have negative integer values! Something like
+ * (c - 'A') could actually become (c + 63)! Use the ord() macro to
+ * ensure you're getting a value in [0, 255] (ORD for constants).
+ * 4. '\n' is actually NL (0x15, U+0085) instead of LF (0x25, U+000A).
+ * EBCDIC has a proper newline character instead of "emulating" one
+ * with line feeds, although this is mapped to LF for our purposes.
+ * 5. Note that it is possible to compile programs in ASCII mode on IBM
+ * mainframe systems, using the -qascii option to the XL C compiler.
+ * We can determine the build mode by looking at __CHARSET_LIB:
+ * 0 == EBCDIC, 1 == ASCII
+ */
+
+void
+ebcdic_init(void)
+{
+ int i = 256;
+ unsigned char t;
+ bool mapcache[256];
+
+ while (i--)
+ ebcdic_rtt_toascii[i] = i;
+ memset(ebcdic_rtt_fromascii, 0xFF, sizeof(ebcdic_rtt_fromascii));
+ setlocale(LC_ALL, "");
+#ifdef MKSH_EBCDIC
+ if (__etoa_l(ebcdic_rtt_toascii, 256) != 256) {
+ write(2, "mksh: could not map EBCDIC to ASCII\n", 36);
+ exit(255);
+ }
+#endif
+
+ memset(mapcache, 0, sizeof(mapcache));
+ i = 256;
+ while (i--) {
+ t = ebcdic_rtt_toascii[i];
+ /* ensure unique round-trip capable mapping */
+ if (mapcache[t]) {
+ write(2, "mksh: duplicate EBCDIC to ASCII mapping\n", 40);
+ exit(255);
+ }
+ /*
+ * since there are 256 input octets, this also ensures
+ * the other mapping direction is completely filled
+ */
+ mapcache[t] = true;
+ /* fill the complete round-trip map */
+ ebcdic_rtt_fromascii[t] = i;
+ /*
+ * Only use the converted value if it's in the range
+ * [0x00; 0x7F], which I checked; the "extended ASCII"
+ * characters can be any encoding, not just Latin1,
+ * and the C1 control characters other than NEL are
+ * hopeless, but we map EBCDIC NEL to ASCII LF so we
+ * cannot even use C1 NEL.
+ * If ever we map to Unicode, bump the table width to
+ * an unsigned int, and or the raw unconverted EBCDIC
+ * values with 0x01000000 instead.
+ */
+ if (t < 0x80U)
+ ebcdic_map[i] = (unsigned short)ord(t);
+ else
+ ebcdic_map[i] = (unsigned short)(0x100U | ord(i));
+ }
+ if (ebcdic_rtt_toascii[0] || ebcdic_rtt_fromascii[0] || ebcdic_map[0]) {
+ write(2, "mksh: NUL not at position 0\n", 28);
+ exit(255);
+ }
+}
+#endif
Modified: trunk/contrib/mksh/strlcpy.c
===================================================================
--- trunk/contrib/mksh/strlcpy.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/strlcpy.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2006, 2008, 2009, 2013
- * Thorsten Glaser <tg at mirbsd.org>
+ * mirabilos <m at mirbsd.org>
* Copyright (c) 1998 Todd C. Miller <Todd.Miller at courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -18,7 +18,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/strlcpy.c,v 1.8 2013/11/05 22:10:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/strlcpy.c,v 1.10 2015/11/29 17:05:02 tg Exp $");
/*
* Copy src to string dst of size siz. At most siz-1 characters
Modified: trunk/contrib/mksh/syn.c
===================================================================
--- trunk/contrib/mksh/syn.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/syn.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,10 @@
-/* $OpenBSD: syn.c,v 1.29 2013/06/03 18:40:05 jca Exp $ */
+/* $OpenBSD: syn.c,v 1.30 2015/09/01 13:12:31 tedu Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- * 2011, 2012, 2013, 2014
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017,
+ * 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,7 +24,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.94.2.1 2015/01/25 15:35:54 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.127 2018/01/14 00:22:30 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@@ -31,34 +32,33 @@
};
struct yyrecursive_state {
+ struct ioword *old_heres[HERES];
struct yyrecursive_state *next;
struct ioword **old_herep;
int old_symbol;
- int old_salias;
- int old_nesting_type;
+ unsigned int old_nesting_type;
bool old_reject;
};
-static void yyparse(void);
-static struct op *pipeline(int);
-static struct op *andor(void);
-static struct op *c_list(bool);
+static void yyparse(bool);
+static struct op *pipeline(int, int);
+static struct op *andor(int);
+static struct op *c_list(int, bool);
static struct ioword *synio(int);
-static struct op *nested(int, int, int);
-static struct op *get_command(int);
-static struct op *dogroup(void);
-static struct op *thenpart(void);
-static struct op *elsepart(void);
-static struct op *caselist(void);
-static struct op *casepart(int);
-static struct op *function_body(char *, bool);
-static char **wordlist(void);
+static struct op *nested(int, int, int, int);
+static struct op *get_command(int, int);
+static struct op *dogroup(int);
+static struct op *thenpart(int);
+static struct op *elsepart(int);
+static struct op *caselist(int);
+static struct op *casepart(int, int);
+static struct op *function_body(char *, int, bool);
+static char **wordlist(int);
static struct op *block(int, struct op *, struct op *);
static struct op *newtp(int);
static void syntaxerr(const char *) MKSH_A_NORETURN;
static void nesting_push(struct nesting_state *, int);
static void nesting_pop(struct nesting_state *);
-static int assign_command(const char *);
static int inalias(struct source *) MKSH_A_PURE;
static Test_op dbtestp_isa(Test_env *, Test_meta);
static const char *dbtestp_getopnd(Test_env *, Test_op, bool);
@@ -71,41 +71,43 @@
static bool reject; /* token(cf) gets symbol again */
static int symbol; /* yylex value */
-static int sALIAS = ALIAS; /* 0 in yyrecursive */
#define REJECT (reject = true)
#define ACCEPT (reject = false)
#define token(cf) ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
#define tpeek(cf) ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
-#define musthave(c,cf) do { if (token(cf) != (c)) syntaxerr(NULL); } while (/* CONSTCOND */ 0)
+#define musthave(c,cf) do { \
+ if ((unsigned int)token(cf) != (unsigned int)(c)) \
+ syntaxerr(NULL); \
+} while (/* CONSTCOND */ 0)
static const char Tcbrace[] = "}";
static const char Tesac[] = "esac";
static void
-yyparse(void)
+yyparse(bool doalias)
{
int c;
ACCEPT;
- outtree = c_list(source->type == SSTRING);
+ outtree = c_list(doalias ? ALIAS : 0, source->type == SSTRING);
c = tpeek(0);
if (c == 0 && !outtree)
outtree = newtp(TEOF);
- else if (c != '\n' && c != 0)
+ else if (!cinttype(c, C_LF | C_NUL))
syntaxerr(NULL);
}
static struct op *
-pipeline(int cf)
+pipeline(int cf, int sALIAS)
{
struct op *t, *p, *tl = NULL;
- t = get_command(cf);
+ t = get_command(cf, sALIAS);
if (t != NULL) {
while (token(0) == '|') {
- if ((p = get_command(CONTIN)) == NULL)
+ if ((p = get_command(CONTIN, sALIAS)) == NULL)
syntaxerr(NULL);
if (tl == NULL)
t = tl = block(TPIPE, t, p);
@@ -118,15 +120,15 @@
}
static struct op *
-andor(void)
+andor(int sALIAS)
{
struct op *t, *p;
int c;
- t = pipeline(0);
+ t = pipeline(0, sALIAS);
if (t != NULL) {
while ((c = token(0)) == LOGAND || c == LOGOR) {
- if ((p = pipeline(CONTIN)) == NULL)
+ if ((p = pipeline(CONTIN, sALIAS)) == NULL)
syntaxerr(NULL);
t = block(c == LOGAND? TAND: TOR, t, p);
}
@@ -136,7 +138,7 @@
}
static struct op *
-c_list(bool multi)
+c_list(int sALIAS, bool multi)
{
struct op *t = NULL, *p, *tl = NULL;
int c;
@@ -143,7 +145,7 @@
bool have_sep;
while (/* CONSTCOND */ 1) {
- p = andor();
+ p = andor(sALIAS);
/*
* Token has always been read/rejected at this point, so
* we don't worry about what flags to pass token()
@@ -173,6 +175,8 @@
return (t);
}
+static const char IONDELIM_delim[] = { CHAR, '<', CHAR, '<', EOS };
+
static struct ioword *
synio(int cf)
{
@@ -190,40 +194,39 @@
return (NULL);
ACCEPT;
iop = yylval.iop;
- if (iop->flag & IONDELIM)
- goto gotnulldelim;
- ishere = (iop->flag & IOTYPE) == IOHERE;
- musthave(LWORD, ishere ? HEREDELIM : 0);
+ ishere = (iop->ioflag & IOTYPE) == IOHERE;
+ if (iop->ioflag & IOHERESTR) {
+ musthave(LWORD, 0);
+ } else if (ishere && tpeek(HEREDELIM) == '\n') {
+ ACCEPT;
+ yylval.cp = wdcopy(IONDELIM_delim, ATEMP);
+ iop->ioflag |= IOEVAL | IONDELIM;
+ } else
+ musthave(LWORD, ishere ? HEREDELIM : 0);
if (ishere) {
iop->delim = yylval.cp;
- if (*ident != 0) {
+ if (*ident != 0 && !(iop->ioflag & IOHERESTR)) {
/* unquoted */
- gotnulldelim:
- iop->flag |= IOEVAL;
+ iop->ioflag |= IOEVAL;
}
if (herep > &heres[HERES - 1])
- yyerror("too many %ss\n", "<<");
+ yyerror(Tf_toomany, "<<");
*herep++ = iop;
} else
- iop->name = yylval.cp;
+ iop->ioname = yylval.cp;
- if (iop->flag & IOBASH) {
+ if (iop->ioflag & IOBASH) {
char *cp;
nextiop = alloc(sizeof(*iop), ATEMP);
- nextiop->name = cp = alloc(5, ATEMP);
-
- if (iop->unit > 9) {
- *cp++ = CHAR;
- *cp++ = '0' + (iop->unit / 10);
- }
+ nextiop->ioname = cp = alloc(3, ATEMP);
*cp++ = CHAR;
- *cp++ = '0' + (iop->unit % 10);
+ *cp++ = digits_lc[iop->unit % 10];
*cp = EOS;
- iop->flag &= ~IOBASH;
+ iop->ioflag &= ~IOBASH;
nextiop->unit = 2;
- nextiop->flag = IODUP;
+ nextiop->ioflag = IODUP;
nextiop->delim = NULL;
nextiop->heredoc = NULL;
}
@@ -231,20 +234,24 @@
}
static struct op *
-nested(int type, int smark, int emark)
+nested(int type, int smark, int emark, int sALIAS)
{
struct op *t;
struct nesting_state old_nesting;
nesting_push(&old_nesting, smark);
- t = c_list(true);
+ t = c_list(sALIAS, true);
musthave(emark, KEYWORD|sALIAS);
nesting_pop(&old_nesting);
return (block(type, t, NULL));
}
+static const char builtin_cmd[] = {
+ QCHAR, '\\', CHAR, 'b', CHAR, 'u', CHAR, 'i',
+ CHAR, 'l', CHAR, 't', CHAR, 'i', CHAR, 'n', EOS
+};
static const char let_cmd[] = {
- CHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS
+ CHAR, 'l', CHAR, 'e', CHAR, 't', EOS
};
static const char setA_cmd0[] = {
CHAR, 's', CHAR, 'e', CHAR, 't', EOS
@@ -257,13 +264,12 @@
};
static struct op *
-get_command(int cf)
+get_command(int cf, int sALIAS)
{
struct op *t;
int c, iopn = 0, syniocf, lno;
struct ioword *iop, **iops;
XPtrV args, vars;
- char *tcp;
struct nesting_state old_nesting;
/* NUFILE is small enough to leave this addition unchecked */
@@ -272,7 +278,7 @@
XPinit(vars, 16);
syniocf = KEYWORD|sALIAS;
- switch (c = token(cf|KEYWORD|sALIAS|VARASN)) {
+ switch (c = token(cf|KEYWORD|sALIAS|CMDASN)) {
default:
REJECT;
afree(iops, ATEMP);
@@ -287,15 +293,24 @@
syniocf &= ~(KEYWORD|sALIAS);
t = newtp(TCOM);
t->lineno = source->line;
+ goto get_command_start;
while (/* CONSTCOND */ 1) {
- cf = (t->u.evalflags ? ARRAYVAR : 0) |
- (XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD);
+ bool check_decl_utility;
+
+ if (XPsize(args) == 0) {
+ get_command_start:
+ 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("too many %ss\n",
- "redirection");
+ yyerror(Tf_toomany,
+ Tredirection);
iops[iopn++] = iop;
}
break;
@@ -302,14 +317,16 @@
case LWORD:
ACCEPT;
- /*
- * the iopn == 0 and XPsize(vars) == 0 are
- * dubious but AT&T ksh acts this way
- */
- if (iopn == 0 && XPsize(vars) == 0 &&
- XPsize(args) == 0 &&
- assign_command(ident))
- t->u.evalflags = DOVACHECK;
+ 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);
@@ -317,9 +334,11 @@
XPput(args, yylval.cp);
break;
- case '(' /*)*/:
+ case ORD('(' /*)*/):
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
is_wdvarassign(yylval.cp)) {
+ char *tcp;
+
/* wdarrassign: foo=(bar) */
ACCEPT;
@@ -329,6 +348,7 @@
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);
@@ -357,8 +377,9 @@
XPsize(vars) != 0)
syntaxerr(NULL);
ACCEPT;
- musthave(/*(*/')', 0);
- t = function_body(XPptrv(args)[0], false);
+ musthave(/*(*/ ')', 0);
+ t = function_body(XPptrv(args)[0],
+ sALIAS, false);
}
goto Leave;
@@ -369,18 +390,18 @@
Leave:
break;
- case '(': /*)*/ {
- int subshell_nesting_type_saved;
+ case ORD('(' /*)*/): {
+ unsigned int subshell_nesting_type_saved;
Subshell:
subshell_nesting_type_saved = subshell_nesting_type;
- subshell_nesting_type = ')';
- t = nested(TPAREN, '(', ')');
+ subshell_nesting_type = ORD(')');
+ t = nested(TPAREN, ORD('('), ORD(')'), sALIAS);
subshell_nesting_type = subshell_nesting_type_saved;
break;
}
- case '{': /*}*/
- t = nested(TBRACE, '{', '}');
+ case ORD('{' /*}*/):
+ t = nested(TBRACE, ORD('{'), ORD('}'), sALIAS);
break;
case MDPAREN:
@@ -390,7 +411,8 @@
switch (token(LETEXPR)) {
case LWORD:
break;
- case '(': /*)*/
+ case ORD('(' /*)*/):
+ c = ORD('(');
goto Subshell;
default:
syntaxerr(NULL);
@@ -397,6 +419,7 @@
}
t = newtp(TCOM);
t->lineno = lno;
+ XPput(args, wdcopy(builtin_cmd, ATEMP));
XPput(args, wdcopy(let_cmd, ATEMP));
XPput(args, yylval.cp);
break;
@@ -422,14 +445,14 @@
case FOR:
case SELECT:
t = newtp((c == FOR) ? TFOR : TSELECT);
- musthave(LWORD, ARRAYVAR);
+ musthave(LWORD, CMDASN);
if (!is_wdvarname(yylval.cp, true))
- yyerror("%s: %s\n", c == FOR ? "for" : Tselect,
- "bad identifier");
+ yyerror("%s: bad identifier",
+ c == FOR ? "for" : Tselect);
strdupx(t->str, ident, ATEMP);
nesting_push(&old_nesting, c);
- t->vars = wordlist();
- t->left = dogroup();
+ t->vars = wordlist(sALIAS);
+ t->left = dogroup(sALIAS);
nesting_pop(&old_nesting);
break;
@@ -437,8 +460,8 @@
case UNTIL:
nesting_push(&old_nesting, c);
t = newtp((c == WHILE) ? TWHILE : TUNTIL);
- t->left = c_list(true);
- t->right = dogroup();
+ t->left = c_list(sALIAS, true);
+ t->right = dogroup(sALIAS);
nesting_pop(&old_nesting);
break;
@@ -447,7 +470,7 @@
musthave(LWORD, 0);
t->str = yylval.cp;
nesting_push(&old_nesting, c);
- t->left = caselist();
+ t->left = caselist(sALIAS);
nesting_pop(&old_nesting);
break;
@@ -454,8 +477,8 @@
case IF:
nesting_push(&old_nesting, c);
t = newtp(TIF);
- t->left = c_list(true);
- t->right = thenpart();
+ t->left = c_list(sALIAS, true);
+ t->right = thenpart(sALIAS);
musthave(FI, KEYWORD|sALIAS);
nesting_pop(&old_nesting);
break;
@@ -462,7 +485,7 @@
case BANG:
syniocf &= ~(KEYWORD|sALIAS);
- t = pipeline(0);
+ t = pipeline(0, sALIAS);
if (t == NULL)
syntaxerr(NULL);
t = block(TBANG, NULL, t);
@@ -470,7 +493,7 @@
case TIME:
syniocf &= ~(KEYWORD|sALIAS);
- t = pipeline(0);
+ t = pipeline(0, sALIAS);
if (t && t->type == TCOM) {
t->str = alloc(2, ATEMP);
/* TF_* flags */
@@ -482,13 +505,13 @@
case FUNCTION:
musthave(LWORD, 0);
- t = function_body(yylval.cp, true);
+ t = function_body(yylval.cp, sALIAS, true);
break;
}
while ((iop = synio(syniocf)) != NULL) {
if (iopn >= NUFILE)
- yyerror("too many %ss\n", "redirection");
+ yyerror(Tf_toomany, Tredirection);
iops[iopn++] = iop;
}
@@ -511,11 +534,17 @@
XPfree(vars);
}
+ if (c == MDPAREN) {
+ t = block(TBRACE, t, NULL);
+ t->ioact = t->left->ioact;
+ t->left->ioact = NULL;
+ }
+
return (t);
}
static struct op *
-dogroup(void)
+dogroup(int sALIAS)
{
int c;
struct op *list;
@@ -529,44 +558,44 @@
*/
if (c == DO)
c = DONE;
- else if (c == '{')
- c = '}';
+ else if ((unsigned int)c == ORD('{'))
+ c = ORD('}');
else
syntaxerr(NULL);
- list = c_list(true);
+ list = c_list(sALIAS, true);
musthave(c, KEYWORD|sALIAS);
return (list);
}
static struct op *
-thenpart(void)
+thenpart(int sALIAS)
{
struct op *t;
musthave(THEN, KEYWORD|sALIAS);
t = newtp(0);
- t->left = c_list(true);
+ t->left = c_list(sALIAS, true);
if (t->left == NULL)
syntaxerr(NULL);
- t->right = elsepart();
+ t->right = elsepart(sALIAS);
return (t);
}
static struct op *
-elsepart(void)
+elsepart(int sALIAS)
{
struct op *t;
- switch (token(KEYWORD|sALIAS|VARASN)) {
+ switch (token(KEYWORD|sALIAS|CMDASN)) {
case ELSE:
- if ((t = c_list(true)) == NULL)
+ if ((t = c_list(sALIAS, true)) == NULL)
syntaxerr(NULL);
return (t);
case ELIF:
t = newtp(TELIF);
- t->left = c_list(true);
- t->right = thenpart();
+ t->left = c_list(sALIAS, true);
+ t->right = thenpart(sALIAS);
return (t);
default:
@@ -576,7 +605,7 @@
}
static struct op *
-caselist(void)
+caselist(int sALIAS)
{
struct op *t, *tl;
int c;
@@ -585,14 +614,14 @@
/* A {...} can be used instead of in...esac for case statements */
if (c == IN)
c = ESAC;
- else if (c == '{')
- c = '}';
+ else if ((unsigned int)c == ORD('{'))
+ c = ORD('}');
else
syntaxerr(NULL);
t = tl = NULL;
/* no ALIAS here */
while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) {
- struct op *tc = casepart(c);
+ struct op *tc = casepart(c, sALIAS);
if (tl == NULL)
t = tl = tc, tl->right = NULL;
else
@@ -603,7 +632,7 @@
}
static struct op *
-casepart(int endtok)
+casepart(int endtok, int sALIAS)
{
struct op *t;
XPtrV ptns;
@@ -611,17 +640,17 @@
XPinit(ptns, 16);
t = newtp(TPAT);
/* no ALIAS here */
- if (token(CONTIN | KEYWORD) != '(')
+ if ((unsigned int)token(CONTIN | KEYWORD) != ORD('('))
REJECT;
do {
switch (token(0)) {
case LWORD:
break;
- case '}':
+ case ORD('}'):
case ESAC:
if (symbol != endtok) {
- strdupx(yylval.cp,
- symbol == '}' ? Tcbrace : Tesac, ATEMP);
+ strdupx(yylval.cp, (unsigned int)symbol ==
+ ORD('}') ? Tcbrace : Tesac, ATEMP);
break;
}
/* FALLTHROUGH */
@@ -633,12 +662,12 @@
REJECT;
XPput(ptns, NULL);
t->vars = (char **)XPclose(ptns);
- musthave(')', 0);
+ musthave(ORD(')'), 0);
- t->left = c_list(true);
+ t->left = c_list(sALIAS, true);
/* initialise to default for ;; or omitted */
- t->u.charflag = ';';
+ t->u.charflag = ORD(';');
/* SUSv4 requires the ;; except in the last casepart */
if ((tpeek(CONTIN|KEYWORD|sALIAS)) != endtok)
switch (symbol) {
@@ -645,11 +674,11 @@
default:
syntaxerr(NULL);
case BRKEV:
- t->u.charflag = '|';
+ t->u.charflag = ORD('|');
if (0)
/* FALLTHROUGH */
case BRKFT:
- t->u.charflag = '&';
+ t->u.charflag = ORD('&');
/* FALLTHROUGH */
case BREAK:
/* initialised above, but we need to eat the token */
@@ -659,7 +688,7 @@
}
static struct op *
-function_body(char *name,
+function_body(char *name, int sALIAS,
/* function foo { ... } vs foo() { .. } */
bool ksh_func)
{
@@ -672,11 +701,11 @@
* only allow [a-zA-Z_0-9] but this allows more as old pdkshs
* have allowed more; the following were never allowed:
* NUL TAB NL SP " $ & ' ( ) ; < = > \ ` |
- * C_QUOTE covers all but adds # * ? [ ]
+ * C_QUOTE|C_SPC covers all but adds # * ? [ ]
*/
for (p = sname; *p; p++)
- if (ctype(*p, C_QUOTE))
- yyerror("%s: %s\n", sname, "invalid function name");
+ if (ctype(*p, C_QUOTE | C_SPC))
+ yyerror(Tinvname, sname, Tfunction);
/*
* Note that POSIX allows only compound statements after foo(),
@@ -685,14 +714,14 @@
* only accepts an open-brace.
*/
if (ksh_func) {
- if (tpeek(CONTIN|KEYWORD|sALIAS) == '(' /*)*/) {
+ if ((unsigned int)tpeek(CONTIN|KEYWORD|sALIAS) == ORD('(' /*)*/)) {
/* function foo () { //}*/
ACCEPT;
- musthave(')', 0);
+ musthave(ORD(/*(*/ ')'), 0);
/* degrade to POSIX function */
ksh_func = false;
}
- musthave('{' /*}*/, CONTIN|KEYWORD|sALIAS);
+ musthave(ORD('{' /*}*/), CONTIN|KEYWORD|sALIAS);
REJECT;
}
@@ -701,7 +730,7 @@
t->u.ksh_func = tobool(ksh_func);
t->lineno = source->line;
- if ((t->left = get_command(CONTIN)) == NULL) {
+ if ((t->left = get_command(CONTIN, sALIAS)) == NULL) {
char *tv;
/*
* Probably something like foo() followed by EOF or ';'.
@@ -713,7 +742,7 @@
/* (2 * sizeof(char *)) is small enough */
t->left->args = alloc(2 * sizeof(char *), ATEMP);
t->left->args[0] = tv = alloc(3, ATEMP);
- tv[0] = CHAR;
+ tv[0] = QCHAR;
tv[1] = ':';
tv[2] = EOS;
t->left->args[1] = NULL;
@@ -726,7 +755,7 @@
}
static char **
-wordlist(void)
+wordlist(int sALIAS)
{
int c;
XPtrV args;
@@ -783,9 +812,9 @@
{ "done", DONE, true },
{ "in", IN, true },
{ Tfunction, FUNCTION, true },
- { "time", TIME, true },
- { "{", '{', true },
- { Tcbrace, '}', true },
+ { Ttime, TIME, true },
+ { "{", ORD('{'), true },
+ { Tcbrace, ORD('}'), true },
{ "!", BANG, true },
{ "[[", DBRACKET, true },
/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
@@ -797,7 +826,7 @@
{ "((", MDPAREN, false },
{ "|&", COPROC, false },
/* and some special cases... */
- { "newline", '\n', false },
+ { "newline", ORD('\n'), false },
{ NULL, 0, false }
};
@@ -823,14 +852,14 @@
static void
syntaxerr(const char *what)
{
- /* 2<<- is the longest redirection, I think */
- char redir[6];
+ /* 23<<- is the longest redirection, I think */
+ char redir[8];
const char *s;
struct tokeninfo const *tt;
int c;
if (!what)
- what = "unexpected";
+ what = Tunexpected;
REJECT;
c = token(0);
Again:
@@ -843,15 +872,15 @@
goto Again;
}
/* don't quote the EOF */
- yyerror("%s: %s %s\n", Tsynerr, "unexpected", "EOF");
+ yyerror("%s: unexpected EOF", Tsynerr);
/* NOTREACHED */
case LWORD:
- s = snptreef(NULL, 32, "%S", yylval.cp);
+ s = snptreef(NULL, 32, Tf_S, yylval.cp);
break;
case REDIR:
- s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
+ s = snptreef(redir, sizeof(redir), Tft_R, yylval.iop);
break;
default:
@@ -870,7 +899,7 @@
s = redir;
}
}
- yyerror("%s: '%s' %s\n", Tsynerr, s, what);
+ yyerror(Tf_sD_s_qs, Tsynerr, what, s);
}
static void
@@ -904,7 +933,7 @@
}
struct op *
-compile(Source *s, bool skiputf8bom)
+compile(Source *s, bool skiputf8bom, bool doalias)
{
nesting.start_token = 0;
nesting.start_line = 0;
@@ -912,39 +941,19 @@
source = s;
if (skiputf8bom)
yyskiputf8bom();
- yyparse();
+ yyparse(doalias);
return (outtree);
}
-/*-
- * This kludge exists to take care of sh/AT&T ksh oddity in which
- * the arguments of alias/export/readonly/typeset have no field
- * splitting, file globbing, or (normal) tilde expansion done.
- * AT&T ksh seems to do something similar to this since
- * $ touch a=a; typeset a=[ab]; echo "$a"
- * a=[ab]
- * $ x=typeset; $x a=[ab]; echo "$a"
- * a=a
- * $
- */
-static int
-assign_command(const char *s)
-{
- if (!*s)
- return (0);
- return ((strcmp(s, Talias) == 0) ||
- (strcmp(s, Texport) == 0) ||
- (strcmp(s, Treadonly) == 0) ||
- (strcmp(s, Ttypeset) == 0));
-}
-
/* Check if we are in the middle of reading an alias */
static int
inalias(struct source *s)
{
- for (; s && s->type == SALIAS; s = s->next)
+ while (s && s->type == SALIAS) {
if (!(s->flags & SF_ALIASEND))
return (1);
+ s = s->next;
+ }
return (0);
}
@@ -976,7 +985,7 @@
static Test_op
dbtestp_isa(Test_env *te, Test_meta meta)
{
- int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
+ int c = tpeek(CMDASN | (meta == TM_BINOP ? 0 : CONTIN));
bool uqword;
char *save = NULL;
Test_op ret = TO_NONOP;
@@ -992,14 +1001,15 @@
ret = (uqword && !strcmp(yylval.cp,
dbtest_tokens[(int)TM_NOT])) ? TO_NONNULL : TO_NONOP;
else if (meta == TM_OPAREN)
- ret = c == '(' /*)*/ ? TO_NONNULL : TO_NONOP;
+ ret = (unsigned int)c == ORD('(') /*)*/ ? TO_NONNULL : TO_NONOP;
else if (meta == TM_CPAREN)
- ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP;
+ ret = (unsigned int)c == /*(*/ ORD(')') ? TO_NONNULL : TO_NONOP;
else if (meta == TM_UNOP || meta == TM_BINOP) {
if (meta == TM_BINOP && c == REDIR &&
- (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) {
+ (yylval.iop->ioflag == IOREAD ||
+ yylval.iop->ioflag == IOWRITE)) {
ret = TO_NONNULL;
- save = wdcopy(yylval.iop->flag == IOREAD ?
+ save = wdcopy(yylval.iop->ioflag == IOREAD ?
db_lthan : db_gthan, ATEMP);
} else if (uqword && (ret = test_isop(meta, ident)))
save = yylval.cp;
@@ -1021,7 +1031,7 @@
dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED,
bool do_eval MKSH_A_UNUSED)
{
- int c = tpeek(ARRAYVAR);
+ int c = tpeek(CMDASN);
if (c != LWORD)
return (NULL);
@@ -1073,8 +1083,9 @@
tv->tv_sec = 0;
/* parse integral part */
- while (ksh_isdigit(*s)) {
- tt.tv_sec = tv->tv_sec * 10 + (*s++ - '0');
+ while (ctype(*s, C_DIGIT)) {
+ tt.tv_sec = tv->tv_sec * 10 + ksh_numdig(*s++);
+ /*XXX this overflow check maybe UB */
if (tt.tv_sec / 10 != tv->tv_sec) {
errno = EOVERFLOW;
return (true);
@@ -1094,14 +1105,14 @@
/* parse decimal fraction */
i = 100000;
- while (ksh_isdigit(*s)) {
- tv->tv_usec += i * (*s++ - '0');
+ while (ctype(*s, C_DIGIT)) {
+ tv->tv_usec += i * ksh_numdig(*s++);
if (i == 1)
break;
i /= 10;
}
/* check for junk after fractional part */
- while (ksh_isdigit(*s))
+ while (ctype(*s, C_DIGIT))
++s;
if (*s) {
errno = EINVAL;
@@ -1118,19 +1129,19 @@
* a COMSUB recursively using the main shell parser and lexer
*/
char *
-yyrecursive(int subtype MKSH_A_UNUSED)
+yyrecursive(int subtype)
{
struct op *t;
char *cp;
struct yyrecursive_state *ys;
- int stok, etok;
+ unsigned int stok, etok;
if (subtype != COMSUB) {
- stok = '{';
- etok = '}';
+ stok = ORD('{');
+ etok = ORD('}');
} else {
- stok = '(';
- etok = ')';
+ stok = ORD('(');
+ etok = ORD(')');
}
ys = alloc(sizeof(struct yyrecursive_state), ATEMP);
@@ -1143,17 +1154,17 @@
ys->old_reject = reject;
ys->old_symbol = symbol;
ACCEPT;
+ memcpy(ys->old_heres, heres, sizeof(heres));
ys->old_herep = herep;
- ys->old_salias = sALIAS;
- sALIAS = 0;
+ herep = heres;
ys->next = e->yyrecursive_statep;
e->yyrecursive_statep = ys;
/* we use TPAREN as a helper container here */
- t = nested(TPAREN, stok, etok);
+ t = nested(TPAREN, stok, etok, ALIAS);
yyrecursive_pop(false);
/* t->left because nested(TPAREN, ...) hides our goodies there */
- cp = snptreef(NULL, 0, "%T", t->left);
+ cp = snptreef(NULL, 0, Tf_T, t->left);
tfree(t, ATEMP);
return (cp);
@@ -1169,7 +1180,7 @@
return;
e->yyrecursive_statep = ys->next;
- sALIAS = ys->old_salias;
+ memcpy(heres, ys->old_heres, sizeof(heres));
herep = ys->old_herep;
reject = ys->old_reject;
symbol = ys->old_symbol;
Added: trunk/contrib/mksh/test.sh
===================================================================
--- trunk/contrib/mksh/test.sh (rev 0)
+++ trunk/contrib/mksh/test.sh 2019-01-18 21:15:37 UTC (rev 12141)
@@ -0,0 +1,93 @@
+#!/usr/src/contrib/mksh/mksh
+LC_ALL=C PATH='/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/root/bin'; export LC_ALL PATH
+test -n "$KSH_VERSION" || exit 1
+set -A check_categories -- shell:legacy-no int:32 shell:textmode-no shell:binmode-yes stdout-ed,no-stderr-ed
+pflag='/usr/src/contrib/mksh/mksh'
+sflag='./check.t'
+usee=0 useU=0 Pflag=0 Sflag=0 uset=0 vflag=1 xflag=0
+while getopts "C:e:fPp:QSs:t:U:v" ch; do case $ch {
+(C) check_categories[${#check_categories[*]}]=$OPTARG ;;
+(e) usee=1; eflag=$OPTARG ;;
+(f) check_categories[${#check_categories[*]}]=fastbox ;;
+(P) Pflag=1 ;;
+(+P) Pflag=0 ;;
+(p) pflag=$OPTARG ;;
+(Q) vflag=0 ;;
+(+Q) vflag=1 ;;
+(S) Sflag=1 ;;
+(+S) Sflag=0 ;;
+(s) sflag=$OPTARG ;;
+(t) uset=1; tflag=$OPTARG ;;
+(U) useU=1; Uflag=$OPTARG ;;
+(v) vflag=1 ;;
+(+v) vflag=0 ;;
+(*) xflag=1 ;;
+}
+done
+shift $((OPTIND - 1))
+set -A args -- './check.pl' -p "$pflag"
+if false; then
+args[${#args[*]}]=-E
+fi
+x=
+for y in "${check_categories[@]}"; do
+x=$x,$y
+done
+if [[ -n $x ]]; then
+args[${#args[*]}]=-C
+args[${#args[*]}]=${x#,}
+fi
+if (( usee )); then
+args[${#args[*]}]=-e
+args[${#args[*]}]=$eflag
+fi
+(( Pflag )) && args[${#args[*]}]=-P
+if (( uset )); then
+args[${#args[*]}]=-t
+args[${#args[*]}]=$tflag
+fi
+if (( useU )); then
+args[${#args[*]}]=-U
+args[${#args[*]}]=$Uflag
+fi
+(( vflag )) && args[${#args[*]}]=-v
+(( xflag )) && args[${#args[*]}]=-x # force usage by synerr
+if [[ -n $TMPDIR && -d $TMPDIR/. ]]; then
+args[${#args[*]}]=-T
+args[${#args[*]}]=$TMPDIR
+fi
+print Testing mksh for conformance:
+grep -F -e Mir''OS: -e MIRBSD "$sflag"
+print "This shell is actually:\n\t$KSH_VERSION"
+print 'test.sh built for mksh R56 2018/01/14'
+cstr='$os = defined $^O ? $^O : "unknown";'
+cstr="$cstr"'print $os . ", Perl version " . $];'
+for perli in $PERL perl5 perl no; do
+if [[ $perli = no ]]; then
+print Cannot find a working Perl interpreter, aborting.
+exit 1
+fi
+print "Trying Perl interpreter '$perli'..."
+perlos=$($perli -e "$cstr")
+rv=$?
+print "Errorlevel $rv, running on '$perlos'"
+if (( rv )); then
+print "=> not using"
+continue
+fi
+if [[ -n $perlos ]]; then
+print "=> using it"
+break
+fi
+done
+(( Sflag )) || echo + $perli "${args[@]}" -s "$sflag" "$@"
+(( Sflag )) || exec $perli "${args[@]}" -s "$sflag" "$@"
+# use of the -S option for check.t split into multiple chunks
+rv=0
+for s in "$sflag".*; do
+echo + $perli "${args[@]}" -s "$s" "$@"
+$perli "${args[@]}" -s "$s" "$@"
+rc=$?
+(( rv = rv ? rv : rc ))
+done
+exit $rv
Property changes on: trunk/contrib/mksh/test.sh
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/contrib/mksh/tree.c
===================================================================
--- trunk/contrib/mksh/tree.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/tree.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-/* $OpenBSD: tree.c,v 1.20 2012/06/27 07:17:19 otto Exp $ */
+/* $OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2015, 2016, 2017
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.72 2013/09/24 20:19:45 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.95 2018/01/14 00:03:05 tg Exp $");
#define INDENT 8
@@ -49,6 +49,7 @@
struct ioword **ioact;
struct op *t1;
int i;
+ const char *ccp;
Chain:
if (t == NULL)
@@ -56,12 +57,9 @@
switch (t->type) {
case TCOM:
prevent_semicolon = false;
- /*
- * special-case 'var=<<EOF' (rough; see
- * exec.c:execute() for full code)
- */
- if (
- /* we have zero arguments, i.e. no programme to run */
+ /* special-case 'var=<<EOF' (cf. exec.c:execute) */
+ if (t->args &&
+ /* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
@@ -69,8 +67,14 @@
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
- (t->ioact[0]->flag & IOTYPE) == IOHERE) {
- fptreef(shf, indent, "%S", t->vars[0]);
+ (t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
+ /* the variable assignment begins with a valid varname */
+ (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
+ /* and has no right-hand side (i.e. "varname=") */
+ ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
+ /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
+ ccp[3] == '=' && ccp[4] == EOS))) {
+ fptreef(shf, indent, Tf_S, t->vars[0]);
break;
}
@@ -77,13 +81,22 @@
if (t->vars) {
w = (const char **)t->vars;
while (*w)
- fptreef(shf, indent, "%S ", *w++);
+ fptreef(shf, indent, Tf_S_, *w++);
} else
shf_puts("#no-vars# ", shf);
if (t->args) {
w = t->args;
+ if (*w && **w == CHAR) {
+ char *cp = wdstrip(*w++, WDS_TPUTS);
+
+ if (valid_alias_name(cp))
+ shf_putc('\\', shf);
+ shf_puts(cp, shf);
+ shf_putc(' ', shf);
+ afree(cp, ATEMP);
+ }
while (*w)
- fptreef(shf, indent, "%S ", *w++);
+ fptreef(shf, indent, Tf_S_, *w++);
} else
shf_puts("#no-args# ", shf);
break;
@@ -115,7 +128,7 @@
w = t->args;
shf_puts("[[", shf);
while (*w)
- fptreef(shf, indent, " %S", *w++);
+ fptreef(shf, indent, Tf__S, *w++);
shf_puts(" ]] ", shf);
break;
case TSELECT:
@@ -126,8 +139,8 @@
shf_puts("in ", shf);
w = (const char **)t->vars;
while (*w)
- fptreef(shf, indent, "%S ", *w++);
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tf_S_, *w++);
+ fptreef(shf, indent, Tft_end);
}
fptreef(shf, indent + INDENT, "do%N%T", t->left);
fptreef(shf, indent, "%;done ");
@@ -147,11 +160,9 @@
}
fptreef(shf, indent, "%Nesac ");
break;
-#ifdef DEBUG
case TELIF:
- internal_errorf("TELIF in tree.c:ptree() unexpected");
+ internal_errorf(TELIF_unexpected);
/* FALLTHROUGH */
-#endif
case TIF:
i = 2;
t1 = t;
@@ -159,19 +170,19 @@
do {
t1 = t1->right;
i = 0;
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
process_TIF:
/* 5 == strlen("elif ") */
fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
t1 = t1->right;
if (t1->left != NULL) {
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
fptreef(shf, indent + INDENT, "%s%N%T",
"then", t1->left);
}
} while (t1->right && t1->right->type == TELIF);
if (t1->right != NULL) {
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
fptreef(shf, indent + INDENT, "%s%N%T",
"else", t1->right);
}
@@ -180,10 +191,10 @@
case TWHILE:
case TUNTIL:
/* 6 == strlen("while "/"until ") */
- fptreef(shf, indent + 6, "%s %T",
+ fptreef(shf, indent + 6, Tf_s_T,
(t->type == TWHILE) ? "while" : "until",
t->left);
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
fptreef(shf, indent + INDENT, "do%N%T", t->right);
fptreef(shf, indent, "%;done ");
break;
@@ -203,7 +214,7 @@
fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
break;
case TTIME:
- fptreef(shf, indent, "%s %T", "time", t->left);
+ fptreef(shf, indent, Tf_s_T, Ttime, t->left);
break;
default:
shf_puts("<botch>", shf);
@@ -221,12 +232,11 @@
struct ioword *iop = *ioact++;
/* heredoc is NULL when tracing (set -x) */
- if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE &&
+ if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE &&
iop->heredoc) {
shf_putc('\n', shf);
shf_puts(iop->heredoc, shf);
- fptreef(shf, indent, "%s",
- iop->flag & IONDELIM ? "<<" :
+ fptreef(shf, indent, Tf_s,
evalstr(iop->delim, 0));
need_nl = true;
}
@@ -246,9 +256,9 @@
static void
pioact(struct shf *shf, struct ioword *iop)
{
- int flag = iop->flag;
- int type = flag & IOTYPE;
- int expected;
+ unsigned short flag = iop->ioflag;
+ unsigned short type = flag & IOTYPE;
+ short expected;
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
(type == IOCAT || type == IOWRITE) ? 1 :
@@ -255,7 +265,7 @@
(type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
iop->unit + 1;
if (iop->unit != expected)
- shf_fprintf(shf, "%d", iop->unit);
+ shf_fprintf(shf, Tf_d, (int)iop->unit);
switch (type) {
case IOREAD:
@@ -265,6 +275,8 @@
shf_puts("<<", shf);
if (flag & IOSKIP)
shf_putc('-', shf);
+ else if (flag & IOHERESTR)
+ shf_putc('<', shf);
break;
case IOCAT:
shf_puts(">>", shf);
@@ -283,17 +295,15 @@
}
/* name/delim are NULL when printing syntax errors */
if (type == IOHERE) {
- if (iop->delim)
+ if (iop->delim && !(iop->ioflag & IONDELIM))
wdvarput(shf, iop->delim, 0, WDS_TPUTS);
- if (iop->flag & IOHERESTR)
- shf_putc(' ', shf);
- } else if (iop->name) {
- if (iop->flag & IONAMEXP)
- print_value_quoted(shf, iop->name);
+ } else if (iop->ioname) {
+ if (flag & IONAMEXP)
+ print_value_quoted(shf, iop->ioname);
else
- wdvarput(shf, iop->name, 0, WDS_TPUTS);
- shf_putc(' ', shf);
+ wdvarput(shf, iop->ioname, 0, WDS_TPUTS);
}
+ shf_putc(' ', shf);
prevent_semicolon = false;
}
@@ -309,7 +319,7 @@
* `...` -> $(...)
* 'foo' -> "foo"
* x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
- * x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ
+ * x${foo:-'hi'} -> x${foo:-hi}
* could change encoding to:
* OQUOTE ["'] ... CQUOTE ["']
* COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
@@ -319,45 +329,56 @@
case EOS:
return (--wp);
case ADELIM:
+ if (ord(*wp) == ORD(/*{*/ '}')) {
+ ++wp;
+ goto wdvarput_csubst;
+ }
+ /* FALLTHROUGH */
case CHAR:
- c = *wp++;
- if ((opmode & WDS_MAGIC) &&
- (ISMAGIC(c) || c == '[' || c == '!' ||
- c == '-' || c == ']' || c == '*' || c == '?'))
- shf_putc(MAGIC, shf);
+ c = ord(*wp++);
shf_putc(c, shf);
break;
- case QCHAR: {
- bool doq;
-
- c = *wp++;
- doq = (c == '"' || c == '`' || c == '$' || c == '\\');
- if (opmode & WDS_TPUTS) {
- if (quotelevel == 0)
- doq = true;
- } else {
- if (!(opmode & WDS_KEEPQ))
- doq = false;
- }
- if (doq)
- shf_putc('\\', shf);
+ case QCHAR:
+ c = ord(*wp++);
+ if (opmode & WDS_TPUTS)
+ switch (c) {
+ case ORD('\n'):
+ if (quotelevel == 0) {
+ c = ORD('\'');
+ shf_putc(c, shf);
+ shf_putc(ORD('\n'), shf);
+ }
+ break;
+ default:
+ if (quotelevel == 0)
+ /* FALLTHROUGH */
+ case ORD('"'):
+ case ORD('`'):
+ case ORD('$'):
+ case ORD('\\'):
+ shf_putc(ORD('\\'), shf);
+ break;
+ }
shf_putc(c, shf);
break;
- }
+ case COMASUB:
case COMSUB:
shf_puts("$(", shf);
cs = ")";
+ if (ord(*wp) == ORD('(' /*)*/))
+ shf_putc(' ', shf);
pSUB:
while ((c = *wp++) != 0)
shf_putc(c, shf);
shf_puts(cs, shf);
break;
+ case FUNASUB:
case FUNSUB:
- c = ' ';
+ c = ORD(' ');
if (0)
/* FALLTHROUGH */
case VALSUB:
- c = '|';
+ c = ORD('|');
shf_putc('$', shf);
shf_putc('{', shf);
shf_putc(c, shf);
@@ -382,7 +403,7 @@
break;
case OSUBST:
shf_putc('$', shf);
- if (*wp++ == '{')
+ if (ord(*wp++) == ORD('{'))
shf_putc('{', shf);
while ((c = *wp++) != 0)
shf_putc(c, shf);
@@ -389,25 +410,21 @@
wp = wdvarput(shf, wp, 0, opmode);
break;
case CSUBST:
- if (*wp++ == '}')
+ if (ord(*wp++) == ORD('}')) {
+ wdvarput_csubst:
shf_putc('}', shf);
+ }
return (wp);
case OPAT:
- if (opmode & WDS_MAGIC) {
- shf_putc(MAGIC, shf);
- shf_putchar(*wp++ | 0x80, shf);
- } else {
- shf_putchar(*wp++, shf);
- shf_putc('(', shf);
- }
+ shf_putchar(*wp++, shf);
+ shf_putc('(', shf);
break;
case SPAT:
- c = '|';
+ c = ORD('|');
if (0)
+ /* FALLTHROUGH */
case CPAT:
- c = /*(*/ ')';
- if (opmode & WDS_MAGIC)
- shf_putc(MAGIC, shf);
+ c = ORD(/*(*/ ')');
shf_putc(c, shf);
break;
}
@@ -450,39 +467,40 @@
{
int c;
- while ((c = *fmt++)) {
+ while ((c = ord(*fmt++))) {
if (c == '%') {
- switch ((c = *fmt++)) {
- case 'c':
+ switch ((c = ord(*fmt++))) {
+ case ORD('c'):
/* character (octet, probably) */
shf_putchar(va_arg(va, int), shf);
break;
- case 's':
+ case ORD('s'):
/* string */
shf_puts(va_arg(va, char *), shf);
break;
- case 'S':
+ case ORD('S'):
/* word */
wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
break;
- case 'd':
+ case ORD('d'):
/* signed decimal */
- shf_fprintf(shf, "%d", va_arg(va, int));
+ shf_fprintf(shf, Tf_d, va_arg(va, int));
break;
- case 'u':
+ case ORD('u'):
/* unsigned decimal */
shf_fprintf(shf, "%u", va_arg(va, unsigned int));
break;
- case 'T':
+ case ORD('T'):
/* format tree */
ptree(va_arg(va, struct op *), indent, shf);
goto dont_trash_prevent_semicolon;
- case ';':
+ case ORD(';'):
/* newline or ; */
- case 'N':
+ case ORD('N'):
/* newline or space */
if (shf->flags & SHF_STRING) {
- if (c == ';' && !prevent_semicolon)
+ if ((unsigned int)c == ORD(';') &&
+ !prevent_semicolon)
shf_putc(';', shf);
shf_putc(' ', shf);
} else {
@@ -498,7 +516,7 @@
shf_putc(' ', shf);
}
break;
- case 'R':
+ case ORD('R'):
/* I/O redirection */
pioact(shf, va_arg(va, struct ioword *));
break;
@@ -594,14 +612,18 @@
case EOS:
return (wp);
case ADELIM:
- if (c == ADELIM)
+ if (c == ADELIM && nest == 0)
return (wp + 1);
+ if (ord(*wp) == ORD(/*{*/ '}'))
+ goto wdscan_csubst;
/* FALLTHROUGH */
case CHAR:
case QCHAR:
wp++;
break;
+ case COMASUB:
case COMSUB:
+ case FUNASUB:
case FUNSUB:
case VALSUB:
case EXPRSUB:
@@ -617,6 +639,7 @@
;
break;
case CSUBST:
+ wdscan_csubst:
wp++;
if (c == CSUBST && nest == 0)
return (wp);
@@ -635,8 +658,8 @@
break;
default:
internal_warningf(
- "wdscan: unknown char 0x%x (carrying on)",
- wp[-1]);
+ "wdscan: unknown char 0x%X (carrying on)",
+ (unsigned char)wp[-1]);
}
}
@@ -673,8 +696,8 @@
q = alloc(sizeof(struct ioword), ap);
ior[i] = q;
*q = *p;
- if (p->name != NULL)
- q->name = wdcopy(p->name, ap);
+ if (p->ioname != NULL)
+ q->ioname = wdcopy(p->ioname, ap);
if (p->delim != NULL)
q->delim = wdcopy(p->delim, ap);
if (p->heredoc != NULL)
@@ -696,8 +719,7 @@
if (t == NULL)
return;
- if (t->str != NULL)
- afree(t->str, ap);
+ afree(t->str, ap);
if (t->vars != NULL) {
for (w = t->vars; *w != NULL; w++)
@@ -732,12 +754,9 @@
iop = iow;
while ((p = *iop++) != NULL) {
- if (p->name != NULL)
- afree(p->name, ap);
- if (p->delim != NULL)
- afree(p->delim, ap);
- if (p->heredoc != NULL)
- afree(p->heredoc, ap);
+ afree(p->ioname, ap);
+ afree(p->delim, ap);
+ afree(p->heredoc, ap);
afree(p, ap);
}
afree(iow, ap);
@@ -748,6 +767,8 @@
{
if (isksh)
fptreef(shf, i, "%s %s %T", Tfunction, k, v);
+ else if (ktsearch(&keywords, k, hash(k)))
+ fptreef(shf, i, "%s %s() %T", Tfunction, k, v);
else
fptreef(shf, i, "%s() %T", k, v);
}
@@ -762,7 +783,7 @@
size_t n;
buf = alloc(sz + 16, ATEMP);
- snptreef(buf, sz + 16, "%T", t);
+ snptreef(buf, sz + 16, Tf_T, t);
cp = buf;
vist_loop:
if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
@@ -775,20 +796,20 @@
*dst++ = *cp++;
goto vist_loop;
}
- if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0)
+ if (--sz == 0 || (c = ord(*cp++)) == 0)
/* NUL or not enough free space */
goto vist_out;
- if (ISCTRL(c & 0x7F)) {
+ if (ksh_isctrl(c)) {
/* C0 or C1 control character or DEL */
if (--sz == 0)
/* not enough free space for two chars */
goto vist_out;
- *dst++ = (c & 0x80) ? '$' : '^';
- c = UNCTRL(c & 0x7F);
- } else if (UTFMODE && c > 0x7F) {
+ *dst++ = '^';
+ c = ksh_unctrl(c);
+ } else if (UTFMODE && rtt2asc(c) > 0x7F) {
/* better not try to display broken multibyte chars */
/* also go easy on the Unicode: no U+FFFD here */
- c = '?';
+ c = ORD('?');
}
*dst++ = c;
goto vist_loop;
@@ -802,10 +823,10 @@
void
dumpchar(struct shf *shf, int c)
{
- if (ISCTRL(c & 0x7F)) {
+ if (ksh_isctrl(c)) {
/* C0 or C1 control character or DEL */
- shf_putc((c & 0x80) ? '$' : '^', shf);
- c = UNCTRL(c & 0x7F);
+ shf_putc('^', shf);
+ c = ksh_unctrl(c);
}
shf_putc(c, shf);
}
@@ -822,20 +843,28 @@
shf_puts("EOS", shf);
return (--wp);
case ADELIM:
+ if (ord(*wp) == ORD(/*{*/ '}')) {
+ shf_puts(/*{*/ "]ADELIM(})", shf);
+ return (wp + 1);
+ }
shf_puts("ADELIM=", shf);
if (0)
+ /* FALLTHROUGH */
case CHAR:
- shf_puts("CHAR=", shf);
+ shf_puts("CHAR=", shf);
dumpchar(shf, *wp++);
break;
case QCHAR:
shf_puts("QCHAR<", shf);
- c = *wp++;
- if (quotelevel == 0 ||
- (c == '"' || c == '`' || c == '$' || c == '\\'))
+ c = ord(*wp++);
+ if (quotelevel == 0 || c == ORD('"') ||
+ c == ORD('\\') || ctype(c, C_DOLAR | C_GRAVE))
shf_putc('\\', shf);
dumpchar(shf, c);
goto closeandout;
+ case COMASUB:
+ shf_puts("COMASUB<", shf);
+ goto dumpsub;
case COMSUB:
shf_puts("COMSUB<", shf);
dumpsub:
@@ -844,6 +873,9 @@
closeandout:
shf_putc('>', shf);
break;
+ case FUNASUB:
+ shf_puts("FUNASUB<", shf);
+ goto dumpsub;
case FUNSUB:
shf_puts("FUNSUB<", shf);
goto dumpsub;
@@ -854,10 +886,10 @@
shf_puts("EXPRSUB<", shf);
goto dumpsub;
case OQUOTE:
- shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
+ shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel);
break;
case CQUOTE:
- shf_fprintf(shf, "%d}CQUOTE", quotelevel);
+ shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel);
if (quotelevel)
quotelevel--;
else
@@ -910,9 +942,9 @@
shf_puts("{IOACT", shf);
while ((iop = *ioact++) != NULL) {
- int type = iop->flag & IOTYPE;
+ unsigned short type = iop->ioflag & IOTYPE;
#define DT(x) case x: shf_puts(#x, shf); break;
-#define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
+#define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
shf_putc(';', shf);
switch (type) {
@@ -933,19 +965,19 @@
DB(IOBASH)
DB(IOHERESTR)
DB(IONDELIM)
- shf_fprintf(shf, ",unit=%d", iop->unit);
- if (iop->delim) {
+ shf_fprintf(shf, ",unit=%d", (int)iop->unit);
+ if (iop->delim && !(iop->ioflag & IONDELIM)) {
shf_puts(",delim<", shf);
dumpwdvar(shf, iop->delim);
shf_putc('>', shf);
}
- if (iop->name) {
- if (iop->flag & IONAMEXP) {
+ if (iop->ioname) {
+ if (iop->ioflag & IONAMEXP) {
shf_puts(",name=", shf);
- print_value_quoted(shf, iop->name);
+ print_value_quoted(shf, iop->ioname);
} else {
shf_puts(",name<", shf);
- dumpwdvar(shf, iop->name);
+ dumpwdvar(shf, iop->ioname);
shf_putc('>', shf);
}
}
@@ -1097,7 +1129,7 @@
goto dumpleftandout;
OPEN(TFUNCT)
shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
- t->u.ksh_func ? "yes" : "no");
+ t->u.ksh_func ? Ttrue : Tfalse);
goto dumpleftandout;
OPEN(TTIME)
goto dumpleftandout;
@@ -1124,7 +1156,7 @@
break;
OPEN(TEOF)
dumpunexpected:
- shf_puts("unexpected", shf);
+ shf_puts(Tunexpected, shf);
break;
OPEN(TELIF)
goto dumpunexpected;
Modified: trunk/contrib/mksh/var.c
===================================================================
--- trunk/contrib/mksh/var.c 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/var.c 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,9 +1,9 @@
-/* $OpenBSD: var.c,v 1.38 2013/12/20 17:53:09 zhuk Exp $ */
+/* $OpenBSD: var.c,v 1.44 2015/09/10 11:37:42 jca Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
- * Thorsten Glaser <tg at mirbsd.org>
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ * mirabilos <m at mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
* are retained or reproduced in an accompanying document, permission
@@ -28,7 +28,7 @@
#include <sys/sysctl.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.183.2.2 2015/03/01 15:43:07 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.223 2018/01/13 23:55:15 tg Exp $");
/*-
* Variables
@@ -45,6 +45,9 @@
/* may only be set by typeset() just before call to array_index_calc() */
static enum namerefflag innermost_refflag = SRF_NOP;
+static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
+static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
+ bool);
static char *formatstr(struct tbl *, const char *);
static void exportprep(struct tbl *, const char *);
static int special(const char *);
@@ -133,7 +136,7 @@
struct tbl *tp;
ktinit(APERM, &specials,
- /* currently 14 specials: 75% of 32 = 2^5 */
+ /* currently 21 specials: 75% of 32 = 2^5 */
5);
while (i < V_MAX - 1) {
tp = ktenter(&specials, initvar_names[i],
@@ -180,7 +183,7 @@
*arrayp = false;
redo_from_ref:
p = skip_varname(n, false);
- if (innermost_refflag == SRF_NOP && (p != n) && ksh_isalphx(n[0])) {
+ if (innermost_refflag == SRF_NOP && (p != n) && ctype(n[0], C_ALPHX)) {
struct tbl *vp;
char *vn;
@@ -193,7 +196,7 @@
char *cp;
/* gotcha! */
- cp = shf_smprintf("%s%s", str_val(vp), p);
+ cp = shf_smprintf(Tf_ss, str_val(vp), p);
afree(ap, ATEMP);
n = ap = cp;
goto redo_from_ref;
@@ -201,7 +204,7 @@
}
innermost_refflag = SRF_NOP;
- if (p != n && *p == '[' && (len = array_ref_len(p))) {
+ if (p != n && ord(*p) == ORD('[') && (len = array_ref_len(p))) {
char *sub, *tmp;
mksh_ari_t rval;
@@ -218,6 +221,7 @@
return (n);
}
+#define vn vname.ro
/*
* Search for variable, if not found create globally.
*/
@@ -224,8 +228,16 @@
struct tbl *
global(const char *n)
{
+ return (isglobal(n, true));
+}
+
+/* search for variable; if not found, return NULL or create globally */
+struct tbl *
+isglobal(const char *n, bool docreate)
+{
+ struct tbl *vp;
+ union mksh_cchack vname;
struct block *l = e->loc;
- struct tbl *vp;
int c;
bool array;
uint32_t h, val;
@@ -234,27 +246,35 @@
* check to see if this is an array;
* dereference namerefs; must come first
*/
- n = array_index_calc(n, &array, &val);
- h = hash(n);
- c = (unsigned char)n[0];
- if (!ksh_isalphx(c)) {
+ vn = array_index_calc(n, &array, &val);
+ h = hash(vn);
+ c = (unsigned char)vn[0];
+ if (!ctype(c, C_ALPHX)) {
if (array)
- errorf("bad substitution");
- vp = &vtemp;
+ errorf(Tbadsubst);
+ vp = vtemp;
vp->flag = DEFINED;
vp->type = 0;
vp->areap = ATEMP;
- *vp->name = c;
- if (ksh_isdigit(c)) {
- if (getn(n, &c) && (c <= l->argc))
- /* setstr can't fail here */
- setstr(vp, l->argv[c], KSH_RETURN_ERROR);
+ if (ctype(c, C_DIGIT)) {
+ if (getn(vn, &c)) {
+ /* main.c:main_init() says 12 */
+ shf_snprintf(vp->name, 12, Tf_d, c);
+ if (c <= l->argc) {
+ /* setstr can't fail here */
+ setstr(vp, l->argv[c],
+ KSH_RETURN_ERROR);
+ }
+ } else
+ vp->name[0] = '\0';
vp->flag |= RDONLY;
- return (vp);
+ goto out;
}
+ vp->name[0] = c;
+ vp->name[1] = '\0';
vp->flag |= RDONLY;
- if (n[1] != '\0')
- return (vp);
+ if (vn[1] != '\0')
+ goto out;
vp->flag |= ISSET|INTEGER;
switch (c) {
case '$':
@@ -278,17 +298,26 @@
default:
vp->flag &= ~(ISSET|INTEGER);
}
- return (vp);
+ goto out;
}
- l = varsearch(e->loc, &vp, n, h);
- if (vp != NULL)
- return (array ? arraysearch(vp, val) : vp);
- vp = ktenter(&l->vars, n, h);
- if (array)
- vp = arraysearch(vp, val);
- vp->flag |= DEFINED;
- if (special(n))
- vp->flag |= SPECIAL;
+ l = varsearch(e->loc, &vp, vn, h);
+ if (vp == NULL && docreate)
+ vp = ktenter(&l->vars, vn, h);
+ else
+ docreate = false;
+ if (vp != NULL) {
+ if (array)
+ vp = arraysearch(vp, val);
+ if (docreate) {
+ vp->flag |= DEFINED;
+ if (special(vn))
+ vp->flag |= SPECIAL;
+ }
+ }
+ out:
+ last_lookup_was_array = array;
+ if (vn != n)
+ afree(vname.rw, ATEMP);
return (vp);
}
@@ -298,8 +327,9 @@
struct tbl *
local(const char *n, bool copy)
{
+ struct tbl *vp;
+ union mksh_cchack vname;
struct block *l = e->loc;
- struct tbl *vp;
bool array;
uint32_t h, val;
@@ -307,20 +337,20 @@
* check to see if this is an array;
* dereference namerefs; must come first
*/
- n = array_index_calc(n, &array, &val);
- h = hash(n);
- if (!ksh_isalphx(*n)) {
- vp = &vtemp;
+ vn = array_index_calc(n, &array, &val);
+ h = hash(vn);
+ if (!ctype(*vn, C_ALPHX)) {
+ vp = vtemp;
vp->flag = DEFINED|RDONLY;
vp->type = 0;
vp->areap = ATEMP;
- return (vp);
+ goto out;
}
- vp = ktenter(&l->vars, n, h);
+ vp = ktenter(&l->vars, vn, h);
if (copy && !(vp->flag & DEFINED)) {
struct tbl *vq;
- varsearch(l->next, &vq, n, h);
+ varsearch(l->next, &vq, vn, h);
if (vq != NULL) {
vp->flag |= vq->flag &
(EXPORT | INTEGER | RDONLY | LJUST | RJUST |
@@ -333,10 +363,15 @@
if (array)
vp = arraysearch(vp, val);
vp->flag |= DEFINED;
- if (special(n))
+ if (special(vn))
vp->flag |= SPECIAL;
+ out:
+ last_lookup_was_array = array;
+ if (vn != n)
+ afree(vname.rw, ATEMP);
return (vp);
}
+#undef vn
/* get variable string value */
char *
@@ -379,9 +414,11 @@
*(s = strbuf) = '1';
s[1] = '#';
- if (!UTFMODE || ((n & 0xFF80) == 0xEF80))
+ if (!UTFMODE)
+ s[2] = (unsigned char)n;
+ else if ((n & 0xFF80) == 0xEF80)
/* OPTU-16 -> raw octet */
- s[2] = n & 0xFF;
+ s[2] = asc2rtt(n & 0xFF);
else
sz = utf_wctomb(s + 2, n);
s[2 + sz] = '\0';
@@ -418,7 +455,7 @@
error_ok &= ~0x4;
if ((vq->flag & RDONLY) && !no_ro_check) {
- warningf(true, "read-only: %s", vq->name);
+ warningf(true, Tf_ro, vq->name);
if (!error_ok)
errorfxz(2);
return (0);
@@ -429,7 +466,7 @@
#ifndef MKSH_SMALL
/* debugging */
if (s >= vq->val.s &&
- s <= vq->val.s + strlen(vq->val.s)) {
+ s <= strnul(vq->val.s)) {
internal_errorf(
"setstr: %s=%s: assigning to self",
vq->name, s);
@@ -464,13 +501,12 @@
setint(struct tbl *vq, mksh_ari_t n)
{
if (!(vq->flag&INTEGER)) {
- struct tbl *vp = &vtemp;
- vp->flag = (ISSET|INTEGER);
- vp->type = 0;
- vp->areap = ATEMP;
- vp->val.i = n;
+ vtemp->flag = (ISSET|INTEGER);
+ vtemp->type = 0;
+ vtemp->areap = ATEMP;
+ vtemp->val.i = n;
/* setstr can't fail here */
- setstr(vq, str_val(vp), KSH_RETURN_ERROR);
+ setstr(vq, str_val(vtemp), KSH_RETURN_ERROR);
} else
vq->val.i = n;
vq->flag |= ISSET;
@@ -481,41 +517,57 @@
static int
getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
{
- mksh_uari_t c, num, base;
+ mksh_uari_t c, num = 0, base = 10;
const char *s;
bool have_base = false, neg = false;
- if (vp->flag&SPECIAL)
+ if (vp->flag & SPECIAL)
getspec(vp);
/* XXX is it possible for ISSET to be set and val.s to be NULL? */
- if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
+ if (!(vp->flag & ISSET) || (!(vp->flag & INTEGER) && vp->val.s == NULL))
return (-1);
- if (vp->flag&INTEGER) {
+ if (vp->flag & INTEGER) {
nump->i = vp->val.i;
return (vp->type);
}
s = vp->val.s + vp->type;
- base = 10;
- num = 0;
- if (arith && s[0] == '0' && (s[1] | 0x20) == 'x') {
- s += 2;
- base = 16;
- have_base = true;
+
+ do {
+ c = (unsigned char)*s++;
+ } while (ctype(c, C_SPACE));
+
+ switch (c) {
+ case '-':
+ neg = true;
+ /* FALLTHROUGH */
+ case '+':
+ c = (unsigned char)*s++;
+ break;
}
- if (Flag(FPOSIX) && arith && s[0] == '0' && ksh_isdigit(s[1]) &&
- !(vp->flag & ZEROFIL)) {
- /* interpret as octal (deprecated) */
- base = 8;
- have_base = true;
+
+ if (c == '0' && arith) {
+ if (ksh_eq(s[0], 'X', 'x')) {
+ /* interpret as hexadecimal */
+ base = 16;
+ ++s;
+ goto getint_c_style_base;
+ } else if (Flag(FPOSIX) && ctype(s[0], C_DIGIT) &&
+ !(vp->flag & ZEROFIL)) {
+ /* interpret as octal (deprecated) */
+ base = 8;
+ getint_c_style_base:
+ have_base = true;
+ c = (unsigned char)*s++;
+ }
}
- while ((c = (unsigned char)*s++)) {
- if (c == '-') {
- neg = true;
- continue;
- } else if (c == '#') {
- if (have_base || num < 1 || num > 36)
+
+ do {
+ if (c == '#') {
+ /* ksh-style base determination */
+ if (have_base || num < 1)
return (-1);
if ((base = num) == 1) {
+ /* mksh-specific extension */
unsigned int wc;
if (!UTFMODE)
@@ -527,25 +579,29 @@
* the same as 1#\x80 does, thus is
* not round-tripping correctly XXX)
*/
- wc = 0xEF00 + *(const unsigned char *)s;
+ wc = 0xEF00 + rtt2asc(*s);
nump->u = (mksh_uari_t)wc;
return (1);
- }
+ } else if (base > 36)
+ base = 10;
num = 0;
have_base = true;
continue;
- } else if (ksh_isdigit(c))
- c -= '0';
- else if (ksh_islower(c))
- c -= 'a' - 10;
- else if (ksh_isupper(c))
- c -= 'A' - 10;
+ }
+ if (ctype(c, C_DIGIT))
+ c = ksh_numdig(c);
+ else if (ctype(c, C_UPPER))
+ c = ksh_numuc(c) + 10;
+ else if (ctype(c, C_LOWER))
+ c = ksh_numlc(c) + 10;
else
return (-1);
if (c >= base)
return (-1);
+ /* handle overflow as truncation */
num = num * base + c;
- }
+ } while ((c = (unsigned char)*s++));
+
if (neg)
num = -num;
nump->u = num;
@@ -607,16 +663,16 @@
p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
if (vp->flag & (RJUST|LJUST)) {
- int slen = olen, i = 0;
+ int slen = olen;
if (vp->flag & RJUST) {
- const char *qq = s;
+ const char *qq;
int n = 0;
- while (i < slen)
- i += utf_widthadj(qq, &qq);
+ qq = utf_skipcols(s, slen, &slen);
+
/* strip trailing spaces (AT&T uses qq[-1] == ' ') */
- while (qq > s && ksh_isspace(qq[-1])) {
+ while (qq > s && ctype(qq[-1], C_SPACE)) {
--qq;
--slen;
}
@@ -646,7 +702,7 @@
"%.*s", slen, s);
} else {
/* strip leading spaces/zeros */
- while (ksh_isspace(*s))
+ while (ctype(*s, C_SPACE))
s++;
if (vp->flag & ZEROFIL)
while (*s == '0')
@@ -690,8 +746,7 @@
/* offset to value */
vp->type = xp - vp->val.s;
memcpy(xp, val, vallen);
- if (op != NULL)
- afree(op, vp->areap);
+ afree(op, vp->areap);
}
/*
@@ -725,9 +780,9 @@
/* no variable name given */
return (NULL);
}
- if (*val == '[') {
+ if (ord(*val) == ORD('[')) {
if (new_refflag != SRF_NOP)
- errorf("%s: %s", var,
+ errorf(Tf_sD_s, var,
"reference variable can't be an array");
len = array_ref_len(val);
if (len == 0)
@@ -743,18 +798,18 @@
size_t i;
for (i = 1; i < len - 1; i++)
- if (!ksh_isdigit(val[i]))
+ if (!ctype(val[i], C_DIGIT))
return (NULL);
}
val += len;
}
- if (val[0] == '=') {
+ if (ord(val[0]) == ORD('=')) {
strndupx(tvar, var, val - var, ATEMP);
++val;
} else if (set & IMPORT) {
/* environment invalid variable name or no assignment */
return (NULL);
- } else if (val[0] == '+' && val[1] == '=') {
+ } else if (ord(val[0]) == ORD('+') && ord(val[1]) == ORD('=')) {
strndupx(tvar, var, val - var, ATEMP);
val += 2;
vappend = true;
@@ -767,8 +822,9 @@
val = NULL;
/* handle foo[*] => foo (whole array) mapping for R39b */
len = strlen(tvar);
- if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' &&
- tvar[len - 1] == ']')
+ if (len > 3 && ord(tvar[len - 3]) == ORD('[') &&
+ ord(tvar[len - 2]) == ORD('*') &&
+ ord(tvar[len - 1]) == ORD(']'))
tvar[len - 3] = '\0';
}
@@ -788,12 +844,13 @@
/* check target value for being a valid variable name */
ccp = skip_varname(qval, false);
if (ccp == qval) {
- if (ksh_isdigit(qval[0])) {
- int c;
+ int c;
- if (getn(qval, &c))
- goto nameref_rhs_checked;
- } else if (qval[1] == '\0') switch (qval[0]) {
+ if (!(c = (unsigned char)qval[0]))
+ goto nameref_empty;
+ else if (ctype(c, C_DIGIT) && getn(qval, &c))
+ goto nameref_rhs_checked;
+ else if (qval[1] == '\0') switch (c) {
case '$':
case '!':
case '?':
@@ -802,9 +859,9 @@
goto nameref_rhs_checked;
}
nameref_empty:
- errorf("%s: %s", var, "empty nameref target");
+ errorf(Tf_sD_s, var, "empty nameref target");
}
- len = (*ccp == '[') ? array_ref_len(ccp) : 0;
+ len = (ord(*ccp) == ORD('[')) ? array_ref_len(ccp) : 0;
if (ccp[len]) {
/*
* works for cases "no array", "valid array with
@@ -811,7 +868,7 @@
* junk after it" and "invalid array"; in the
* latter case, len is also 0 and points to '['
*/
- errorf("%s: %s", qval,
+ errorf(Tf_sD_s, qval,
"nameref target not a valid parameter name");
}
nameref_rhs_checked:
@@ -818,7 +875,7 @@
/* prevent nameref loops */
while (qval) {
if (!strcmp(qval, tvar))
- errorf("%s: %s", qval,
+ errorf(Tf_sD_s, qval,
"expression recurses on parameter");
varsearch(e->loc, &vp, qval, hash(qval));
qval = NULL;
@@ -828,9 +885,9 @@
}
/* prevent typeset from creating a local PATH/ENV/SHELL */
- if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
- strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
- errorf("%s: %s", tvar, "restricted");
+ if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
+ strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
+ errorf(Tf_sD_s, tvar, "restricted");
innermost_refflag = new_refflag;
vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
@@ -860,14 +917,14 @@
vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
/*
- * only allow export flag to be set; AT&T ksh allows any
- * attribute to be changed which means it can be truncated or
- * modified (-L/-R/-Z/-i)
+ * only allow export and readonly flag to be set; AT&T ksh
+ * allows any attribute to be changed which means it can be
+ * truncated or modified (-L/-R/-Z/-i)
*/
if ((vpbase->flag & RDONLY) &&
- (val || clr || (set & ~EXPORT)))
+ (val || clr || (set & ~(EXPORT | RDONLY))))
/* XXX check calls - is error here ok by POSIX? */
- errorfx(2, "read-only: %s", tvar);
+ errorfx(2, Tf_ro, tvar);
afree(tvar, ATEMP);
/* most calls are with set/clr == 0 */
@@ -929,8 +986,7 @@
t->type = 0;
}
}
- if (free_me)
- afree(free_me, t->areap);
+ afree(free_me, t->areap);
}
}
if (!ok)
@@ -941,7 +997,7 @@
char *tval;
if (vappend) {
- tval = shf_smprintf("%s%s", str_val(vp), val);
+ tval = shf_smprintf(Tf_ss, str_val(vp), val);
val = tval;
} else
tval = NULL;
@@ -956,8 +1012,7 @@
/* setstr can't fail (readonly check already done) */
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
- if (tval != NULL)
- afree(tval, ATEMP);
+ afree(tval, ATEMP);
}
/* only x[0] is ever exported, so use vpbase */
@@ -1012,10 +1067,11 @@
{
size_t alen;
- if (s && ksh_isalphx(*s)) {
- while (*++s && ksh_isalnux(*s))
- ;
- if (aok && *s == '[' && (alen = array_ref_len(s)))
+ if (s && ctype(*s, C_ALPHX)) {
+ do {
+ ++s;
+ } while (ctype(*s, C_ALNUX));
+ if (aok && ord(*s) == ORD('[') && (alen = array_ref_len(s)))
s += alen;
}
return (s);
@@ -1027,11 +1083,11 @@
/* skip array de-reference? */
bool aok)
{
- if (s[0] == CHAR && ksh_isalphx(s[1])) {
+ if (s[0] == CHAR && ctype(s[1], C_ALPHX)) {
do {
s += 2;
- } while (s[0] == CHAR && ksh_isalnux(s[1]));
- if (aok && s[0] == CHAR && s[1] == '[') {
+ } while (s[0] == CHAR && ctype(s[1], C_ALNUX));
+ if (aok && s[0] == CHAR && ord(s[1]) == ORD('[')) {
/* skip possible array de-reference */
const char *p = s;
char c;
@@ -1042,9 +1098,9 @@
break;
c = p[1];
p += 2;
- if (c == '[')
+ if (ord(c) == ORD('['))
depth++;
- else if (c == ']' && --depth == 0) {
+ else if (ord(c) == ORD(']') && --depth == 0) {
s = p;
break;
}
@@ -1109,6 +1165,13 @@
/* setstr can't fail here */
setstr(vp, val, KSH_RETURN_ERROR);
}
+#ifdef __OS2__
+ /* these special variables are not exported */
+ if (!strcmp(vp->name, "BEGINLIBPATH") ||
+ !strcmp(vp->name, "ENDLIBPATH") ||
+ !strcmp(vp->name, "LIBPATHSTRICT"))
+ continue;
+#endif
XPput(denv, vp->val.s);
}
if (l->flags & BF_STOPENV)
@@ -1144,8 +1207,12 @@
}
static time_t seconds; /* time SECONDS last set */
-static int user_lineno; /* what user set $LINENO to */
+static mksh_uari_t user_lineno; /* what user set $LINENO to */
+/* minimum values from the OS we consider sane, lowered for R53 */
+#define MIN_COLS 4
+#define MIN_LINS 2
+
static void
getspec(struct tbl *vp)
{
@@ -1178,7 +1245,7 @@
num.i = histsize;
break;
case V_LINENO:
- num.i = current_lineno + user_lineno;
+ num.u = (mksh_uari_t)current_lineno + user_lineno;
break;
case V_LINES:
num.i = x_lins;
@@ -1227,9 +1294,36 @@
{
mksh_ari_u num;
char *s;
- int st;
+ int st = special(vp->name);
- switch ((st = special(vp->name))) {
+#ifdef MKSH_DOSPATH
+ switch (st) {
+ case V_PATH:
+ case V_TMPDIR:
+#ifdef __OS2__
+ case V_BEGINLIBPATH:
+ case V_ENDLIBPATH:
+#endif
+ /* convert backslashes to slashes for convenience */
+ if (!(vp->flag&INTEGER)) {
+ s = str_val(vp);
+ do {
+ if (*s == ORD('\\'))
+ *s = '/';
+ } while (*s++);
+ }
+ break;
+ }
+#endif
+
+ switch (st) {
+#ifdef __OS2__
+ case V_BEGINLIBPATH:
+ case V_ENDLIBPATH:
+ case V_LIBPATHSTRICT:
+ setextlibpath(vp->name, str_val(vp));
+ return;
+#endif
#if HAVE_PERSISTENT_HISTORY
case V_HISTFILE:
sethistfile(str_val(vp));
@@ -1236,22 +1330,23 @@
return;
#endif
case V_IFS:
- setctypes(s = str_val(vp), C_IFS);
- ifs0 = *s;
+ set_ifs(str_val(vp));
return;
case V_PATH:
- if (path)
- afree(path, APERM);
+ afree(path, APERM);
s = str_val(vp);
strdupx(path, s, APERM);
/* clear tracked aliases */
flushcom(true);
return;
+#ifndef MKSH_NO_CMDLINE_EDITING
+ case V_TERM:
+ x_initterm(str_val(vp));
+ return;
+#endif
case V_TMPDIR:
- if (tmpdir) {
- afree(tmpdir, APERM);
- tmpdir = NULL;
- }
+ afree(tmpdir, APERM);
+ tmpdir = NULL;
/*
* Use tmpdir iff it is an absolute path, is writable
* and searchable and is a directory...
@@ -1261,7 +1356,7 @@
s = str_val(vp);
/* LINTED use of access */
- if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&
+ if (mksh_abspath(s) && access(s, W_OK|X_OK) == 0 &&
stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
strdupx(tmpdir, s, APERM);
}
@@ -1286,11 +1381,18 @@
if (getint(vp, &num, false) == -1) {
s = str_val(vp);
if (st != V_RANDOM)
- errorf("%s: %s: %s", vp->name, "bad number", s);
+ errorf(Tf_sD_sD_s, vp->name, Tbadnum, s);
num.u = hash(s);
}
vp->flag |= SPECIAL;
break;
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+ case V_LANG:
+ case V_LC_ALL:
+ case V_LC_CTYPE:
+ recheck_ctype();
+ return;
+#endif
default:
/* do nothing, do not touch vp at all */
return;
@@ -1308,7 +1410,7 @@
break;
case V_LINENO:
/* The -1 is because line numbering starts at 1. */
- user_lineno = num.u - current_lineno - 1;
+ user_lineno = num.u - (mksh_uari_t)current_lineno - 1;
break;
case V_LINES:
if (num.i >= MIN_LINS)
@@ -1350,17 +1452,32 @@
*/
switch (special(vp->name)) {
+#ifdef __OS2__
+ case V_BEGINLIBPATH:
+ case V_ENDLIBPATH:
+ case V_LIBPATHSTRICT:
+ setextlibpath(vp->name, "");
+ return;
+#endif
+#if HAVE_PERSISTENT_HISTORY
+ case V_HISTFILE:
+ sethistfile(NULL);
+ return;
+#endif
case V_IFS:
- setctypes(TC_IFSWS, C_IFS);
- ifs0 = ' ';
+ set_ifs(TC_IFSWS);
break;
case V_PATH:
- if (path)
- afree(path, APERM);
+ afree(path, APERM);
strdupx(path, def_path, APERM);
/* clear tracked aliases */
flushcom(true);
break;
+#ifndef MKSH_NO_CMDLINE_EDITING
+ case V_TERM:
+ x_initterm(null);
+ return;
+#endif
case V_TMPDIR:
/* should not become unspecial */
if (tmpdir) {
@@ -1375,6 +1492,13 @@
/* AT&T ksh leaves previous value in place */
unspecial(vp->name);
break;
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+ case V_LANG:
+ case V_LC_ALL:
+ case V_LC_CTYPE:
+ recheck_ctype();
+ return;
+#endif
}
}
@@ -1438,8 +1562,8 @@
char c;
int depth = 0;
- while ((c = *s++) && (c != ']' || --depth))
- if (c == '[')
+ while ((c = *s++) && (ord(c) != ORD(']') || --depth))
+ if (ord(c) == ORD('['))
depth++;
if (!c)
return (0);
@@ -1455,7 +1579,7 @@
const char *p;
char *rv;
- if ((p = cstrchr(str, '[')) == 0)
+ if (!(p = cstrchr(str, '[')))
/* Shouldn't happen, but why worry? */
strdupx(rv, str, ATEMP);
else
@@ -1486,7 +1610,7 @@
/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
if ((vp->flag&RDONLY))
- errorfx(2, "read-only: %s", ccp);
+ errorfx(2, Tf_ro, ccp);
/* This code is quite non-optimal */
if (reset) {
/* trash existing values and attributes */
@@ -1511,17 +1635,18 @@
}
while ((ccp = vals[i])) {
#if 0 /* temporarily taken out due to regression */
- if (*ccp == '[') {
+ if (ord(*ccp) == ORD('[')) {
int level = 0;
while (*ccp) {
- if (*ccp == ']' && --level == 0)
+ if (ord(*ccp) == ORD(']') && --level == 0)
break;
- if (*ccp == '[')
+ if (ord(*ccp) == ORD('['))
++level;
++ccp;
}
- if (*ccp == ']' && level == 0 && ccp[1] == '=') {
+ if (ord(*ccp) == ORD(']') && level == 0 &&
+ ord(ccp[1]) == ORD('=')) {
strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
ATEMP);
evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
@@ -1631,10 +1756,8 @@
short r;
} z;
-#ifdef DEBUG
- /* clear the allocated space, for valgrind */
+ /* clear the allocated space, for valgrind and to avoid UB */
memset(&z, 0, sizeof(z));
-#endif
h = lcg_state;
BAFHFinish_reg(h);
@@ -1684,3 +1807,393 @@
BAFHUpdateOctet_reg(h, 0);
qh_state = h;
}
+
+/* record last glob match */
+void
+record_match(const char *istr)
+{
+ struct tbl *vp;
+
+ vp = local("KSH_MATCH", false);
+ unset(vp, 1);
+ vp->flag = DEFINED | RDONLY;
+ setstr(vp, istr, 0x4);
+}
+
+/* typeset, global(deprecated), export, and readonly */
+int
+c_typeset(const char **wp)
+{
+ struct tbl *vp, **p;
+ uint32_t fset = 0, fclr = 0, flag;
+ int thing = 0, field = 0, base = 0, i;
+ struct block *l;
+ const char *opts;
+ const char *fieldstr = NULL, *basestr = NULL;
+ bool localv = false, func = false, pflag = false, istset = true;
+ enum namerefflag new_refflag = SRF_NOP;
+
+ switch (**wp) {
+
+ /* export */
+ case 'e':
+ fset |= EXPORT;
+ istset = false;
+ break;
+
+ /* readonly */
+ case 'r':
+ fset |= RDONLY;
+ istset = false;
+ break;
+
+ /* set */
+ case 's':
+ /* called with 'typeset -' */
+ break;
+
+ /* typeset */
+ case 't':
+ localv = true;
+ break;
+ }
+
+ /* see comment below regarding possible opions */
+ opts = istset ? "L#R#UZ#afgi#lnprtux" : "p";
+
+ builtin_opt.flags |= GF_PLUSOPT;
+ /*
+ * AT&T ksh seems to have 0-9 as options which are multiplied
+ * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
+ * sets right justify in a field of 12). This allows options
+ * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
+ * does not allow the number to be specified as a separate argument
+ * Here, the number must follow the RLZi option, but is optional
+ * (see the # kludge in ksh_getopt()).
+ */
+ while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
+ flag = 0;
+ switch (i) {
+ case 'L':
+ flag = LJUST;
+ fieldstr = builtin_opt.optarg;
+ break;
+ case 'R':
+ flag = RJUST;
+ fieldstr = builtin_opt.optarg;
+ break;
+ case 'U':
+ /*
+ * AT&T ksh uses u, but this conflicts with
+ * upper/lower case. If this option is changed,
+ * need to change the -U below as well
+ */
+ flag = INT_U;
+ break;
+ case 'Z':
+ flag = ZEROFIL;
+ fieldstr = builtin_opt.optarg;
+ break;
+ case 'a':
+ /*
+ * this is supposed to set (-a) or unset (+a) the
+ * indexed array attribute; it does nothing on an
+ * existing regular string or indexed array though
+ */
+ break;
+ case 'f':
+ func = true;
+ break;
+ case 'g':
+ localv = (builtin_opt.info & GI_PLUS) ? true : false;
+ break;
+ case 'i':
+ flag = INTEGER;
+ basestr = builtin_opt.optarg;
+ break;
+ case 'l':
+ flag = LCASEV;
+ break;
+ case 'n':
+ new_refflag = (builtin_opt.info & GI_PLUS) ?
+ SRF_DISABLE : SRF_ENABLE;
+ break;
+ /* export, readonly: POSIX -p flag */
+ case 'p':
+ /* typeset: show values as well */
+ pflag = true;
+ if (istset)
+ continue;
+ break;
+ case 'r':
+ flag = RDONLY;
+ break;
+ case 't':
+ flag = TRACE;
+ break;
+ case 'u':
+ /* upper case / autoload */
+ flag = UCASEV_AL;
+ break;
+ case 'x':
+ flag = EXPORT;
+ break;
+ case '?':
+ return (1);
+ }
+ if (builtin_opt.info & GI_PLUS) {
+ fclr |= flag;
+ fset &= ~flag;
+ thing = '+';
+ } else {
+ fset |= flag;
+ fclr &= ~flag;
+ thing = '-';
+ }
+ }
+
+ if (fieldstr && !getn(fieldstr, &field)) {
+ bi_errorf(Tf_sD_s, Tbadnum, fieldstr);
+ return (1);
+ }
+ if (basestr) {
+ if (!getn(basestr, &base)) {
+ bi_errorf(Tf_sD_s, "bad integer base", basestr);
+ return (1);
+ }
+ if (base < 1 || base > 36)
+ base = 10;
+ }
+
+ if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
+ (wp[builtin_opt.optind][0] == '-' ||
+ wp[builtin_opt.optind][0] == '+') &&
+ wp[builtin_opt.optind][1] == '\0') {
+ thing = wp[builtin_opt.optind][0];
+ builtin_opt.optind++;
+ }
+
+ if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
+ new_refflag != SRF_NOP)) {
+ bi_errorf("only -t, -u and -x options may be used with -f");
+ return (1);
+ }
+ if (wp[builtin_opt.optind]) {
+ /*
+ * Take care of exclusions.
+ * At this point, flags in fset are cleared in fclr and vice
+ * versa. This property should be preserved.
+ */
+ if (fset & LCASEV)
+ /* LCASEV has priority over UCASEV_AL */
+ fset &= ~UCASEV_AL;
+ if (fset & LJUST)
+ /* LJUST has priority over RJUST */
+ fset &= ~RJUST;
+ if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
+ /* -Z implies -ZR */
+ fset |= RJUST;
+ fclr &= ~RJUST;
+ }
+ /*
+ * Setting these attributes clears the others, unless they
+ * are also set in this command
+ */
+ if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
+ INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
+ fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
+ LCASEV | INTEGER | INT_U | INT_L);
+ }
+ if (new_refflag != SRF_NOP) {
+ fclr &= ~(ARRAY | ASSOC);
+ fset &= ~(ARRAY | ASSOC);
+ fclr |= EXPORT;
+ fset |= ASSOC;
+ if (new_refflag == SRF_DISABLE)
+ fclr |= ASSOC;
+ }
+
+ /* set variables and attributes */
+ if (wp[builtin_opt.optind] &&
+ /* not "typeset -p varname" */
+ !(!func && pflag && !(fset | fclr))) {
+ int rv = 0;
+ struct tbl *f;
+
+ if (localv && !func)
+ fset |= LOCAL;
+ for (i = builtin_opt.optind; wp[i]; i++) {
+ if (func) {
+ f = findfunc(wp[i], hash(wp[i]),
+ tobool(fset & UCASEV_AL));
+ if (!f) {
+ /* AT&T ksh does ++rv: bogus */
+ rv = 1;
+ continue;
+ }
+ if (fset | fclr) {
+ f->flag |= fset;
+ f->flag &= ~fclr;
+ } else {
+ fpFUNCTf(shl_stdout, 0,
+ tobool(f->flag & FKSH),
+ wp[i], f->val.t);
+ shf_putc('\n', shl_stdout);
+ }
+ } else if (!typeset(wp[i], fset, fclr, field, base)) {
+ bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
+ return (1);
+ }
+ }
+ return (rv);
+ }
+
+ /* list variables and attributes */
+
+ /* no difference at this point.. */
+ flag = fset | fclr;
+ if (func) {
+ for (l = e->loc; l; l = l->next) {
+ for (p = ktsort(&l->funs); (vp = *p++); ) {
+ if (flag && (vp->flag & flag) == 0)
+ continue;
+ if (thing == '-')
+ fpFUNCTf(shl_stdout, 0,
+ tobool(vp->flag & FKSH),
+ vp->name, vp->val.t);
+ else
+ shf_puts(vp->name, shl_stdout);
+ shf_putc('\n', shl_stdout);
+ }
+ }
+ } else if (wp[builtin_opt.optind]) {
+ for (i = builtin_opt.optind; wp[i]; i++) {
+ vp = isglobal(wp[i], false);
+ c_typeset_vardump(vp, flag, thing,
+ last_lookup_was_array ? 4 : 0, pflag, istset);
+ }
+ } else
+ c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
+ return (0);
+}
+
+static void
+c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
+ bool pflag, bool istset)
+{
+ struct tbl **blockvars, *vp;
+
+ if (l->next)
+ c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
+ blockvars = ktsort(&l->vars);
+ while ((vp = *blockvars++))
+ c_typeset_vardump(vp, flag, thing, 0, pflag, istset);
+ /*XXX doesn’t this leak? */
+}
+
+static void
+c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set,
+ bool pflag, bool istset)
+{
+ struct tbl *tvp;
+ char *s;
+
+ if (!vp)
+ return;
+
+ /*
+ * See if the parameter is set (for arrays, if any
+ * element is set).
+ */
+ for (tvp = vp; tvp; tvp = tvp->u.array)
+ if (tvp->flag & ISSET) {
+ any_set |= 1;
+ break;
+ }
+
+ /*
+ * Check attributes - note that all array elements
+ * have (should have?) the same attributes, so checking
+ * the first is sufficient.
+ *
+ * Report an unset param only if the user has
+ * explicitly given it some attribute (like export);
+ * otherwise, after "echo $FOO", we would report FOO...
+ */
+ if (!any_set && !(vp->flag & USERATTRIB))
+ return;
+ if (flag && (vp->flag & flag) == 0)
+ return;
+ if (!(vp->flag & ARRAY))
+ /* optimise later conditionals */
+ any_set = 0;
+ do {
+ /*
+ * Ignore array elements that aren't set unless there
+ * are no set elements, in which case the first is
+ * reported on
+ */
+ if (any_set && !(vp->flag & ISSET))
+ continue;
+ /* no arguments */
+ if (!thing && !flag) {
+ if (any_set == 1) {
+ shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
+ any_set = 2;
+ }
+ /*
+ * AT&T ksh prints things like export, integer,
+ * leftadj, zerofill, etc., but POSIX says must
+ * be suitable for re-entry...
+ */
+ shprintf(Tf_s_s, Ttypeset, "");
+ if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
+ shprintf(Tf__c_, 'n');
+ if ((vp->flag & INTEGER))
+ shprintf(Tf__c_, 'i');
+ if ((vp->flag & EXPORT))
+ shprintf(Tf__c_, 'x');
+ if ((vp->flag & RDONLY))
+ shprintf(Tf__c_, 'r');
+ if ((vp->flag & TRACE))
+ shprintf(Tf__c_, 't');
+ if ((vp->flag & LJUST))
+ shprintf("-L%d ", vp->u2.field);
+ if ((vp->flag & RJUST))
+ shprintf("-R%d ", vp->u2.field);
+ if ((vp->flag & ZEROFIL))
+ shprintf(Tf__c_, 'Z');
+ if ((vp->flag & LCASEV))
+ shprintf(Tf__c_, 'l');
+ if ((vp->flag & UCASEV_AL))
+ shprintf(Tf__c_, 'u');
+ if ((vp->flag & INT_U))
+ shprintf(Tf__c_, 'U');
+ } else if (pflag) {
+ shprintf(Tf_s_s, istset ? Ttypeset :
+ (flag & EXPORT) ? Texport : Treadonly, "");
+ }
+ if (any_set)
+ shprintf("%s[%lu]", vp->name, arrayindex(vp));
+ else
+ shf_puts(vp->name, shl_stdout);
+ if ((!thing && !flag && pflag) ||
+ (thing == '-' && (vp->flag & ISSET))) {
+ s = str_val(vp);
+ shf_putc('=', shl_stdout);
+ /* AT&T ksh can't have justified integers... */
+ if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
+ shf_puts(s, shl_stdout);
+ else
+ print_value_quoted(shl_stdout, s);
+ }
+ shf_putc('\n', shl_stdout);
+
+ /*
+ * Only report first 'element' of an array with
+ * no set elements.
+ */
+ if (!any_set)
+ return;
+ } while (!(any_set & 4) && (vp = vp->u.array));
+}
Modified: trunk/contrib/mksh/var_spec.h
===================================================================
--- trunk/contrib/mksh/var_spec.h 2019-01-18 20:59:41 UTC (rev 12140)
+++ trunk/contrib/mksh/var_spec.h 2019-01-18 21:15:37 UTC (rev 12141)
@@ -1,5 +1,25 @@
+/*-
+ * Copyright (c) 2009, 2011, 2012, 2016, 2018
+ * mirabilos <m at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
#if defined(VARSPEC_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.6 2012/11/30 16:45:25 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.11 2018/01/13 21:38:10 tg Exp $");
#define FN(name) /* nothing */
#elif defined(VARSPEC_ENUMS)
#define FN(name) V_##name,
@@ -20,7 +40,13 @@
/* 1 and up are special variables */
FN(BASHPID)
+#ifdef __OS2__
+FN(BEGINLIBPATH)
+#endif
FN(COLUMNS)
+#ifdef __OS2__
+FN(ENDLIBPATH)
+#endif
FN(EPOCHREALTIME)
#if HAVE_PERSISTENT_HISTORY
FN(HISTFILE)
@@ -27,6 +53,14 @@
#endif
FN(HISTSIZE)
FN(IFS)
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+FN(LANG)
+FN(LC_ALL)
+FN(LC_CTYPE)
+#endif
+#ifdef __OS2__
+FN(LIBPATHSTRICT)
+#endif
FN(LINENO)
FN(LINES)
FN(OPTIND)
@@ -33,6 +67,9 @@
FN(PATH)
FN(RANDOM)
FN(SECONDS)
+#ifndef MKSH_NO_CMDLINE_EDITING
+FN(TERM)
+#endif
FN(TMOUT)
FN(TMPDIR)
More information about the Midnightbsd-cvs
mailing list