Merge "Upgrade to mksh R59."
This commit is contained in:
commit
f54e593e39
30 changed files with 3668 additions and 2008 deletions
|
@ -107,7 +107,7 @@ cc_defaults {
|
|||
"-DHAVE_SYS_ERRLIST_DECL=0",
|
||||
"-DHAVE_SYS_SIGLIST_DECL=1",
|
||||
"-DHAVE_PERSISTENT_HISTORY=0",
|
||||
"-DMKSH_BUILD_R=571",
|
||||
"-DMKSH_BUILD_R=591",
|
||||
|
||||
// Additional flags
|
||||
"-DMKSH_DEFAULT_PROFILEDIR=\"/system/etc\"",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff -ru mksh-R57/funcs.c src/funcs.c
|
||||
--- mksh-R57/funcs.c 2018-10-20 14:04:55.000000000 -0700
|
||||
+++ src/funcs.c 2019-03-26 12:05:23.976821773 -0700
|
||||
@@ -103,7 +103,9 @@
|
||||
diff -u mksh-R59/funcs.c src/funcs.c
|
||||
--- mksh-R59/funcs.c 2020-04-13 12:51:34.000000000 -0700
|
||||
+++ src/funcs.c 2020-05-15 16:10:35.035013192 -0700
|
||||
@@ -104,7 +104,9 @@
|
||||
{Tsgbreak, c_brkcont},
|
||||
{T__builtin, c_builtin},
|
||||
{Tbuiltin, c_builtin},
|
||||
|
@ -11,7 +11,7 @@ diff -ru mksh-R57/funcs.c src/funcs.c
|
|||
{Tcd, c_cd},
|
||||
/* dash compatibility hack */
|
||||
{"chdir", c_cd},
|
||||
@@ -126,7 +128,9 @@
|
||||
@@ -125,7 +127,9 @@
|
||||
{"pwd", c_pwd},
|
||||
{Tread, c_read},
|
||||
{Tdsgreadonly, c_typeset},
|
||||
|
@ -20,8 +20,8 @@ diff -ru mksh-R57/funcs.c src/funcs.c
|
|||
+#endif
|
||||
{"~rename", c_rename},
|
||||
{"*=return", c_exitreturn},
|
||||
{Tsgset, c_set},
|
||||
@@ -160,8 +164,10 @@
|
||||
{Tsghset, c_set},
|
||||
@@ -159,8 +163,10 @@
|
||||
{"~printf", c_printf},
|
||||
#endif
|
||||
#if HAVE_SELECT
|
||||
|
@ -32,10 +32,11 @@ diff -ru mksh-R57/funcs.c src/funcs.c
|
|||
#ifdef __MirBSD__
|
||||
/* alias to "true" for historical reasons */
|
||||
{"domainname", c_true},
|
||||
diff -ru mksh-R57/main.c src/main.c
|
||||
--- mksh-R57/main.c 2019-01-05 05:24:45.000000000 -0800
|
||||
+++ src/main.c 2019-03-26 12:05:23.980821764 -0700
|
||||
@@ -399,6 +399,12 @@
|
||||
Only in src: funcs.c.orig
|
||||
diff -u mksh-R59/main.c src/main.c
|
||||
--- mksh-R59/main.c 2020-04-13 10:04:41.000000000 -0700
|
||||
+++ src/main.c 2020-05-15 16:10:35.035013192 -0700
|
||||
@@ -402,6 +402,12 @@
|
||||
/* import environment */
|
||||
init_environ();
|
||||
|
||||
|
@ -48,3 +49,4 @@ diff -ru mksh-R57/main.c src/main.c
|
|||
/* for security */
|
||||
typeset(TinitIFS, 0, 0, 0, 0);
|
||||
|
||||
Only in src: main.c.orig
|
||||
|
|
212
src/Build.sh
212
src/Build.sh
|
@ -1,8 +1,9 @@
|
|||
#!/bin/sh
|
||||
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
|
||||
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.755 2020/04/07 23:15:11 tg Exp $'
|
||||
#-
|
||||
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
|
||||
# 2020
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -25,8 +26,8 @@ srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
|
|||
#
|
||||
# Used environment documentation is at the end of this file.
|
||||
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
LC_ALL=C; LANGUAGE=C
|
||||
export LC_ALL; unset LANGUAGE
|
||||
|
||||
case $ZSH_VERSION:$VERSION in
|
||||
:zsh*) ZSH_VERSION=2 ;;
|
||||
|
@ -53,6 +54,15 @@ alll=qwertyuiopasdfghjklzxcvbnm
|
|||
alln=0123456789
|
||||
alls=______________________________________________________________
|
||||
|
||||
test_n() {
|
||||
test x"$1" = x"" || return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
test_z() {
|
||||
test x"$1" = x""
|
||||
}
|
||||
|
||||
case `echo a | tr '\201' X` in
|
||||
X)
|
||||
# EBCDIC build system
|
||||
|
@ -64,11 +74,11 @@ X)
|
|||
esac
|
||||
|
||||
genopt_die() {
|
||||
if test -n "$1"; then
|
||||
echo >&2 "E: $*"
|
||||
echo >&2 "E: in '$srcfile': '$line'"
|
||||
else
|
||||
if test_z "$1"; then
|
||||
echo >&2 "E: invalid input in '$srcfile': '$line'"
|
||||
else
|
||||
echo >&2 "E: $*"
|
||||
echo >&2 "N: in '$srcfile': '$line'"
|
||||
fi
|
||||
rm -f "$bn.gen"
|
||||
exit 1
|
||||
|
@ -172,9 +182,9 @@ do_genopt() {
|
|||
esac
|
||||
IFS= read line || genopt_die Unexpected EOF
|
||||
IFS=$safeIFS
|
||||
test -n "$cond" && o_gen=$o_gen$nl"$cond"
|
||||
test_z "$cond" || o_gen=$o_gen$nl"$cond"
|
||||
o_gen=$o_gen$nl"$line, $optc)"
|
||||
test -n "$cond" && o_gen=$o_gen$nl"#endif"
|
||||
test_z "$cond" || o_gen=$o_gen$nl"#endif"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
@ -185,11 +195,11 @@ do_genopt() {
|
|||
esac
|
||||
echo "$o_str" | sort | while IFS='|' read x opts cond; do
|
||||
IFS=$safeIFS
|
||||
test -n "$x" || continue
|
||||
test_n "$x" || continue
|
||||
genopt_scond
|
||||
test -n "$cond" && echo "$cond"
|
||||
test_z "$cond" || echo "$cond"
|
||||
echo "\"$opts\""
|
||||
test -n "$cond" && echo "#endif"
|
||||
test_z "$cond" || echo "#endif"
|
||||
done | {
|
||||
echo "$o_hdr"
|
||||
echo "#ifndef $o_sym$o_gen"
|
||||
|
@ -234,7 +244,7 @@ vq() {
|
|||
rmf() {
|
||||
for _f in "$@"; do
|
||||
case $_f in
|
||||
Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;;
|
||||
*.1|*.faq|*.ico) ;;
|
||||
*) rm -f "$_f" ;;
|
||||
esac
|
||||
done
|
||||
|
@ -343,7 +353,7 @@ ac_testnnd() {
|
|||
test $ct = pcc && vscan='unsupported'
|
||||
test $ct = sunpro && vscan='-e ignored -e turned.off'
|
||||
fi
|
||||
test -n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
|
||||
test_n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
|
||||
return 0
|
||||
}
|
||||
ac_testn() {
|
||||
|
@ -406,10 +416,8 @@ ac_flags() {
|
|||
test x"$ft" = x"" && ft="if $f can be used"
|
||||
save_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS $f"
|
||||
if test -n "$fl"; then
|
||||
save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS $fl"
|
||||
fi
|
||||
save_LDFLAGS=$LDFLAGS
|
||||
test_z "$fl" || LDFLAGS="$LDFLAGS $fl"
|
||||
if test 1 = $hf; then
|
||||
ac_testn can_$vn '' "$ft"
|
||||
else
|
||||
|
@ -421,9 +429,7 @@ ac_flags() {
|
|||
#'
|
||||
fi
|
||||
eval fv=\$HAVE_CAN_`upper $vn`
|
||||
if test -n "$fl"; then
|
||||
test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
|
||||
fi
|
||||
test_z "$fl" || test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
|
||||
test 11 = $fa$fv || CFLAGS=$save_CFLAGS
|
||||
}
|
||||
|
||||
|
@ -512,7 +518,7 @@ ebcdic=false
|
|||
for i
|
||||
do
|
||||
case $last:$i in
|
||||
c:combine|c:dragonegg|c:llvm|c:lto)
|
||||
c:dragonegg|c:llvm)
|
||||
cm=$i
|
||||
last=
|
||||
;;
|
||||
|
@ -524,10 +530,6 @@ do
|
|||
optflags=$i
|
||||
last=
|
||||
;;
|
||||
t:*)
|
||||
tfn=$i
|
||||
last=
|
||||
;;
|
||||
:-c)
|
||||
last=c
|
||||
;;
|
||||
|
@ -573,9 +575,6 @@ do
|
|||
:+T)
|
||||
textmode=0
|
||||
;;
|
||||
:-t)
|
||||
last=t
|
||||
;;
|
||||
:-v)
|
||||
echo "Build.sh $srcversion"
|
||||
echo "for mksh $dstversion"
|
||||
|
@ -591,12 +590,12 @@ do
|
|||
;;
|
||||
esac
|
||||
done
|
||||
if test -n "$last"; then
|
||||
if test_n "$last"; then
|
||||
echo "$me: Option -'$last' not followed by argument!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -z "$tfn" && if test $legacy = 0; then
|
||||
test_n "$tfn" || if test $legacy = 0; then
|
||||
tfn=mksh
|
||||
else
|
||||
tfn=lksh
|
||||
|
@ -606,7 +605,7 @@ if test -d $tfn || test -d $tfn.exe; then
|
|||
exit 1
|
||||
fi
|
||||
rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \
|
||||
*.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out
|
||||
*.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out *.htm
|
||||
|
||||
SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
|
||||
SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
|
||||
|
@ -634,17 +633,17 @@ if test x"$srcdir" = x"."; then
|
|||
else
|
||||
CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS"
|
||||
fi
|
||||
test -n "$LDSTATIC" && if test -n "$LDFLAGS"; then
|
||||
LDFLAGS="$LDFLAGS $LDSTATIC"
|
||||
else
|
||||
test_z "$LDSTATIC" || if test_z "$LDFLAGS"; then
|
||||
LDFLAGS=$LDSTATIC
|
||||
else
|
||||
LDFLAGS="$LDFLAGS $LDSTATIC"
|
||||
fi
|
||||
|
||||
if test -z "$TARGET_OS"; then
|
||||
if test_z "$TARGET_OS"; then
|
||||
x=`uname -s 2>/dev/null || uname`
|
||||
test x"$x" = x"`uname -n 2>/dev/null`" || TARGET_OS=$x
|
||||
fi
|
||||
if test -z "$TARGET_OS"; then
|
||||
if test_z "$TARGET_OS"; then
|
||||
echo "$me: Set TARGET_OS, your uname is broken!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
@ -652,7 +651,7 @@ oswarn=
|
|||
ccpc=-Wc,
|
||||
ccpl=-Wl,
|
||||
tsts=
|
||||
ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;; *) rm -f "$_f" ;; esac; done'
|
||||
ccpr='|| for _f in ${tcfn}*; do case $_f in *.1|*.faq|*.ico) ;; *) rm -f "$_f" ;; esac; done'
|
||||
|
||||
# Evil hack
|
||||
if test x"$TARGET_OS" = x"Android"; then
|
||||
|
@ -706,12 +705,12 @@ fi
|
|||
# Configuration depending on OS revision, on OSes that need them
|
||||
case $TARGET_OS in
|
||||
NEXTSTEP)
|
||||
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`hostinfo 2>&1 | \
|
||||
test_n "$TARGET_OSREV" || TARGET_OSREV=`hostinfo 2>&1 | \
|
||||
grep 'NeXT Mach [0-9][0-9.]*:' | \
|
||||
sed 's/^.*NeXT Mach \([0-9][0-9.]*\):.*$/\1/'`
|
||||
;;
|
||||
QNX|SCO_SV)
|
||||
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
|
||||
test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -847,6 +846,11 @@ Linux)
|
|||
LynxOS)
|
||||
oswarn="; it has minor issues"
|
||||
;;
|
||||
midipix)
|
||||
add_cppflags -D_GNU_SOURCE
|
||||
# their Perl (currently…) identifies as os:linux ☹
|
||||
check_categories="$check_categories os:midipix"
|
||||
;;
|
||||
MidnightBSD)
|
||||
;;
|
||||
Minix-vmd)
|
||||
|
@ -1037,11 +1041,11 @@ _svr4)
|
|||
# generic target for SVR4 Unix with uname -s = uname -n
|
||||
# this duplicates the * target below
|
||||
oswarn='; it may or may not work'
|
||||
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
|
||||
test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
|
||||
;;
|
||||
*)
|
||||
oswarn='; it may or may not work'
|
||||
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
|
||||
test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -1084,11 +1088,12 @@ SCO_SV|UnixWare|UNIX_SV)
|
|||
vv '|' "uname -a >&2"
|
||||
;;
|
||||
esac
|
||||
test -z "$oswarn" || echo >&2 "
|
||||
test_z "$oswarn" || echo >&2 "
|
||||
Warning: mksh has not yet been ported to or tested on your
|
||||
operating system '$TARGET_OS'$oswarn. If you can provide
|
||||
a shell account to the developer, this may improve; please
|
||||
drop us a success or failure notice or even send in diffs.
|
||||
drop us a success or failure notice or even send in diffs,
|
||||
at the very least, complete logs (Build.sh + test.sh) will help.
|
||||
"
|
||||
$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
|
||||
|
||||
|
@ -1115,6 +1120,10 @@ ct="icc"
|
|||
ct="xlc"
|
||||
#elif defined(__SUNPRO_C)
|
||||
ct="sunpro"
|
||||
#elif defined(__neatcc__)
|
||||
ct="neatcc"
|
||||
#elif defined(__lacc__)
|
||||
ct="lacc"
|
||||
#elif defined(__ACK__)
|
||||
ct="ack"
|
||||
#elif defined(__BORLANDC__)
|
||||
|
@ -1230,9 +1239,8 @@ dmc)
|
|||
;;
|
||||
gcc)
|
||||
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
|
||||
vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
|
||||
-dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
|
||||
$LIBS -dumpversion`'
|
||||
vv '|' 'eval echo "\`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpmachine\`" \
|
||||
"gcc\`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpversion\`"'
|
||||
: "${HAVE_STRING_POOLING=i2}"
|
||||
;;
|
||||
hpcc)
|
||||
|
@ -1250,6 +1258,9 @@ icc)
|
|||
kencc)
|
||||
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
|
||||
;;
|
||||
lacc)
|
||||
# no version information
|
||||
;;
|
||||
lcc)
|
||||
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
|
||||
add_cppflags -D__inline__=__inline
|
||||
|
@ -1266,21 +1277,27 @@ msc)
|
|||
ccpr= # errorlevels are not reliable
|
||||
case $TARGET_OS in
|
||||
Interix)
|
||||
if [[ -n $C89_COMPILER ]]; then
|
||||
C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
|
||||
else
|
||||
if test_z "$C89_COMPILER"; then
|
||||
C89_COMPILER=CL.EXE
|
||||
fi
|
||||
if [[ -n $C89_LINKER ]]; then
|
||||
C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
|
||||
else
|
||||
C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
|
||||
fi
|
||||
if test_z "$C89_LINKER"; then
|
||||
C89_LINKER=LINK.EXE
|
||||
else
|
||||
C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
|
||||
fi
|
||||
vv '|' "$C89_COMPILER /HELP >&2"
|
||||
vv '|' "$C89_LINKER /LINK >&2"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
neatcc)
|
||||
add_cppflags -DMKSH_DONT_EMIT_IDSTRING
|
||||
add_cppflags -DMKSH_NO_SIGSETJMP
|
||||
add_cppflags -Dsig_atomic_t=int
|
||||
vv '|' "$CC"
|
||||
;;
|
||||
nwcc)
|
||||
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
|
||||
;;
|
||||
|
@ -1333,7 +1350,7 @@ xlc)
|
|||
*)
|
||||
test x"$ct" = x"untested" && $e "!!! detecting preprocessor failed"
|
||||
ct=unknown
|
||||
vv "$CC --version"
|
||||
vv '|' "$CC --version"
|
||||
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
|
||||
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
|
||||
;;
|
||||
|
@ -1376,14 +1393,14 @@ if ac_ifcpp 'if 0' compiler_fails '' \
|
|||
case $ct in
|
||||
dec)
|
||||
CFLAGS="$CFLAGS ${ccpl}-non_shared"
|
||||
ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF
|
||||
ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-'EOF'
|
||||
#include <unistd.h>
|
||||
int main(void) { return (isatty(0)); }
|
||||
EOF
|
||||
;;
|
||||
dmc)
|
||||
CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE"
|
||||
ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF
|
||||
ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-'EOF'
|
||||
#include <unistd.h>
|
||||
int main(void) { return (isatty(0)); }
|
||||
EOF
|
||||
|
@ -1393,9 +1410,8 @@ if ac_ifcpp 'if 0' compiler_fails '' \
|
|||
;;
|
||||
esac
|
||||
test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS
|
||||
ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF
|
||||
EOF
|
||||
test 1 = $HAVE_COMPILER_STILL_FAILS && exit 1
|
||||
ac_ifcpp 'if 0' compiler_still_fails \
|
||||
'if the compiler still does not fail correctly' && exit 1
|
||||
fi
|
||||
if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \
|
||||
'if this could be tcc'; then
|
||||
|
@ -1478,7 +1494,7 @@ NOWARN=$save_NOWARN
|
|||
#
|
||||
i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln`
|
||||
# optimisation: only if orig_CFLAGS is empty
|
||||
test x"$i" = x"" && case $ct in
|
||||
test_n "$i" || case $ct in
|
||||
hpcc)
|
||||
phase=u
|
||||
ac_flags 1 otwo +O2
|
||||
|
@ -1526,6 +1542,7 @@ dmc)
|
|||
ac_flags 1 schk "${ccpc}-s" 'for stack overflow checking'
|
||||
;;
|
||||
gcc)
|
||||
ac_flags 1 fnolto -fno-lto 'whether we can explicitly disable buggy GCC LTO' -fno-lto
|
||||
# The following tests run with -Werror (gcc only) if possible
|
||||
NOWARN=$DOWARN; phase=u
|
||||
ac_flags 1 wnodeprecateddecls -Wno-deprecated-declarations
|
||||
|
@ -1708,7 +1725,7 @@ ac_test attribute_format '' 'for __attribute__((__format__))' <<-'EOF'
|
|||
#undef fprintf
|
||||
extern int fprintf(FILE *, const char *format, ...)
|
||||
__attribute__((__format__(__printf__, 2, 3)));
|
||||
int main(int ac, char **av) { return (fprintf(stderr, "%s%d", *av, ac)); }
|
||||
int main(int ac, char *av[]) { return (fprintf(stderr, "%s%d", *av, ac)); }
|
||||
#endif
|
||||
EOF
|
||||
ac_test attribute_noreturn '' 'for __attribute__((__noreturn__))' <<-'EOF'
|
||||
|
@ -1733,7 +1750,7 @@ ac_test attribute_pure '' 'for __attribute__((__pure__))' <<-'EOF'
|
|||
#include <unistd.h>
|
||||
#undef __attribute__
|
||||
int foo(const char *) __attribute__((__pure__));
|
||||
int main(int ac, char **av) { return (foo(av[ac - 1]) + isatty(0)); }
|
||||
int main(int ac, char *av[]) { return (foo(av[ac - 1]) + isatty(0)); }
|
||||
int foo(const char *s) { return ((int)s[0]); }
|
||||
#endif
|
||||
EOF
|
||||
|
@ -1745,7 +1762,7 @@ ac_test attribute_unused '' 'for __attribute__((__unused__))' <<-'EOF'
|
|||
#else
|
||||
#include <unistd.h>
|
||||
#undef __attribute__
|
||||
int main(int ac __attribute__((__unused__)), char **av
|
||||
int main(int ac __attribute__((__unused__)), char *av[]
|
||||
__attribute__((__unused__))) { return (isatty(0)); }
|
||||
#endif
|
||||
EOF
|
||||
|
@ -1863,22 +1880,22 @@ rmf lft* # end of large file support test
|
|||
ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF'
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h>
|
||||
int main(int ac, char **av) { return ((uint32_t)(size_t)*av + (int32_t)ac); }
|
||||
int main(int ac, char *av[]) { return ((uint32_t)(size_t)*av + (int32_t)ac); }
|
||||
EOF
|
||||
ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF'
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h>
|
||||
int main(int ac, char **av) { return ((u_int32_t)(size_t)*av + (int32_t)ac); }
|
||||
int main(int ac, char *av[]) { return ((u_int32_t)(size_t)*av + (int32_t)ac); }
|
||||
EOF
|
||||
ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF'
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h>
|
||||
int main(int ac, char **av) { return ((uint8_t)(size_t)av[ac]); }
|
||||
int main(int ac, char *av[]) { return ((uint8_t)(size_t)av[ac]); }
|
||||
EOF
|
||||
ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF'
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h>
|
||||
int main(int ac, char **av) { return ((u_int8_t)(size_t)av[ac]); }
|
||||
int main(int ac, char *av[]) { return ((u_int8_t)(size_t)av[ac]); }
|
||||
EOF
|
||||
|
||||
ac_test rlim_t <<-'EOF'
|
||||
|
@ -1948,7 +1965,11 @@ else
|
|||
#define MKSH_INCLUDES_ONLY
|
||||
#include "sh.h"
|
||||
__RCSID("$srcversion");
|
||||
int main(void) { printf("Hello, World!\\n"); return (isatty(0)); }
|
||||
int main(void) {
|
||||
struct timeval tv;
|
||||
printf("Hello, World!\\n");
|
||||
return (time(&tv.tv_sec));
|
||||
}
|
||||
EOF
|
||||
case $cm in
|
||||
llvm)
|
||||
|
@ -2005,15 +2026,15 @@ ac_cppflags SYS_ERRLIST
|
|||
|
||||
for what in name list; do
|
||||
uwhat=`upper $what`
|
||||
ac_testn sys_sig$what '' "the sys_sig${what}[] array" <<-EOF
|
||||
extern const char * const sys_sig${what}[];
|
||||
ac_testn sys_sig$what '' "the sys_sig$what[] array" <<-EOF
|
||||
extern const char * const sys_sig$what[];
|
||||
extern int isatty(int);
|
||||
int main(void) { return (sys_sig${what}[0][0] + isatty(0)); }
|
||||
int main(void) { return (sys_sig$what[0][0] + isatty(0)); }
|
||||
EOF
|
||||
ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig${what}[] array" <<-EOF
|
||||
extern const char * const _sys_sig${what}[];
|
||||
ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig$what[] array" <<-EOF
|
||||
extern const char * const _sys_sig$what[];
|
||||
extern int isatty(int);
|
||||
int main(void) { return (_sys_sig${what}[0][0] + isatty(0)); }
|
||||
int main(void) { return (_sys_sig$what[0][0] + isatty(0)); }
|
||||
EOF
|
||||
eval uwhat_v=\$HAVE__SYS_SIG$uwhat
|
||||
if test 1 = "$uwhat_v"; then
|
||||
|
@ -2265,6 +2286,17 @@ ac_test sys_siglist_decl sys_siglist 0 'for declaration of sys_siglist[]' <<-'EO
|
|||
int main(void) { return (sys_siglist[0][0] + isatty(0)); }
|
||||
EOF
|
||||
|
||||
ac_test st_mtim '' 'for struct stat.st_mtim.tv_nsec' <<-'EOF'
|
||||
#define MKSH_INCLUDES_ONLY
|
||||
#include "sh.h"
|
||||
int main(void) { struct stat sb; return (sizeof(sb.st_mtim.tv_nsec)); }
|
||||
EOF
|
||||
ac_test st_mtimensec '!' st_mtim 0 'for struct stat.st_mtimensec' <<-'EOF'
|
||||
#define MKSH_INCLUDES_ONLY
|
||||
#include "sh.h"
|
||||
int main(void) { struct stat sb; return (sizeof(sb.st_mtimensec)); }
|
||||
EOF
|
||||
|
||||
#
|
||||
# other checks
|
||||
#
|
||||
|
@ -2287,7 +2319,7 @@ if test $legacy = 1; then
|
|||
#define CHAR_BIT 0
|
||||
#endif
|
||||
struct ctasserts {
|
||||
#define cta(name, assertion) char name[(assertion) ? 1 : -1]
|
||||
#define cta(name,assertion) char name[(assertion) ? 1 : -1]
|
||||
cta(char_is_8_bits, (CHAR_BIT) == 8);
|
||||
cta(long_is_32_bits, sizeof(long) == 4);
|
||||
};
|
||||
|
@ -2301,7 +2333,7 @@ EOF
|
|||
#define CHAR_BIT 0
|
||||
#endif
|
||||
struct ctasserts {
|
||||
#define cta(name, assertion) char name[(assertion) ? 1 : -1]
|
||||
#define cta(name,assertion) char name[(assertion) ? 1 : -1]
|
||||
cta(char_is_8_bits, (CHAR_BIT) == 8);
|
||||
cta(long_is_64_bits, sizeof(long) == 8);
|
||||
};
|
||||
|
@ -2429,7 +2461,7 @@ addsrcs '!' HAVE_STRLCPY strlcpy.c
|
|||
addsrcs USE_PRINTF_BUILTIN printf.c
|
||||
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
|
||||
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
|
||||
add_cppflags -DMKSH_BUILD_R=571
|
||||
add_cppflags -DMKSH_BUILD_R=591
|
||||
|
||||
$e $bi$me: Finished configuration testing, now producing output.$ao
|
||||
|
||||
|
@ -2452,7 +2484,10 @@ esac
|
|||
cat >test.sh <<-EOF
|
||||
$mkshshebang
|
||||
LC_ALL=C PATH='$PATH'; export LC_ALL PATH
|
||||
test -n "\$KSH_VERSION" || exit 1
|
||||
case \$KSH_VERSION in
|
||||
*MIRBSD*|*LEGACY*) ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
set -A check_categories -- $check_categories
|
||||
pflag='$curdir/$mkshexe'
|
||||
sflag='$srcdir/check.t'
|
||||
|
@ -2614,8 +2649,8 @@ INDSRCS= $extras
|
|||
NONSRCS_INST= dot.mkshrc \$(MAN)
|
||||
NONSRCS_NOINST= Build.sh Makefile Rebuild.sh check.pl check.t test.sh
|
||||
CC= $CC
|
||||
CFLAGS= $CFLAGS
|
||||
CPPFLAGS= $CPPFLAGS
|
||||
CFLAGS= $CFLAGS
|
||||
LDFLAGS= $LDFLAGS
|
||||
LIBS= $LIBS
|
||||
|
||||
|
@ -2685,6 +2720,7 @@ test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
|
|||
test -f $tcfn || exit 1
|
||||
test 1 = $r || v "$NROFF -mdoc <'$srcdir/lksh.1' >lksh.cat1" || rmf lksh.cat1
|
||||
test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >mksh.cat1" || rmf mksh.cat1
|
||||
test 1 = $r || v "(set -- ''; . '$srcdir/FAQ2HTML.sh')" || rmf FAQ.htm
|
||||
test 0 = $eq && v $SIZE $tcfn
|
||||
i=install
|
||||
test -f /usr/ucb/$i && i=/usr/ucb/$i
|
||||
|
@ -2698,7 +2734,13 @@ if test $legacy = 0; then
|
|||
fi
|
||||
$e
|
||||
$e Installing the manual:
|
||||
if test -e FAQ.htm; then
|
||||
$e "# $i -c -o root -g bin -m 444 FAQ.htm /usr/share/doc/mksh/"
|
||||
fi
|
||||
if test -f mksh.cat1; then
|
||||
if test -e FAQ.htm; then
|
||||
$e plus either
|
||||
fi
|
||||
$e "# $i -c -o root -g bin -m 444 lksh.cat1" \
|
||||
"/usr/share/man/cat1/lksh.0"
|
||||
$e "# $i -c -o root -g bin -m 444 mksh.cat1" \
|
||||
|
@ -2709,6 +2751,8 @@ $e "# $i -c -o root -g bin -m 444 lksh.1 mksh.1 /usr/share/man/man1/"
|
|||
$e
|
||||
$e Run the regression test suite: ./test.sh
|
||||
$e Please also read the sample file dot.mkshrc and the fine manual.
|
||||
test -e FAQ.htm || \
|
||||
$e Run FAQ2HTML.sh and place FAQ.htm into a suitable location as well.
|
||||
exit 0
|
||||
|
||||
: <<'EOD'
|
||||
|
@ -2740,6 +2784,7 @@ HAVE_CAN_FSTACKPROTECTORALL ac_flags
|
|||
==== cpp definitions ====
|
||||
DEBUG dont use in production, wants gcc, implies:
|
||||
DEBUG_LEAKS enable freeing resources before exiting
|
||||
KSH_VERSIONNAME_VENDOR_EXT when patching; space+plus+word (e.g. " +SuSE")
|
||||
MKSHRC_PATH "~/.mkshrc" (do not change)
|
||||
MKSH_A4PB force use of arc4random_pushb
|
||||
MKSH_ASSUME_UTF8 (0=disabled, 1=enabled; default: unset)
|
||||
|
@ -2768,6 +2813,7 @@ MKSH_S_NOVI=1 disable Vi editing mode (default if MKSH_SMALL)
|
|||
MKSH_TYPEDEF_SIG_ATOMIC_T define to e.g. 'int' if sig_atomic_t is missing
|
||||
MKSH_TYPEDEF_SSIZE_T define to e.g. 'long' if your OS has no ssize_t
|
||||
MKSH_UNEMPLOYED disable job control (but not jobs/co-processes)
|
||||
USE_REALLOC_MALLOC define as 0 to not use realloc as malloc
|
||||
|
||||
=== generic installation instructions ===
|
||||
|
||||
|
@ -2777,15 +2823,15 @@ them, set to a value other than 0 or 1. Ensure /bin/ed is installed. For
|
|||
MKSH_SMALL but with Vi mode, add -DMKSH_S_NOVI=0 to CPPFLAGS as well.
|
||||
|
||||
Normally, the following command is what you want to run, then:
|
||||
$ (sh Build.sh -r -c lto && ./test.sh -f) 2>&1 | tee log
|
||||
$ (sh Build.sh -r && ./test.sh -f) 2>&1 | tee log
|
||||
|
||||
Copy dot.mkshrc to /etc/skel/.mkshrc; install mksh into $prefix/bin; or
|
||||
/bin; install the manpage, if omitting the -r flag a catmanpage is made
|
||||
using $NROFF. Consider using a forward script as /etc/skel/.mkshrc like
|
||||
http://anonscm.debian.org/cgit/collab-maint/mksh.git/plain/debian/.mkshrc
|
||||
https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=alioth/mksh.git;a=blob;f=debian/.mkshrc
|
||||
and put dot.mkshrc as /etc/mkshrc so users need not keep up their HOME.
|
||||
|
||||
You may also want to install the lksh binary (also as /bin/sh) built by:
|
||||
$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r -c lto
|
||||
$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r
|
||||
|
||||
EOD
|
||||
|
|
136
src/FAQ2HTML.sh
Normal file
136
src/FAQ2HTML.sh
Normal file
|
@ -0,0 +1,136 @@
|
|||
#!/bin/mksh
|
||||
rcsid='$MirOS: src/bin/mksh/FAQ2HTML.sh,v 1.1 2020/02/03 22:23:33 tg Exp $'
|
||||
#-
|
||||
# Copyright © 2020
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
# are retained or reproduced in an accompanying document, permission
|
||||
# is granted to deal in this work without restriction, including un‐
|
||||
# limited rights to use, publicly perform, distribute, sell, modify,
|
||||
# merge, give away, or sublicence.
|
||||
#
|
||||
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
|
||||
# the utmost extent permitted by applicable law, neither express nor
|
||||
# implied; without malicious intent or gross negligence. In no event
|
||||
# may a licensor, author or contributor be held liable for indirect,
|
||||
# direct, other damage, loss, or other issues arising in any way out
|
||||
# of dealing in the work, even if advised of the possibility of such
|
||||
# damage or existence of a defect, except proven that it results out
|
||||
# of said person’s immediate fault when using the work as intended.
|
||||
#-
|
||||
|
||||
set -e
|
||||
LC_ALL=C; LANGUAGE=C
|
||||
export LC_ALL; unset LANGUAGE
|
||||
nl='
|
||||
'
|
||||
srcdir=$(dirname "$0")
|
||||
|
||||
p=--posix
|
||||
sed $p -e q </dev/null >/dev/null 2>&1 || p=
|
||||
|
||||
v=$1
|
||||
if test -z "$v"; then
|
||||
v=$(sed $p -n '/^#define MKSH_VERSION "\(.*\)"$/s//\1/p' "$srcdir"/sh.h)
|
||||
fi
|
||||
src_id=$(sed $p -n '/^RCSID: /s///p' "$srcdir"/mksh.faq)
|
||||
# sanity check
|
||||
case $src_id in
|
||||
(*"$nl"*)
|
||||
echo >&2 "E: more than one RCSID in mksh.faq?"
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
sed $p \
|
||||
-e '/^RCSID: \$/s/^.*$/----/' \
|
||||
-e 's!@@RELPATH@@!http://www.mirbsd.org/!g' \
|
||||
-e 's^ <span style="display:none;"> </span>' \
|
||||
"$srcdir"/mksh.faq | tr '\n' '' | sed $p \
|
||||
-e 'sg' \
|
||||
-e 's----g' \
|
||||
-e 's\([^]*\)\1g' \
|
||||
-e 's\([^]*\)\1g' \
|
||||
-e 's\([^]*\)*ToC: \([^]*\)Title: \([^]*\)\([^]*\)\{0,1\}</div><h2 id="\2"><a href="#\2">\3</a></h2><div>g' \
|
||||
-e 's[^]*</div><div>g' \
|
||||
-e 's^</div>*' \
|
||||
-e 's$</div>' \
|
||||
-e 's<><error><>g' \
|
||||
-e 'sg' | tr '' '\n' >FAQ.tmp
|
||||
|
||||
exec >FAQ.htm~
|
||||
cat <<EOF
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>mksh $v FAQ (local copy)</title>
|
||||
<meta name="source" content="$src_id" />
|
||||
<meta name="generator" content="$rcsid" />
|
||||
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
|
||||
.boxhead {
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
.boxtext {
|
||||
border:4px ridge green;
|
||||
margin:0px 24px 0px 18px;
|
||||
padding:2px 3px 2px 3px;
|
||||
}
|
||||
|
||||
.boxfoot {
|
||||
margin-top:0px;
|
||||
}
|
||||
|
||||
h2:before {
|
||||
content:"🔗 ";
|
||||
}
|
||||
|
||||
a[href^="ftp://"]:after,
|
||||
a[href^="http://"]:after,
|
||||
a[href^="https://"]:after,
|
||||
a[href^="irc://"]:after,
|
||||
a[href^="mailto:"]:after,
|
||||
a[href^="news:"]:after,
|
||||
a[href^="nntp://"]:after {
|
||||
content:"⏍";
|
||||
color:#FF0000;
|
||||
vertical-align:super;
|
||||
margin:0 0 0 1px;
|
||||
}
|
||||
|
||||
pre {
|
||||
/* ↑ → ↓ ← */
|
||||
margin:0px 9px 0px 15px;
|
||||
}
|
||||
|
||||
tt {
|
||||
white-space:nowrap;
|
||||
}
|
||||
/*]]>*/--></style>
|
||||
</head><body>
|
||||
<p>Note: Links marked like <a href="irc://chat.freenode.net/!/bin/mksh">this
|
||||
one to the mksh IRC channel</a> connect to external resources.</p>
|
||||
<p>⚠ <b>Notice:</b> the website will have <a
|
||||
href="http://www.mirbsd.org/mksh-faq.htm">the latest version of the
|
||||
mksh FAQ</a> online.</p>
|
||||
<h1>Table of Contents</h1>
|
||||
<ul>
|
||||
EOF
|
||||
sed $p -n \
|
||||
'/^<h2 id="\([^"]*"\)><a[^>]*\(>.*<\/a><\/\)h2>$/s//<li><a href="#\1\2li>/p' \
|
||||
<FAQ.tmp
|
||||
cat <<EOF
|
||||
</ul>
|
||||
|
||||
<h1>Frequently Asked Questions</h1>
|
||||
EOF
|
||||
cat FAQ.tmp - <<EOF
|
||||
<h1>Imprint</h1>
|
||||
<p>This offline HTML page for mksh $v was automatically generated
|
||||
from the sources.</p>
|
||||
</body></html>
|
||||
EOF
|
||||
exec >/dev/null
|
||||
rm FAQ.tmp
|
||||
mv FAQ.htm~ FAQ.htm
|
|
@ -1,4 +1,4 @@
|
|||
# $MirOS: src/bin/mksh/check.pl,v 1.49 2017/05/05 21:17:31 tg Exp $
|
||||
# $MirOS: src/bin/mksh/check.pl,v 1.50 2019/08/01 20:05:55 tg Exp $
|
||||
# $OpenBSD: th,v 1.1 2013/12/02 20:39:44 millert Exp $
|
||||
#-
|
||||
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
|
||||
|
@ -292,7 +292,7 @@ $all_tests = @ARGV == 0;
|
|||
# Set up a very minimal environment
|
||||
%new_env = ();
|
||||
foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
|
||||
'PATH', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
|
||||
'PATH', 'PERLIO', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
|
||||
$new_env{$env} = $ENV{$env} if defined $ENV{$env};
|
||||
}
|
||||
$new_env{'CYGWIN'} = 'nodosfilewarning';
|
||||
|
|
368
src/check.t
368
src/check.t
|
@ -1,9 +1,9 @@
|
|||
# $MirOS: src/bin/mksh/check.t,v 1.812 2019/03/01 16:17:29 tg Exp $
|
||||
# $MirOS: src/bin/mksh/check.t,v 1.838 2020/04/13 19:51:08 tg Exp $
|
||||
# -*- mode: sh -*-
|
||||
#-
|
||||
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
# 2019
|
||||
# 2019, 2020
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -31,20 +31,29 @@
|
|||
# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
|
||||
|
||||
expected-stdout:
|
||||
@(#)MIRBSD KSH R57 2019/03/01
|
||||
KSH R59 2020/04/14
|
||||
description:
|
||||
Check base version of full shell
|
||||
stdin:
|
||||
echo ${KSH_VERSION%%' +'*}
|
||||
vsn=${KSH_VERSION%%' +'*}
|
||||
echo "${vsn#* }"
|
||||
name: KSH_VERSION
|
||||
---
|
||||
expected-stdout:
|
||||
@(#)MIRBSD
|
||||
description:
|
||||
Check this identifies as legacy shell
|
||||
stdin:
|
||||
echo "${KSH_VERSION%% *}"
|
||||
name: KSH_VERSION-modern
|
||||
category: !shell:legacy-yes
|
||||
---
|
||||
expected-stdout:
|
||||
@(#)LEGACY KSH R57 2019/03/01
|
||||
@(#)LEGACY
|
||||
description:
|
||||
Check base version of legacy shell
|
||||
Check this identifies as legacy shell
|
||||
stdin:
|
||||
echo ${KSH_VERSION%%' +'*}
|
||||
echo "${KSH_VERSION%% *}"
|
||||
name: KSH_VERSION-legacy
|
||||
category: !shell:legacy-no
|
||||
---
|
||||
|
@ -53,6 +62,7 @@ description:
|
|||
Check that the shell version tag does not include EBCDIC
|
||||
category: !shell:ebcdic-yes
|
||||
stdin:
|
||||
set -o noglob
|
||||
for x in $KSH_VERSION; do
|
||||
[[ $x = '+EBCDIC' ]] && exit 1
|
||||
done
|
||||
|
@ -63,6 +73,7 @@ description:
|
|||
Check that the shell version tag includes EBCDIC
|
||||
category: !shell:ebcdic-no
|
||||
stdin:
|
||||
set -o noglob
|
||||
for x in $KSH_VERSION; do
|
||||
[[ $x = '+EBCDIC' ]] && exit 0
|
||||
done
|
||||
|
@ -73,6 +84,7 @@ description:
|
|||
Check that the shell version tag does not include TEXTMODE
|
||||
category: !shell:textmode-yes
|
||||
stdin:
|
||||
set -o noglob
|
||||
for x in $KSH_VERSION; do
|
||||
[[ $x = '+TEXTMODE' ]] && exit 1
|
||||
done
|
||||
|
@ -83,6 +95,7 @@ description:
|
|||
Check that the shell version tag includes TEXTMODE
|
||||
category: !shell:textmode-no
|
||||
stdin:
|
||||
set -o noglob
|
||||
for x in $KSH_VERSION; do
|
||||
[[ $x = '+TEXTMODE' ]] && exit 0
|
||||
done
|
||||
|
@ -178,7 +191,7 @@ stdin:
|
|||
expected-stdout:
|
||||
ok
|
||||
expected-stderr-pattern:
|
||||
/mksh: warning: won't have full job control\nXX/
|
||||
/ksh: warning: won't have full job control\nXX/
|
||||
---
|
||||
name: selftest-tty-present
|
||||
description:
|
||||
|
@ -1392,7 +1405,7 @@ need-pass: no
|
|||
# the mv command fails on Cygwin and z/OS
|
||||
# Hurd aborts the testsuite (permission denied)
|
||||
# QNX does not find subdir to cd into
|
||||
category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!os:os390,!nosymlink
|
||||
category: !os:cygwin,!os:gnu,!os:midipix,!os:msys,!os:nto,!os:os390,!nosymlink
|
||||
file-setup: file 644 "x"
|
||||
mkdir noread noread/target noread/target/subdir
|
||||
ln -s noread link
|
||||
|
@ -1999,7 +2012,7 @@ expected-stdout:
|
|||
name: eglob-bad-1
|
||||
description:
|
||||
Check that globbing isn't done when glob has syntax error
|
||||
category: !os:cygwin,!os:msys,!os:os2
|
||||
category: !os:cygwin,!os:midipix,!os:msys,!os:os2
|
||||
file-setup: file 644 "@(a[b|)c]foo"
|
||||
stdin:
|
||||
echo @(a[b|)c]*
|
||||
|
@ -2491,7 +2504,7 @@ description:
|
|||
# breaks on Mac OSX (HFS+ non-standard UTF-8 canonical decomposition)
|
||||
# breaks on Cygwin 1.7 (files are now UTF-16 or something)
|
||||
# breaks on QNX 6.4.1 (says RT)
|
||||
category: !os:cygwin,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
|
||||
category: !os:cygwin,!os:midipix,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
|
||||
need-pass: no
|
||||
file-setup: file 644 "aÂc"
|
||||
stdin:
|
||||
|
@ -2526,6 +2539,19 @@ expected-stdout:
|
|||
-bc abc bbc cbc ebc
|
||||
@bc
|
||||
---
|
||||
name: glob-range-6
|
||||
description:
|
||||
ksh93 fails this but POSIX probably demands it
|
||||
file-setup: file 644 "abc"
|
||||
file-setup: file 644 "cbc"
|
||||
stdin:
|
||||
echo *b*
|
||||
[ '*b*' = *b* ] && echo yep; echo $?
|
||||
expected-stdout:
|
||||
abc cbc
|
||||
2
|
||||
expected-stderr-pattern: /.*/
|
||||
---
|
||||
name: glob-word-1
|
||||
description:
|
||||
Check BSD word boundary matches
|
||||
|
@ -2721,7 +2747,7 @@ expected-stdout:
|
|||
h\b
|
||||
done
|
||||
---
|
||||
name: heredoc-9a
|
||||
name: heredoc-9
|
||||
description:
|
||||
Check that here strings work.
|
||||
stdin:
|
||||
|
@ -2736,6 +2762,19 @@ stdin:
|
|||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")"
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"A $(echo "foo bar") B"
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$b\$b$bar
|
||||
fnord=42
|
||||
bar="bar
|
||||
\$fnord baz"
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
|
||||
echo $(tr r z <<<'bar' 2>/dev/null)
|
||||
cat <<< "$( : )aa"
|
||||
IFS=$'\n'
|
||||
x=(a "b c")
|
||||
tr ac 12 <<< ${x[*]}
|
||||
tr ac 34 <<< "${x[*]}"
|
||||
tr ac 56 <<< ${x[@]}
|
||||
tr ac 78 <<< "${x[@]}"
|
||||
expected-stdout:
|
||||
sbb
|
||||
sbb
|
||||
|
@ -2748,54 +2787,17 @@ expected-stdout:
|
|||
A sbb one B
|
||||
$o$oone
|
||||
onm
|
||||
---
|
||||
name: heredoc-9b
|
||||
description:
|
||||
Check that a corner case of here strings works like bash
|
||||
stdin:
|
||||
fnord=42
|
||||
bar="bar
|
||||
\$fnord baz"
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
|
||||
expected-stdout:
|
||||
one $sabeq onm
|
||||
category: bash
|
||||
---
|
||||
name: heredoc-9c
|
||||
description:
|
||||
Check that a corner case of here strings works like ksh93, zsh
|
||||
stdin:
|
||||
fnord=42
|
||||
bar="bar
|
||||
\$fnord baz"
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
|
||||
expected-stdout:
|
||||
one
|
||||
$sabeq onm
|
||||
---
|
||||
name: heredoc-9d
|
||||
description:
|
||||
Check another corner case of here strings
|
||||
stdin:
|
||||
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
|
||||
expected-stdout:
|
||||
one
|
||||
---
|
||||
name: heredoc-9e
|
||||
description:
|
||||
Check here string related regression with multiple iops
|
||||
stdin:
|
||||
echo $(tr r z <<<'bar' 2>/dev/null)
|
||||
expected-stdout:
|
||||
baz
|
||||
---
|
||||
name: heredoc-9f
|
||||
description:
|
||||
Check long here strings
|
||||
stdin:
|
||||
cat <<< "$( : )aa"
|
||||
expected-stdout:
|
||||
aa
|
||||
1
|
||||
b 2
|
||||
3
|
||||
b 4
|
||||
5 b 6
|
||||
7 b 8
|
||||
---
|
||||
name: heredoc-10
|
||||
description:
|
||||
|
@ -4981,6 +4983,17 @@ expected-stdout:
|
|||
var=onetwo threefour
|
||||
<onetwo threefour> .
|
||||
---
|
||||
name: IFS-subst-11
|
||||
description:
|
||||
Check leading non-whitespace after trim makes only one field
|
||||
stdin:
|
||||
showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
|
||||
v="foo!one!two!three"
|
||||
IFS="!"
|
||||
showargs x ${v:3} y
|
||||
expected-stdout:
|
||||
<x> <> <one> <two> <three> <y> .
|
||||
---
|
||||
name: IFS-arith-1
|
||||
description:
|
||||
http://austingroupbugs.net/view.php?id=832
|
||||
|
@ -6653,12 +6666,35 @@ name: regression-62
|
|||
description:
|
||||
Check if test -nt/-ot succeeds if second(first) file is missing.
|
||||
stdin:
|
||||
matrix() {
|
||||
local a b c d e f g h
|
||||
test a -nt b; a=$?
|
||||
test b -nt a; b=$?
|
||||
test a -ot b; c=$?
|
||||
test b -ot a; d=$?
|
||||
test a -nt a; e=$?
|
||||
test b -nt b; f=$?
|
||||
test a -ot a; g=$?
|
||||
test b -ot b; h=$?
|
||||
echo $1 $a $b $c $d / $e $f $g $h .
|
||||
}
|
||||
matrix a
|
||||
:>a
|
||||
test a -nt b && echo nt OK || echo nt BAD
|
||||
test b -ot a && echo ot OK || echo ot BAD
|
||||
matrix b
|
||||
sleep 2 # mtime granularity for OS/2 and FAT
|
||||
:>b
|
||||
matrix c
|
||||
sleep 2
|
||||
echo dummy >a # Debian GNU/Hurd #955270
|
||||
matrix d
|
||||
rm a
|
||||
matrix e
|
||||
expected-stdout:
|
||||
nt OK
|
||||
ot OK
|
||||
a 1 1 1 1 / 1 1 1 1 .
|
||||
b 0 1 1 0 / 1 1 1 1 .
|
||||
c 1 0 0 1 / 1 1 1 1 .
|
||||
d 0 1 1 0 / 1 1 1 1 .
|
||||
e 1 0 0 1 / 1 1 1 1 .
|
||||
---
|
||||
name: regression-63
|
||||
description:
|
||||
|
@ -7118,13 +7154,13 @@ stdin:
|
|||
name: exec-function-environment-1
|
||||
description:
|
||||
Check assignments in function calls and whether they affect
|
||||
the current execution environment (ksh93, SUSv4)
|
||||
the current execution environment
|
||||
stdin:
|
||||
f() { a=2; }; g() { b=3; echo y$c-; }; a=1 f; b=2; c=1 g
|
||||
echo x$a-$b- z$c-
|
||||
expected-stdout:
|
||||
y1-
|
||||
x2-3- z1-
|
||||
x-3- z-
|
||||
---
|
||||
name: exec-modern-korn-shell
|
||||
description:
|
||||
|
@ -7389,6 +7425,8 @@ expected-stdout:
|
|||
name: xxx-stat-1
|
||||
description:
|
||||
Check that tests on files are consistent
|
||||
(fails when run as root, unfortunately)
|
||||
category: disabled
|
||||
stdin:
|
||||
mkdir a
|
||||
echo x >a/b
|
||||
|
@ -7580,7 +7618,7 @@ stdin:
|
|||
showargs 18 "$a"
|
||||
set -A bla
|
||||
typeset bla[1]=~:~
|
||||
global gbl=~ g2=$1
|
||||
typeset -g gbl=~ g2=$1
|
||||
local lcl=~ l2=$1
|
||||
readonly ro=~ r2=$1
|
||||
showargs 19 "${bla[1]}" a=~ "$gbl" "$lcl" "$ro" "$g2" "$l2" "$r2"
|
||||
|
@ -7761,6 +7799,32 @@ expected-stdout:
|
|||
1 ok
|
||||
expected-exit: 1
|
||||
---
|
||||
name: exit-err-10
|
||||
description:
|
||||
Debian #269067 (cf. regression-38 but with eval)
|
||||
arguments: !-e!
|
||||
stdin:
|
||||
eval false || true
|
||||
echo = $? .
|
||||
expected-stdout:
|
||||
= 0 .
|
||||
---
|
||||
name: exit-err-11
|
||||
description:
|
||||
Fix -e inside eval, from Martijn Dekker; expected-stdout from ksh93
|
||||
stdin:
|
||||
"$__progname" -c 'eval '\''echo ${-//[!eh]}; false; echo phantom e'\''; echo x$?'
|
||||
echo = $?
|
||||
"$__progname" -ec 'eval '\''echo ${-//[!eh]}; false; echo phantom e'\''; echo x$?'
|
||||
echo = $?
|
||||
expected-stdout:
|
||||
h
|
||||
phantom e
|
||||
x0
|
||||
= 0
|
||||
eh
|
||||
= 1
|
||||
---
|
||||
name: exit-enoent-1
|
||||
description:
|
||||
SUSv4 says that the shell should exit with 126/127 in some situations
|
||||
|
@ -7832,11 +7896,9 @@ expected-exit: 9
|
|||
---
|
||||
name: exit-trap-2
|
||||
description:
|
||||
Check that ERR and EXIT traps are run just like ksh93 does.
|
||||
GNU bash does not run ERtrap in ±e eval-undef but runs it
|
||||
twice (bug?) in +e eval-false, so does ksh93 (bug?), which
|
||||
also has a bug to continue execution (echoing "and out" and
|
||||
returning 0) in +e eval-undef.
|
||||
Check that ERR and EXIT traps are run just like GNU bash does.
|
||||
ksh93 runs ERtrap after “parameter null or not set” (which mksh
|
||||
used to do) but (bug) continues “and out”, exit 0, in +e eval-undef.
|
||||
file-setup: file 644 "x"
|
||||
v=; unset v
|
||||
trap 'echo EXtrap' EXIT
|
||||
|
@ -7920,7 +7982,6 @@ expected-stdout:
|
|||
= eval-false 1 .
|
||||
and run ${v?}
|
||||
x: v: parameter null or not set
|
||||
ERtrap
|
||||
EXtrap
|
||||
= eval-undef 1 .
|
||||
and run true
|
||||
|
@ -7942,12 +8003,12 @@ expected-stdout:
|
|||
= eval-true 0 .
|
||||
and run false
|
||||
ERtrap
|
||||
ERtrap
|
||||
and out
|
||||
EXtrap
|
||||
= eval-false 0 .
|
||||
and run ${v?}
|
||||
x: v: parameter null or not set
|
||||
ERtrap
|
||||
EXtrap
|
||||
= eval-undef 1 .
|
||||
and run true
|
||||
|
@ -8017,6 +8078,50 @@ expected-stdout:
|
|||
nein
|
||||
expected-stderr-pattern: !/unexpected op/
|
||||
---
|
||||
name: test-str-pattern
|
||||
description:
|
||||
Check that [[ x = $y ]] can take extglobs, like ksh93
|
||||
stdin:
|
||||
x='a\'
|
||||
[[ $x = a\ ]]; echo 1 $? .
|
||||
[[ $x = a\\ ]]; echo 2 $? .
|
||||
y='a\'
|
||||
[[ $x = $y ]]; echo 3 $? .
|
||||
[[ $x = "$y" ]]; echo 4 $? .
|
||||
x='a\b'
|
||||
y='a\b'
|
||||
[[ $x = $y ]]; echo 5 $? .
|
||||
[[ $x = "$y" ]]; echo 6 $? .
|
||||
y='a\\b'
|
||||
[[ $x = $y ]]; echo 7 $? .
|
||||
[[ $x = "$y" ]]; echo 8 $? .
|
||||
x='foo'
|
||||
y='f+(o)'
|
||||
[[ $x = $y ]]; echo 9 $? .
|
||||
[[ $x = "$y" ]]; echo 10 $? .
|
||||
x=$y
|
||||
[[ $x = $y ]]; echo 11 $? .
|
||||
[[ $x = "$y" ]]; echo 12 $? .
|
||||
x='f+(o'
|
||||
y=$x
|
||||
[[ $x = $y ]]; echo 13 $? .
|
||||
[[ $x = "$y" ]]; echo 14 $? .
|
||||
expected-stdout:
|
||||
1 1 .
|
||||
2 0 .
|
||||
3 0 .
|
||||
4 0 .
|
||||
5 1 .
|
||||
6 0 .
|
||||
7 0 .
|
||||
8 1 .
|
||||
9 0 .
|
||||
10 1 .
|
||||
11 1 .
|
||||
12 0 .
|
||||
13 0 .
|
||||
14 0 .
|
||||
---
|
||||
name: test-precedence-1
|
||||
description:
|
||||
Check a weird precedence case (and POSIX echo)
|
||||
|
@ -8672,7 +8777,7 @@ description:
|
|||
note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
|
||||
XXX fails when LD_PRELOAD is set with -e and Perl chokes it (ASan)
|
||||
need-pass: no
|
||||
category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
|
||||
category: !os:cygwin,!os:midipix,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
|
||||
env-setup: !FOO=BAR!
|
||||
stdin:
|
||||
print '#!'"$__progname"'\nprint "1 a=$ENV{FOO}";' >t1
|
||||
|
@ -9621,8 +9726,7 @@ expected-stdout:
|
|||
---
|
||||
name: varexpand-substr-3
|
||||
description:
|
||||
Check that some things that work in bash fail.
|
||||
This is by design. Oh and vice versa, nowadays.
|
||||
Match bash5
|
||||
stdin:
|
||||
export x=abcdefghi n=2
|
||||
"$__progname" -c 'echo v${x:(n)}x'
|
||||
|
@ -9630,15 +9734,15 @@ stdin:
|
|||
"$__progname" -c 'echo x${x:n}x'
|
||||
"$__progname" -c 'echo y${x:}x'
|
||||
"$__progname" -c 'echo z${x}x'
|
||||
# next fails only in bash
|
||||
"$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
|
||||
"$__progname" -c 'x=abcdef;y=123;echo q${x:${y:2:1}:2}q'
|
||||
expected-stdout:
|
||||
vcdefghix
|
||||
wcdefghix
|
||||
xcdefghix
|
||||
zabcdefghix
|
||||
0
|
||||
qdeq
|
||||
expected-stderr-pattern:
|
||||
/x:n.*bad substitution.*\n.*bad substitution/
|
||||
/x:}.*bad substitution/
|
||||
---
|
||||
name: varexpand-substr-4
|
||||
description:
|
||||
|
@ -10841,6 +10945,43 @@ stdin:
|
|||
expected-stdout:
|
||||
okay
|
||||
---
|
||||
name: ulimit-3
|
||||
description:
|
||||
Check that there are no duplicate limits (if this fails,
|
||||
immediately contact with system information the developers)
|
||||
stdin:
|
||||
[[ -z $(set | grep ^opt) ]]; mis=$?
|
||||
set | grep ^opt | sed 's/^/unexpectedly set in environment: /'
|
||||
opta='<used for showing all limits>'
|
||||
optH='<used to set hard limits>'
|
||||
optS='<used to set soft limits>'
|
||||
ulimit -a >tmpf
|
||||
set -o noglob
|
||||
while IFS= read -r line; do
|
||||
x=${line:1:1}
|
||||
if [[ -z $x || ${#x}/${%x} != 1/1 ]]; then
|
||||
print -r -- "weird line: $line"
|
||||
(( mis |= 1 ))
|
||||
continue
|
||||
fi
|
||||
set -- $line
|
||||
nameref v=opt$x
|
||||
if [[ -n $v ]]; then
|
||||
print -r -- "duplicate -$x \"$2\" already seen as \"$v\""
|
||||
(( mis |= 2 ))
|
||||
fi
|
||||
v=$2
|
||||
done <tmpf
|
||||
if (( mis & 2 )); then
|
||||
echo failed
|
||||
elif (( mis & 1 )); then
|
||||
echo inconclusive
|
||||
else
|
||||
echo done
|
||||
fi
|
||||
expected-stdout:
|
||||
done
|
||||
---
|
||||
name: redir-1
|
||||
description:
|
||||
Check some of the most basic invariants of I/O redirection
|
||||
|
@ -11201,7 +11342,7 @@ description:
|
|||
(Inspired by PR 2450 on OpenBSD.) Calling
|
||||
FOO=bar f
|
||||
where f is a ksh style function, should not set FOO in the current
|
||||
env. If f is a Bourne style function, FOO should be set. Furthermore,
|
||||
env. If f is a Bourne style function, (new) also not. Furthermore,
|
||||
the function should receive a correct value of FOO. However, differing
|
||||
from oksh, setting FOO in the function itself must change the value in
|
||||
setting FOO in the function itself should not change the value in
|
||||
|
@ -11247,7 +11388,7 @@ stdin:
|
|||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
if [ x$FOO != xfoo ]; then
|
||||
if [ x$FOO != x ]; then
|
||||
exit 1
|
||||
fi
|
||||
FOO=barbar
|
||||
|
@ -11262,7 +11403,7 @@ stdin:
|
|||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
if [ x$FOO != xfoo ]; then
|
||||
if [ x$FOO != xbarbar ]; then
|
||||
exit 1
|
||||
fi
|
||||
---
|
||||
|
@ -12659,7 +12800,7 @@ stdin:
|
|||
echo =14
|
||||
(mypid=$$; try mypid)
|
||||
echo =15
|
||||
) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
|
||||
) 2>&1 | sed -e 's/^[A-Za-z]://' -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
|
||||
exit ${PIPESTATUS[0]}
|
||||
expected-stdout:
|
||||
y
|
||||
|
@ -13238,7 +13379,7 @@ description:
|
|||
Crashed during March 2011, fixed on vernal equinÅ<EFBFBD>x ☺
|
||||
category: os:mirbsd,os:openbsd
|
||||
stdin:
|
||||
export MALLOC_OPTIONS=FGJPRSX
|
||||
export MALLOC_OPTIONS=FGJRSX
|
||||
"$__progname" -c 'x=$(tr z r <<<baz); echo $x'
|
||||
expected-stdout:
|
||||
bar
|
||||
|
@ -13362,6 +13503,59 @@ expected-stdout:
|
|||
after 0='swc' 1='二' 2=''
|
||||
= done
|
||||
---
|
||||
name: command-set
|
||||
description:
|
||||
Same but with set
|
||||
stdin:
|
||||
showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
|
||||
showargs 1 "$@"
|
||||
set -- foo bar baz
|
||||
showargs 2 "$@"
|
||||
command set -- miau 'meow nyao'
|
||||
showargs 3 "$@"
|
||||
expected-stdout:
|
||||
<1> .
|
||||
<2> <foo> <bar> <baz> .
|
||||
<3> <miau> <meow nyao> .
|
||||
---
|
||||
name: command-readonly
|
||||
description:
|
||||
These should not exit on error when prefixed
|
||||
stdin:
|
||||
exec 2>/dev/null
|
||||
"$__progname" -c 'readonly v; export v=foo || echo ok'
|
||||
echo ef=$?
|
||||
"$__progname" -c 'readonly v; command export v=foo || echo ok'
|
||||
echo en=$?
|
||||
"$__progname" -c 'readonly v; readonly v=foo || echo ok'
|
||||
echo rf=$?
|
||||
"$__progname" -c 'readonly v; command readonly v=foo || echo ok'
|
||||
echo rn=$?
|
||||
expected-stdout:
|
||||
ef=2
|
||||
ok
|
||||
en=0
|
||||
rf=2
|
||||
ok
|
||||
rn=0
|
||||
---
|
||||
name: command-dot-regression
|
||||
description:
|
||||
Check a regression in fixing the above does not appear
|
||||
stdin:
|
||||
cat >test.mksh <<\EOF
|
||||
set -- one two
|
||||
shift
|
||||
for s_arg in "$#" "$@"; do echo -n "<$s_arg> "; done; echo .
|
||||
EOF
|
||||
"$__progname" -c '. ./test.mksh' dummy oh dear this is not good
|
||||
echo =
|
||||
"$__progname" -c 'command . ./test.mksh' dummy oh dear this is not good
|
||||
expected-stdout:
|
||||
<1> <two> .
|
||||
=
|
||||
<1> <two> .
|
||||
---
|
||||
name: command-pvV-posix-priorities
|
||||
description:
|
||||
For POSIX compatibility, command -v should find aliases and reserved
|
||||
|
@ -13370,13 +13564,17 @@ description:
|
|||
stdin:
|
||||
PATH=/bin:/usr/bin
|
||||
alias foo="bar baz"
|
||||
alias '[ab]=:'
|
||||
bar() { :; }
|
||||
for word in 'if' 'foo' 'bar' 'set' 'true'; do
|
||||
for word in 'if' 'foo' 'bar' 'set' 'true' '[ab]'; do
|
||||
command -v "$word"
|
||||
command -pv "$word"
|
||||
command -V "$word"
|
||||
command -pV "$word"
|
||||
done
|
||||
# extra checks
|
||||
alias '[ab]'
|
||||
whence '[ab]'
|
||||
expected-stdout:
|
||||
if
|
||||
if
|
||||
|
@ -13398,6 +13596,12 @@ expected-stdout:
|
|||
true
|
||||
true is a shell builtin
|
||||
true is a shell builtin
|
||||
alias '[ab]'=:
|
||||
alias '[ab]'=:
|
||||
'[ab]' is an alias for :
|
||||
'[ab]' is an alias for :
|
||||
'[ab]'=:
|
||||
:
|
||||
---
|
||||
name: whence-preserve-tradition
|
||||
description:
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# $Id$
|
||||
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.121 2017/08/08 21:10:21 tg Exp $
|
||||
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.128 2020/04/13 18:39:03 tg Exp $
|
||||
#-
|
||||
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
|
||||
# 2020
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -64,11 +65,10 @@ for EDITOR in "${EDITOR:-}" jupp jstar mcedit ed vi; do
|
|||
done
|
||||
|
||||
\\builtin alias ls=ls l='ls -F' la='l -a' ll='l -l' lo='l -alo'
|
||||
\: "${HOSTNAME:=$(\\builtin ulimit -c 0; \\builtin print -r -- $(hostname \
|
||||
2>/dev/null))}${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit \
|
||||
-c 0; id -un 2>/dev/null)}${USER:=?}"
|
||||
\: "${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit -c 0; id -un \
|
||||
2>/dev/null)}${HOSTNAME:=$(\\builtin ulimit -c 0; hostname 2>/dev/null)}"
|
||||
[[ $HOSTNAME = ?(?(ip6-)localhost?(6)) ]] && HOSTNAME=nil; \\builtin unalias ls
|
||||
\\builtin export EDITOR HOSTNAME TERM USER
|
||||
\\builtin export EDITOR HOSTNAME TERM USER="${USER:-?}"
|
||||
|
||||
# minimal support for lksh users
|
||||
if [[ $KSH_VERSION = *LEGACY\ KSH* ]]; then
|
||||
|
@ -109,7 +109,7 @@ elif \\builtin command -v hexdump >/dev/null; then
|
|||
}
|
||||
else
|
||||
function hd {
|
||||
\\builtin cat "$@" | hd_mksh "$@"
|
||||
\\builtin cat "$@" | hd_mksh
|
||||
}
|
||||
fi
|
||||
|
||||
|
@ -150,6 +150,48 @@ function hd_mksh {
|
|||
(( hv == 2147483647 )) || \\builtin print -r -- "$dasc|"
|
||||
}
|
||||
|
||||
function which {
|
||||
\\builtin typeset p x c
|
||||
\\builtin typeset -i a=0 rv=2 e
|
||||
\\builtin set +e
|
||||
\\builtin set -o noglob
|
||||
|
||||
while \\builtin getopts "a" x; do
|
||||
case $x {
|
||||
(a) a=1 ;;
|
||||
(+a) a=0 ;;
|
||||
(*) \\builtin print -ru2 'Usage: which [-a] name [...]'
|
||||
\\builtin return 255 ;;
|
||||
}
|
||||
done
|
||||
\\builtin shift $((OPTIND - 1))
|
||||
|
||||
# vvvvvvvvvvvvvvvvvvvv should be def_path
|
||||
p=${PATH-/usr/bin$PATHSEP/bin}
|
||||
# ^ no colon!
|
||||
|
||||
# trailing PATHSEP vs field splitting
|
||||
[[ $p = *"$PATHSEP" ]] && p+=.
|
||||
|
||||
IFS=$PATHSEP
|
||||
\\builtin set -A p -- ${p:-.}
|
||||
IFS=$' \t\n'
|
||||
|
||||
for x in "$@"; do
|
||||
if (( !a )) || [[ $x = */* ]]; then
|
||||
\\builtin whence -p -- "$x"
|
||||
e=$?
|
||||
else
|
||||
e=1
|
||||
for c in "${p[@]}"; do
|
||||
PATH=${c:-.} \\builtin whence -p -- "$x" && e=0
|
||||
done
|
||||
fi
|
||||
(( rv = (e == 0) ? (rv & ~2) : (rv == 2 ? 2 : 1) ))
|
||||
done
|
||||
\\builtin return $rv
|
||||
}
|
||||
|
||||
# Berkeley C shell compatible dirs, popd, and pushd functions
|
||||
# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
|
||||
DIRSTACKBASE=$(\\builtin realpath ~/. 2>/dev/null || \
|
||||
|
@ -450,7 +492,6 @@ function enable {
|
|||
i_func[nfunc++]=false
|
||||
i_func[nfunc++]=fc
|
||||
i_func[nfunc++]=getopts
|
||||
i_func[nfunc++]=global
|
||||
i_func[nfunc++]=jobs
|
||||
i_func[nfunc++]=kill
|
||||
i_func[nfunc++]=let
|
||||
|
@ -499,6 +540,7 @@ function enable {
|
|||
i_func[nfunc++]=smores
|
||||
i_func[nfunc++]=hd
|
||||
i_func[nfunc++]=hd_mksh
|
||||
i_func[nfunc++]=which
|
||||
i_func[nfunc++]=chpwd
|
||||
i_func[nfunc++]=cd
|
||||
i_func[nfunc++]=cd_csh
|
||||
|
@ -604,7 +646,7 @@ function enable {
|
|||
|
||||
\: place customisations below this line
|
||||
|
||||
# some defaults follow — you are supposed to adjust these to your
|
||||
# some defaults / samples which you are supposed to adjust to your
|
||||
# liking; by default we add ~/.etc/bin and ~/bin (whichever exist)
|
||||
# to $PATH, set $SHELL to mksh, set some defaults for man and less
|
||||
# and show a few more possible things for users to begin moving in
|
||||
|
@ -618,11 +660,15 @@ done
|
|||
\\builtin export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
|
||||
\\builtin alias cls='\\builtin print -n \\ec'
|
||||
|
||||
#\\builtin unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
|
||||
# LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
|
||||
#\\builtin unset LC_ADDRESS LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
|
||||
# LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
|
||||
# LC_TELEPHONE LC_TIME LANGUAGE LANG LC_ALL
|
||||
#p=en_GB.UTF-8
|
||||
#\\builtin export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
|
||||
#\\builtin export LANG=C.UTF-8 LC_CTYPE=C.UTF-8
|
||||
#\\builtin export LC_ALL=C.UTF-8
|
||||
#\\builtin set -U
|
||||
#[[ ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} = *[Uu][Tt][Ff]?(-)8* ]] || \\builtin set +U
|
||||
|
||||
\\builtin unset p
|
||||
|
||||
|
|
480
src/edit.c
480
src/edit.c
|
@ -5,7 +5,8 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
* 2019, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -28,7 +29,7 @@
|
|||
|
||||
#ifndef MKSH_NO_CMDLINE_EDITING
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.343 2018/07/15 16:16:38 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.350 2020/04/13 20:46:37 tg Exp $");
|
||||
|
||||
/*
|
||||
* in later versions we might use libtermcap for this, but since external
|
||||
|
@ -176,7 +177,7 @@ x_getc(void)
|
|||
static void
|
||||
x_putcf(int c)
|
||||
{
|
||||
shf_putc(c, shl_out);
|
||||
shf_putc_i(c, shl_out);
|
||||
}
|
||||
|
||||
/*********************************
|
||||
|
@ -353,7 +354,7 @@ x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
|
|||
*--cp = '/';
|
||||
} else {
|
||||
/* ok, expand and replace */
|
||||
cp = shf_smprintf(Tf_sSs, dp, cp);
|
||||
strpathx(cp, dp, cp, 1);
|
||||
if (magic_flag)
|
||||
afree(s, ATEMP);
|
||||
s = cp;
|
||||
|
@ -995,10 +996,7 @@ static int x_search_dir(int);
|
|||
static int x_match(char *, char *);
|
||||
static void x_redraw(int);
|
||||
static void x_push(size_t);
|
||||
static char *x_mapin(const char *, Area *);
|
||||
static char *x_mapout(int);
|
||||
static void x_mapout2(int, char **);
|
||||
static void x_print(int, int);
|
||||
static void x_bind_showone(int, int);
|
||||
static void x_e_ungetc(int);
|
||||
static int x_e_getc(void);
|
||||
static void x_e_putc2(int);
|
||||
|
@ -1145,6 +1143,7 @@ static struct x_defbindings const x_defbindings[] = {
|
|||
#ifndef MKSH_SMALL
|
||||
/* more non-standard ones */
|
||||
{ XFUNC_eval_region, 1, CTRL_E },
|
||||
{ XFUNC_quote_region, 1, 'Q' },
|
||||
{ XFUNC_edit_line, 2, 'e' }
|
||||
#endif
|
||||
};
|
||||
|
@ -2389,190 +2388,223 @@ x_vt_hack(int c)
|
|||
}
|
||||
#endif
|
||||
|
||||
static char *
|
||||
x_mapin(const char *cp, Area *ap)
|
||||
int
|
||||
x_bind_check(void)
|
||||
{
|
||||
char *news, *op;
|
||||
return (x_tab == NULL);
|
||||
}
|
||||
|
||||
strdupx(news, cp, ap);
|
||||
op = news;
|
||||
while (*cp) {
|
||||
switch (*cp) {
|
||||
case '^':
|
||||
cp++;
|
||||
*op++ = ksh_toctrl(*cp);
|
||||
break;
|
||||
case '\\':
|
||||
if (cp[1] == '\\' || cp[1] == '^')
|
||||
++cp;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
*op++ = *cp;
|
||||
}
|
||||
cp++;
|
||||
static XString x_bind_show_xs;
|
||||
static char *x_bind_show_xp;
|
||||
|
||||
static void
|
||||
x_bind_show_ch(unsigned char ch)
|
||||
{
|
||||
Xcheck(x_bind_show_xs, x_bind_show_xp);
|
||||
switch (ch) {
|
||||
case ORD('^'):
|
||||
case ORD('\\'):
|
||||
case ORD('='):
|
||||
*x_bind_show_xp++ = '\\';
|
||||
*x_bind_show_xp++ = ch;
|
||||
break;
|
||||
default:
|
||||
if (ksh_isctrl(ch)) {
|
||||
*x_bind_show_xp++ = '^';
|
||||
*x_bind_show_xp++ = ksh_unctrl(ch);
|
||||
} else
|
||||
*x_bind_show_xp++ = ch;
|
||||
break;
|
||||
}
|
||||
*op = '\0';
|
||||
|
||||
return (news);
|
||||
}
|
||||
|
||||
static void
|
||||
x_mapout2(int c, char **buf)
|
||||
x_bind_showone(int prefix, int key)
|
||||
{
|
||||
char *p = *buf;
|
||||
unsigned char f = XFUNC_VALUE(x_tab[prefix][key]);
|
||||
|
||||
if (ksh_isctrl(c)) {
|
||||
*p++ = '^';
|
||||
*p++ = ksh_unctrl(c);
|
||||
} else
|
||||
*p++ = c;
|
||||
*p = 0;
|
||||
*buf = p;
|
||||
}
|
||||
if (!x_bind_show_xs.areap)
|
||||
XinitN(x_bind_show_xs, 16, AEDIT);
|
||||
|
||||
static char *
|
||||
x_mapout(int c)
|
||||
{
|
||||
static char buf[8];
|
||||
char *bp = buf;
|
||||
|
||||
x_mapout2(c, &bp);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
x_print(int prefix, int key)
|
||||
{
|
||||
int f = x_tab[prefix][key];
|
||||
|
||||
if (prefix)
|
||||
/* prefix == 1 || prefix == 2 || prefix == 3 */
|
||||
shf_puts(x_mapout(prefix == 1 ? CTRL_BO :
|
||||
prefix == 2 ? CTRL_X : 0), shl_stdout);
|
||||
#ifdef MKSH_SMALL
|
||||
shprintf("%s = ", x_mapout(key));
|
||||
#else
|
||||
shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
|
||||
if (XFUNC_VALUE(f) != XFUNC_ins_string)
|
||||
#endif
|
||||
shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
|
||||
x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
|
||||
shf_puts("bind ", shl_stdout);
|
||||
#ifndef MKSH_SMALL
|
||||
else
|
||||
shprintf("'%s'\n", x_atab[prefix][key]);
|
||||
if (f == XFUNC_ins_string)
|
||||
shf_puts("-m ", shl_stdout);
|
||||
#endif
|
||||
switch (prefix) {
|
||||
case 1:
|
||||
x_bind_show_ch(CTRL_BO);
|
||||
break;
|
||||
case 2:
|
||||
x_bind_show_ch(CTRL_X);
|
||||
break;
|
||||
case 3:
|
||||
x_bind_show_ch(0);
|
||||
break;
|
||||
}
|
||||
x_bind_show_ch(key);
|
||||
#ifndef MKSH_SMALL
|
||||
if (x_tab[prefix][key] & 0x80)
|
||||
*x_bind_show_xp++ = '~';
|
||||
#endif
|
||||
*x_bind_show_xp = '\0';
|
||||
x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
|
||||
print_value_quoted(shl_stdout, x_bind_show_xp);
|
||||
shf_putc('=', shl_stdout);
|
||||
#ifndef MKSH_SMALL
|
||||
if (f == XFUNC_ins_string) {
|
||||
const unsigned char *cp = (const void *)x_atab[prefix][key];
|
||||
unsigned char c;
|
||||
|
||||
while ((c = *cp++))
|
||||
x_bind_show_ch(c);
|
||||
*x_bind_show_xp = '\0';
|
||||
x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
|
||||
print_value_quoted(shl_stdout, x_bind_show_xp);
|
||||
} else
|
||||
#endif
|
||||
shf_puts(x_ftab[f].xf_name, shl_stdout);
|
||||
shf_putc('\n', shl_stdout);
|
||||
}
|
||||
|
||||
int
|
||||
x_bind(const char *a1, const char *a2,
|
||||
#ifndef MKSH_SMALL
|
||||
/* bind -m */
|
||||
bool macro,
|
||||
#endif
|
||||
/* bind -l */
|
||||
bool list)
|
||||
x_bind_list(void)
|
||||
{
|
||||
size_t f;
|
||||
|
||||
for (f = 0; f < NELEM(x_ftab); f++)
|
||||
if (!(x_ftab[f].xf_flags & XF_NOBIND))
|
||||
shprintf(Tf_sN, x_ftab[f].xf_name);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
x_bind_showall(void)
|
||||
{
|
||||
unsigned char f;
|
||||
int prefix, key;
|
||||
char *m1, *m2;
|
||||
#ifndef MKSH_SMALL
|
||||
char *sp = NULL;
|
||||
bool hastilde;
|
||||
#endif
|
||||
|
||||
if (x_tab == NULL) {
|
||||
bi_errorf("can't bind, not a tty");
|
||||
return (1);
|
||||
}
|
||||
/* List function names */
|
||||
if (list) {
|
||||
for (f = 0; f < NELEM(x_ftab); f++)
|
||||
if (!(x_ftab[f].xf_flags & XF_NOBIND))
|
||||
shprintf(Tf_sN, x_ftab[f].xf_name);
|
||||
return (0);
|
||||
}
|
||||
if (a1 == NULL) {
|
||||
for (prefix = 0; prefix < X_NTABS; prefix++)
|
||||
for (key = 0; key < X_TABSZ; key++) {
|
||||
f = XFUNC_VALUE(x_tab[prefix][key]);
|
||||
if (f == XFUNC_insert || f == XFUNC_error
|
||||
#ifndef MKSH_SMALL
|
||||
|| (macro && f != XFUNC_ins_string)
|
||||
#endif
|
||||
)
|
||||
continue;
|
||||
x_print(prefix, key);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
m2 = m1 = x_mapin(a1, ATEMP);
|
||||
prefix = 0;
|
||||
for (;; m1++) {
|
||||
key = (unsigned char)*m1;
|
||||
f = XFUNC_VALUE(x_tab[prefix][key]);
|
||||
if (f == XFUNC_meta1)
|
||||
prefix = 1;
|
||||
else if (f == XFUNC_meta2)
|
||||
prefix = 2;
|
||||
else if (f == XFUNC_meta3)
|
||||
prefix = 3;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (*++m1
|
||||
#ifndef MKSH_SMALL
|
||||
&& ((*m1 != '~') || *(m1 + 1))
|
||||
#endif
|
||||
) {
|
||||
char msg[256];
|
||||
const char *c = a1;
|
||||
m1 = msg;
|
||||
while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
|
||||
x_mapout2(*c++, &m1);
|
||||
bi_errorf("too long key sequence: %s", msg);
|
||||
return (1);
|
||||
}
|
||||
#ifndef MKSH_SMALL
|
||||
hastilde = tobool(*m1);
|
||||
#endif
|
||||
afree(m2, ATEMP);
|
||||
|
||||
if (a2 == NULL) {
|
||||
x_print(prefix, key);
|
||||
return (0);
|
||||
}
|
||||
if (*a2 == 0) {
|
||||
f = XFUNC_insert;
|
||||
#ifndef MKSH_SMALL
|
||||
} else if (macro) {
|
||||
f = XFUNC_ins_string;
|
||||
sp = x_mapin(a2, AEDIT);
|
||||
#endif
|
||||
} else {
|
||||
for (f = 0; f < NELEM(x_ftab); f++)
|
||||
if (!strcmp(x_ftab[f].xf_name, a2))
|
||||
for (prefix = 0; prefix < X_NTABS; prefix++)
|
||||
for (key = 0; key < X_TABSZ; key++)
|
||||
switch (XFUNC_VALUE(x_tab[prefix][key])) {
|
||||
case XFUNC_error: /* unset */
|
||||
case XFUNC_insert: /* auto-insert */
|
||||
break;
|
||||
if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
|
||||
bi_errorf("%s: no such function", a2);
|
||||
default:
|
||||
x_bind_showone(prefix, key);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
x_bind_getc(const char **ccpp)
|
||||
{
|
||||
unsigned int ch, ec;
|
||||
|
||||
if ((ch = ord(**ccpp)))
|
||||
++(*ccpp);
|
||||
switch (ch) {
|
||||
case ORD('^'):
|
||||
ch = ksh_toctrl(**ccpp) | 0x100U;
|
||||
if (**ccpp)
|
||||
++(*ccpp);
|
||||
break;
|
||||
case ORD('\\'):
|
||||
switch ((ec = ord(**ccpp))) {
|
||||
case ORD('^'):
|
||||
case ORD('\\'):
|
||||
case ORD('='):
|
||||
ch = ec | 0x100U;
|
||||
++(*ccpp);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (ch);
|
||||
}
|
||||
|
||||
int
|
||||
x_bind(const char *s SMALLP(bool macro))
|
||||
{
|
||||
const char *ccp = s;
|
||||
int prefix, key;
|
||||
unsigned int c;
|
||||
#ifndef MKSH_SMALL
|
||||
bool hastilde = false;
|
||||
char *ms = NULL;
|
||||
#endif
|
||||
|
||||
prefix = 0;
|
||||
c = x_bind_getc(&ccp);
|
||||
if (!c || c == ORD('=')) {
|
||||
bi_errorf("no key to bind");
|
||||
return (1);
|
||||
}
|
||||
key = c & 0xFF;
|
||||
while ((c = x_bind_getc(&ccp)) != ORD('=')) {
|
||||
if (!c) {
|
||||
x_bind_showone(prefix, key);
|
||||
return (0);
|
||||
}
|
||||
switch (XFUNC_VALUE(x_tab[prefix][key])) {
|
||||
case XFUNC_meta1:
|
||||
prefix = 1;
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case XFUNC_meta2:
|
||||
prefix = 2;
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case XFUNC_meta3:
|
||||
prefix = 3;
|
||||
key = c & 0xFF;
|
||||
continue;
|
||||
}
|
||||
#ifndef MKSH_SMALL
|
||||
if (c == ORD('~')) {
|
||||
hastilde = true;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
bi_errorf("too long key sequence: %s", s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#ifndef MKSH_SMALL
|
||||
if (macro) {
|
||||
char *cp;
|
||||
|
||||
cp = ms = alloc(strlen(ccp) + 1, AEDIT);
|
||||
while ((c = x_bind_getc(&ccp)))
|
||||
*cp++ = c;
|
||||
*cp = '\0';
|
||||
c = XFUNC_ins_string;
|
||||
} else
|
||||
#endif
|
||||
if (!*ccp) {
|
||||
c = XFUNC_insert;
|
||||
} else {
|
||||
for (c = 0; c < NELEM(x_ftab); ++c)
|
||||
if (!strcmp(x_ftab[c].xf_name, ccp))
|
||||
break;
|
||||
if (c == NELEM(x_ftab) || x_ftab[c].xf_flags & XF_NOBIND) {
|
||||
bi_errorf("%s: no such editing command", ccp);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MKSH_SMALL
|
||||
if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
|
||||
x_atab[prefix][key])
|
||||
if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string)
|
||||
afree(x_atab[prefix][key], AEDIT);
|
||||
x_atab[prefix][key] = ms;
|
||||
if (hastilde)
|
||||
c |= 0x80U;
|
||||
#endif
|
||||
x_tab[prefix][key] = f
|
||||
#ifndef MKSH_SMALL
|
||||
| (hastilde ? 0x80 : 0)
|
||||
#endif
|
||||
;
|
||||
#ifndef MKSH_SMALL
|
||||
x_atab[prefix][key] = sp;
|
||||
#endif
|
||||
x_tab[prefix][key] = c;
|
||||
|
||||
/* Track what the user has bound so x_mode(true) won't toast things */
|
||||
if (f == XFUNC_insert)
|
||||
/* track what the user has bound, so x_mode(true) won't toast things */
|
||||
if (c == XFUNC_insert)
|
||||
x_bound[(prefix * X_TABSZ + key) / 8] &=
|
||||
~(1 << ((prefix * X_TABSZ + key) % 8));
|
||||
else
|
||||
|
@ -3507,11 +3539,11 @@ static int inslen; /* length of input buffer */
|
|||
static int srchlen; /* length of current search pattern */
|
||||
static char *ybuf; /* yank buffer */
|
||||
static int yanklen; /* length of yank buffer */
|
||||
static int fsavecmd = ' '; /* last find command */
|
||||
static uint8_t fsavecmd = ORD(' '); /* last find command */
|
||||
static int fsavech; /* character to find */
|
||||
static char lastcmd[MAXVICMD]; /* last non-move command */
|
||||
static int lastac; /* argcnt for lastcmd */
|
||||
static int lastsearch = ' '; /* last search command */
|
||||
static uint8_t lastsearch = ORD(' '); /* last search command */
|
||||
static char srchpat[SRCHLEN]; /* last search pattern */
|
||||
static int insert; /* <>0 in insert mode */
|
||||
static int hnum; /* position in history */
|
||||
|
@ -3651,21 +3683,25 @@ vi_hook(int ch)
|
|||
|
||||
case VNORMAL:
|
||||
/* PC scancodes */
|
||||
if (!ch) switch (cmdlen = 0, (ch = x_getc())) {
|
||||
case 71: ch = '0'; goto pseudo_vi_command;
|
||||
case 72: ch = 'k'; goto pseudo_vi_command;
|
||||
case 73: ch = 'A'; goto vi_xfunc_search_up;
|
||||
case 75: ch = 'h'; goto pseudo_vi_command;
|
||||
case 77: ch = 'l'; goto pseudo_vi_command;
|
||||
case 79: ch = '$'; goto pseudo_vi_command;
|
||||
case 80: ch = 'j'; goto pseudo_vi_command;
|
||||
case 83: ch = 'x'; goto pseudo_vi_command;
|
||||
default: ch = 0; goto vi_insert_failed;
|
||||
if (!ch) {
|
||||
cmdlen = 0;
|
||||
switch (ch = x_getc()) {
|
||||
case 71: ch = ORD('0'); goto pseudo_vi_command;
|
||||
case 72: ch = ORD('k'); goto pseudo_vi_command;
|
||||
case 73: ch = ORD('A'); goto vi_xfunc_search;
|
||||
case 75: ch = ORD('h'); goto pseudo_vi_command;
|
||||
case 77: ch = ORD('l'); goto pseudo_vi_command;
|
||||
case 79: ch = ORD('$'); goto pseudo_vi_command;
|
||||
case 80: ch = ORD('j'); goto pseudo_vi_command;
|
||||
case 81: ch = ORD('B'); goto vi_xfunc_search;
|
||||
case 83: ch = ORD('x'); goto pseudo_vi_command;
|
||||
default: ch = 0; goto vi_insert_failed;
|
||||
}
|
||||
}
|
||||
if (insert != 0) {
|
||||
if (ch == CTRL_V) {
|
||||
state = VLIT;
|
||||
ch = '^';
|
||||
ch = ORD('^');
|
||||
}
|
||||
switch (vi_insert(ch)) {
|
||||
case -1:
|
||||
|
@ -3699,8 +3735,8 @@ vi_hook(int ch)
|
|||
save_cbuf();
|
||||
vs->cursor = 0;
|
||||
vs->linelen = 0;
|
||||
if (putbuf(ch == '/' ? "/" : "?", 1,
|
||||
false) != 0)
|
||||
if (putbuf(ord(ch) == ORD('/') ?
|
||||
"/" : "?", 1, false) != 0)
|
||||
return (-1);
|
||||
refresh(0);
|
||||
}
|
||||
|
@ -3861,10 +3897,11 @@ vi_hook(int ch)
|
|||
break;
|
||||
|
||||
case VPREFIX2:
|
||||
vi_xfunc_search_up:
|
||||
vi_xfunc_search:
|
||||
state = VFAIL;
|
||||
switch (ch) {
|
||||
case 'A':
|
||||
case ORD('A'):
|
||||
case ORD('B'):
|
||||
/* the cursor may not be at the BOL */
|
||||
if (!vs->cursor)
|
||||
break;
|
||||
|
@ -3873,13 +3910,14 @@ vi_hook(int ch)
|
|||
vs->cursor = sizeof(srchpat) - 2;
|
||||
/* anchor the search pattern */
|
||||
srchpat[0] = '^';
|
||||
/* take the current line up to the cursor */
|
||||
memmove(srchpat + 1, vs->cbuf, vs->cursor);
|
||||
/* take current line up to the cursor */
|
||||
memcpy(srchpat + 1, vs->cbuf, vs->cursor);
|
||||
srchpat[vs->cursor + 1] = '\0';
|
||||
/* set a magic flag */
|
||||
argc1 = 2 + (int)vs->cursor;
|
||||
/* and emulate a backwards history search */
|
||||
lastsearch = '/';
|
||||
/* and emulate a history search */
|
||||
/* search backwards if PgUp, forwards for PgDn */
|
||||
lastsearch = ch == ORD('A') ? '/' : '?';
|
||||
*curcmd = 'n';
|
||||
goto pseudo_VCMD;
|
||||
}
|
||||
|
@ -4442,9 +4480,9 @@ vi_cmd(int argcnt, const char *cmd)
|
|||
/* FALLTHROUGH */
|
||||
case ORD('n'):
|
||||
case ORD('N'):
|
||||
if (lastsearch == ' ')
|
||||
if (lastsearch == ORD(' '))
|
||||
return (-1);
|
||||
if (lastsearch == '?')
|
||||
if (lastsearch == ORD('?'))
|
||||
c1 = 1;
|
||||
else
|
||||
c1 = 0;
|
||||
|
@ -4648,10 +4686,10 @@ domove(int argcnt, const char *cmd, int sub)
|
|||
/* FALLTHROUGH */
|
||||
case ORD(','):
|
||||
case ORD(';'):
|
||||
if (fsavecmd == ' ')
|
||||
if (fsavecmd == ORD(' '))
|
||||
return (-1);
|
||||
i = ksh_eq(fsavecmd, 'F', 'f');
|
||||
t = fsavecmd > 'a';
|
||||
t = rtt2asc(fsavecmd) > rtt2asc('a');
|
||||
if (*cmd == ',')
|
||||
t = !t;
|
||||
if ((ncursor = findch(fsavech, argcnt, tobool(t),
|
||||
|
@ -5589,39 +5627,40 @@ x_eval_region_helper(const char *cmd, size_t len)
|
|||
afree(wds, ATEMP);
|
||||
strdupx(cp, cp, AEDIT);
|
||||
} else
|
||||
/* command cannot be parsed */
|
||||
cp = NULL;
|
||||
quitenv(NULL);
|
||||
return (cp);
|
||||
}
|
||||
|
||||
static int
|
||||
x_eval_region(int c MKSH_A_UNUSED)
|
||||
x_operate_region(char *(*helper)(const char *, size_t))
|
||||
{
|
||||
char *evbeg, *evend, *cp;
|
||||
char *rgbeg, *rgend, *cp;
|
||||
size_t newlen;
|
||||
/* only for LINE overflow checking */
|
||||
size_t restlen;
|
||||
|
||||
if (xmp == NULL) {
|
||||
evbeg = xbuf;
|
||||
evend = xep;
|
||||
rgbeg = xbuf;
|
||||
rgend = xep;
|
||||
} else if (xmp < xcp) {
|
||||
evbeg = xmp;
|
||||
evend = xcp;
|
||||
rgbeg = xmp;
|
||||
rgend = xcp;
|
||||
} else {
|
||||
evbeg = xcp;
|
||||
evend = xmp;
|
||||
rgbeg = xcp;
|
||||
rgend = xmp;
|
||||
}
|
||||
|
||||
x_e_putc2('\r');
|
||||
x_clrtoeol(' ', false);
|
||||
x_flush();
|
||||
x_mode(false);
|
||||
cp = x_eval_region_helper(evbeg, evend - evbeg);
|
||||
cp = helper(rgbeg, rgend - rgbeg);
|
||||
x_mode(true);
|
||||
|
||||
if (cp == NULL) {
|
||||
/* command cannot be parsed */
|
||||
/* error return from helper */
|
||||
x_eval_region_err:
|
||||
x_e_putc2(KSH_BEL);
|
||||
x_redraw('\r');
|
||||
|
@ -5629,20 +5668,49 @@ x_eval_region(int c MKSH_A_UNUSED)
|
|||
}
|
||||
|
||||
newlen = strlen(cp);
|
||||
restlen = xep - evend;
|
||||
restlen = xep - rgend;
|
||||
/* check for LINE overflow, until this is dynamically allocated */
|
||||
if (evbeg + newlen + restlen >= xend)
|
||||
if (rgbeg + newlen + restlen >= xend)
|
||||
goto x_eval_region_err;
|
||||
|
||||
xmp = evbeg;
|
||||
xcp = evbeg + newlen;
|
||||
xmp = rgbeg;
|
||||
xcp = rgbeg + newlen;
|
||||
xep = xcp + restlen;
|
||||
memmove(xcp, evend, restlen + /* NUL */ 1);
|
||||
memmove(xcp, rgend, restlen + /* NUL */ 1);
|
||||
memcpy(xmp, cp, newlen);
|
||||
afree(cp, AEDIT);
|
||||
x_adjust();
|
||||
x_modified();
|
||||
return (KSTD);
|
||||
}
|
||||
|
||||
static int
|
||||
x_eval_region(int c MKSH_A_UNUSED)
|
||||
{
|
||||
return (x_operate_region(x_eval_region_helper));
|
||||
}
|
||||
|
||||
static char *
|
||||
x_quote_region_helper(const char *cmd, size_t len)
|
||||
{
|
||||
char *s;
|
||||
size_t newlen;
|
||||
struct shf shf;
|
||||
|
||||
strndupx(s, cmd, len, ATEMP);
|
||||
newlen = len < 256 ? 256 : 4096;
|
||||
shf_sopen(alloc(newlen, AEDIT), newlen, SHF_WR | SHF_DYNAMIC, &shf);
|
||||
shf.areap = AEDIT;
|
||||
shf.flags |= SHF_ALLOCB;
|
||||
print_value_quoted(&shf, s);
|
||||
afree(s, ATEMP);
|
||||
return (shf_sclose(&shf));
|
||||
}
|
||||
|
||||
static int
|
||||
x_quote_region(int c MKSH_A_UNUSED)
|
||||
{
|
||||
return (x_operate_region(x_quote_region_helper));
|
||||
}
|
||||
#endif /* !MKSH_SMALL */
|
||||
#endif /* !MKSH_NO_CMDLINE_EDITING */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2009, 2010, 2015, 2016
|
||||
* Copyright (c) 2009, 2010, 2015, 2016, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
|
||||
#if defined(EMACSFN_DEFNS)
|
||||
__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.10 2016/09/01 12:59:09 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.11 2020/04/13 20:46:39 tg Exp $");
|
||||
#define FN(cname,sname,flags) static int x_##cname(int);
|
||||
#elif defined(EMACSFN_ENUMS)
|
||||
#define FN(cname,sname,flags) XFUNC_##cname,
|
||||
|
@ -89,6 +89,9 @@ FN(nl_next_com, "newline-and-next", 0)
|
|||
FN(noop, "no-op", 0)
|
||||
FN(prev_com, "up-history", XF_ARG)
|
||||
FN(prev_histword, "prev-hist-word", XF_ARG)
|
||||
#ifndef MKSH_SMALL
|
||||
FN(quote_region, "quote-region", 0)
|
||||
#endif
|
||||
FN(search_char_back, "search-character-backward", XF_ARG)
|
||||
FN(search_char_forw, "search-character-forward", XF_ARG)
|
||||
FN(search_hist, "search-history", 0)
|
||||
|
|
595
src/eval.c
595
src/eval.c
|
@ -2,7 +2,8 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
* 2019, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +24,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.219 2018/01/14 01:29:47 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.230 2020/04/07 23:14:41 tg Exp $");
|
||||
|
||||
/*
|
||||
* string expansion
|
||||
|
@ -50,13 +51,17 @@ typedef struct {
|
|||
bool split;
|
||||
} Expand;
|
||||
|
||||
#define XBASE 0 /* scanning original */
|
||||
#define XSUB 1 /* expanding ${} string */
|
||||
#define XARGSEP 2 /* ifs0 between "$*" */
|
||||
#define XARG 3 /* expanding $*, $@ */
|
||||
#define XCOM 4 /* expanding $() */
|
||||
#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
|
||||
#define XSUBMID 6 /* middle of expanding ${} */
|
||||
#define XBASE 0 /* scanning original string */
|
||||
#define XARGSEP 1 /* ifs0 between "$*" */
|
||||
#define XARG 2 /* expanding $*, $@ */
|
||||
#define XCOM 3 /* expanding $() */
|
||||
#define XNULLSUB 4 /* "$@" when $# is 0, so don't generate word */
|
||||
#define XSUB 5 /* expanding ${} string */
|
||||
#define XSUBMID 6 /* middle of expanding ${}; must be XSUB+1 */
|
||||
#define XSUBPAT 7 /* expanding [[ x = ${} ]] string */
|
||||
#define XSUBPATMID 8 /* middle, must be XSUBPAT+1 */
|
||||
|
||||
#define isXSUB(t) ((t) == XSUB || (t) == XSUBPAT)
|
||||
|
||||
/* States used for field splitting */
|
||||
#define IFS_WORD 0 /* word has chars (or quotes except "$@") */
|
||||
|
@ -71,7 +76,7 @@ typedef struct {
|
|||
#define STYPE_SINGLE 0x2FF
|
||||
#define STYPE_MASK 0x300
|
||||
|
||||
static int varsub(Expand *, const char *, const char *, int *, int *);
|
||||
static int varsub(Expand *, const char *, const char *, unsigned int *, int *);
|
||||
static int comsub(Expand *, const char *, int);
|
||||
static char *valsub(struct op *, Area *);
|
||||
static char *trimsub(char *, char *, int);
|
||||
|
@ -205,7 +210,7 @@ typedef struct SubType {
|
|||
struct SubType *prev; /* old type */
|
||||
struct SubType *next; /* poped type (to avoid re-allocating) */
|
||||
size_t base; /* start position of expanded word */
|
||||
short stype; /* [=+-?%#] action after expanded word */
|
||||
unsigned short stype; /* [=+-?%#] action after expanded word */
|
||||
short f; /* saved value of f (DOPAT, etc) */
|
||||
uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */
|
||||
uint8_t quotew; /* saved value of quote (for ${..[+-=]..}) */
|
||||
|
@ -334,7 +339,7 @@ expand(
|
|||
Xcheck(ds, dp);
|
||||
*dp++ = *sp++;
|
||||
}
|
||||
if ((unsigned int)c == ORD('}'))
|
||||
if ((unsigned int)c == ORD(/*{*/'}'))
|
||||
*dp++ = ';';
|
||||
*dp++ = c;
|
||||
} else {
|
||||
|
@ -381,7 +386,7 @@ expand(
|
|||
*/
|
||||
/* skip the { or x (}) */
|
||||
const char *varname = ++sp;
|
||||
int stype;
|
||||
unsigned int stype;
|
||||
int slen = 0;
|
||||
|
||||
/* skip variable */
|
||||
|
@ -437,17 +442,8 @@ expand(
|
|||
sp += slen;
|
||||
switch (stype & STYPE_SINGLE) {
|
||||
case ORD('#') | STYPE_AT:
|
||||
x.str = shf_smprintf("%08X",
|
||||
(unsigned int)hash(str_val(st->var)));
|
||||
case ORD('Q') | STYPE_AT:
|
||||
break;
|
||||
case ORD('Q') | STYPE_AT: {
|
||||
struct shf shf;
|
||||
|
||||
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
|
||||
print_value_quoted(&shf, str_val(st->var));
|
||||
x.str = shf_sclose(&shf);
|
||||
break;
|
||||
}
|
||||
case ORD('0'): {
|
||||
char *beg, *mid, *end, *stg;
|
||||
mksh_ari_t from = 0, num = -1, flen, finc = 0;
|
||||
|
@ -497,10 +493,11 @@ expand(
|
|||
}
|
||||
case ORD('/') | STYPE_AT:
|
||||
case ORD('/'): {
|
||||
char *s, *p, *d, *sbeg, *end;
|
||||
char *pat = NULL, *rrep = null;
|
||||
char *s, *p, *d, *sbeg;
|
||||
char *pat = NULL, *rrep;
|
||||
char fpat = 0, *tpat1, *tpat2;
|
||||
char *ws, *wpat, *wrep;
|
||||
char *ws, *wpat, *wrep, tch;
|
||||
size_t rreplen;
|
||||
|
||||
s = ws = wdcopy(sp, ATEMP);
|
||||
p = s + (wdscan(sp, ADELIM) - sp);
|
||||
|
@ -516,11 +513,17 @@ expand(
|
|||
ctype(s[1], C_SUB2))
|
||||
fpat = s[1];
|
||||
wpat = s + (fpat ? 2 : 0);
|
||||
wrep = d ? p : NULL;
|
||||
if (!(stype & STYPE_AT)) {
|
||||
rrep = wrep ? evalstr(wrep,
|
||||
DOTILDE | DOSCALAR) :
|
||||
null;
|
||||
if (!(wrep = d ? p : NULL)) {
|
||||
rrep = null;
|
||||
rreplen = 0;
|
||||
} else if (!(stype & STYPE_AT)) {
|
||||
rrep = evalstr(wrep,
|
||||
DOTILDE | DOSCALAR);
|
||||
rreplen = strlen(rrep);
|
||||
} else {
|
||||
rrep = NULL;
|
||||
/* shut up GCC */
|
||||
rreplen = 0;
|
||||
}
|
||||
|
||||
/* prepare string on which to work */
|
||||
|
@ -567,17 +570,17 @@ expand(
|
|||
*/
|
||||
if (!gmatchx(sbeg, tpat1, false))
|
||||
goto end_repl;
|
||||
end = strnul(s);
|
||||
d = strnul(s);
|
||||
/* now anchor the beginning of the match */
|
||||
if (ord(fpat) != ORD('#'))
|
||||
while (sbeg <= end) {
|
||||
while (sbeg <= d) {
|
||||
if (gmatchx(sbeg, tpat2, false))
|
||||
break;
|
||||
else
|
||||
sbeg++;
|
||||
}
|
||||
/* now anchor the end of the match */
|
||||
p = end;
|
||||
p = d;
|
||||
if (ord(fpat) != ORD('%'))
|
||||
while (p >= sbeg) {
|
||||
bool gotmatch;
|
||||
|
@ -590,22 +593,56 @@ expand(
|
|||
break;
|
||||
p--;
|
||||
}
|
||||
strndupx(end, sbeg, p - sbeg, ATEMP);
|
||||
record_match(end);
|
||||
afree(end, ATEMP);
|
||||
if (stype & STYPE_AT) {
|
||||
if (rrep != null)
|
||||
afree(rrep, ATEMP);
|
||||
rrep = wrep ? evalstr(wrep,
|
||||
DOTILDE | DOSCALAR) :
|
||||
null;
|
||||
|
||||
/* record partial string as match */
|
||||
tch = *p;
|
||||
*p = '\0';
|
||||
record_match(sbeg);
|
||||
*p = tch;
|
||||
/* get replacement string, if necessary */
|
||||
if ((stype & STYPE_AT) &&
|
||||
rrep != null) {
|
||||
afree(rrep, ATEMP);
|
||||
/* might access match! */
|
||||
rrep = evalstr(wrep,
|
||||
DOTILDE | DOSCALAR);
|
||||
rreplen = strlen(rrep);
|
||||
}
|
||||
|
||||
/*
|
||||
* string:
|
||||
* |--------|---------|-------\0
|
||||
* s n1 sbeg n2 p n3 d
|
||||
*
|
||||
* replacement:
|
||||
* |------------|
|
||||
* rrep rreplen
|
||||
*/
|
||||
|
||||
/* move strings around and replace */
|
||||
{
|
||||
size_t n1 = sbeg - s;
|
||||
size_t n2 = p - sbeg;
|
||||
size_t n3 = d - p;
|
||||
/* move part3 to the front, OR… */
|
||||
if (rreplen < n2)
|
||||
memmove(sbeg + rreplen,
|
||||
p, n3 + 1);
|
||||
/* … adjust size, move to back */
|
||||
if (rreplen > n2) {
|
||||
s = aresize(s,
|
||||
n1 + rreplen + n3 + 1,
|
||||
ATEMP);
|
||||
memmove(s + n1 + rreplen,
|
||||
s + n1 + n2,
|
||||
n3 + 1);
|
||||
}
|
||||
/* insert replacement */
|
||||
if (rreplen)
|
||||
memcpy(s + n1, rrep, rreplen);
|
||||
/* continue after the place */
|
||||
sbeg = s + n1 + rreplen;
|
||||
}
|
||||
strndupx(end, s, sbeg - s, ATEMP);
|
||||
d = shf_smprintf(Tf_sss, end, rrep, p);
|
||||
afree(end, ATEMP);
|
||||
sbeg = d + (sbeg - s) + strlen(rrep);
|
||||
afree(s, ATEMP);
|
||||
s = d;
|
||||
if (stype & STYPE_AT) {
|
||||
afree(tpat1, ATEMP);
|
||||
afree(pat, ATEMP);
|
||||
|
@ -769,11 +806,22 @@ expand(
|
|||
errorf(Tf_sD_s, st->var->name,
|
||||
debunk(dp, dp, strlen(dp) + 1));
|
||||
break;
|
||||
case ORD('#') | STYPE_AT:
|
||||
x.str = shf_smprintf("%08X",
|
||||
(unsigned int)hash(str_val(st->var)));
|
||||
goto common_CSUBST;
|
||||
case ORD('Q') | STYPE_AT: {
|
||||
struct shf shf;
|
||||
|
||||
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
|
||||
print_value_quoted(&shf, str_val(st->var));
|
||||
x.str = shf_sclose(&shf);
|
||||
goto common_CSUBST;
|
||||
}
|
||||
case ORD('0'):
|
||||
case ORD('/') | STYPE_AT:
|
||||
case ORD('/'):
|
||||
case ORD('#') | STYPE_AT:
|
||||
case ORD('Q') | STYPE_AT:
|
||||
common_CSUBST:
|
||||
dp = Xrestpos(ds, dp, st->base);
|
||||
type = XSUB;
|
||||
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
|
||||
|
@ -822,9 +870,69 @@ expand(
|
|||
}
|
||||
continue;
|
||||
|
||||
case XSUBPAT:
|
||||
case XSUBPATMID:
|
||||
XSUBPAT_beg:
|
||||
switch ((c = ord(*x.str++))) {
|
||||
case 0:
|
||||
goto XSUB_end;
|
||||
case ORD('\\'):
|
||||
if ((c = ord(*x.str)) == 0)
|
||||
/* keep backslash at EOS */
|
||||
c = ORD('\\');
|
||||
else
|
||||
++x.str;
|
||||
break;
|
||||
/* ctype(c, C_PATMO) */
|
||||
case ORD('!'):
|
||||
case ORD('*'):
|
||||
case ORD('+'):
|
||||
case ORD('?'):
|
||||
case ORD('@'):
|
||||
if (ord(*x.str) == ORD('('/*)*/)) {
|
||||
++x.str;
|
||||
c |= 0x80U;
|
||||
make_magic = true;
|
||||
}
|
||||
break;
|
||||
case ORD('('):
|
||||
c = ORD(' ') | 0x80U;
|
||||
/* FALLTHROUGH */
|
||||
case ORD('|'):
|
||||
case ORD(')'):
|
||||
make_magic = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case XSUB:
|
||||
if (!quote && (f & DODBMAGIC)) {
|
||||
const char *cs = x.str;
|
||||
int level = 0;
|
||||
|
||||
while ((c = *cs++))
|
||||
switch (c) {
|
||||
case '\\':
|
||||
if ((c = *cs))
|
||||
++cs;
|
||||
break;
|
||||
case ORD('('):
|
||||
++level;
|
||||
break;
|
||||
case ORD(')'):
|
||||
--level;
|
||||
break;
|
||||
}
|
||||
/* balanced parentheses? */
|
||||
if (!level) {
|
||||
type = XSUBPAT;
|
||||
goto XSUBPAT_beg;
|
||||
}
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case XSUBMID:
|
||||
if ((c = ord(*x.str++)) == 0) {
|
||||
XSUB_end:
|
||||
type = XBASE;
|
||||
if (f & DOBLANK)
|
||||
doblank--;
|
||||
|
@ -980,23 +1088,17 @@ expand(
|
|||
Xinit(ds, dp, 128, ATEMP);
|
||||
} else if (c == 0) {
|
||||
return;
|
||||
} else if (type == XSUB && ctype(c, C_IFS) &&
|
||||
} else if (isXSUB(type) && ctype(c, C_IFS) &&
|
||||
!ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
|
||||
*(cp = alloc(1, ATEMP)) = '\0';
|
||||
XPput(*wp, cp);
|
||||
type = XSUBMID;
|
||||
++type;
|
||||
}
|
||||
if (word != IFS_NWS)
|
||||
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
|
||||
} else {
|
||||
if (type == XSUB) {
|
||||
if (word == IFS_NWS &&
|
||||
Xlength(ds, dp) == 0) {
|
||||
*(cp = alloc(1, ATEMP)) = '\0';
|
||||
XPput(*wp, cp);
|
||||
}
|
||||
type = XSUBMID;
|
||||
}
|
||||
if (isXSUB(type))
|
||||
++type;
|
||||
|
||||
/* age tilde_ok info - ~ code tests second bit */
|
||||
tilde_ok <<= 1;
|
||||
|
@ -1111,16 +1213,20 @@ hasnonempty(const char **strv)
|
|||
*/
|
||||
static int
|
||||
varsub(Expand *xp, const char *sp, const char *word,
|
||||
int *stypep, /* becomes qualifier type */
|
||||
int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */
|
||||
/* becomes qualifier type */
|
||||
unsigned int *stypep,
|
||||
/* becomes qualifier type len (=, :=, etc.) valid iff *stypep != 0 */
|
||||
int *slenp)
|
||||
{
|
||||
int c;
|
||||
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
|
||||
int stype; /* substitution type */
|
||||
unsigned int c;
|
||||
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
|
||||
unsigned int stype; /* substitution type */
|
||||
int slen = 0;
|
||||
const char *p;
|
||||
struct tbl *vp;
|
||||
bool zero_ok = false;
|
||||
int sc;
|
||||
XPtrV wv;
|
||||
|
||||
if ((stype = ord(sp[0])) == '\0')
|
||||
/* Bad variable name */
|
||||
|
@ -1128,108 +1234,166 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
|
||||
xp->var = NULL;
|
||||
|
||||
/* entirety of named array? */
|
||||
if ((p = cstrchr(sp, '[')) && (sc = ord(p[1])) &&
|
||||
ord(p[2]) == ORD(']'))
|
||||
/* keep p (for ${!foo[1]} below)! */
|
||||
switch (sc) {
|
||||
case ORD('*'):
|
||||
sc = 3;
|
||||
break;
|
||||
case ORD('@'):
|
||||
sc = 7;
|
||||
break;
|
||||
default:
|
||||
/* bit2 = @, bit1 = array, bit0 = enabled */
|
||||
sc = 0;
|
||||
}
|
||||
else
|
||||
/* $* and $@ checked below */
|
||||
sc = 0;
|
||||
|
||||
/*-
|
||||
* ${#var}, string length (-U: characters, +U: octets) or array size
|
||||
* ${%var}, string width (-U: screen columns, +U: octets)
|
||||
* ${#var}, string length (-U: characters, +U: octets) or array size
|
||||
* ${!var}, variable name
|
||||
* ${*…} -> set flag for argv
|
||||
* ${@…} -> set flag for argv
|
||||
*/
|
||||
c = ord(sp[1]);
|
||||
if ((unsigned int)stype == ORD('%') && c == '\0')
|
||||
return (-1);
|
||||
if (ctype(stype, C_SUB2) && c != '\0') {
|
||||
/* Can't have any modifiers for ${#...} or ${%...} */
|
||||
if (ctype(stype, C_SUB2 | CiVAR1)) {
|
||||
switch (stype) {
|
||||
case ORD('*'):
|
||||
if (!sc)
|
||||
sc = 1;
|
||||
goto nopfx;
|
||||
case ORD('@'):
|
||||
if (!sc)
|
||||
sc = 5;
|
||||
goto nopfx;
|
||||
}
|
||||
/* varname required */
|
||||
if ((c = ord(sp[1])) == '\0') {
|
||||
if (stype == ORD('%'))
|
||||
/* $% */
|
||||
return (-1);
|
||||
/* $# or $! */
|
||||
goto nopfx;
|
||||
}
|
||||
/* can’t have any modifiers for ${#…} or ${%…} or ${!…} */
|
||||
if (*word != CSUBST)
|
||||
return (-1);
|
||||
sp++;
|
||||
/* Check for size of array */
|
||||
if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
|
||||
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
|
||||
int n = 0;
|
||||
|
||||
if ((unsigned int)stype != ORD('#'))
|
||||
/* check for argv past prefix */
|
||||
if (!sc) switch (c) {
|
||||
case ORD('*'):
|
||||
sc = 1;
|
||||
break;
|
||||
case ORD('@'):
|
||||
sc = 5;
|
||||
break;
|
||||
}
|
||||
/* skip past prefix */
|
||||
++sp;
|
||||
/* determine result */
|
||||
switch (stype) {
|
||||
case ORD('!'):
|
||||
if (sc & 2) {
|
||||
stype = 0;
|
||||
XPinit(wv, 32);
|
||||
vp = global(arrayname(sp));
|
||||
do {
|
||||
if (vp->flag & ISSET)
|
||||
XPput(wv, shf_smprintf(Tf_lu,
|
||||
arrayindex(vp)));
|
||||
} while ((vp = vp->u.array));
|
||||
goto arraynames;
|
||||
}
|
||||
xp->var = global(sp);
|
||||
/* use saved p from above */
|
||||
xp->str = p ? shf_smprintf("%s[%lu]", xp->var->name,
|
||||
arrayindex(xp->var)) : xp->var->name;
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
default:
|
||||
internal_errorf("stype mismatch");
|
||||
/* NOTREACHED */
|
||||
#endif
|
||||
case ORD('%'):
|
||||
/* cannot do this on an array */
|
||||
if (sc)
|
||||
return (-1);
|
||||
vp = global(arrayname(sp));
|
||||
if (vp->flag & (ISSET|ARRAY))
|
||||
zero_ok = true;
|
||||
for (; vp; vp = vp->u.array)
|
||||
if (vp->flag & ISSET)
|
||||
n++;
|
||||
c = n;
|
||||
} else if ((unsigned int)c == ORD('*') ||
|
||||
(unsigned int)c == ORD('@')) {
|
||||
if ((unsigned int)stype != ORD('#'))
|
||||
return (-1);
|
||||
c = e->loc->argc;
|
||||
} else {
|
||||
p = str_val(global(sp));
|
||||
zero_ok = p != null;
|
||||
if ((unsigned int)stype == ORD('#'))
|
||||
c = utflen(p);
|
||||
else {
|
||||
/* partial utf_mbswidth reimplementation */
|
||||
const char *s = p;
|
||||
unsigned int wc;
|
||||
size_t len;
|
||||
int cw;
|
||||
|
||||
c = 0;
|
||||
while (*s) {
|
||||
if (!UTFMODE || (len = utf_mbtowc(&wc,
|
||||
s)) == (size_t)-1)
|
||||
/* not UTFMODE or not UTF-8 */
|
||||
wc = rtt2asc(*s++);
|
||||
else
|
||||
/* UTFMODE and UTF-8 */
|
||||
s += len;
|
||||
/* wc == char or wchar at s++ */
|
||||
if ((cw = utf_wcwidth(wc)) == -1) {
|
||||
/* 646, 8859-1, 10646 C0/C1 */
|
||||
c = -1;
|
||||
break;
|
||||
}
|
||||
c += cw;
|
||||
/* partial utf_mbswidth reimplementation */
|
||||
sc = 0;
|
||||
while (*p) {
|
||||
if (!UTFMODE ||
|
||||
(wv.len = utf_mbtowc(&c, p)) == (size_t)-1)
|
||||
/* not UTFMODE or not UTF-8 */
|
||||
c = rtt2asc(*p++);
|
||||
else
|
||||
/* UTFMODE and UTF-8 */
|
||||
p += wv.len;
|
||||
/* c == char or wchar at p++ */
|
||||
if ((slen = utf_wcwidth(c)) == -1) {
|
||||
/* 646, 8859-1, 10646 C0/C1 */
|
||||
sc = -1;
|
||||
break;
|
||||
}
|
||||
sc += slen;
|
||||
}
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case ORD('#'):
|
||||
switch (sc & 3) {
|
||||
case 3:
|
||||
vp = global(arrayname(sp));
|
||||
if (vp->flag & (ISSET|ARRAY))
|
||||
zero_ok = true;
|
||||
sc = 0;
|
||||
do {
|
||||
if (vp->flag & ISSET)
|
||||
sc++;
|
||||
} while ((vp = vp->u.array));
|
||||
break;
|
||||
case 1:
|
||||
sc = e->loc->argc;
|
||||
break;
|
||||
default:
|
||||
p = str_val(global(sp));
|
||||
zero_ok = p != null;
|
||||
sc = utflen(p);
|
||||
break;
|
||||
}
|
||||
/* ${%var} also here */
|
||||
if (Flag(FNOUNSET) && sc == 0 && !zero_ok)
|
||||
errorf(Tf_parm, sp);
|
||||
xp->str = shf_smprintf(Tf_d, sc);
|
||||
break;
|
||||
}
|
||||
if (Flag(FNOUNSET) && c == 0 && !zero_ok)
|
||||
errorf(Tf_parm, sp);
|
||||
/* unqualified variable/string substitution */
|
||||
*stypep = 0;
|
||||
xp->str = shf_smprintf(Tf_d, c);
|
||||
return (XSUB);
|
||||
}
|
||||
if ((unsigned int)stype == ORD('!') && c != '\0' && *word == CSUBST) {
|
||||
sp++;
|
||||
if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
|
||||
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
|
||||
c = ORD('!');
|
||||
stype = 0;
|
||||
goto arraynames;
|
||||
}
|
||||
xp->var = global(sp);
|
||||
xp->str = p ? shf_smprintf("%s[%lu]",
|
||||
xp->var->name, arrayindex(xp->var)) : xp->var->name;
|
||||
*stypep = 0;
|
||||
return (XSUB);
|
||||
}
|
||||
nopfx:
|
||||
|
||||
/* Check for qualifiers in word part */
|
||||
/* check for qualifiers in word part */
|
||||
stype = 0;
|
||||
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
|
||||
if ((unsigned int)c == ORD(':')) {
|
||||
/*slen = 0;*/
|
||||
c = word[/*slen +*/ 0] == CHAR ? ord(word[/*slen +*/ 1]) : 0;
|
||||
if (c == ORD(':')) {
|
||||
slen += 2;
|
||||
stype = STYPE_DBL;
|
||||
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
|
||||
}
|
||||
if (!stype && (unsigned int)c == ORD('/')) {
|
||||
if (!stype && c == ORD('/')) {
|
||||
slen += 2;
|
||||
stype = c;
|
||||
if (word[slen] == ADELIM &&
|
||||
ord(word[slen + 1]) == (unsigned int)c) {
|
||||
ord(word[slen + 1]) == c) {
|
||||
slen += 2;
|
||||
stype |= STYPE_DBL;
|
||||
}
|
||||
} else if (stype == STYPE_DBL && ((unsigned int)c == ORD(' ') ||
|
||||
(unsigned int)c == ORD('0'))) {
|
||||
} else if (stype == STYPE_DBL && (c == ORD(' ') || c == ORD('0'))) {
|
||||
stype |= ORD('0');
|
||||
} else if (ctype(c, C_SUB1)) {
|
||||
slen += 2;
|
||||
|
@ -1238,12 +1402,11 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
/* Note: ksh88 allows :%, :%%, etc */
|
||||
slen += 2;
|
||||
stype = c;
|
||||
if (word[slen + 0] == CHAR &&
|
||||
ord(word[slen + 1]) == (unsigned int)c) {
|
||||
if (word[slen + 0] == CHAR && ord(word[slen + 1]) == c) {
|
||||
stype |= STYPE_DBL;
|
||||
slen += 2;
|
||||
}
|
||||
} else if ((unsigned int)c == ORD('@')) {
|
||||
} else if (c == ORD('@')) {
|
||||
/* @x where x is command char */
|
||||
switch (c = ord(word[slen + 2]) == CHAR ?
|
||||
ord(word[slen + 3]) : 0) {
|
||||
|
@ -1262,84 +1425,70 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
if (!stype && *word != CSUBST)
|
||||
return (-1);
|
||||
|
||||
c = ord(sp[0]);
|
||||
if ((unsigned int)c == ORD('*') || (unsigned int)c == ORD('@')) {
|
||||
switch (stype & STYPE_SINGLE) {
|
||||
/* can't assign to a vector */
|
||||
case ORD('='):
|
||||
/* can't trim a vector (yet) */
|
||||
case ORD('%'):
|
||||
case ORD('#'):
|
||||
case ORD('?'):
|
||||
case ORD('0'):
|
||||
case ORD('/') | STYPE_AT:
|
||||
case ORD('/'):
|
||||
case ORD('#') | STYPE_AT:
|
||||
case ORD('Q') | STYPE_AT:
|
||||
return (-1);
|
||||
}
|
||||
if (e->loc->argc == 0) {
|
||||
xp->str = null;
|
||||
xp->var = global(sp);
|
||||
state = (unsigned int)c == ORD('@') ? XNULLSUB : XSUB;
|
||||
} else {
|
||||
xp->u.strv = (const char **)e->loc->argv + 1;
|
||||
xp->str = *xp->u.strv++;
|
||||
/* $@ */
|
||||
xp->split = tobool((unsigned int)c == ORD('@'));
|
||||
state = XARG;
|
||||
}
|
||||
/* POSIX 2009? */
|
||||
zero_ok = true;
|
||||
} else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
|
||||
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
|
||||
XPtrV wv;
|
||||
|
||||
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 {
|
||||
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 {
|
||||
if (!sc) {
|
||||
xp->var = global(sp);
|
||||
xp->str = str_val(xp->var);
|
||||
/* can't assign things like $! or $1 */
|
||||
if ((unsigned int)(stype & STYPE_SINGLE) == ORD('=') &&
|
||||
if ((stype & STYPE_SINGLE) == ORD('=') &&
|
||||
!*xp->str && ctype(*sp, C_VAR1 | C_DIGIT))
|
||||
return (-1);
|
||||
state = XSUB;
|
||||
} else {
|
||||
/* can’t assign/trim a vector (yet) */
|
||||
switch (stype & STYPE_SINGLE) {
|
||||
case ORD('-'):
|
||||
case ORD('+'):
|
||||
/* allowed ops */
|
||||
case 0:
|
||||
/* or no ops */
|
||||
break;
|
||||
/* case ORD('='):
|
||||
case ORD('?'):
|
||||
case ORD('#'):
|
||||
case ORD('%'):
|
||||
case ORD('/'):
|
||||
case ORD('/') | STYPE_AT:
|
||||
case ORD('0'):
|
||||
case ORD('#') | STYPE_AT:
|
||||
case ORD('Q') | STYPE_AT:
|
||||
*/ default:
|
||||
return (-1);
|
||||
}
|
||||
/* do what we can */
|
||||
if (sc & 2) {
|
||||
XPinit(wv, 32);
|
||||
vp = global(arrayname(sp));
|
||||
do {
|
||||
if (vp->flag & ISSET)
|
||||
XPput(wv, str_val(vp));
|
||||
} while ((vp = vp->u.array));
|
||||
arraynames:
|
||||
if ((c = (XPsize(wv) == 0)))
|
||||
XPfree(wv);
|
||||
else {
|
||||
XPput(wv, NULL);
|
||||
xp->u.strv = (const char **)XPptrv(wv);
|
||||
}
|
||||
} else {
|
||||
if ((c = (e->loc->argc == 0)))
|
||||
xp->var = global(sp);
|
||||
else
|
||||
xp->u.strv = (const char **)e->loc->argv + 1;
|
||||
/* POSIX 2009? */
|
||||
zero_ok = true;
|
||||
}
|
||||
/* have we got any elements? */
|
||||
if (c) {
|
||||
/* no */
|
||||
xp->str = null;
|
||||
state = sc & 4 ? XNULLSUB : XSUB;
|
||||
} else {
|
||||
/* yes → load first */
|
||||
xp->str = *xp->u.strv++;
|
||||
/* $@ or ${foo[@]} */
|
||||
xp->split = tobool(sc & 4);
|
||||
state = XARG;
|
||||
}
|
||||
}
|
||||
|
||||
c = stype & STYPE_CHAR;
|
||||
|
@ -1348,15 +1497,15 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
(((stype & STYPE_DBL) ? *xp->str == '\0' : xp->str == null) &&
|
||||
(state != XARG || (ifs0 || xp->split ?
|
||||
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
|
||||
ctype(c, C_EQUAL | C_MINUS | C_QUEST) : (unsigned int)c == ORD('+')))) ||
|
||||
(unsigned int)stype == (ORD('0') | STYPE_DBL) ||
|
||||
(unsigned int)stype == (ORD('#') | STYPE_AT) ||
|
||||
(unsigned int)stype == (ORD('Q') | STYPE_AT) ||
|
||||
(unsigned int)(stype & STYPE_CHAR) == ORD('/'))
|
||||
ctype(c, C_EQUAL | C_MINUS | C_QUEST) : c == ORD('+')))) ||
|
||||
stype == (ORD('0') | STYPE_DBL) ||
|
||||
stype == (ORD('#') | STYPE_AT) ||
|
||||
stype == (ORD('Q') | STYPE_AT) ||
|
||||
(stype & STYPE_CHAR) == ORD('/'))
|
||||
/* expand word instead of variable value */
|
||||
state = XBASE;
|
||||
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
|
||||
(ctype(c, C_SUB2) || (state != XBASE && (unsigned int)c != ORD('+'))))
|
||||
(ctype(c, C_SUB2) || (state != XBASE && c != ORD('+'))))
|
||||
errorf(Tf_parm, sp);
|
||||
*stypep = stype;
|
||||
*slenp = slen;
|
||||
|
@ -1950,7 +2099,7 @@ valsub(struct op *t, Area *ap)
|
|||
newenv(E_FUNC);
|
||||
newblock();
|
||||
if (ap)
|
||||
vp = local("REPLY", false);
|
||||
vp = local(TREPLY, false);
|
||||
if (!kshsetjmp(e->jbuf))
|
||||
execute(t, XXCOM | XERROK, NULL);
|
||||
if (vp)
|
||||
|
|
73
src/exec.c
73
src/exec.c
|
@ -3,7 +3,7 @@
|
|||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
* 2019
|
||||
* 2019, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.206 2019/03/01 16:17:53 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.223 2020/04/07 23:14:41 tg Exp $");
|
||||
|
||||
#ifndef MKSH_DEFAULT_EXECSHELL
|
||||
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
|
||||
|
@ -108,8 +108,8 @@ execute(struct op * volatile t,
|
|||
|
||||
if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
|
||||
cp = NULL;
|
||||
dp = shf_smprintf(Tf_ss, evalstr(t->vars[0],
|
||||
DOASNTILDE | DOSCALAR), rv ? null : cp);
|
||||
strdup2x(dp, evalstr(t->vars[0], DOASNTILDE | DOSCALAR),
|
||||
rv ? null : cp);
|
||||
typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
|
||||
/* free the expanded value */
|
||||
afree(cp, APERM);
|
||||
|
@ -127,7 +127,7 @@ execute(struct op * volatile t,
|
|||
timex_hook(t, &up);
|
||||
ap = (const char **)up;
|
||||
if (ap[0])
|
||||
tp = findcom(ap[0], FC_BI|FC_FUNC);
|
||||
tp = findcom(ap[0], FC_BI | FC_FUNC);
|
||||
}
|
||||
flags &= ~XTIME;
|
||||
|
||||
|
@ -446,7 +446,7 @@ execute(struct op * volatile t,
|
|||
if (rv == ENOEXEC)
|
||||
scriptexec(t, (const char **)up);
|
||||
else
|
||||
errorf(Tf_sD_s, t->str, cstrerror(rv));
|
||||
errorfx(126, Tf_sD_s, t->str, cstrerror(rv));
|
||||
}
|
||||
Break:
|
||||
exstat = rv & 0xFF;
|
||||
|
@ -464,14 +464,9 @@ execute(struct op * volatile t,
|
|||
unwind(LEXIT);
|
||||
if (rv != 0 && !(flags & XERROK) &&
|
||||
(xerrok == NULL || !*xerrok)) {
|
||||
if (Flag(FERREXIT) & 0x80) {
|
||||
/* inside eval */
|
||||
Flag(FERREXIT) = 0;
|
||||
} else {
|
||||
trapsig(ksh_SIGERR);
|
||||
if (Flag(FERREXIT))
|
||||
unwind(LERROR);
|
||||
}
|
||||
trapsig(ksh_SIGERR);
|
||||
if (Flag(FERREXIT))
|
||||
unwind(LERREXT);
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
@ -492,7 +487,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
static struct op texec;
|
||||
int type_flags;
|
||||
bool resetspec;
|
||||
int fcflags = FC_BI|FC_FUNC|FC_PATH;
|
||||
int fcflags = FC_BI | FC_FUNC | FC_PATH;
|
||||
struct block *l_expand, *l_assign;
|
||||
int optc;
|
||||
const char *exec_argv0 = NULL;
|
||||
|
@ -529,7 +524,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
resetspec = false;
|
||||
while (tp && tp->type == CSHELL) {
|
||||
/* undo effects of command */
|
||||
fcflags = FC_BI|FC_FUNC|FC_PATH;
|
||||
fcflags = FC_BI | FC_FUNC | FC_PATH;
|
||||
if (tp->val.f == c_builtin) {
|
||||
if ((cp = *++ap) == NULL ||
|
||||
(!strcmp(cp, "--") && (cp = *++ap) == NULL)) {
|
||||
|
@ -578,7 +573,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
/* command -vV or something */
|
||||
break;
|
||||
/* don't look for functions */
|
||||
fcflags = FC_BI|FC_PATH;
|
||||
fcflags = FC_BI | FC_PATH;
|
||||
if (saw_p) {
|
||||
if (Flag(FRESTRICTED)) {
|
||||
warningf(true, Tf_sD_s,
|
||||
|
@ -608,8 +603,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
(tp->flag & LOWER_BI)) {
|
||||
struct tbl *ext_cmd;
|
||||
|
||||
ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC);
|
||||
if (ext_cmd && (ext_cmd->type != CTALIAS ||
|
||||
ext_cmd = findcom(tp->name, FC_FUNC | FC_PATH);
|
||||
if (ext_cmd && (ext_cmd->type == CFUNC ||
|
||||
(ext_cmd->flag & ISSET)))
|
||||
tp = ext_cmd;
|
||||
}
|
||||
|
@ -619,7 +614,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
break;
|
||||
} else
|
||||
break;
|
||||
tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
|
||||
tp = findcom(ap[0], fcflags & (FC_BI | FC_FUNC));
|
||||
}
|
||||
if (t->u.evalflags & DOTCOMEXEC)
|
||||
flags |= XEXEC;
|
||||
|
@ -629,12 +624,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
else {
|
||||
/* create new variable/function block */
|
||||
newblock();
|
||||
/* ksh functions don't keep assignments, POSIX functions do. */
|
||||
if (!resetspec && tp && tp->type == CFUNC &&
|
||||
!(tp->flag & FKSH))
|
||||
type_flags = EXPORT;
|
||||
else
|
||||
type_flags = LOCAL|LOCAL_COPY|EXPORT;
|
||||
/* all functions keep assignments */
|
||||
type_flags = LOCAL | LOCAL_COPY | EXPORT;
|
||||
}
|
||||
l_assign = e->loc;
|
||||
if (exec_clrenv)
|
||||
|
@ -694,11 +685,9 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
/* shell built-in */
|
||||
case CSHELL:
|
||||
do_call_builtin:
|
||||
if (l_expand != l_assign)
|
||||
l_assign->flags |= (tp->flag & NEXTLOC_BI);
|
||||
rv = call_builtin(tp, (const char **)ap, null, resetspec);
|
||||
if (resetspec && tp->val.f == c_shift) {
|
||||
l_expand->argc = l_assign->argc;
|
||||
l_expand->argv = l_assign->argv;
|
||||
}
|
||||
break;
|
||||
|
||||
/* function call */
|
||||
|
@ -804,6 +793,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
switch (i) {
|
||||
case LRETURN:
|
||||
case LERROR:
|
||||
case LERREXT:
|
||||
rv = exstat & 0xFF;
|
||||
break;
|
||||
case LINTR:
|
||||
|
@ -1147,6 +1137,10 @@ builtin(const char *name, int (*func) (const char **))
|
|||
/* is declaration utility (POSIX: export, readonly) */
|
||||
flag |= DECL_UTIL;
|
||||
break;
|
||||
case '#':
|
||||
/* is set or shift */
|
||||
flag |= NEXTLOC_BI;
|
||||
break;
|
||||
default:
|
||||
goto flags_seen;
|
||||
}
|
||||
|
@ -1309,10 +1303,10 @@ search_access(const char *fn, int mode)
|
|||
}
|
||||
|
||||
#ifdef __OS2__
|
||||
/* check if path is something we want to find, adding executable extensions */
|
||||
#define search_access(fn, mode) access_ex((search_access), (fn), (mode))
|
||||
/* check if path is something we want to find adding executable extensions */
|
||||
#define search_access(fn,mode) access_ex((search_access), (fn), (mode))
|
||||
#else
|
||||
#define search_access(fn, mode) (search_access)((fn), (mode))
|
||||
#define search_access(fn,mode) (search_access)((fn), (mode))
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -1605,7 +1599,7 @@ hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
|
|||
return (-2);
|
||||
}
|
||||
if (iop->ioflag & IOHERESTR) {
|
||||
ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC);
|
||||
ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR);
|
||||
} else if (sub) {
|
||||
/* do substitutions on the content of heredoc */
|
||||
s = pushs(SSTRING, ATEMP);
|
||||
|
@ -1682,7 +1676,7 @@ static const char *
|
|||
do_selectargs(const char **ap, bool print_menu)
|
||||
{
|
||||
static const char *read_args[] = {
|
||||
Tread, "-r", "REPLY", NULL
|
||||
Tread, Tdr, TREPLY, NULL
|
||||
};
|
||||
char *s;
|
||||
int i, argct;
|
||||
|
@ -1696,13 +1690,13 @@ do_selectargs(const char **ap, bool print_menu)
|
|||
* - the user enters a blank line
|
||||
* - the REPLY parameter is empty
|
||||
*/
|
||||
if (print_menu || !*str_val(global("REPLY")))
|
||||
if (print_menu || !*str_val(global(TREPLY)))
|
||||
pr_menu(ap);
|
||||
shellf(Tf_s, str_val(global("PS3")));
|
||||
if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect,
|
||||
false))
|
||||
return (NULL);
|
||||
if (*(s = str_val(global("REPLY"))))
|
||||
if (*(s = str_val(global(TREPLY))))
|
||||
return ((getn(s, &i) && i >= 1 && i <= argct) ?
|
||||
ap[i - 1] : null);
|
||||
print_menu = true;
|
||||
|
@ -1861,8 +1855,11 @@ dbteste_getopnd(Test_env *te, Test_op op, bool do_eval)
|
|||
if (!do_eval)
|
||||
return (null);
|
||||
|
||||
if (op == TO_STEQL || op == TO_STNEQ)
|
||||
if (op == TO_STEQL || op == TO_STNEQ) {
|
||||
flags |= DOPAT;
|
||||
if (!Flag(FSH))
|
||||
flags |= DODBMAGIC;
|
||||
}
|
||||
|
||||
return (evalstr(s, flags));
|
||||
}
|
||||
|
|
57
src/expr.c
57
src/expr.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2016, 2017, 2018
|
||||
* 2011, 2012, 2013, 2014, 2016, 2017, 2018, 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.105 2018/08/10 02:53:33 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.107 2020/03/27 02:49:40 tg Exp $");
|
||||
|
||||
#define EXPRTOK_DEFNS
|
||||
#include "exprtok.h"
|
||||
|
@ -864,7 +864,8 @@ ksh_access(const char *fn, int mode)
|
|||
int rv;
|
||||
struct stat sb;
|
||||
|
||||
if ((rv = access(fn, mode)) == 0 && kshuid == 0 && (mode & X_OK) &&
|
||||
if ((rv = access(fn, mode)) == 0 && (mode & X_OK) &&
|
||||
(kshuid == 0 || ksheuid == 0) &&
|
||||
(rv = stat(fn, &sb)) == 0 && !S_ISDIR(sb.st_mode) &&
|
||||
(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
|
||||
rv = -1;
|
||||
|
@ -885,8 +886,43 @@ static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems,
|
|||
unsigned int val) MKSH_A_PURE;
|
||||
|
||||
/*
|
||||
* Generated from the UCD 11.0.0 by
|
||||
* MirOS: contrib/code/Snippets/eawparse,v 1.12 2017/09/06 16:05:45 tg Exp $
|
||||
* Generated from the UCD 13.0.0 by
|
||||
* MirOS: contrib/code/Snippets/eawparse,v 1.14 2020/03/27 01:33:21 tg Exp $
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Parts Copyright © 1991–2020 Unicode, Inc. All rights reserved.
|
||||
* Distributed under the Terms of Use in
|
||||
* https://www.unicode.org/copyright.html.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of the Unicode data files and any associated documentation
|
||||
* (the "Data Files") or Unicode software and any associated documentation
|
||||
* (the "Software") to deal in the Data Files or Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, and/or sell copies of
|
||||
* the Data Files or Software, and to permit persons to whom the Data Files
|
||||
* or Software are furnished to do so, provided that either
|
||||
* (a) this copyright and permission notice appear with all copies
|
||||
* of the Data Files or Software, or
|
||||
* (b) this copyright and permission notice appear in associated
|
||||
* Documentation.
|
||||
*
|
||||
* THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
|
||||
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
|
||||
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the name of a copyright holder
|
||||
* shall not be used in advertising or otherwise to promote the sale,
|
||||
* use or other dealings in these Data Files or Software without prior
|
||||
* written authorization of the copyright holder.
|
||||
*/
|
||||
|
||||
static const struct mb_ucsrange mb_ucs_combining[] = {
|
||||
|
@ -949,7 +985,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
|||
{ 0x0B3F, 0x0B3F },
|
||||
{ 0x0B41, 0x0B44 },
|
||||
{ 0x0B4D, 0x0B4D },
|
||||
{ 0x0B56, 0x0B56 },
|
||||
{ 0x0B55, 0x0B56 },
|
||||
{ 0x0B62, 0x0B63 },
|
||||
{ 0x0B82, 0x0B82 },
|
||||
{ 0x0BC0, 0x0BC0 },
|
||||
|
@ -972,6 +1008,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
|||
{ 0x0D41, 0x0D44 },
|
||||
{ 0x0D4D, 0x0D4D },
|
||||
{ 0x0D62, 0x0D63 },
|
||||
{ 0x0D81, 0x0D81 },
|
||||
{ 0x0DCA, 0x0DCA },
|
||||
{ 0x0DD2, 0x0DD4 },
|
||||
{ 0x0DD6, 0x0DD6 },
|
||||
|
@ -979,8 +1016,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
|||
{ 0x0E34, 0x0E3A },
|
||||
{ 0x0E47, 0x0E4E },
|
||||
{ 0x0EB1, 0x0EB1 },
|
||||
{ 0x0EB4, 0x0EB9 },
|
||||
{ 0x0EBB, 0x0EBC },
|
||||
{ 0x0EB4, 0x0EBC },
|
||||
{ 0x0EC8, 0x0ECD },
|
||||
{ 0x0F18, 0x0F19 },
|
||||
{ 0x0F35, 0x0F35 },
|
||||
|
@ -1030,7 +1066,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
|||
{ 0x1A65, 0x1A6C },
|
||||
{ 0x1A73, 0x1A7C },
|
||||
{ 0x1A7F, 0x1A7F },
|
||||
{ 0x1AB0, 0x1ABE },
|
||||
{ 0x1AB0, 0x1AC0 },
|
||||
{ 0x1B00, 0x1B03 },
|
||||
{ 0x1B34, 0x1B34 },
|
||||
{ 0x1B36, 0x1B3A },
|
||||
|
@ -1073,6 +1109,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
|||
{ 0xA806, 0xA806 },
|
||||
{ 0xA80B, 0xA80B },
|
||||
{ 0xA825, 0xA826 },
|
||||
{ 0xA82C, 0xA82C },
|
||||
{ 0xA8C4, 0xA8C5 },
|
||||
{ 0xA8E0, 0xA8F1 },
|
||||
{ 0xA8FF, 0xA8FF },
|
||||
|
@ -1081,7 +1118,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
|||
{ 0xA980, 0xA982 },
|
||||
{ 0xA9B3, 0xA9B3 },
|
||||
{ 0xA9B6, 0xA9B9 },
|
||||
{ 0xA9BC, 0xA9BC },
|
||||
{ 0xA9BC, 0xA9BD },
|
||||
{ 0xA9E5, 0xA9E5 },
|
||||
{ 0xAA29, 0xAA2E },
|
||||
{ 0xAA31, 0xAA32 },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2016
|
||||
* Copyright (c) 2016, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -19,25 +19,25 @@
|
|||
*/
|
||||
|
||||
#if defined(EXPRTOK_DEFNS)
|
||||
__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.4 2020/04/07 11:56:46 tg Exp $");
|
||||
/* see range comment below */
|
||||
#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
|
||||
#define FN(name, len, prec, enum) /* nothing */
|
||||
#define FN(name,len,prec,enum) /* nothing */
|
||||
#define F1(enum) /* nothing */
|
||||
#elif defined(EXPRTOK_ENUM)
|
||||
#define F0(name, len, prec, enum) enum = 0,
|
||||
#define FN(name, len, prec, enum) enum,
|
||||
#define F0(name,len,prec,enum) enum = 0,
|
||||
#define FN(name,len,prec,enum) enum,
|
||||
#define F1(enum) enum,
|
||||
#define F2(enum) enum,
|
||||
#define F9(enum) enum
|
||||
#elif defined(EXPRTOK_NAME)
|
||||
#define FN(name, len, prec, enum) name,
|
||||
#define FN(name,len,prec,enum) name,
|
||||
#define F1(enum) ""
|
||||
#elif defined(EXPRTOK_LEN)
|
||||
#define FN(name, len, prec, enum) len,
|
||||
#define FN(name,len,prec,enum) len,
|
||||
#define F1(enum) 0
|
||||
#elif defined(EXPRTOK_PREC)
|
||||
#define FN(name, len, prec, enum) prec,
|
||||
#define FN(name,len,prec,enum) prec,
|
||||
#define F1(enum) P_PRIMARY
|
||||
#endif
|
||||
|
||||
|
@ -53,7 +53,7 @@ __RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
|
|||
/* tokens must be ordered so the longest are first (e.g. += before +) */
|
||||
|
||||
/* some (long) unary operators */
|
||||
FN("++", 2, P_PRIMARY, O_PLUSPLUS = 0) /* before + */
|
||||
F0("++", 2, P_PRIMARY, O_PLUSPLUS) /* before + */
|
||||
FN("--", 2, P_PRIMARY, O_MINUSMINUS) /* before - */
|
||||
/* binary operators */
|
||||
FN("==", 2, P_EQUALITY, O_EQ) /* before = */
|
||||
|
|
252
src/funcs.c
252
src/funcs.c
|
@ -5,7 +5,8 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
|
||||
* 2019, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -38,7 +39,7 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.355 2018/10/20 21:04:28 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.372 2020/04/13 19:51:07 tg Exp $");
|
||||
|
||||
#if HAVE_KILLPG
|
||||
/*
|
||||
|
@ -119,8 +120,6 @@ const struct builtin mkshbuiltins[] = {
|
|||
{Tfalse, c_false},
|
||||
{"fc", c_fc},
|
||||
{Tgetopts, c_getopts},
|
||||
/* deprecated, replaced by typeset -g */
|
||||
{"^=global", c_typeset},
|
||||
{Tjobs, c_jobs},
|
||||
{"kill", c_kill},
|
||||
{"let", c_let},
|
||||
|
@ -133,8 +132,8 @@ const struct builtin mkshbuiltins[] = {
|
|||
#endif
|
||||
{"~rename", c_rename},
|
||||
{"*=return", c_exitreturn},
|
||||
{Tsgset, c_set},
|
||||
{"*=shift", c_shift},
|
||||
{Tsghset, c_set},
|
||||
{"*=#shift", c_shift},
|
||||
{Tgsource, c_dot},
|
||||
#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
|
||||
{Tsuspend, c_suspend},
|
||||
|
@ -183,11 +182,8 @@ struct kill_info {
|
|||
int name_width;
|
||||
};
|
||||
|
||||
static const struct t_op {
|
||||
char op_text[4];
|
||||
Test_op op_num;
|
||||
} u_ops[] = {
|
||||
{"-a", TO_FILAXST },
|
||||
const struct t_op u_ops[] = {
|
||||
/* 0*/ {"-a", TO_FILAXST },
|
||||
{"-b", TO_FILBDEV },
|
||||
{"-c", TO_FILCDEV },
|
||||
{"-d", TO_FILID },
|
||||
|
@ -199,22 +195,23 @@ static const struct t_op {
|
|||
{"-h", TO_FILSYM },
|
||||
{"-k", TO_FILSTCK },
|
||||
{"-L", TO_FILSYM },
|
||||
{"-n", TO_STNZE },
|
||||
/*12*/ {"-n", TO_STNZE },
|
||||
{"-O", TO_FILUID },
|
||||
{"-o", TO_OPTION },
|
||||
/*14*/ {"-o", TO_OPTION },
|
||||
{"-p", TO_FILFIFO },
|
||||
{"-r", TO_FILRD },
|
||||
/*16*/ {"-r", TO_FILRD },
|
||||
{"-S", TO_FILSOCK },
|
||||
{"-s", TO_FILGZ },
|
||||
{"-t", TO_FILTT },
|
||||
{"-u", TO_FILSETU },
|
||||
/*20*/ {"-u", TO_FILSETU },
|
||||
{"-v", TO_ISSET },
|
||||
{"-w", TO_FILWR },
|
||||
{"-x", TO_FILEX },
|
||||
/*23*/ {"-x", TO_FILEX },
|
||||
{"-z", TO_STZER },
|
||||
{"", TO_NONOP }
|
||||
};
|
||||
static const struct t_op b_ops[] = {
|
||||
cta(u_ops_size, NELEM(u_ops) == 26);
|
||||
const struct t_op b_ops[] = {
|
||||
{"=", TO_STEQL },
|
||||
{"==", TO_STEQL },
|
||||
{"!=", TO_STNEQ },
|
||||
|
@ -337,7 +334,7 @@ c_print(const char **wp)
|
|||
/* BSD "echo" cmd, Debian Policy 10.4 compliant */
|
||||
++wp;
|
||||
bsd_echo:
|
||||
if (*wp && !strcmp(*wp, "-n")) {
|
||||
if (*wp && !strcmp(*wp, Tdn)) {
|
||||
po.nl = false;
|
||||
++wp;
|
||||
}
|
||||
|
@ -724,12 +721,16 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
|
|||
}
|
||||
break;
|
||||
case CALIAS:
|
||||
if (vflag) {
|
||||
shprintf("%s is an %s%s for ", id,
|
||||
if (!vflag && iscommand)
|
||||
shprintf(Tf_s_, Talias);
|
||||
if (vflag || iscommand)
|
||||
print_value_quoted(shl_stdout, id);
|
||||
if (vflag)
|
||||
shprintf(" is an %s%s for ",
|
||||
(tp->flag & EXPORT) ? "exported " : "",
|
||||
Talias);
|
||||
} else if (iscommand)
|
||||
shprintf("%s %s=", Talias, id);
|
||||
else if (iscommand)
|
||||
shf_putc('=', shl_stdout);
|
||||
print_value_quoted(shl_stdout, tp->val.s);
|
||||
break;
|
||||
case CKEYWD:
|
||||
|
@ -751,10 +752,15 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
|
|||
bool
|
||||
valid_alias_name(const char *cp)
|
||||
{
|
||||
if (ord(*cp) == ORD('-'))
|
||||
return (false);
|
||||
if (ord(cp[0]) == ORD('[') && ord(cp[1]) == ORD('[') && !cp[2])
|
||||
switch (ord(*cp)) {
|
||||
case ORD('+'):
|
||||
case ORD('-'):
|
||||
return (false);
|
||||
case ORD('['):
|
||||
if (ord(cp[1]) == ORD('[') && !cp[2])
|
||||
return (false);
|
||||
break;
|
||||
}
|
||||
while (*cp)
|
||||
if (ctype(*cp, C_ALIAS))
|
||||
++cp;
|
||||
|
@ -843,7 +849,7 @@ c_alias(const char **wp)
|
|||
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
|
||||
if (pflag)
|
||||
shprintf(Tf_s_, Talias);
|
||||
shf_puts(ap->name, shl_stdout);
|
||||
print_value_quoted(shl_stdout, ap->name);
|
||||
if (prefix != '+') {
|
||||
shf_putc('=', shl_stdout);
|
||||
print_value_quoted(shl_stdout, ap->val.s);
|
||||
|
@ -873,7 +879,7 @@ c_alias(const char **wp)
|
|||
if (ap != NULL && (ap->flag&ISSET)) {
|
||||
if (pflag)
|
||||
shprintf(Tf_s_, Talias);
|
||||
shf_puts(ap->name, shl_stdout);
|
||||
print_value_quoted(shl_stdout, ap->name);
|
||||
if (prefix != '+') {
|
||||
shf_putc('=', shl_stdout);
|
||||
print_value_quoted(shl_stdout, ap->val.s);
|
||||
|
@ -1297,54 +1303,32 @@ c_bind(const char **wp)
|
|||
#ifndef MKSH_SMALL
|
||||
bool macro = false;
|
||||
#endif
|
||||
bool list = false;
|
||||
const char *cp;
|
||||
char *up;
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt,
|
||||
#ifndef MKSH_SMALL
|
||||
"lm"
|
||||
#else
|
||||
"l"
|
||||
#endif
|
||||
)) != -1)
|
||||
if (x_bind_check()) {
|
||||
bi_errorf("can't bind, not a tty");
|
||||
return (1);
|
||||
}
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != -1)
|
||||
switch (optc) {
|
||||
case 'l':
|
||||
list = true;
|
||||
break;
|
||||
return (x_bind_list());
|
||||
#ifndef MKSH_SMALL
|
||||
case 'm':
|
||||
macro = true;
|
||||
break;
|
||||
#endif
|
||||
case '?':
|
||||
default:
|
||||
return (1);
|
||||
}
|
||||
wp += builtin_opt.optind;
|
||||
|
||||
if (*wp == NULL)
|
||||
/* list all */
|
||||
rv = x_bind(NULL, NULL,
|
||||
#ifndef MKSH_SMALL
|
||||
false,
|
||||
#endif
|
||||
list);
|
||||
return (x_bind_showall());
|
||||
|
||||
for (; *wp != NULL; wp++) {
|
||||
if ((cp = cstrchr(*wp, '=')) == NULL)
|
||||
up = NULL;
|
||||
else {
|
||||
strdupx(up, *wp, ATEMP);
|
||||
up[cp++ - *wp] = '\0';
|
||||
}
|
||||
if (x_bind(up ? up : *wp, cp,
|
||||
#ifndef MKSH_SMALL
|
||||
macro,
|
||||
#endif
|
||||
false))
|
||||
rv = 1;
|
||||
afree(up, ATEMP);
|
||||
}
|
||||
do {
|
||||
rv |= x_bind(*wp SMALLP(macro));
|
||||
} while (*++wp);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
@ -1353,10 +1337,17 @@ c_bind(const char **wp)
|
|||
int
|
||||
c_shift(const char **wp)
|
||||
{
|
||||
struct block *l = e->loc;
|
||||
int n;
|
||||
mksh_ari_t val;
|
||||
const char *arg;
|
||||
struct block *l = e->loc;
|
||||
|
||||
if ((l->flags & BF_RESETSPEC)) {
|
||||
/* prevent pollution */
|
||||
l->flags &= ~BF_RESETSPEC;
|
||||
/* operate on parent environment */
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return (1);
|
||||
|
@ -1375,6 +1366,7 @@ c_shift(const char **wp)
|
|||
bi_errorf(Tf_sD_s, Tbadnum, arg);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (l->argc < n) {
|
||||
bi_errorf("nothing to shift");
|
||||
return (1);
|
||||
|
@ -1589,7 +1581,6 @@ c_wait(const char **wp)
|
|||
return (rv);
|
||||
}
|
||||
|
||||
static const char REPLY[] = "REPLY";
|
||||
int
|
||||
c_read(const char **wp)
|
||||
{
|
||||
|
@ -1670,7 +1661,7 @@ c_read(const char **wp)
|
|||
if (!builtin_opt.optarg[0])
|
||||
fd = 0;
|
||||
else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
|
||||
bi_errorf(Tf_sD_sD_s, "-u", builtin_opt.optarg, ccp);
|
||||
bi_errorf(Tf_sD_sD_s, Tdu, builtin_opt.optarg, ccp);
|
||||
return (2);
|
||||
}
|
||||
break;
|
||||
|
@ -1679,7 +1670,7 @@ c_read(const char **wp)
|
|||
}
|
||||
wp += builtin_opt.optind;
|
||||
if (*wp == NULL)
|
||||
*--wp = REPLY;
|
||||
*--wp = TREPLY;
|
||||
|
||||
if (intoarray && wp[1] != NULL) {
|
||||
bi_errorf(Ttoo_many_args);
|
||||
|
@ -2029,7 +2020,6 @@ int
|
|||
c_eval(const char **wp)
|
||||
{
|
||||
struct source *s, *saves = source;
|
||||
unsigned char savef;
|
||||
int rv;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
|
@ -2072,10 +2062,7 @@ c_eval(const char **wp)
|
|||
/* SUSv4: OR with a high value never written otherwise */
|
||||
exstat |= 0x4000;
|
||||
|
||||
savef = Flag(FERREXIT);
|
||||
Flag(FERREXIT) |= 0x80;
|
||||
rv = shell(s, 2);
|
||||
Flag(FERREXIT) = savef;
|
||||
source = saves;
|
||||
afree(s, ATEMP);
|
||||
if (exstat & 0x4000)
|
||||
|
@ -2237,7 +2224,13 @@ c_set(const char **wp)
|
|||
int argi;
|
||||
bool setargs;
|
||||
struct block *l = e->loc;
|
||||
const char **owp;
|
||||
|
||||
if ((l->flags & BF_RESETSPEC)) {
|
||||
/* prevent pollution */
|
||||
l->flags &= ~BF_RESETSPEC;
|
||||
/* operate on parent environment */
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
if (wp[1] == NULL) {
|
||||
static const char *args[] = { Tset, "-", NULL };
|
||||
|
@ -2248,6 +2241,8 @@ c_set(const char **wp)
|
|||
return (2);
|
||||
/* set $# and $* */
|
||||
if (setargs) {
|
||||
const char **owp;
|
||||
|
||||
wp += argi - 1;
|
||||
owp = wp;
|
||||
/* save $0 */
|
||||
|
@ -2579,25 +2574,25 @@ c_mknod(const char **wp)
|
|||
#endif
|
||||
|
||||
/*-
|
||||
test(1) roughly accepts the following grammar:
|
||||
oexpr ::= aexpr | aexpr "-o" oexpr ;
|
||||
aexpr ::= nexpr | nexpr "-a" aexpr ;
|
||||
nexpr ::= primary | "!" nexpr ;
|
||||
primary ::= unary-operator operand
|
||||
| operand binary-operator operand
|
||||
| operand
|
||||
| "(" oexpr ")"
|
||||
;
|
||||
|
||||
unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
|
||||
"-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
|
||||
"-u"|"-v"|"-w"|"-x"|"-z";
|
||||
|
||||
binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
|
||||
"-lt"|"-le"|"-ef"|"-nt"|"-ot";
|
||||
|
||||
operand ::= <anything>
|
||||
*/
|
||||
* test(1) roughly accepts the following grammar:
|
||||
* oexpr ::= aexpr | aexpr "-o" oexpr ;
|
||||
* aexpr ::= nexpr | nexpr "-a" aexpr ;
|
||||
* nexpr ::= primary | "!" nexpr ;
|
||||
* primary ::= unary-operator operand
|
||||
* | operand binary-operator operand
|
||||
* | operand
|
||||
* | "(" oexpr ")"
|
||||
* ;
|
||||
*
|
||||
* unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
|
||||
* "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
|
||||
* "-u"|"-v"|"-w"|"-x"|"-z";
|
||||
*
|
||||
* binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
|
||||
* "-lt"|"-le"|"-ef"|"-nt"|"-ot";
|
||||
*
|
||||
* operand ::= <anything>
|
||||
*/
|
||||
|
||||
/* POSIX says > 1 for errors */
|
||||
#define T_ERR_EXIT 2
|
||||
|
@ -2749,13 +2744,36 @@ test_isop(Test_meta meta, const char *s)
|
|||
}
|
||||
|
||||
#ifdef __OS2__
|
||||
#define test_access(name, mode) access_ex(access, (name), (mode))
|
||||
#define test_stat(name, buffer) stat_ex((name), (buffer))
|
||||
#define test_access(name,mode) access_ex(access, (name), (mode))
|
||||
#define test_stat(name,buffer) stat_ex(stat, (name), (buffer))
|
||||
#define test_lstat(name,buffer) stat_ex(lstat, (name), (buffer))
|
||||
#else
|
||||
#define test_access(name, mode) access((name), (mode))
|
||||
#define test_stat(name, buffer) stat((name), (buffer))
|
||||
#define test_access(name,mode) access((name), (mode))
|
||||
#define test_stat(name,buffer) stat((name), (buffer))
|
||||
#define test_lstat(name,buffer) lstat((name), (buffer))
|
||||
#endif
|
||||
|
||||
#if HAVE_ST_MTIM
|
||||
#undef st_mtimensec
|
||||
#define st_mtimensec st_mtim.tv_nsec
|
||||
#endif
|
||||
|
||||
static int
|
||||
mtimecmp(const struct stat *sb1, const struct stat *sb2)
|
||||
{
|
||||
if (sb1->st_mtime < sb2->st_mtime)
|
||||
return (-1);
|
||||
if (sb1->st_mtime > sb2->st_mtime)
|
||||
return (1);
|
||||
#if (HAVE_ST_MTIMENSEC || HAVE_ST_MTIM)
|
||||
if (sb1->st_mtimensec < sb2->st_mtimensec)
|
||||
return (-1);
|
||||
if (sb1->st_mtimensec > sb2->st_mtimensec)
|
||||
return (1);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
||||
bool do_eval)
|
||||
|
@ -2849,31 +2867,31 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
|
||||
/* -d */
|
||||
case TO_FILID:
|
||||
return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
|
||||
return (test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
|
||||
|
||||
/* -c */
|
||||
case TO_FILCDEV:
|
||||
return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
|
||||
return (test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
|
||||
|
||||
/* -b */
|
||||
case TO_FILBDEV:
|
||||
return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
|
||||
return (test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
|
||||
|
||||
/* -p */
|
||||
case TO_FILFIFO:
|
||||
return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
|
||||
return (test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
|
||||
|
||||
/* -h or -L */
|
||||
case TO_FILSYM:
|
||||
#ifdef MKSH__NO_SYMLINK
|
||||
return (0);
|
||||
#else
|
||||
return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
|
||||
return (test_lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
|
||||
#endif
|
||||
|
||||
/* -S */
|
||||
case TO_FILSOCK:
|
||||
return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
|
||||
return (test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
|
||||
|
||||
/* -H => HP context dependent files (directories) */
|
||||
case TO_FILCDF:
|
||||
|
@ -2892,7 +2910,7 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
*/
|
||||
|
||||
nv = shf_smprintf("%s+", opnd1);
|
||||
i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
|
||||
i = (test_stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
|
||||
afree(nv, ATEMP);
|
||||
return (i);
|
||||
}
|
||||
|
@ -2902,18 +2920,18 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
|
||||
/* -u */
|
||||
case TO_FILSETU:
|
||||
return (stat(opnd1, &b1) == 0 &&
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(b1.st_mode & S_ISUID) == S_ISUID);
|
||||
|
||||
/* -g */
|
||||
case TO_FILSETG:
|
||||
return (stat(opnd1, &b1) == 0 &&
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(b1.st_mode & S_ISGID) == S_ISGID);
|
||||
|
||||
/* -k */
|
||||
case TO_FILSTCK:
|
||||
#ifdef S_ISVTX
|
||||
return (stat(opnd1, &b1) == 0 &&
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(b1.st_mode & S_ISVTX) == S_ISVTX);
|
||||
#else
|
||||
return (0);
|
||||
|
@ -2921,7 +2939,8 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
|
||||
/* -s */
|
||||
case TO_FILGZ:
|
||||
return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(off_t)b1.st_size > (off_t)0);
|
||||
|
||||
/* -t */
|
||||
case TO_FILTT:
|
||||
|
@ -2934,11 +2953,13 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
|
||||
/* -O */
|
||||
case TO_FILUID:
|
||||
return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(uid_t)b1.st_uid == ksheuid);
|
||||
|
||||
/* -G */
|
||||
case TO_FILGID:
|
||||
return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(gid_t)b1.st_gid == kshegid);
|
||||
|
||||
/*
|
||||
* Binary Operators
|
||||
|
@ -2976,9 +2997,9 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
* ksh88/ksh93 succeed if file2 can't be stated
|
||||
* (subtly different from 'does not exist').
|
||||
*/
|
||||
return (stat(opnd1, &b1) == 0 &&
|
||||
(((s = stat(opnd2, &b2)) == 0 &&
|
||||
b1.st_mtime > b2.st_mtime) || s < 0));
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
(((s = test_stat(opnd2, &b2)) == 0 &&
|
||||
mtimecmp(&b1, &b2) > 0) || s < 0));
|
||||
|
||||
/* -ot */
|
||||
case TO_FILOT:
|
||||
|
@ -2986,13 +3007,14 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||
* ksh88/ksh93 succeed if file1 can't be stated
|
||||
* (subtly different from 'does not exist').
|
||||
*/
|
||||
return (stat(opnd2, &b2) == 0 &&
|
||||
(((s = stat(opnd1, &b1)) == 0 &&
|
||||
b1.st_mtime < b2.st_mtime) || s < 0));
|
||||
return (test_stat(opnd2, &b2) == 0 &&
|
||||
(((s = test_stat(opnd1, &b1)) == 0 &&
|
||||
mtimecmp(&b1, &b2) < 0) || s < 0));
|
||||
|
||||
/* -ef */
|
||||
case TO_FILEQ:
|
||||
return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
|
||||
return (test_stat(opnd1, &b1) == 0 &&
|
||||
test_stat(opnd2, &b2) == 0 &&
|
||||
b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
|
||||
|
||||
/* all other cases */
|
||||
|
@ -3155,7 +3177,7 @@ ptest_isa(Test_env *te, Test_meta meta)
|
|||
{
|
||||
/* Order important - indexed by Test_meta values */
|
||||
static const char * const tokens[] = {
|
||||
"-o", "-a", "!", "(", ")"
|
||||
Tdo, Tda, "!", "(", ")"
|
||||
};
|
||||
Test_op rv;
|
||||
|
||||
|
@ -3469,7 +3491,7 @@ c_cat(const char **wp)
|
|||
#define MKSH_CAT_BUFSIZ 4096
|
||||
|
||||
/* parse options: POSIX demands we support "-u" as no-op */
|
||||
while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
|
||||
while ((rv = ksh_getopt(wp, &builtin_opt, Tu)) != -1) {
|
||||
switch (rv) {
|
||||
case 'u':
|
||||
/* we already operate unbuffered */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2014, 2015, 2016, 2017, 2018
|
||||
* 2011, 2012, 2014, 2015, 2016, 2017, 2018, 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -27,7 +27,7 @@
|
|||
#include <sys/file.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.167 2018/04/28 17:16:54 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.169 2019/09/16 21:10:33 tg Exp $");
|
||||
|
||||
Trap sigtraps[ksh_NSIG + 1];
|
||||
static struct sigaction Sigact_ign;
|
||||
|
@ -504,6 +504,8 @@ findhist(int start, int fwd, const char *str, bool anchored)
|
|||
void
|
||||
sethistsize(mksh_ari_t n)
|
||||
{
|
||||
if (n > 65535)
|
||||
n = 65535;
|
||||
if (n > 0 && n != histsize) {
|
||||
int cursize = histptr - history;
|
||||
|
||||
|
|
31
src/jobs.c
31
src/jobs.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
|
||||
* 2012, 2013, 2014, 2015, 2016, 2018
|
||||
* 2012, 2013, 2014, 2015, 2016, 2018, 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.127 2018/07/15 16:23:10 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.128 2019/12/11 19:46:20 tg Exp $");
|
||||
|
||||
#if HAVE_KILLPG
|
||||
#define mksh_killpg killpg
|
||||
|
@ -88,6 +88,7 @@ struct proc {
|
|||
|
||||
typedef struct job Job;
|
||||
struct job {
|
||||
ALLOC_ITEM alloc_INT; /* internal, do not touch */
|
||||
Job *next; /* next job in list */
|
||||
Proc *proc_list; /* process list */
|
||||
Proc *last_proc; /* last process in list */
|
||||
|
@ -1778,16 +1779,26 @@ new_job(void)
|
|||
if (free_jobs != NULL) {
|
||||
newj = free_jobs;
|
||||
free_jobs = free_jobs->next;
|
||||
} else
|
||||
newj = alloc(sizeof(Job), APERM);
|
||||
} else {
|
||||
char *cp;
|
||||
|
||||
/*
|
||||
* struct job includes ALLOC_ITEM for alignment constraints
|
||||
* so first get the actually used memory, then assign it
|
||||
*/
|
||||
cp = alloc(sizeof(Job) - sizeof(ALLOC_ITEM), APERM);
|
||||
/* undo what alloc() did to the malloc result address */
|
||||
newj = (void *)(cp - sizeof(ALLOC_ITEM));
|
||||
}
|
||||
|
||||
/* brute force method */
|
||||
for (i = 1; ; i++) {
|
||||
for (j = job_list; j && j->job != i; j = j->next)
|
||||
;
|
||||
if (j == NULL)
|
||||
break;
|
||||
}
|
||||
i = 0;
|
||||
do {
|
||||
++i;
|
||||
j = job_list;
|
||||
while (j && j->job != i)
|
||||
j = j->next;
|
||||
} while (j);
|
||||
newj->job = i;
|
||||
|
||||
return (newj);
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.250 2018/10/20 18:34:14 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.251 2020/03/10 23:48:40 tg Exp $");
|
||||
|
||||
/*
|
||||
* states while lexing word
|
||||
|
@ -451,8 +451,7 @@ yylex(int cf)
|
|||
statep->ls_adelim.num = 1;
|
||||
statep->nparen = 0;
|
||||
break;
|
||||
} else if (ctype(c, C_DIGIT | C_DOLAR | C_SPC) ||
|
||||
/*XXX what else? */
|
||||
} else if (ctype(c, C_ALNUX | C_DOLAR | C_SPC) ||
|
||||
c == '(' /*)*/) {
|
||||
/* substring subst. */
|
||||
if (c != ' ') {
|
||||
|
|
127
src/main.c
127
src/main.c
|
@ -6,7 +6,7 @@
|
|||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
* 2019
|
||||
* 2019, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -35,7 +35,7 @@
|
|||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.351 2019/01/05 13:24:18 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.364 2020/04/13 17:04:14 tg Exp $");
|
||||
|
||||
#ifndef MKSHRC_PATH
|
||||
#define MKSHRC_PATH "~/.mkshrc"
|
||||
|
@ -65,8 +65,8 @@ static const char initsubs[] =
|
|||
"${EPOCHREALTIME=}";
|
||||
|
||||
static const char *initcoms[] = {
|
||||
Ttypeset, "-r", initvsn, NULL,
|
||||
Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
|
||||
Ttypeset, Tdr, initvsn, NULL,
|
||||
Ttypeset, Tdx, "HOME", TPATH, TSHELL, NULL,
|
||||
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
|
||||
Talias,
|
||||
"integer=\\\\builtin typeset -i",
|
||||
|
@ -90,7 +90,7 @@ static const char *initcoms[] = {
|
|||
};
|
||||
|
||||
static const char *restr_com[] = {
|
||||
Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
|
||||
Ttypeset, Tdr, TPATH, TENV, TSHELL, NULL
|
||||
};
|
||||
|
||||
static bool initio_done;
|
||||
|
@ -100,7 +100,6 @@ static struct env env;
|
|||
struct env *e = &env;
|
||||
|
||||
/* compile-time assertions */
|
||||
#define cta(name, expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
|
||||
|
||||
/* this one should be defined by the standard */
|
||||
cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
|
||||
|
@ -227,7 +226,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
|||
int argi, i;
|
||||
Source *s = NULL;
|
||||
struct block *l;
|
||||
unsigned char restricted_shell, errexit, utf_flag;
|
||||
unsigned char restricted_shell = 0, errexit, utf_flag;
|
||||
char *cp;
|
||||
const char *ccp, **wp;
|
||||
struct tbl *vp;
|
||||
|
@ -303,7 +302,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
|||
|
||||
/* define built-in commands and see if we were called as one */
|
||||
ktinit(APERM, &builtins,
|
||||
/* currently up to 54 builtins: 75% of 128 = 2^7 */
|
||||
/* currently up to 52 builtins: 75% of 128 = 2^7 */
|
||||
7);
|
||||
for (i = 0; mkshbuiltins[i].name != NULL; i++)
|
||||
if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
|
||||
|
@ -315,7 +314,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
|||
argi = parse_args(argv, OF_FIRSTTIME, NULL);
|
||||
if (argi < 0)
|
||||
return (1);
|
||||
|
||||
/* called as rsh, rmksh, -rsh, -rmksh, etc.? */
|
||||
if (ord(*ccp) == ORD('r')) {
|
||||
++ccp;
|
||||
++restricted_shell;
|
||||
}
|
||||
#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
|
||||
/* are we called as -sh or /bin/sh or so? */
|
||||
if (!strcmp(ccp, "sh" MKSH_EXE_EXT)) {
|
||||
|
@ -642,7 +645,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
|||
}
|
||||
|
||||
/* Disable during .profile/ENV reading */
|
||||
restricted_shell = Flag(FRESTRICTED);
|
||||
restricted_shell |= Flag(FRESTRICTED);
|
||||
Flag(FRESTRICTED) = 0;
|
||||
errexit = Flag(FERREXIT);
|
||||
Flag(FERREXIT) = 0;
|
||||
|
@ -656,7 +659,16 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
|||
|
||||
if (Flag(FLOGIN))
|
||||
include(MKSH_SYSTEM_PROFILE, 0, NULL, true);
|
||||
if (!Flag(FPRIVILEGED)) {
|
||||
if (Flag(FPRIVILEGED)) {
|
||||
include(MKSH_SUID_PROFILE, 0, NULL, true);
|
||||
/* note whether -p was enabled during startup */
|
||||
if (Flag(FPRIVILEGED) == 1)
|
||||
/* allow set -p to setuid() later */
|
||||
Flag(FPRIVILEGED) = 3;
|
||||
else
|
||||
/* turn off -p if not set explicitly */
|
||||
change_flag(FPRIVILEGED, OF_INTERNAL, false);
|
||||
} else {
|
||||
if (Flag(FLOGIN))
|
||||
include(substitute("$HOME/.profile", 0), 0, NULL, true);
|
||||
if (Flag(FTALKING)) {
|
||||
|
@ -664,13 +676,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
|||
if (cp[0] != '\0')
|
||||
include(cp, 0, NULL, true);
|
||||
}
|
||||
} else {
|
||||
include(MKSH_SUID_PROFILE, 0, NULL, true);
|
||||
/* turn off -p if not set explicitly */
|
||||
if (Flag(FPRIVILEGED) != 1)
|
||||
change_flag(FPRIVILEGED, OF_INTERNAL, false);
|
||||
}
|
||||
|
||||
if (restricted_shell) {
|
||||
c_builtin(restr_com);
|
||||
/* After typeset command... */
|
||||
|
@ -741,6 +747,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
|
|||
switch (i) {
|
||||
case LRETURN:
|
||||
case LERROR:
|
||||
case LERREXT:
|
||||
/* see below */
|
||||
return (exstat & 0xFF);
|
||||
case LINTR:
|
||||
|
@ -808,6 +815,8 @@ shell(Source * volatile s, volatile int level)
|
|||
int i;
|
||||
|
||||
newenv(level == 2 ? E_EVAL : E_PARSE);
|
||||
if (level == 2)
|
||||
e->flags |= EF_IN_EVAL;
|
||||
if (interactive)
|
||||
really_exit = false;
|
||||
switch ((i = kshsetjmp(e->jbuf))) {
|
||||
|
@ -815,18 +824,21 @@ shell(Source * volatile s, volatile int level)
|
|||
break;
|
||||
case LBREAK:
|
||||
case LCONTIN:
|
||||
if (level != 2) {
|
||||
source = old_source;
|
||||
quitenv(NULL);
|
||||
internal_errorf(Tf_cant_s, Tshell,
|
||||
i == LBREAK ? Tbreak : Tcontinue);
|
||||
/* assert: interactive == false */
|
||||
source = old_source;
|
||||
quitenv(NULL);
|
||||
if (level == 2) {
|
||||
/* keep on going */
|
||||
unwind(i);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
/* assert: interactive == false */
|
||||
/* FALLTHROUGH */
|
||||
internal_errorf(Tf_cant_s, Tshell,
|
||||
i == LBREAK ? Tbreak : Tcontinue);
|
||||
/* NOTREACHED */
|
||||
case LINTR:
|
||||
/* we get here if SIGINT not caught or ignored */
|
||||
case LERROR:
|
||||
case LERREXT:
|
||||
case LSHELL:
|
||||
if (interactive) {
|
||||
if (i == LINTR)
|
||||
|
@ -857,6 +869,8 @@ shell(Source * volatile s, volatile int level)
|
|||
case LRETURN:
|
||||
source = old_source;
|
||||
quitenv(NULL);
|
||||
if (i == LERREXT && level == 2)
|
||||
return (exstat & 0xFF);
|
||||
/* keep on going */
|
||||
unwind(i);
|
||||
/* NOTREACHED */
|
||||
|
@ -916,8 +930,8 @@ shell(Source * volatile s, volatile int level)
|
|||
source_no_tree:
|
||||
reclaim();
|
||||
}
|
||||
quitenv(NULL);
|
||||
source = old_source;
|
||||
quitenv(NULL);
|
||||
return (exstat & 0xFF);
|
||||
}
|
||||
|
||||
|
@ -926,36 +940,25 @@ shell(Source * volatile s, volatile int level)
|
|||
void
|
||||
unwind(int i)
|
||||
{
|
||||
/*
|
||||
* This is a kludge. We need to restore everything that was
|
||||
* changed in the new environment, see cid 1005090337C7A669439
|
||||
* and 10050903386452ACBF1, but fail to even save things most of
|
||||
* the time. funcs.c:c_eval() changes FERREXIT temporarily to 0,
|
||||
* which needs to be restored thus (related to Debian #696823).
|
||||
* We did not save the shell flags, so we use a special or'd
|
||||
* value here... this is mostly to clean up behind *other*
|
||||
* callers of unwind(LERROR) here; exec.c has the regular case.
|
||||
*/
|
||||
if (Flag(FERREXIT) & 0x80) {
|
||||
/* GNU bash does not run this trapsig */
|
||||
trapsig(ksh_SIGERR);
|
||||
Flag(FERREXIT) &= ~0x80;
|
||||
}
|
||||
/* during eval, skip FERREXIT trap */
|
||||
if (i == LERREXT && (e->flags & EF_IN_EVAL))
|
||||
goto defer_traps;
|
||||
|
||||
/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
|
||||
if (i == LEXIT || ((i == LERROR || i == LINTR) &&
|
||||
if (i == LEXIT || ((i == LERROR || i == LERREXT || i == LINTR) &&
|
||||
sigtraps[ksh_SIGEXIT].trap &&
|
||||
(!Flag(FTALKING) || Flag(FERREXIT)))) {
|
||||
++trap_nested;
|
||||
runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
|
||||
--trap_nested;
|
||||
i = LLEAVE;
|
||||
} else if (Flag(FERREXIT) == 1 && (i == LERROR || i == LINTR)) {
|
||||
} else if (Flag(FERREXIT) && (i == LERROR || i == LERREXT || i == LINTR)) {
|
||||
++trap_nested;
|
||||
runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
|
||||
--trap_nested;
|
||||
i = LLEAVE;
|
||||
}
|
||||
defer_traps:
|
||||
|
||||
while (/* CONSTCOND */ 1) {
|
||||
switch (e->type) {
|
||||
|
@ -998,8 +1001,7 @@ newenv(int type)
|
|||
ep->temps = NULL;
|
||||
ep->yyrecursive_statep = NULL;
|
||||
ep->type = type;
|
||||
ep->flags = 0;
|
||||
/* jump buffer is invalid because flags == 0 */
|
||||
ep->flags = e->flags & EF_IN_EVAL;
|
||||
e = ep;
|
||||
}
|
||||
|
||||
|
@ -1336,6 +1338,39 @@ bi_errorf(const char *fmt, ...)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by functions called by builtins and not:
|
||||
* identical to errorfx if first argument is nil,
|
||||
* like bi_errorf storing the errorlevel into it otherwise
|
||||
*/
|
||||
void
|
||||
maybe_errorf(int *ep, int rc, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
/* debugging: note that stdout not valid */
|
||||
shl_stdout_ok = false;
|
||||
|
||||
exstat = rc;
|
||||
|
||||
va_start(va, fmt);
|
||||
vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
|
||||
(ep ? VWARNINGF_BUILTIN : 0), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (!ep)
|
||||
goto and_out;
|
||||
*ep = rc;
|
||||
|
||||
/* POSIX special builtins cause non-interactive shells to exit */
|
||||
if (builtin_spec) {
|
||||
builtin_argv0 = NULL;
|
||||
/* may not want to use LERROR here */
|
||||
and_out:
|
||||
unwind(LERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when something that shouldn't happen does */
|
||||
void
|
||||
internal_errorf(const char *fmt, ...)
|
||||
|
@ -1435,7 +1470,7 @@ initio(void)
|
|||
if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
|
||||
if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
|
||||
errorf("can't get home directory");
|
||||
lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
|
||||
strpathx(lfp, lfp, "mksh-dbg.txt", 1);
|
||||
}
|
||||
|
||||
if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
|
||||
|
@ -1997,7 +2032,7 @@ init_environ(void)
|
|||
errno = 0;
|
||||
if ((dent = readdir(dirp)) != NULL) {
|
||||
if (skip_varname(dent->d_name, true)[0] == '\0') {
|
||||
xp = shf_smprintf(Tf_sSs, MKSH_ENVDIR, dent->d_name);
|
||||
strpathx(xp, MKSH_ENVDIR, dent->d_name, 1);
|
||||
if (!(shf = shf_open(xp, O_RDONLY, 0, 0))) {
|
||||
warningf(false,
|
||||
"cannot read environment %s from %s: %s",
|
||||
|
|
117
src/misc.c
117
src/misc.c
|
@ -3,7 +3,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
* Copyright (c) 2015
|
||||
* Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
|
@ -32,7 +32,7 @@
|
|||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.293 2018/08/10 02:53:35 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.297 2020/04/07 11:56:46 tg Exp $");
|
||||
|
||||
#define KSH_CHVT_FLAG
|
||||
#ifdef MKSH_SMALL
|
||||
|
@ -62,13 +62,13 @@ static int make_path(const char *, const char *, char **, XString *, int *);
|
|||
|
||||
#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
|
||||
/* we don't need to check for other codes, EPERM won't happen */
|
||||
#define DO_SETUID(func, argvec) do { \
|
||||
#define DO_SETUID(func,argvec) do { \
|
||||
if ((func argvec) && errno == EAGAIN) \
|
||||
errorf("%s failed with EAGAIN, probably due to a" \
|
||||
" too low process limit; aborting", #func); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#else
|
||||
#define DO_SETUID(func, argvec) func argvec
|
||||
#define DO_SETUID(func,argvec) func argvec
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -216,70 +216,85 @@ getoptions(void)
|
|||
void
|
||||
change_flag(enum sh_flag f, int what, bool newset)
|
||||
{
|
||||
unsigned char oldval;
|
||||
unsigned char oldval = Flag(f);
|
||||
unsigned char newval = (newset ? 1 : 0);
|
||||
|
||||
if (f == FXTRACE) {
|
||||
change_xtrace(newval, true);
|
||||
return;
|
||||
}
|
||||
oldval = Flag(f);
|
||||
Flag(f) = newval = (newset ? 1 : 0);
|
||||
#ifndef MKSH_UNEMPLOYED
|
||||
if (f == FMONITOR) {
|
||||
if (what != OF_CMDLINE && newval != oldval)
|
||||
j_change();
|
||||
} else
|
||||
#endif
|
||||
#ifndef MKSH_NO_CMDLINE_EDITING
|
||||
if ((
|
||||
#if !MKSH_S_NOVI
|
||||
f == FVI ||
|
||||
#endif
|
||||
f == FEMACS || f == FGMACS) && newval) {
|
||||
#if !MKSH_S_NOVI
|
||||
Flag(FVI) =
|
||||
#endif
|
||||
Flag(FEMACS) = Flag(FGMACS) = 0;
|
||||
Flag(f) = newval;
|
||||
} else
|
||||
#endif
|
||||
if (f == FPRIVILEGED && oldval && !newval) {
|
||||
/* Turning off -p? */
|
||||
} else if (f == FPRIVILEGED) {
|
||||
if (!oldval)
|
||||
/* no getting back dropped privs */
|
||||
return;
|
||||
else if (!newval) {
|
||||
/* turning off -p */
|
||||
kshegid = kshgid;
|
||||
ksheuid = kshuid;
|
||||
} else if (oldval != 3)
|
||||
/* nor going full sugid */
|
||||
goto change_flag;
|
||||
|
||||
/*XXX this can probably be optimised */
|
||||
kshegid = kshgid = getgid();
|
||||
ksheuid = kshuid = getuid();
|
||||
/* +++ set group IDs +++ */
|
||||
#if HAVE_SETRESUGID
|
||||
DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
|
||||
#if HAVE_SETGROUPS
|
||||
/* setgroups doesn't EAGAIN on Linux */
|
||||
setgroups(1, &kshegid);
|
||||
#endif
|
||||
DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
|
||||
DO_SETUID(setresgid, (kshegid, kshegid, kshgid));
|
||||
#else /* !HAVE_SETRESUGID */
|
||||
/* setgid, setegid, seteuid don't EAGAIN on Linux */
|
||||
/* setgid, setegid don't EAGAIN on Linux */
|
||||
setgid(kshegid);
|
||||
#ifndef MKSH__NO_SETEUGID
|
||||
setegid(kshegid);
|
||||
#endif
|
||||
#endif /* !MKSH__NO_SETEUGID */
|
||||
#endif /* !HAVE_SETRESUGID */
|
||||
|
||||
/* +++ wipe groups vector +++ */
|
||||
#if HAVE_SETGROUPS
|
||||
/* setgroups doesn't EAGAIN on Linux */
|
||||
setgroups(0, NULL);
|
||||
#endif /* HAVE_SETGROUPS */
|
||||
|
||||
/* +++ set user IDs +++ */
|
||||
#if HAVE_SETRESUGID
|
||||
DO_SETUID(setresuid, (ksheuid, ksheuid, kshuid));
|
||||
#else /* !HAVE_SETRESUGID */
|
||||
/* seteuid doesn't EAGAIN on Linux */
|
||||
DO_SETUID(setuid, (ksheuid));
|
||||
#ifndef MKSH__NO_SETEUGID
|
||||
seteuid(ksheuid);
|
||||
#endif
|
||||
#endif /* !MKSH__NO_SETEUGID */
|
||||
#endif /* !HAVE_SETRESUGID */
|
||||
|
||||
/* +++ privs changed +++ */
|
||||
} else if ((f == FPOSIX || f == FSH) && newval) {
|
||||
/* Turning on -o posix or -o sh? */
|
||||
Flag(FBRACEEXPAND) = 0;
|
||||
/* Turning on -o posix? */
|
||||
if (f == FPOSIX) {
|
||||
if (f == FPOSIX)
|
||||
/* C locale required for compliance */
|
||||
UTFMODE = 0;
|
||||
}
|
||||
} else if (f == FTALKING) {
|
||||
/* Turning on -o posix or -o sh? */
|
||||
Flag(FBRACEEXPAND) = 0;
|
||||
#ifndef MKSH_NO_CMDLINE_EDITING
|
||||
} else if ((f == FEMACS ||
|
||||
#if !MKSH_S_NOVI
|
||||
f == FVI ||
|
||||
#endif
|
||||
f == FGMACS) && newval) {
|
||||
#if !MKSH_S_NOVI
|
||||
Flag(FVI) = 0;
|
||||
#endif
|
||||
Flag(FEMACS) = Flag(FGMACS) = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
change_flag:
|
||||
Flag(f) = newval;
|
||||
|
||||
if (f == FTALKING) {
|
||||
/* Changing interactive flag? */
|
||||
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
|
||||
Flag(FTALKING_I) = newval;
|
||||
#ifndef MKSH_UNEMPLOYED
|
||||
} else if (f == FMONITOR) {
|
||||
if (what != OF_CMDLINE && newval != oldval)
|
||||
j_change();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1674,14 +1689,13 @@ do_realpath(const char *upath)
|
|||
if (getdrvwd(&ldest, ord(*upath)))
|
||||
return (NULL);
|
||||
/* A:foo -> A:/cwd/foo; A: -> A:/cwd */
|
||||
ipath = shf_smprintf(Tf_sss, ldest,
|
||||
upath[2] ? "/" : "", upath + 2);
|
||||
strpathx(ipath, ldest, upath + 2, 0);
|
||||
#endif
|
||||
} else {
|
||||
/* upath is a relative pathname, prepend cwd */
|
||||
if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
|
||||
return (NULL);
|
||||
ipath = shf_smprintf(Tf_sss, tp, "/", upath);
|
||||
strpathx(ipath, tp, upath, 1);
|
||||
afree(tp, ATEMP);
|
||||
}
|
||||
|
||||
|
@ -1783,7 +1797,7 @@ do_realpath(const char *upath)
|
|||
assemble_symlink:
|
||||
#endif
|
||||
/* append rest of current input path to link target */
|
||||
tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
|
||||
strpathx(tp, ldest, ip, 0);
|
||||
afree(ipath, ATEMP);
|
||||
ip = ipath = tp;
|
||||
if (!mksh_abspath(ipath)) {
|
||||
|
@ -2199,8 +2213,7 @@ c_cd(const char **wp)
|
|||
tryp = NULL;
|
||||
if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
|
||||
!getdrvwd(&tryp, ord(*dir))) {
|
||||
dir = shf_smprintf(Tf_sss, tryp,
|
||||
dir[2] ? "/" : "", dir + 2);
|
||||
strpathx(dir, tryp, dir + 2, 0);
|
||||
afree(tryp, ATEMP);
|
||||
afree(allocd, ATEMP);
|
||||
allocd = dir;
|
||||
|
|
1853
src/mksh.1
1853
src/mksh.1
File diff suppressed because it is too large
Load diff
620
src/mksh.faq
Normal file
620
src/mksh.faq
Normal file
|
@ -0,0 +1,620 @@
|
|||
RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.6 2020/04/13 20:46:39 tg Exp $
|
||||
ToC: spelling
|
||||
Title: How do you spell <tt>mksh</tt>? How do you pronounce it?
|
||||
|
||||
<p>This <a href="@@RELPATH@@mksh.htm">shell</a> is spelt either
|
||||
“<tt>mksh</tt>” (with, even at the beginning of a sentence, <a
|
||||
href="https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Capital_letters#Items_that_require_initial_lower_case">an
|
||||
initial lowercase letter</a>; this is important) or “MirBSD Korn Shell”,
|
||||
possibly with “the”.</p>
|
||||
<p>I usually pronounce it as “<span xml:lang="de-DE-1901">em-ka-es-ha</span>”,
|
||||
that is, the letters individually in my native German, or say “MirBSD Korn
|
||||
Shell”, although it is manageable, mostly for Slavic speakers, to actually
|
||||
say “mksh” as if it were a word ☺</p>
|
||||
<p>Oh… I’ve run into this one, didn’t I? “MirBSD” is pronounced “<span
|
||||
xml:lang="de-DE-1901">Mir-Be-Es-De</span>” germanically, for anglophones
|
||||
“Mir-beas’tie” is fine.</p>
|
||||
----
|
||||
ToC: sowhatismksh
|
||||
Title: I’m a $OS (<i>Android, OS/2, …</i>) user, so what’s mksh?
|
||||
|
||||
<p>mksh is a so-called (Unix) “shell” or “command interpreter”, similar to
|
||||
<tt>COMMAND.COM</tt>, <tt>CMD.EXE</tt> or PowerShell on other operating
|
||||
systems you might know. Basically, it runs in a terminal (“console” or
|
||||
“DOS box”) window, taking user input and running that as commands. It’s
|
||||
also used to write so-called (shell) “script”s, short programs made by
|
||||
putting several of those commands into a “batch file”.</p>
|
||||
<p>On Android, mksh is used as the system shell — basically, the one
|
||||
running commands at system startup, in the background, and on user
|
||||
behalf (but never of its own). Any privilege pop-ups you might <a
|
||||
href="https://forum.xda-developers.com/showthread.php?t=1963976">be
|
||||
encountering</a> are therefore <a
|
||||
href="https://forum.xda-developers.com/showpost.php?p=33550523&postcount=1553">not
|
||||
caused by mksh</a> but by some other code invoking mksh to do something
|
||||
on its behalf.</p>
|
||||
----
|
||||
ToC: os2
|
||||
Title: I’m an OS/2 user, what else do I need to know?
|
||||
|
||||
<p>Unlike the native command prompt, the current working directory is,
|
||||
for security reasons common on Unix systems which the shell is designed
|
||||
for, not in the search path at all; if you really need this, run the
|
||||
command <tt>PATH=.$PATHSEP$PATH</tt> or add that to a suitable
|
||||
initialisation file (<tt>~/.mkshrc</tt>).</p>
|
||||
<p>There are two different newline modes for mksh-os2: standard (Unix)
|
||||
mode, in which only LF (0A hex) is supported as line separator, and
|
||||
“textmode”, which also accepts ASCII newlines (CR+LF), like most other
|
||||
tools on OS/2, but creating an incompatibility with standard mksh. If
|
||||
you compiled mksh from source, you will get the standard Unix mode unless
|
||||
<tt>-T</tt> is added during compilation; however, you will most likely
|
||||
have gotten this shell through komh’s port on Hobbes, or from his OS/2
|
||||
Factory on eComStation Korea, which uses “textmode”, though. Most OS/2
|
||||
users will want to use “textmode” unless they need absolute compatibility
|
||||
with Unix mksh and other Unix shells and tools.</p>
|
||||
----
|
||||
ToC: kornshell
|
||||
Title: How does this relate to ksh or the Korn Shell?
|
||||
|
||||
<p>The Korn Shell (AT&T ksh) was authored by David Korn; two major
|
||||
flavours exist (ksh88 and ksh93), the latter having been maintained
|
||||
until 2012 (last formal release) and 2014 (last beta snapshot, buggy).
|
||||
A ksh86 did exist.</p>
|
||||
<p>There’s now <tt>ksh2020</tt>, a project having restarted development
|
||||
around November 2017 forking the last <tt>ksh93 v-</tt> (beta) snapshot
|
||||
and continuing to develop it, presented at FOSDEM.</p>
|
||||
<p>AT&T ksh88 is “the (original) Korn Shell”. Other implementations,
|
||||
of varying quality (MKS Toolkit’s MKS ksh being named as an example of
|
||||
the lower end, MirBSD’s mksh at the upper end). They are all <em>not</em>
|
||||
“Korn Shell” or “ksh”. However, mksh got blessed by David Korn, as long
|
||||
as it cannot be confused with the original Korn Shell.</p>
|
||||
<p>The POSIX shell standard, while lacking most Korn Shell features, was
|
||||
largely based on AT&T ksh88, with some from the Bourne shell.</p>
|
||||
<p>mksh is the currently active development of what started as the Public
|
||||
Domain Bourne Shell in the mid-1980s with ksh88-compatibl-ish extensions
|
||||
having been added later, making the Public Domain Korn Shell (pdksh),
|
||||
which, while never officially blessed, was the only way for most to get
|
||||
a Korn Shell-like command interpreter for AT&T’s was proprietary,
|
||||
closed-source code for a very long time. pdksh’s development ended in
|
||||
1999, with some projects like Debian and NetBSD® creating small bug fixes
|
||||
(which often introduced new bugs) as part of maintenance. Around 2003,
|
||||
OpenBSD started cleaning up their shipped version of pdksh, removing old
|
||||
and compatibility code and modernising it. In 2002, development of what
|
||||
is now mksh started as the system shell of MirBSD, which took over almost
|
||||
all of OpenBSD’s cleanup, adding compatibility to other operating systems
|
||||
back on top of it, and after 2004, independent, massive development of
|
||||
bugfixes including a complete reorganisation of the way the parser works,
|
||||
and of new features both independent and compatible with other shells
|
||||
(ksh93, GNU bash, zsh, BSD csh) started and was followed by working with
|
||||
the group behind POSIX to fix issues both in the standard and in mksh.
|
||||
mksh became the system shell in several other operating systems and Linux
|
||||
distributions and Android and thus is likely the Korn shell, if not Unix
|
||||
shell, flavour with the largest user base. It has replaced pdksh in all
|
||||
contemporary systems except QNX, NetBSD® and OpenBSD (who continue to
|
||||
maintain their variant on “low flame”).</p>
|
||||
<p>dtksh is the “Desktop Korn Shell”, a build of AT&T ksh93 with some
|
||||
additional built-in utilities for graphics programming (windows, menu
|
||||
bars, dialogue boxes, etc.) utilising Motif bindings.</p>
|
||||
<p>MKS ksh is a proprietary reimplemention aiming for, but not quite
|
||||
getting close to, ksh88 compatibility.</p>
|
||||
<p>SKsh is an AmigaOS-specific Korn Shell-lookalike by Steve Koren.</p>
|
||||
<p>The <a href="@@RELPATH@@ksh-chan.htm">Homepage of the <tt>#ksh</tt>
|
||||
channel on Freenode IRC</a> contains more information about the Korn
|
||||
Shell in general and its flavours.</p>
|
||||
----
|
||||
ToC: packaging
|
||||
Title: How should I package mksh? (common cases)
|
||||
|
||||
<p>Export a few environment variables, namely <tt>CC</tt> (the C compiler),
|
||||
<tt>CPPFLAGS</tt> (all C præprocessor definitions), <tt>CFLAGS</tt> (only
|
||||
compiler flags, <em>no</em> <tt>-Dfoo</tt> or anything!), <tt>LDFLAGS</tt>
|
||||
(for anything to pass to the C compiler while linking) and <tt>LIBS</tt>
|
||||
(appended to the linking command line after everything else. You might
|
||||
wish to <tt>export LDSTATIC=-static</tt> for a static build as well.</p>
|
||||
<p>When cross-compiling, <tt>CC</tt> is the <em>cross</em> compiler (mksh
|
||||
currently does not require a compiler targetting the build system), but
|
||||
you <em>must</em> also export <tt>TARGET_OS</tt> to whatever system you
|
||||
are compiling for, e.g. “Linux”. For most operating systems, that’s just
|
||||
the uname(1) output. Some very rare systems also need <tt>TARGET_OSREV</tt>;
|
||||
consult the source code of <tt>Build.sh</tt> for details.</p>
|
||||
<p>Create two subdirectories, say <tt>build-mksh</tt> and <tt>build-lksh</tt>.
|
||||
In each of them, start a compilation by issuing <tt>sh ../Build.sh -r</tt>
|
||||
followed by running the testsuite<a href="#packaging-fn1">¹</a> via
|
||||
<tt>./test.sh</tt>. For lksh(1) add <tt>-DMKSH_BINSHPOSIX</tt> to
|
||||
<tt>CPPFLAGS</tt> and use <tt>sh ../Build.sh -r -L</tt> to compile.</p>
|
||||
<p>See <a href="#testsuite-fails">below</a> if the testsuite fails.</p>
|
||||
<p>Install <tt>build-mksh/mksh</tt> as <tt>/bin/mksh</tt> (or similar),
|
||||
<tt>build-lksh/lksh</tt> as <tt>/bin/lksh</tt> with a symlink(7) to it
|
||||
from <tt>/bin/sh</tt> (if desred), and <tt>mksh.1</tt> and <tt>lksh.1</tt>
|
||||
as manpages (mdoc macropackage required). Install <tt>dot.mkshrc</tt>
|
||||
either as <tt>/etc/skel/.mkshrc</tt> (meaning your users will have to
|
||||
manually resynchronise their home directories’ copies after every package
|
||||
upgrade) or as <tt>/etc/mkshrc</tt>, in which case you install a <a
|
||||
href="https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=alioth/mksh.git;a=blob;f=debian/.mkshrc;hb=HEAD">redirection
|
||||
script like Debian’s</a> into <tt>/etc/skel/.mkshrc</tt>. You may need a <a
|
||||
href="@@RELPATH@@TaC-mksh.txt">summary of the licence information</a>.</p>
|
||||
<p>At runtime, the presence of <tt>/bin/ed</tt> as default history editor
|
||||
is recommended, as well as a manpage formatter; you can also install
|
||||
preformatted manpages from <tt>build-*ksh/*ksh.cat1</tt> if nroff(1) (or
|
||||
<tt>$NROFF</tt>) is available at build time by removing the <tt>-r</tt>
|
||||
flag from either <tt>Build.sh</tt> invocation.</p>
|
||||
<p>Some shell features require the ability to create temporary files and
|
||||
FIFOS (cf. mkfifo(2))/pipes at runtime. Set <tt>TMPDIR</tt> to a suitable
|
||||
location if <tt>/tmp</tt> isn’t it; if this is known ahead of time, you
|
||||
can add <tt>-DMKSH_DEFAULT_TMPDIR=\"/path/to/tmp\"</tt> to CPPFLAGS. We
|
||||
currently are unable to determine one on Android because its bionic libc
|
||||
does not expose any method suitable to do so in the generic case.</p>
|
||||
<p id="packaging-fn1">① To run the testsuite, ed(1) must be available as
|
||||
<tt>/bin/ed</tt>, and perl(1) is needed. When cross-compiling, the version
|
||||
of the first <tt>ed</tt> binary on the <tt>PATH</tt> <em>must</em> be the
|
||||
same as that in the target system on which the tests are to be run, in
|
||||
order to be able to detect which flavour of ed to adjust the tests for.
|
||||
Busybox ed is broken beyond repair, and all three ed-related tests will
|
||||
always fail with it.</p>
|
||||
----
|
||||
ToC: mkshrc
|
||||
Title: How does mksh load configuration files?
|
||||
|
||||
<p>The shell loads first <tt>/etc/profile</tt> then <tt>~/.profile</tt>
|
||||
if called as login shell or with the <tt>-l</tt> flag, then loads the file
|
||||
<tt>$ENV</tt> points to (defaulting to <tt>~/.mkshrc</tt>) for interactive
|
||||
shells (that includes login shells).</p>
|
||||
<p>Distributors should take care to either install the <tt>dot.mkshrc</tt>
|
||||
example provided into <tt>/etc/skel/.mkshrc</tt> (so that it’s available
|
||||
for newly created user accounts) and ensure it can propagate to existing
|
||||
accounts or, if upgrading these is difficult, install the shipped file
|
||||
as, for example, <tt>/etc/mkshrc</tt> and install a skeleton file, such
|
||||
as the one in Debian, that sources the file in <tt>/etc</tt>.</p>
|
||||
<p>It’s vital that users can change the configuration, so do not force a
|
||||
root-provided config file onto them; the shipped file, after all, is just
|
||||
an example.</p>
|
||||
<p>If you need central user and configuration management and cannot use
|
||||
something that installs skeleton files upon home directory creation
|
||||
(like pam_mkhomedir), you can <tt>export ENV</tt> in <tt>/etc/profile</tt>
|
||||
to a file (say <tt>/etc/shellrc</tt>) that sources the per-shell file.
|
||||
Users can, this way, still override it by setting a different <tt>$ENV</tt>
|
||||
in their <tt>~/.profile</tt>.</p>
|
||||
----
|
||||
ToC: testsuite-fails
|
||||
Title: The testsuite fails!
|
||||
|
||||
<p>The mksh testsuite has uncovered numerous bugs in operating systems
|
||||
(kernels, libraries), compilers and toolchains. It is likely that you
|
||||
just ran into one. If you’re using LTO (the <tt>Build.sh</tt> option
|
||||
<tt>-c lto</tt>) try to disable it first — especially GCC is a repeat
|
||||
offender breaking LTO and its antecessor <tt>-fwhole-program --combine</tt>
|
||||
and tends to do wrong code generation quite a bit. Otherwise, try
|
||||
lowering the optimisation levels, bisecting, etc.</p>
|
||||
----
|
||||
ToC: selinux-androidiocy
|
||||
Title: I forbid stat(2) in my SELinux policy, and some things do not work!
|
||||
|
||||
Don’t break Unix. Read up on the GIGO principle. Duh.
|
||||
----
|
||||
ToC: makefile
|
||||
Title: Why doesn’t this use a Makefile to build?
|
||||
|
||||
<p>Not all supported target operating environments have a make utility
|
||||
available, and shell was required for “mirtoconf” (like autoconf)
|
||||
already anyway, so it was chosen to run the make part as well.</p>
|
||||
<p>You can, however, add the <tt>-M</tt> flag to your <tt>Build.sh</tt>
|
||||
invocations to let it produce a <tt>Makefrag.inc</tt> file <em>tailored
|
||||
for this specific build</em> which you can then include in a Makefile,
|
||||
such as with the BSD make(1) “.include” command or <a
|
||||
href="https://www.gnu.org/software/make/manual/make.html#Include">GNU
|
||||
make</a> equivalent. It even contains, for the user to start out with,
|
||||
a commented-out example of how to do that in the most basic manner.</p>
|
||||
----
|
||||
ToC: oldbsd
|
||||
Title: Why do other BSDs and QNX still use pdksh instead of mksh?
|
||||
|
||||
<p>Some systems are resistent to change, mostly due to bikeshedding
|
||||
(some people would, for example, rather see all shells banned to
|
||||
ports/pkgsrc®) and hysterial raisins (historical reasons ☻). Most
|
||||
BSDs have mksh packages available, and it works on all of them and
|
||||
QNX just fine.</p>
|
||||
<p>In fact, on all of these systems, you can replace their 1999-era
|
||||
<tt>/bin/ksh</tt> (which is a pdksh) with mksh. On at least NetBSD®
|
||||
1.6 and up (not 1.5) and OpenBSD, even <tt>/bin/sh</tt> is fair game.</p>
|
||||
<p>MidnightBSD notably has adopted mksh as system shell (thanks laffer1).</p>
|
||||
----
|
||||
ToC: openbsd
|
||||
Title: Why is there no mksh in OpenBSD’s ports tree?
|
||||
|
||||
OpenBSD don’t like people who fork off their project at all; heck,
|
||||
they don’t even like the people they themselves forked off (NetBSD®).
|
||||
Several people tried over the years to get one committed, but nobody
|
||||
dared so as to not lose their commit bit. If you try, succeed, and
|
||||
survive Theo, however, kudos to you! See also <a href="#oldbsd">the
|
||||
“other BSDs” FAQ entry</a>.
|
||||
----
|
||||
ToC: book
|
||||
Title: I’d like an introduction.
|
||||
|
||||
Unfortunately, nobody has written a book about mksh yet, although
|
||||
other shells have received (sometimes decent) attention from authors
|
||||
and publishers. This FAQ lists a subset of things packagers and
|
||||
generic people ask, and the mksh(1) manpage is more of a reference,
|
||||
so you are probably best off starting with a shell-agnostic, POSIX
|
||||
or ksh88 reference such as the first edition (the second one deals
|
||||
with ksh93 which differs far more from mksh than ksh88, as ancient
|
||||
as it is, does) of the O’Reilly book (⚠ disclaimer: only an example,
|
||||
not a recommendation) and going forward by reading scripts (the
|
||||
“shellsnippets” repository referenced in the <tt>#ksh</tt> channel
|
||||
homepage (see the top of this document) has many examples) and
|
||||
trying to understand them and the mksh specifics from the manpage.
|
||||
----
|
||||
ToC: ps1conv
|
||||
Title: My prompt from <<i>some other shell</i>> does not work!
|
||||
|
||||
<a href="#contact">Contact</a> us on the mailing list or on IRC,
|
||||
we’ll convert it for you. Also have a look at the PS1 section in
|
||||
the mksh(1) manpage (search for “otherwise unused char”, e.g. with
|
||||
<tt>/</tt> in less(1), to spot it quickly).
|
||||
----
|
||||
ToC: ps1weird
|
||||
Title: My prompt is weird!
|
||||
|
||||
<p>There are several reasons why your <tt>PS1</tt> might be not
|
||||
what you’d expect:</p><ul>
|
||||
<li><tt>$PS1</tt> is <tt>export</tt>ed. <strong>Do not export PS1!</strong>
|
||||
(This was agreed upon as suggestion in a discussion between bash, zsh and
|
||||
Korn shell developers.) The feature set of different shells vastly differs
|
||||
and each shell should use its default PS1 or from its startup files.</li>
|
||||
<li><tt>$ENV</tt> <a href="#env">is set and/or <tt>export</tt>ed</a>.</li>
|
||||
<li>Your prompt is just “<tt># </tt>”: you’re entering a root shell, and
|
||||
<tt>$PS1</tt> does not contain the ‘#’ character, in which case the shell
|
||||
forces this prompt, making extra privileges obvious.</li>
|
||||
<li>Your prompt is just “<tt>$ </tt>”: perhaps your system administrator
|
||||
did not install the shipped <tt>dot.mkshrc</tt> file, or you did not copy
|
||||
<tt>/etc/skel/.mkshrc</tt> into your home directory (perhaps it was created
|
||||
before <tt>mksh</tt> was installed?). Without another idea for a fix, get <a
|
||||
href="http://www.mirbsd.org/cvs.cgi/~checkout~/src/bin/mksh/dot.mkshrc?rev=HEAD;content-type=text%2Fplain">this
|
||||
file</a> and store it as <tt>~/.mkshrc</tt> then run <tt>mksh</tt>; this
|
||||
will at the very least install our sample (“user@host:path $ ”) prompt.</li>
|
||||
<li>Your prompt contains things like “\u” or “\w”: it is for another shell
|
||||
and <a href="#ps1conv">needs converting</a>.</li>
|
||||
<li>Your prompt contains colours, and when the command line is long the
|
||||
cursor position or screen contents, especially using the history, is off:
|
||||
terminal escapes must be escaped from the shell; check the PS1 section in
|
||||
the manpage: search for “otherwise unused char” (see above).</li>
|
||||
<li>If the prompt doesn’t leave enough space on the right, the shell inserts
|
||||
a line break after it when rendering.</li>
|
||||
</ul>
|
||||
----
|
||||
ToC: env
|
||||
Title: On startup files and <tt>$ENV</tt> across and detecting various shells
|
||||
|
||||
Interactive shells look at <tt>~/.mkshrc</tt> (or <tt>/system/etc/mkshrc</tt>
|
||||
on Android and <tt>/etc/mkshrc</tt> on FreeWRT and OpenWrt) by default. This
|
||||
location can, however, be overridden by setting the <tt>ENV</tt> environment
|
||||
variable. (FreeBSD is rumoured to set it in their system profile.) It’s better
|
||||
to not set <tt>$ENV</tt> if possible and let every shell user their native
|
||||
startup files; otherwise, you must ensure that it runs under all shells. Check
|
||||
<tt>$BASH_VERSION</tt> (GNU bash), <tt>$KSH_VERSION</tt> (contains “LEGACY KSH”
|
||||
or “MIRBSD KSH” for mksh, “PD KSH” for ancient mirbsdksh/oksh/pdksh, “Version”
|
||||
for ksh93); <tt>$NETBSD_SHELL</tt> (NetBSD ash); <tt>POSH_VERSION</tt> (posh, a
|
||||
pdksh derivative); <tt>$SH_VERSION</tt> (“PD KSH” as sh), <tt>$YASH_VERSION</tt>
|
||||
(yash), <tt>$ZSH_VERSION</tt> (or if <tt>$VERSION</tt> begins with “zsh”); a <a
|
||||
href="@@RELPATH@@ksh-chan.htm#which-shell">list of more approaches</a> exists.
|
||||
----
|
||||
ToC: ctrl-l-cls
|
||||
Title: ^L (Ctrl-L) does not clear the screen
|
||||
|
||||
Use ^[^L (Escape+Ctrl-L) or rebind it:<br />
|
||||
<tt>bind '^L=clear-screen'</tt>
|
||||
----
|
||||
ToC: ctrl-u-pico
|
||||
Title: ^U (Ctrl-U) clears the entire line
|
||||
|
||||
If it should only delete the line up to the cursor, use:<br />
|
||||
<tt>bind -m ^U='^[0^K'</tt>
|
||||
----
|
||||
ToC: cur-up-zsh
|
||||
Title: Cursor Up behaves differently from zsh
|
||||
|
||||
Some shells make Cursor Up search in the history only for commands
|
||||
starting with what was already entered. mksh separates the shortcuts:
|
||||
Cursor Up goes up one command and PgUp searches the history as described
|
||||
above. You can, of course, rebind:<br />
|
||||
<tt>bind '^XA=search-history-up'</tt>
|
||||
----
|
||||
ToC: current
|
||||
Title: Can mksh set the title of the window according to the command running?
|
||||
|
||||
There’s no such thing as “the command currently running”; consider
|
||||
pipelines and delays (<tt>cmd1 | (cmd2; sleep 3; cmd3) | cmd4</tt>).
|
||||
There is, however, a way to make the shell display the command <em>line</em>
|
||||
during the time it is executed; for testing, you will need to download <a
|
||||
href="https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=shellsnippets/shellsnippets.git;a=blob;f=mksh/terminal-title;hb=HEAD">this
|
||||
script</a> and <tt>source</tt> it. For merging into your <tt>~/.mkshrc</tt>
|
||||
you should first understand how it works: lines 4–18 set a <tt>PS1</tt>
|
||||
(prompt) equivalent to lines 84–96 of the stock <tt>dot.mkshrc</tt>, with
|
||||
one change: line 15 (<tt>print >/dev/tty …</tt>) is new, inserted just
|
||||
before the <tt>return</tt> command of the function substitution in the
|
||||
default prompt; this is what you’ll need to merge into your own, custom,
|
||||
prompt (if you have one; otherwise pull this adaption to the default
|
||||
one). Line 19 is the only other thing in this script rebinding the Ctrl-M
|
||||
key (which is normally produced by the Enter/Return key) to code that…
|
||||
does <em>something crazy</em>. This trick however <em>does funny things with
|
||||
multiline commands</em>, so if you type something out in multiple lines,
|
||||
for example <strong>here documents</strong> or <strong>loops</strong> press
|
||||
<strong>Ctrl-J instead of Enter/Return</strong> after <em>each</em> line
|
||||
including the first (at PS1) and final (at PS2) one.
|
||||
----
|
||||
ToC: other-tty
|
||||
Title: How do I start mksh on a specific terminal?
|
||||
|
||||
<p>Normally: <tt>mksh -T<i>/dev/tty2</i></tt></p>
|
||||
<p>However, if you want for it to return (e.g. for an embedded system rescue
|
||||
shell), use this on your real console device instead:
|
||||
<tt>mksh -T!<i>/dev/ttyACM0</i></tt></p>
|
||||
<p>mksh can also daemonise (send to the background):
|
||||
<tt>mksh -T- -c 'exec cdio lock'</tt></p>
|
||||
----
|
||||
ToC: completion
|
||||
Title: What about programmable tab completion?
|
||||
|
||||
The shell itself provides static deterministic tab completion.
|
||||
However, you can use hooks like reprogramming the Tab key to a
|
||||
command line editor macro, and using the <tt>evaluate-region</tt>
|
||||
editor command (modulo a bugfix) together with <tt>quote-region<tt> and shell functions to
|
||||
implement a programmable completion engine. Multiple people have
|
||||
been considering doing so in our IRC channel; we’ll hyperlink to
|
||||
these engines when they are available.
|
||||
----
|
||||
ToC: posix-mode
|
||||
Title: How POSIX compliant is mksh? Also, UTF-8 vs. locales?
|
||||
|
||||
<p>You’ll need to use the <tt>lksh</tt> binary, unless your C <tt>long</tt>
|
||||
type is 32 bits wide, for POSIX-compliant arithmetic in the shell. This is
|
||||
because <tt>mksh</tt> provides consistent, wraparound-defined, 32-bit
|
||||
arithmetics on all platforms normally. You’ll also need to enable POSIX mode
|
||||
(<tt>set -o posix</tt>) explicitly, which also disables brace expansion upon
|
||||
being enabled (use <tt>set -o braceexpand</tt> to reenable if needed).</p>
|
||||
<p>For the purpose of POSIX, mksh supports only the <tt>C</tt> locale. mksh’s
|
||||
<tt>utf8-mode</tt> (which only supports the BMP (Basic Multilingual Plane) of
|
||||
UCS and maps raw octets into the U+EF80‥U+EFFF wide character range; see
|
||||
<tt>Arithmetic expressions</tt> in mksh(1) for details) <em>must</em> stay
|
||||
disabled in POSIX mode (it is disabled upon enabling POSIX mode in R56+).</p>
|
||||
<p class="boxhead">The following POSIX sh-compatible code toggles the
|
||||
<tt>utf8-mode</tt> option dependent on the current POSIX locale, for mksh
|
||||
to allow using the UTF-8 mode, within the constraints outlined above, in
|
||||
code portable across various shell implementations:</p>
|
||||
<div class="boxtext">
|
||||
<pre>
|
||||
case ${KSH_VERSION:-} in
|
||||
*MIRBSD KSH*|*LEGACY KSH*)
|
||||
case ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} in
|
||||
*[Uu][Tt][Ff]8*|*[Uu][Tt][Ff]-8*) set -U ;;
|
||||
*) set +U ;;
|
||||
esac ;;
|
||||
esac
|
||||
</pre>
|
||||
</div><p class="boxfoot">In near future, (UTF-8) locale tracking will
|
||||
be implemented, though.</p>
|
||||
<p>The shell is pretty close to POSIX, when run as <tt>lksh -o posix</tt>
|
||||
under the "C" locale it is intended to match. It does not do everything
|
||||
like other POSIX-compatible or ‑compliant shells, though.</p>
|
||||
----
|
||||
ToC: function-local-scopes
|
||||
Title: What differences in function-local scopes are there?
|
||||
|
||||
<p><tt>mksh</tt> has a different scope model from AT&T <tt>ksh</tt>,
|
||||
which leads to subtle differences in semantics for identical builtins.
|
||||
This can cause issues with a <tt>nameref</tt> to suddenly point to a
|
||||
local variable by accident. (Other common shells share mksh’s scoping
|
||||
model.)</p>
|
||||
<p class="boxhead">GNU <tt>bash</tt> allows unsetting local variables; in
|
||||
<tt>mksh</tt>, doing so in a function allows back access to the global
|
||||
variable (actually the one in the next scope up) with the same name. The
|
||||
following code, when run before function definitions, changes the behaviour
|
||||
of <tt>unset</tt> to behave like other shells (the alias can be removed
|
||||
after the definitions):</p>
|
||||
<div class="boxtext">
|
||||
<pre>
|
||||
case ${KSH_VERSION:-} in
|
||||
*MIRBSD KSH*|*LEGACY KSH*)
|
||||
function unset_compat {
|
||||
\\builtin typeset unset_compat_x
|
||||
|
||||
for unset_compat_x in "$@"; do
|
||||
eval "\\\\builtin unset $unset_compat_x[*]"
|
||||
done
|
||||
}
|
||||
\\builtin alias unset=unset_compat
|
||||
;;
|
||||
esac
|
||||
</pre>
|
||||
</div><p class="boxfoot">When a local variable is created (e.g. using
|
||||
<tt>local</tt>, <tt>typeset</tt>, <tt>integer</tt> or
|
||||
<tt>\\builtin typeset</tt>) it does not, like in other shells, inherit
|
||||
the value from the global (next scope up) variable with the same name;
|
||||
it is rather created without any value (unset but defined).</p>
|
||||
----
|
||||
ToC: regex-comparison
|
||||
Title: I get an error in this regex comparison
|
||||
|
||||
<p>Use extglobs instead of regexes:<br />
|
||||
<tt>[[ foo =~ (foo|bar).*baz ]]</tt><br />
|
||||
… becomes…<br />
|
||||
<tt>[[ foo = *@(foo|bar)*baz* ]]</tt></p>
|
||||
----
|
||||
ToC: extensions-to-avoid
|
||||
Title: Are there any extensions to avoid?
|
||||
|
||||
<p>GNU <tt>bash</tt> supports “<tt>&></tt>” (and “|&”) to redirect
|
||||
both stdout and stderr in one go, but this breaks POSIX and Korn Shell syntax;
|
||||
use POSIX redirections instead:</p>
|
||||
<table border="1" cellpadding="3">
|
||||
<tr><td>GNU bash</td><td>
|
||||
<tt>foo |& bar |& baz &>log</tt>
|
||||
</td></tr>
|
||||
<tr><td>POSIX</td><td>
|
||||
<tt>foo 2>&1 | bar 2>&1 | baz >log 2>&1</tt>
|
||||
</td></tr>
|
||||
</table>
|
||||
----
|
||||
ToC: while-read-pipe
|
||||
Title: Something is going wrong with my while...read loop
|
||||
|
||||
<p class="boxhead">Most likely, you’ve encountered the problem in which
|
||||
the shell runs all parts of a pipeline as subshell. The inner loop will
|
||||
be executed in a subshell and variable changes cannot be propagated if
|
||||
run in a pipeline:</p>
|
||||
<div class="boxtext">
|
||||
<pre>
|
||||
bar | baz | while read foo; do ...; done
|
||||
</pre>
|
||||
</div><p class="boxfoot">Note that <tt>exit</tt> in the inner loop will
|
||||
also only exit the subshell and not the original shell. Likewise, if the
|
||||
code is inside a function, <tt>return</tt> in the inner loop will only
|
||||
exit the subshell and won’t terminate the function.</p>
|
||||
<p class="boxhead">Use co-processes instead:</p>
|
||||
<div class="boxtext">
|
||||
<pre>
|
||||
bar | baz |&
|
||||
while read -p foo; do ...; done
|
||||
exec 3>&p; exec 3>&-
|
||||
</pre>
|
||||
</div><p class="boxfoot">If <tt>read</tt> is run in a way such as
|
||||
<tt>while read foo; do ...; done</tt> then leading whitespace will be
|
||||
removed (IFS) and backslashes processed. You might want to use
|
||||
<tt>while IFS= read -r foo; do ...; done</tt> for pristine I/O.</p>
|
||||
<p class="boxhead">Similarly, when using the <tt>-a</tt> option, use of the
|
||||
<tt>-r</tt> option might be prudent (<tt>read -raN-1 arr <file</tt>);
|
||||
the same applies for NUL-terminated lines:</p>
|
||||
<div class="boxtext">
|
||||
<pre>
|
||||
find . -type f -print0 |& \
|
||||
while IFS= read -d '' -pr filename; do
|
||||
print -r -- "found <${filename#./}>"
|
||||
done
|
||||
</pre>
|
||||
</div>
|
||||
----
|
||||
ToC: command-alias
|
||||
Title: “command” doesn’t expand aliases as in ksh93
|
||||
|
||||
This is because AT&T ksh93 ships a predefined alias enabling this:<br />
|
||||
<tt>alias command='command '</tt><br />
|
||||
put this into your <tt>~/.mkshrc</tt>
|
||||
(note the space before the closing single quote)
|
||||
----
|
||||
ToC: builtin-rename
|
||||
Title: “rename” doesn’t work as expected!
|
||||
|
||||
<p>There’s a <tt>rename</tt> built-in utility in mksh, which is a very
|
||||
thin wrapper around the rename(2) syscall. It receives two pathnames,
|
||||
source and destination where the first is then atomically renamed to
|
||||
the latter. It does not move, i.e. fails for different filesystems.</p>
|
||||
<p>The GNU package <tt>util-linux</tt> has a different <tt>rename</tt>
|
||||
command. If you wish to invoke an external utility (in favour over a
|
||||
builtin), you can use <tt>dot.mkshrc</tt>’s function <tt>enable</tt>
|
||||
or put the following into your <tt>~/.mkshrc</tt>:</p>
|
||||
<pre>alias rename="$(whence -p rename)"</pre>
|
||||
----
|
||||
ToC: builtin-sleep
|
||||
Title: “sleep” does not accept ‘m’ for minutes!
|
||||
|
||||
<p>mksh contains a <tt>sleep</tt> built-in utility, in order to be
|
||||
able to offer sub-second sleep to shell scripts for most platforms.
|
||||
(It does not exist if the platform lacks select(2) — which should
|
||||
be rare.)</p>
|
||||
<p>GNU coreutils contains a sleep implementation accepting suffixed
|
||||
numbers. If you wish to invoke an external utility (in favour over a
|
||||
builtin), you can use <tt>dot.mkshrc</tt>’s function <tt>enable</tt>
|
||||
or put something along the following lines into <tt>~/.mkshrc</tt>:</p>
|
||||
<pre>alias sleep="$(whence -p sleep)"</pre>
|
||||
<pre>timer() { sleep $(($1*60${2:++$2})); } # timer mins [secs]</pre>
|
||||
<pre>timer() {
|
||||
local arg=${1/m/'*60+'}
|
||||
[[ $arg = *+ ]] && arg+=0
|
||||
sleep $(($arg)
|
||||
}</pre>
|
||||
----
|
||||
ToC: string-concat
|
||||
Title: “+=” behaves differently from other shells
|
||||
|
||||
<p>In POSIX shell, “=” in code like <tt>var=content</tt> is a string
|
||||
assignment, always. You can use <tt>var=$((content))</tt> for an
|
||||
arithmetic assignment that mostly uses C language rules.</p>
|
||||
<p>It stands to consider that the common shell extension “+=” as in
|
||||
<tt>var+=content</tt> would always do string concatenation; it does
|
||||
in mksh, but not in some other shells, in which, when <tt>var</tt> has
|
||||
been declared integer, addition is done instead.</p>
|
||||
<p>You can make the code portable by using “((…))” (a.k.a. <tt>let</tt>)
|
||||
instead: <tt>(( var += content ))</tt> does arithmetic addition in
|
||||
all shells involved.</p>
|
||||
----
|
||||
ToC: set-e
|
||||
Title: I use “set -e” and my code unexpectedly errors out
|
||||
|
||||
<p>I personally recommend people to not use “<tt>set -e</tt>”, as it
|
||||
makes error handling more difficult. However, some insist. There have
|
||||
been bugfixes (relative to e.g. oksh/loksh and posh) in this aspect,
|
||||
and the user has to make sure <tt>$?</tt> is always 0 ASAP even after
|
||||
a command that doesn’t check it.</p>
|
||||
<pre>istwo() {
|
||||
for i in "$@"; do
|
||||
test x"$i" = x"2" && echo two
|
||||
done
|
||||
}
|
||||
set -e
|
||||
istwo 1
|
||||
echo END</pre>
|
||||
<p>This can be fixed by either adding an explicit “<tt>:</tt>” (or
|
||||
“<tt>true</tt>”) after the comparison, or even…</p>
|
||||
<pre>test x"$i" = x"2" && echo two || :</pre>
|
||||
<p>… or right after the <tt>done</tt> inside the function, but…</p>
|
||||
<pre>test x"$i" != x"2" || echo two</pre>
|
||||
<p>… negating the condition and using “<tt>||</tt>” is preferable.</p>
|
||||
|
||||
<p>Remember that Korn shell-style functions (with <tt>function</tt>
|
||||
keyword and <strong>without</strong> parenthesēs) in AT&T ksh93
|
||||
and mksh R51 and up have their own shell option scope, but while…</p>
|
||||
<pre>function istwo {
|
||||
set +e
|
||||
…
|
||||
}</pre>
|
||||
<p>… might help in error handling, the return status of a function is
|
||||
still the last errorlevel inside, so an explicit true (“<tt>:</tt>”)
|
||||
or, more explicitly, “<tt>return 0</tt>” at its end is still needed
|
||||
if the <em>caller</em> runs under <tt>set -e</tt>.</p>
|
||||
----
|
||||
ToC: set-eo-pipefail
|
||||
Title: I use “set -eo pipefail” and my code unexpectedly errors out
|
||||
|
||||
<p class="boxhead">Related to the above FAQ entry, using
|
||||
<tt>set -o pipefail</tt> makes the following construct error out:</p>
|
||||
<div class="boxtext">
|
||||
<pre>
|
||||
set -e
|
||||
for x in 1 2; do
|
||||
false && echo $x
|
||||
done | cat
|
||||
</pre>
|
||||
</div><p class="boxfoot">This is because, while the <tt>&&</tt>
|
||||
ensures that the inner command’s failure is not taken, it sets the entire
|
||||
<tt>for</tt>‥<tt>done</tt> loop’s errorlevel, which is passed on by
|
||||
<tt>-o pipefail</tt>.</p>
|
||||
<p>Invert the inner command:<br />
|
||||
<tt>true || echo $x</tt></p>
|
||||
----
|
||||
ToC: faq
|
||||
Title: My question is not answered here!
|
||||
|
||||
Do read the mksh(1) manual page. You might also wish to read the <a
|
||||
href="@@RELPATH@@ksh-chan.htm">homepage of the <tt>#ksh</tt> IRC channel
|
||||
on Freenode</a> which lists several resources for Korn or POSIX-compatible
|
||||
shells in general. Or, <a href="#contact">contact</a> us (developer and
|
||||
users), for example via IRC.
|
||||
----
|
||||
ToC: contact
|
||||
Title: How do I contact you (to say thanks)?
|
||||
|
||||
You can say hi in the <tt>#!/bin/mksh</tt> channel on Freenode <a
|
||||
href="@@RELPATH@@irc.htm">IRC</a>, although a <a
|
||||
href="@@RELPATH@@danke.htm">donation</a> wouldn’t be amiss ☺ The <a
|
||||
href="http://www.mail-archive.com/miros-mksh@mirbsd.org/">mailing
|
||||
list</a> can also be used.
|
||||
----
|
20
src/os2.c
20
src/os2.c
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2015, 2017
|
||||
* Copyright (c) 2015, 2017, 2020
|
||||
* KO Myung-Hun <komh@chollian.net>
|
||||
* Copyright (c) 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
|
@ -20,6 +20,7 @@
|
|||
* of said person's immediate fault when using the work as intended.
|
||||
*/
|
||||
|
||||
#define INCL_KBD
|
||||
#define INCL_DOS
|
||||
#include <os2.h>
|
||||
|
||||
|
@ -31,7 +32,7 @@
|
|||
#include <unistd.h>
|
||||
#include <process.h>
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.8 2017/12/22 16:41:42 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.10 2020/04/07 11:13:45 tg Exp $");
|
||||
|
||||
static char *remove_trailing_dots(char *);
|
||||
static int access_stat_ex(int (*)(), const char *, void *);
|
||||
|
@ -172,6 +173,8 @@ init_extlibpath(void)
|
|||
void
|
||||
os2_init(int *argcp, const char ***argvp)
|
||||
{
|
||||
KBDINFO ki;
|
||||
|
||||
response(argcp, argvp);
|
||||
|
||||
init_extlibpath();
|
||||
|
@ -183,6 +186,12 @@ os2_init(int *argcp, const char ***argvp)
|
|||
if (!isatty(STDERR_FILENO))
|
||||
setmode(STDERR_FILENO, O_BINARY);
|
||||
|
||||
/* ensure ECHO mode is ON so that read command echoes. */
|
||||
memset(&ki, 0, sizeof(ki));
|
||||
ki.cb = sizeof(ki);
|
||||
ki.fsMask |= KEYBOARD_ECHO_ON;
|
||||
KbdSetStatus(&ki, 0);
|
||||
|
||||
atexit(cleanup);
|
||||
}
|
||||
|
||||
|
@ -295,11 +304,12 @@ access_ex(int (*fn)(const char *, int), const char *name, int mode)
|
|||
return (access_stat_ex(fn, name, (void *)mode));
|
||||
}
|
||||
|
||||
/* stat() version */
|
||||
/* stat()/lstat() version */
|
||||
int
|
||||
stat_ex(const char *name, struct stat *buffer)
|
||||
stat_ex(int (*fn)(const char *, struct stat *),
|
||||
const char *name, struct stat *buffer)
|
||||
{
|
||||
return (access_stat_ex(stat, name, buffer));
|
||||
return (access_stat_ex(fn, name, buffer));
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#ifndef RLIMITS_OPTCS
|
||||
#if defined(RLIMITS_DEFNS)
|
||||
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.4 2019/04/24 20:56:31 tg Exp $");
|
||||
#elif defined(RLIMITS_ITEMS)
|
||||
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
|
||||
#endif
|
||||
|
@ -82,6 +82,9 @@ FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024, 'B')
|
|||
#ifdef RLIMIT_PTHREAD
|
||||
FN("threadsperprocess", RLIMIT_PTHREAD, 1, 'P')
|
||||
#endif
|
||||
#ifdef RLIMIT_THREADS
|
||||
FN("threadsperprocess", RLIMIT_THREADS, 1, 'r')
|
||||
#endif
|
||||
#ifdef RLIMIT_NICE
|
||||
FN("maxnice", RLIMIT_NICE, 1, 'e')
|
||||
#endif
|
||||
|
@ -100,6 +103,9 @@ FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024, 'v')
|
|||
#ifdef ULIMIT_V_IS_AS
|
||||
FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
|
||||
#endif
|
||||
#ifdef RLIMIT_LOCKS
|
||||
FN("filelocks", RLIMIT_LOCKS, 1, 'x')
|
||||
#endif
|
||||
#undef F0
|
||||
#undef FN
|
||||
#undef RLIMITS_DEFNS
|
||||
|
@ -158,6 +164,9 @@ FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
|
|||
#ifdef RLIMIT_RTPRIO
|
||||
"r"
|
||||
#endif
|
||||
#ifdef RLIMIT_THREADS
|
||||
"r"
|
||||
#endif
|
||||
"S"
|
||||
#ifdef RLIMIT_STACK
|
||||
"s"
|
||||
|
@ -180,5 +189,8 @@ FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
|
|||
#ifdef RLIMIT_SWAP
|
||||
"w"
|
||||
#endif
|
||||
#ifdef RLIMIT_LOCKS
|
||||
"x"
|
||||
#endif
|
||||
#undef RLIMITS_OPTCS
|
||||
#endif
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
|
||||
@RLIMITS_DEFNS
|
||||
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.4 2019/04/24 20:56:31 tg Exp $");
|
||||
@RLIMITS_ITEMS
|
||||
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
|
||||
@@
|
||||
|
@ -86,6 +86,9 @@ FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024
|
|||
>P|RLIMIT_PTHREAD
|
||||
FN("threadsperprocess", RLIMIT_PTHREAD, 1
|
||||
|
||||
>r|RLIMIT_THREADS
|
||||
FN("threadsperprocess", RLIMIT_THREADS, 1
|
||||
|
||||
>e|RLIMIT_NICE
|
||||
FN("maxnice", RLIMIT_NICE, 1
|
||||
|
||||
|
@ -102,4 +105,7 @@ FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024
|
|||
>v|ULIMIT_V_IS_AS
|
||||
FN("address-space(KiB)", RLIMIT_AS, 1024
|
||||
|
||||
>x|RLIMIT_LOCKS
|
||||
FN("filelocks", RLIMIT_LOCKS, 1
|
||||
|
||||
|RLIMITS_OPTCS
|
||||
|
|
214
src/sh.h
214
src/sh.h
|
@ -10,7 +10,8 @@
|
|||
|
||||
/*-
|
||||
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
* 2019, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -116,7 +117,7 @@
|
|||
#if (defined(__KLIBC__) || defined(__dietlibc__)) && \
|
||||
((defined(__GNUC__) && (__GNUC__ > 3)) || defined(__NWCC__))
|
||||
#undef offsetof
|
||||
#define offsetof(s, e) __builtin_offsetof(s, e)
|
||||
#define offsetof(s,e) __builtin_offsetof(s, e)
|
||||
#endif
|
||||
|
||||
#undef __attribute__
|
||||
|
@ -170,9 +171,17 @@
|
|||
#define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p
|
||||
#define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p)
|
||||
#ifdef MKSH_DONT_EMIT_IDSTRING
|
||||
#define __IDSTRING(prefix, string) /* nothing */
|
||||
#define __IDSTRING(prefix,string) /* nothing */
|
||||
#elif defined(__ELF__) && defined(__GNUC__) && \
|
||||
!(defined(__GNUC__) && defined(__mips16) && (__GNUC__ >= 8)) && \
|
||||
!defined(__llvm__) && !defined(__NWCC__) && !defined(NO_ASM)
|
||||
#define __IDSTRING(prefix,string) \
|
||||
__asm__(".section .comment" \
|
||||
"\n .ascii \"@(\"\"#)" #prefix ": \"" \
|
||||
"\n .asciz \"" string "\"" \
|
||||
"\n .previous")
|
||||
#else
|
||||
#define __IDSTRING(prefix, string) \
|
||||
#define __IDSTRING(prefix,string) \
|
||||
static const char __IDSTRING_EXPAND(__LINE__,prefix) [] \
|
||||
MKSH_A_USED = "@(""#)" #prefix ": " string
|
||||
#endif
|
||||
|
@ -182,9 +191,9 @@
|
|||
#endif
|
||||
|
||||
#ifdef EXTERN
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.870 2019/03/01 16:18:14 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.892 2020/04/14 22:45:22 tg Exp $");
|
||||
#endif
|
||||
#define MKSH_VERSION "R57 2019/03/01"
|
||||
#define MKSH_VERSION "R59 2020/04/14"
|
||||
|
||||
/* arithmetic types: C implementation */
|
||||
#if !HAVE_CAN_INTTYPES
|
||||
|
@ -255,6 +264,16 @@ typedef MKSH_TYPEDEF_SIG_ATOMIC_T sig_atomic_t;
|
|||
typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
#define MKSH_SHF_NO_INLINE
|
||||
#endif
|
||||
|
||||
/* do not merge these conditionals as neatcc’s preprocessor is simple */
|
||||
#ifdef __neatcc__
|
||||
/* parsing of comma operator <,> in expressions broken */
|
||||
#define MKSH_SHF_NO_INLINE
|
||||
#endif
|
||||
|
||||
/* un-do vendor damage */
|
||||
|
||||
#undef BAD /* AIX defines that somewhere */
|
||||
|
@ -264,6 +283,9 @@ typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
|
|||
|
||||
#ifndef MKSH_INCLUDES_ONLY
|
||||
|
||||
/* compile-time assertions */
|
||||
#define cta(name,expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
|
||||
|
||||
/* EBCDIC fun */
|
||||
|
||||
/* see the large comment in shf.c for an EBCDIC primer */
|
||||
|
@ -307,7 +329,7 @@ struct rusage {
|
|||
} while (/* CONSTCOND */ 0)
|
||||
#endif
|
||||
#ifndef timeradd
|
||||
#define timeradd(tvp, uvp, vvp) \
|
||||
#define timeradd(tvp,uvp,vvp) \
|
||||
do { \
|
||||
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
|
||||
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
|
||||
|
@ -318,7 +340,7 @@ struct rusage {
|
|||
} while (/* CONSTCOND */ 0)
|
||||
#endif
|
||||
#ifndef timersub
|
||||
#define timersub(tvp, uvp, vvp) \
|
||||
#define timersub(tvp,uvp,vvp) \
|
||||
do { \
|
||||
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
|
||||
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
|
||||
|
@ -453,7 +475,7 @@ extern int getrusage(int, struct rusage *);
|
|||
|
||||
#if !HAVE_MEMMOVE
|
||||
/* we assume either memmove or bcopy exist, at the moment */
|
||||
#define memmove(dst, src, len) bcopy((src), (dst), (len))
|
||||
#define memmove(dst,src,len) bcopy((src), (dst), (len))
|
||||
#endif
|
||||
|
||||
#if !HAVE_REVOKE_DECL
|
||||
|
@ -647,7 +669,7 @@ char *ucstrstr(char *, const char *);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 571)
|
||||
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 591)
|
||||
#error Must run Build.sh to compile this.
|
||||
extern void thiswillneverbedefinedIhope(void);
|
||||
int
|
||||
|
@ -661,20 +683,20 @@ im_sorry_dave(void)
|
|||
/* use this ipv strchr(s, 0) but no side effects in s! */
|
||||
#define strnul(s) ((s) + strlen((const void *)s))
|
||||
|
||||
#define utf_ptradjx(src, dst) do { \
|
||||
#define utf_ptradjx(src,dst) do { \
|
||||
(dst) = (src) + utf_ptradj(src); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
#define strdupx(d, s, ap) do { \
|
||||
#define strdupx(d,s,ap) do { \
|
||||
(d) = strdup_i((s), (ap)); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#define strndupx(d, s, n, ap) do { \
|
||||
#define strndupx(d,s,n,ap) do { \
|
||||
(d) = strndup_i((s), (n), (ap)); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#else
|
||||
/* be careful to evaluate arguments only once! */
|
||||
#define strdupx(d, s, ap) do { \
|
||||
#define strdupx(d,s,ap) do { \
|
||||
const char *strdup_src = (const void *)(s); \
|
||||
char *strdup_dst = NULL; \
|
||||
\
|
||||
|
@ -685,7 +707,7 @@ im_sorry_dave(void)
|
|||
} \
|
||||
(d) = strdup_dst; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#define strndupx(d, s, n, ap) do { \
|
||||
#define strndupx(d,s,n,ap) do { \
|
||||
const char *strdup_src = (const void *)(s); \
|
||||
char *strdup_dst = NULL; \
|
||||
\
|
||||
|
@ -698,6 +720,33 @@ im_sorry_dave(void)
|
|||
(d) = strdup_dst; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#endif
|
||||
#define strdup2x(d,s1,s2) do { \
|
||||
const char *strdup_src = (const void *)(s1); \
|
||||
const char *strdup_app = (const void *)(s2); \
|
||||
size_t strndup_len = strlen(strdup_src); \
|
||||
size_t strndup_ln2 = strlen(strdup_app) + 1; \
|
||||
char *strdup_dst = alloc(strndup_len + strndup_ln2, ATEMP); \
|
||||
\
|
||||
memcpy(strdup_dst, strdup_src, strndup_len); \
|
||||
memcpy(strdup_dst + strndup_len, strdup_app, strndup_ln2); \
|
||||
(d) = strdup_dst; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#define strpathx(d,s1,s2,cond) do { \
|
||||
const char *strdup_src = (const void *)(s1); \
|
||||
const char *strdup_app = (const void *)(s2); \
|
||||
size_t strndup_len = strlen(strdup_src) + 1; \
|
||||
size_t strndup_ln2 = ((cond) || *strdup_app) ? \
|
||||
strlen(strdup_app) + 1 : 0; \
|
||||
char *strdup_dst = alloc(strndup_len + strndup_ln2, ATEMP); \
|
||||
\
|
||||
memcpy(strdup_dst, strdup_src, strndup_len); \
|
||||
if (strndup_ln2) { \
|
||||
strdup_dst[strndup_len - 1] = '/'; \
|
||||
memcpy(strdup_dst + strndup_len, strdup_app, \
|
||||
strndup_ln2); \
|
||||
} \
|
||||
(d) = strdup_dst; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
#ifdef MKSH_SMALL
|
||||
#ifndef MKSH_NOPWNAM
|
||||
|
@ -839,6 +888,7 @@ extern struct env {
|
|||
/* struct env.flag values */
|
||||
#define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */
|
||||
#define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */
|
||||
#define EF_IN_EVAL BIT(3) /* inside an eval */
|
||||
|
||||
/* Do breaks/continues stop at env type e? */
|
||||
#define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE || \
|
||||
|
@ -851,12 +901,13 @@ extern struct env {
|
|||
#define LRETURN 1 /* return statement */
|
||||
#define LEXIT 2 /* exit statement */
|
||||
#define LERROR 3 /* errorf() called */
|
||||
#define LLEAVE 4 /* untrappable exit/error */
|
||||
#define LERREXT 4 /* set -e caused */
|
||||
#define LINTR 5 /* ^C noticed */
|
||||
#define LBREAK 6 /* break statement */
|
||||
#define LCONTIN 7 /* continue statement */
|
||||
#define LSHELL 8 /* return to interactive shell() */
|
||||
#define LAEXPR 9 /* error in arithmetic expression */
|
||||
#define LLEAVE 10 /* untrappable exit/error */
|
||||
|
||||
/* sort of shell global state */
|
||||
EXTERN pid_t procpid; /* PID of executing process */
|
||||
|
@ -868,9 +919,9 @@ EXTERN uint8_t trap_nested; /* running nested traps */
|
|||
EXTERN uint8_t shell_flags[FNFLAGS];
|
||||
EXTERN const char *kshname; /* $0 */
|
||||
EXTERN struct {
|
||||
uid_t kshuid_v; /* real UID of shell */
|
||||
uid_t kshuid_v; /* real UID of shell at startup */
|
||||
uid_t ksheuid_v; /* effective UID of shell */
|
||||
gid_t kshgid_v; /* real GID of shell */
|
||||
gid_t kshgid_v; /* real GID of shell at startup */
|
||||
gid_t kshegid_v; /* effective GID of shell */
|
||||
pid_t kshpgrp_v; /* process group of shell */
|
||||
pid_t kshppid_v; /* PID of parent of shell */
|
||||
|
@ -955,6 +1006,7 @@ EXTERN const char Tsgcontinue[] E_INIT("*=continue");
|
|||
EXTERN const char Tcreate[] E_INIT("create");
|
||||
EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected");
|
||||
EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL");
|
||||
EXTERN const char TENV[] E_INIT("ENV");
|
||||
EXTERN const char Tdsgexport[] E_INIT("^*=export");
|
||||
#define Texport (Tdsgexport + 3)
|
||||
#ifdef __OS2__
|
||||
|
@ -997,10 +1049,11 @@ EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection");
|
|||
#define Tredirection (Tredirection_dup + 19)
|
||||
#define Treal_sp1 (Treal_sp2 + 1)
|
||||
EXTERN const char Treal_sp2[] E_INIT(" real ");
|
||||
EXTERN const char TREPLY[] E_INIT("REPLY");
|
||||
EXTERN const char Treq_arg[] E_INIT("requires an argument");
|
||||
EXTERN const char Tselect[] E_INIT("select");
|
||||
EXTERN const char Tsgset[] E_INIT("*=set");
|
||||
#define Tset (Tf_parm + 18)
|
||||
EXTERN const char Tsghset[] E_INIT("*=#set");
|
||||
#define Tsh (Tmksh + 2)
|
||||
#define TSHELL (TEXECSHELL + 4)
|
||||
#define Tshell (Ttoo_many_files + 23)
|
||||
|
@ -1032,7 +1085,6 @@ EXTERN const char Tf__S[] E_INIT(" %S");
|
|||
#define Tf__d (Tunexpected_type + 22)
|
||||
EXTERN const char Tf__ss[] E_INIT(" %s%s");
|
||||
#define Tf__sN (Tf_s_s_sN + 5)
|
||||
EXTERN const char Tf_sSs[] E_INIT("%s/%s");
|
||||
#define Tf_T (Tf_s_T + 3)
|
||||
EXTERN const char Tf_dN[] E_INIT("%d\n");
|
||||
EXTERN const char Tf_s_[] E_INIT("%s ");
|
||||
|
@ -1056,8 +1108,6 @@ EXTERN const char Tf_S_[] E_INIT("%S ");
|
|||
#define Tf_lu (Tf_toolarge + 17)
|
||||
EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu");
|
||||
EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s");
|
||||
#define Tf_ss (Tf_sss + 2)
|
||||
EXTERN const char Tf_sss[] E_INIT("%s%s%s");
|
||||
EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s");
|
||||
EXTERN const char Tf_toomany[] E_INIT("too many %ss");
|
||||
EXTERN const char Tf_sd[] E_INIT("%s %d");
|
||||
|
@ -1116,6 +1166,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
|
|||
#define Tcreate "create"
|
||||
#define TELIF_unexpected "TELIF unexpected"
|
||||
#define TEXECSHELL "EXECSHELL"
|
||||
#define TENV "ENV"
|
||||
#define Tdsgexport "^*=export"
|
||||
#define Texport "export"
|
||||
#ifdef __OS2__
|
||||
|
@ -1158,10 +1209,11 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
|
|||
#define Tredirection "redirection"
|
||||
#define Treal_sp1 "real "
|
||||
#define Treal_sp2 " real "
|
||||
#define TREPLY "REPLY"
|
||||
#define Treq_arg "requires an argument"
|
||||
#define Tselect "select"
|
||||
#define Tsgset "*=set"
|
||||
#define Tset "set"
|
||||
#define Tsghset "*=#set"
|
||||
#define Tsh "sh"
|
||||
#define TSHELL "SHELL"
|
||||
#define Tshell "shell"
|
||||
|
@ -1193,7 +1245,6 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
|
|||
#define Tf__d " %d"
|
||||
#define Tf__ss " %s%s"
|
||||
#define Tf__sN " %s\n"
|
||||
#define Tf_sSs "%s/%s"
|
||||
#define Tf_T "%T"
|
||||
#define Tf_dN "%d\n"
|
||||
#define Tf_s_ "%s "
|
||||
|
@ -1217,8 +1268,6 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
|
|||
#define Tf_lu "%lu"
|
||||
#define Tf_toolarge "%s %s too large: %lu"
|
||||
#define Tf_ldfailed "%s %s(%d, %ld) failed: %s"
|
||||
#define Tf_ss "%s%s"
|
||||
#define Tf_sss "%s%s%s"
|
||||
#define Tf_sD_s_sD_s "%s: %s %s: %s"
|
||||
#define Tf_toomany "too many %ss"
|
||||
#define Tf_sd "%s %d"
|
||||
|
@ -1372,7 +1421,7 @@ EXTERN bool really_exit;
|
|||
#define CiCOLON BIT(26) /* : */
|
||||
#define CiEQUAL BIT(27) /* = */
|
||||
#define CiQUEST BIT(28) /* ? */
|
||||
#define CiBRACK BIT(29) /* ] */
|
||||
#define CiBRACK BIT(29) /* [] */
|
||||
#define CiUNDER BIT(30) /* _ */
|
||||
#define CiGRAVE BIT(31) /* ` */
|
||||
/* out of space, but one for *@ would make sense, possibly others */
|
||||
|
@ -1386,8 +1435,8 @@ EXTERN char ifs0;
|
|||
|
||||
/* external types */
|
||||
|
||||
/* !%,-.0‥9:@A‥Z[]_a‥z valid characters in alias names */
|
||||
#define C_ALIAS (CiALIAS | CiBRACK | CiCOLON | CiDIGIT | CiLOWER | CiMINUS | CiOCTAL | CiPERCT | CiUNDER | CiUPPER)
|
||||
/* !%+,-.0‥9:@A‥Z[]_a‥z valid characters in alias names */
|
||||
#define C_ALIAS (CiALIAS | CiBRACK | CiCOLON | CiDIGIT | CiLOWER | CiMINUS | CiOCTAL | CiPERCT | CiPLUS | CiUNDER | CiUPPER)
|
||||
/* 0‥9A‥Za‥z alphanumerical */
|
||||
#define C_ALNUM (CiDIGIT | CiLOWER | CiOCTAL | CiUPPER)
|
||||
/* 0‥9A‥Z_a‥z alphanumerical plus underscore (“word character”) */
|
||||
|
@ -1533,6 +1582,12 @@ extern void ebcdic_init(void);
|
|||
#define ksh_toctrl(c) asc2rtt(ord(c) == ORD('?') ? 0x7F : rtt2asc(c) & 0x9F)
|
||||
#define ksh_unctrl(c) asc2rtt(rtt2asc(c) ^ 0x40U)
|
||||
|
||||
#ifdef MKSH_SMALL
|
||||
#define SMALLP(x) /* nothing */
|
||||
#else
|
||||
#define SMALLP(x) , x
|
||||
#endif
|
||||
|
||||
/* Argument parsing for built-in commands and getopts command */
|
||||
|
||||
/* Values for Getopt.flags */
|
||||
|
@ -1626,7 +1681,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
|
|||
#define shf_getc_i(shf) ((shf)->rnleft > 0 ? \
|
||||
(shf)->rnleft--, (int)ord(*(shf)->rp++) : \
|
||||
shf_getchar(shf))
|
||||
#define shf_putc_i(c, shf) ((shf)->wnleft == 0 ? \
|
||||
#define shf_putc_i(c,shf) ((shf)->wnleft == 0 ? \
|
||||
shf_putchar((uint8_t)(c), (shf)) : \
|
||||
((shf)->wnleft--, *(shf)->wp++ = (c)))
|
||||
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
|
||||
|
@ -1637,7 +1692,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
|
|||
/* Flags passed to shf_*open() */
|
||||
#define SHF_RD 0x0001
|
||||
#define SHF_WR 0x0002
|
||||
#define SHF_RDWR (SHF_RD|SHF_WR)
|
||||
#define SHF_RDWR (SHF_RD | SHF_WR)
|
||||
#define SHF_ACCMODE 0x0003 /* mask */
|
||||
#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
|
||||
#define SHF_UNBUF 0x0008 /* unbuffered I/O */
|
||||
|
@ -1756,14 +1811,15 @@ EXTERN bool last_lookup_was_array;
|
|||
#define LOW_BI BIT(14) /* external utility overrides built-in one */
|
||||
#define DECL_UTIL BIT(15) /* is declaration utility */
|
||||
#define DECL_FWDR BIT(16) /* is declaration utility forwarder */
|
||||
#define NEXTLOC_BI BIT(17) /* needs BF_RESETSPEC on e->loc */
|
||||
|
||||
/*
|
||||
* Attributes that can be set by the user (used to decide if an unset
|
||||
* param should be repoted by set/typeset). Does not include ARRAY or
|
||||
* LOCAL.
|
||||
*/
|
||||
#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
|
||||
LCASEV|UCASEV_AL|INT_U|INT_L)
|
||||
#define USERATTRIB (EXPORT | INTEGER | RDONLY | LJUST | RJUST | ZEROFIL | \
|
||||
LCASEV | UCASEV_AL | INT_U | INT_L)
|
||||
|
||||
#define arrayindex(vp) ((unsigned long)((vp)->flag & AINDEX ? \
|
||||
(vp)->ua.index : 0))
|
||||
|
@ -1794,7 +1850,7 @@ enum namerefflag {
|
|||
|
||||
#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
|
||||
#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
|
||||
#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
|
||||
#define AI_ARGV(a,i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
|
||||
#define AI_ARGC(a) ((a).ai_argc - (a).skip)
|
||||
|
||||
/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
|
||||
|
@ -1824,6 +1880,8 @@ struct block {
|
|||
/* Values for struct block.flags */
|
||||
#define BF_DOGETOPTS BIT(0) /* save/restore getopts state */
|
||||
#define BF_STOPENV BIT(1) /* do not export further */
|
||||
/* BF_RESETSPEC and NEXTLOC_BI must be numerically identical! */
|
||||
#define BF_RESETSPEC BIT(17) /* use ->next for set and shift */
|
||||
|
||||
/*
|
||||
* Used by ktwalk() and ktnext() routines.
|
||||
|
@ -1993,8 +2051,14 @@ struct ioword {
|
|||
#define DOSCALAR BIT(12) /* change field handling to non-list context */
|
||||
#define DOHEREDOC BIT(13) /* change scalar handling to heredoc body */
|
||||
#define DOHERESTR BIT(14) /* append a newline char */
|
||||
#define DODBMAGIC BIT(15) /* add magic to expansions for [[ x = $y ]] */
|
||||
|
||||
#define X_EXTRA 20 /* this many extra bytes in X string */
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
#define X_WASTE 15 /* allowed extra bytes to avoid shrinking, */
|
||||
#else
|
||||
#define X_WASTE 255 /* … must be 2ⁿ-1 */
|
||||
#endif
|
||||
|
||||
typedef struct XString {
|
||||
/* beginning of string */
|
||||
|
@ -2008,44 +2072,44 @@ typedef struct XString {
|
|||
} XString;
|
||||
|
||||
/* initialise expandable string */
|
||||
#define XinitN(xs, length, area) do { \
|
||||
#define XinitN(xs,length,area) do { \
|
||||
(xs).len = (length); \
|
||||
(xs).areap = (area); \
|
||||
(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \
|
||||
(xs).end = (xs).beg + (xs).len; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#define Xinit(xs, xp, length, area) do { \
|
||||
#define Xinit(xs,xp,length,area) do { \
|
||||
XinitN((xs), (length), (area)); \
|
||||
(xp) = (xs).beg; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
/* stuff char into string */
|
||||
#define Xput(xs, xp, c) (*xp++ = (c))
|
||||
#define Xput(xs,xp,c) (*xp++ = (c))
|
||||
|
||||
/* check if there are at least n bytes left */
|
||||
#define XcheckN(xs, xp, n) do { \
|
||||
#define XcheckN(xs,xp,n) do { \
|
||||
ssize_t more = ((xp) + (n)) - (xs).end; \
|
||||
if (more > 0) \
|
||||
(xp) = Xcheck_grow(&(xs), (xp), (size_t)more); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
/* check for overflow, expand string */
|
||||
#define Xcheck(xs, xp) XcheckN((xs), (xp), 1)
|
||||
#define Xcheck(xs,xp) XcheckN((xs), (xp), 1)
|
||||
|
||||
/* free string */
|
||||
#define Xfree(xs, xp) afree((xs).beg, (xs).areap)
|
||||
#define Xfree(xs,xp) afree((xs).beg, (xs).areap)
|
||||
|
||||
/* close, return string */
|
||||
#define Xclose(xs, xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
|
||||
#define Xclose(xs,xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
|
||||
|
||||
/* beginning of string */
|
||||
#define Xstring(xs, xp) ((xs).beg)
|
||||
#define Xstring(xs,xp) ((xs).beg)
|
||||
|
||||
#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
|
||||
#define Xlength(xs, xp) ((xp) - (xs).beg)
|
||||
#define Xsize(xs, xp) ((xs).end - (xs).beg)
|
||||
#define Xsavepos(xs, xp) ((xp) - (xs).beg)
|
||||
#define Xrestpos(xs, xp, n) ((xs).beg + (n))
|
||||
#define Xnleft(xs,xp) ((xs).end - (xp)) /* may be less than 0 */
|
||||
#define Xlength(xs,xp) ((xp) - (xs).beg)
|
||||
#define Xsize(xs,xp) ((xs).end - (xs).beg)
|
||||
#define Xsavepos(xs,xp) ((xp) - (xs).beg)
|
||||
#define Xrestpos(xs,xp,n) ((xs).beg + (n))
|
||||
|
||||
char *Xcheck_grow(XString *, const char *, size_t);
|
||||
|
||||
|
@ -2062,13 +2126,13 @@ typedef struct {
|
|||
size_t siz;
|
||||
} XPtrV;
|
||||
|
||||
#define XPinit(x, n) do { \
|
||||
#define XPinit(x,n) do { \
|
||||
(x).siz = (n); \
|
||||
(x).len = 0; \
|
||||
(x).beg = alloc2((x).siz, sizeof(void *), ATEMP); \
|
||||
} while (/* CONSTCOND */ 0) \
|
||||
|
||||
#define XPput(x, p) do { \
|
||||
#define XPput(x,p) do { \
|
||||
if ((x).len == (x).siz) { \
|
||||
(x).beg = aresize2((x).beg, (x).siz, \
|
||||
2 * sizeof(void *), ATEMP); \
|
||||
|
@ -2296,12 +2360,12 @@ EXTERN mksh_ari_t histsize; /* history size */
|
|||
/* user and system time of last j_waitjed job */
|
||||
EXTERN struct timeval j_usrtime, j_systime;
|
||||
|
||||
#define notok2mul(max, val, c) (((val) != 0) && ((c) != 0) && \
|
||||
#define notok2mul(max,val,c) (((val) != 0) && ((c) != 0) && \
|
||||
(((max) / (c)) < (val)))
|
||||
#define notok2add(max, val, c) ((val) > ((max) - (c)))
|
||||
#define notoktomul(val, cnst) notok2mul(SIZE_MAX, (val), (cnst))
|
||||
#define notoktoadd(val, cnst) notok2add(SIZE_MAX, (val), (cnst))
|
||||
#define checkoktoadd(val, cnst) do { \
|
||||
#define notok2add(max,val,c) ((val) > ((max) - (c)))
|
||||
#define notoktomul(val,cnst) notok2mul(SIZE_MAX, (val), (cnst))
|
||||
#define notoktoadd(val,cnst) notok2add(SIZE_MAX, (val), (cnst))
|
||||
#define checkoktoadd(val,cnst) do { \
|
||||
if (notoktoadd((val), (cnst))) \
|
||||
internal_errorf(Tintovfl, (size_t)(val), \
|
||||
'+', (size_t)(cnst)); \
|
||||
|
@ -2312,18 +2376,20 @@ EXTERN struct timeval j_usrtime, j_systime;
|
|||
void ainit(Area *);
|
||||
void afreeall(Area *);
|
||||
/* these cannot fail and can take NULL (not for ap) */
|
||||
#define alloc(n, ap) aresize(NULL, (n), (ap))
|
||||
#define alloc2(m, n, ap) aresize2(NULL, (m), (n), (ap))
|
||||
#define alloc(n,ap) aresize(NULL, (n), (ap))
|
||||
#define alloc2(m,n,ap) aresize2(NULL, (m), (n), (ap))
|
||||
void *aresize(void *, size_t, Area *);
|
||||
void *aresize2(void *, size_t, size_t, Area *);
|
||||
void afree(void *, Area *); /* can take NULL */
|
||||
#define aresizeif(z,p,n,ap) (((p) == NULL) || ((z) < (n)) || \
|
||||
(((z) & ~X_WASTE) > ((n) & ~X_WASTE)) ? \
|
||||
aresize((p), (n), (ap)) : (p))
|
||||
/* edit.c */
|
||||
#ifndef MKSH_NO_CMDLINE_EDITING
|
||||
#ifndef MKSH_SMALL
|
||||
int x_bind(const char *, const char *, bool, bool);
|
||||
#else
|
||||
int x_bind(const char *, const char *, bool);
|
||||
#endif
|
||||
int x_bind(const char * SMALLP(bool));
|
||||
int x_bind_check(void);
|
||||
int x_bind_list(void);
|
||||
int x_bind_showall(void);
|
||||
void x_init(void);
|
||||
#ifdef DEBUG_LEAKS
|
||||
void x_done(void);
|
||||
|
@ -2511,6 +2577,8 @@ void warningf(bool, const char *, ...)
|
|||
MKSH_A_FORMAT(__printf__, 2, 3);
|
||||
void bi_errorf(const char *, ...)
|
||||
MKSH_A_FORMAT(__printf__, 1, 2);
|
||||
void maybe_errorf(int *, int, const char *, ...)
|
||||
MKSH_A_FORMAT(__printf__, 3, 4);
|
||||
#define errorfz() errorf(NULL)
|
||||
#define errorfxz(rc) errorfx((rc), NULL)
|
||||
#define bi_errorfz() bi_errorf(NULL)
|
||||
|
@ -2543,7 +2611,7 @@ struct temp *maketemp(Area *, Temp_type, struct temp **);
|
|||
void ktinit(Area *, struct table *, uint8_t);
|
||||
struct tbl *ktscan(struct table *, const char *, uint32_t, struct tbl ***);
|
||||
/* table, name (key) to search for, hash(n) */
|
||||
#define ktsearch(tp, s, h) ktscan((tp), (s), (h), NULL)
|
||||
#define ktsearch(tp,s,h) ktscan((tp), (s), (h), NULL)
|
||||
struct tbl *ktenter(struct table *, const char *, uint32_t);
|
||||
#define ktdelete(p) do { p->flag = 0; } while (/* CONSTCOND */ 0)
|
||||
void ktwalk(struct tstate *, struct table *);
|
||||
|
@ -2591,7 +2659,7 @@ int unbksl(bool, int (*)(void), void (*)(int));
|
|||
void os2_init(int *, const char ***);
|
||||
void setextlibpath(const char *, const char *);
|
||||
int access_ex(int (*)(const char *, int), const char *, int);
|
||||
int stat_ex(const char *, struct stat *);
|
||||
int stat_ex(int (*)(const char *, struct stat *), const char *, struct stat *);
|
||||
const char *real_exec_name(const char *);
|
||||
#endif
|
||||
/* shf.c */
|
||||
|
@ -2607,7 +2675,7 @@ ssize_t shf_read(char *, ssize_t, struct shf *);
|
|||
char *shf_getse(char *, ssize_t, struct shf *);
|
||||
int shf_getchar(struct shf *s);
|
||||
int shf_ungetc(int, struct shf *);
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
#ifdef MKSH_SHF_NO_INLINE
|
||||
int shf_getc(struct shf *);
|
||||
int shf_putc(int, struct shf *);
|
||||
#else
|
||||
|
@ -2712,6 +2780,24 @@ enum Test_meta {
|
|||
};
|
||||
typedef enum Test_meta Test_meta;
|
||||
|
||||
struct t_op {
|
||||
const char op_text[4];
|
||||
Test_op op_num;
|
||||
};
|
||||
|
||||
/* for string reuse */
|
||||
extern const struct t_op u_ops[];
|
||||
extern const struct t_op b_ops[];
|
||||
/* ensure order with funcs.c */
|
||||
#define Tda (u_ops[0].op_text)
|
||||
#define Tdn (u_ops[12].op_text)
|
||||
#define Tdo (u_ops[14].op_text)
|
||||
#define Tdr (u_ops[16].op_text)
|
||||
#define Tdu (u_ops[20].op_text) /* "-u" */
|
||||
#define Tdx (u_ops[23].op_text)
|
||||
|
||||
#define Tu (Tdu + 1) /* "u" */
|
||||
|
||||
#define TEF_ERROR BIT(0) /* set if we've hit an error */
|
||||
#define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#ifndef SHFLAGS_OPTCS
|
||||
#if defined(SHFLAGS_DEFNS)
|
||||
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.8 2019/12/30 03:58:58 tg Exp $");
|
||||
#elif defined(SHFLAGS_ENUMS)
|
||||
#define FN(sname,cname,flags,ochar) cname,
|
||||
#define F0(sname,cname,flags,ochar) cname = 0,
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
|
||||
@SHFLAGS_DEFNS
|
||||
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.8 2019/12/30 03:58:58 tg Exp $");
|
||||
@SHFLAGS_ENUMS
|
||||
#define FN(sname,cname,flags,ochar) cname,
|
||||
#define F0(sname,cname,flags,ochar) cname = 0,
|
||||
|
@ -184,7 +184,7 @@ FN("xtrace", FXTRACE, OF_ANY
|
|||
FN("", FCOMMAND, OF_CMDLINE
|
||||
|
||||
/*
|
||||
* anonymous flags: used internally by shell only (not visible to user
|
||||
* anonymous flags: used internally by shell only (not visible to user)
|
||||
*/
|
||||
|
||||
/* ./. direct builtin call (divined from argv[0] multi-call binary) */
|
||||
|
|
13
src/shf.c
13
src/shf.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
|
||||
* 2012, 2013, 2015, 2016, 2017, 2018
|
||||
* 2012, 2013, 2015, 2016, 2017, 2018, 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
* Copyright (c) 2015
|
||||
* Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.98 2018/08/10 02:53:39 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.101 2019/12/11 17:56:58 tg Exp $");
|
||||
|
||||
/* flags to shf_emptybuf() */
|
||||
#define EB_READSW 0x01 /* about to switch to reading */
|
||||
|
@ -523,7 +523,8 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
|
|||
buf += ncopy;
|
||||
bsize -= ncopy;
|
||||
#ifdef MKSH_WITH_TEXTMODE
|
||||
if (end && buf > orig_buf + 1 && buf[-2] == '\r') {
|
||||
if (buf > orig_buf + 1 && ord(buf[-2]) == ORD('\r') &&
|
||||
ord(buf[-1]) == ORD('\n')) {
|
||||
buf--;
|
||||
bsize++;
|
||||
buf[-1] = '\n';
|
||||
|
@ -531,9 +532,9 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
|
|||
#endif
|
||||
} while (!end && bsize);
|
||||
#ifdef MKSH_WITH_TEXTMODE
|
||||
if (!bsize && buf[-1] == '\r') {
|
||||
if (!bsize && ord(buf[-1]) == ORD('\r')) {
|
||||
int c = shf_getc(shf);
|
||||
if (c == '\n')
|
||||
if (ord(c) == ORD('\n'))
|
||||
buf[-1] = '\n';
|
||||
else if (c != -1)
|
||||
shf_ungetc(c, shf);
|
||||
|
@ -1073,7 +1074,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
return (shf_error(shf) ? -1 : nwritten);
|
||||
}
|
||||
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
#ifdef MKSH_SHF_NO_INLINE
|
||||
int
|
||||
shf_getc(struct shf *shf)
|
||||
{
|
||||
|
|
176
src/syn.c
176
src/syn.c
|
@ -3,7 +3,7 @@
|
|||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017,
|
||||
* 2018
|
||||
* 2018, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.127 2018/01/14 00:22:30 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.128 2020/03/31 00:30:05 tg Exp $");
|
||||
|
||||
struct nesting_state {
|
||||
int start_token; /* token than began nesting (eg, FOR) */
|
||||
|
@ -271,6 +271,7 @@ get_command(int cf, int sALIAS)
|
|||
struct ioword *iop, **iops;
|
||||
XPtrV args, vars;
|
||||
struct nesting_state old_nesting;
|
||||
bool check_decl_utility;
|
||||
|
||||
/* NUFILE is small enough to leave this addition unchecked */
|
||||
iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP);
|
||||
|
@ -294,100 +295,93 @@ get_command(int cf, int sALIAS)
|
|||
t = newtp(TCOM);
|
||||
t->lineno = source->line;
|
||||
goto get_command_start;
|
||||
while (/* CONSTCOND */ 1) {
|
||||
bool check_decl_utility;
|
||||
|
||||
if (XPsize(args) == 0) {
|
||||
get_command_loop:
|
||||
if (XPsize(args) == 0) {
|
||||
get_command_start:
|
||||
check_decl_utility = true;
|
||||
cf = sALIAS | CMDASN;
|
||||
} else if (t->u.evalflags)
|
||||
cf = CMDWORD | CMDASN;
|
||||
else
|
||||
cf = CMDWORD;
|
||||
switch (tpeek(cf)) {
|
||||
case REDIR:
|
||||
while ((iop = synio(cf)) != NULL) {
|
||||
if (iopn >= NUFILE)
|
||||
yyerror(Tf_toomany,
|
||||
Tredirection);
|
||||
iops[iopn++] = iop;
|
||||
}
|
||||
break;
|
||||
check_decl_utility = true;
|
||||
cf = sALIAS | CMDASN;
|
||||
} else if (t->u.evalflags)
|
||||
cf = CMDWORD | CMDASN;
|
||||
else
|
||||
cf = CMDWORD;
|
||||
|
||||
case LWORD:
|
||||
ACCEPT;
|
||||
if (check_decl_utility) {
|
||||
struct tbl *tt = get_builtin(ident);
|
||||
uint32_t flag;
|
||||
|
||||
flag = tt ? tt->flag : 0;
|
||||
if (flag & DECL_UTIL)
|
||||
t->u.evalflags = DOVACHECK;
|
||||
if (!(flag & DECL_FWDR))
|
||||
check_decl_utility = false;
|
||||
}
|
||||
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
|
||||
is_wdvarassign(yylval.cp))
|
||||
XPput(vars, yylval.cp);
|
||||
else
|
||||
XPput(args, yylval.cp);
|
||||
break;
|
||||
|
||||
case ORD('(' /*)*/):
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
|
||||
is_wdvarassign(yylval.cp)) {
|
||||
char *tcp;
|
||||
|
||||
/* wdarrassign: foo=(bar) */
|
||||
ACCEPT;
|
||||
|
||||
/* manipulate the vars string */
|
||||
tcp = XPptrv(vars)[(vars.len = 0)];
|
||||
/* 'varname=' -> 'varname' */
|
||||
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
|
||||
|
||||
/* construct new args strings */
|
||||
XPput(args, wdcopy(builtin_cmd, ATEMP));
|
||||
XPput(args, wdcopy(setA_cmd0, ATEMP));
|
||||
XPput(args, wdcopy(setA_cmd1, ATEMP));
|
||||
XPput(args, tcp);
|
||||
XPput(args, wdcopy(setA_cmd2, ATEMP));
|
||||
|
||||
/* slurp in words till closing paren */
|
||||
while (token(CONTIN) == LWORD)
|
||||
XPput(args, yylval.cp);
|
||||
if (symbol != /*(*/ ')')
|
||||
syntaxerr(NULL);
|
||||
} else {
|
||||
/*
|
||||
* Check for "> foo (echo hi)"
|
||||
* which AT&T ksh allows (not
|
||||
* POSIX, but not disallowed)
|
||||
*/
|
||||
afree(t, ATEMP);
|
||||
if (XPsize(args) == 0 &&
|
||||
XPsize(vars) == 0) {
|
||||
ACCEPT;
|
||||
goto Subshell;
|
||||
}
|
||||
|
||||
/* must be a function */
|
||||
if (iopn != 0 || XPsize(args) != 1 ||
|
||||
XPsize(vars) != 0)
|
||||
syntaxerr(NULL);
|
||||
ACCEPT;
|
||||
musthave(/*(*/ ')', 0);
|
||||
t = function_body(XPptrv(args)[0],
|
||||
sALIAS, false);
|
||||
}
|
||||
goto Leave;
|
||||
|
||||
default:
|
||||
goto Leave;
|
||||
switch (tpeek(cf)) {
|
||||
case REDIR:
|
||||
while ((iop = synio(cf)) != NULL) {
|
||||
if (iopn >= NUFILE)
|
||||
yyerror(Tf_toomany, Tredirection);
|
||||
iops[iopn++] = iop;
|
||||
}
|
||||
goto get_command_loop;
|
||||
|
||||
case LWORD:
|
||||
ACCEPT;
|
||||
if (check_decl_utility) {
|
||||
struct tbl *tt = get_builtin(ident);
|
||||
uint32_t flag;
|
||||
|
||||
flag = tt ? tt->flag : 0;
|
||||
if (flag & DECL_UTIL)
|
||||
t->u.evalflags = DOVACHECK;
|
||||
if (!(flag & DECL_FWDR))
|
||||
check_decl_utility = false;
|
||||
}
|
||||
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
|
||||
is_wdvarassign(yylval.cp))
|
||||
XPput(vars, yylval.cp);
|
||||
else
|
||||
XPput(args, yylval.cp);
|
||||
goto get_command_loop;
|
||||
|
||||
case ORD('(' /*)*/):
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
|
||||
is_wdvarassign(yylval.cp)) {
|
||||
char *tcp;
|
||||
|
||||
/* wdarrassign: foo=(bar) */
|
||||
ACCEPT;
|
||||
|
||||
/* manipulate the vars string */
|
||||
tcp = XPptrv(vars)[(vars.len = 0)];
|
||||
/* 'varname=' -> 'varname' */
|
||||
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
|
||||
|
||||
/* construct new args strings */
|
||||
XPput(args, wdcopy(builtin_cmd, ATEMP));
|
||||
XPput(args, wdcopy(setA_cmd0, ATEMP));
|
||||
XPput(args, wdcopy(setA_cmd1, ATEMP));
|
||||
XPput(args, tcp);
|
||||
XPput(args, wdcopy(setA_cmd2, ATEMP));
|
||||
|
||||
/* slurp in words till closing paren */
|
||||
while (token(CONTIN) == LWORD)
|
||||
XPput(args, yylval.cp);
|
||||
if (symbol != /*(*/ ')')
|
||||
syntaxerr(NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
afree(t, ATEMP);
|
||||
|
||||
/*
|
||||
* Check for "> foo (echo hi)" which AT&T ksh allows
|
||||
* (not POSIX, but not disallowed)
|
||||
*/
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 0) {
|
||||
ACCEPT;
|
||||
goto Subshell;
|
||||
}
|
||||
|
||||
/* must be a function */
|
||||
if (iopn != 0 || XPsize(args) != 1 || XPsize(vars) != 0)
|
||||
syntaxerr(NULL);
|
||||
ACCEPT;
|
||||
musthave(/*(*/ ')', 0);
|
||||
t = function_body(XPptrv(args)[0],
|
||||
sALIAS, false);
|
||||
break;
|
||||
}
|
||||
Leave:
|
||||
break;
|
||||
|
||||
case ORD('(' /*)*/): {
|
||||
|
|
170
src/var.c
170
src/var.c
|
@ -2,7 +2,8 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
* 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -28,7 +29,7 @@
|
|||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.226 2018/07/15 17:21:24 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.236 2020/04/13 16:29:34 tg Exp $");
|
||||
|
||||
/*-
|
||||
* Variables
|
||||
|
@ -49,7 +50,7 @@ static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
|
|||
static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
|
||||
bool);
|
||||
static char *formatstr(struct tbl *, const char *);
|
||||
static void exportprep(struct tbl *, const char *);
|
||||
static void exportprep(struct tbl *, const char *, size_t);
|
||||
static int special(const char *);
|
||||
static void unspecial(const char *);
|
||||
static void getspec(struct tbl *);
|
||||
|
@ -57,6 +58,7 @@ static void setspec(struct tbl *);
|
|||
static void unsetspec(struct tbl *, bool);
|
||||
static int getint(struct tbl *, mksh_ari_u *, bool);
|
||||
static const char *array_index_calc(const char *, bool *, uint32_t *);
|
||||
static struct tbl *vtypeset(int *, const char *, uint32_t, uint32_t, int, int);
|
||||
|
||||
/*
|
||||
* create a new block for function calls and simple commands
|
||||
|
@ -196,7 +198,7 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
|
|||
char *cp;
|
||||
|
||||
/* gotcha! */
|
||||
cp = shf_smprintf(Tf_ss, str_val(vp), p);
|
||||
strdup2x(cp, str_val(vp), p);
|
||||
afree(ap, ATEMP);
|
||||
n = ap = cp;
|
||||
goto redo_from_ref;
|
||||
|
@ -207,16 +209,21 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
|
|||
if (p != n && ord(*p) == ORD('[') && (len = array_ref_len(p))) {
|
||||
char *sub, *tmp;
|
||||
mksh_ari_t rval;
|
||||
size_t tmplen = p - n;
|
||||
|
||||
/* calculate the value of the subscript */
|
||||
*arrayp = true;
|
||||
strndupx(tmp, p + 1, len - 2, ATEMP);
|
||||
len -= 2;
|
||||
tmp = alloc((len > tmplen ? len : tmplen) + 1, ATEMP);
|
||||
memcpy(tmp, p + 1, len);
|
||||
tmp[len] = '\0';
|
||||
sub = substitute(tmp, 0);
|
||||
afree(tmp, ATEMP);
|
||||
strndupx(n, n, p - n, ATEMP);
|
||||
evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
|
||||
*valp = (uint32_t)rval;
|
||||
afree(sub, ATEMP);
|
||||
memcpy(tmp, n, tmplen);
|
||||
tmp[tmplen] = '\0';
|
||||
n = tmp;
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
@ -273,7 +280,7 @@ isglobal(const char *n, bool docreate)
|
|||
vp->name[0] = c;
|
||||
vp->name[1] = '\0';
|
||||
vp->flag |= RDONLY;
|
||||
if (vn[1] != '\0')
|
||||
if (!c || vn[1] != '\0')
|
||||
goto out;
|
||||
vp->flag |= ISSET|INTEGER;
|
||||
switch (c) {
|
||||
|
@ -450,7 +457,6 @@ str_val(struct tbl *vp)
|
|||
int
|
||||
setstr(struct tbl *vq, const char *s, int error_ok)
|
||||
{
|
||||
char *salloc = NULL;
|
||||
bool no_ro_check = tobool(error_ok & 0x4);
|
||||
|
||||
error_ok &= ~0x4;
|
||||
|
@ -462,28 +468,33 @@ setstr(struct tbl *vq, const char *s, int error_ok)
|
|||
}
|
||||
if (!(vq->flag&INTEGER)) {
|
||||
/* string dest */
|
||||
char *salloc = NULL;
|
||||
size_t cursz;
|
||||
if ((vq->flag&ALLOC)) {
|
||||
cursz = strlen(vq->val.s) + 1;
|
||||
#ifndef MKSH_SMALL
|
||||
/* debugging */
|
||||
if (s >= vq->val.s &&
|
||||
s <= strnul(vq->val.s)) {
|
||||
if (s >= vq->val.s && s < (vq->val.s + cursz)) {
|
||||
internal_errorf(
|
||||
"setstr: %s=%s: assigning to self",
|
||||
vq->name, s);
|
||||
}
|
||||
#endif
|
||||
afree(vq->val.s, vq->areap);
|
||||
}
|
||||
vq->flag &= ~(ISSET|ALLOC);
|
||||
vq->type = 0;
|
||||
} else
|
||||
cursz = 0;
|
||||
if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
|
||||
s = salloc = formatstr(vq, s);
|
||||
if ((vq->flag&EXPORT))
|
||||
exportprep(vq, s);
|
||||
exportprep(vq, s, cursz);
|
||||
else {
|
||||
strdupx(vq->val.s, s, vq->areap);
|
||||
size_t n = strlen(s) + 1;
|
||||
vq->val.s = aresizeif(cursz, (vq->flag & ALLOC) ?
|
||||
vq->val.s : NULL, n, vq->areap);
|
||||
memcpy(vq->val.s, s, n);
|
||||
vq->flag |= ALLOC;
|
||||
vq->type = 0;
|
||||
}
|
||||
afree(salloc, ATEMP);
|
||||
} else {
|
||||
/* integer dest */
|
||||
if (!v_evaluate(vq, s, error_ok, true))
|
||||
|
@ -492,7 +503,6 @@ setstr(struct tbl *vq, const char *s, int error_ok)
|
|||
vq->flag |= ISSET;
|
||||
if ((vq->flag&SPECIAL))
|
||||
setspec(vq);
|
||||
afree(salloc, ATEMP);
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
@ -728,25 +738,19 @@ formatstr(struct tbl *vp, const char *s)
|
|||
* make vp->val.s be "name=value" for quick exporting.
|
||||
*/
|
||||
static void
|
||||
exportprep(struct tbl *vp, const char *val)
|
||||
exportprep(struct tbl *vp, const char *val, size_t cursz)
|
||||
{
|
||||
char *xp;
|
||||
char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
|
||||
size_t namelen, vallen;
|
||||
|
||||
namelen = strlen(vp->name);
|
||||
vallen = strlen(val) + 1;
|
||||
char *cp = (vp->flag & ALLOC) ? vp->val.s : NULL;
|
||||
size_t namelen = strlen(vp->name);
|
||||
size_t vallen = strlen(val) + 1;
|
||||
|
||||
vp->flag |= ALLOC;
|
||||
vp->type = namelen + 1;
|
||||
/* since name+val are both in memory this can go unchecked */
|
||||
xp = alloc(namelen + 1 + vallen, vp->areap);
|
||||
memcpy(vp->val.s = xp, vp->name, namelen);
|
||||
xp += namelen;
|
||||
*xp++ = '=';
|
||||
/* offset to value */
|
||||
vp->type = xp - vp->val.s;
|
||||
memcpy(xp, val, vallen);
|
||||
afree(op, vp->areap);
|
||||
vp->val.s = aresizeif(cursz, cp, vp->type + vallen, vp->areap);
|
||||
memmove(vp->val.s + vp->type, val == cp ? vp->val.s : val, vallen);
|
||||
memcpy(vp->val.s, vp->name, namelen);
|
||||
vp->val.s[namelen] = '=';
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -756,15 +760,24 @@ exportprep(struct tbl *vp, const char *val)
|
|||
*/
|
||||
struct tbl *
|
||||
typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
||||
{
|
||||
return (vtypeset(NULL, var, set, clr, field, base));
|
||||
}
|
||||
static struct tbl *
|
||||
vtypeset(int *ep, const char *var, uint32_t set, uint32_t clr,
|
||||
int field, int base)
|
||||
{
|
||||
struct tbl *vp;
|
||||
struct tbl *vpbase, *t;
|
||||
char *tvar;
|
||||
char *tvar, tvarbuf[32];
|
||||
const char *val;
|
||||
size_t len;
|
||||
bool vappend = false;
|
||||
enum namerefflag new_refflag = SRF_NOP;
|
||||
|
||||
if (ep)
|
||||
*ep = 0;
|
||||
|
||||
if ((set & (ARRAY | ASSOC)) == ASSOC) {
|
||||
new_refflag = SRF_ENABLE;
|
||||
set &= ~(ARRAY | ASSOC);
|
||||
|
@ -782,8 +795,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
}
|
||||
if (ord(*val) == ORD('[')) {
|
||||
if (new_refflag != SRF_NOP)
|
||||
errorf(Tf_sD_s, var,
|
||||
"reference variable can't be an array");
|
||||
return (maybe_errorf(ep, 1, Tf_sD_s, var,
|
||||
"reference variable can't be an array"), NULL);
|
||||
len = array_ref_len(val);
|
||||
if (len == 0)
|
||||
return (NULL);
|
||||
|
@ -804,13 +817,19 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
val += len;
|
||||
}
|
||||
if (ord(val[0]) == ORD('=')) {
|
||||
strndupx(tvar, var, val - var, ATEMP);
|
||||
len = val - var;
|
||||
tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
|
||||
memcpy(tvar, var, len);
|
||||
tvar[len] = '\0';
|
||||
++val;
|
||||
} else if (set & IMPORT) {
|
||||
/* environment invalid variable name or no assignment */
|
||||
return (NULL);
|
||||
} else if (ord(val[0]) == ORD('+') && ord(val[1]) == ORD('=')) {
|
||||
strndupx(tvar, var, val - var, ATEMP);
|
||||
len = val - var;
|
||||
tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
|
||||
memcpy(tvar, var, len);
|
||||
tvar[len] = '\0';
|
||||
val += 2;
|
||||
vappend = true;
|
||||
} else if (val[0] != '\0') {
|
||||
|
@ -818,10 +837,12 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
return (NULL);
|
||||
} else {
|
||||
/* just varname with no value part nor equals sign */
|
||||
strdupx(tvar, var, ATEMP);
|
||||
len = strlen(var);
|
||||
tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
|
||||
memcpy(tvar, var, len);
|
||||
tvar[len] = '\0';
|
||||
val = NULL;
|
||||
/* handle foo[*] => foo (whole array) mapping for R39b */
|
||||
len = strlen(tvar);
|
||||
if (len > 3 && ord(tvar[len - 3]) == ORD('[') &&
|
||||
ord(tvar[len - 2]) == ORD('*') &&
|
||||
ord(tvar[len - 1]) == ORD(']'))
|
||||
|
@ -833,7 +854,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
|
||||
/* bail out on 'nameref foo+=bar' */
|
||||
if (vappend)
|
||||
errorf("appending not allowed for nameref");
|
||||
return (maybe_errorf(ep, 1,
|
||||
"appending not allowed for nameref"), NULL);
|
||||
/* find value if variable already exists */
|
||||
if ((qval = val) == NULL) {
|
||||
varsearch(e->loc, &vp, tvar, hash(tvar));
|
||||
|
@ -859,7 +881,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
goto nameref_rhs_checked;
|
||||
}
|
||||
nameref_empty:
|
||||
errorf(Tf_sD_s, var, "empty nameref target");
|
||||
return (maybe_errorf(ep, 1, Tf_sD_s, var,
|
||||
"empty nameref target"), NULL);
|
||||
}
|
||||
len = (ord(*ccp) == ORD('[')) ? array_ref_len(ccp) : 0;
|
||||
if (ccp[len]) {
|
||||
|
@ -868,15 +891,15 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
* junk after it" and "invalid array"; in the
|
||||
* latter case, len is also 0 and points to '['
|
||||
*/
|
||||
errorf(Tf_sD_s, qval,
|
||||
"nameref target not a valid parameter name");
|
||||
return (maybe_errorf(ep, 1, Tf_sD_s, qval,
|
||||
"nameref target not a valid parameter name"), NULL);
|
||||
}
|
||||
nameref_rhs_checked:
|
||||
/* prevent nameref loops */
|
||||
while (qval) {
|
||||
if (!strcmp(qval, tvar))
|
||||
errorf(Tf_sD_s, qval,
|
||||
"expression recurses on parameter");
|
||||
return (maybe_errorf(ep, 1, Tf_sD_s, qval,
|
||||
"expression recurses on parameter"), NULL);
|
||||
varsearch(e->loc, &vp, qval, hash(qval));
|
||||
qval = NULL;
|
||||
if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
|
||||
|
@ -886,8 +909,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
|
||||
/* prevent typeset from creating a local PATH/ENV/SHELL */
|
||||
if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
|
||||
strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
|
||||
errorf(Tf_sD_s, tvar, "restricted");
|
||||
strcmp(tvar, TENV) == 0 || strcmp(tvar, TSHELL) == 0))
|
||||
return (maybe_errorf(ep, 1, Tf_sD_s,
|
||||
tvar, "restricted"), NULL);
|
||||
|
||||
innermost_refflag = new_refflag;
|
||||
vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
|
||||
|
@ -923,9 +947,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
*/
|
||||
if ((vpbase->flag & RDONLY) &&
|
||||
(val || clr || (set & ~(EXPORT | RDONLY))))
|
||||
/* XXX check calls - is error here ok by POSIX? */
|
||||
errorfx(2, Tf_ro, tvar);
|
||||
afree(tvar, ATEMP);
|
||||
return (maybe_errorf(ep, 2, Tf_ro, tvar), NULL);
|
||||
if (tvar != tvarbuf)
|
||||
afree(tvar, ATEMP);
|
||||
|
||||
/* most calls are with set/clr == 0 */
|
||||
if (set | clr) {
|
||||
|
@ -990,18 +1014,24 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
}
|
||||
}
|
||||
if (!ok)
|
||||
errorfz();
|
||||
return (maybe_errorf(ep, 1, NULL), NULL);
|
||||
}
|
||||
|
||||
if (val != NULL) {
|
||||
char *tval;
|
||||
|
||||
if (vappend) {
|
||||
tval = shf_smprintf(Tf_ss, str_val(vp), val);
|
||||
val = tval;
|
||||
} else
|
||||
tval = NULL;
|
||||
|
||||
if (vappend) {
|
||||
size_t tlen;
|
||||
if ((vp->flag & (ISSET|ALLOC|SPECIAL|INTEGER|UCASEV_AL|LCASEV|LJUST|RJUST)) != (ISSET|ALLOC)) {
|
||||
/* cannot special-case this */
|
||||
strdup2x(tvar, str_val(vp), val);
|
||||
val = tvar;
|
||||
goto vassign;
|
||||
}
|
||||
/* trivial string appending */
|
||||
len = strlen(vp->val.s);
|
||||
tlen = strlen(val) + 1;
|
||||
vp->val.s = aresize(vp->val.s, len + tlen, vp->areap);
|
||||
memcpy(vp->val.s + len, val, tlen);
|
||||
} else if (val != NULL) {
|
||||
vassign:
|
||||
if (vp->flag&INTEGER) {
|
||||
/* do not zero base before assignment */
|
||||
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
|
||||
|
@ -1012,13 +1042,16 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
/* setstr can't fail (readonly check already done) */
|
||||
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
|
||||
|
||||
afree(tval, ATEMP);
|
||||
/* came here from vappend? need to free temp val */
|
||||
if (vappend)
|
||||
afree(tvar, ATEMP);
|
||||
}
|
||||
|
||||
/* only x[0] is ever exported, so use vpbase */
|
||||
if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
|
||||
if ((vpbase->flag & (EXPORT|INTEGER)) == EXPORT &&
|
||||
vpbase->type == 0)
|
||||
exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
|
||||
exportprep(vpbase, (vpbase->flag & ISSET) ?
|
||||
vpbase->val.s : null, 0);
|
||||
|
||||
return (vp);
|
||||
}
|
||||
|
@ -1829,7 +1862,7 @@ record_match(const char *istr)
|
|||
setstr(vp, istr, 0x4);
|
||||
}
|
||||
|
||||
/* typeset, global(deprecated), export, and readonly */
|
||||
/* typeset, export and readonly */
|
||||
int
|
||||
c_typeset(const char **wp)
|
||||
{
|
||||
|
@ -2026,7 +2059,7 @@ c_typeset(const char **wp)
|
|||
if (wp[builtin_opt.optind] &&
|
||||
/* not "typeset -p varname" */
|
||||
!(!func && pflag && !(fset | fclr))) {
|
||||
int rv = 0;
|
||||
int rv = 0, x;
|
||||
struct tbl *f;
|
||||
|
||||
if (localv && !func)
|
||||
|
@ -2049,7 +2082,10 @@ c_typeset(const char **wp)
|
|||
wp[i], f->val.t);
|
||||
shf_putc('\n', shl_stdout);
|
||||
}
|
||||
} else if (!typeset(wp[i], fset, fclr, field, base)) {
|
||||
} else if (!vtypeset(&x, wp[i], fset, fclr,
|
||||
field, base)) {
|
||||
if (x)
|
||||
return (x);
|
||||
bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
|
||||
return (1);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue