mirror of
https://github.com/openharmony/third_party_mksh.git
synced 2026-07-01 12:24:52 -04:00
升级至R59c版本
Signed-off-by: xuxinyu <xuxinyu6@huawei.com> Change-Id: I5c4757b10786cd1d3b3d79f7c23c5f668daefa2d
This commit is contained in:
@@ -129,7 +129,8 @@ if (defined(ohos_lite)) {
|
||||
"-DHAVE_SYS_ERRLIST_DECL=1",
|
||||
"-DHAVE_SYS_SIGLIST_DECL=1",
|
||||
"-DHAVE_PERSISTENT_HISTORY=1",
|
||||
"-DMKSH_BUILD_R=571",
|
||||
"-DMKSH_UNLIMITED",
|
||||
"-DMKSH_BUILD_R=593",
|
||||
]
|
||||
|
||||
ldflags = [
|
||||
@@ -241,7 +242,8 @@ if (defined(ohos_lite)) {
|
||||
"-DHAVE_SYS_ERRLIST_DECL=1",
|
||||
"-DHAVE_SYS_SIGLIST_DECL=1",
|
||||
"-DHAVE_PERSISTENT_HISTORY=1",
|
||||
"-DMKSH_BUILD_R=571",
|
||||
"-DMKSH_UNLIMITED",
|
||||
"-DMKSH_BUILD_R=593",
|
||||
]
|
||||
ldflags = [
|
||||
"-pie",
|
||||
|
||||
@@ -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.763 2020/09/04 21:01:37 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
|
||||
|
||||
@@ -722,6 +721,13 @@ case $TARGET_OS in
|
||||
add_cppflags -DMKSH_NO_SIGSETJMP
|
||||
add_cppflags -DMKSH_TYPEDEF_SIG_ATOMIC_T=int
|
||||
;;
|
||||
A/UX)
|
||||
add_cppflags -D_POSIX_SOURCE
|
||||
: "${CC=gcc}"
|
||||
: "${LIBS=-lposix}"
|
||||
# GCC defines AUX but cc nothing
|
||||
add_cppflags -D__A_UX__
|
||||
;;
|
||||
AIX)
|
||||
add_cppflags -D_ALL_SOURCE
|
||||
: "${HAVE_SETLOCALE_CTYPE=0}"
|
||||
@@ -799,6 +805,7 @@ OpenHarmony)
|
||||
: "${MKSH_SMALL_BUT_FAST=0}"
|
||||
: "${MKSH_ASSUME_UTF8=1}"
|
||||
: "${MKSH_S_NOVI=1}"
|
||||
: "${MKSH_UNLIMITED=1}"
|
||||
;;
|
||||
Harvey)
|
||||
add_cppflags -D_POSIX_SOURCE
|
||||
@@ -859,6 +866,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)
|
||||
@@ -870,10 +882,9 @@ Minix-vmd)
|
||||
;;
|
||||
Minix3)
|
||||
add_cppflags -DMKSH_UNEMPLOYED
|
||||
add_cppflags -DMKSH_NO_LIMITS
|
||||
add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX
|
||||
oldish_ed=no-stderr-ed # /usr/bin/ed(!) is broken
|
||||
: "${HAVE_SETLOCALE_CTYPE=0}"
|
||||
: "${HAVE_SETLOCALE_CTYPE=0}${MKSH_UNLIMITED=1}" #XXX recheck ulimit
|
||||
;;
|
||||
MirBSD)
|
||||
;;
|
||||
@@ -906,7 +917,7 @@ NEXTSTEP)
|
||||
Ninix3)
|
||||
# similar to Minix3
|
||||
add_cppflags -DMKSH_UNEMPLOYED
|
||||
add_cppflags -DMKSH_NO_LIMITS
|
||||
: "${MKSH_UNLIMITED=1}" #XXX recheck ulimit
|
||||
# but no idea what else could be needed
|
||||
oswarn="; it has unknown issues"
|
||||
;;
|
||||
@@ -919,15 +930,14 @@ OS/2)
|
||||
HAVE_ISOFF_MKSH_ASSUME_UTF8=1
|
||||
HAVE_TERMIOS_H=0
|
||||
HAVE_MKNOD=0 # setmode() incompatible
|
||||
oswarn="; it is being ported"
|
||||
check_categories="$check_categories nosymlink"
|
||||
: "${CC=gcc}"
|
||||
: "${SIZE=: size}"
|
||||
SRCS="$SRCS os2.c"
|
||||
add_cppflags -DMKSH_UNEMPLOYED
|
||||
add_cppflags -DMKSH_NOPROSPECTOFWORK
|
||||
add_cppflags -DMKSH_NO_LIMITS
|
||||
add_cppflags -DMKSH_DOSPATH
|
||||
: "${MKSH_UNLIMITED=1}"
|
||||
if test $textmode = 0; then
|
||||
x='dis'
|
||||
y='standard OS/2 tools'
|
||||
@@ -1049,11 +1059,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
|
||||
|
||||
@@ -1096,11 +1106,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}..."
|
||||
|
||||
@@ -1127,6 +1138,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__)
|
||||
@@ -1242,9 +1257,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)
|
||||
@@ -1262,6 +1276,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
|
||||
@@ -1278,21 +1295,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"
|
||||
;;
|
||||
@@ -1345,7 +1368,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"
|
||||
;;
|
||||
@@ -1358,7 +1381,7 @@ esac
|
||||
etd=" on $et"
|
||||
case $et in
|
||||
klibc)
|
||||
add_cppflags -DMKSH_NO_LIMITS
|
||||
: "${MKSH_UNLIMITED=1}"
|
||||
;;
|
||||
unknown)
|
||||
# nothing special detected, don’t worry
|
||||
@@ -1388,14 +1411,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
|
||||
@@ -1405,9 +1428,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
|
||||
@@ -1490,7 +1512,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
|
||||
@@ -1538,6 +1560,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
|
||||
@@ -1720,7 +1743,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'
|
||||
@@ -1745,7 +1768,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
|
||||
@@ -1757,7 +1780,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
|
||||
@@ -1875,39 +1898,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]); }
|
||||
EOF
|
||||
|
||||
ac_test rlim_t <<-'EOF'
|
||||
#include <sys/types.h>
|
||||
#if HAVE_BOTH_TIME_H
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#elif HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#elif HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#if HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
int main(void) { return (((int)(rlim_t)0) + isatty(0)); }
|
||||
int main(int ac, char *av[]) { return ((u_int8_t)(size_t)av[ac]); }
|
||||
EOF
|
||||
|
||||
# only testn: added later below
|
||||
@@ -1960,7 +1966,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)
|
||||
@@ -2017,15 +2027,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
|
||||
@@ -2058,6 +2068,35 @@ ac_test lock_fcntl '!' flock 1 'whether we can lock files with fcntl' <<-'EOF'
|
||||
}
|
||||
EOF
|
||||
|
||||
ac_test rlimit '' 'getrlimit and setrlimit' <<-'EOF'
|
||||
#define MKSH_INCLUDES_ONLY
|
||||
#include "sh.h"
|
||||
int main(void) {
|
||||
struct rlimit l;
|
||||
if (getrlimit(0, &l)) return 1;
|
||||
l.rlim_max = l.rlim_cur;
|
||||
l.rlim_cur = RLIM_INFINITY;
|
||||
return (setrlimit(0, &l));
|
||||
}
|
||||
EOF
|
||||
|
||||
ac_test rlim_t rlimit 0 <<-'EOF'
|
||||
#include <sys/types.h>
|
||||
#if HAVE_BOTH_TIME_H
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#elif HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#elif HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#if HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
int main(void) { return (((int)(rlim_t)0) + isatty(0)); }
|
||||
EOF
|
||||
|
||||
ac_test getrusage <<-'EOF'
|
||||
#define MKSH_INCLUDES_ONLY
|
||||
#include "sh.h"
|
||||
@@ -2277,6 +2316,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
|
||||
#
|
||||
@@ -2299,7 +2349,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);
|
||||
};
|
||||
@@ -2313,7 +2363,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);
|
||||
};
|
||||
@@ -2437,11 +2487,24 @@ mksh_cfg= cfg_NSIG
|
||||
$e done.
|
||||
fi
|
||||
|
||||
if test 1 = "$MKSH_UNLIMITED"; then
|
||||
add_cppflags -DMKSH_UNLIMITED
|
||||
else
|
||||
MKSH_UNLIMITED=0
|
||||
fi
|
||||
|
||||
if test 1 = "$USE_PRINTF_BUILTIN"; then
|
||||
add_cppflags -DMKSH_PRINTF_BUILTIN
|
||||
else
|
||||
USE_PRINTF_BUILTIN=0
|
||||
fi
|
||||
|
||||
addsrcs '!' HAVE_STRLCPY strlcpy.c
|
||||
addsrcs USE_PRINTF_BUILTIN printf.c
|
||||
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
|
||||
addsrcs '!' MKSH_UNLIMITED ulimit.c
|
||||
|
||||
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
|
||||
add_cppflags -DMKSH_BUILD_R=571
|
||||
add_cppflags -DMKSH_BUILD_R=593
|
||||
|
||||
$e $bi$me: Finished configuration testing, now producing output.$ao
|
||||
|
||||
@@ -2464,7 +2527,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'
|
||||
@@ -2521,7 +2587,7 @@ cat >test.sh <<-EOF
|
||||
args[\${#args[*]}]=\$TMPDIR
|
||||
fi
|
||||
print Testing mksh for conformance:
|
||||
grep -F -e Mir''OS: -e MIRBSD "\$sflag"
|
||||
grep -F -e 'KSH R' -e Mir''OS: "\$sflag" | sed '/KSH/s/^./& /'
|
||||
print "This shell is actually:\\n\\t\$KSH_VERSION"
|
||||
print 'test.sh built for mksh $dstversion'
|
||||
cstr='\$os = defined \$^O ? \$^O : "unknown";'
|
||||
@@ -2581,7 +2647,6 @@ for file in $SRCS; do
|
||||
op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
|
||||
test -f $file || file=$srcdir/$file
|
||||
files="$files$sp$file"
|
||||
sp=' '
|
||||
echo "$CC $CFLAGS $CPPFLAGS $emitbc $file || exit 1" >>Rebuild.sh
|
||||
if test $cm = dragonegg; then
|
||||
echo "mv ${op}s ${op}ll" >>Rebuild.sh
|
||||
@@ -2590,6 +2655,7 @@ for file in $SRCS; do
|
||||
else
|
||||
objs="$objs$sp${op}o"
|
||||
fi
|
||||
sp=' '
|
||||
done
|
||||
case $cm in
|
||||
dragonegg|llvm)
|
||||
@@ -2605,7 +2671,7 @@ echo tcfn=$mkshexe >>Rebuild.sh
|
||||
echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
|
||||
echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh
|
||||
if test $cm = makefile; then
|
||||
extras='emacsfn.h exprtok.h rlimits.opt sh.h sh_flags.opt var_spec.h'
|
||||
extras='emacsfn.h exprtok.h rlimits.opt sh.h sh_flags.opt ulimits.opt var_spec.h'
|
||||
test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
|
||||
gens= genq=
|
||||
for file in $optfiles; do
|
||||
@@ -2626,8 +2692,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
|
||||
|
||||
@@ -2697,6 +2763,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
|
||||
@@ -2710,7 +2777,13 @@ if test $legacy = 0; then
|
||||
fi
|
||||
$e
|
||||
$e Installing the manual:
|
||||
if test -f 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 -f 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" \
|
||||
@@ -2721,6 +2794,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 -f FAQ.htm || \
|
||||
$e Run FAQ2HTML.sh and place FAQ.htm into a suitable location as well.
|
||||
exit 0
|
||||
|
||||
: <<'EOD'
|
||||
@@ -2743,6 +2818,7 @@ TARGET_OS default: $(uname -s || uname)
|
||||
TARGET_OSREV [QNX] default: $(uname -r)
|
||||
|
||||
==== feature selectors ====
|
||||
MKSH_UNLIMITED 1 to omit ulimit builtin completely
|
||||
USE_PRINTF_BUILTIN 1 to include (unsupported) printf(1) as builtin
|
||||
===== general format =====
|
||||
HAVE_STRLEN ac_test
|
||||
@@ -2750,8 +2826,9 @@ HAVE_STRING_H ac_header
|
||||
HAVE_CAN_FSTACKPROTECTORALL ac_flags
|
||||
|
||||
==== cpp definitions ====
|
||||
DEBUG dont use in production, wants gcc, implies:
|
||||
DEBUG don’t 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)
|
||||
@@ -2771,7 +2848,6 @@ MKSH_NOPROSPECTOFWORK disable jobs, co-processes, etc. (do not use)
|
||||
MKSH_NOPWNAM skip PAM calls, for -static on glibc or Solaris
|
||||
MKSH_NO_CMDLINE_EDITING disable command line editing code entirely
|
||||
MKSH_NO_DEPRECATED_WARNING omit warning when deprecated stuff is run
|
||||
MKSH_NO_LIMITS omit ulimit code
|
||||
MKSH_NO_SIGSETJMP define if sigsetjmp is broken or not available
|
||||
MKSH_NO_SIGSUSPEND use sigprocmask+pause instead of sigsuspend
|
||||
MKSH_SMALL omit some code, optimise hard for size (slower)
|
||||
@@ -2780,6 +2856,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 ===
|
||||
|
||||
@@ -2789,15 +2866,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
|
||||
|
||||
@@ -26,3 +26,7 @@ Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
2018
|
||||
2011, 2012, 2013, 2015, 2016, 2017
|
||||
Copyright (c) 2009, 2011, 2012, 2016, 2018
|
||||
Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
|
||||
2019, 2020
|
||||
mirabilos <m@mirbsd.org>
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#!/bin/sh
|
||||
rcsid='$MirOS: src/bin/mksh/FAQ2HTML.sh,v 1.2 2020/10/31 04:17:36 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
-1
@@ -3,7 +3,7 @@
|
||||
"Name" : "mksh",
|
||||
"License" : "MirOS License",
|
||||
"License File" : "COPYRIGHT",
|
||||
"Version Number" : "R57",
|
||||
"Version Number" : "R59c",
|
||||
"Owner" : "maguangyao@huawei.com",
|
||||
"Upstream URL" : "https://www.mirbsd.org/mksh.htm",
|
||||
"Description" : "a secure operating system from the BSD family for 32-bit i386 and sparc systems"
|
||||
|
||||
@@ -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.51 2020/06/22 17:10:59 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,
|
||||
@@ -55,7 +55,7 @@
|
||||
# default is no arguments.
|
||||
# script m Value is written to a file which
|
||||
# is passed as an argument to the program
|
||||
# (after the arguments arguments)
|
||||
# (after the arguments from arguments)
|
||||
# stdin m Value is written to a file which is
|
||||
# used as standard-input for the program;
|
||||
# default is to use /dev/null.
|
||||
@@ -195,7 +195,7 @@ Usage: $prog [-Pv] [-C cat] [-e e=v] [-p prog] [-s fn] [-T dir] \
|
||||
the path (kludge option)
|
||||
-p p Use p as the program to test
|
||||
-s s Read tests from file s; if s is a directory, it is recursively
|
||||
scaned for test files (which end in .t).
|
||||
scanned for test files (which end in .t).
|
||||
-T dir Use dir instead of /tmp to hold temporary files
|
||||
-t t Use t as default time limit for tests (default is unlimited)
|
||||
-U lcl Use lcl as UTF-8 locale (e.g. C.UTF-8) instead of the default
|
||||
@@ -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';
|
||||
|
||||
@@ -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.853 2020/10/31 03:53:03 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/10/31
|
||||
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
|
||||
@@ -6096,7 +6109,7 @@ expected-stderr-pattern: /does\/not\/exist/
|
||||
---
|
||||
name: regression-28
|
||||
description:
|
||||
variable assignements not detected well
|
||||
variable assignments not detected well
|
||||
stdin:
|
||||
a.x=1 echo hi
|
||||
expected-exit: e != 0
|
||||
@@ -6171,7 +6184,7 @@ expected-stdout:
|
||||
---
|
||||
name: regression-35
|
||||
description:
|
||||
Tempory files used for here-docs in functions get trashed after
|
||||
Temporay files used for here-docs in functions get trashed after
|
||||
the function is parsed (before it is executed)
|
||||
stdin:
|
||||
f1() {
|
||||
@@ -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
|
||||
@@ -7419,7 +7457,7 @@ expected-stdout:
|
||||
name: xxx-param-subst-qmark-1
|
||||
description:
|
||||
Check suppresion of error message with null string. According to
|
||||
POSIX, it shouldn't print the error as 'word' isn't ommitted.
|
||||
POSIX, it shouldn't print the error as 'word' isn't omitted.
|
||||
ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error.
|
||||
stdin:
|
||||
unset foo
|
||||
@@ -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,55 @@ expected-stdout:
|
||||
nein
|
||||
expected-stderr-pattern: !/unexpected op/
|
||||
---
|
||||
name: test-str-pattern
|
||||
description:
|
||||
Check that [[ x = $y ]] can take extglobs, like ksh93
|
||||
stdin:
|
||||
[[ -n $BASH_VERSION ]] && shopt -s extglob
|
||||
function one {
|
||||
n=$1 x=$2 y=$3 z=${4:-$3}
|
||||
[[ $x = $y ]]; a=$?
|
||||
[[ $x = "$y" ]]; b=$?
|
||||
eval '[[ $x = '"$z"' ]]; c=$?'
|
||||
eval '[[ $x = "'"$z"'" ]]; d=$?'
|
||||
echo $n $a $b $c $d .
|
||||
}
|
||||
x='a\'
|
||||
[[ $x = a\ ]]; echo 01 $? .
|
||||
[[ $x = a\\ ]]; echo 02 $? .
|
||||
one 03 'a\' 'a\' 'a\\'
|
||||
one 04 'a\b' 'a\b'
|
||||
one 05 'a\b' 'a\\b'
|
||||
one 06 'foo' 'f+(o)'
|
||||
one 07 'f+(o)' 'f+(o)'
|
||||
one 08 'f+(o' 'f+(o' 'f+\(o'
|
||||
one 09 foo 'f+(o' 'f+\(o'
|
||||
one 10 abcde 'a\*e'
|
||||
one 11 'a*e' 'a\*e'
|
||||
one 12 'a\*e' 'a\*e'
|
||||
echo extras:
|
||||
x='f+(o'
|
||||
z='f+(o'
|
||||
eval '[[ $x = "'"$z"'" ]]; echo 14 $? "(08:4)" .'
|
||||
x=foo
|
||||
eval '[[ $x = "'"$z"'" ]]; echo 15 $? "(09:4)" .'
|
||||
expected-stdout:
|
||||
01 1 .
|
||||
02 0 .
|
||||
03 0 0 0 0 .
|
||||
04 1 0 1 0 .
|
||||
05 0 1 0 0 .
|
||||
06 0 1 0 1 .
|
||||
07 1 0 1 0 .
|
||||
08 0 0 0 1 .
|
||||
09 1 1 1 1 .
|
||||
10 1 1 1 1 .
|
||||
11 0 1 0 1 .
|
||||
12 1 0 1 0 .
|
||||
extras:
|
||||
14 0 (08:4) .
|
||||
15 1 (09:4) .
|
||||
---
|
||||
name: test-precedence-1
|
||||
description:
|
||||
Check a weird precedence case (and POSIX echo)
|
||||
@@ -8343,8 +8453,10 @@ description:
|
||||
stdin:
|
||||
set -o braceexpand
|
||||
set +o sh
|
||||
[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
|
||||
[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
|
||||
[[ -o sh ]] && echo sh
|
||||
[[ -o !sh ]] && echo nosh
|
||||
[[ -o braceexpand ]] && echo brex
|
||||
[[ -o !braceexpand ]] && echo nobrex
|
||||
echo {a,b,c}
|
||||
set +o braceexpand
|
||||
echo {a,b,c}
|
||||
@@ -8352,12 +8464,17 @@ stdin:
|
||||
echo {a,b,c}
|
||||
set -o sh
|
||||
echo {a,b,c}
|
||||
[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
|
||||
[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
|
||||
[[ -o sh ]] && echo sh
|
||||
[[ -o !sh ]] && echo nosh
|
||||
[[ -o braceexpand ]] && echo brex
|
||||
[[ -o !braceexpand ]] && echo nobrex
|
||||
set -o braceexpand
|
||||
echo {a,b,c}
|
||||
[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
|
||||
[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
|
||||
[[ -o sh ]] && echo sh
|
||||
[[ -o !sh ]] && echo nosh
|
||||
[[ -o braceexpand ]] && echo brex
|
||||
[[ -o !braceexpand ]] && echo nobrex
|
||||
[[ $(exec -a -set "$__progname" -o) = *login+(' ')on* ]]; echo $?
|
||||
expected-stdout:
|
||||
nosh
|
||||
brex
|
||||
@@ -8370,44 +8487,110 @@ expected-stdout:
|
||||
a b c
|
||||
sh
|
||||
brex
|
||||
0
|
||||
---
|
||||
name: sh-mode-2a
|
||||
description:
|
||||
Check that posix or sh mode is *not* automatically turned on
|
||||
category: !binsh
|
||||
stdin:
|
||||
ln -s "$__progname" ksh || cp "$__progname" ksh
|
||||
ln -s "$__progname" sh || cp "$__progname" sh
|
||||
ln -s "$__progname" ./-ksh || cp "$__progname" ./-ksh
|
||||
ln -s "$__progname" ./-sh || cp "$__progname" ./-sh
|
||||
for shell in {,-}{,k}sh; do
|
||||
print -- $shell $(./$shell +l -c \
|
||||
'[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh')
|
||||
for shell in {,-}{,r}{,k,mk}sh {,-}{,R}{,K,MK}SH.EXE; do
|
||||
ln -s "$__progname" ./$shell || cp "$__progname" ./$shell
|
||||
print -- $shell $(./$shell +l -c '
|
||||
[[ -o sh || -o posix ]] && echo sh
|
||||
[[ -o !sh && -o !posix ]] && echo nosh
|
||||
[[ -o restricted ]] && echo lim || echo ok
|
||||
')
|
||||
done
|
||||
expected-stdout:
|
||||
sh nosh
|
||||
ksh nosh
|
||||
-sh nosh
|
||||
-ksh nosh
|
||||
sh nosh ok
|
||||
ksh nosh ok
|
||||
mksh nosh ok
|
||||
rsh nosh lim
|
||||
rksh nosh lim
|
||||
rmksh nosh lim
|
||||
-sh nosh ok
|
||||
-ksh nosh ok
|
||||
-mksh nosh ok
|
||||
-rsh nosh lim
|
||||
-rksh nosh lim
|
||||
-rmksh nosh lim
|
||||
SH.EXE nosh ok
|
||||
KSH.EXE nosh ok
|
||||
MKSH.EXE nosh ok
|
||||
RSH.EXE nosh lim
|
||||
RKSH.EXE nosh lim
|
||||
RMKSH.EXE nosh lim
|
||||
-SH.EXE nosh ok
|
||||
-KSH.EXE nosh ok
|
||||
-MKSH.EXE nosh ok
|
||||
-RSH.EXE nosh lim
|
||||
-RKSH.EXE nosh lim
|
||||
-RMKSH.EXE nosh lim
|
||||
---
|
||||
name: sh-mode-2b
|
||||
description:
|
||||
Check that posix or sh mode *is* automatically turned on
|
||||
category: binsh
|
||||
stdin:
|
||||
ln -s "$__progname" ksh || cp "$__progname" ksh
|
||||
ln -s "$__progname" sh || cp "$__progname" sh
|
||||
ln -s "$__progname" ./-ksh || cp "$__progname" ./-ksh
|
||||
ln -s "$__progname" ./-sh || cp "$__progname" ./-sh
|
||||
for shell in {,-}{,k}sh; do
|
||||
print -- $shell $(./$shell +l -c \
|
||||
'[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh')
|
||||
for shell in {,-}{,r}{,k,mk}sh {,-}{,R}{,K,MK}SH.EXE; do
|
||||
ln -s "$__progname" ./$shell || cp "$__progname" ./$shell
|
||||
print -- $shell $(./$shell +l -c '
|
||||
[[ -o sh || -o posix ]] && echo sh
|
||||
[[ -o !sh && -o !posix ]] && echo nosh
|
||||
[[ -o restricted ]] && echo lim || echo ok
|
||||
')
|
||||
done
|
||||
expected-stdout:
|
||||
sh sh
|
||||
ksh nosh
|
||||
-sh sh
|
||||
-ksh nosh
|
||||
sh sh ok
|
||||
ksh nosh ok
|
||||
mksh nosh ok
|
||||
rsh sh lim
|
||||
rksh nosh lim
|
||||
rmksh nosh lim
|
||||
-sh sh ok
|
||||
-ksh nosh ok
|
||||
-mksh nosh ok
|
||||
-rsh sh lim
|
||||
-rksh nosh lim
|
||||
-rmksh nosh lim
|
||||
SH.EXE sh ok
|
||||
KSH.EXE nosh ok
|
||||
MKSH.EXE nosh ok
|
||||
RSH.EXE sh lim
|
||||
RKSH.EXE nosh lim
|
||||
RMKSH.EXE nosh lim
|
||||
-SH.EXE sh ok
|
||||
-KSH.EXE nosh ok
|
||||
-MKSH.EXE nosh ok
|
||||
-RSH.EXE sh lim
|
||||
-RKSH.EXE nosh lim
|
||||
-RMKSH.EXE nosh lim
|
||||
---
|
||||
name: sh-options
|
||||
description:
|
||||
Check that "set +o" DTRT per POSIX
|
||||
stdin:
|
||||
t() {
|
||||
[[ -o vi ]]; a=$?
|
||||
[[ -o pipefail ]]; b=$?
|
||||
echo $((++i)) $a $b .
|
||||
}
|
||||
set -e
|
||||
set -o vi
|
||||
set +o pipefail
|
||||
set +e
|
||||
t
|
||||
x=$(set +o)
|
||||
set +o vi
|
||||
set -o pipefail
|
||||
t
|
||||
eval "$x"
|
||||
t
|
||||
expected-stdout:
|
||||
1 0 1 .
|
||||
2 1 0 .
|
||||
3 0 1 .
|
||||
---
|
||||
name: pipeline-1
|
||||
description:
|
||||
@@ -8670,9 +8853,10 @@ description:
|
||||
XXX if the OS can already execute them, we lose
|
||||
note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose
|
||||
note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
|
||||
note: A/UX perl5 returns 6400 (exit-code 25), passes #1-3
|
||||
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:aux,!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 +9805,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 +9813,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 +11024,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 +11421,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 +11467,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 +11482,7 @@ stdin:
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
if [ x$FOO != xfoo ]; then
|
||||
if [ x$FOO != xbarbar ]; then
|
||||
exit 1
|
||||
fi
|
||||
---
|
||||
@@ -11270,13 +11490,11 @@ name: fd-cloexec-1
|
||||
description:
|
||||
Verify that file descriptors > 2 are private for Korn shells
|
||||
AT&T ksh93 does this still, which means we must keep it as well
|
||||
XXX fails on some old Perl installations
|
||||
need-pass: no
|
||||
stdin:
|
||||
cat >cld <<-EOF
|
||||
#!$__perlname
|
||||
open(my \$fh, ">&", 9) or die "E: open \$!";
|
||||
syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!";
|
||||
open(FH, ">&9") or die "E: open \$!";
|
||||
syswrite(FH, "Fowl\\n", 5) or die "E: write \$!";
|
||||
EOF
|
||||
chmod +x cld
|
||||
exec 9>&1
|
||||
@@ -11289,13 +11507,11 @@ name: fd-cloexec-2
|
||||
description:
|
||||
Verify that file descriptors > 2 are not private for POSIX shells
|
||||
See Debian Bug #154540, Closes: #499139
|
||||
XXX fails on some old Perl installations
|
||||
need-pass: no
|
||||
stdin:
|
||||
cat >cld <<-EOF
|
||||
#!$__perlname
|
||||
open(my \$fh, ">&", 9) or die "E: open \$!";
|
||||
syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!";
|
||||
open(FH, ">&9") or die "E: open \$!";
|
||||
syswrite(FH, "Fowl\\n", 5) or die "E: write \$!";
|
||||
EOF
|
||||
chmod +x cld
|
||||
test -n "$POSH_VERSION" || set -o posix
|
||||
@@ -11860,10 +12076,10 @@ expected-stdout:
|
||||
EOFN
|
||||
)|tr u x); }
|
||||
function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
|
||||
x=$( ( \cat >|bar <<"EOFN"
|
||||
x=$( ( \cat >|bar <<"EOFN" ) | \tr u x
|
||||
foo
|
||||
EOFN
|
||||
) | \tr u x )
|
||||
)
|
||||
}
|
||||
inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
|
||||
cat 1>bar <<-EOFI
|
||||
@@ -11893,10 +12109,10 @@ expected-stdout:
|
||||
EOFI
|
||||
)|tr u x); }
|
||||
function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
|
||||
x=$( ( \cat >bar <<-EOFI
|
||||
x=$( ( \cat >bar <<-EOFI ) | \tr u x
|
||||
foo
|
||||
EOFI
|
||||
) | \tr u x )
|
||||
)
|
||||
}
|
||||
inline_IORDWR_IODUP() {
|
||||
sh 1<>/dev/console 0<&1 2>&1
|
||||
@@ -12659,7 +12875,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 +13454,7 @@ description:
|
||||
Crashed during March 2011, fixed on vernal equin�x ☺
|
||||
category: os:mirbsd,os:openbsd
|
||||
stdin:
|
||||
export MALLOC_OPTIONS=FGJPRSX
|
||||
export MALLOC_OPTIONS=FGJRSX
|
||||
"$__progname" -c 'x=$(tr z r <<<baz); echo $x'
|
||||
expected-stdout:
|
||||
bar
|
||||
@@ -13362,21 +13578,91 @@ 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
|
||||
words, and command -p[vV] should find aliases, reserved words, and
|
||||
builtins over external commands.
|
||||
stdin:
|
||||
# extra checks prep
|
||||
mkdir mrr
|
||||
:>mrr/miau
|
||||
chmod +x mrr/miau
|
||||
# priorities
|
||||
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]'
|
||||
PATH=mrr
|
||||
case $(command -v miau) {
|
||||
(mrr/miau) echo fail ;;
|
||||
(!(/*|[A-Z]:/*)) echo fail2 ;;
|
||||
($PWD/mrr/miau) echo ok ;;
|
||||
(/*|[A-Z]:/*) echo pwd bad? ;;
|
||||
(*) echo not reached ;;
|
||||
}
|
||||
expected-stdout:
|
||||
if
|
||||
if
|
||||
@@ -13398,6 +13684,13 @@ 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]'=:
|
||||
:
|
||||
ok
|
||||
---
|
||||
name: whence-preserve-tradition
|
||||
description:
|
||||
|
||||
+57
-11
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -90,6 +90,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)
|
||||
|
||||
@@ -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.231 2020/05/05 21:34:27 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,70 @@ 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;
|
||||
quote |= 2;
|
||||
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 +1089,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 +1214,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 +1235,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 +1403,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 +1426,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 +1498,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 +2100,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)
|
||||
|
||||
@@ -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.224 2020/08/27 19:52:43 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)) {
|
||||
@@ -579,7 +574,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,
|
||||
@@ -610,8 +605,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;
|
||||
}
|
||||
@@ -621,7 +616,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;
|
||||
@@ -631,12 +626,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)
|
||||
@@ -696,11 +687,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 */
|
||||
@@ -806,6 +795,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:
|
||||
@@ -1149,6 +1139,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;
|
||||
}
|
||||
@@ -1221,7 +1215,7 @@ findcom(const char *name, int flags)
|
||||
}
|
||||
|
||||
Search:
|
||||
if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) &&
|
||||
if ((!tp || (tp->type == CTALIAS && !(tp->flag & ISSET))) &&
|
||||
(flags & FC_PATH)) {
|
||||
if (!tp) {
|
||||
if (insert && !(flags & FC_DEFPATH)) {
|
||||
@@ -1311,10 +1305,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
|
||||
|
||||
/*
|
||||
@@ -1359,10 +1353,8 @@ search_path(const char *name, const char *lpath,
|
||||
XcheckN(xs, xp, p - sp);
|
||||
memcpy(xp, sp, p - sp);
|
||||
xp += p - sp;
|
||||
#ifdef __OS2__
|
||||
if (xp > Xstring(xs, xp) && mksh_cdirsep(xp[-1]))
|
||||
if (mksh_cdirsep(xp[-1]))
|
||||
xp--;
|
||||
#endif
|
||||
*xp++ = '/';
|
||||
}
|
||||
sp = p;
|
||||
@@ -1607,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);
|
||||
@@ -1684,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;
|
||||
@@ -1698,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;
|
||||
@@ -1863,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));
|
||||
}
|
||||
|
||||
@@ -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.108 2020/06/20 02:27:50 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 - see /usr/share/doc/legal/LICENCE-BSD - by
|
||||
* MirOS: contrib/code/Snippets/eawparse,v 1.15 2020/06/15 20:31:13 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 },
|
||||
@@ -1099,6 +1136,7 @@ static const struct mb_ucsrange mb_ucs_combining[] = {
|
||||
{ 0xABE5, 0xABE5 },
|
||||
{ 0xABE8, 0xABE8 },
|
||||
{ 0xABED, 0xABED },
|
||||
{ 0xD7B0, 0xD7FF },
|
||||
{ 0xFB1E, 0xFB1E },
|
||||
{ 0xFE00, 0xFE0F },
|
||||
{ 0xFE20, 0xFE2F },
|
||||
|
||||
@@ -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 = */
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* $OpenBSD: c_ksh.c,v 1.37 2015/09/10 22:48:58 nicm Exp $ */
|
||||
/* $OpenBSD: c_sh.c,v 1.46 2015/07/20 20:46:24 guenther Exp $ */
|
||||
/* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */
|
||||
/* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2010, 2011, 2012, 2013, 2014, 2015, 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 +38,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.379 2020/08/27 19:52:44 tg Exp $");
|
||||
|
||||
#if HAVE_KILLPG
|
||||
/*
|
||||
@@ -51,12 +51,7 @@ __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.355 2018/10/20 21:04:28 tg Exp $");
|
||||
#define mksh_kill kill
|
||||
#endif
|
||||
|
||||
/* XXX conditions correct? */
|
||||
#if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
|
||||
#define MKSH_NO_LIMITS 1
|
||||
#endif
|
||||
|
||||
#ifdef MKSH_NO_LIMITS
|
||||
#ifdef MKSH_UNLIMITED
|
||||
#define c_ulimit c_true
|
||||
#endif
|
||||
|
||||
@@ -118,8 +113,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},
|
||||
@@ -130,8 +123,8 @@ const struct builtin mkshbuiltins[] = {
|
||||
{"!realpath", c_realpath},
|
||||
{"~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},
|
||||
@@ -194,11 +187,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 },
|
||||
@@ -210,22 +200,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 },
|
||||
@@ -344,11 +335,11 @@ c_print(const char **wp)
|
||||
#ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
|
||||
Flag(FSH) ||
|
||||
#endif
|
||||
Flag(FAS_BUILTIN)) {
|
||||
as_builtin) {
|
||||
/* 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;
|
||||
}
|
||||
@@ -727,7 +718,23 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
|
||||
"exported " : "",
|
||||
Talias);
|
||||
}
|
||||
shf_puts(tp->val.s, shl_stdout);
|
||||
if (!mksh_abspath(tp->val.s)) {
|
||||
const char *xcwd = current_wd[0] ?
|
||||
current_wd : ".";
|
||||
size_t xlen = strlen(xcwd);
|
||||
size_t clen = strlen(tp->val.s) + 1;
|
||||
char *xp = alloc(xlen + 1 + clen, ATEMP);
|
||||
|
||||
memcpy(xp, xcwd, xlen);
|
||||
if (mksh_cdirsep(xp[xlen - 1]))
|
||||
--xlen;
|
||||
xp[xlen++] = '/';
|
||||
memcpy(xp + xlen, tp->val.s, clen);
|
||||
simplify_path(xp);
|
||||
shf_puts(xp, shl_stdout);
|
||||
afree(xp, ATEMP);
|
||||
} else
|
||||
shf_puts(tp->val.s, shl_stdout);
|
||||
} else {
|
||||
if (vflag)
|
||||
shprintf(Tnot_found_s, id);
|
||||
@@ -735,12 +742,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:
|
||||
@@ -762,10 +773,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;
|
||||
@@ -854,7 +870,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);
|
||||
@@ -884,7 +900,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);
|
||||
@@ -1308,54 +1324,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);
|
||||
}
|
||||
@@ -1364,10 +1358,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);
|
||||
@@ -1386,6 +1387,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);
|
||||
@@ -1600,7 +1602,6 @@ c_wait(const char **wp)
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static const char REPLY[] = "REPLY";
|
||||
int
|
||||
c_read(const char **wp)
|
||||
{
|
||||
@@ -1681,7 +1682,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;
|
||||
@@ -1690,7 +1691,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);
|
||||
@@ -2040,7 +2041,6 @@ int
|
||||
c_eval(const char **wp)
|
||||
{
|
||||
struct source *s, *saves = source;
|
||||
unsigned char savef;
|
||||
int rv;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
@@ -2083,10 +2083,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)
|
||||
@@ -2248,7 +2245,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 };
|
||||
@@ -2259,6 +2262,8 @@ c_set(const char **wp)
|
||||
return (2);
|
||||
/* set $# and $* */
|
||||
if (setargs) {
|
||||
const char **owp;
|
||||
|
||||
wp += argi - 1;
|
||||
owp = wp;
|
||||
/* save $0 */
|
||||
@@ -2590,25 +2595,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
|
||||
@@ -2760,13 +2765,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)
|
||||
@@ -2860,31 +2888,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:
|
||||
@@ -2903,7 +2931,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);
|
||||
}
|
||||
@@ -2913,18 +2941,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);
|
||||
@@ -2932,7 +2960,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:
|
||||
@@ -2945,11 +2974,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
|
||||
@@ -2987,9 +3018,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:
|
||||
@@ -2997,13 +3028,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 */
|
||||
@@ -3166,7 +3198,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;
|
||||
|
||||
@@ -3208,214 +3240,6 @@ ptest_error(Test_env *te, int ofs, const char *msg)
|
||||
bi_errorf(Tf_s, msg);
|
||||
}
|
||||
|
||||
#ifndef MKSH_NO_LIMITS
|
||||
#define SOFT 0x1
|
||||
#define HARD 0x2
|
||||
|
||||
/* Magic to divine the 'm' and 'v' limits */
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
|
||||
!defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
|
||||
#define ULIMIT_V_IS_AS
|
||||
#elif defined(RLIMIT_VMEM)
|
||||
#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
|
||||
#define ULIMIT_V_IS_AS
|
||||
#else
|
||||
#define ULIMIT_V_IS_VMEM
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_RSS
|
||||
#ifdef ULIMIT_V_IS_VMEM
|
||||
#define ULIMIT_M_IS_RSS
|
||||
#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
|
||||
#define ULIMIT_M_IS_VMEM
|
||||
#else
|
||||
#define ULIMIT_M_IS_RSS
|
||||
#endif
|
||||
#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
|
||||
#undef ULIMIT_M_IS_RSS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
|
||||
#define ULIMIT_V_IS_VMEM
|
||||
#endif
|
||||
|
||||
#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
|
||||
(!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
|
||||
#define ULIMIT_M_IS_VMEM
|
||||
#endif
|
||||
|
||||
#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
|
||||
(RLIMIT_VMEM == RLIMIT_AS)
|
||||
#undef ULIMIT_M_IS_VMEM
|
||||
#endif
|
||||
|
||||
#if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM)
|
||||
# error nonsensical m ulimit
|
||||
#endif
|
||||
|
||||
#if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS)
|
||||
# error nonsensical v ulimit
|
||||
#endif
|
||||
|
||||
struct limits {
|
||||
/* limit resource */
|
||||
int resource;
|
||||
/* multiply by to get rlim_{cur,max} values */
|
||||
unsigned int factor;
|
||||
/* getopts char */
|
||||
char optchar;
|
||||
/* limit name */
|
||||
char name[1];
|
||||
};
|
||||
|
||||
#define RLIMITS_DEFNS
|
||||
#define FN(lname,lid,lfac,lopt) \
|
||||
static const struct { \
|
||||
int resource; \
|
||||
unsigned int factor; \
|
||||
char optchar; \
|
||||
char name[sizeof(lname)]; \
|
||||
} rlimits_ ## lid = { \
|
||||
lid, lfac, lopt, lname \
|
||||
};
|
||||
#include "rlimits.gen"
|
||||
|
||||
static void print_ulimit(const struct limits *, int);
|
||||
static int set_ulimit(const struct limits *, const char *, int);
|
||||
|
||||
static const struct limits * const rlimits[] = {
|
||||
#define RLIMITS_ITEMS
|
||||
#include "rlimits.gen"
|
||||
};
|
||||
|
||||
static const char rlimits_opts[] =
|
||||
#define RLIMITS_OPTCS
|
||||
#include "rlimits.gen"
|
||||
;
|
||||
|
||||
int
|
||||
c_ulimit(const char **wp)
|
||||
{
|
||||
size_t i = 0;
|
||||
int how = SOFT | HARD, optc, what = 'f';
|
||||
bool all = false;
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1)
|
||||
switch (optc) {
|
||||
case 'H':
|
||||
how = HARD;
|
||||
break;
|
||||
case 'S':
|
||||
how = SOFT;
|
||||
break;
|
||||
case 'a':
|
||||
all = true;
|
||||
break;
|
||||
case '?':
|
||||
bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts);
|
||||
return (1);
|
||||
default:
|
||||
what = optc;
|
||||
}
|
||||
|
||||
while (i < NELEM(rlimits)) {
|
||||
if (rlimits[i]->optchar == what)
|
||||
goto found;
|
||||
++i;
|
||||
}
|
||||
internal_warningf("ulimit: %c", what);
|
||||
return (1);
|
||||
found:
|
||||
if (wp[builtin_opt.optind]) {
|
||||
if (all || wp[builtin_opt.optind + 1]) {
|
||||
bi_errorf(Ttoo_many_args);
|
||||
return (1);
|
||||
}
|
||||
return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
|
||||
}
|
||||
if (!all)
|
||||
print_ulimit(rlimits[i], how);
|
||||
else for (i = 0; i < NELEM(rlimits); ++i) {
|
||||
shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name);
|
||||
print_ulimit(rlimits[i], how);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
set_ulimit(const struct limits *l, const char *v, int how)
|
||||
{
|
||||
rlim_t val = (rlim_t)0;
|
||||
struct rlimit limit;
|
||||
|
||||
if (strcmp(v, "unlimited") == 0)
|
||||
val = (rlim_t)RLIM_INFINITY;
|
||||
else {
|
||||
mksh_uari_t rval;
|
||||
|
||||
if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
|
||||
return (1);
|
||||
/*
|
||||
* Avoid problems caused by typos that evaluate misses due
|
||||
* to evaluating unset parameters to 0...
|
||||
* If this causes problems, will have to add parameter to
|
||||
* evaluate() to control if unset params are 0 or an error.
|
||||
*/
|
||||
if (!rval && !ctype(v[0], C_DIGIT)) {
|
||||
bi_errorf("invalid %s limit: %s", l->name, v);
|
||||
return (1);
|
||||
}
|
||||
val = (rlim_t)((rlim_t)rval * l->factor);
|
||||
}
|
||||
|
||||
if (getrlimit(l->resource, &limit) < 0) {
|
||||
#ifndef MKSH_SMALL
|
||||
bi_errorf("limit %s could not be read, contact the mksh developers: %s",
|
||||
l->name, cstrerror(errno));
|
||||
#endif
|
||||
/* some can't be read */
|
||||
limit.rlim_cur = RLIM_INFINITY;
|
||||
limit.rlim_max = RLIM_INFINITY;
|
||||
}
|
||||
if (how & SOFT)
|
||||
limit.rlim_cur = val;
|
||||
if (how & HARD)
|
||||
limit.rlim_max = val;
|
||||
if (!setrlimit(l->resource, &limit))
|
||||
return (0);
|
||||
if (errno == EPERM)
|
||||
bi_errorf("%s exceeds allowable %s limit", v, l->name);
|
||||
else
|
||||
bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
print_ulimit(const struct limits *l, int how)
|
||||
{
|
||||
rlim_t val = (rlim_t)0;
|
||||
struct rlimit limit;
|
||||
|
||||
if (getrlimit(l->resource, &limit)) {
|
||||
shf_puts("unknown\n", shl_stdout);
|
||||
return;
|
||||
}
|
||||
if (how & SOFT)
|
||||
val = limit.rlim_cur;
|
||||
else if (how & HARD)
|
||||
val = limit.rlim_max;
|
||||
if (val == (rlim_t)RLIM_INFINITY)
|
||||
shf_puts("unlimited\n", shl_stdout);
|
||||
else
|
||||
shprintf("%lu\n", (unsigned long)(val / l->factor));
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
c_rename(const char **wp)
|
||||
{
|
||||
@@ -3472,7 +3296,7 @@ c_realpath(const char **wp)
|
||||
int
|
||||
c_cat(const char **wp)
|
||||
{
|
||||
int fd = STDIN_FILENO, rv;
|
||||
int fd = 0, rv;
|
||||
ssize_t n, w;
|
||||
const char *fn = "<stdin>";
|
||||
char *buf, *cp;
|
||||
@@ -3480,7 +3304,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 */
|
||||
@@ -3505,7 +3329,7 @@ c_cat(const char **wp)
|
||||
if (*wp) {
|
||||
fn = *wp++;
|
||||
if (ksh_isdash(fn))
|
||||
fd = STDIN_FILENO;
|
||||
fd = 0;
|
||||
else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
|
||||
bi_errorf(Tf_sD_s, fn, cstrerror(errno));
|
||||
rv = 1;
|
||||
@@ -3524,7 +3348,7 @@ c_cat(const char **wp)
|
||||
opipe = block_pipe();
|
||||
continue;
|
||||
}
|
||||
/* an error occured during reading */
|
||||
/* an error occurred during reading */
|
||||
bi_errorf(Tf_sD_s, fn, cstrerror(errno));
|
||||
rv = 1;
|
||||
break;
|
||||
@@ -3534,7 +3358,7 @@ c_cat(const char **wp)
|
||||
while (n) {
|
||||
if (intrsig)
|
||||
goto has_intrsig;
|
||||
if ((w = write(STDOUT_FILENO, cp, n)) != -1) {
|
||||
if ((w = write(1, cp, n)) != -1) {
|
||||
n -= w;
|
||||
cp += w;
|
||||
continue;
|
||||
@@ -3553,17 +3377,17 @@ c_cat(const char **wp)
|
||||
/* fake receiving signal */
|
||||
rv = ksh_sigmask(SIGPIPE);
|
||||
} else {
|
||||
/* an error occured during writing */
|
||||
/* an error occurred during writing */
|
||||
bi_errorf(Tf_sD_s, "<stdout>",
|
||||
cstrerror(errno));
|
||||
rv = 1;
|
||||
}
|
||||
if (fd != STDIN_FILENO)
|
||||
if (fd != 0)
|
||||
close(fd);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (fd != STDIN_FILENO)
|
||||
if (fd != 0)
|
||||
close(fd);
|
||||
} while (*wp);
|
||||
|
||||
|
||||
@@ -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.170 2020/10/01 22:53:20 tg Exp $");
|
||||
|
||||
Trap sigtraps[ksh_NSIG + 1];
|
||||
static struct sigaction Sigact_ign;
|
||||
@@ -415,7 +415,8 @@ hist_get(const char *str, bool approx, bool allow_cur)
|
||||
bool anchored = *str == '?' ? (++str, false) : true;
|
||||
|
||||
/* the -1 is to avoid the current fc command */
|
||||
if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
|
||||
if ((n = findhist(histptr - history - 1, str, false,
|
||||
anchored)) < 0)
|
||||
bi_errorf(Tf_sD_s, str, Tnot_in_history);
|
||||
else
|
||||
hp = &history[n];
|
||||
@@ -479,7 +480,7 @@ histnum(int n)
|
||||
* direction.
|
||||
*/
|
||||
int
|
||||
findhist(int start, int fwd, const char *str, bool anchored)
|
||||
findhist(int start, const char *str, bool fwd, bool anchored)
|
||||
{
|
||||
char **hp;
|
||||
int maxhist = histptr - history;
|
||||
@@ -504,6 +505,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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -1786,16 +1787,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 != ' ') {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.\" $MirOS: src/bin/mksh/lksh.1,v 1.25 2018/12/25 19:38:08 tg Exp $
|
||||
.\" $MirOS: src/bin/mksh/lksh.1,v 1.26 2020/09/04 22:37:01 tg Exp $
|
||||
.\"-
|
||||
.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017, 2018
|
||||
.\" mirabilos <m@mirbsd.org>
|
||||
@@ -25,7 +25,8 @@
|
||||
.\" thus use - for hyphens and \- for minus signs and option dashes
|
||||
.\" * ~ is size-reduced and placed atop in groff, so use \*(TI
|
||||
.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
|
||||
.\" * \(en does not work in nroff, so use \*(en
|
||||
.\" * \(en does not work in nroff, so use \*(en for a solo en dash
|
||||
.\" * and \*(EM for a correctly spaced em dash
|
||||
.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
|
||||
.\" Also make sure to use \& *before* a punctuation char that is to not
|
||||
.\" be interpreted as punctuation, and especially with two-letter words
|
||||
@@ -59,6 +60,12 @@
|
||||
. ds ha ^
|
||||
. ds en \(em
|
||||
.\}
|
||||
.ie n \{\
|
||||
. ds EM \ \*(en\ \&
|
||||
.\}
|
||||
.el \{\
|
||||
. ds EM \f(TR\^\(em\^\fP
|
||||
.\}
|
||||
.\"
|
||||
.\" Implement .Dd with the Mdocdate RCS keyword
|
||||
.\"
|
||||
@@ -74,7 +81,7 @@
|
||||
.\" with -mandoc, it might implement .Mx itself, but we want to
|
||||
.\" use our own definition. And .Dd must come *first*, always.
|
||||
.\"
|
||||
.Dd $Mdocdate: December 25 2018 $
|
||||
.Dd $Mdocdate: September 4 2020 $
|
||||
.\"
|
||||
.\" Check which macro package we use, and do other -mdoc setup.
|
||||
.\"
|
||||
|
||||
@@ -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.374 2020/10/01 20:28:54 tg Exp $");
|
||||
|
||||
#ifndef MKSHRC_PATH
|
||||
#ifndef ADAPT_FOR_LITEOS_A
|
||||
@@ -70,8 +70,8 @@ static const char initsubs[] =
|
||||
|
||||
#ifndef MKSH_NO_INITCOMS
|
||||
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",
|
||||
@@ -96,7 +96,7 @@ static const char *initcoms[] = {
|
||||
#endif // MKSH_NO_INITCOMS
|
||||
|
||||
static const char *restr_com[] = {
|
||||
Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
|
||||
Ttypeset, Tdr, TPATH, TENV, TSHELL, NULL
|
||||
};
|
||||
|
||||
static bool initio_done;
|
||||
@@ -106,7 +106,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) &&
|
||||
@@ -233,7 +232,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;
|
||||
#ifndef MKSH_NO_INITCOMS
|
||||
const char *ccp, **wp;
|
||||
@@ -253,6 +252,9 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
|
||||
#ifdef __OS2__
|
||||
os2_init(&argc, &argv);
|
||||
#define builtin_name_cmp stricmp
|
||||
#else
|
||||
#define builtin_name_cmp strcmp
|
||||
#endif
|
||||
|
||||
/* do things like getpgrp() et al. */
|
||||
@@ -287,10 +289,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
ccp += argi;
|
||||
begin_parsing_kshname:
|
||||
argi = 0;
|
||||
if (*ccp == '-')
|
||||
++ccp;
|
||||
}
|
||||
}
|
||||
Flag(FLOGIN) = (ord(*ccp) == ORD('-')) || (ord(*kshname) == ORD('-'));
|
||||
if (ord(*ccp) == ORD('-'))
|
||||
++ccp;
|
||||
if (!*ccp)
|
||||
ccp = empty_argv[0];
|
||||
|
||||
@@ -313,22 +316,34 @@ 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,
|
||||
mkshbuiltins[i].func)))
|
||||
Flag(FAS_BUILTIN) = 1;
|
||||
for (i = 0; mkshbuiltins[i].name != NULL; ++i) {
|
||||
const char *builtin_name;
|
||||
|
||||
if (!Flag(FAS_BUILTIN)) {
|
||||
builtin_name = builtin(mkshbuiltins[i].name,
|
||||
mkshbuiltins[i].func);
|
||||
if (!builtin_name_cmp(ccp, builtin_name)) {
|
||||
/* canonicalise argv[0] */
|
||||
ccp = builtin_name;
|
||||
as_builtin = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!as_builtin) {
|
||||
/* check for -T option early */
|
||||
argi = parse_args(argv, OF_FIRSTTIME, NULL);
|
||||
if (argi < 0)
|
||||
return (1);
|
||||
|
||||
/* called as rsh, rmksh, -rsh, RKSH.EXE, etc.? */
|
||||
if (ksh_eq(*ccp, 'R', '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)) {
|
||||
/* are we called as -rsh or /bin/sh or SH.EXE or so? */
|
||||
if (ksh_eq(ccp[0], 'S', 's') &&
|
||||
ksh_eq(ccp[1], 'H', 'h')) {
|
||||
/* either also turns off braceexpand */
|
||||
#ifdef MKSH_BINSHPOSIX
|
||||
/* enable better POSIX conformance */
|
||||
@@ -477,7 +492,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
/* this to note if utf-8 mode is set on command line (see below) */
|
||||
UTFMODE = 2;
|
||||
|
||||
if (!Flag(FAS_BUILTIN)) {
|
||||
if (!as_builtin) {
|
||||
argi = parse_args(argv, OF_CMDLINE, NULL);
|
||||
if (argi < 0)
|
||||
return (1);
|
||||
@@ -487,7 +502,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
utf_flag = UTFMODE;
|
||||
UTFMODE = 0;
|
||||
|
||||
if (Flag(FAS_BUILTIN)) {
|
||||
if (as_builtin) {
|
||||
/* auto-detect from environment variables, always */
|
||||
utf_flag = 3;
|
||||
} else if (Flag(FCOMMAND)) {
|
||||
@@ -552,8 +567,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
}
|
||||
|
||||
/* this bizarreness is mandated by POSIX */
|
||||
if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
|
||||
Flag(FTALKING))
|
||||
if (Flag(FTALKING) && fstat(0, &s_stdin) >= 0 &&
|
||||
S_ISCHR(s_stdin.st_mode))
|
||||
reset_nonblock(0);
|
||||
|
||||
/* initialise job control */
|
||||
@@ -585,7 +600,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
#endif
|
||||
|
||||
l = e->loc;
|
||||
if (Flag(FAS_BUILTIN)) {
|
||||
if (as_builtin) {
|
||||
l->argc = argc;
|
||||
l->argv = argv;
|
||||
l->argv[0] = ccp;
|
||||
@@ -648,11 +663,26 @@ 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;
|
||||
|
||||
/* save flags for "set +o" handling */
|
||||
memcpy(baseline_flags, shell_flags, sizeof(shell_flags));
|
||||
/* disable these because they have special handling */
|
||||
baseline_flags[(int)FPOSIX] = 0;
|
||||
baseline_flags[(int)FSH] = 0;
|
||||
/* ensure these always show up setting, for FPOSIX/FSH */
|
||||
baseline_flags[(int)FBRACEEXPAND] = 0;
|
||||
baseline_flags[(int)FUNNYCODE] = 0;
|
||||
#if !defined(MKSH_SMALL) || defined(DEBUG)
|
||||
/* mark as initialised */
|
||||
baseline_flags[(int)FNFLAGS] = 1;
|
||||
#endif
|
||||
if (as_builtin)
|
||||
goto skip_startup_files;
|
||||
|
||||
/*
|
||||
* Do this before profile/$ENV so that if it causes problems in them,
|
||||
* user will know why things broke.
|
||||
@@ -662,7 +692,18 @@ 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);
|
||||
/* track shell-imposed changes */
|
||||
baseline_flags[(int)FPRIVILEGED] = Flag(FPRIVILEGED);
|
||||
} else {
|
||||
if (Flag(FLOGIN))
|
||||
include(substitute("$HOME/.profile", 0), 0, NULL, true);
|
||||
if (Flag(FTALKING)) {
|
||||
@@ -674,25 +715,25 @@ 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... */
|
||||
Flag(FRESTRICTED) = 1;
|
||||
/* track shell-imposed changes */
|
||||
baseline_flags[(int)FRESTRICTED] = 1;
|
||||
}
|
||||
Flag(FERREXIT) = errexit;
|
||||
|
||||
if (Flag(FTALKING) && s)
|
||||
hist_init(s);
|
||||
else
|
||||
else {
|
||||
/* set after ENV */
|
||||
skip_startup_files:
|
||||
Flag(FTRACKALL) = 1;
|
||||
/* track shell-imposed change (might lower surprise) */
|
||||
baseline_flags[(int)FTRACKALL] = 1;
|
||||
}
|
||||
|
||||
alarm_init();
|
||||
|
||||
@@ -711,7 +752,7 @@ main(int argc, const char *argv[])
|
||||
struct block *l;
|
||||
|
||||
if ((rv = main_init(argc, argv, &s, &l)) == 0) {
|
||||
if (Flag(FAS_BUILTIN)) {
|
||||
if (as_builtin) {
|
||||
rv = c_builtin(l->argv);
|
||||
} else {
|
||||
shell(s, 0);
|
||||
@@ -751,6 +792,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:
|
||||
@@ -818,6 +860,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))) {
|
||||
@@ -825,18 +869,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)
|
||||
@@ -867,6 +914,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 */
|
||||
@@ -926,8 +975,8 @@ shell(Source * volatile s, volatile int level)
|
||||
source_no_tree:
|
||||
reclaim();
|
||||
}
|
||||
quitenv(NULL);
|
||||
source = old_source;
|
||||
quitenv(NULL);
|
||||
return (exstat & 0xFF);
|
||||
}
|
||||
|
||||
@@ -936,36 +985,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) {
|
||||
@@ -1008,8 +1046,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;
|
||||
}
|
||||
|
||||
@@ -1176,7 +1213,7 @@ remove_temps(struct temp *tp)
|
||||
|
||||
/*
|
||||
* Initialise tty_fd. Used for tracking the size of the terminal,
|
||||
* saving/resetting tty modes upon forground job completion, and
|
||||
* saving/resetting tty modes upon foreground job completion, and
|
||||
* for setting up the tty process group. Return values:
|
||||
* 0 = got controlling tty
|
||||
* 1 = got terminal but no controlling tty
|
||||
@@ -1346,6 +1383,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, ...)
|
||||
@@ -1375,7 +1445,8 @@ error_prefix(bool fileline)
|
||||
/* Avoid foo: foo[2]: ... */
|
||||
if (!fileline || !source || !source->file ||
|
||||
strcmp(source->file, kshname) != 0)
|
||||
shf_fprintf(shl_out, Tf_sD_, kshname + (*kshname == '-'));
|
||||
shf_fprintf(shl_out, Tf_sD_, kshname +
|
||||
(*kshname == '-' ? 1 : 0));
|
||||
if (fileline && source && source->file != NULL) {
|
||||
shf_fprintf(shl_out, "%s[%lu]: ", source->file,
|
||||
(unsigned long)(source->errline ?
|
||||
@@ -1445,7 +1516,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)
|
||||
@@ -2007,7 +2078,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",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
* Copyright (c) 2015
|
||||
* Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
@@ -32,7 +33,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.302 2020/08/27 19:52:45 tg Exp $");
|
||||
|
||||
#define KSH_CHVT_FLAG
|
||||
#ifdef MKSH_SMALL
|
||||
@@ -62,13 +63,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
|
||||
|
||||
|
||||
@@ -141,7 +142,8 @@ struct options_info {
|
||||
};
|
||||
|
||||
static void options_fmt_entry(char *, size_t, unsigned int, const void *);
|
||||
static void printoptions(bool);
|
||||
static int printoptions(bool);
|
||||
static int printoption(size_t);
|
||||
|
||||
/* format a single select menu item */
|
||||
static void
|
||||
@@ -154,10 +156,32 @@ options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
|
||||
Flag(oi->opts[i]) ? "on" : "off");
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
printoption(size_t i)
|
||||
{
|
||||
if (Flag(i) == baseline_flags[i])
|
||||
return (0);
|
||||
if (!OFN(i)[0]) {
|
||||
#if !defined(MKSH_SMALL) || defined(DEBUG)
|
||||
bi_errorf(Tf_sd, "change in unnamed option", (int)i);
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
if (Flag(i) != 0 && Flag(i) != 1) {
|
||||
#if !defined(MKSH_SMALL) || defined(DEBUG)
|
||||
bi_errorf(Tf_s_sD_s, Tdo, OFN(i), "not 0 or 1");
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
shprintf(Tf__s_s, Flag(i) ? Tdo : Tpo, OFN(i));
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
printoptions(bool verbose)
|
||||
{
|
||||
size_t i = 0;
|
||||
int rv = 0;
|
||||
|
||||
if (verbose) {
|
||||
size_t n = 0, len, octs = 0;
|
||||
@@ -187,13 +211,17 @@ printoptions(bool verbose)
|
||||
} else {
|
||||
/* short version like AT&T ksh93 */
|
||||
shf_puts(Tset, shl_stdout);
|
||||
while (i < NELEM(options)) {
|
||||
if (Flag(i) && OFN(i)[0])
|
||||
shprintf(" -o %s", OFN(i));
|
||||
shf_puts(To_o_reset, shl_stdout);
|
||||
printoption(FSH);
|
||||
printoption(FPOSIX);
|
||||
while (i < FNFLAGS) {
|
||||
if (i != FSH && i != FPOSIX)
|
||||
rv |= printoption(i);
|
||||
++i;
|
||||
}
|
||||
shf_putc('\n', shl_stdout);
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
char *
|
||||
@@ -216,70 +244,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +390,8 @@ parse_args(const char **argv,
|
||||
#undef SHFLAGS_NOT_CMD
|
||||
;
|
||||
bool set;
|
||||
const char *opts;
|
||||
const char *opts = what == OF_CMDLINE || what == OF_FIRSTTIME ?
|
||||
cmd_opts : set_opts;
|
||||
const char *array = NULL;
|
||||
Getopt go;
|
||||
size_t i;
|
||||
@@ -355,22 +399,6 @@ parse_args(const char **argv,
|
||||
bool sortargs = false;
|
||||
bool fcompatseen = false;
|
||||
|
||||
if (what == OF_CMDLINE) {
|
||||
const char *p = argv[0], *q;
|
||||
/*
|
||||
* Set FLOGIN before parsing options so user can clear
|
||||
* flag using +l.
|
||||
*/
|
||||
if (*p != '-')
|
||||
for (q = p; *q; )
|
||||
if (mksh_cdirsep(*q++))
|
||||
p = q;
|
||||
Flag(FLOGIN) = (*p == '-');
|
||||
opts = cmd_opts;
|
||||
} else if (what == OF_FIRSTTIME) {
|
||||
opts = cmd_opts;
|
||||
} else
|
||||
opts = set_opts;
|
||||
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
|
||||
while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
|
||||
set = tobool(!(go.info & GI_PLUS));
|
||||
@@ -393,7 +421,15 @@ parse_args(const char **argv,
|
||||
* an option (ie, can't get here if what is
|
||||
* OF_CMDLINE).
|
||||
*/
|
||||
printoptions(set);
|
||||
#if !defined(MKSH_SMALL) || defined(DEBUG)
|
||||
if (!set && !baseline_flags[(int)FNFLAGS]) {
|
||||
bi_errorf(Tf_s_s, "too early",
|
||||
Tset_po);
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
if (printoptions(set))
|
||||
return (-1);
|
||||
break;
|
||||
}
|
||||
i = option(go.optarg);
|
||||
@@ -418,7 +454,23 @@ parse_args(const char **argv,
|
||||
;
|
||||
else if ((i != (size_t)-1) && (OFF(i) & what))
|
||||
change_flag((enum sh_flag)i, what, set);
|
||||
else {
|
||||
else if (!strcmp(go.optarg, To_reset)) {
|
||||
#if !defined(MKSH_SMALL) || defined(DEBUG)
|
||||
if (!baseline_flags[(int)FNFLAGS]) {
|
||||
bi_errorf(Tf_ss, "too early",
|
||||
To_o_reset);
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* ordering, with respect to side effects,
|
||||
* was ensured above by printoptions
|
||||
*/
|
||||
for (i = 0; i < FNFLAGS; ++i)
|
||||
if (Flag(i) != baseline_flags[i])
|
||||
change_flag((enum sh_flag)i,
|
||||
what, baseline_flags[i]);
|
||||
} else {
|
||||
bi_errorf(Tf_sD_s, go.optarg,
|
||||
Tunknown_option);
|
||||
return (-1);
|
||||
@@ -1276,7 +1328,7 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
|
||||
if (go->flags & GF_ERROR)
|
||||
bi_errorfz();
|
||||
}
|
||||
return ('?');
|
||||
return (ORD('?'));
|
||||
}
|
||||
/**
|
||||
* : means argument must be present, may be part of option argument
|
||||
@@ -1295,7 +1347,7 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
|
||||
if (optionsp[0] == ':') {
|
||||
go->buf[0] = c;
|
||||
go->optarg = go->buf;
|
||||
return (':');
|
||||
return (ORD(':'));
|
||||
}
|
||||
warningf(true, Tf_optfoo,
|
||||
(go->flags & GF_NONAME) ? "" : argv[0],
|
||||
@@ -1303,7 +1355,7 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
|
||||
c, Treq_arg);
|
||||
if (go->flags & GF_ERROR)
|
||||
bi_errorfz();
|
||||
return ('?');
|
||||
return (ORD('?'));
|
||||
}
|
||||
go->p = 0;
|
||||
} else if (*o == ',') {
|
||||
@@ -1331,7 +1383,7 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
|
||||
go->optarg = NULL;
|
||||
}
|
||||
}
|
||||
return (c);
|
||||
return (ord(c));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1674,14 +1726,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 +1834,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)) {
|
||||
@@ -1876,7 +1927,7 @@ do_realpath(const char *upath)
|
||||
* - if file starts with '/', append file to result & set cdpathp to NULL
|
||||
* - if file starts with ./ or ../ append cwd and file to result
|
||||
* and set cdpathp to NULL
|
||||
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
|
||||
* - if the first element of cdpathp doesn't start with a '/' xx or '.' xx
|
||||
* then cwd is appended to result.
|
||||
* - the first element of cdpathp is appended to result
|
||||
* - file is appended to result
|
||||
@@ -1932,16 +1983,18 @@ make_path(const char *cwd, const char *file,
|
||||
XcheckN(*xsp, xp, len);
|
||||
memcpy(xp, cwd, len);
|
||||
xp += len;
|
||||
if (!mksh_cdirsep(cwd[len - 1]))
|
||||
Xput(*xsp, xp, '/');
|
||||
if (mksh_cdirsep(xp[-1]))
|
||||
xp--;
|
||||
*xp++ = '/';
|
||||
}
|
||||
*phys_pathp = Xlength(*xsp, xp);
|
||||
if (use_cdpath && plen) {
|
||||
XcheckN(*xsp, xp, plen);
|
||||
memcpy(xp, plist, plen);
|
||||
xp += plen;
|
||||
if (!mksh_cdirsep(plist[plen - 1]))
|
||||
Xput(*xsp, xp, '/');
|
||||
if (mksh_cdirsep(xp[-1]))
|
||||
xp--;
|
||||
*xp++ = '/';
|
||||
rval = 1;
|
||||
}
|
||||
}
|
||||
@@ -2005,9 +2058,14 @@ simplify_path(char *p)
|
||||
case '\\':
|
||||
#endif
|
||||
/* exactly two leading slashes? (SUSv4 3.266) */
|
||||
if (p[1] == p[0] && !mksh_cdirsep(p[2]))
|
||||
if (p[1] == p[0] && !mksh_cdirsep(p[2])) {
|
||||
/* keep them, e.g. for UNC pathnames */
|
||||
#ifdef MKSH_DOSPATH
|
||||
*p++ = '/';
|
||||
#else
|
||||
++p;
|
||||
#endif
|
||||
}
|
||||
needslash = true;
|
||||
break;
|
||||
default:
|
||||
@@ -2138,26 +2196,26 @@ c_cd(const char **wp)
|
||||
oldpwd_s = global(TOLDPWD);
|
||||
|
||||
if (!wp[0]) {
|
||||
/* No arguments - go home */
|
||||
/* no arguments; go home */
|
||||
if ((dir = str_val(global("HOME"))) == null) {
|
||||
bi_errorf("no home directory (HOME not set)");
|
||||
return (2);
|
||||
}
|
||||
} else if (!wp[1]) {
|
||||
/* One argument: - or dir */
|
||||
strdupx(allocd, wp[0], ATEMP);
|
||||
if (ksh_isdash((dir = allocd))) {
|
||||
afree(allocd, ATEMP);
|
||||
allocd = NULL;
|
||||
/* one argument: - or dir */
|
||||
if (ksh_isdash(wp[0])) {
|
||||
dir = str_val(oldpwd_s);
|
||||
if (dir == null) {
|
||||
bi_errorf(Tno_OLDPWD);
|
||||
return (2);
|
||||
}
|
||||
printpath = true;
|
||||
} else {
|
||||
strdupx(allocd, wp[0], ATEMP);
|
||||
dir = allocd;
|
||||
}
|
||||
} else if (!wp[2]) {
|
||||
/* Two arguments - substitute arg1 in PWD for arg2 */
|
||||
/* two arguments; substitute arg1 in PWD for arg2 */
|
||||
size_t ilen, olen, nlen, elen;
|
||||
char *cp;
|
||||
|
||||
@@ -2166,10 +2224,9 @@ c_cd(const char **wp)
|
||||
return (2);
|
||||
}
|
||||
/*
|
||||
* substitute arg1 for arg2 in current path.
|
||||
* if the first substitution fails because the cd fails
|
||||
* we could try to find another substitution. For now
|
||||
* we don't
|
||||
* Substitute arg1 for arg2 in current path. If the first
|
||||
* substitution fails because the cd fails we could try to
|
||||
* find another substitution. For now, we don't.
|
||||
*/
|
||||
if ((cp = strstr(current_wd, wp[0])) == NULL) {
|
||||
bi_errorf(Tbadsubst);
|
||||
@@ -2199,8 +2256,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;
|
||||
|
||||
@@ -0,0 +1,652 @@
|
||||
RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.10 2020/10/01 22:59:12 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 resistant 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-x-e
|
||||
Title: Multiline command editing
|
||||
|
||||
<p>mksh is very independent of the terminal and external libraries and
|
||||
databases, such as termcap, and therefore is conservative in which ANSI
|
||||
control codes are sent to the terminal.</p>
|
||||
<p>For this reason, mksh’s input line editing uses a “windowed one-line”
|
||||
concept: the line the cursor is on is a “window” into the whole input,
|
||||
horizontally scrolled. Some other shells (that are much larger and have
|
||||
more dependencies on external tooling) use a “multi-line” editing mode,
|
||||
and users occasionally wish for this. It is on the long-term TODO, but
|
||||
(due to the aforementioned implications) this is not trivial.</p>
|
||||
<p>One way to achieve multi-line editing is to <em>dis</em>able input
|
||||
line editing: <tt>set +o emacs +o vi</tt><br />This will, however, lose
|
||||
you all editing features: tab completion, cursor keys, history, etc.</p>
|
||||
<p>Another way, if you don’t need it all the time, is to use a function
|
||||
that spawns your editor on the input line: press <tt>^Xe</tt> in the
|
||||
default emacs mode or <tt>Esc + v</tt> in vi mode. Once you exit the
|
||||
editor, whatever was written there is run; this includes the original
|
||||
command line if you quit without saving, so request the editor to exit
|
||||
nōn-zero (e.g. using jupp’s “abendjoe” command) to prevent execution.
|
||||
This is <em>really</em> useful to write ad-hōc scripts as well.</p>
|
||||
----
|
||||
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: trim-vector
|
||||
Title: ${@?}: bad substitution
|
||||
|
||||
<p>In mksh, you cannot assign to or trim a vector (yet). For most
|
||||
cases it is possible to write the affected code in a way avoiding
|
||||
this extension; for example, trimming <tt>${@#foo}</tt> could be
|
||||
applied to <tt>$1</tt> only and <tt>${@?}</tt> can be replaced
|
||||
with a test whether <tt>$# -eq 0</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.
|
||||
----
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 2015, 2017
|
||||
* Copyright (c) 2015, 2017, 2020
|
||||
* KO Myung-Hun <komh@chollian.net>
|
||||
* Copyright (c) 2017
|
||||
* Copyright (c) 2017, 2020
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@@ -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,11 +32,23 @@
|
||||
#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.11 2020/10/01 21:13:45 tg Exp $");
|
||||
|
||||
static char *remove_trailing_dots(char *);
|
||||
static int access_stat_ex(int (*)(), const char *, void *);
|
||||
static int test_exec_exist(const char *, char *);
|
||||
struct a_s_arg {
|
||||
union {
|
||||
int (*i)(const char *, int);
|
||||
int (*p)(const char *, void *);
|
||||
} fn;
|
||||
union {
|
||||
int i;
|
||||
void *p;
|
||||
} arg;
|
||||
bool isint;
|
||||
};
|
||||
|
||||
static void remove_trailing_dots(char *, size_t);
|
||||
static int access_stat_ex(const char *, struct a_s_arg *);
|
||||
static int test_exec_exist(const char *, void *);
|
||||
static void response(int *, const char ***);
|
||||
static char *make_response_file(char * const *);
|
||||
static void add_temp(const char *);
|
||||
@@ -172,6 +185,8 @@ init_extlibpath(void)
|
||||
void
|
||||
os2_init(int *argcp, const char ***argvp)
|
||||
{
|
||||
KBDINFO ki;
|
||||
|
||||
response(argcp, argvp);
|
||||
|
||||
init_extlibpath();
|
||||
@@ -183,6 +198,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);
|
||||
}
|
||||
|
||||
@@ -214,23 +235,18 @@ setextlibpath(const char *name, const char *val)
|
||||
}
|
||||
|
||||
/* remove trailing dots */
|
||||
static char *
|
||||
remove_trailing_dots(char *name)
|
||||
static void
|
||||
remove_trailing_dots(char *name, size_t namelen)
|
||||
{
|
||||
char *p = strnul(name);
|
||||
char *p = name + namelen;
|
||||
|
||||
while (--p > name && *p == '.')
|
||||
/* nothing */;
|
||||
|
||||
if (*p != '.' && *p != '/' && *p != '\\' && *p != ':')
|
||||
p[1] = '\0';
|
||||
|
||||
return (name);
|
||||
}
|
||||
|
||||
#define REMOVE_TRAILING_DOTS(name) \
|
||||
remove_trailing_dots(memcpy(alloca(strlen(name) + 1), name, strlen(name) + 1))
|
||||
|
||||
/* alias of stat() */
|
||||
extern int _std_stat(const char *, struct stat *);
|
||||
|
||||
@@ -238,7 +254,12 @@ extern int _std_stat(const char *, struct stat *);
|
||||
int
|
||||
stat(const char *name, struct stat *buffer)
|
||||
{
|
||||
return (_std_stat(REMOVE_TRAILING_DOTS(name), buffer));
|
||||
size_t namelen = strlen(name) + 1;
|
||||
char nodots[namelen];
|
||||
|
||||
memcpy(nodots, name, namelen);
|
||||
remove_trailing_dots(nodots, namelen);
|
||||
return (_std_stat(nodots, buffer));
|
||||
}
|
||||
|
||||
/* alias of access() */
|
||||
@@ -248,6 +269,9 @@ extern int _std_access(const char *, int);
|
||||
int
|
||||
access(const char *name, int mode)
|
||||
{
|
||||
size_t namelen = strlen(name) + 1;
|
||||
char nodots[namelen];
|
||||
|
||||
/*
|
||||
* On OS/2 kLIBC, X_OK is set only for executable files.
|
||||
* This prevents scripts from being executed.
|
||||
@@ -255,7 +279,9 @@ access(const char *name, int mode)
|
||||
if (mode & X_OK)
|
||||
mode = (mode & ~X_OK) | R_OK;
|
||||
|
||||
return (_std_access(REMOVE_TRAILING_DOTS(name), mode));
|
||||
memcpy(nodots, name, namelen);
|
||||
remove_trailing_dots(nodots, namelen);
|
||||
return (_std_access(nodots, mode));
|
||||
}
|
||||
|
||||
#define MAX_X_SUFFIX_LEN 4
|
||||
@@ -265,7 +291,7 @@ static const char *x_suffix_list[] =
|
||||
|
||||
/* call fn() by appending executable extensions */
|
||||
static int
|
||||
access_stat_ex(int (*fn)(), const char *name, void *arg)
|
||||
access_stat_ex(const char *name, struct a_s_arg *action)
|
||||
{
|
||||
char *x_name;
|
||||
const char **x_suffix;
|
||||
@@ -279,7 +305,8 @@ access_stat_ex(int (*fn)(), const char *name, void *arg)
|
||||
strlcpy(x_name, name, x_namelen);
|
||||
strlcat(x_name, *x_suffix, x_namelen);
|
||||
|
||||
rc = fn(x_name, arg);
|
||||
rc = action->isint ? action->fn.i(x_name, action->arg.i) :
|
||||
action->fn.p(x_name, action->arg.p);
|
||||
}
|
||||
|
||||
afree(x_name, ATEMP);
|
||||
@@ -291,42 +318,52 @@ access_stat_ex(int (*fn)(), const char *name, void *arg)
|
||||
int
|
||||
access_ex(int (*fn)(const char *, int), const char *name, int mode)
|
||||
{
|
||||
/*XXX this smells fishy --mirabilos */
|
||||
return (access_stat_ex(fn, name, (void *)mode));
|
||||
struct a_s_arg arg;
|
||||
|
||||
arg.fn.i = fn;
|
||||
arg.arg.i = mode;
|
||||
arg.isint = true;
|
||||
return (access_stat_ex(name, &arg));
|
||||
}
|
||||
|
||||
/* 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));
|
||||
struct a_s_arg arg;
|
||||
|
||||
arg.fn.p = fn;
|
||||
arg.arg.p = buffer;
|
||||
arg.isint = false;
|
||||
return (access_stat_ex(name, &arg));
|
||||
}
|
||||
|
||||
static int
|
||||
test_exec_exist(const char *name, char *real_name)
|
||||
test_exec_exist(const char *name, void *arg)
|
||||
{
|
||||
struct stat sb;
|
||||
char *real_name;
|
||||
|
||||
if (stat(name, &sb) < 0 || !S_ISREG(sb.st_mode))
|
||||
return (-1);
|
||||
|
||||
/* safe due to calculations in real_exec_name() */
|
||||
memcpy(real_name, name, strlen(name) + 1);
|
||||
|
||||
/*XXX memory leak */
|
||||
strdupx(real_name, name, ATEMP);
|
||||
*((char **)arg) = real_name;
|
||||
return (0);
|
||||
}
|
||||
|
||||
const char *
|
||||
real_exec_name(const char *name)
|
||||
{
|
||||
char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1];
|
||||
const char *real_name = name;
|
||||
struct a_s_arg arg;
|
||||
char *real_name;
|
||||
|
||||
if (access_stat_ex(test_exec_exist, real_name, x_name) != -1)
|
||||
/*XXX memory leak */
|
||||
strdupx(real_name, x_name, ATEMP);
|
||||
|
||||
return (real_name);
|
||||
arg.fn.p = &test_exec_exist;
|
||||
arg.arg.p = (void *)(&real_name);
|
||||
arg.isint = false;
|
||||
return (access_stat_ex(name, &arg) ? name : real_name);
|
||||
}
|
||||
|
||||
/* make a response file to pass a very long command line */
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2013, 2015
|
||||
* Copyright (c) 2013, 2015, 2019
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@@ -16,10 +16,12 @@
|
||||
* 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.
|
||||
*-
|
||||
* Keep {r,u}limits.opt in sync with each other!
|
||||
*/
|
||||
|
||||
@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.5 2020/07/24 20:11:18 tg Exp $");
|
||||
@RLIMITS_ITEMS
|
||||
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
|
||||
@@
|
||||
@@ -86,6 +88,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 +107,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
|
||||
|
||||
@@ -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
|
||||
@@ -77,7 +78,9 @@
|
||||
#if HAVE_PATHS_H
|
||||
#include <paths.h>
|
||||
#endif
|
||||
#ifndef MKSH_NOPWNAM
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
@@ -116,7 +119,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 +173,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 +193,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.904 2020/10/31 03:53:06 tg Exp $");
|
||||
#endif
|
||||
#define MKSH_VERSION "R57 2019/03/01"
|
||||
#define MKSH_VERSION "R59 2020/10/31"
|
||||
|
||||
/* arithmetic types: C implementation */
|
||||
#if !HAVE_CAN_INTTYPES
|
||||
@@ -238,10 +249,6 @@ typedef u_int8_t uint8_t;
|
||||
|
||||
/* other standard types */
|
||||
|
||||
#if !HAVE_RLIM_T
|
||||
typedef unsigned long rlim_t;
|
||||
#endif
|
||||
|
||||
#if !HAVE_SIG_T
|
||||
#undef sig_t
|
||||
typedef void (*sig_t)(int);
|
||||
@@ -255,6 +262,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 +281,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 +327,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 +338,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 +473,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
|
||||
@@ -491,9 +511,8 @@ extern int __cdecl setegid(gid_t);
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#ifndef O_MAYEXEC
|
||||
#undef O_MAYEXEC /* https://lwn.net/Articles/820658/ */
|
||||
#define O_MAYEXEC 0
|
||||
#endif
|
||||
|
||||
#ifdef MKSH__NO_SYMLINK
|
||||
#undef S_ISLNK
|
||||
@@ -647,7 +666,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 != 593)
|
||||
#error Must run Build.sh to compile this.
|
||||
extern void thiswillneverbedefinedIhope(void);
|
||||
int
|
||||
@@ -661,20 +680,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 +704,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 +717,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 +885,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 +898,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 */
|
||||
@@ -866,11 +914,17 @@ EXTERN struct tbl *vp_pipest; /* global PIPESTATUS array */
|
||||
EXTERN short trap_exstat; /* exit status before running a trap */
|
||||
EXTERN uint8_t trap_nested; /* running nested traps */
|
||||
EXTERN uint8_t shell_flags[FNFLAGS];
|
||||
EXTERN uint8_t baseline_flags[FNFLAGS
|
||||
#if !defined(MKSH_SMALL) || defined(DEBUG)
|
||||
+ 1
|
||||
#endif
|
||||
];
|
||||
EXTERN bool as_builtin; /* direct builtin call */
|
||||
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 +1009,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__
|
||||
@@ -986,7 +1041,10 @@ EXTERN const char Tnot_found_s[] E_INIT("%s not found");
|
||||
#define Tnot_started (Tjob_not_started + 4)
|
||||
#define TOLDPWD (Tno_OLDPWD + 3)
|
||||
#define Topen (Tcant_open + 6)
|
||||
EXTERN const char To_o_reset[] E_INIT(" -o .reset");
|
||||
#define To_reset (To_o_reset + 4)
|
||||
#define TPATH (TFPATH + 1)
|
||||
#define Tpo (Tset_po + 4)
|
||||
#define Tpv (TpVv + 1)
|
||||
EXTERN const char TpVv[] E_INIT("Vpv");
|
||||
#define TPWD (Tno_OLDPWD + 6)
|
||||
@@ -997,10 +1055,12 @@ 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 Tset_po[] E_INIT("set +o");
|
||||
EXTERN const char Tsghset[] E_INIT("*=#set");
|
||||
#define Tsh (Tmksh + 2)
|
||||
#define TSHELL (TEXECSHELL + 4)
|
||||
#define Tshell (Ttoo_many_files + 23)
|
||||
@@ -1030,15 +1090,16 @@ EXTERN const char Tuser_sp2[] E_INIT(" user ");
|
||||
#define Twrite (Tshf_write + 4)
|
||||
EXTERN const char Tf__S[] E_INIT(" %S");
|
||||
#define Tf__d (Tunexpected_type + 22)
|
||||
#define Tf_ss (Tf__ss + 1)
|
||||
EXTERN const char Tf__ss[] E_INIT(" %s%s");
|
||||
#define Tf__sN (Tf_s_s_sN + 5)
|
||||
EXTERN const char Tf_sSs[] E_INIT("%s/%s");
|
||||
#define Tf_T (Tf_s_T + 3)
|
||||
EXTERN const char Tf_dN[] E_INIT("%d\n");
|
||||
EXTERN const char Tf_s_[] E_INIT("%s ");
|
||||
EXTERN const char Tf_s_T[] E_INIT("%s %T");
|
||||
EXTERN const char Tf_s_s_sN[] E_INIT("%s %s %s\n");
|
||||
#define Tf_s_s (Tf_sD_s_s + 4)
|
||||
#define Tf__s_s (Tf_sD_s_s + 3)
|
||||
#define Tf_s_sD_s (Tf_cant_ss_s + 6)
|
||||
EXTERN const char Tf_optfoo[] E_INIT("%s%s-%c: %s");
|
||||
EXTERN const char Tf_sD_[] E_INIT("%s: ");
|
||||
@@ -1056,8 +1117,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 +1175,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__
|
||||
@@ -1147,7 +1207,10 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
|
||||
#define Tnot_started "not started"
|
||||
#define TOLDPWD "OLDPWD"
|
||||
#define Topen "open"
|
||||
#define To_o_reset " -o .reset"
|
||||
#define To_reset ".reset"
|
||||
#define TPATH "PATH"
|
||||
#define Tpo "+o"
|
||||
#define Tpv "pv"
|
||||
#define TpVv "Vpv"
|
||||
#define TPWD "PWD"
|
||||
@@ -1158,10 +1221,12 @@ 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 Tset_po "set +o"
|
||||
#define Tsghset "*=#set"
|
||||
#define Tsh "sh"
|
||||
#define TSHELL "SHELL"
|
||||
#define Tshell "shell"
|
||||
@@ -1191,15 +1256,16 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
|
||||
#define Twrite "write"
|
||||
#define Tf__S " %S"
|
||||
#define Tf__d " %d"
|
||||
#define Tf_ss "%s%s"
|
||||
#define Tf__ss " %s%s"
|
||||
#define Tf__sN " %s\n"
|
||||
#define Tf_sSs "%s/%s"
|
||||
#define Tf_T "%T"
|
||||
#define Tf_dN "%d\n"
|
||||
#define Tf_s_ "%s "
|
||||
#define Tf_s_T "%s %T"
|
||||
#define Tf_s_s_sN "%s %s %s\n"
|
||||
#define Tf_s_s "%s %s"
|
||||
#define Tf__s_s " %s %s"
|
||||
#define Tf_s_sD_s "%s %s: %s"
|
||||
#define Tf_optfoo "%s%s-%c: %s"
|
||||
#define Tf_sD_ "%s: "
|
||||
@@ -1217,8 +1283,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 +1436,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 +1450,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 +1597,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 +1696,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 +1707,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 +1826,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 +1865,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 +1895,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 +2066,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 +2087,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 +2141,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 +2375,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 +2391,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);
|
||||
@@ -2439,7 +2520,7 @@ void sethistfile(const char *);
|
||||
char **histpos(void) MKSH_A_PURE;
|
||||
int histnum(int);
|
||||
#endif
|
||||
int findhist(int, int, const char *, bool) MKSH_A_PURE;
|
||||
int findhist(int, const char *, bool, bool) MKSH_A_PURE;
|
||||
char **hist_get_newest(bool);
|
||||
void inittraps(void);
|
||||
void alarm_init(void);
|
||||
@@ -2511,6 +2592,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 +2626,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 +2674,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 +2690,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 +2795,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 */
|
||||
|
||||
|
||||
+2
-6
@@ -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.9 2020/05/16 22:38:25 tg Exp $");
|
||||
@SHFLAGS_ENUMS
|
||||
#define FN(sname,cname,flags,ochar) cname,
|
||||
#define F0(sname,cname,flags,ochar) cname = 0,
|
||||
@@ -184,13 +184,9 @@ 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) */
|
||||
>|
|
||||
FN("", FAS_BUILTIN, OF_INTERNAL
|
||||
|
||||
/* ./. (internal) initial shell was interactive */
|
||||
>|
|
||||
FN("", FTALKING_I, OF_INTERNAL
|
||||
|
||||
@@ -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.102 2020/06/22 17:11:03 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);
|
||||
@@ -665,7 +666,7 @@ shf_write(const char *buf, ssize_t nbytes, struct shf *shf)
|
||||
if (nbytes < 0)
|
||||
internal_errorf(Tf_szs, Tshf_write, nbytes, Tbytes);
|
||||
|
||||
/* Don't buffer if buffer is empty and we're writting a large amount. */
|
||||
/* don't buffer if buffer is empty and we're writing a large amount */
|
||||
if ((ncopy = shf->wnleft) &&
|
||||
(shf->wp != shf->buf || nbytes < shf->wnleft)) {
|
||||
if (ncopy > nbytes)
|
||||
@@ -781,7 +782,7 @@ shf_smprintf(const char *fmt, ...)
|
||||
#define FL_ZERO 0x040 /* '0' seen */
|
||||
#define FL_DOT 0x080 /* '.' seen */
|
||||
#define FL_UPPER 0x100 /* format character was uppercase */
|
||||
#define FL_NUMBER 0x200 /* a number was formated %[douxefg] */
|
||||
#define FL_NUMBER 0x200 /* a number was formatted %[douxefg] */
|
||||
#define FL_SIZET 0x400 /* 'z' seen */
|
||||
#define FM_SIZES 0x430 /* h/l/z mask */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.129 2020/10/31 01:21:58 tg Exp $");
|
||||
|
||||
struct nesting_state {
|
||||
int start_token; /* token than began nesting (eg, FOR) */
|
||||
@@ -268,12 +268,12 @@ get_command(int cf, int sALIAS)
|
||||
{
|
||||
struct op *t;
|
||||
int c, iopn = 0, syniocf, lno;
|
||||
struct ioword *iop, **iops;
|
||||
struct ioword *iop;
|
||||
XPtrV args, vars;
|
||||
struct nesting_state old_nesting;
|
||||
bool check_decl_utility;
|
||||
static struct ioword *iops[NUFILE + 1];
|
||||
|
||||
/* NUFILE is small enough to leave this addition unchecked */
|
||||
iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP);
|
||||
XPinit(args, 16);
|
||||
XPinit(vars, 16);
|
||||
|
||||
@@ -281,7 +281,6 @@ get_command(int cf, int sALIAS)
|
||||
switch (c = token(cf|KEYWORD|sALIAS|CMDASN)) {
|
||||
default:
|
||||
REJECT;
|
||||
afree(iops, ATEMP);
|
||||
XPfree(args);
|
||||
XPfree(vars);
|
||||
/* empty line */
|
||||
@@ -294,100 +293,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('(' /*)*/): {
|
||||
@@ -516,12 +508,11 @@ get_command(int cf, int sALIAS)
|
||||
}
|
||||
|
||||
if (iopn == 0) {
|
||||
afree(iops, ATEMP);
|
||||
t->ioact = NULL;
|
||||
} else {
|
||||
iops[iopn++] = NULL;
|
||||
iops = aresize2(iops, iopn, sizeof(struct ioword *), ATEMP);
|
||||
t->ioact = iops;
|
||||
t->ioact = alloc2(iopn, sizeof(struct ioword *), ATEMP);
|
||||
memcpy(t->ioact, iops, iopn * sizeof(struct ioword *));
|
||||
}
|
||||
|
||||
if (t->type == TCOM || t->type == TDBRACKET) {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.97 2018/10/20 18:46:00 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.100 2020/10/31 04:28:54 tg Exp $");
|
||||
|
||||
#define INDENT 8
|
||||
|
||||
@@ -37,6 +37,19 @@ static void iofree(struct ioword **, Area *);
|
||||
/* "foo& ; bar" and "foo |& ; bar" are invalid */
|
||||
static bool prevent_semicolon;
|
||||
|
||||
/* here document diversion */
|
||||
static unsigned short ptree_nest;
|
||||
static bool ptree_hashere;
|
||||
static struct shf ptree_heredoc;
|
||||
#define ptree_outhere(shf) do { \
|
||||
if (ptree_hashere) { \
|
||||
shf_puts(shf_sclose(&ptree_heredoc), (shf)); \
|
||||
shf_putc('\n', (shf)); \
|
||||
ptree_hashere = false; \
|
||||
/*prevent_semicolon = true;*/ \
|
||||
} \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
static const char Telif_pT[] = "elif %T";
|
||||
|
||||
/*
|
||||
@@ -82,8 +95,11 @@ ptree(struct op *t, int indent, struct shf *shf)
|
||||
w = (const char **)t->vars;
|
||||
while (*w)
|
||||
fptreef(shf, indent, Tf_S_, *w++);
|
||||
} else
|
||||
}
|
||||
#ifndef MKSH_SMALL
|
||||
else
|
||||
shf_puts("#no-vars# ", shf);
|
||||
#endif
|
||||
if (t->args) {
|
||||
w = t->args;
|
||||
if (*w && **w == CHAR) {
|
||||
@@ -97,8 +113,11 @@ ptree(struct op *t, int indent, struct shf *shf)
|
||||
}
|
||||
while (*w)
|
||||
fptreef(shf, indent, Tf_S_, *w++);
|
||||
} else
|
||||
}
|
||||
#ifndef MKSH_SMALL
|
||||
else
|
||||
shf_puts("#no-args# ", shf);
|
||||
#endif
|
||||
break;
|
||||
case TEXEC:
|
||||
t = t->left;
|
||||
@@ -221,36 +240,9 @@ ptree(struct op *t, int indent, struct shf *shf)
|
||||
prevent_semicolon = false;
|
||||
break;
|
||||
}
|
||||
if ((ioact = t->ioact) != NULL) {
|
||||
bool need_nl = false;
|
||||
|
||||
if ((ioact = t->ioact) != NULL)
|
||||
while (*ioact != NULL)
|
||||
pioact(shf, *ioact++);
|
||||
/* Print here documents after everything else... */
|
||||
ioact = t->ioact;
|
||||
while (*ioact != NULL) {
|
||||
struct ioword *iop = *ioact++;
|
||||
|
||||
/* heredoc is NULL when tracing (set -x) */
|
||||
if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE &&
|
||||
iop->heredoc) {
|
||||
shf_putc('\n', shf);
|
||||
shf_puts(iop->heredoc, shf);
|
||||
fptreef(shf, indent, Tf_s,
|
||||
evalstr(iop->delim, 0));
|
||||
need_nl = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Last delimiter must be followed by a newline (this
|
||||
* often leads to an extra blank line, but it's not
|
||||
* worth worrying about)
|
||||
*/
|
||||
if (need_nl) {
|
||||
shf_putc('\n', shf);
|
||||
prevent_semicolon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -272,11 +264,29 @@ pioact(struct shf *shf, struct ioword *iop)
|
||||
shf_putc('<', shf);
|
||||
break;
|
||||
case IOHERE:
|
||||
if (flag & IOHERESTR) {
|
||||
shf_puts("<<<", shf);
|
||||
goto ioheredelim;
|
||||
}
|
||||
shf_puts("<<", shf);
|
||||
if (flag & IOSKIP)
|
||||
shf_putc('-', shf);
|
||||
else if (flag & IOHERESTR)
|
||||
shf_putc('<', shf);
|
||||
if (iop->heredoc /* nil when tracing */) {
|
||||
/* here document diversion */
|
||||
if (!ptree_hashere) {
|
||||
shf_sopen(NULL, 0, SHF_WR | SHF_DYNAMIC,
|
||||
&ptree_heredoc);
|
||||
ptree_hashere = true;
|
||||
}
|
||||
shf_putc('\n', &ptree_heredoc);
|
||||
shf_puts(iop->heredoc, &ptree_heredoc);
|
||||
/* iop->delim is set before iop->heredoc */
|
||||
shf_puts(evalstr(iop->delim, 0), &ptree_heredoc);
|
||||
}
|
||||
ioheredelim:
|
||||
/* delim is NULL during syntax error printing */
|
||||
if (iop->delim && !(iop->ioflag & IONDELIM))
|
||||
wdvarput(shf, iop->delim, 0, WDS_TPUTS);
|
||||
break;
|
||||
case IOCAT:
|
||||
shf_puts(">>", shf);
|
||||
@@ -293,11 +303,8 @@ pioact(struct shf *shf, struct ioword *iop)
|
||||
shf_puts(flag & IORDUP ? "<&" : ">&", shf);
|
||||
break;
|
||||
}
|
||||
/* name/delim are NULL when printing syntax errors */
|
||||
if (type == IOHERE) {
|
||||
if (iop->delim && !(iop->ioflag & IONDELIM))
|
||||
wdvarput(shf, iop->delim, 0, WDS_TPUTS);
|
||||
} else if (iop->ioname) {
|
||||
/* name is NULL for IOHERE or when printing syntax errors */
|
||||
if (iop->ioname) {
|
||||
if (flag & IONAMEXP)
|
||||
print_value_quoted(shf, iop->ioname);
|
||||
else
|
||||
@@ -342,13 +349,6 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
|
||||
c = ord(*wp++);
|
||||
if (opmode & WDS_TPUTS)
|
||||
switch (c) {
|
||||
case ORD('\n'):
|
||||
if (quotelevel == 0) {
|
||||
c = ORD('\'');
|
||||
shf_putc(c, shf);
|
||||
shf_putc(ORD('\n'), shf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (quotelevel == 0)
|
||||
/* FALLTHROUGH */
|
||||
@@ -467,6 +467,9 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!ptree_nest++)
|
||||
ptree_hashere = false;
|
||||
|
||||
while ((c = ord(*fmt++))) {
|
||||
if (c == '%') {
|
||||
switch ((c = ord(*fmt++))) {
|
||||
@@ -504,10 +507,10 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
|
||||
shf_putc(';', shf);
|
||||
shf_putc(' ', shf);
|
||||
} else {
|
||||
int i;
|
||||
int i = indent;
|
||||
|
||||
ptree_outhere(shf);
|
||||
shf_putc('\n', shf);
|
||||
i = indent;
|
||||
while (i >= 8) {
|
||||
shf_putc('\t', shf);
|
||||
i -= 8;
|
||||
@@ -530,6 +533,9 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
|
||||
dont_trash_prevent_semicolon:
|
||||
;
|
||||
}
|
||||
|
||||
if (!--ptree_nest)
|
||||
ptree_outhere(shf);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
|
||||
* 2019, 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.
|
||||
*/
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/ulimit.c,v 1.3 2020/07/24 21:08:26 tg Exp $");
|
||||
|
||||
#define SOFT 0x1
|
||||
#define HARD 0x2
|
||||
|
||||
#if HAVE_RLIMIT
|
||||
|
||||
#if !HAVE_RLIM_T
|
||||
typedef unsigned long rlim_t;
|
||||
#endif
|
||||
|
||||
/* Magic to divine the 'm' and 'v' limits */
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
|
||||
!defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
|
||||
#define ULIMIT_V_IS_AS
|
||||
#elif defined(RLIMIT_VMEM)
|
||||
#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
|
||||
#define ULIMIT_V_IS_AS
|
||||
#else
|
||||
#define ULIMIT_V_IS_VMEM
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_RSS
|
||||
#ifdef ULIMIT_V_IS_VMEM
|
||||
#define ULIMIT_M_IS_RSS
|
||||
#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
|
||||
#define ULIMIT_M_IS_VMEM
|
||||
#else
|
||||
#define ULIMIT_M_IS_RSS
|
||||
#endif
|
||||
#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && \
|
||||
!defined(__APPLE__) && (RLIMIT_RSS == RLIMIT_AS)
|
||||
/* On Mac OSX keep -m as -v alias for pkgsrc and other software expecting it */
|
||||
#undef ULIMIT_M_IS_RSS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
|
||||
#define ULIMIT_V_IS_VMEM
|
||||
#endif
|
||||
|
||||
#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
|
||||
(!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
|
||||
#define ULIMIT_M_IS_VMEM
|
||||
#endif
|
||||
|
||||
#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
|
||||
(RLIMIT_VMEM == RLIMIT_AS)
|
||||
#undef ULIMIT_M_IS_VMEM
|
||||
#endif
|
||||
|
||||
#if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM)
|
||||
# error nonsensical m ulimit
|
||||
#endif
|
||||
|
||||
#if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS)
|
||||
# error nonsensical v ulimit
|
||||
#endif
|
||||
|
||||
#define LIMITS_GEN "rlimits.gen"
|
||||
|
||||
#else /* !HAVE_RLIMIT */
|
||||
|
||||
#undef RLIMIT_CORE /* just in case */
|
||||
|
||||
#if defined(UL_GETFSIZE)
|
||||
#define KSH_UL_GFIL UL_GETFSIZE
|
||||
#elif defined(UL_GFILLIM)
|
||||
#define KSH_UL_GFIL UL_GFILLIM
|
||||
#elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST)
|
||||
#define KSH_UL_GFIL 1
|
||||
#endif
|
||||
|
||||
#if defined(UL_SETFSIZE)
|
||||
#define KSH_UL_SFIL UL_SETFSIZE
|
||||
#elif defined(UL_SFILLIM)
|
||||
#define KSH_UL_SFIL UL_SFILLIM
|
||||
#elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST)
|
||||
#define KSH_UL_SFIL 2
|
||||
#endif
|
||||
|
||||
#if defined(KSH_UL_SFIL)
|
||||
#define KSH_UL_WFIL true
|
||||
#else
|
||||
#define KSH_UL_WFIL false
|
||||
#define KSH_UL_SFIL 0
|
||||
#endif
|
||||
|
||||
#if defined(UL_GETMAXBRK)
|
||||
#define KSH_UL_GBRK UL_GETMAXBRK
|
||||
#elif defined(UL_GMEMLIM)
|
||||
#define KSH_UL_GBRK UL_GMEMLIM
|
||||
#elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST)
|
||||
#define KSH_UL_GBRK 3
|
||||
#endif
|
||||
|
||||
#if defined(UL_GDESLIM)
|
||||
#define KSH_UL_GDES UL_GDESLIM
|
||||
#elif defined(__GLIBC__) || defined(KSH_ULIMIT2_TEST)
|
||||
#define KSH_UL_GDES 4
|
||||
#endif
|
||||
|
||||
extern char etext;
|
||||
extern long ulimit(int, long);
|
||||
|
||||
#define LIMITS_GEN "ulimits.gen"
|
||||
|
||||
#endif /* !HAVE_RLIMIT */
|
||||
|
||||
struct limits {
|
||||
/* limit resource / read command */
|
||||
int resource;
|
||||
#if HAVE_RLIMIT
|
||||
/* multiply by to get rlim_{cur,max} values */
|
||||
unsigned int factor;
|
||||
#else
|
||||
/* write command */
|
||||
int wesource;
|
||||
/* writable? */
|
||||
bool writable;
|
||||
#endif
|
||||
/* getopts char */
|
||||
char optchar;
|
||||
/* limit name */
|
||||
char name[1];
|
||||
};
|
||||
|
||||
#define RLIMITS_DEFNS
|
||||
#if HAVE_RLIMIT
|
||||
#define FN(lname,lid,lfac,lopt) \
|
||||
static const struct { \
|
||||
int resource; \
|
||||
unsigned int factor; \
|
||||
char optchar; \
|
||||
char name[sizeof(lname)]; \
|
||||
} rlimits_ ## lid = { \
|
||||
lid, lfac, lopt, lname \
|
||||
};
|
||||
#else
|
||||
#define FN(lname,lg,ls,lw,lopt) \
|
||||
static const struct { \
|
||||
int rcmd; \
|
||||
int wcmd; \
|
||||
bool writable; \
|
||||
char optchar; \
|
||||
char name[sizeof(lname)]; \
|
||||
} rlimits_ ## lg = { \
|
||||
lg, ls, lw, lopt, lname \
|
||||
};
|
||||
#endif
|
||||
#include LIMITS_GEN
|
||||
|
||||
static void print_ulimit(const struct limits *, int);
|
||||
static int set_ulimit(const struct limits *, const char *, int);
|
||||
|
||||
static const struct limits * const rlimits[] = {
|
||||
#define RLIMITS_ITEMS
|
||||
#include LIMITS_GEN
|
||||
};
|
||||
|
||||
static const char rlimits_opts[] =
|
||||
#define RLIMITS_OPTCS
|
||||
#include LIMITS_GEN
|
||||
#ifndef RLIMIT_CORE
|
||||
"c"
|
||||
#endif
|
||||
;
|
||||
|
||||
int
|
||||
c_ulimit(const char **wp)
|
||||
{
|
||||
size_t i = 0;
|
||||
int how = SOFT | HARD, optc;
|
||||
char what = 'f';
|
||||
bool all = false;
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1)
|
||||
switch (optc) {
|
||||
case ORD('H'):
|
||||
how = HARD;
|
||||
break;
|
||||
case ORD('S'):
|
||||
how = SOFT;
|
||||
break;
|
||||
case ORD('a'):
|
||||
all = true;
|
||||
break;
|
||||
case ORD('?'):
|
||||
bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts);
|
||||
return (1);
|
||||
default:
|
||||
what = optc;
|
||||
}
|
||||
|
||||
while (i < NELEM(rlimits)) {
|
||||
if (rlimits[i]->optchar == what)
|
||||
goto found;
|
||||
++i;
|
||||
}
|
||||
#ifndef RLIMIT_CORE
|
||||
if (what == ORD('c'))
|
||||
/* silently accept */
|
||||
return 0;
|
||||
#endif
|
||||
internal_warningf("ulimit: %c", what);
|
||||
return (1);
|
||||
found:
|
||||
if (wp[builtin_opt.optind]) {
|
||||
if (all || wp[builtin_opt.optind + 1]) {
|
||||
bi_errorf(Ttoo_many_args);
|
||||
return (1);
|
||||
}
|
||||
return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
|
||||
}
|
||||
if (!all)
|
||||
print_ulimit(rlimits[i], how);
|
||||
else for (i = 0; i < NELEM(rlimits); ++i) {
|
||||
shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name);
|
||||
print_ulimit(rlimits[i], how);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if HAVE_RLIMIT
|
||||
#define RL_T rlim_t
|
||||
#define RL_U (rlim_t)RLIM_INFINITY
|
||||
#else
|
||||
#define RL_T long
|
||||
#define RL_U LONG_MAX
|
||||
#endif
|
||||
|
||||
static int
|
||||
set_ulimit(const struct limits *l, const char *v, int how MKSH_A_UNUSED)
|
||||
{
|
||||
RL_T val = (RL_T)0;
|
||||
#if HAVE_RLIMIT
|
||||
struct rlimit limit;
|
||||
#endif
|
||||
|
||||
if (strcmp(v, "unlimited") == 0)
|
||||
val = RL_U;
|
||||
else {
|
||||
mksh_uari_t rval;
|
||||
|
||||
if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
|
||||
return (1);
|
||||
/*
|
||||
* Avoid problems caused by typos that evaluate misses due
|
||||
* to evaluating unset parameters to 0...
|
||||
* If this causes problems, will have to add parameter to
|
||||
* evaluate() to control if unset params are 0 or an error.
|
||||
*/
|
||||
if (!rval && !ctype(v[0], C_DIGIT)) {
|
||||
bi_errorf("invalid %s limit: %s", l->name, v);
|
||||
return (1);
|
||||
}
|
||||
#if HAVE_RLIMIT
|
||||
val = (rlim_t)((rlim_t)rval * l->factor);
|
||||
#else
|
||||
val = (RL_T)rval;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_RLIMIT
|
||||
if (getrlimit(l->resource, &limit) < 0) {
|
||||
#ifndef MKSH_SMALL
|
||||
bi_errorf("limit %s could not be read, contact the mksh developers: %s",
|
||||
l->name, cstrerror(errno));
|
||||
#endif
|
||||
/* some can't be read */
|
||||
limit.rlim_cur = RLIM_INFINITY;
|
||||
limit.rlim_max = RLIM_INFINITY;
|
||||
}
|
||||
if (how & SOFT)
|
||||
limit.rlim_cur = val;
|
||||
if (how & HARD)
|
||||
limit.rlim_max = val;
|
||||
if (!setrlimit(l->resource, &limit))
|
||||
return (0);
|
||||
#else
|
||||
if (l->writable == false) {
|
||||
/* check.t:ulimit-2 fails if we return 1 and/or do:
|
||||
bi_errorf(Tf_ro, l->name);
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
if (ulimit(l->wesource, val) != -1L)
|
||||
return (0);
|
||||
#endif
|
||||
if (errno == EPERM)
|
||||
bi_errorf("%s exceeds allowable %s limit", v, l->name);
|
||||
else
|
||||
bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
print_ulimit(const struct limits *l, int how MKSH_A_UNUSED)
|
||||
{
|
||||
RL_T val = (RL_T)0;
|
||||
#if HAVE_RLIMIT
|
||||
struct rlimit limit;
|
||||
|
||||
if (getrlimit(l->resource, &limit))
|
||||
#else
|
||||
if ((val = ulimit(l->resource, 0)) < 0)
|
||||
#endif
|
||||
{
|
||||
shf_puts("unknown\n", shl_stdout);
|
||||
return;
|
||||
}
|
||||
#if HAVE_RLIMIT
|
||||
if (how & SOFT)
|
||||
val = limit.rlim_cur;
|
||||
else if (how & HARD)
|
||||
val = limit.rlim_max;
|
||||
#endif
|
||||
if (val == RL_U)
|
||||
shf_puts("unlimited\n", shl_stdout);
|
||||
else {
|
||||
#if HAVE_RLIMIT
|
||||
val /= l->factor;
|
||||
#elif defined(KSH_UL_GBRK)
|
||||
if (l->resource == KSH_UL_GBRK)
|
||||
val = (RL_T)(((size_t)val - (size_t)&etext) /
|
||||
(size_t)1024);
|
||||
#endif
|
||||
shprintf("%lu\n", (unsigned long)val);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*-
|
||||
* Copyright (c) 2013, 2015, 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.
|
||||
*-
|
||||
* Keep {r,u}limits.opt in sync with each other!
|
||||
*/
|
||||
|
||||
@RLIMITS_DEFNS
|
||||
__RCSID("$MirOS: src/bin/mksh/ulimits.opt,v 1.2 2020/07/24 20:50:11 tg Exp $");
|
||||
@RLIMITS_ITEMS
|
||||
#define FN(lname,lg,ls,lw,lopt) (const struct limits *)(&rlimits_ ## lg),
|
||||
@@
|
||||
|
||||
/* generic options for the ulimit builtin */
|
||||
|
||||
<a|
|
||||
<H|
|
||||
<S|
|
||||
|
||||
/* do not use options -H, -S or -a or change the order */
|
||||
|
||||
>f|KSH_UL_GFIL
|
||||
FN("file(blocks)", KSH_UL_GFIL, KSH_UL_SFIL, KSH_UL_WFIL
|
||||
|
||||
>d|KSH_UL_GBRK
|
||||
FN("data(KiB)", KSH_UL_GBRK, 0, false
|
||||
|
||||
>n|KSH_UL_GDES
|
||||
FN("nofiles(descriptors)", KSH_UL_GDES, 0, false
|
||||
|
||||
|RLIMITS_OPTCS
|
||||
@@ -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.237 2020/06/22 17:11:03 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)
|
||||
{
|
||||
@@ -1867,7 +1900,7 @@ c_typeset(const char **wp)
|
||||
break;
|
||||
}
|
||||
|
||||
/* see comment below regarding possible opions */
|
||||
/* see comment below regarding possible options */
|
||||
opts = istset ? "L#R#UZ#afgi#lnprtux" : "p";
|
||||
|
||||
builtin_opt.flags |= GF_PLUSOPT;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user