Merge "Upgrade to mksh R59." am: f54e593e39 am: ce5d2ec295 am: fbd053ec48

Change-Id: I85c6edfcc6174ceff7170110f901895c020e35db
This commit is contained in:
Treehugger Robot 2020-05-19 23:58:07 +00:00 committed by Automerger Merge Worker
commit 959e0c2663
30 changed files with 3668 additions and 2008 deletions

View file

@ -107,7 +107,7 @@ cc_defaults {
"-DHAVE_SYS_ERRLIST_DECL=0",
"-DHAVE_SYS_SIGLIST_DECL=1",
"-DHAVE_PERSISTENT_HISTORY=0",
"-DMKSH_BUILD_R=571",
"-DMKSH_BUILD_R=591",
// Additional flags
"-DMKSH_DEFAULT_PROFILEDIR=\"/system/etc\"",

View file

@ -1,7 +1,7 @@
diff -ru mksh-R57/funcs.c src/funcs.c
--- mksh-R57/funcs.c 2018-10-20 14:04:55.000000000 -0700
+++ src/funcs.c 2019-03-26 12:05:23.976821773 -0700
@@ -103,7 +103,9 @@
diff -u mksh-R59/funcs.c src/funcs.c
--- mksh-R59/funcs.c 2020-04-13 12:51:34.000000000 -0700
+++ src/funcs.c 2020-05-15 16:10:35.035013192 -0700
@@ -104,7 +104,9 @@
{Tsgbreak, c_brkcont},
{T__builtin, c_builtin},
{Tbuiltin, c_builtin},
@ -11,7 +11,7 @@ diff -ru mksh-R57/funcs.c src/funcs.c
{Tcd, c_cd},
/* dash compatibility hack */
{"chdir", c_cd},
@@ -126,7 +128,9 @@
@@ -125,7 +127,9 @@
{"pwd", c_pwd},
{Tread, c_read},
{Tdsgreadonly, c_typeset},
@ -20,8 +20,8 @@ diff -ru mksh-R57/funcs.c src/funcs.c
+#endif
{"~rename", c_rename},
{"*=return", c_exitreturn},
{Tsgset, c_set},
@@ -160,8 +164,10 @@
{Tsghset, c_set},
@@ -159,8 +163,10 @@
{"~printf", c_printf},
#endif
#if HAVE_SELECT
@ -32,10 +32,11 @@ diff -ru mksh-R57/funcs.c src/funcs.c
#ifdef __MirBSD__
/* alias to "true" for historical reasons */
{"domainname", c_true},
diff -ru mksh-R57/main.c src/main.c
--- mksh-R57/main.c 2019-01-05 05:24:45.000000000 -0800
+++ src/main.c 2019-03-26 12:05:23.980821764 -0700
@@ -399,6 +399,12 @@
Only in src: funcs.c.orig
diff -u mksh-R59/main.c src/main.c
--- mksh-R59/main.c 2020-04-13 10:04:41.000000000 -0700
+++ src/main.c 2020-05-15 16:10:35.035013192 -0700
@@ -402,6 +402,12 @@
/* import environment */
init_environ();
@ -48,3 +49,4 @@ diff -ru mksh-R57/main.c src/main.c
/* for security */
typeset(TinitIFS, 0, 0, 0, 0);
Only in src: main.c.orig

View file

@ -1,8 +1,9 @@
#!/bin/sh
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.755 2020/04/07 23:15:11 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015, 2016, 2017
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
# 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@ -25,8 +26,8 @@ srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
#
# Used environment documentation is at the end of this file.
LC_ALL=C
export LC_ALL
LC_ALL=C; LANGUAGE=C
export LC_ALL; unset LANGUAGE
case $ZSH_VERSION:$VERSION in
:zsh*) ZSH_VERSION=2 ;;
@ -53,6 +54,15 @@ alll=qwertyuiopasdfghjklzxcvbnm
alln=0123456789
alls=______________________________________________________________
test_n() {
test x"$1" = x"" || return 0
return 1
}
test_z() {
test x"$1" = x""
}
case `echo a | tr '\201' X` in
X)
# EBCDIC build system
@ -64,11 +74,11 @@ X)
esac
genopt_die() {
if test -n "$1"; then
echo >&2 "E: $*"
echo >&2 "E: in '$srcfile': '$line'"
else
if test_z "$1"; then
echo >&2 "E: invalid input in '$srcfile': '$line'"
else
echo >&2 "E: $*"
echo >&2 "N: in '$srcfile': '$line'"
fi
rm -f "$bn.gen"
exit 1
@ -172,9 +182,9 @@ do_genopt() {
esac
IFS= read line || genopt_die Unexpected EOF
IFS=$safeIFS
test -n "$cond" && o_gen=$o_gen$nl"$cond"
test_z "$cond" || o_gen=$o_gen$nl"$cond"
o_gen=$o_gen$nl"$line, $optc)"
test -n "$cond" && o_gen=$o_gen$nl"#endif"
test_z "$cond" || o_gen=$o_gen$nl"#endif"
;;
esac
done
@ -185,11 +195,11 @@ do_genopt() {
esac
echo "$o_str" | sort | while IFS='|' read x opts cond; do
IFS=$safeIFS
test -n "$x" || continue
test_n "$x" || continue
genopt_scond
test -n "$cond" && echo "$cond"
test_z "$cond" || echo "$cond"
echo "\"$opts\""
test -n "$cond" && echo "#endif"
test_z "$cond" || echo "#endif"
done | {
echo "$o_hdr"
echo "#ifndef $o_sym$o_gen"
@ -234,7 +244,7 @@ vq() {
rmf() {
for _f in "$@"; do
case $_f in
Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;;
*.1|*.faq|*.ico) ;;
*) rm -f "$_f" ;;
esac
done
@ -343,7 +353,7 @@ ac_testnnd() {
test $ct = pcc && vscan='unsupported'
test $ct = sunpro && vscan='-e ignored -e turned.off'
fi
test -n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
test_n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
return 0
}
ac_testn() {
@ -406,10 +416,8 @@ ac_flags() {
test x"$ft" = x"" && ft="if $f can be used"
save_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $f"
if test -n "$fl"; then
save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $fl"
fi
save_LDFLAGS=$LDFLAGS
test_z "$fl" || LDFLAGS="$LDFLAGS $fl"
if test 1 = $hf; then
ac_testn can_$vn '' "$ft"
else
@ -421,9 +429,7 @@ ac_flags() {
#'
fi
eval fv=\$HAVE_CAN_`upper $vn`
if test -n "$fl"; then
test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
fi
test_z "$fl" || test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
test 11 = $fa$fv || CFLAGS=$save_CFLAGS
}
@ -512,7 +518,7 @@ ebcdic=false
for i
do
case $last:$i in
c:combine|c:dragonegg|c:llvm|c:lto)
c:dragonegg|c:llvm)
cm=$i
last=
;;
@ -524,10 +530,6 @@ do
optflags=$i
last=
;;
t:*)
tfn=$i
last=
;;
:-c)
last=c
;;
@ -573,9 +575,6 @@ do
:+T)
textmode=0
;;
:-t)
last=t
;;
:-v)
echo "Build.sh $srcversion"
echo "for mksh $dstversion"
@ -591,12 +590,12 @@ do
;;
esac
done
if test -n "$last"; then
if test_n "$last"; then
echo "$me: Option -'$last' not followed by argument!" >&2
exit 1
fi
test -z "$tfn" && if test $legacy = 0; then
test_n "$tfn" || if test $legacy = 0; then
tfn=mksh
else
tfn=lksh
@ -606,7 +605,7 @@ if test -d $tfn || test -d $tfn.exe; then
exit 1
fi
rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \
*.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out
*.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out *.htm
SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
@ -634,17 +633,17 @@ if test x"$srcdir" = x"."; then
else
CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS"
fi
test -n "$LDSTATIC" && if test -n "$LDFLAGS"; then
LDFLAGS="$LDFLAGS $LDSTATIC"
else
test_z "$LDSTATIC" || if test_z "$LDFLAGS"; then
LDFLAGS=$LDSTATIC
else
LDFLAGS="$LDFLAGS $LDSTATIC"
fi
if test -z "$TARGET_OS"; then
if test_z "$TARGET_OS"; then
x=`uname -s 2>/dev/null || uname`
test x"$x" = x"`uname -n 2>/dev/null`" || TARGET_OS=$x
fi
if test -z "$TARGET_OS"; then
if test_z "$TARGET_OS"; then
echo "$me: Set TARGET_OS, your uname is broken!" >&2
exit 1
fi
@ -652,7 +651,7 @@ oswarn=
ccpc=-Wc,
ccpl=-Wl,
tsts=
ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;; *) rm -f "$_f" ;; esac; done'
ccpr='|| for _f in ${tcfn}*; do case $_f in *.1|*.faq|*.ico) ;; *) rm -f "$_f" ;; esac; done'
# Evil hack
if test x"$TARGET_OS" = x"Android"; then
@ -706,12 +705,12 @@ fi
# Configuration depending on OS revision, on OSes that need them
case $TARGET_OS in
NEXTSTEP)
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`hostinfo 2>&1 | \
test_n "$TARGET_OSREV" || TARGET_OSREV=`hostinfo 2>&1 | \
grep 'NeXT Mach [0-9][0-9.]*:' | \
sed 's/^.*NeXT Mach \([0-9][0-9.]*\):.*$/\1/'`
;;
QNX|SCO_SV)
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
;;
esac
@ -847,6 +846,11 @@ Linux)
LynxOS)
oswarn="; it has minor issues"
;;
midipix)
add_cppflags -D_GNU_SOURCE
# their Perl (currently…) identifies as os:linux ☹
check_categories="$check_categories os:midipix"
;;
MidnightBSD)
;;
Minix-vmd)
@ -1037,11 +1041,11 @@ _svr4)
# generic target for SVR4 Unix with uname -s = uname -n
# this duplicates the * target below
oswarn='; it may or may not work'
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
;;
*)
oswarn='; it may or may not work'
test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r`
;;
esac
@ -1084,11 +1088,12 @@ SCO_SV|UnixWare|UNIX_SV)
vv '|' "uname -a >&2"
;;
esac
test -z "$oswarn" || echo >&2 "
test_z "$oswarn" || echo >&2 "
Warning: mksh has not yet been ported to or tested on your
operating system '$TARGET_OS'$oswarn. If you can provide
a shell account to the developer, this may improve; please
drop us a success or failure notice or even send in diffs.
drop us a success or failure notice or even send in diffs,
at the very least, complete logs (Build.sh + test.sh) will help.
"
$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
@ -1115,6 +1120,10 @@ ct="icc"
ct="xlc"
#elif defined(__SUNPRO_C)
ct="sunpro"
#elif defined(__neatcc__)
ct="neatcc"
#elif defined(__lacc__)
ct="lacc"
#elif defined(__ACK__)
ct="ack"
#elif defined(__BORLANDC__)
@ -1230,9 +1239,8 @@ dmc)
;;
gcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
-dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
$LIBS -dumpversion`'
vv '|' 'eval echo "\`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpmachine\`" \
"gcc\`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpversion\`"'
: "${HAVE_STRING_POOLING=i2}"
;;
hpcc)
@ -1250,6 +1258,9 @@ icc)
kencc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
;;
lacc)
# no version information
;;
lcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
add_cppflags -D__inline__=__inline
@ -1266,21 +1277,27 @@ msc)
ccpr= # errorlevels are not reliable
case $TARGET_OS in
Interix)
if [[ -n $C89_COMPILER ]]; then
C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
else
if test_z "$C89_COMPILER"; then
C89_COMPILER=CL.EXE
fi
if [[ -n $C89_LINKER ]]; then
C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
else
C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
fi
if test_z "$C89_LINKER"; then
C89_LINKER=LINK.EXE
else
C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
fi
vv '|' "$C89_COMPILER /HELP >&2"
vv '|' "$C89_LINKER /LINK >&2"
;;
esac
;;
neatcc)
add_cppflags -DMKSH_DONT_EMIT_IDSTRING
add_cppflags -DMKSH_NO_SIGSETJMP
add_cppflags -Dsig_atomic_t=int
vv '|' "$CC"
;;
nwcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
;;
@ -1333,7 +1350,7 @@ xlc)
*)
test x"$ct" = x"untested" && $e "!!! detecting preprocessor failed"
ct=unknown
vv "$CC --version"
vv '|' "$CC --version"
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
;;
@ -1376,14 +1393,14 @@ if ac_ifcpp 'if 0' compiler_fails '' \
case $ct in
dec)
CFLAGS="$CFLAGS ${ccpl}-non_shared"
ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF
ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-'EOF'
#include <unistd.h>
int main(void) { return (isatty(0)); }
EOF
;;
dmc)
CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE"
ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF
ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-'EOF'
#include <unistd.h>
int main(void) { return (isatty(0)); }
EOF
@ -1393,9 +1410,8 @@ if ac_ifcpp 'if 0' compiler_fails '' \
;;
esac
test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS
ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF
EOF
test 1 = $HAVE_COMPILER_STILL_FAILS && exit 1
ac_ifcpp 'if 0' compiler_still_fails \
'if the compiler still does not fail correctly' && exit 1
fi
if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \
'if this could be tcc'; then
@ -1478,7 +1494,7 @@ NOWARN=$save_NOWARN
#
i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln`
# optimisation: only if orig_CFLAGS is empty
test x"$i" = x"" && case $ct in
test_n "$i" || case $ct in
hpcc)
phase=u
ac_flags 1 otwo +O2
@ -1526,6 +1542,7 @@ dmc)
ac_flags 1 schk "${ccpc}-s" 'for stack overflow checking'
;;
gcc)
ac_flags 1 fnolto -fno-lto 'whether we can explicitly disable buggy GCC LTO' -fno-lto
# The following tests run with -Werror (gcc only) if possible
NOWARN=$DOWARN; phase=u
ac_flags 1 wnodeprecateddecls -Wno-deprecated-declarations
@ -1708,7 +1725,7 @@ ac_test attribute_format '' 'for __attribute__((__format__))' <<-'EOF'
#undef fprintf
extern int fprintf(FILE *, const char *format, ...)
__attribute__((__format__(__printf__, 2, 3)));
int main(int ac, char **av) { return (fprintf(stderr, "%s%d", *av, ac)); }
int main(int ac, char *av[]) { return (fprintf(stderr, "%s%d", *av, ac)); }
#endif
EOF
ac_test attribute_noreturn '' 'for __attribute__((__noreturn__))' <<-'EOF'
@ -1733,7 +1750,7 @@ ac_test attribute_pure '' 'for __attribute__((__pure__))' <<-'EOF'
#include <unistd.h>
#undef __attribute__
int foo(const char *) __attribute__((__pure__));
int main(int ac, char **av) { return (foo(av[ac - 1]) + isatty(0)); }
int main(int ac, char *av[]) { return (foo(av[ac - 1]) + isatty(0)); }
int foo(const char *s) { return ((int)s[0]); }
#endif
EOF
@ -1745,7 +1762,7 @@ ac_test attribute_unused '' 'for __attribute__((__unused__))' <<-'EOF'
#else
#include <unistd.h>
#undef __attribute__
int main(int ac __attribute__((__unused__)), char **av
int main(int ac __attribute__((__unused__)), char *av[]
__attribute__((__unused__))) { return (isatty(0)); }
#endif
EOF
@ -1863,22 +1880,22 @@ rmf lft* # end of large file support test
ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
int main(int ac, char **av) { return ((uint32_t)(size_t)*av + (int32_t)ac); }
int main(int ac, char *av[]) { return ((uint32_t)(size_t)*av + (int32_t)ac); }
EOF
ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
int main(int ac, char **av) { return ((u_int32_t)(size_t)*av + (int32_t)ac); }
int main(int ac, char *av[]) { return ((u_int32_t)(size_t)*av + (int32_t)ac); }
EOF
ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
int main(int ac, char **av) { return ((uint8_t)(size_t)av[ac]); }
int main(int ac, char *av[]) { return ((uint8_t)(size_t)av[ac]); }
EOF
ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF'
#include <sys/types.h>
#include <stddef.h>
int main(int ac, char **av) { return ((u_int8_t)(size_t)av[ac]); }
int main(int ac, char *av[]) { return ((u_int8_t)(size_t)av[ac]); }
EOF
ac_test rlim_t <<-'EOF'
@ -1948,7 +1965,11 @@ else
#define MKSH_INCLUDES_ONLY
#include "sh.h"
__RCSID("$srcversion");
int main(void) { printf("Hello, World!\\n"); return (isatty(0)); }
int main(void) {
struct timeval tv;
printf("Hello, World!\\n");
return (time(&tv.tv_sec));
}
EOF
case $cm in
llvm)
@ -2005,15 +2026,15 @@ ac_cppflags SYS_ERRLIST
for what in name list; do
uwhat=`upper $what`
ac_testn sys_sig$what '' "the sys_sig${what}[] array" <<-EOF
extern const char * const sys_sig${what}[];
ac_testn sys_sig$what '' "the sys_sig$what[] array" <<-EOF
extern const char * const sys_sig$what[];
extern int isatty(int);
int main(void) { return (sys_sig${what}[0][0] + isatty(0)); }
int main(void) { return (sys_sig$what[0][0] + isatty(0)); }
EOF
ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig${what}[] array" <<-EOF
extern const char * const _sys_sig${what}[];
ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig$what[] array" <<-EOF
extern const char * const _sys_sig$what[];
extern int isatty(int);
int main(void) { return (_sys_sig${what}[0][0] + isatty(0)); }
int main(void) { return (_sys_sig$what[0][0] + isatty(0)); }
EOF
eval uwhat_v=\$HAVE__SYS_SIG$uwhat
if test 1 = "$uwhat_v"; then
@ -2265,6 +2286,17 @@ ac_test sys_siglist_decl sys_siglist 0 'for declaration of sys_siglist[]' <<-'EO
int main(void) { return (sys_siglist[0][0] + isatty(0)); }
EOF
ac_test st_mtim '' 'for struct stat.st_mtim.tv_nsec' <<-'EOF'
#define MKSH_INCLUDES_ONLY
#include "sh.h"
int main(void) { struct stat sb; return (sizeof(sb.st_mtim.tv_nsec)); }
EOF
ac_test st_mtimensec '!' st_mtim 0 'for struct stat.st_mtimensec' <<-'EOF'
#define MKSH_INCLUDES_ONLY
#include "sh.h"
int main(void) { struct stat sb; return (sizeof(sb.st_mtimensec)); }
EOF
#
# other checks
#
@ -2287,7 +2319,7 @@ if test $legacy = 1; then
#define CHAR_BIT 0
#endif
struct ctasserts {
#define cta(name, assertion) char name[(assertion) ? 1 : -1]
#define cta(name,assertion) char name[(assertion) ? 1 : -1]
cta(char_is_8_bits, (CHAR_BIT) == 8);
cta(long_is_32_bits, sizeof(long) == 4);
};
@ -2301,7 +2333,7 @@ EOF
#define CHAR_BIT 0
#endif
struct ctasserts {
#define cta(name, assertion) char name[(assertion) ? 1 : -1]
#define cta(name,assertion) char name[(assertion) ? 1 : -1]
cta(char_is_8_bits, (CHAR_BIT) == 8);
cta(long_is_64_bits, sizeof(long) == 8);
};
@ -2429,7 +2461,7 @@ addsrcs '!' HAVE_STRLCPY strlcpy.c
addsrcs USE_PRINTF_BUILTIN printf.c
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
add_cppflags -DMKSH_BUILD_R=571
add_cppflags -DMKSH_BUILD_R=591
$e $bi$me: Finished configuration testing, now producing output.$ao
@ -2452,7 +2484,10 @@ esac
cat >test.sh <<-EOF
$mkshshebang
LC_ALL=C PATH='$PATH'; export LC_ALL PATH
test -n "\$KSH_VERSION" || exit 1
case \$KSH_VERSION in
*MIRBSD*|*LEGACY*) ;;
*) exit 1 ;;
esac
set -A check_categories -- $check_categories
pflag='$curdir/$mkshexe'
sflag='$srcdir/check.t'
@ -2614,8 +2649,8 @@ INDSRCS= $extras
NONSRCS_INST= dot.mkshrc \$(MAN)
NONSRCS_NOINST= Build.sh Makefile Rebuild.sh check.pl check.t test.sh
CC= $CC
CFLAGS= $CFLAGS
CPPFLAGS= $CPPFLAGS
CFLAGS= $CFLAGS
LDFLAGS= $LDFLAGS
LIBS= $LIBS
@ -2685,6 +2720,7 @@ test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
test -f $tcfn || exit 1
test 1 = $r || v "$NROFF -mdoc <'$srcdir/lksh.1' >lksh.cat1" || rmf lksh.cat1
test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >mksh.cat1" || rmf mksh.cat1
test 1 = $r || v "(set -- ''; . '$srcdir/FAQ2HTML.sh')" || rmf FAQ.htm
test 0 = $eq && v $SIZE $tcfn
i=install
test -f /usr/ucb/$i && i=/usr/ucb/$i
@ -2698,7 +2734,13 @@ if test $legacy = 0; then
fi
$e
$e Installing the manual:
if test -e FAQ.htm; then
$e "# $i -c -o root -g bin -m 444 FAQ.htm /usr/share/doc/mksh/"
fi
if test -f mksh.cat1; then
if test -e FAQ.htm; then
$e plus either
fi
$e "# $i -c -o root -g bin -m 444 lksh.cat1" \
"/usr/share/man/cat1/lksh.0"
$e "# $i -c -o root -g bin -m 444 mksh.cat1" \
@ -2709,6 +2751,8 @@ $e "# $i -c -o root -g bin -m 444 lksh.1 mksh.1 /usr/share/man/man1/"
$e
$e Run the regression test suite: ./test.sh
$e Please also read the sample file dot.mkshrc and the fine manual.
test -e FAQ.htm || \
$e Run FAQ2HTML.sh and place FAQ.htm into a suitable location as well.
exit 0
: <<'EOD'
@ -2740,6 +2784,7 @@ HAVE_CAN_FSTACKPROTECTORALL ac_flags
==== cpp definitions ====
DEBUG dont use in production, wants gcc, implies:
DEBUG_LEAKS enable freeing resources before exiting
KSH_VERSIONNAME_VENDOR_EXT when patching; space+plus+word (e.g. " +SuSE")
MKSHRC_PATH "~/.mkshrc" (do not change)
MKSH_A4PB force use of arc4random_pushb
MKSH_ASSUME_UTF8 (0=disabled, 1=enabled; default: unset)
@ -2768,6 +2813,7 @@ MKSH_S_NOVI=1 disable Vi editing mode (default if MKSH_SMALL)
MKSH_TYPEDEF_SIG_ATOMIC_T define to e.g. 'int' if sig_atomic_t is missing
MKSH_TYPEDEF_SSIZE_T define to e.g. 'long' if your OS has no ssize_t
MKSH_UNEMPLOYED disable job control (but not jobs/co-processes)
USE_REALLOC_MALLOC define as 0 to not use realloc as malloc
=== generic installation instructions ===
@ -2777,15 +2823,15 @@ them, set to a value other than 0 or 1. Ensure /bin/ed is installed. For
MKSH_SMALL but with Vi mode, add -DMKSH_S_NOVI=0 to CPPFLAGS as well.
Normally, the following command is what you want to run, then:
$ (sh Build.sh -r -c lto && ./test.sh -f) 2>&1 | tee log
$ (sh Build.sh -r && ./test.sh -f) 2>&1 | tee log
Copy dot.mkshrc to /etc/skel/.mkshrc; install mksh into $prefix/bin; or
/bin; install the manpage, if omitting the -r flag a catmanpage is made
using $NROFF. Consider using a forward script as /etc/skel/.mkshrc like
http://anonscm.debian.org/cgit/collab-maint/mksh.git/plain/debian/.mkshrc
https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=alioth/mksh.git;a=blob;f=debian/.mkshrc
and put dot.mkshrc as /etc/mkshrc so users need not keep up their HOME.
You may also want to install the lksh binary (also as /bin/sh) built by:
$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r -c lto
$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r
EOD

136
src/FAQ2HTML.sh Normal file
View file

@ -0,0 +1,136 @@
#!/bin/mksh
rcsid='$MirOS: src/bin/mksh/FAQ2HTML.sh,v 1.1 2020/02/03 22:23:33 tg Exp $'
#-
# Copyright © 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
# is granted to deal in this work without restriction, including un
# limited rights to use, publicly perform, distribute, sell, modify,
# merge, give away, or sublicence.
#
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
# the utmost extent permitted by applicable law, neither express nor
# implied; without malicious intent or gross negligence. In no event
# may a licensor, author or contributor be held liable for indirect,
# direct, other damage, loss, or other issues arising in any way out
# of dealing in the work, even if advised of the possibility of such
# damage or existence of a defect, except proven that it results out
# of said persons 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

View file

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.pl,v 1.49 2017/05/05 21:17:31 tg Exp $
# $MirOS: src/bin/mksh/check.pl,v 1.50 2019/08/01 20:05:55 tg Exp $
# $OpenBSD: th,v 1.1 2013/12/02 20:39:44 millert Exp $
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
@ -292,7 +292,7 @@ $all_tests = @ARGV == 0;
# Set up a very minimal environment
%new_env = ();
foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
'PATH', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
'PATH', 'PERLIO', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) {
$new_env{$env} = $ENV{$env} if defined $ENV{$env};
}
$new_env{'CYGWIN'} = 'nodosfilewarning';

View file

@ -1,9 +1,9 @@
# $MirOS: src/bin/mksh/check.t,v 1.812 2019/03/01 16:17:29 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.838 2020/04/13 19:51:08 tg Exp $
# -*- mode: sh -*-
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
# 2019
# 2019, 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@ -31,20 +31,29 @@
# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout:
@(#)MIRBSD KSH R57 2019/03/01
KSH R59 2020/04/14
description:
Check base version of full shell
stdin:
echo ${KSH_VERSION%%' +'*}
vsn=${KSH_VERSION%%' +'*}
echo "${vsn#* }"
name: KSH_VERSION
---
expected-stdout:
@(#)MIRBSD
description:
Check this identifies as legacy shell
stdin:
echo "${KSH_VERSION%% *}"
name: KSH_VERSION-modern
category: !shell:legacy-yes
---
expected-stdout:
@(#)LEGACY KSH R57 2019/03/01
@(#)LEGACY
description:
Check base version of legacy shell
Check this identifies as legacy shell
stdin:
echo ${KSH_VERSION%%' +'*}
echo "${KSH_VERSION%% *}"
name: KSH_VERSION-legacy
category: !shell:legacy-no
---
@ -53,6 +62,7 @@ description:
Check that the shell version tag does not include EBCDIC
category: !shell:ebcdic-yes
stdin:
set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+EBCDIC' ]] && exit 1
done
@ -63,6 +73,7 @@ description:
Check that the shell version tag includes EBCDIC
category: !shell:ebcdic-no
stdin:
set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+EBCDIC' ]] && exit 0
done
@ -73,6 +84,7 @@ description:
Check that the shell version tag does not include TEXTMODE
category: !shell:textmode-yes
stdin:
set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+TEXTMODE' ]] && exit 1
done
@ -83,6 +95,7 @@ description:
Check that the shell version tag includes TEXTMODE
category: !shell:textmode-no
stdin:
set -o noglob
for x in $KSH_VERSION; do
[[ $x = '+TEXTMODE' ]] && exit 0
done
@ -178,7 +191,7 @@ stdin:
expected-stdout:
ok
expected-stderr-pattern:
/mksh: warning: won't have full job control\nXX/
/ksh: warning: won't have full job control\nXX/
---
name: selftest-tty-present
description:
@ -1392,7 +1405,7 @@ need-pass: no
# the mv command fails on Cygwin and z/OS
# Hurd aborts the testsuite (permission denied)
# QNX does not find subdir to cd into
category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!os:os390,!nosymlink
category: !os:cygwin,!os:gnu,!os:midipix,!os:msys,!os:nto,!os:os390,!nosymlink
file-setup: file 644 "x"
mkdir noread noread/target noread/target/subdir
ln -s noread link
@ -1999,7 +2012,7 @@ expected-stdout:
name: eglob-bad-1
description:
Check that globbing isn't done when glob has syntax error
category: !os:cygwin,!os:msys,!os:os2
category: !os:cygwin,!os:midipix,!os:msys,!os:os2
file-setup: file 644 "@(a[b|)c]foo"
stdin:
echo @(a[b|)c]*
@ -2491,7 +2504,7 @@ description:
# breaks on Mac OSX (HFS+ non-standard UTF-8 canonical decomposition)
# breaks on Cygwin 1.7 (files are now UTF-16 or something)
# breaks on QNX 6.4.1 (says RT)
category: !os:cygwin,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
category: !os:cygwin,!os:midipix,!os:darwin,!os:msys,!os:nto,!os:os2,!os:os390
need-pass: no
file-setup: file 644 "aÂc"
stdin:
@ -2526,6 +2539,19 @@ expected-stdout:
-bc abc bbc cbc ebc
@bc
---
name: glob-range-6
description:
ksh93 fails this but POSIX probably demands it
file-setup: file 644 "abc"
file-setup: file 644 "cbc"
stdin:
echo *b*
[ '*b*' = *b* ] && echo yep; echo $?
expected-stdout:
abc cbc
2
expected-stderr-pattern: /.*/
---
name: glob-word-1
description:
Check BSD word boundary matches
@ -2721,7 +2747,7 @@ expected-stdout:
h\b
done
---
name: heredoc-9a
name: heredoc-9
description:
Check that here strings work.
stdin:
@ -2736,6 +2762,19 @@ stdin:
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"A $(echo "foo bar") B"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$b\$b$bar
fnord=42
bar="bar
\$fnord baz"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
echo $(tr r z <<<'bar' 2>/dev/null)
cat <<< "$( : )aa"
IFS=$'\n'
x=(a "b c")
tr ac 12 <<< ${x[*]}
tr ac 34 <<< "${x[*]}"
tr ac 56 <<< ${x[@]}
tr ac 78 <<< "${x[@]}"
expected-stdout:
sbb
sbb
@ -2748,54 +2787,17 @@ expected-stdout:
A sbb one B
$o$oone
onm
---
name: heredoc-9b
description:
Check that a corner case of here strings works like bash
stdin:
fnord=42
bar="bar
\$fnord baz"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
expected-stdout:
one $sabeq onm
category: bash
---
name: heredoc-9c
description:
Check that a corner case of here strings works like ksh93, zsh
stdin:
fnord=42
bar="bar
\$fnord baz"
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
expected-stdout:
one
$sabeq onm
---
name: heredoc-9d
description:
Check another corner case of here strings
stdin:
tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
expected-stdout:
one
---
name: heredoc-9e
description:
Check here string related regression with multiple iops
stdin:
echo $(tr r z <<<'bar' 2>/dev/null)
expected-stdout:
baz
---
name: heredoc-9f
description:
Check long here strings
stdin:
cat <<< "$( : )aa"
expected-stdout:
aa
1
b 2
3
b 4
5 b 6
7 b 8
---
name: heredoc-10
description:
@ -4981,6 +4983,17 @@ expected-stdout:
var=onetwo threefour
<onetwo threefour> .
---
name: IFS-subst-11
description:
Check leading non-whitespace after trim makes only one field
stdin:
showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
v="foo!one!two!three"
IFS="!"
showargs x ${v:3} y
expected-stdout:
<x> <> <one> <two> <three> <y> .
---
name: IFS-arith-1
description:
http://austingroupbugs.net/view.php?id=832
@ -6653,12 +6666,35 @@ name: regression-62
description:
Check if test -nt/-ot succeeds if second(first) file is missing.
stdin:
matrix() {
local a b c d e f g h
test a -nt b; a=$?
test b -nt a; b=$?
test a -ot b; c=$?
test b -ot a; d=$?
test a -nt a; e=$?
test b -nt b; f=$?
test a -ot a; g=$?
test b -ot b; h=$?
echo $1 $a $b $c $d / $e $f $g $h .
}
matrix a
:>a
test a -nt b && echo nt OK || echo nt BAD
test b -ot a && echo ot OK || echo ot BAD
matrix b
sleep 2 # mtime granularity for OS/2 and FAT
:>b
matrix c
sleep 2
echo dummy >a # Debian GNU/Hurd #955270
matrix d
rm a
matrix e
expected-stdout:
nt OK
ot OK
a 1 1 1 1 / 1 1 1 1 .
b 0 1 1 0 / 1 1 1 1 .
c 1 0 0 1 / 1 1 1 1 .
d 0 1 1 0 / 1 1 1 1 .
e 1 0 0 1 / 1 1 1 1 .
---
name: regression-63
description:
@ -7118,13 +7154,13 @@ stdin:
name: exec-function-environment-1
description:
Check assignments in function calls and whether they affect
the current execution environment (ksh93, SUSv4)
the current execution environment
stdin:
f() { a=2; }; g() { b=3; echo y$c-; }; a=1 f; b=2; c=1 g
echo x$a-$b- z$c-
expected-stdout:
y1-
x2-3- z1-
x-3- z-
---
name: exec-modern-korn-shell
description:
@ -7389,6 +7425,8 @@ expected-stdout:
name: xxx-stat-1
description:
Check that tests on files are consistent
(fails when run as root, unfortunately)
category: disabled
stdin:
mkdir a
echo x >a/b
@ -7580,7 +7618,7 @@ stdin:
showargs 18 "$a"
set -A bla
typeset bla[1]=~:~
global gbl=~ g2=$1
typeset -g gbl=~ g2=$1
local lcl=~ l2=$1
readonly ro=~ r2=$1
showargs 19 "${bla[1]}" a=~ "$gbl" "$lcl" "$ro" "$g2" "$l2" "$r2"
@ -7761,6 +7799,32 @@ expected-stdout:
1 ok
expected-exit: 1
---
name: exit-err-10
description:
Debian #269067 (cf. regression-38 but with eval)
arguments: !-e!
stdin:
eval false || true
echo = $? .
expected-stdout:
= 0 .
---
name: exit-err-11
description:
Fix -e inside eval, from Martijn Dekker; expected-stdout from ksh93
stdin:
"$__progname" -c 'eval '\''echo ${-//[!eh]}; false; echo phantom e'\''; echo x$?'
echo = $?
"$__progname" -ec 'eval '\''echo ${-//[!eh]}; false; echo phantom e'\''; echo x$?'
echo = $?
expected-stdout:
h
phantom e
x0
= 0
eh
= 1
---
name: exit-enoent-1
description:
SUSv4 says that the shell should exit with 126/127 in some situations
@ -7832,11 +7896,9 @@ expected-exit: 9
---
name: exit-trap-2
description:
Check that ERR and EXIT traps are run just like ksh93 does.
GNU bash does not run ERtrap in ±e eval-undef but runs it
twice (bug?) in +e eval-false, so does ksh93 (bug?), which
also has a bug to continue execution (echoing "and out" and
returning 0) in +e eval-undef.
Check that ERR and EXIT traps are run just like GNU bash does.
ksh93 runs ERtrap after parameter null or not set (which mksh
used to do) but (bug) continues and out, exit 0, in +e eval-undef.
file-setup: file 644 "x"
v=; unset v
trap 'echo EXtrap' EXIT
@ -7920,7 +7982,6 @@ expected-stdout:
= eval-false 1 .
and run ${v?}
x: v: parameter null or not set
ERtrap
EXtrap
= eval-undef 1 .
and run true
@ -7942,12 +8003,12 @@ expected-stdout:
= eval-true 0 .
and run false
ERtrap
ERtrap
and out
EXtrap
= eval-false 0 .
and run ${v?}
x: v: parameter null or not set
ERtrap
EXtrap
= eval-undef 1 .
and run true
@ -8017,6 +8078,50 @@ expected-stdout:
nein
expected-stderr-pattern: !/unexpected op/
---
name: test-str-pattern
description:
Check that [[ x = $y ]] can take extglobs, like ksh93
stdin:
x='a\'
[[ $x = a\ ]]; echo 1 $? .
[[ $x = a\\ ]]; echo 2 $? .
y='a\'
[[ $x = $y ]]; echo 3 $? .
[[ $x = "$y" ]]; echo 4 $? .
x='a\b'
y='a\b'
[[ $x = $y ]]; echo 5 $? .
[[ $x = "$y" ]]; echo 6 $? .
y='a\\b'
[[ $x = $y ]]; echo 7 $? .
[[ $x = "$y" ]]; echo 8 $? .
x='foo'
y='f+(o)'
[[ $x = $y ]]; echo 9 $? .
[[ $x = "$y" ]]; echo 10 $? .
x=$y
[[ $x = $y ]]; echo 11 $? .
[[ $x = "$y" ]]; echo 12 $? .
x='f+(o'
y=$x
[[ $x = $y ]]; echo 13 $? .
[[ $x = "$y" ]]; echo 14 $? .
expected-stdout:
1 1 .
2 0 .
3 0 .
4 0 .
5 1 .
6 0 .
7 0 .
8 1 .
9 0 .
10 1 .
11 1 .
12 0 .
13 0 .
14 0 .
---
name: test-precedence-1
description:
Check a weird precedence case (and POSIX echo)
@ -8672,7 +8777,7 @@ description:
note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
XXX fails when LD_PRELOAD is set with -e and Perl chokes it (ASan)
need-pass: no
category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
category: !os:cygwin,!os:midipix,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
env-setup: !FOO=BAR!
stdin:
print '#!'"$__progname"'\nprint "1 a=$ENV{FOO}";' >t1
@ -9621,8 +9726,7 @@ expected-stdout:
---
name: varexpand-substr-3
description:
Check that some things that work in bash fail.
This is by design. Oh and vice versa, nowadays.
Match bash5
stdin:
export x=abcdefghi n=2
"$__progname" -c 'echo v${x:(n)}x'
@ -9630,15 +9734,15 @@ stdin:
"$__progname" -c 'echo x${x:n}x'
"$__progname" -c 'echo y${x:}x'
"$__progname" -c 'echo z${x}x'
# next fails only in bash
"$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
"$__progname" -c 'x=abcdef;y=123;echo q${x:${y:2:1}:2}q'
expected-stdout:
vcdefghix
wcdefghix
xcdefghix
zabcdefghix
0
qdeq
expected-stderr-pattern:
/x:n.*bad substitution.*\n.*bad substitution/
/x:}.*bad substitution/
---
name: varexpand-substr-4
description:
@ -10841,6 +10945,43 @@ stdin:
expected-stdout:
okay
---
name: ulimit-3
description:
Check that there are no duplicate limits (if this fails,
immediately contact with system information the developers)
stdin:
[[ -z $(set | grep ^opt) ]]; mis=$?
set | grep ^opt | sed 's/^/unexpectedly set in environment: /'
opta='<used for showing all limits>'
optH='<used to set hard limits>'
optS='<used to set soft limits>'
ulimit -a >tmpf
set -o noglob
while IFS= read -r line; do
x=${line:1:1}
if [[ -z $x || ${#x}/${%x} != 1/1 ]]; then
print -r -- "weird line: $line"
(( mis |= 1 ))
continue
fi
set -- $line
nameref v=opt$x
if [[ -n $v ]]; then
print -r -- "duplicate -$x \"$2\" already seen as \"$v\""
(( mis |= 2 ))
fi
v=$2
done <tmpf
if (( mis & 2 )); then
echo failed
elif (( mis & 1 )); then
echo inconclusive
else
echo done
fi
expected-stdout:
done
---
name: redir-1
description:
Check some of the most basic invariants of I/O redirection
@ -11201,7 +11342,7 @@ description:
(Inspired by PR 2450 on OpenBSD.) Calling
FOO=bar f
where f is a ksh style function, should not set FOO in the current
env. If f is a Bourne style function, FOO should be set. Furthermore,
env. If f is a Bourne style function, (new) also not. Furthermore,
the function should receive a correct value of FOO. However, differing
from oksh, setting FOO in the function itself must change the value in
setting FOO in the function itself should not change the value in
@ -11247,7 +11388,7 @@ stdin:
if [ $? != 0 ]; then
exit 1
fi
if [ x$FOO != xfoo ]; then
if [ x$FOO != x ]; then
exit 1
fi
FOO=barbar
@ -11262,7 +11403,7 @@ stdin:
if [ $? != 0 ]; then
exit 1
fi
if [ x$FOO != xfoo ]; then
if [ x$FOO != xbarbar ]; then
exit 1
fi
---
@ -12659,7 +12800,7 @@ stdin:
echo =14
(mypid=$$; try mypid)
echo =15
) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
) 2>&1 | sed -e 's/^[A-Za-z]://' -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
exit ${PIPESTATUS[0]}
expected-stdout:
y
@ -13238,7 +13379,7 @@ description:
Crashed during March 2011, fixed on vernal equinÅ<EFBFBD>x ☺
category: os:mirbsd,os:openbsd
stdin:
export MALLOC_OPTIONS=FGJPRSX
export MALLOC_OPTIONS=FGJRSX
"$__progname" -c 'x=$(tr z r <<<baz); echo $x'
expected-stdout:
bar
@ -13362,6 +13503,59 @@ expected-stdout:
after 0='swc' 1='二' 2=''
= done
---
name: command-set
description:
Same but with set
stdin:
showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; }
showargs 1 "$@"
set -- foo bar baz
showargs 2 "$@"
command set -- miau 'meow nyao'
showargs 3 "$@"
expected-stdout:
<1> .
<2> <foo> <bar> <baz> .
<3> <miau> <meow nyao> .
---
name: command-readonly
description:
These should not exit on error when prefixed
stdin:
exec 2>/dev/null
"$__progname" -c 'readonly v; export v=foo || echo ok'
echo ef=$?
"$__progname" -c 'readonly v; command export v=foo || echo ok'
echo en=$?
"$__progname" -c 'readonly v; readonly v=foo || echo ok'
echo rf=$?
"$__progname" -c 'readonly v; command readonly v=foo || echo ok'
echo rn=$?
expected-stdout:
ef=2
ok
en=0
rf=2
ok
rn=0
---
name: command-dot-regression
description:
Check a regression in fixing the above does not appear
stdin:
cat >test.mksh <<\EOF
set -- one two
shift
for s_arg in "$#" "$@"; do echo -n "<$s_arg> "; done; echo .
EOF
"$__progname" -c '. ./test.mksh' dummy oh dear this is not good
echo =
"$__progname" -c 'command . ./test.mksh' dummy oh dear this is not good
expected-stdout:
<1> <two> .
=
<1> <two> .
---
name: command-pvV-posix-priorities
description:
For POSIX compatibility, command -v should find aliases and reserved
@ -13370,13 +13564,17 @@ description:
stdin:
PATH=/bin:/usr/bin
alias foo="bar baz"
alias '[ab]=:'
bar() { :; }
for word in 'if' 'foo' 'bar' 'set' 'true'; do
for word in 'if' 'foo' 'bar' 'set' 'true' '[ab]'; do
command -v "$word"
command -pv "$word"
command -V "$word"
command -pV "$word"
done
# extra checks
alias '[ab]'
whence '[ab]'
expected-stdout:
if
if
@ -13398,6 +13596,12 @@ expected-stdout:
true
true is a shell builtin
true is a shell builtin
alias '[ab]'=:
alias '[ab]'=:
'[ab]' is an alias for :
'[ab]' is an alias for :
'[ab]'=:
:
---
name: whence-preserve-tradition
description:

View file

@ -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 followyou 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

View file

@ -5,7 +5,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
* 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -28,7 +29,7 @@
#ifndef MKSH_NO_CMDLINE_EDITING
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.343 2018/07/15 16:16:38 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.350 2020/04/13 20:46:37 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
@ -176,7 +177,7 @@ x_getc(void)
static void
x_putcf(int c)
{
shf_putc(c, shl_out);
shf_putc_i(c, shl_out);
}
/*********************************
@ -353,7 +354,7 @@ x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
*--cp = '/';
} else {
/* ok, expand and replace */
cp = shf_smprintf(Tf_sSs, dp, cp);
strpathx(cp, dp, cp, 1);
if (magic_flag)
afree(s, ATEMP);
s = cp;
@ -995,10 +996,7 @@ static int x_search_dir(int);
static int x_match(char *, char *);
static void x_redraw(int);
static void x_push(size_t);
static char *x_mapin(const char *, Area *);
static char *x_mapout(int);
static void x_mapout2(int, char **);
static void x_print(int, int);
static void x_bind_showone(int, int);
static void x_e_ungetc(int);
static int x_e_getc(void);
static void x_e_putc2(int);
@ -1145,6 +1143,7 @@ static struct x_defbindings const x_defbindings[] = {
#ifndef MKSH_SMALL
/* more non-standard ones */
{ XFUNC_eval_region, 1, CTRL_E },
{ XFUNC_quote_region, 1, 'Q' },
{ XFUNC_edit_line, 2, 'e' }
#endif
};
@ -2389,190 +2388,223 @@ x_vt_hack(int c)
}
#endif
static char *
x_mapin(const char *cp, Area *ap)
int
x_bind_check(void)
{
char *news, *op;
return (x_tab == NULL);
}
strdupx(news, cp, ap);
op = news;
while (*cp) {
switch (*cp) {
case '^':
cp++;
*op++ = ksh_toctrl(*cp);
break;
case '\\':
if (cp[1] == '\\' || cp[1] == '^')
++cp;
/* FALLTHROUGH */
default:
*op++ = *cp;
}
cp++;
static XString x_bind_show_xs;
static char *x_bind_show_xp;
static void
x_bind_show_ch(unsigned char ch)
{
Xcheck(x_bind_show_xs, x_bind_show_xp);
switch (ch) {
case ORD('^'):
case ORD('\\'):
case ORD('='):
*x_bind_show_xp++ = '\\';
*x_bind_show_xp++ = ch;
break;
default:
if (ksh_isctrl(ch)) {
*x_bind_show_xp++ = '^';
*x_bind_show_xp++ = ksh_unctrl(ch);
} else
*x_bind_show_xp++ = ch;
break;
}
*op = '\0';
return (news);
}
static void
x_mapout2(int c, char **buf)
x_bind_showone(int prefix, int key)
{
char *p = *buf;
unsigned char f = XFUNC_VALUE(x_tab[prefix][key]);
if (ksh_isctrl(c)) {
*p++ = '^';
*p++ = ksh_unctrl(c);
} else
*p++ = c;
*p = 0;
*buf = p;
}
if (!x_bind_show_xs.areap)
XinitN(x_bind_show_xs, 16, AEDIT);
static char *
x_mapout(int c)
{
static char buf[8];
char *bp = buf;
x_mapout2(c, &bp);
return (buf);
}
static void
x_print(int prefix, int key)
{
int f = x_tab[prefix][key];
if (prefix)
/* prefix == 1 || prefix == 2 || prefix == 3 */
shf_puts(x_mapout(prefix == 1 ? CTRL_BO :
prefix == 2 ? CTRL_X : 0), shl_stdout);
#ifdef MKSH_SMALL
shprintf("%s = ", x_mapout(key));
#else
shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
if (XFUNC_VALUE(f) != XFUNC_ins_string)
#endif
shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
shf_puts("bind ", shl_stdout);
#ifndef MKSH_SMALL
else
shprintf("'%s'\n", x_atab[prefix][key]);
if (f == XFUNC_ins_string)
shf_puts("-m ", shl_stdout);
#endif
switch (prefix) {
case 1:
x_bind_show_ch(CTRL_BO);
break;
case 2:
x_bind_show_ch(CTRL_X);
break;
case 3:
x_bind_show_ch(0);
break;
}
x_bind_show_ch(key);
#ifndef MKSH_SMALL
if (x_tab[prefix][key] & 0x80)
*x_bind_show_xp++ = '~';
#endif
*x_bind_show_xp = '\0';
x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
print_value_quoted(shl_stdout, x_bind_show_xp);
shf_putc('=', shl_stdout);
#ifndef MKSH_SMALL
if (f == XFUNC_ins_string) {
const unsigned char *cp = (const void *)x_atab[prefix][key];
unsigned char c;
while ((c = *cp++))
x_bind_show_ch(c);
*x_bind_show_xp = '\0';
x_bind_show_xp = Xstring(x_bind_show_xs, x_bind_show_xp);
print_value_quoted(shl_stdout, x_bind_show_xp);
} else
#endif
shf_puts(x_ftab[f].xf_name, shl_stdout);
shf_putc('\n', shl_stdout);
}
int
x_bind(const char *a1, const char *a2,
#ifndef MKSH_SMALL
/* bind -m */
bool macro,
#endif
/* bind -l */
bool list)
x_bind_list(void)
{
size_t f;
for (f = 0; f < NELEM(x_ftab); f++)
if (!(x_ftab[f].xf_flags & XF_NOBIND))
shprintf(Tf_sN, x_ftab[f].xf_name);
return (0);
}
int
x_bind_showall(void)
{
unsigned char f;
int prefix, key;
char *m1, *m2;
#ifndef MKSH_SMALL
char *sp = NULL;
bool hastilde;
#endif
if (x_tab == NULL) {
bi_errorf("can't bind, not a tty");
return (1);
}
/* List function names */
if (list) {
for (f = 0; f < NELEM(x_ftab); f++)
if (!(x_ftab[f].xf_flags & XF_NOBIND))
shprintf(Tf_sN, x_ftab[f].xf_name);
return (0);
}
if (a1 == NULL) {
for (prefix = 0; prefix < X_NTABS; prefix++)
for (key = 0; key < X_TABSZ; key++) {
f = XFUNC_VALUE(x_tab[prefix][key]);
if (f == XFUNC_insert || f == XFUNC_error
#ifndef MKSH_SMALL
|| (macro && f != XFUNC_ins_string)
#endif
)
continue;
x_print(prefix, key);
}
return (0);
}
m2 = m1 = x_mapin(a1, ATEMP);
prefix = 0;
for (;; m1++) {
key = (unsigned char)*m1;
f = XFUNC_VALUE(x_tab[prefix][key]);
if (f == XFUNC_meta1)
prefix = 1;
else if (f == XFUNC_meta2)
prefix = 2;
else if (f == XFUNC_meta3)
prefix = 3;
else
break;
}
if (*++m1
#ifndef MKSH_SMALL
&& ((*m1 != '~') || *(m1 + 1))
#endif
) {
char msg[256];
const char *c = a1;
m1 = msg;
while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
x_mapout2(*c++, &m1);
bi_errorf("too long key sequence: %s", msg);
return (1);
}
#ifndef MKSH_SMALL
hastilde = tobool(*m1);
#endif
afree(m2, ATEMP);
if (a2 == NULL) {
x_print(prefix, key);
return (0);
}
if (*a2 == 0) {
f = XFUNC_insert;
#ifndef MKSH_SMALL
} else if (macro) {
f = XFUNC_ins_string;
sp = x_mapin(a2, AEDIT);
#endif
} else {
for (f = 0; f < NELEM(x_ftab); f++)
if (!strcmp(x_ftab[f].xf_name, a2))
for (prefix = 0; prefix < X_NTABS; prefix++)
for (key = 0; key < X_TABSZ; key++)
switch (XFUNC_VALUE(x_tab[prefix][key])) {
case XFUNC_error: /* unset */
case XFUNC_insert: /* auto-insert */
break;
if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
bi_errorf("%s: no such function", a2);
default:
x_bind_showone(prefix, key);
break;
}
return (0);
}
static unsigned int
x_bind_getc(const char **ccpp)
{
unsigned int ch, ec;
if ((ch = ord(**ccpp)))
++(*ccpp);
switch (ch) {
case ORD('^'):
ch = ksh_toctrl(**ccpp) | 0x100U;
if (**ccpp)
++(*ccpp);
break;
case ORD('\\'):
switch ((ec = ord(**ccpp))) {
case ORD('^'):
case ORD('\\'):
case ORD('='):
ch = ec | 0x100U;
++(*ccpp);
break;
}
break;
}
return (ch);
}
int
x_bind(const char *s SMALLP(bool macro))
{
const char *ccp = s;
int prefix, key;
unsigned int c;
#ifndef MKSH_SMALL
bool hastilde = false;
char *ms = NULL;
#endif
prefix = 0;
c = x_bind_getc(&ccp);
if (!c || c == ORD('=')) {
bi_errorf("no key to bind");
return (1);
}
key = c & 0xFF;
while ((c = x_bind_getc(&ccp)) != ORD('=')) {
if (!c) {
x_bind_showone(prefix, key);
return (0);
}
switch (XFUNC_VALUE(x_tab[prefix][key])) {
case XFUNC_meta1:
prefix = 1;
if (0)
/* FALLTHROUGH */
case XFUNC_meta2:
prefix = 2;
if (0)
/* FALLTHROUGH */
case XFUNC_meta3:
prefix = 3;
key = c & 0xFF;
continue;
}
#ifndef MKSH_SMALL
if (c == ORD('~')) {
hastilde = true;
continue;
}
#endif
bi_errorf("too long key sequence: %s", s);
return (-1);
}
#ifndef MKSH_SMALL
if (macro) {
char *cp;
cp = ms = alloc(strlen(ccp) + 1, AEDIT);
while ((c = x_bind_getc(&ccp)))
*cp++ = c;
*cp = '\0';
c = XFUNC_ins_string;
} else
#endif
if (!*ccp) {
c = XFUNC_insert;
} else {
for (c = 0; c < NELEM(x_ftab); ++c)
if (!strcmp(x_ftab[c].xf_name, ccp))
break;
if (c == NELEM(x_ftab) || x_ftab[c].xf_flags & XF_NOBIND) {
bi_errorf("%s: no such editing command", ccp);
return (1);
}
}
#ifndef MKSH_SMALL
if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
x_atab[prefix][key])
if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string)
afree(x_atab[prefix][key], AEDIT);
x_atab[prefix][key] = ms;
if (hastilde)
c |= 0x80U;
#endif
x_tab[prefix][key] = f
#ifndef MKSH_SMALL
| (hastilde ? 0x80 : 0)
#endif
;
#ifndef MKSH_SMALL
x_atab[prefix][key] = sp;
#endif
x_tab[prefix][key] = c;
/* Track what the user has bound so x_mode(true) won't toast things */
if (f == XFUNC_insert)
/* track what the user has bound, so x_mode(true) won't toast things */
if (c == XFUNC_insert)
x_bound[(prefix * X_TABSZ + key) / 8] &=
~(1 << ((prefix * X_TABSZ + key) % 8));
else
@ -3507,11 +3539,11 @@ static int inslen; /* length of input buffer */
static int srchlen; /* length of current search pattern */
static char *ybuf; /* yank buffer */
static int yanklen; /* length of yank buffer */
static int fsavecmd = ' '; /* last find command */
static uint8_t fsavecmd = ORD(' '); /* last find command */
static int fsavech; /* character to find */
static char lastcmd[MAXVICMD]; /* last non-move command */
static int lastac; /* argcnt for lastcmd */
static int lastsearch = ' '; /* last search command */
static uint8_t lastsearch = ORD(' '); /* last search command */
static char srchpat[SRCHLEN]; /* last search pattern */
static int insert; /* <>0 in insert mode */
static int hnum; /* position in history */
@ -3651,21 +3683,25 @@ vi_hook(int ch)
case VNORMAL:
/* PC scancodes */
if (!ch) switch (cmdlen = 0, (ch = x_getc())) {
case 71: ch = '0'; goto pseudo_vi_command;
case 72: ch = 'k'; goto pseudo_vi_command;
case 73: ch = 'A'; goto vi_xfunc_search_up;
case 75: ch = 'h'; goto pseudo_vi_command;
case 77: ch = 'l'; goto pseudo_vi_command;
case 79: ch = '$'; goto pseudo_vi_command;
case 80: ch = 'j'; goto pseudo_vi_command;
case 83: ch = 'x'; goto pseudo_vi_command;
default: ch = 0; goto vi_insert_failed;
if (!ch) {
cmdlen = 0;
switch (ch = x_getc()) {
case 71: ch = ORD('0'); goto pseudo_vi_command;
case 72: ch = ORD('k'); goto pseudo_vi_command;
case 73: ch = ORD('A'); goto vi_xfunc_search;
case 75: ch = ORD('h'); goto pseudo_vi_command;
case 77: ch = ORD('l'); goto pseudo_vi_command;
case 79: ch = ORD('$'); goto pseudo_vi_command;
case 80: ch = ORD('j'); goto pseudo_vi_command;
case 81: ch = ORD('B'); goto vi_xfunc_search;
case 83: ch = ORD('x'); goto pseudo_vi_command;
default: ch = 0; goto vi_insert_failed;
}
}
if (insert != 0) {
if (ch == CTRL_V) {
state = VLIT;
ch = '^';
ch = ORD('^');
}
switch (vi_insert(ch)) {
case -1:
@ -3699,8 +3735,8 @@ vi_hook(int ch)
save_cbuf();
vs->cursor = 0;
vs->linelen = 0;
if (putbuf(ch == '/' ? "/" : "?", 1,
false) != 0)
if (putbuf(ord(ch) == ORD('/') ?
"/" : "?", 1, false) != 0)
return (-1);
refresh(0);
}
@ -3861,10 +3897,11 @@ vi_hook(int ch)
break;
case VPREFIX2:
vi_xfunc_search_up:
vi_xfunc_search:
state = VFAIL;
switch (ch) {
case 'A':
case ORD('A'):
case ORD('B'):
/* the cursor may not be at the BOL */
if (!vs->cursor)
break;
@ -3873,13 +3910,14 @@ vi_hook(int ch)
vs->cursor = sizeof(srchpat) - 2;
/* anchor the search pattern */
srchpat[0] = '^';
/* take the current line up to the cursor */
memmove(srchpat + 1, vs->cbuf, vs->cursor);
/* take current line up to the cursor */
memcpy(srchpat + 1, vs->cbuf, vs->cursor);
srchpat[vs->cursor + 1] = '\0';
/* set a magic flag */
argc1 = 2 + (int)vs->cursor;
/* and emulate a backwards history search */
lastsearch = '/';
/* and emulate a history search */
/* search backwards if PgUp, forwards for PgDn */
lastsearch = ch == ORD('A') ? '/' : '?';
*curcmd = 'n';
goto pseudo_VCMD;
}
@ -4442,9 +4480,9 @@ vi_cmd(int argcnt, const char *cmd)
/* FALLTHROUGH */
case ORD('n'):
case ORD('N'):
if (lastsearch == ' ')
if (lastsearch == ORD(' '))
return (-1);
if (lastsearch == '?')
if (lastsearch == ORD('?'))
c1 = 1;
else
c1 = 0;
@ -4648,10 +4686,10 @@ domove(int argcnt, const char *cmd, int sub)
/* FALLTHROUGH */
case ORD(','):
case ORD(';'):
if (fsavecmd == ' ')
if (fsavecmd == ORD(' '))
return (-1);
i = ksh_eq(fsavecmd, 'F', 'f');
t = fsavecmd > 'a';
t = rtt2asc(fsavecmd) > rtt2asc('a');
if (*cmd == ',')
t = !t;
if ((ncursor = findch(fsavech, argcnt, tobool(t),
@ -5589,39 +5627,40 @@ x_eval_region_helper(const char *cmd, size_t len)
afree(wds, ATEMP);
strdupx(cp, cp, AEDIT);
} else
/* command cannot be parsed */
cp = NULL;
quitenv(NULL);
return (cp);
}
static int
x_eval_region(int c MKSH_A_UNUSED)
x_operate_region(char *(*helper)(const char *, size_t))
{
char *evbeg, *evend, *cp;
char *rgbeg, *rgend, *cp;
size_t newlen;
/* only for LINE overflow checking */
size_t restlen;
if (xmp == NULL) {
evbeg = xbuf;
evend = xep;
rgbeg = xbuf;
rgend = xep;
} else if (xmp < xcp) {
evbeg = xmp;
evend = xcp;
rgbeg = xmp;
rgend = xcp;
} else {
evbeg = xcp;
evend = xmp;
rgbeg = xcp;
rgend = xmp;
}
x_e_putc2('\r');
x_clrtoeol(' ', false);
x_flush();
x_mode(false);
cp = x_eval_region_helper(evbeg, evend - evbeg);
cp = helper(rgbeg, rgend - rgbeg);
x_mode(true);
if (cp == NULL) {
/* command cannot be parsed */
/* error return from helper */
x_eval_region_err:
x_e_putc2(KSH_BEL);
x_redraw('\r');
@ -5629,20 +5668,49 @@ x_eval_region(int c MKSH_A_UNUSED)
}
newlen = strlen(cp);
restlen = xep - evend;
restlen = xep - rgend;
/* check for LINE overflow, until this is dynamically allocated */
if (evbeg + newlen + restlen >= xend)
if (rgbeg + newlen + restlen >= xend)
goto x_eval_region_err;
xmp = evbeg;
xcp = evbeg + newlen;
xmp = rgbeg;
xcp = rgbeg + newlen;
xep = xcp + restlen;
memmove(xcp, evend, restlen + /* NUL */ 1);
memmove(xcp, rgend, restlen + /* NUL */ 1);
memcpy(xmp, cp, newlen);
afree(cp, AEDIT);
x_adjust();
x_modified();
return (KSTD);
}
static int
x_eval_region(int c MKSH_A_UNUSED)
{
return (x_operate_region(x_eval_region_helper));
}
static char *
x_quote_region_helper(const char *cmd, size_t len)
{
char *s;
size_t newlen;
struct shf shf;
strndupx(s, cmd, len, ATEMP);
newlen = len < 256 ? 256 : 4096;
shf_sopen(alloc(newlen, AEDIT), newlen, SHF_WR | SHF_DYNAMIC, &shf);
shf.areap = AEDIT;
shf.flags |= SHF_ALLOCB;
print_value_quoted(&shf, s);
afree(s, ATEMP);
return (shf_sclose(&shf));
}
static int
x_quote_region(int c MKSH_A_UNUSED)
{
return (x_operate_region(x_quote_region_helper));
}
#endif /* !MKSH_SMALL */
#endif /* !MKSH_NO_CMDLINE_EDITING */

View file

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2009, 2010, 2015, 2016
* Copyright (c) 2009, 2010, 2015, 2016, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -19,7 +19,7 @@
*/
#if defined(EMACSFN_DEFNS)
__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.10 2016/09/01 12:59:09 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.11 2020/04/13 20:46:39 tg Exp $");
#define FN(cname,sname,flags) static int x_##cname(int);
#elif defined(EMACSFN_ENUMS)
#define FN(cname,sname,flags) XFUNC_##cname,
@ -89,6 +89,9 @@ FN(nl_next_com, "newline-and-next", 0)
FN(noop, "no-op", 0)
FN(prev_com, "up-history", XF_ARG)
FN(prev_histword, "prev-hist-word", XF_ARG)
#ifndef MKSH_SMALL
FN(quote_region, "quote-region", 0)
#endif
FN(search_char_back, "search-character-backward", XF_ARG)
FN(search_char_forw, "search-character-forward", XF_ARG)
FN(search_hist, "search-history", 0)

View file

@ -2,7 +2,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
* 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -23,7 +24,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.219 2018/01/14 01:29:47 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.230 2020/04/07 23:14:41 tg Exp $");
/*
* string expansion
@ -50,13 +51,17 @@ typedef struct {
bool split;
} Expand;
#define XBASE 0 /* scanning original */
#define XSUB 1 /* expanding ${} string */
#define XARGSEP 2 /* ifs0 between "$*" */
#define XARG 3 /* expanding $*, $@ */
#define XCOM 4 /* expanding $() */
#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
#define XSUBMID 6 /* middle of expanding ${} */
#define XBASE 0 /* scanning original string */
#define XARGSEP 1 /* ifs0 between "$*" */
#define XARG 2 /* expanding $*, $@ */
#define XCOM 3 /* expanding $() */
#define XNULLSUB 4 /* "$@" when $# is 0, so don't generate word */
#define XSUB 5 /* expanding ${} string */
#define XSUBMID 6 /* middle of expanding ${}; must be XSUB+1 */
#define XSUBPAT 7 /* expanding [[ x = ${} ]] string */
#define XSUBPATMID 8 /* middle, must be XSUBPAT+1 */
#define isXSUB(t) ((t) == XSUB || (t) == XSUBPAT)
/* States used for field splitting */
#define IFS_WORD 0 /* word has chars (or quotes except "$@") */
@ -71,7 +76,7 @@ typedef struct {
#define STYPE_SINGLE 0x2FF
#define STYPE_MASK 0x300
static int varsub(Expand *, const char *, const char *, int *, int *);
static int varsub(Expand *, const char *, const char *, unsigned int *, int *);
static int comsub(Expand *, const char *, int);
static char *valsub(struct op *, Area *);
static char *trimsub(char *, char *, int);
@ -205,7 +210,7 @@ typedef struct SubType {
struct SubType *prev; /* old type */
struct SubType *next; /* poped type (to avoid re-allocating) */
size_t base; /* start position of expanded word */
short stype; /* [=+-?%#] action after expanded word */
unsigned short stype; /* [=+-?%#] action after expanded word */
short f; /* saved value of f (DOPAT, etc) */
uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */
uint8_t quotew; /* saved value of quote (for ${..[+-=]..}) */
@ -334,7 +339,7 @@ expand(
Xcheck(ds, dp);
*dp++ = *sp++;
}
if ((unsigned int)c == ORD('}'))
if ((unsigned int)c == ORD(/*{*/'}'))
*dp++ = ';';
*dp++ = c;
} else {
@ -381,7 +386,7 @@ expand(
*/
/* skip the { or x (}) */
const char *varname = ++sp;
int stype;
unsigned int stype;
int slen = 0;
/* skip variable */
@ -437,17 +442,8 @@ expand(
sp += slen;
switch (stype & STYPE_SINGLE) {
case ORD('#') | STYPE_AT:
x.str = shf_smprintf("%08X",
(unsigned int)hash(str_val(st->var)));
case ORD('Q') | STYPE_AT:
break;
case ORD('Q') | STYPE_AT: {
struct shf shf;
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
print_value_quoted(&shf, str_val(st->var));
x.str = shf_sclose(&shf);
break;
}
case ORD('0'): {
char *beg, *mid, *end, *stg;
mksh_ari_t from = 0, num = -1, flen, finc = 0;
@ -497,10 +493,11 @@ expand(
}
case ORD('/') | STYPE_AT:
case ORD('/'): {
char *s, *p, *d, *sbeg, *end;
char *pat = NULL, *rrep = null;
char *s, *p, *d, *sbeg;
char *pat = NULL, *rrep;
char fpat = 0, *tpat1, *tpat2;
char *ws, *wpat, *wrep;
char *ws, *wpat, *wrep, tch;
size_t rreplen;
s = ws = wdcopy(sp, ATEMP);
p = s + (wdscan(sp, ADELIM) - sp);
@ -516,11 +513,17 @@ expand(
ctype(s[1], C_SUB2))
fpat = s[1];
wpat = s + (fpat ? 2 : 0);
wrep = d ? p : NULL;
if (!(stype & STYPE_AT)) {
rrep = wrep ? evalstr(wrep,
DOTILDE | DOSCALAR) :
null;
if (!(wrep = d ? p : NULL)) {
rrep = null;
rreplen = 0;
} else if (!(stype & STYPE_AT)) {
rrep = evalstr(wrep,
DOTILDE | DOSCALAR);
rreplen = strlen(rrep);
} else {
rrep = NULL;
/* shut up GCC */
rreplen = 0;
}
/* prepare string on which to work */
@ -567,17 +570,17 @@ expand(
*/
if (!gmatchx(sbeg, tpat1, false))
goto end_repl;
end = strnul(s);
d = strnul(s);
/* now anchor the beginning of the match */
if (ord(fpat) != ORD('#'))
while (sbeg <= end) {
while (sbeg <= d) {
if (gmatchx(sbeg, tpat2, false))
break;
else
sbeg++;
}
/* now anchor the end of the match */
p = end;
p = d;
if (ord(fpat) != ORD('%'))
while (p >= sbeg) {
bool gotmatch;
@ -590,22 +593,56 @@ expand(
break;
p--;
}
strndupx(end, sbeg, p - sbeg, ATEMP);
record_match(end);
afree(end, ATEMP);
if (stype & STYPE_AT) {
if (rrep != null)
afree(rrep, ATEMP);
rrep = wrep ? evalstr(wrep,
DOTILDE | DOSCALAR) :
null;
/* record partial string as match */
tch = *p;
*p = '\0';
record_match(sbeg);
*p = tch;
/* get replacement string, if necessary */
if ((stype & STYPE_AT) &&
rrep != null) {
afree(rrep, ATEMP);
/* might access match! */
rrep = evalstr(wrep,
DOTILDE | DOSCALAR);
rreplen = strlen(rrep);
}
/*
* string:
* |--------|---------|-------\0
* s n1 sbeg n2 p n3 d
*
* replacement:
* |------------|
* rrep rreplen
*/
/* move strings around and replace */
{
size_t n1 = sbeg - s;
size_t n2 = p - sbeg;
size_t n3 = d - p;
/* move part3 to the front, OR… */
if (rreplen < n2)
memmove(sbeg + rreplen,
p, n3 + 1);
/* … adjust size, move to back */
if (rreplen > n2) {
s = aresize(s,
n1 + rreplen + n3 + 1,
ATEMP);
memmove(s + n1 + rreplen,
s + n1 + n2,
n3 + 1);
}
/* insert replacement */
if (rreplen)
memcpy(s + n1, rrep, rreplen);
/* continue after the place */
sbeg = s + n1 + rreplen;
}
strndupx(end, s, sbeg - s, ATEMP);
d = shf_smprintf(Tf_sss, end, rrep, p);
afree(end, ATEMP);
sbeg = d + (sbeg - s) + strlen(rrep);
afree(s, ATEMP);
s = d;
if (stype & STYPE_AT) {
afree(tpat1, ATEMP);
afree(pat, ATEMP);
@ -769,11 +806,22 @@ expand(
errorf(Tf_sD_s, st->var->name,
debunk(dp, dp, strlen(dp) + 1));
break;
case ORD('#') | STYPE_AT:
x.str = shf_smprintf("%08X",
(unsigned int)hash(str_val(st->var)));
goto common_CSUBST;
case ORD('Q') | STYPE_AT: {
struct shf shf;
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
print_value_quoted(&shf, str_val(st->var));
x.str = shf_sclose(&shf);
goto common_CSUBST;
}
case ORD('0'):
case ORD('/') | STYPE_AT:
case ORD('/'):
case ORD('#') | STYPE_AT:
case ORD('Q') | STYPE_AT:
common_CSUBST:
dp = Xrestpos(ds, dp, st->base);
type = XSUB;
word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
@ -822,9 +870,69 @@ expand(
}
continue;
case XSUBPAT:
case XSUBPATMID:
XSUBPAT_beg:
switch ((c = ord(*x.str++))) {
case 0:
goto XSUB_end;
case ORD('\\'):
if ((c = ord(*x.str)) == 0)
/* keep backslash at EOS */
c = ORD('\\');
else
++x.str;
break;
/* ctype(c, C_PATMO) */
case ORD('!'):
case ORD('*'):
case ORD('+'):
case ORD('?'):
case ORD('@'):
if (ord(*x.str) == ORD('('/*)*/)) {
++x.str;
c |= 0x80U;
make_magic = true;
}
break;
case ORD('('):
c = ORD(' ') | 0x80U;
/* FALLTHROUGH */
case ORD('|'):
case ORD(')'):
make_magic = true;
break;
}
break;
case XSUB:
if (!quote && (f & DODBMAGIC)) {
const char *cs = x.str;
int level = 0;
while ((c = *cs++))
switch (c) {
case '\\':
if ((c = *cs))
++cs;
break;
case ORD('('):
++level;
break;
case ORD(')'):
--level;
break;
}
/* balanced parentheses? */
if (!level) {
type = XSUBPAT;
goto XSUBPAT_beg;
}
}
/* FALLTHROUGH */
case XSUBMID:
if ((c = ord(*x.str++)) == 0) {
XSUB_end:
type = XBASE;
if (f & DOBLANK)
doblank--;
@ -980,23 +1088,17 @@ expand(
Xinit(ds, dp, 128, ATEMP);
} else if (c == 0) {
return;
} else if (type == XSUB && ctype(c, C_IFS) &&
} else if (isXSUB(type) && ctype(c, C_IFS) &&
!ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
*(cp = alloc(1, ATEMP)) = '\0';
XPput(*wp, cp);
type = XSUBMID;
++type;
}
if (word != IFS_NWS)
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
} else {
if (type == XSUB) {
if (word == IFS_NWS &&
Xlength(ds, dp) == 0) {
*(cp = alloc(1, ATEMP)) = '\0';
XPput(*wp, cp);
}
type = XSUBMID;
}
if (isXSUB(type))
++type;
/* age tilde_ok info - ~ code tests second bit */
tilde_ok <<= 1;
@ -1111,16 +1213,20 @@ hasnonempty(const char **strv)
*/
static int
varsub(Expand *xp, const char *sp, const char *word,
int *stypep, /* becomes qualifier type */
int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */
/* becomes qualifier type */
unsigned int *stypep,
/* becomes qualifier type len (=, :=, etc.) valid iff *stypep != 0 */
int *slenp)
{
int c;
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
int stype; /* substitution type */
unsigned int c;
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
unsigned int stype; /* substitution type */
int slen = 0;
const char *p;
struct tbl *vp;
bool zero_ok = false;
int sc;
XPtrV wv;
if ((stype = ord(sp[0])) == '\0')
/* Bad variable name */
@ -1128,108 +1234,166 @@ varsub(Expand *xp, const char *sp, const char *word,
xp->var = NULL;
/* entirety of named array? */
if ((p = cstrchr(sp, '[')) && (sc = ord(p[1])) &&
ord(p[2]) == ORD(']'))
/* keep p (for ${!foo[1]} below)! */
switch (sc) {
case ORD('*'):
sc = 3;
break;
case ORD('@'):
sc = 7;
break;
default:
/* bit2 = @, bit1 = array, bit0 = enabled */
sc = 0;
}
else
/* $* and $@ checked below */
sc = 0;
/*-
* ${#var}, string length (-U: characters, +U: octets) or array size
* ${%var}, string width (-U: screen columns, +U: octets)
* ${#var}, string length (-U: characters, +U: octets) or array size
* ${!var}, variable name
* ${*} -> set flag for argv
* ${@} -> set flag for argv
*/
c = ord(sp[1]);
if ((unsigned int)stype == ORD('%') && c == '\0')
return (-1);
if (ctype(stype, C_SUB2) && c != '\0') {
/* Can't have any modifiers for ${#...} or ${%...} */
if (ctype(stype, C_SUB2 | CiVAR1)) {
switch (stype) {
case ORD('*'):
if (!sc)
sc = 1;
goto nopfx;
case ORD('@'):
if (!sc)
sc = 5;
goto nopfx;
}
/* varname required */
if ((c = ord(sp[1])) == '\0') {
if (stype == ORD('%'))
/* $% */
return (-1);
/* $# or $! */
goto nopfx;
}
/* cant have any modifiers for ${#…} or ${%…} or ${!…} */
if (*word != CSUBST)
return (-1);
sp++;
/* Check for size of array */
if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
int n = 0;
if ((unsigned int)stype != ORD('#'))
/* check for argv past prefix */
if (!sc) switch (c) {
case ORD('*'):
sc = 1;
break;
case ORD('@'):
sc = 5;
break;
}
/* skip past prefix */
++sp;
/* determine result */
switch (stype) {
case ORD('!'):
if (sc & 2) {
stype = 0;
XPinit(wv, 32);
vp = global(arrayname(sp));
do {
if (vp->flag & ISSET)
XPput(wv, shf_smprintf(Tf_lu,
arrayindex(vp)));
} while ((vp = vp->u.array));
goto arraynames;
}
xp->var = global(sp);
/* use saved p from above */
xp->str = p ? shf_smprintf("%s[%lu]", xp->var->name,
arrayindex(xp->var)) : xp->var->name;
break;
#ifdef DEBUG
default:
internal_errorf("stype mismatch");
/* NOTREACHED */
#endif
case ORD('%'):
/* cannot do this on an array */
if (sc)
return (-1);
vp = global(arrayname(sp));
if (vp->flag & (ISSET|ARRAY))
zero_ok = true;
for (; vp; vp = vp->u.array)
if (vp->flag & ISSET)
n++;
c = n;
} else if ((unsigned int)c == ORD('*') ||
(unsigned int)c == ORD('@')) {
if ((unsigned int)stype != ORD('#'))
return (-1);
c = e->loc->argc;
} else {
p = str_val(global(sp));
zero_ok = p != null;
if ((unsigned int)stype == ORD('#'))
c = utflen(p);
else {
/* partial utf_mbswidth reimplementation */
const char *s = p;
unsigned int wc;
size_t len;
int cw;
c = 0;
while (*s) {
if (!UTFMODE || (len = utf_mbtowc(&wc,
s)) == (size_t)-1)
/* not UTFMODE or not UTF-8 */
wc = rtt2asc(*s++);
else
/* UTFMODE and UTF-8 */
s += len;
/* wc == char or wchar at s++ */
if ((cw = utf_wcwidth(wc)) == -1) {
/* 646, 8859-1, 10646 C0/C1 */
c = -1;
break;
}
c += cw;
/* partial utf_mbswidth reimplementation */
sc = 0;
while (*p) {
if (!UTFMODE ||
(wv.len = utf_mbtowc(&c, p)) == (size_t)-1)
/* not UTFMODE or not UTF-8 */
c = rtt2asc(*p++);
else
/* UTFMODE and UTF-8 */
p += wv.len;
/* c == char or wchar at p++ */
if ((slen = utf_wcwidth(c)) == -1) {
/* 646, 8859-1, 10646 C0/C1 */
sc = -1;
break;
}
sc += slen;
}
if (0)
/* FALLTHROUGH */
case ORD('#'):
switch (sc & 3) {
case 3:
vp = global(arrayname(sp));
if (vp->flag & (ISSET|ARRAY))
zero_ok = true;
sc = 0;
do {
if (vp->flag & ISSET)
sc++;
} while ((vp = vp->u.array));
break;
case 1:
sc = e->loc->argc;
break;
default:
p = str_val(global(sp));
zero_ok = p != null;
sc = utflen(p);
break;
}
/* ${%var} also here */
if (Flag(FNOUNSET) && sc == 0 && !zero_ok)
errorf(Tf_parm, sp);
xp->str = shf_smprintf(Tf_d, sc);
break;
}
if (Flag(FNOUNSET) && c == 0 && !zero_ok)
errorf(Tf_parm, sp);
/* unqualified variable/string substitution */
*stypep = 0;
xp->str = shf_smprintf(Tf_d, c);
return (XSUB);
}
if ((unsigned int)stype == ORD('!') && c != '\0' && *word == CSUBST) {
sp++;
if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
c = ORD('!');
stype = 0;
goto arraynames;
}
xp->var = global(sp);
xp->str = p ? shf_smprintf("%s[%lu]",
xp->var->name, arrayindex(xp->var)) : xp->var->name;
*stypep = 0;
return (XSUB);
}
nopfx:
/* Check for qualifiers in word part */
/* check for qualifiers in word part */
stype = 0;
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
if ((unsigned int)c == ORD(':')) {
/*slen = 0;*/
c = word[/*slen +*/ 0] == CHAR ? ord(word[/*slen +*/ 1]) : 0;
if (c == ORD(':')) {
slen += 2;
stype = STYPE_DBL;
c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
}
if (!stype && (unsigned int)c == ORD('/')) {
if (!stype && c == ORD('/')) {
slen += 2;
stype = c;
if (word[slen] == ADELIM &&
ord(word[slen + 1]) == (unsigned int)c) {
ord(word[slen + 1]) == c) {
slen += 2;
stype |= STYPE_DBL;
}
} else if (stype == STYPE_DBL && ((unsigned int)c == ORD(' ') ||
(unsigned int)c == ORD('0'))) {
} else if (stype == STYPE_DBL && (c == ORD(' ') || c == ORD('0'))) {
stype |= ORD('0');
} else if (ctype(c, C_SUB1)) {
slen += 2;
@ -1238,12 +1402,11 @@ varsub(Expand *xp, const char *sp, const char *word,
/* Note: ksh88 allows :%, :%%, etc */
slen += 2;
stype = c;
if (word[slen + 0] == CHAR &&
ord(word[slen + 1]) == (unsigned int)c) {
if (word[slen + 0] == CHAR && ord(word[slen + 1]) == c) {
stype |= STYPE_DBL;
slen += 2;
}
} else if ((unsigned int)c == ORD('@')) {
} else if (c == ORD('@')) {
/* @x where x is command char */
switch (c = ord(word[slen + 2]) == CHAR ?
ord(word[slen + 3]) : 0) {
@ -1262,84 +1425,70 @@ varsub(Expand *xp, const char *sp, const char *word,
if (!stype && *word != CSUBST)
return (-1);
c = ord(sp[0]);
if ((unsigned int)c == ORD('*') || (unsigned int)c == ORD('@')) {
switch (stype & STYPE_SINGLE) {
/* can't assign to a vector */
case ORD('='):
/* can't trim a vector (yet) */
case ORD('%'):
case ORD('#'):
case ORD('?'):
case ORD('0'):
case ORD('/') | STYPE_AT:
case ORD('/'):
case ORD('#') | STYPE_AT:
case ORD('Q') | STYPE_AT:
return (-1);
}
if (e->loc->argc == 0) {
xp->str = null;
xp->var = global(sp);
state = (unsigned int)c == ORD('@') ? XNULLSUB : XSUB;
} else {
xp->u.strv = (const char **)e->loc->argv + 1;
xp->str = *xp->u.strv++;
/* $@ */
xp->split = tobool((unsigned int)c == ORD('@'));
state = XARG;
}
/* POSIX 2009? */
zero_ok = true;
} else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
XPtrV wv;
switch (stype & STYPE_SINGLE) {
/* can't assign to a vector */
case ORD('='):
/* can't trim a vector (yet) */
case ORD('%'):
case ORD('#'):
case ORD('?'):
case ORD('0'):
case ORD('/') | STYPE_AT:
case ORD('/'):
case ORD('#') | STYPE_AT:
case ORD('Q') | STYPE_AT:
return (-1);
}
c = 0;
arraynames:
XPinit(wv, 32);
vp = global(arrayname(sp));
for (; vp; vp = vp->u.array) {
if (!(vp->flag&ISSET))
continue;
XPput(wv, (unsigned int)c == ORD('!') ?
shf_smprintf(Tf_lu, arrayindex(vp)) :
str_val(vp));
}
if (XPsize(wv) == 0) {
xp->str = null;
state = ord(p[1]) == ORD('@') ? XNULLSUB : XSUB;
XPfree(wv);
} else {
XPput(wv, 0);
xp->u.strv = (const char **)XPptrv(wv);
xp->str = *xp->u.strv++;
/* ${foo[@]} */
xp->split = tobool(ord(p[1]) == ORD('@'));
state = XARG;
}
} else {
if (!sc) {
xp->var = global(sp);
xp->str = str_val(xp->var);
/* can't assign things like $! or $1 */
if ((unsigned int)(stype & STYPE_SINGLE) == ORD('=') &&
if ((stype & STYPE_SINGLE) == ORD('=') &&
!*xp->str && ctype(*sp, C_VAR1 | C_DIGIT))
return (-1);
state = XSUB;
} else {
/* cant assign/trim a vector (yet) */
switch (stype & STYPE_SINGLE) {
case ORD('-'):
case ORD('+'):
/* allowed ops */
case 0:
/* or no ops */
break;
/* case ORD('='):
case ORD('?'):
case ORD('#'):
case ORD('%'):
case ORD('/'):
case ORD('/') | STYPE_AT:
case ORD('0'):
case ORD('#') | STYPE_AT:
case ORD('Q') | STYPE_AT:
*/ default:
return (-1);
}
/* do what we can */
if (sc & 2) {
XPinit(wv, 32);
vp = global(arrayname(sp));
do {
if (vp->flag & ISSET)
XPput(wv, str_val(vp));
} while ((vp = vp->u.array));
arraynames:
if ((c = (XPsize(wv) == 0)))
XPfree(wv);
else {
XPput(wv, NULL);
xp->u.strv = (const char **)XPptrv(wv);
}
} else {
if ((c = (e->loc->argc == 0)))
xp->var = global(sp);
else
xp->u.strv = (const char **)e->loc->argv + 1;
/* POSIX 2009? */
zero_ok = true;
}
/* have we got any elements? */
if (c) {
/* no */
xp->str = null;
state = sc & 4 ? XNULLSUB : XSUB;
} else {
/* yes → load first */
xp->str = *xp->u.strv++;
/* $@ or ${foo[@]} */
xp->split = tobool(sc & 4);
state = XARG;
}
}
c = stype & STYPE_CHAR;
@ -1348,15 +1497,15 @@ varsub(Expand *xp, const char *sp, const char *word,
(((stype & STYPE_DBL) ? *xp->str == '\0' : xp->str == null) &&
(state != XARG || (ifs0 || xp->split ?
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
ctype(c, C_EQUAL | C_MINUS | C_QUEST) : (unsigned int)c == ORD('+')))) ||
(unsigned int)stype == (ORD('0') | STYPE_DBL) ||
(unsigned int)stype == (ORD('#') | STYPE_AT) ||
(unsigned int)stype == (ORD('Q') | STYPE_AT) ||
(unsigned int)(stype & STYPE_CHAR) == ORD('/'))
ctype(c, C_EQUAL | C_MINUS | C_QUEST) : c == ORD('+')))) ||
stype == (ORD('0') | STYPE_DBL) ||
stype == (ORD('#') | STYPE_AT) ||
stype == (ORD('Q') | STYPE_AT) ||
(stype & STYPE_CHAR) == ORD('/'))
/* expand word instead of variable value */
state = XBASE;
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
(ctype(c, C_SUB2) || (state != XBASE && (unsigned int)c != ORD('+'))))
(ctype(c, C_SUB2) || (state != XBASE && c != ORD('+'))))
errorf(Tf_parm, sp);
*stypep = stype;
*slenp = slen;
@ -1950,7 +2099,7 @@ valsub(struct op *t, Area *ap)
newenv(E_FUNC);
newblock();
if (ap)
vp = local("REPLY", false);
vp = local(TREPLY, false);
if (!kshsetjmp(e->jbuf))
execute(t, XXCOM | XERROK, NULL);
if (vp)

View file

@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
* 2019
* 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -24,7 +24,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.206 2019/03/01 16:17:53 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.223 2020/04/07 23:14:41 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@ -108,8 +108,8 @@ execute(struct op * volatile t,
if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
cp = NULL;
dp = shf_smprintf(Tf_ss, evalstr(t->vars[0],
DOASNTILDE | DOSCALAR), rv ? null : cp);
strdup2x(dp, evalstr(t->vars[0], DOASNTILDE | DOSCALAR),
rv ? null : cp);
typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
/* free the expanded value */
afree(cp, APERM);
@ -127,7 +127,7 @@ execute(struct op * volatile t,
timex_hook(t, &up);
ap = (const char **)up;
if (ap[0])
tp = findcom(ap[0], FC_BI|FC_FUNC);
tp = findcom(ap[0], FC_BI | FC_FUNC);
}
flags &= ~XTIME;
@ -446,7 +446,7 @@ execute(struct op * volatile t,
if (rv == ENOEXEC)
scriptexec(t, (const char **)up);
else
errorf(Tf_sD_s, t->str, cstrerror(rv));
errorfx(126, Tf_sD_s, t->str, cstrerror(rv));
}
Break:
exstat = rv & 0xFF;
@ -464,14 +464,9 @@ execute(struct op * volatile t,
unwind(LEXIT);
if (rv != 0 && !(flags & XERROK) &&
(xerrok == NULL || !*xerrok)) {
if (Flag(FERREXIT) & 0x80) {
/* inside eval */
Flag(FERREXIT) = 0;
} else {
trapsig(ksh_SIGERR);
if (Flag(FERREXIT))
unwind(LERROR);
}
trapsig(ksh_SIGERR);
if (Flag(FERREXIT))
unwind(LERREXT);
}
return (rv);
}
@ -492,7 +487,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
static struct op texec;
int type_flags;
bool resetspec;
int fcflags = FC_BI|FC_FUNC|FC_PATH;
int fcflags = FC_BI | FC_FUNC | FC_PATH;
struct block *l_expand, *l_assign;
int optc;
const char *exec_argv0 = NULL;
@ -529,7 +524,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
resetspec = false;
while (tp && tp->type == CSHELL) {
/* undo effects of command */
fcflags = FC_BI|FC_FUNC|FC_PATH;
fcflags = FC_BI | FC_FUNC | FC_PATH;
if (tp->val.f == c_builtin) {
if ((cp = *++ap) == NULL ||
(!strcmp(cp, "--") && (cp = *++ap) == NULL)) {
@ -578,7 +573,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
/* command -vV or something */
break;
/* don't look for functions */
fcflags = FC_BI|FC_PATH;
fcflags = FC_BI | FC_PATH;
if (saw_p) {
if (Flag(FRESTRICTED)) {
warningf(true, Tf_sD_s,
@ -608,8 +603,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
(tp->flag & LOWER_BI)) {
struct tbl *ext_cmd;
ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC);
if (ext_cmd && (ext_cmd->type != CTALIAS ||
ext_cmd = findcom(tp->name, FC_FUNC | FC_PATH);
if (ext_cmd && (ext_cmd->type == CFUNC ||
(ext_cmd->flag & ISSET)))
tp = ext_cmd;
}
@ -619,7 +614,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
break;
} else
break;
tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
tp = findcom(ap[0], fcflags & (FC_BI | FC_FUNC));
}
if (t->u.evalflags & DOTCOMEXEC)
flags |= XEXEC;
@ -629,12 +624,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
else {
/* create new variable/function block */
newblock();
/* ksh functions don't keep assignments, POSIX functions do. */
if (!resetspec && tp && tp->type == CFUNC &&
!(tp->flag & FKSH))
type_flags = EXPORT;
else
type_flags = LOCAL|LOCAL_COPY|EXPORT;
/* all functions keep assignments */
type_flags = LOCAL | LOCAL_COPY | EXPORT;
}
l_assign = e->loc;
if (exec_clrenv)
@ -694,11 +685,9 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
/* shell built-in */
case CSHELL:
do_call_builtin:
if (l_expand != l_assign)
l_assign->flags |= (tp->flag & NEXTLOC_BI);
rv = call_builtin(tp, (const char **)ap, null, resetspec);
if (resetspec && tp->val.f == c_shift) {
l_expand->argc = l_assign->argc;
l_expand->argv = l_assign->argv;
}
break;
/* function call */
@ -804,6 +793,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
switch (i) {
case LRETURN:
case LERROR:
case LERREXT:
rv = exstat & 0xFF;
break;
case LINTR:
@ -1147,6 +1137,10 @@ builtin(const char *name, int (*func) (const char **))
/* is declaration utility (POSIX: export, readonly) */
flag |= DECL_UTIL;
break;
case '#':
/* is set or shift */
flag |= NEXTLOC_BI;
break;
default:
goto flags_seen;
}
@ -1309,10 +1303,10 @@ search_access(const char *fn, int mode)
}
#ifdef __OS2__
/* check if path is something we want to find, adding executable extensions */
#define search_access(fn, mode) access_ex((search_access), (fn), (mode))
/* check if path is something we want to find adding executable extensions */
#define search_access(fn,mode) access_ex((search_access), (fn), (mode))
#else
#define search_access(fn, mode) (search_access)((fn), (mode))
#define search_access(fn,mode) (search_access)((fn), (mode))
#endif
/*
@ -1605,7 +1599,7 @@ hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
return (-2);
}
if (iop->ioflag & IOHERESTR) {
ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC);
ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR);
} else if (sub) {
/* do substitutions on the content of heredoc */
s = pushs(SSTRING, ATEMP);
@ -1682,7 +1676,7 @@ static const char *
do_selectargs(const char **ap, bool print_menu)
{
static const char *read_args[] = {
Tread, "-r", "REPLY", NULL
Tread, Tdr, TREPLY, NULL
};
char *s;
int i, argct;
@ -1696,13 +1690,13 @@ do_selectargs(const char **ap, bool print_menu)
* - the user enters a blank line
* - the REPLY parameter is empty
*/
if (print_menu || !*str_val(global("REPLY")))
if (print_menu || !*str_val(global(TREPLY)))
pr_menu(ap);
shellf(Tf_s, str_val(global("PS3")));
if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect,
false))
return (NULL);
if (*(s = str_val(global("REPLY"))))
if (*(s = str_val(global(TREPLY))))
return ((getn(s, &i) && i >= 1 && i <= argct) ?
ap[i - 1] : null);
print_menu = true;
@ -1861,8 +1855,11 @@ dbteste_getopnd(Test_env *te, Test_op op, bool do_eval)
if (!do_eval)
return (null);
if (op == TO_STEQL || op == TO_STNEQ)
if (op == TO_STEQL || op == TO_STNEQ) {
flags |= DOPAT;
if (!Flag(FSH))
flags |= DODBMAGIC;
}
return (evalstr(s, flags));
}

View file

@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2016, 2017, 2018
* 2011, 2012, 2013, 2014, 2016, 2017, 2018, 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.105 2018/08/10 02:53:33 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.107 2020/03/27 02:49:40 tg Exp $");
#define EXPRTOK_DEFNS
#include "exprtok.h"
@ -864,7 +864,8 @@ ksh_access(const char *fn, int mode)
int rv;
struct stat sb;
if ((rv = access(fn, mode)) == 0 && kshuid == 0 && (mode & X_OK) &&
if ((rv = access(fn, mode)) == 0 && (mode & X_OK) &&
(kshuid == 0 || ksheuid == 0) &&
(rv = stat(fn, &sb)) == 0 && !S_ISDIR(sb.st_mode) &&
(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
rv = -1;
@ -885,8 +886,43 @@ static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems,
unsigned int val) MKSH_A_PURE;
/*
* Generated from the UCD 11.0.0 by
* MirOS: contrib/code/Snippets/eawparse,v 1.12 2017/09/06 16:05:45 tg Exp $
* Generated from the UCD 13.0.0 by
* MirOS: contrib/code/Snippets/eawparse,v 1.14 2020/03/27 01:33:21 tg Exp $
*/
/*-
* Parts Copyright © 19912020 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 },

View file

@ -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 = */

View file

@ -5,7 +5,8 @@
/*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
* 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
* 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -38,7 +39,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.355 2018/10/20 21:04:28 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.372 2020/04/13 19:51:07 tg Exp $");
#if HAVE_KILLPG
/*
@ -119,8 +120,6 @@ const struct builtin mkshbuiltins[] = {
{Tfalse, c_false},
{"fc", c_fc},
{Tgetopts, c_getopts},
/* deprecated, replaced by typeset -g */
{"^=global", c_typeset},
{Tjobs, c_jobs},
{"kill", c_kill},
{"let", c_let},
@ -133,8 +132,8 @@ const struct builtin mkshbuiltins[] = {
#endif
{"~rename", c_rename},
{"*=return", c_exitreturn},
{Tsgset, c_set},
{"*=shift", c_shift},
{Tsghset, c_set},
{"*=#shift", c_shift},
{Tgsource, c_dot},
#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
{Tsuspend, c_suspend},
@ -183,11 +182,8 @@ struct kill_info {
int name_width;
};
static const struct t_op {
char op_text[4];
Test_op op_num;
} u_ops[] = {
{"-a", TO_FILAXST },
const struct t_op u_ops[] = {
/* 0*/ {"-a", TO_FILAXST },
{"-b", TO_FILBDEV },
{"-c", TO_FILCDEV },
{"-d", TO_FILID },
@ -199,22 +195,23 @@ static const struct t_op {
{"-h", TO_FILSYM },
{"-k", TO_FILSTCK },
{"-L", TO_FILSYM },
{"-n", TO_STNZE },
/*12*/ {"-n", TO_STNZE },
{"-O", TO_FILUID },
{"-o", TO_OPTION },
/*14*/ {"-o", TO_OPTION },
{"-p", TO_FILFIFO },
{"-r", TO_FILRD },
/*16*/ {"-r", TO_FILRD },
{"-S", TO_FILSOCK },
{"-s", TO_FILGZ },
{"-t", TO_FILTT },
{"-u", TO_FILSETU },
/*20*/ {"-u", TO_FILSETU },
{"-v", TO_ISSET },
{"-w", TO_FILWR },
{"-x", TO_FILEX },
/*23*/ {"-x", TO_FILEX },
{"-z", TO_STZER },
{"", TO_NONOP }
};
static const struct t_op b_ops[] = {
cta(u_ops_size, NELEM(u_ops) == 26);
const struct t_op b_ops[] = {
{"=", TO_STEQL },
{"==", TO_STEQL },
{"!=", TO_STNEQ },
@ -337,7 +334,7 @@ c_print(const char **wp)
/* BSD "echo" cmd, Debian Policy 10.4 compliant */
++wp;
bsd_echo:
if (*wp && !strcmp(*wp, "-n")) {
if (*wp && !strcmp(*wp, Tdn)) {
po.nl = false;
++wp;
}
@ -724,12 +721,16 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
}
break;
case CALIAS:
if (vflag) {
shprintf("%s is an %s%s for ", id,
if (!vflag && iscommand)
shprintf(Tf_s_, Talias);
if (vflag || iscommand)
print_value_quoted(shl_stdout, id);
if (vflag)
shprintf(" is an %s%s for ",
(tp->flag & EXPORT) ? "exported " : "",
Talias);
} else if (iscommand)
shprintf("%s %s=", Talias, id);
else if (iscommand)
shf_putc('=', shl_stdout);
print_value_quoted(shl_stdout, tp->val.s);
break;
case CKEYWD:
@ -751,10 +752,15 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
bool
valid_alias_name(const char *cp)
{
if (ord(*cp) == ORD('-'))
return (false);
if (ord(cp[0]) == ORD('[') && ord(cp[1]) == ORD('[') && !cp[2])
switch (ord(*cp)) {
case ORD('+'):
case ORD('-'):
return (false);
case ORD('['):
if (ord(cp[1]) == ORD('[') && !cp[2])
return (false);
break;
}
while (*cp)
if (ctype(*cp, C_ALIAS))
++cp;
@ -843,7 +849,7 @@ c_alias(const char **wp)
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
if (pflag)
shprintf(Tf_s_, Talias);
shf_puts(ap->name, shl_stdout);
print_value_quoted(shl_stdout, ap->name);
if (prefix != '+') {
shf_putc('=', shl_stdout);
print_value_quoted(shl_stdout, ap->val.s);
@ -873,7 +879,7 @@ c_alias(const char **wp)
if (ap != NULL && (ap->flag&ISSET)) {
if (pflag)
shprintf(Tf_s_, Talias);
shf_puts(ap->name, shl_stdout);
print_value_quoted(shl_stdout, ap->name);
if (prefix != '+') {
shf_putc('=', shl_stdout);
print_value_quoted(shl_stdout, ap->val.s);
@ -1297,54 +1303,32 @@ c_bind(const char **wp)
#ifndef MKSH_SMALL
bool macro = false;
#endif
bool list = false;
const char *cp;
char *up;
while ((optc = ksh_getopt(wp, &builtin_opt,
#ifndef MKSH_SMALL
"lm"
#else
"l"
#endif
)) != -1)
if (x_bind_check()) {
bi_errorf("can't bind, not a tty");
return (1);
}
while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != -1)
switch (optc) {
case 'l':
list = true;
break;
return (x_bind_list());
#ifndef MKSH_SMALL
case 'm':
macro = true;
break;
#endif
case '?':
default:
return (1);
}
wp += builtin_opt.optind;
if (*wp == NULL)
/* list all */
rv = x_bind(NULL, NULL,
#ifndef MKSH_SMALL
false,
#endif
list);
return (x_bind_showall());
for (; *wp != NULL; wp++) {
if ((cp = cstrchr(*wp, '=')) == NULL)
up = NULL;
else {
strdupx(up, *wp, ATEMP);
up[cp++ - *wp] = '\0';
}
if (x_bind(up ? up : *wp, cp,
#ifndef MKSH_SMALL
macro,
#endif
false))
rv = 1;
afree(up, ATEMP);
}
do {
rv |= x_bind(*wp SMALLP(macro));
} while (*++wp);
return (rv);
}
@ -1353,10 +1337,17 @@ c_bind(const char **wp)
int
c_shift(const char **wp)
{
struct block *l = e->loc;
int n;
mksh_ari_t val;
const char *arg;
struct block *l = e->loc;
if ((l->flags & BF_RESETSPEC)) {
/* prevent pollution */
l->flags &= ~BF_RESETSPEC;
/* operate on parent environment */
l = l->next;
}
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return (1);
@ -1375,6 +1366,7 @@ c_shift(const char **wp)
bi_errorf(Tf_sD_s, Tbadnum, arg);
return (1);
}
if (l->argc < n) {
bi_errorf("nothing to shift");
return (1);
@ -1589,7 +1581,6 @@ c_wait(const char **wp)
return (rv);
}
static const char REPLY[] = "REPLY";
int
c_read(const char **wp)
{
@ -1670,7 +1661,7 @@ c_read(const char **wp)
if (!builtin_opt.optarg[0])
fd = 0;
else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
bi_errorf(Tf_sD_sD_s, "-u", builtin_opt.optarg, ccp);
bi_errorf(Tf_sD_sD_s, Tdu, builtin_opt.optarg, ccp);
return (2);
}
break;
@ -1679,7 +1670,7 @@ c_read(const char **wp)
}
wp += builtin_opt.optind;
if (*wp == NULL)
*--wp = REPLY;
*--wp = TREPLY;
if (intoarray && wp[1] != NULL) {
bi_errorf(Ttoo_many_args);
@ -2029,7 +2020,6 @@ int
c_eval(const char **wp)
{
struct source *s, *saves = source;
unsigned char savef;
int rv;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
@ -2072,10 +2062,7 @@ c_eval(const char **wp)
/* SUSv4: OR with a high value never written otherwise */
exstat |= 0x4000;
savef = Flag(FERREXIT);
Flag(FERREXIT) |= 0x80;
rv = shell(s, 2);
Flag(FERREXIT) = savef;
source = saves;
afree(s, ATEMP);
if (exstat & 0x4000)
@ -2237,7 +2224,13 @@ c_set(const char **wp)
int argi;
bool setargs;
struct block *l = e->loc;
const char **owp;
if ((l->flags & BF_RESETSPEC)) {
/* prevent pollution */
l->flags &= ~BF_RESETSPEC;
/* operate on parent environment */
l = l->next;
}
if (wp[1] == NULL) {
static const char *args[] = { Tset, "-", NULL };
@ -2248,6 +2241,8 @@ c_set(const char **wp)
return (2);
/* set $# and $* */
if (setargs) {
const char **owp;
wp += argi - 1;
owp = wp;
/* save $0 */
@ -2579,25 +2574,25 @@ c_mknod(const char **wp)
#endif
/*-
test(1) roughly accepts the following grammar:
oexpr ::= aexpr | aexpr "-o" oexpr ;
aexpr ::= nexpr | nexpr "-a" aexpr ;
nexpr ::= primary | "!" nexpr ;
primary ::= unary-operator operand
| operand binary-operator operand
| operand
| "(" oexpr ")"
;
unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
"-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
"-u"|"-v"|"-w"|"-x"|"-z";
binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
"-lt"|"-le"|"-ef"|"-nt"|"-ot";
operand ::= <anything>
*/
* test(1) roughly accepts the following grammar:
* oexpr ::= aexpr | aexpr "-o" oexpr ;
* aexpr ::= nexpr | nexpr "-a" aexpr ;
* nexpr ::= primary | "!" nexpr ;
* primary ::= unary-operator operand
* | operand binary-operator operand
* | operand
* | "(" oexpr ")"
* ;
*
* unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
* "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
* "-u"|"-v"|"-w"|"-x"|"-z";
*
* binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
* "-lt"|"-le"|"-ef"|"-nt"|"-ot";
*
* operand ::= <anything>
*/
/* POSIX says > 1 for errors */
#define T_ERR_EXIT 2
@ -2749,13 +2744,36 @@ test_isop(Test_meta meta, const char *s)
}
#ifdef __OS2__
#define test_access(name, mode) access_ex(access, (name), (mode))
#define test_stat(name, buffer) stat_ex((name), (buffer))
#define test_access(name,mode) access_ex(access, (name), (mode))
#define test_stat(name,buffer) stat_ex(stat, (name), (buffer))
#define test_lstat(name,buffer) stat_ex(lstat, (name), (buffer))
#else
#define test_access(name, mode) access((name), (mode))
#define test_stat(name, buffer) stat((name), (buffer))
#define test_access(name,mode) access((name), (mode))
#define test_stat(name,buffer) stat((name), (buffer))
#define test_lstat(name,buffer) lstat((name), (buffer))
#endif
#if HAVE_ST_MTIM
#undef st_mtimensec
#define st_mtimensec st_mtim.tv_nsec
#endif
static int
mtimecmp(const struct stat *sb1, const struct stat *sb2)
{
if (sb1->st_mtime < sb2->st_mtime)
return (-1);
if (sb1->st_mtime > sb2->st_mtime)
return (1);
#if (HAVE_ST_MTIMENSEC || HAVE_ST_MTIM)
if (sb1->st_mtimensec < sb2->st_mtimensec)
return (-1);
if (sb1->st_mtimensec > sb2->st_mtimensec)
return (1);
#endif
return (0);
}
int
test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
bool do_eval)
@ -2849,31 +2867,31 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -d */
case TO_FILID:
return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
return (test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
/* -c */
case TO_FILCDEV:
return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
return (test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
/* -b */
case TO_FILBDEV:
return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
return (test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
/* -p */
case TO_FILFIFO:
return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
return (test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
/* -h or -L */
case TO_FILSYM:
#ifdef MKSH__NO_SYMLINK
return (0);
#else
return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
return (test_lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
#endif
/* -S */
case TO_FILSOCK:
return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
return (test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
/* -H => HP context dependent files (directories) */
case TO_FILCDF:
@ -2892,7 +2910,7 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
*/
nv = shf_smprintf("%s+", opnd1);
i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
i = (test_stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
afree(nv, ATEMP);
return (i);
}
@ -2902,18 +2920,18 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -u */
case TO_FILSETU:
return (stat(opnd1, &b1) == 0 &&
return (test_stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISUID) == S_ISUID);
/* -g */
case TO_FILSETG:
return (stat(opnd1, &b1) == 0 &&
return (test_stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISGID) == S_ISGID);
/* -k */
case TO_FILSTCK:
#ifdef S_ISVTX
return (stat(opnd1, &b1) == 0 &&
return (test_stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISVTX) == S_ISVTX);
#else
return (0);
@ -2921,7 +2939,8 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -s */
case TO_FILGZ:
return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
return (test_stat(opnd1, &b1) == 0 &&
(off_t)b1.st_size > (off_t)0);
/* -t */
case TO_FILTT:
@ -2934,11 +2953,13 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -O */
case TO_FILUID:
return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
return (test_stat(opnd1, &b1) == 0 &&
(uid_t)b1.st_uid == ksheuid);
/* -G */
case TO_FILGID:
return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
return (test_stat(opnd1, &b1) == 0 &&
(gid_t)b1.st_gid == kshegid);
/*
* Binary Operators
@ -2976,9 +2997,9 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
* ksh88/ksh93 succeed if file2 can't be stated
* (subtly different from 'does not exist').
*/
return (stat(opnd1, &b1) == 0 &&
(((s = stat(opnd2, &b2)) == 0 &&
b1.st_mtime > b2.st_mtime) || s < 0));
return (test_stat(opnd1, &b1) == 0 &&
(((s = test_stat(opnd2, &b2)) == 0 &&
mtimecmp(&b1, &b2) > 0) || s < 0));
/* -ot */
case TO_FILOT:
@ -2986,13 +3007,14 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
* ksh88/ksh93 succeed if file1 can't be stated
* (subtly different from 'does not exist').
*/
return (stat(opnd2, &b2) == 0 &&
(((s = stat(opnd1, &b1)) == 0 &&
b1.st_mtime < b2.st_mtime) || s < 0));
return (test_stat(opnd2, &b2) == 0 &&
(((s = test_stat(opnd1, &b1)) == 0 &&
mtimecmp(&b1, &b2) < 0) || s < 0));
/* -ef */
case TO_FILEQ:
return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
return (test_stat(opnd1, &b1) == 0 &&
test_stat(opnd2, &b2) == 0 &&
b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
/* all other cases */
@ -3155,7 +3177,7 @@ ptest_isa(Test_env *te, Test_meta meta)
{
/* Order important - indexed by Test_meta values */
static const char * const tokens[] = {
"-o", "-a", "!", "(", ")"
Tdo, Tda, "!", "(", ")"
};
Test_op rv;
@ -3469,7 +3491,7 @@ c_cat(const char **wp)
#define MKSH_CAT_BUFSIZ 4096
/* parse options: POSIX demands we support "-u" as no-op */
while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
while ((rv = ksh_getopt(wp, &builtin_opt, Tu)) != -1) {
switch (rv) {
case 'u':
/* we already operate unbuffered */

View file

@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2014, 2015, 2016, 2017, 2018
* 2011, 2012, 2014, 2015, 2016, 2017, 2018, 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -27,7 +27,7 @@
#include <sys/file.h>
#endif
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.167 2018/04/28 17:16:54 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.169 2019/09/16 21:10:33 tg Exp $");
Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign;
@ -504,6 +504,8 @@ findhist(int start, int fwd, const char *str, bool anchored)
void
sethistsize(mksh_ari_t n)
{
if (n > 65535)
n = 65535;
if (n > 0 && n != histsize) {
int cursize = histptr - history;

View file

@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
* 2012, 2013, 2014, 2015, 2016, 2018
* 2012, 2013, 2014, 2015, 2016, 2018, 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.127 2018/07/15 16:23:10 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.128 2019/12/11 19:46:20 tg Exp $");
#if HAVE_KILLPG
#define mksh_killpg killpg
@ -88,6 +88,7 @@ struct proc {
typedef struct job Job;
struct job {
ALLOC_ITEM alloc_INT; /* internal, do not touch */
Job *next; /* next job in list */
Proc *proc_list; /* process list */
Proc *last_proc; /* last process in list */
@ -1778,16 +1779,26 @@ new_job(void)
if (free_jobs != NULL) {
newj = free_jobs;
free_jobs = free_jobs->next;
} else
newj = alloc(sizeof(Job), APERM);
} else {
char *cp;
/*
* struct job includes ALLOC_ITEM for alignment constraints
* so first get the actually used memory, then assign it
*/
cp = alloc(sizeof(Job) - sizeof(ALLOC_ITEM), APERM);
/* undo what alloc() did to the malloc result address */
newj = (void *)(cp - sizeof(ALLOC_ITEM));
}
/* brute force method */
for (i = 1; ; i++) {
for (j = job_list; j && j->job != i; j = j->next)
;
if (j == NULL)
break;
}
i = 0;
do {
++i;
j = job_list;
while (j && j->job != i)
j = j->next;
} while (j);
newj->job = i;
return (newj);

View file

@ -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 != ' ') {

View file

@ -6,7 +6,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
* 2019
* 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -35,7 +35,7 @@
#include <locale.h>
#endif
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.351 2019/01/05 13:24:18 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.364 2020/04/13 17:04:14 tg Exp $");
#ifndef MKSHRC_PATH
#define MKSHRC_PATH "~/.mkshrc"
@ -65,8 +65,8 @@ static const char initsubs[] =
"${EPOCHREALTIME=}";
static const char *initcoms[] = {
Ttypeset, "-r", initvsn, NULL,
Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
Ttypeset, Tdr, initvsn, NULL,
Ttypeset, Tdx, "HOME", TPATH, TSHELL, NULL,
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias,
"integer=\\\\builtin typeset -i",
@ -90,7 +90,7 @@ static const char *initcoms[] = {
};
static const char *restr_com[] = {
Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
Ttypeset, Tdr, TPATH, TENV, TSHELL, NULL
};
static bool initio_done;
@ -100,7 +100,6 @@ static struct env env;
struct env *e = &env;
/* compile-time assertions */
#define cta(name, expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
/* this one should be defined by the standard */
cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
@ -227,7 +226,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
int argi, i;
Source *s = NULL;
struct block *l;
unsigned char restricted_shell, errexit, utf_flag;
unsigned char restricted_shell = 0, errexit, utf_flag;
char *cp;
const char *ccp, **wp;
struct tbl *vp;
@ -303,7 +302,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
/* define built-in commands and see if we were called as one */
ktinit(APERM, &builtins,
/* currently up to 54 builtins: 75% of 128 = 2^7 */
/* currently up to 52 builtins: 75% of 128 = 2^7 */
7);
for (i = 0; mkshbuiltins[i].name != NULL; i++)
if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
@ -315,7 +314,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
argi = parse_args(argv, OF_FIRSTTIME, NULL);
if (argi < 0)
return (1);
/* called as rsh, rmksh, -rsh, -rmksh, etc.? */
if (ord(*ccp) == ORD('r')) {
++ccp;
++restricted_shell;
}
#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
/* are we called as -sh or /bin/sh or so? */
if (!strcmp(ccp, "sh" MKSH_EXE_EXT)) {
@ -642,7 +645,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
}
/* Disable during .profile/ENV reading */
restricted_shell = Flag(FRESTRICTED);
restricted_shell |= Flag(FRESTRICTED);
Flag(FRESTRICTED) = 0;
errexit = Flag(FERREXIT);
Flag(FERREXIT) = 0;
@ -656,7 +659,16 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
if (Flag(FLOGIN))
include(MKSH_SYSTEM_PROFILE, 0, NULL, true);
if (!Flag(FPRIVILEGED)) {
if (Flag(FPRIVILEGED)) {
include(MKSH_SUID_PROFILE, 0, NULL, true);
/* note whether -p was enabled during startup */
if (Flag(FPRIVILEGED) == 1)
/* allow set -p to setuid() later */
Flag(FPRIVILEGED) = 3;
else
/* turn off -p if not set explicitly */
change_flag(FPRIVILEGED, OF_INTERNAL, false);
} else {
if (Flag(FLOGIN))
include(substitute("$HOME/.profile", 0), 0, NULL, true);
if (Flag(FTALKING)) {
@ -664,13 +676,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
if (cp[0] != '\0')
include(cp, 0, NULL, true);
}
} else {
include(MKSH_SUID_PROFILE, 0, NULL, true);
/* turn off -p if not set explicitly */
if (Flag(FPRIVILEGED) != 1)
change_flag(FPRIVILEGED, OF_INTERNAL, false);
}
if (restricted_shell) {
c_builtin(restr_com);
/* After typeset command... */
@ -741,6 +747,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
switch (i) {
case LRETURN:
case LERROR:
case LERREXT:
/* see below */
return (exstat & 0xFF);
case LINTR:
@ -808,6 +815,8 @@ shell(Source * volatile s, volatile int level)
int i;
newenv(level == 2 ? E_EVAL : E_PARSE);
if (level == 2)
e->flags |= EF_IN_EVAL;
if (interactive)
really_exit = false;
switch ((i = kshsetjmp(e->jbuf))) {
@ -815,18 +824,21 @@ shell(Source * volatile s, volatile int level)
break;
case LBREAK:
case LCONTIN:
if (level != 2) {
source = old_source;
quitenv(NULL);
internal_errorf(Tf_cant_s, Tshell,
i == LBREAK ? Tbreak : Tcontinue);
/* assert: interactive == false */
source = old_source;
quitenv(NULL);
if (level == 2) {
/* keep on going */
unwind(i);
/* NOTREACHED */
}
/* assert: interactive == false */
/* FALLTHROUGH */
internal_errorf(Tf_cant_s, Tshell,
i == LBREAK ? Tbreak : Tcontinue);
/* NOTREACHED */
case LINTR:
/* we get here if SIGINT not caught or ignored */
case LERROR:
case LERREXT:
case LSHELL:
if (interactive) {
if (i == LINTR)
@ -857,6 +869,8 @@ shell(Source * volatile s, volatile int level)
case LRETURN:
source = old_source;
quitenv(NULL);
if (i == LERREXT && level == 2)
return (exstat & 0xFF);
/* keep on going */
unwind(i);
/* NOTREACHED */
@ -916,8 +930,8 @@ shell(Source * volatile s, volatile int level)
source_no_tree:
reclaim();
}
quitenv(NULL);
source = old_source;
quitenv(NULL);
return (exstat & 0xFF);
}
@ -926,36 +940,25 @@ shell(Source * volatile s, volatile int level)
void
unwind(int i)
{
/*
* This is a kludge. We need to restore everything that was
* changed in the new environment, see cid 1005090337C7A669439
* and 10050903386452ACBF1, but fail to even save things most of
* the time. funcs.c:c_eval() changes FERREXIT temporarily to 0,
* which needs to be restored thus (related to Debian #696823).
* We did not save the shell flags, so we use a special or'd
* value here... this is mostly to clean up behind *other*
* callers of unwind(LERROR) here; exec.c has the regular case.
*/
if (Flag(FERREXIT) & 0x80) {
/* GNU bash does not run this trapsig */
trapsig(ksh_SIGERR);
Flag(FERREXIT) &= ~0x80;
}
/* during eval, skip FERREXIT trap */
if (i == LERREXT && (e->flags & EF_IN_EVAL))
goto defer_traps;
/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
if (i == LEXIT || ((i == LERROR || i == LINTR) &&
if (i == LEXIT || ((i == LERROR || i == LERREXT || i == LINTR) &&
sigtraps[ksh_SIGEXIT].trap &&
(!Flag(FTALKING) || Flag(FERREXIT)))) {
++trap_nested;
runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
--trap_nested;
i = LLEAVE;
} else if (Flag(FERREXIT) == 1 && (i == LERROR || i == LINTR)) {
} else if (Flag(FERREXIT) && (i == LERROR || i == LERREXT || i == LINTR)) {
++trap_nested;
runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
--trap_nested;
i = LLEAVE;
}
defer_traps:
while (/* CONSTCOND */ 1) {
switch (e->type) {
@ -998,8 +1001,7 @@ newenv(int type)
ep->temps = NULL;
ep->yyrecursive_statep = NULL;
ep->type = type;
ep->flags = 0;
/* jump buffer is invalid because flags == 0 */
ep->flags = e->flags & EF_IN_EVAL;
e = ep;
}
@ -1336,6 +1338,39 @@ bi_errorf(const char *fmt, ...)
}
}
/*
* Used by functions called by builtins and not:
* identical to errorfx if first argument is nil,
* like bi_errorf storing the errorlevel into it otherwise
*/
void
maybe_errorf(int *ep, int rc, const char *fmt, ...)
{
va_list va;
/* debugging: note that stdout not valid */
shl_stdout_ok = false;
exstat = rc;
va_start(va, fmt);
vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
(ep ? VWARNINGF_BUILTIN : 0), fmt, va);
va_end(va);
if (!ep)
goto and_out;
*ep = rc;
/* POSIX special builtins cause non-interactive shells to exit */
if (builtin_spec) {
builtin_argv0 = NULL;
/* may not want to use LERROR here */
and_out:
unwind(LERROR);
}
}
/* Called when something that shouldn't happen does */
void
internal_errorf(const char *fmt, ...)
@ -1435,7 +1470,7 @@ initio(void)
if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
errorf("can't get home directory");
lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
strpathx(lfp, lfp, "mksh-dbg.txt", 1);
}
if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
@ -1997,7 +2032,7 @@ init_environ(void)
errno = 0;
if ((dent = readdir(dirp)) != NULL) {
if (skip_varname(dent->d_name, true)[0] == '\0') {
xp = shf_smprintf(Tf_sSs, MKSH_ENVDIR, dent->d_name);
strpathx(xp, MKSH_ENVDIR, dent->d_name, 1);
if (!(shf = shf_open(xp, O_RDONLY, 0, 0))) {
warningf(false,
"cannot read environment %s from %s: %s",

View file

@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019
* mirabilos <m@mirbsd.org>
* Copyright (c) 2015
* Daniel Richard G. <skunk@iSKUNK.ORG>
@ -32,7 +32,7 @@
#include <grp.h>
#endif
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.293 2018/08/10 02:53:35 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.297 2020/04/07 11:56:46 tg Exp $");
#define KSH_CHVT_FLAG
#ifdef MKSH_SMALL
@ -62,13 +62,13 @@ static int make_path(const char *, const char *, char **, XString *, int *);
#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
/* we don't need to check for other codes, EPERM won't happen */
#define DO_SETUID(func, argvec) do { \
#define DO_SETUID(func,argvec) do { \
if ((func argvec) && errno == EAGAIN) \
errorf("%s failed with EAGAIN, probably due to a" \
" too low process limit; aborting", #func); \
} while (/* CONSTCOND */ 0)
#else
#define DO_SETUID(func, argvec) func argvec
#define DO_SETUID(func,argvec) func argvec
#endif
@ -216,70 +216,85 @@ getoptions(void)
void
change_flag(enum sh_flag f, int what, bool newset)
{
unsigned char oldval;
unsigned char oldval = Flag(f);
unsigned char newval = (newset ? 1 : 0);
if (f == FXTRACE) {
change_xtrace(newval, true);
return;
}
oldval = Flag(f);
Flag(f) = newval = (newset ? 1 : 0);
#ifndef MKSH_UNEMPLOYED
if (f == FMONITOR) {
if (what != OF_CMDLINE && newval != oldval)
j_change();
} else
#endif
#ifndef MKSH_NO_CMDLINE_EDITING
if ((
#if !MKSH_S_NOVI
f == FVI ||
#endif
f == FEMACS || f == FGMACS) && newval) {
#if !MKSH_S_NOVI
Flag(FVI) =
#endif
Flag(FEMACS) = Flag(FGMACS) = 0;
Flag(f) = newval;
} else
#endif
if (f == FPRIVILEGED && oldval && !newval) {
/* Turning off -p? */
} else if (f == FPRIVILEGED) {
if (!oldval)
/* no getting back dropped privs */
return;
else if (!newval) {
/* turning off -p */
kshegid = kshgid;
ksheuid = kshuid;
} else if (oldval != 3)
/* nor going full sugid */
goto change_flag;
/*XXX this can probably be optimised */
kshegid = kshgid = getgid();
ksheuid = kshuid = getuid();
/* +++ set group IDs +++ */
#if HAVE_SETRESUGID
DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
#if HAVE_SETGROUPS
/* setgroups doesn't EAGAIN on Linux */
setgroups(1, &kshegid);
#endif
DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
DO_SETUID(setresgid, (kshegid, kshegid, kshgid));
#else /* !HAVE_SETRESUGID */
/* setgid, setegid, seteuid don't EAGAIN on Linux */
/* setgid, setegid don't EAGAIN on Linux */
setgid(kshegid);
#ifndef MKSH__NO_SETEUGID
setegid(kshegid);
#endif
#endif /* !MKSH__NO_SETEUGID */
#endif /* !HAVE_SETRESUGID */
/* +++ wipe groups vector +++ */
#if HAVE_SETGROUPS
/* setgroups doesn't EAGAIN on Linux */
setgroups(0, NULL);
#endif /* HAVE_SETGROUPS */
/* +++ set user IDs +++ */
#if HAVE_SETRESUGID
DO_SETUID(setresuid, (ksheuid, ksheuid, kshuid));
#else /* !HAVE_SETRESUGID */
/* seteuid doesn't EAGAIN on Linux */
DO_SETUID(setuid, (ksheuid));
#ifndef MKSH__NO_SETEUGID
seteuid(ksheuid);
#endif
#endif /* !MKSH__NO_SETEUGID */
#endif /* !HAVE_SETRESUGID */
/* +++ privs changed +++ */
} else if ((f == FPOSIX || f == FSH) && newval) {
/* Turning on -o posix or -o sh? */
Flag(FBRACEEXPAND) = 0;
/* Turning on -o posix? */
if (f == FPOSIX) {
if (f == FPOSIX)
/* C locale required for compliance */
UTFMODE = 0;
}
} else if (f == FTALKING) {
/* Turning on -o posix or -o sh? */
Flag(FBRACEEXPAND) = 0;
#ifndef MKSH_NO_CMDLINE_EDITING
} else if ((f == FEMACS ||
#if !MKSH_S_NOVI
f == FVI ||
#endif
f == FGMACS) && newval) {
#if !MKSH_S_NOVI
Flag(FVI) = 0;
#endif
Flag(FEMACS) = Flag(FGMACS) = 0;
#endif
}
change_flag:
Flag(f) = newval;
if (f == FTALKING) {
/* Changing interactive flag? */
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
Flag(FTALKING_I) = newval;
#ifndef MKSH_UNEMPLOYED
} else if (f == FMONITOR) {
if (what != OF_CMDLINE && newval != oldval)
j_change();
#endif
}
}
@ -1674,14 +1689,13 @@ do_realpath(const char *upath)
if (getdrvwd(&ldest, ord(*upath)))
return (NULL);
/* A:foo -> A:/cwd/foo; A: -> A:/cwd */
ipath = shf_smprintf(Tf_sss, ldest,
upath[2] ? "/" : "", upath + 2);
strpathx(ipath, ldest, upath + 2, 0);
#endif
} else {
/* upath is a relative pathname, prepend cwd */
if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
return (NULL);
ipath = shf_smprintf(Tf_sss, tp, "/", upath);
strpathx(ipath, tp, upath, 1);
afree(tp, ATEMP);
}
@ -1783,7 +1797,7 @@ do_realpath(const char *upath)
assemble_symlink:
#endif
/* append rest of current input path to link target */
tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
strpathx(tp, ldest, ip, 0);
afree(ipath, ATEMP);
ip = ipath = tp;
if (!mksh_abspath(ipath)) {
@ -2199,8 +2213,7 @@ c_cd(const char **wp)
tryp = NULL;
if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
!getdrvwd(&tryp, ord(*dir))) {
dir = shf_smprintf(Tf_sss, tryp,
dir[2] ? "/" : "", dir + 2);
strpathx(dir, tryp, dir + 2, 0);
afree(tryp, ATEMP);
afree(allocd, ATEMP);
allocd = dir;

1853
src/mksh.1

File diff suppressed because it is too large Load diff

620
src/mksh.faq Normal file
View file

@ -0,0 +1,620 @@
RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.6 2020/04/13 20:46:39 tg Exp $
ToC: spelling
Title: How do you spell <tt>mksh</tt>? How do you pronounce it?
<p>This <a href="@@RELPATH@@mksh.htm">shell</a> is spelt either
“<tt>mksh</tt>” (with, even at the beginning of a sentence, <a
href="https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Capital_letters#Items_that_require_initial_lower_case">an
initial lowercase letter</a>; this is important) or “MirBSD Korn Shell”,
possibly with “the”.</p>
<p>I usually pronounce it as “<span xml:lang="de-DE-1901">em-ka-es-ha</span>”,
that is, the letters individually in my native German, or say “MirBSD Korn
Shell”, although it is manageable, mostly for Slavic speakers, to actually
say “mksh” as if it were a word ☺</p>
<p>Oh… Ive run into this one, didnt I? “MirBSD” is pronounced “<span
xml:lang="de-DE-1901">Mir-Be-Es-De</span>” germanically, for anglophones
“Mir-beastie” is fine.</p>
----
ToC: sowhatismksh
Title: Im a $OS (<i>Android, OS/2, …</i>) user, so whats 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. Its
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 shellbasically, the one
running commands at system startup, in the background, and on user
behalf (but never of its own). Any privilege pop-ups you might <a
href="https://forum.xda-developers.com/showthread.php?t=1963976">be
encountering</a> are therefore <a
href="https://forum.xda-developers.com/showpost.php?p=33550523&amp;postcount=1553">not
caused by mksh</a> but by some other code invoking mksh to do something
on its behalf.</p>
----
ToC: os2
Title: Im 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 komhs port on Hobbes, or from his OS/2
Factory on eComStation Korea, which uses “textmode”, though. Most OS/2
users will want to use “textmode” unless they need absolute compatibility
with Unix mksh and other Unix shells and tools.</p>
----
ToC: kornshell
Title: How does this relate to ksh or the Korn Shell?
<p>The Korn Shell (AT&amp;T ksh) was authored by David Korn; two major
flavours exist (ksh88 and ksh93), the latter having been maintained
until 2012 (last formal release) and 2014 (last beta snapshot, buggy).
A ksh86 did exist.</p>
<p>Theres now <tt>ksh2020</tt>, a project having restarted development
around November 2017 forking the last <tt>ksh93 v-</tt> (beta) snapshot
and continuing to develop it, presented at FOSDEM.</p>
<p>AT&amp;T ksh88 is “the (original) Korn Shell”. Other implementations,
of varying quality (MKS Toolkits MKS ksh being named as an example of
the lower end, MirBSDs mksh at the upper end). They are all <em>not</em>
“Korn Shell” or “ksh”. However, mksh got blessed by David Korn, as long
as it cannot be confused with the original Korn Shell.</p>
<p>The POSIX shell standard, while lacking most Korn Shell features, was
largely based on AT&amp;T ksh88, with some from the Bourne shell.</p>
<p>mksh is the currently active development of what started as the Public
Domain Bourne Shell in the mid-1980s with ksh88-compatibl-ish extensions
having been added later, making the Public Domain Korn Shell (pdksh),
which, while never officially blessed, was the only way for most to get
a Korn Shell-like command interpreter for AT&amp;Ts was proprietary,
closed-source code for a very long time. pdkshs 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 OpenBSDs cleanup, adding compatibility to other operating systems
back on top of it, and after 2004, independent, massive development of
bugfixes including a complete reorganisation of the way the parser works,
and of new features both independent and compatible with other shells
(ksh93, GNU bash, zsh, BSD csh) started and was followed by working with
the group behind POSIX to fix issues both in the standard and in mksh.
mksh became the system shell in several other operating systems and Linux
distributions and Android and thus is likely the Korn shell, if not Unix
shell, flavour with the largest user base. It has replaced pdksh in all
contemporary systems except QNX, NetBSD® and OpenBSD (who continue to
maintain their variant on “low flame”).</p>
<p>dtksh is the “Desktop Korn Shell”, a build of AT&amp;T ksh93 with some
additional built-in utilities for graphics programming (windows, menu
bars, dialogue boxes, etc.) utilising Motif bindings.</p>
<p>MKS ksh is a proprietary reimplemention aiming for, but not quite
getting close to, ksh88 compatibility.</p>
<p>SKsh is an AmigaOS-specific Korn Shell-lookalike by Steve Koren.</p>
<p>The <a href="@@RELPATH@@ksh-chan.htm">Homepage of the <tt>#ksh</tt>
channel on Freenode IRC</a> contains more information about the Korn
Shell in general and its flavours.</p>
----
ToC: packaging
Title: How should I package mksh? (common cases)
<p>Export a few environment variables, namely <tt>CC</tt> (the C compiler),
<tt>CPPFLAGS</tt> (all C præprocessor definitions), <tt>CFLAGS</tt> (only
compiler flags, <em>no</em> <tt>-Dfoo</tt> or anything!), <tt>LDFLAGS</tt>
(for anything to pass to the C compiler while linking) and <tt>LIBS</tt>
(appended to the linking command line after everything else. You might
wish to <tt>export LDSTATIC=-static</tt> for a static build as well.</p>
<p>When cross-compiling, <tt>CC</tt> is the <em>cross</em> compiler (mksh
currently does not require a compiler targetting the build system), but
you <em>must</em> also export <tt>TARGET_OS</tt> to whatever system you
are compiling for, e.g. “Linux”. For most operating systems, thats 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 Debians</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> isnt 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 its 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>Its 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 youre using LTO (the <tt>Build.sh</tt> option
<tt>-c lto</tt>) try to disable it firstespecially 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!
Dont break Unix. Read up on the GIGO principle. Duh.
----
ToC: makefile
Title: Why doesnt this use a Makefile to build?
<p>Not all supported target operating environments have a make utility
available, and shell was required for “mirtoconf” (like autoconf)
already anyway, so it was chosen to run the make part as well.</p>
<p>You can, however, add the <tt>-M</tt> flag to your <tt>Build.sh</tt>
invocations to let it produce a <tt>Makefrag.inc</tt> file <em>tailored
for this specific build</em> which you can then include in a Makefile,
such as with the BSD make(1) “.include” command or <a
href="https://www.gnu.org/software/make/manual/make.html#Include">GNU
make</a> equivalent. It even contains, for the user to start out with,
a commented-out example of how to do that in the most basic manner.</p>
----
ToC: oldbsd
Title: Why do other BSDs and QNX still use pdksh instead of mksh?
<p>Some systems are resistent to change, mostly due to bikeshedding
(some people would, for example, rather see all shells banned to
ports/pkgsrc®) and hysterial raisins (historical reasons ☻). Most
BSDs have mksh packages available, and it works on all of them and
QNX just fine.</p>
<p>In fact, on all of these systems, you can replace their 1999-era
<tt>/bin/ksh</tt> (which is a pdksh) with mksh. On at least NetBSD®
1.6 and up (not 1.5) and OpenBSD, even <tt>/bin/sh</tt> is fair game.</p>
<p>MidnightBSD notably has adopted mksh as system shell (thanks laffer1).</p>
----
ToC: openbsd
Title: Why is there no mksh in OpenBSDs ports tree?
OpenBSD dont like people who fork off their project at all; heck,
they dont 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: Id 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 OReilly book (⚠ disclaimer: only an example,
not a recommendation) and going forward by reading scripts (the
“shellsnippets” repository referenced in the <tt>#ksh</tt> channel
homepage (see the top of this document) has many examples) and
trying to understand them and the mksh specifics from the manpage.
----
ToC: ps1conv
Title: My prompt from &lt;<i>some other shell</i>&gt; does not work!
<a href="#contact">Contact</a> us on the mailing list or on IRC,
well 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 youd 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>”: youre 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 doesnt 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.) Its better
to not set <tt>$ENV</tt> if possible and let every shell user their native
startup files; otherwise, you must ensure that it runs under all shells. Check
<tt>$BASH_VERSION</tt> (GNU bash), <tt>$KSH_VERSION</tt> (contains “LEGACY KSH”
or “MIRBSD KSH” for mksh, “PD KSH” for ancient mirbsdksh/oksh/pdksh, “Version”
for ksh93); <tt>$NETBSD_SHELL</tt> (NetBSD ash); <tt>POSH_VERSION</tt> (posh, a
pdksh derivative); <tt>$SH_VERSION</tt> (“PD KSH” as sh), <tt>$YASH_VERSION</tt>
(yash), <tt>$ZSH_VERSION</tt> (or if <tt>$VERSION</tt> begins with “zsh”); a <a
href="@@RELPATH@@ksh-chan.htm#which-shell">list of more approaches</a> exists.
----
ToC: ctrl-l-cls
Title: ^L (Ctrl-L) does not clear the screen
Use ^[^L (Escape+Ctrl-L) or rebind it:<br />
<tt>bind '^L=clear-screen'</tt>
----
ToC: ctrl-u-pico
Title: ^U (Ctrl-U) clears the entire line
If it should only delete the line up to the cursor, use:<br />
<tt>bind -m ^U='^[0^K'</tt>
----
ToC: cur-up-zsh
Title: Cursor Up behaves differently from zsh
Some shells make Cursor Up search in the history only for commands
starting with what was already entered. mksh separates the shortcuts:
Cursor Up goes up one command and PgUp searches the history as described
above. You can, of course, rebind:<br />
<tt>bind '^XA=search-history-up'</tt>
----
ToC: current
Title: Can mksh set the title of the window according to the command running?
Theres 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 418 set a <tt>PS1</tt>
(prompt) equivalent to lines 8496 of the stock <tt>dot.mkshrc</tt>, with
one change: line 15 (<tt>print &gt;/dev/tty …</tt>) is new, inserted just
before the <tt>return</tt> command of the function substitution in the
default prompt; this is what youll 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; well hyperlink to
these engines when they are available.
----
ToC: posix-mode
Title: How POSIX compliant is mksh? Also, UTF-8 vs. locales?
<p>Youll 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. Youll 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. mkshs
<tt>utf8-mode</tt> (which only supports the BMP (Basic Multilingual Plane) of
UCS and maps raw octets into the U+EF80‥U+EFFF wide character range; see
<tt>Arithmetic expressions</tt> in mksh(1) for details) <em>must</em> stay
disabled in POSIX mode (it is disabled upon enabling POSIX mode in R56+).</p>
<p class="boxhead">The following POSIX sh-compatible code toggles the
<tt>utf8-mode</tt> option dependent on the current POSIX locale, for mksh
to allow using the UTF-8 mode, within the constraints outlined above, in
code portable across various shell implementations:</p>
<div class="boxtext">
<pre>
case ${KSH_VERSION:-} in
*MIRBSD KSH*|*LEGACY KSH*)
case ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} in
*[Uu][Tt][Ff]8*|*[Uu][Tt][Ff]-8*) set -U ;;
*) set +U ;;
esac ;;
esac
</pre>
</div><p class="boxfoot">In near future, (UTF-8) locale tracking will
be implemented, though.</p>
<p>The shell is pretty close to POSIX, when run as <tt>lksh -o posix</tt>
under the "C" locale it is intended to match. It does not do everything
like other POSIX-compatible or compliant shells, though.</p>
----
ToC: function-local-scopes
Title: What differences in function-local scopes are there?
<p><tt>mksh</tt> has a different scope model from AT&amp;T <tt>ksh</tt>,
which leads to subtle differences in semantics for identical builtins.
This can cause issues with a <tt>nameref</tt> to suddenly point to a
local variable by accident. (Other common shells share mkshs scoping
model.)</p>
<p class="boxhead">GNU <tt>bash</tt> allows unsetting local variables; in
<tt>mksh</tt>, doing so in a function allows back access to the global
variable (actually the one in the next scope up) with the same name. The
following code, when run before function definitions, changes the behaviour
of <tt>unset</tt> to behave like other shells (the alias can be removed
after the definitions):</p>
<div class="boxtext">
<pre>
case ${KSH_VERSION:-} in
*MIRBSD KSH*|*LEGACY KSH*)
function unset_compat {
\\builtin typeset unset_compat_x
for unset_compat_x in "$@"; do
eval "\\\\builtin unset $unset_compat_x[*]"
done
}
\\builtin alias unset=unset_compat
;;
esac
</pre>
</div><p class="boxfoot">When a local variable is created (e.g. using
<tt>local</tt>, <tt>typeset</tt>, <tt>integer</tt> or
<tt>\\builtin typeset</tt>) it does not, like in other shells, inherit
the value from the global (next scope up) variable with the same name;
it is rather created without any value (unset but defined).</p>
----
ToC: regex-comparison
Title: I get an error in this regex comparison
<p>Use extglobs instead of regexes:<br />
<tt>[[ foo =~ (foo|bar).*baz ]]</tt><br />
 becomes…<br />
<tt>[[ foo = *@(foo|bar)*baz* ]]</tt></p>
----
ToC: extensions-to-avoid
Title: Are there any extensions to avoid?
<p>GNU <tt>bash</tt> supports “<tt>&amp;&gt;</tt>” (and “|&amp;”) to redirect
both stdout and stderr in one go, but this breaks POSIX and Korn Shell syntax;
use POSIX redirections instead:</p>
<table border="1" cellpadding="3">
<tr><td>GNU bash</td><td>
<tt>foo |&amp; bar |&amp; baz &amp;&gt;log</tt>
</td></tr>
<tr><td>POSIX</td><td>
<tt>foo 2&gt;&amp;1 | bar 2&gt;&amp;1 | baz &gt;log 2&gt;&amp;1</tt>
</td></tr>
</table>
----
ToC: while-read-pipe
Title: Something is going wrong with my while...read loop
<p class="boxhead">Most likely, youve 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 wont terminate the function.</p>
<p class="boxhead">Use co-processes instead:</p>
<div class="boxtext">
<pre>
bar | baz |&amp;
while read -p foo; do ...; done
exec 3&gt;&amp;p; exec 3&gt;&amp;-
</pre>
</div><p class="boxfoot">If <tt>read</tt> is run in a way such as
<tt>while read foo; do ...; done</tt> then leading whitespace will be
removed (IFS) and backslashes processed. You might want to use
<tt>while IFS= read -r foo; do ...; done</tt> for pristine I/O.</p>
<p class="boxhead">Similarly, when using the <tt>-a</tt> option, use of the
<tt>-r</tt> option might be prudent (<tt>read -raN-1 arr &lt;file</tt>);
the same applies for NUL-terminated lines:</p>
<div class="boxtext">
<pre>
find . -type f -print0 |&amp; \
while IFS= read -d '' -pr filename; do
print -r -- "found &lt;${filename#./}&gt;"
done
</pre>
</div>
----
ToC: command-alias
Title: “command” doesnt expand aliases as in ksh93
This is because AT&amp;T ksh93 ships a predefined alias enabling this:<br />
<tt>alias command='command '</tt><br />
put this into your <tt>~/.mkshrc</tt>
(note the space before the closing single quote)
----
ToC: builtin-rename
Title: “rename” doesnt work as expected!
<p>Theres a <tt>rename</tt> built-in utility in mksh, which is a very
thin wrapper around the rename(2) syscall. It receives two pathnames,
source and destination where the first is then atomically renamed to
the latter. It does not move, i.e. fails for different filesystems.</p>
<p>The GNU package <tt>util-linux</tt> has a different <tt>rename</tt>
command. If you wish to invoke an external utility (in favour over a
builtin), you can use <tt>dot.mkshrc</tt>s function <tt>enable</tt>
or put the following into your <tt>~/.mkshrc</tt>:</p>
<pre>alias rename="$(whence -p rename)"</pre>
----
ToC: builtin-sleep
Title: “sleep” does not accept m for minutes!
<p>mksh contains a <tt>sleep</tt> built-in utility, in order to be
able to offer sub-second sleep to shell scripts for most platforms.
(It does not exist if the platform lacks select(2)which should
be rare.)</p>
<p>GNU coreutils contains a sleep implementation accepting suffixed
numbers. If you wish to invoke an external utility (in favour over a
builtin), you can use <tt>dot.mkshrc</tt>s function <tt>enable</tt>
or put something along the following lines into <tt>~/.mkshrc</tt>:</p>
<pre>alias sleep="$(whence -p sleep)"</pre>
<pre>timer() { sleep $(($1*60${2:++$2})); } # timer mins [secs]</pre>
<pre>timer() {
local arg=${1/m/'*60+'}
[[ $arg = *+ ]] &amp;&amp; arg+=0
sleep $(($arg)
}</pre>
----
ToC: string-concat
Title: “+=” behaves differently from other shells
<p>In POSIX shell, “=” in code like <tt>var=content</tt> is a string
assignment, always. You can use <tt>var=$((content))</tt> for an
arithmetic assignment that mostly uses C language rules.</p>
<p>It stands to consider that the common shell extension “+=” as in
<tt>var+=content</tt> would always do string concatenation; it does
in mksh, but not in some other shells, in which, when <tt>var</tt> has
been declared integer, addition is done instead.</p>
<p>You can make the code portable by using “((…))” (a.k.a. <tt>let</tt>)
instead: <tt>(( var += content ))</tt> does arithmetic addition in
all shells involved.</p>
----
ToC: set-e
Title: I use “set -e” and my code unexpectedly errors out
<p>I personally recommend people to not use “<tt>set -e</tt>”, as it
makes error handling more difficult. However, some insist. There have
been bugfixes (relative to e.g. oksh/loksh and posh) in this aspect,
and the user has to make sure <tt>$?</tt> is always 0 ASAP even after
a command that doesnt check it.</p>
<pre>istwo() {
for i in "$@"; do
test x"$i" = x"2" &amp;&amp; echo two
done
}
set -e
istwo 1
echo END</pre>
<p>This can be fixed by either adding an explicit “<tt>:</tt>” (or
“<tt>true</tt>”) after the comparison, or even…</p>
<pre>test x"$i" = x"2" &amp;&amp; echo two || :</pre>
<p>… or right after the <tt>done</tt> inside the function, but…</p>
<pre>test x"$i" != x"2" || echo two</pre>
<p>… negating the condition and using “<tt>||</tt>” is preferable.</p>
<p>Remember that Korn shell-style functions (with <tt>function</tt>
keyword and <strong>without</strong> parenthesēs) in AT&amp;T ksh93
and mksh R51 and up have their own shell option scope, but while…</p>
<pre>function istwo {
set +e
}</pre>
<p>… might help in error handling, the return status of a function is
still the last errorlevel inside, so an explicit true (“<tt>:</tt>”)
or, more explicitly, “<tt>return 0</tt>” at its end is still needed
if the <em>caller</em> runs under <tt>set -e</tt>.</p>
----
ToC: set-eo-pipefail
Title: I use “set -eo pipefail” and my code unexpectedly errors out
<p class="boxhead">Related to the above FAQ entry, using
<tt>set -o pipefail</tt> makes the following construct error out:</p>
<div class="boxtext">
<pre>
set -e
for x in 1 2; do
false &amp;&amp; echo $x
done | cat
</pre>
</div><p class="boxfoot">This is because, while the <tt>&amp;&amp;</tt>
ensures that the inner commands failure is not taken, it sets the entire
<tt>for</tt>‥<tt>done</tt> loops 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> wouldnt be amiss ☺ The <a
href="http://www.mail-archive.com/miros-mksh@mirbsd.org/">mailing
list</a> can also be used.
----

View file

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2015, 2017
* Copyright (c) 2015, 2017, 2020
* KO Myung-Hun <komh@chollian.net>
* Copyright (c) 2017
* mirabilos <m@mirbsd.org>
@ -20,6 +20,7 @@
* of said person's immediate fault when using the work as intended.
*/
#define INCL_KBD
#define INCL_DOS
#include <os2.h>
@ -31,7 +32,7 @@
#include <unistd.h>
#include <process.h>
__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.8 2017/12/22 16:41:42 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.10 2020/04/07 11:13:45 tg Exp $");
static char *remove_trailing_dots(char *);
static int access_stat_ex(int (*)(), const char *, void *);
@ -172,6 +173,8 @@ init_extlibpath(void)
void
os2_init(int *argcp, const char ***argvp)
{
KBDINFO ki;
response(argcp, argvp);
init_extlibpath();
@ -183,6 +186,12 @@ os2_init(int *argcp, const char ***argvp)
if (!isatty(STDERR_FILENO))
setmode(STDERR_FILENO, O_BINARY);
/* ensure ECHO mode is ON so that read command echoes. */
memset(&ki, 0, sizeof(ki));
ki.cb = sizeof(ki);
ki.fsMask |= KEYBOARD_ECHO_ON;
KbdSetStatus(&ki, 0);
atexit(cleanup);
}
@ -295,11 +304,12 @@ access_ex(int (*fn)(const char *, int), const char *name, int mode)
return (access_stat_ex(fn, name, (void *)mode));
}
/* stat() version */
/* stat()/lstat() version */
int
stat_ex(const char *name, struct stat *buffer)
stat_ex(int (*fn)(const char *, struct stat *),
const char *name, struct stat *buffer)
{
return (access_stat_ex(stat, name, buffer));
return (access_stat_ex(fn, name, buffer));
}
static int

View file

@ -21,7 +21,7 @@
#ifndef RLIMITS_OPTCS
#if defined(RLIMITS_DEFNS)
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.4 2019/04/24 20:56:31 tg Exp $");
#elif defined(RLIMITS_ITEMS)
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
#endif
@ -82,6 +82,9 @@ FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024, 'B')
#ifdef RLIMIT_PTHREAD
FN("threadsperprocess", RLIMIT_PTHREAD, 1, 'P')
#endif
#ifdef RLIMIT_THREADS
FN("threadsperprocess", RLIMIT_THREADS, 1, 'r')
#endif
#ifdef RLIMIT_NICE
FN("maxnice", RLIMIT_NICE, 1, 'e')
#endif
@ -100,6 +103,9 @@ FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024, 'v')
#ifdef ULIMIT_V_IS_AS
FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
#endif
#ifdef RLIMIT_LOCKS
FN("filelocks", RLIMIT_LOCKS, 1, 'x')
#endif
#undef F0
#undef FN
#undef RLIMITS_DEFNS
@ -158,6 +164,9 @@ FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
#ifdef RLIMIT_RTPRIO
"r"
#endif
#ifdef RLIMIT_THREADS
"r"
#endif
"S"
#ifdef RLIMIT_STACK
"s"
@ -180,5 +189,8 @@ FN("address-space(KiB)", RLIMIT_AS, 1024, 'v')
#ifdef RLIMIT_SWAP
"w"
#endif
#ifdef RLIMIT_LOCKS
"x"
#endif
#undef RLIMITS_OPTCS
#endif

View file

@ -19,7 +19,7 @@
*/
@RLIMITS_DEFNS
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.4 2019/04/24 20:56:31 tg Exp $");
@RLIMITS_ITEMS
#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
@@
@ -86,6 +86,9 @@ FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024
>P|RLIMIT_PTHREAD
FN("threadsperprocess", RLIMIT_PTHREAD, 1
>r|RLIMIT_THREADS
FN("threadsperprocess", RLIMIT_THREADS, 1
>e|RLIMIT_NICE
FN("maxnice", RLIMIT_NICE, 1
@ -102,4 +105,7 @@ FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024
>v|ULIMIT_V_IS_AS
FN("address-space(KiB)", RLIMIT_AS, 1024
>x|RLIMIT_LOCKS
FN("filelocks", RLIMIT_LOCKS, 1
|RLIMITS_OPTCS

214
src/sh.h
View file

@ -10,7 +10,8 @@
/*-
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
* 2019, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -116,7 +117,7 @@
#if (defined(__KLIBC__) || defined(__dietlibc__)) && \
((defined(__GNUC__) && (__GNUC__ > 3)) || defined(__NWCC__))
#undef offsetof
#define offsetof(s, e) __builtin_offsetof(s, e)
#define offsetof(s,e) __builtin_offsetof(s, e)
#endif
#undef __attribute__
@ -170,9 +171,17 @@
#define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p
#define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p)
#ifdef MKSH_DONT_EMIT_IDSTRING
#define __IDSTRING(prefix, string) /* nothing */
#define __IDSTRING(prefix,string) /* nothing */
#elif defined(__ELF__) && defined(__GNUC__) && \
!(defined(__GNUC__) && defined(__mips16) && (__GNUC__ >= 8)) && \
!defined(__llvm__) && !defined(__NWCC__) && !defined(NO_ASM)
#define __IDSTRING(prefix,string) \
__asm__(".section .comment" \
"\n .ascii \"@(\"\"#)" #prefix ": \"" \
"\n .asciz \"" string "\"" \
"\n .previous")
#else
#define __IDSTRING(prefix, string) \
#define __IDSTRING(prefix,string) \
static const char __IDSTRING_EXPAND(__LINE__,prefix) [] \
MKSH_A_USED = "@(""#)" #prefix ": " string
#endif
@ -182,9 +191,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.870 2019/03/01 16:18:14 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.892 2020/04/14 22:45:22 tg Exp $");
#endif
#define MKSH_VERSION "R57 2019/03/01"
#define MKSH_VERSION "R59 2020/04/14"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@ -255,6 +264,16 @@ typedef MKSH_TYPEDEF_SIG_ATOMIC_T sig_atomic_t;
typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
#endif
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
#define MKSH_SHF_NO_INLINE
#endif
/* do not merge these conditionals as neatccs preprocessor is simple */
#ifdef __neatcc__
/* parsing of comma operator <,> in expressions broken */
#define MKSH_SHF_NO_INLINE
#endif
/* un-do vendor damage */
#undef BAD /* AIX defines that somewhere */
@ -264,6 +283,9 @@ typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
#ifndef MKSH_INCLUDES_ONLY
/* compile-time assertions */
#define cta(name,expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
/* EBCDIC fun */
/* see the large comment in shf.c for an EBCDIC primer */
@ -307,7 +329,7 @@ struct rusage {
} while (/* CONSTCOND */ 0)
#endif
#ifndef timeradd
#define timeradd(tvp, uvp, vvp) \
#define timeradd(tvp,uvp,vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
@ -318,7 +340,7 @@ struct rusage {
} while (/* CONSTCOND */ 0)
#endif
#ifndef timersub
#define timersub(tvp, uvp, vvp) \
#define timersub(tvp,uvp,vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
@ -453,7 +475,7 @@ extern int getrusage(int, struct rusage *);
#if !HAVE_MEMMOVE
/* we assume either memmove or bcopy exist, at the moment */
#define memmove(dst, src, len) bcopy((src), (dst), (len))
#define memmove(dst,src,len) bcopy((src), (dst), (len))
#endif
#if !HAVE_REVOKE_DECL
@ -647,7 +669,7 @@ char *ucstrstr(char *, const char *);
#endif
#endif
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 571)
#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 591)
#error Must run Build.sh to compile this.
extern void thiswillneverbedefinedIhope(void);
int
@ -661,20 +683,20 @@ im_sorry_dave(void)
/* use this ipv strchr(s, 0) but no side effects in s! */
#define strnul(s) ((s) + strlen((const void *)s))
#define utf_ptradjx(src, dst) do { \
#define utf_ptradjx(src,dst) do { \
(dst) = (src) + utf_ptradj(src); \
} while (/* CONSTCOND */ 0)
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
#define strdupx(d, s, ap) do { \
#define strdupx(d,s,ap) do { \
(d) = strdup_i((s), (ap)); \
} while (/* CONSTCOND */ 0)
#define strndupx(d, s, n, ap) do { \
#define strndupx(d,s,n,ap) do { \
(d) = strndup_i((s), (n), (ap)); \
} while (/* CONSTCOND */ 0)
#else
/* be careful to evaluate arguments only once! */
#define strdupx(d, s, ap) do { \
#define strdupx(d,s,ap) do { \
const char *strdup_src = (const void *)(s); \
char *strdup_dst = NULL; \
\
@ -685,7 +707,7 @@ im_sorry_dave(void)
} \
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
#define strndupx(d, s, n, ap) do { \
#define strndupx(d,s,n,ap) do { \
const char *strdup_src = (const void *)(s); \
char *strdup_dst = NULL; \
\
@ -698,6 +720,33 @@ im_sorry_dave(void)
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
#endif
#define strdup2x(d,s1,s2) do { \
const char *strdup_src = (const void *)(s1); \
const char *strdup_app = (const void *)(s2); \
size_t strndup_len = strlen(strdup_src); \
size_t strndup_ln2 = strlen(strdup_app) + 1; \
char *strdup_dst = alloc(strndup_len + strndup_ln2, ATEMP); \
\
memcpy(strdup_dst, strdup_src, strndup_len); \
memcpy(strdup_dst + strndup_len, strdup_app, strndup_ln2); \
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
#define strpathx(d,s1,s2,cond) do { \
const char *strdup_src = (const void *)(s1); \
const char *strdup_app = (const void *)(s2); \
size_t strndup_len = strlen(strdup_src) + 1; \
size_t strndup_ln2 = ((cond) || *strdup_app) ? \
strlen(strdup_app) + 1 : 0; \
char *strdup_dst = alloc(strndup_len + strndup_ln2, ATEMP); \
\
memcpy(strdup_dst, strdup_src, strndup_len); \
if (strndup_ln2) { \
strdup_dst[strndup_len - 1] = '/'; \
memcpy(strdup_dst + strndup_len, strdup_app, \
strndup_ln2); \
} \
(d) = strdup_dst; \
} while (/* CONSTCOND */ 0)
#ifdef MKSH_SMALL
#ifndef MKSH_NOPWNAM
@ -839,6 +888,7 @@ extern struct env {
/* struct env.flag values */
#define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */
#define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */
#define EF_IN_EVAL BIT(3) /* inside an eval */
/* Do breaks/continues stop at env type e? */
#define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE || \
@ -851,12 +901,13 @@ extern struct env {
#define LRETURN 1 /* return statement */
#define LEXIT 2 /* exit statement */
#define LERROR 3 /* errorf() called */
#define LLEAVE 4 /* untrappable exit/error */
#define LERREXT 4 /* set -e caused */
#define LINTR 5 /* ^C noticed */
#define LBREAK 6 /* break statement */
#define LCONTIN 7 /* continue statement */
#define LSHELL 8 /* return to interactive shell() */
#define LAEXPR 9 /* error in arithmetic expression */
#define LLEAVE 10 /* untrappable exit/error */
/* sort of shell global state */
EXTERN pid_t procpid; /* PID of executing process */
@ -868,9 +919,9 @@ EXTERN uint8_t trap_nested; /* running nested traps */
EXTERN uint8_t shell_flags[FNFLAGS];
EXTERN const char *kshname; /* $0 */
EXTERN struct {
uid_t kshuid_v; /* real UID of shell */
uid_t kshuid_v; /* real UID of shell at startup */
uid_t ksheuid_v; /* effective UID of shell */
gid_t kshgid_v; /* real GID of shell */
gid_t kshgid_v; /* real GID of shell at startup */
gid_t kshegid_v; /* effective GID of shell */
pid_t kshpgrp_v; /* process group of shell */
pid_t kshppid_v; /* PID of parent of shell */
@ -955,6 +1006,7 @@ EXTERN const char Tsgcontinue[] E_INIT("*=continue");
EXTERN const char Tcreate[] E_INIT("create");
EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected");
EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL");
EXTERN const char TENV[] E_INIT("ENV");
EXTERN const char Tdsgexport[] E_INIT("^*=export");
#define Texport (Tdsgexport + 3)
#ifdef __OS2__
@ -997,10 +1049,11 @@ EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection");
#define Tredirection (Tredirection_dup + 19)
#define Treal_sp1 (Treal_sp2 + 1)
EXTERN const char Treal_sp2[] E_INIT(" real ");
EXTERN const char TREPLY[] E_INIT("REPLY");
EXTERN const char Treq_arg[] E_INIT("requires an argument");
EXTERN const char Tselect[] E_INIT("select");
EXTERN const char Tsgset[] E_INIT("*=set");
#define Tset (Tf_parm + 18)
EXTERN const char Tsghset[] E_INIT("*=#set");
#define Tsh (Tmksh + 2)
#define TSHELL (TEXECSHELL + 4)
#define Tshell (Ttoo_many_files + 23)
@ -1032,7 +1085,6 @@ EXTERN const char Tf__S[] E_INIT(" %S");
#define Tf__d (Tunexpected_type + 22)
EXTERN const char Tf__ss[] E_INIT(" %s%s");
#define Tf__sN (Tf_s_s_sN + 5)
EXTERN const char Tf_sSs[] E_INIT("%s/%s");
#define Tf_T (Tf_s_T + 3)
EXTERN const char Tf_dN[] E_INIT("%d\n");
EXTERN const char Tf_s_[] E_INIT("%s ");
@ -1056,8 +1108,6 @@ EXTERN const char Tf_S_[] E_INIT("%S ");
#define Tf_lu (Tf_toolarge + 17)
EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu");
EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s");
#define Tf_ss (Tf_sss + 2)
EXTERN const char Tf_sss[] E_INIT("%s%s%s");
EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s");
EXTERN const char Tf_toomany[] E_INIT("too many %ss");
EXTERN const char Tf_sd[] E_INIT("%s %d");
@ -1116,6 +1166,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tcreate "create"
#define TELIF_unexpected "TELIF unexpected"
#define TEXECSHELL "EXECSHELL"
#define TENV "ENV"
#define Tdsgexport "^*=export"
#define Texport "export"
#ifdef __OS2__
@ -1158,10 +1209,11 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tredirection "redirection"
#define Treal_sp1 "real "
#define Treal_sp2 " real "
#define TREPLY "REPLY"
#define Treq_arg "requires an argument"
#define Tselect "select"
#define Tsgset "*=set"
#define Tset "set"
#define Tsghset "*=#set"
#define Tsh "sh"
#define TSHELL "SHELL"
#define Tshell "shell"
@ -1193,7 +1245,6 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tf__d " %d"
#define Tf__ss " %s%s"
#define Tf__sN " %s\n"
#define Tf_sSs "%s/%s"
#define Tf_T "%T"
#define Tf_dN "%d\n"
#define Tf_s_ "%s "
@ -1217,8 +1268,6 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tf_lu "%lu"
#define Tf_toolarge "%s %s too large: %lu"
#define Tf_ldfailed "%s %s(%d, %ld) failed: %s"
#define Tf_ss "%s%s"
#define Tf_sss "%s%s%s"
#define Tf_sD_s_sD_s "%s: %s %s: %s"
#define Tf_toomany "too many %ss"
#define Tf_sd "%s %d"
@ -1372,7 +1421,7 @@ EXTERN bool really_exit;
#define CiCOLON BIT(26) /* : */
#define CiEQUAL BIT(27) /* = */
#define CiQUEST BIT(28) /* ? */
#define CiBRACK BIT(29) /* ] */
#define CiBRACK BIT(29) /* [] */
#define CiUNDER BIT(30) /* _ */
#define CiGRAVE BIT(31) /* ` */
/* out of space, but one for *@ would make sense, possibly others */
@ -1386,8 +1435,8 @@ EXTERN char ifs0;
/* external types */
/* !%,-.0‥9:@A‥Z[]_a‥z valid characters in alias names */
#define C_ALIAS (CiALIAS | CiBRACK | CiCOLON | CiDIGIT | CiLOWER | CiMINUS | CiOCTAL | CiPERCT | CiUNDER | CiUPPER)
/* !%+,-.0‥9:@A‥Z[]_a‥z valid characters in alias names */
#define C_ALIAS (CiALIAS | CiBRACK | CiCOLON | CiDIGIT | CiLOWER | CiMINUS | CiOCTAL | CiPERCT | CiPLUS | CiUNDER | CiUPPER)
/* 0‥9A‥Za‥z alphanumerical */
#define C_ALNUM (CiDIGIT | CiLOWER | CiOCTAL | CiUPPER)
/* 0‥9A‥Z_a‥z alphanumerical plus underscore (“word character”) */
@ -1533,6 +1582,12 @@ extern void ebcdic_init(void);
#define ksh_toctrl(c) asc2rtt(ord(c) == ORD('?') ? 0x7F : rtt2asc(c) & 0x9F)
#define ksh_unctrl(c) asc2rtt(rtt2asc(c) ^ 0x40U)
#ifdef MKSH_SMALL
#define SMALLP(x) /* nothing */
#else
#define SMALLP(x) , x
#endif
/* Argument parsing for built-in commands and getopts command */
/* Values for Getopt.flags */
@ -1626,7 +1681,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
#define shf_getc_i(shf) ((shf)->rnleft > 0 ? \
(shf)->rnleft--, (int)ord(*(shf)->rp++) : \
shf_getchar(shf))
#define shf_putc_i(c, shf) ((shf)->wnleft == 0 ? \
#define shf_putc_i(c,shf) ((shf)->wnleft == 0 ? \
shf_putchar((uint8_t)(c), (shf)) : \
((shf)->wnleft--, *(shf)->wp++ = (c)))
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
@ -1637,7 +1692,7 @@ EXTERN mksh_ari_t x_lins E_INIT(24);
/* Flags passed to shf_*open() */
#define SHF_RD 0x0001
#define SHF_WR 0x0002
#define SHF_RDWR (SHF_RD|SHF_WR)
#define SHF_RDWR (SHF_RD | SHF_WR)
#define SHF_ACCMODE 0x0003 /* mask */
#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
#define SHF_UNBUF 0x0008 /* unbuffered I/O */
@ -1756,14 +1811,15 @@ EXTERN bool last_lookup_was_array;
#define LOW_BI BIT(14) /* external utility overrides built-in one */
#define DECL_UTIL BIT(15) /* is declaration utility */
#define DECL_FWDR BIT(16) /* is declaration utility forwarder */
#define NEXTLOC_BI BIT(17) /* needs BF_RESETSPEC on e->loc */
/*
* Attributes that can be set by the user (used to decide if an unset
* param should be repoted by set/typeset). Does not include ARRAY or
* LOCAL.
*/
#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
LCASEV|UCASEV_AL|INT_U|INT_L)
#define USERATTRIB (EXPORT | INTEGER | RDONLY | LJUST | RJUST | ZEROFIL | \
LCASEV | UCASEV_AL | INT_U | INT_L)
#define arrayindex(vp) ((unsigned long)((vp)->flag & AINDEX ? \
(vp)->ua.index : 0))
@ -1794,7 +1850,7 @@ enum namerefflag {
#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
#define AI_ARGV(a,i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
#define AI_ARGC(a) ((a).ai_argc - (a).skip)
/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
@ -1824,6 +1880,8 @@ struct block {
/* Values for struct block.flags */
#define BF_DOGETOPTS BIT(0) /* save/restore getopts state */
#define BF_STOPENV BIT(1) /* do not export further */
/* BF_RESETSPEC and NEXTLOC_BI must be numerically identical! */
#define BF_RESETSPEC BIT(17) /* use ->next for set and shift */
/*
* Used by ktwalk() and ktnext() routines.
@ -1993,8 +2051,14 @@ struct ioword {
#define DOSCALAR BIT(12) /* change field handling to non-list context */
#define DOHEREDOC BIT(13) /* change scalar handling to heredoc body */
#define DOHERESTR BIT(14) /* append a newline char */
#define DODBMAGIC BIT(15) /* add magic to expansions for [[ x = $y ]] */
#define X_EXTRA 20 /* this many extra bytes in X string */
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
#define X_WASTE 15 /* allowed extra bytes to avoid shrinking, */
#else
#define X_WASTE 255 /* … must be 2ⁿ-1 */
#endif
typedef struct XString {
/* beginning of string */
@ -2008,44 +2072,44 @@ typedef struct XString {
} XString;
/* initialise expandable string */
#define XinitN(xs, length, area) do { \
#define XinitN(xs,length,area) do { \
(xs).len = (length); \
(xs).areap = (area); \
(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \
(xs).end = (xs).beg + (xs).len; \
} while (/* CONSTCOND */ 0)
#define Xinit(xs, xp, length, area) do { \
#define Xinit(xs,xp,length,area) do { \
XinitN((xs), (length), (area)); \
(xp) = (xs).beg; \
} while (/* CONSTCOND */ 0)
/* stuff char into string */
#define Xput(xs, xp, c) (*xp++ = (c))
#define Xput(xs,xp,c) (*xp++ = (c))
/* check if there are at least n bytes left */
#define XcheckN(xs, xp, n) do { \
#define XcheckN(xs,xp,n) do { \
ssize_t more = ((xp) + (n)) - (xs).end; \
if (more > 0) \
(xp) = Xcheck_grow(&(xs), (xp), (size_t)more); \
} while (/* CONSTCOND */ 0)
/* check for overflow, expand string */
#define Xcheck(xs, xp) XcheckN((xs), (xp), 1)
#define Xcheck(xs,xp) XcheckN((xs), (xp), 1)
/* free string */
#define Xfree(xs, xp) afree((xs).beg, (xs).areap)
#define Xfree(xs,xp) afree((xs).beg, (xs).areap)
/* close, return string */
#define Xclose(xs, xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
#define Xclose(xs,xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
/* beginning of string */
#define Xstring(xs, xp) ((xs).beg)
#define Xstring(xs,xp) ((xs).beg)
#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
#define Xlength(xs, xp) ((xp) - (xs).beg)
#define Xsize(xs, xp) ((xs).end - (xs).beg)
#define Xsavepos(xs, xp) ((xp) - (xs).beg)
#define Xrestpos(xs, xp, n) ((xs).beg + (n))
#define Xnleft(xs,xp) ((xs).end - (xp)) /* may be less than 0 */
#define Xlength(xs,xp) ((xp) - (xs).beg)
#define Xsize(xs,xp) ((xs).end - (xs).beg)
#define Xsavepos(xs,xp) ((xp) - (xs).beg)
#define Xrestpos(xs,xp,n) ((xs).beg + (n))
char *Xcheck_grow(XString *, const char *, size_t);
@ -2062,13 +2126,13 @@ typedef struct {
size_t siz;
} XPtrV;
#define XPinit(x, n) do { \
#define XPinit(x,n) do { \
(x).siz = (n); \
(x).len = 0; \
(x).beg = alloc2((x).siz, sizeof(void *), ATEMP); \
} while (/* CONSTCOND */ 0) \
#define XPput(x, p) do { \
#define XPput(x,p) do { \
if ((x).len == (x).siz) { \
(x).beg = aresize2((x).beg, (x).siz, \
2 * sizeof(void *), ATEMP); \
@ -2296,12 +2360,12 @@ EXTERN mksh_ari_t histsize; /* history size */
/* user and system time of last j_waitjed job */
EXTERN struct timeval j_usrtime, j_systime;
#define notok2mul(max, val, c) (((val) != 0) && ((c) != 0) && \
#define notok2mul(max,val,c) (((val) != 0) && ((c) != 0) && \
(((max) / (c)) < (val)))
#define notok2add(max, val, c) ((val) > ((max) - (c)))
#define notoktomul(val, cnst) notok2mul(SIZE_MAX, (val), (cnst))
#define notoktoadd(val, cnst) notok2add(SIZE_MAX, (val), (cnst))
#define checkoktoadd(val, cnst) do { \
#define notok2add(max,val,c) ((val) > ((max) - (c)))
#define notoktomul(val,cnst) notok2mul(SIZE_MAX, (val), (cnst))
#define notoktoadd(val,cnst) notok2add(SIZE_MAX, (val), (cnst))
#define checkoktoadd(val,cnst) do { \
if (notoktoadd((val), (cnst))) \
internal_errorf(Tintovfl, (size_t)(val), \
'+', (size_t)(cnst)); \
@ -2312,18 +2376,20 @@ EXTERN struct timeval j_usrtime, j_systime;
void ainit(Area *);
void afreeall(Area *);
/* these cannot fail and can take NULL (not for ap) */
#define alloc(n, ap) aresize(NULL, (n), (ap))
#define alloc2(m, n, ap) aresize2(NULL, (m), (n), (ap))
#define alloc(n,ap) aresize(NULL, (n), (ap))
#define alloc2(m,n,ap) aresize2(NULL, (m), (n), (ap))
void *aresize(void *, size_t, Area *);
void *aresize2(void *, size_t, size_t, Area *);
void afree(void *, Area *); /* can take NULL */
#define aresizeif(z,p,n,ap) (((p) == NULL) || ((z) < (n)) || \
(((z) & ~X_WASTE) > ((n) & ~X_WASTE)) ? \
aresize((p), (n), (ap)) : (p))
/* edit.c */
#ifndef MKSH_NO_CMDLINE_EDITING
#ifndef MKSH_SMALL
int x_bind(const char *, const char *, bool, bool);
#else
int x_bind(const char *, const char *, bool);
#endif
int x_bind(const char * SMALLP(bool));
int x_bind_check(void);
int x_bind_list(void);
int x_bind_showall(void);
void x_init(void);
#ifdef DEBUG_LEAKS
void x_done(void);
@ -2511,6 +2577,8 @@ void warningf(bool, const char *, ...)
MKSH_A_FORMAT(__printf__, 2, 3);
void bi_errorf(const char *, ...)
MKSH_A_FORMAT(__printf__, 1, 2);
void maybe_errorf(int *, int, const char *, ...)
MKSH_A_FORMAT(__printf__, 3, 4);
#define errorfz() errorf(NULL)
#define errorfxz(rc) errorfx((rc), NULL)
#define bi_errorfz() bi_errorf(NULL)
@ -2543,7 +2611,7 @@ struct temp *maketemp(Area *, Temp_type, struct temp **);
void ktinit(Area *, struct table *, uint8_t);
struct tbl *ktscan(struct table *, const char *, uint32_t, struct tbl ***);
/* table, name (key) to search for, hash(n) */
#define ktsearch(tp, s, h) ktscan((tp), (s), (h), NULL)
#define ktsearch(tp,s,h) ktscan((tp), (s), (h), NULL)
struct tbl *ktenter(struct table *, const char *, uint32_t);
#define ktdelete(p) do { p->flag = 0; } while (/* CONSTCOND */ 0)
void ktwalk(struct tstate *, struct table *);
@ -2591,7 +2659,7 @@ int unbksl(bool, int (*)(void), void (*)(int));
void os2_init(int *, const char ***);
void setextlibpath(const char *, const char *);
int access_ex(int (*)(const char *, int), const char *, int);
int stat_ex(const char *, struct stat *);
int stat_ex(int (*)(const char *, struct stat *), const char *, struct stat *);
const char *real_exec_name(const char *);
#endif
/* shf.c */
@ -2607,7 +2675,7 @@ ssize_t shf_read(char *, ssize_t, struct shf *);
char *shf_getse(char *, ssize_t, struct shf *);
int shf_getchar(struct shf *s);
int shf_ungetc(int, struct shf *);
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
#ifdef MKSH_SHF_NO_INLINE
int shf_getc(struct shf *);
int shf_putc(int, struct shf *);
#else
@ -2712,6 +2780,24 @@ enum Test_meta {
};
typedef enum Test_meta Test_meta;
struct t_op {
const char op_text[4];
Test_op op_num;
};
/* for string reuse */
extern const struct t_op u_ops[];
extern const struct t_op b_ops[];
/* ensure order with funcs.c */
#define Tda (u_ops[0].op_text)
#define Tdn (u_ops[12].op_text)
#define Tdo (u_ops[14].op_text)
#define Tdr (u_ops[16].op_text)
#define Tdu (u_ops[20].op_text) /* "-u" */
#define Tdx (u_ops[23].op_text)
#define Tu (Tdu + 1) /* "u" */
#define TEF_ERROR BIT(0) /* set if we've hit an error */
#define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */

View file

@ -21,7 +21,7 @@
#ifndef SHFLAGS_OPTCS
#if defined(SHFLAGS_DEFNS)
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.8 2019/12/30 03:58:58 tg Exp $");
#elif defined(SHFLAGS_ENUMS)
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,

View file

@ -19,7 +19,7 @@
*/
@SHFLAGS_DEFNS
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.8 2019/12/30 03:58:58 tg Exp $");
@SHFLAGS_ENUMS
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
@ -184,7 +184,7 @@ FN("xtrace", FXTRACE, OF_ANY
FN("", FCOMMAND, OF_CMDLINE
/*
* anonymous flags: used internally by shell only (not visible to user
* anonymous flags: used internally by shell only (not visible to user)
*/
/* ./. direct builtin call (divined from argv[0] multi-call binary) */

View file

@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
* 2012, 2013, 2015, 2016, 2017, 2018
* 2012, 2013, 2015, 2016, 2017, 2018, 2019
* mirabilos <m@mirbsd.org>
* Copyright (c) 2015
* Daniel Richard G. <skunk@iSKUNK.ORG>
@ -27,7 +27,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.98 2018/08/10 02:53:39 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.101 2019/12/11 17:56:58 tg Exp $");
/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
@ -523,7 +523,8 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
buf += ncopy;
bsize -= ncopy;
#ifdef MKSH_WITH_TEXTMODE
if (end && buf > orig_buf + 1 && buf[-2] == '\r') {
if (buf > orig_buf + 1 && ord(buf[-2]) == ORD('\r') &&
ord(buf[-1]) == ORD('\n')) {
buf--;
bsize++;
buf[-1] = '\n';
@ -531,9 +532,9 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
#endif
} while (!end && bsize);
#ifdef MKSH_WITH_TEXTMODE
if (!bsize && buf[-1] == '\r') {
if (!bsize && ord(buf[-1]) == ORD('\r')) {
int c = shf_getc(shf);
if (c == '\n')
if (ord(c) == ORD('\n'))
buf[-1] = '\n';
else if (c != -1)
shf_ungetc(c, shf);
@ -1073,7 +1074,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
return (shf_error(shf) ? -1 : nwritten);
}
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
#ifdef MKSH_SHF_NO_INLINE
int
shf_getc(struct shf *shf)
{

176
src/syn.c
View file

@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017,
* 2018
* 2018, 2020
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -24,7 +24,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.127 2018/01/14 00:22:30 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.128 2020/03/31 00:30:05 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@ -271,6 +271,7 @@ get_command(int cf, int sALIAS)
struct ioword *iop, **iops;
XPtrV args, vars;
struct nesting_state old_nesting;
bool check_decl_utility;
/* NUFILE is small enough to leave this addition unchecked */
iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP);
@ -294,100 +295,93 @@ get_command(int cf, int sALIAS)
t = newtp(TCOM);
t->lineno = source->line;
goto get_command_start;
while (/* CONSTCOND */ 1) {
bool check_decl_utility;
if (XPsize(args) == 0) {
get_command_loop:
if (XPsize(args) == 0) {
get_command_start:
check_decl_utility = true;
cf = sALIAS | CMDASN;
} else if (t->u.evalflags)
cf = CMDWORD | CMDASN;
else
cf = CMDWORD;
switch (tpeek(cf)) {
case REDIR:
while ((iop = synio(cf)) != NULL) {
if (iopn >= NUFILE)
yyerror(Tf_toomany,
Tredirection);
iops[iopn++] = iop;
}
break;
check_decl_utility = true;
cf = sALIAS | CMDASN;
} else if (t->u.evalflags)
cf = CMDWORD | CMDASN;
else
cf = CMDWORD;
case LWORD:
ACCEPT;
if (check_decl_utility) {
struct tbl *tt = get_builtin(ident);
uint32_t flag;
flag = tt ? tt->flag : 0;
if (flag & DECL_UTIL)
t->u.evalflags = DOVACHECK;
if (!(flag & DECL_FWDR))
check_decl_utility = false;
}
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
is_wdvarassign(yylval.cp))
XPput(vars, yylval.cp);
else
XPput(args, yylval.cp);
break;
case ORD('(' /*)*/):
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
is_wdvarassign(yylval.cp)) {
char *tcp;
/* wdarrassign: foo=(bar) */
ACCEPT;
/* manipulate the vars string */
tcp = XPptrv(vars)[(vars.len = 0)];
/* 'varname=' -> 'varname' */
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
/* construct new args strings */
XPput(args, wdcopy(builtin_cmd, ATEMP));
XPput(args, wdcopy(setA_cmd0, ATEMP));
XPput(args, wdcopy(setA_cmd1, ATEMP));
XPput(args, tcp);
XPput(args, wdcopy(setA_cmd2, ATEMP));
/* slurp in words till closing paren */
while (token(CONTIN) == LWORD)
XPput(args, yylval.cp);
if (symbol != /*(*/ ')')
syntaxerr(NULL);
} else {
/*
* Check for "> foo (echo hi)"
* which AT&T ksh allows (not
* POSIX, but not disallowed)
*/
afree(t, ATEMP);
if (XPsize(args) == 0 &&
XPsize(vars) == 0) {
ACCEPT;
goto Subshell;
}
/* must be a function */
if (iopn != 0 || XPsize(args) != 1 ||
XPsize(vars) != 0)
syntaxerr(NULL);
ACCEPT;
musthave(/*(*/ ')', 0);
t = function_body(XPptrv(args)[0],
sALIAS, false);
}
goto Leave;
default:
goto Leave;
switch (tpeek(cf)) {
case REDIR:
while ((iop = synio(cf)) != NULL) {
if (iopn >= NUFILE)
yyerror(Tf_toomany, Tredirection);
iops[iopn++] = iop;
}
goto get_command_loop;
case LWORD:
ACCEPT;
if (check_decl_utility) {
struct tbl *tt = get_builtin(ident);
uint32_t flag;
flag = tt ? tt->flag : 0;
if (flag & DECL_UTIL)
t->u.evalflags = DOVACHECK;
if (!(flag & DECL_FWDR))
check_decl_utility = false;
}
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
is_wdvarassign(yylval.cp))
XPput(vars, yylval.cp);
else
XPput(args, yylval.cp);
goto get_command_loop;
case ORD('(' /*)*/):
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
is_wdvarassign(yylval.cp)) {
char *tcp;
/* wdarrassign: foo=(bar) */
ACCEPT;
/* manipulate the vars string */
tcp = XPptrv(vars)[(vars.len = 0)];
/* 'varname=' -> 'varname' */
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
/* construct new args strings */
XPput(args, wdcopy(builtin_cmd, ATEMP));
XPput(args, wdcopy(setA_cmd0, ATEMP));
XPput(args, wdcopy(setA_cmd1, ATEMP));
XPput(args, tcp);
XPput(args, wdcopy(setA_cmd2, ATEMP));
/* slurp in words till closing paren */
while (token(CONTIN) == LWORD)
XPput(args, yylval.cp);
if (symbol != /*(*/ ')')
syntaxerr(NULL);
break;
}
afree(t, ATEMP);
/*
* Check for "> foo (echo hi)" which AT&T ksh allows
* (not POSIX, but not disallowed)
*/
if (XPsize(args) == 0 && XPsize(vars) == 0) {
ACCEPT;
goto Subshell;
}
/* must be a function */
if (iopn != 0 || XPsize(args) != 1 || XPsize(vars) != 0)
syntaxerr(NULL);
ACCEPT;
musthave(/*(*/ ')', 0);
t = function_body(XPptrv(args)[0],
sALIAS, false);
break;
}
Leave:
break;
case ORD('(' /*)*/): {

170
src/var.c
View file

@ -2,7 +2,8 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
* 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
* 2019
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -28,7 +29,7 @@
#include <sys/sysctl.h>
#endif
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.226 2018/07/15 17:21:24 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.236 2020/04/13 16:29:34 tg Exp $");
/*-
* Variables
@ -49,7 +50,7 @@ static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
bool);
static char *formatstr(struct tbl *, const char *);
static void exportprep(struct tbl *, const char *);
static void exportprep(struct tbl *, const char *, size_t);
static int special(const char *);
static void unspecial(const char *);
static void getspec(struct tbl *);
@ -57,6 +58,7 @@ static void setspec(struct tbl *);
static void unsetspec(struct tbl *, bool);
static int getint(struct tbl *, mksh_ari_u *, bool);
static const char *array_index_calc(const char *, bool *, uint32_t *);
static struct tbl *vtypeset(int *, const char *, uint32_t, uint32_t, int, int);
/*
* create a new block for function calls and simple commands
@ -196,7 +198,7 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
char *cp;
/* gotcha! */
cp = shf_smprintf(Tf_ss, str_val(vp), p);
strdup2x(cp, str_val(vp), p);
afree(ap, ATEMP);
n = ap = cp;
goto redo_from_ref;
@ -207,16 +209,21 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
if (p != n && ord(*p) == ORD('[') && (len = array_ref_len(p))) {
char *sub, *tmp;
mksh_ari_t rval;
size_t tmplen = p - n;
/* calculate the value of the subscript */
*arrayp = true;
strndupx(tmp, p + 1, len - 2, ATEMP);
len -= 2;
tmp = alloc((len > tmplen ? len : tmplen) + 1, ATEMP);
memcpy(tmp, p + 1, len);
tmp[len] = '\0';
sub = substitute(tmp, 0);
afree(tmp, ATEMP);
strndupx(n, n, p - n, ATEMP);
evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
*valp = (uint32_t)rval;
afree(sub, ATEMP);
memcpy(tmp, n, tmplen);
tmp[tmplen] = '\0';
n = tmp;
}
return (n);
}
@ -273,7 +280,7 @@ isglobal(const char *n, bool docreate)
vp->name[0] = c;
vp->name[1] = '\0';
vp->flag |= RDONLY;
if (vn[1] != '\0')
if (!c || vn[1] != '\0')
goto out;
vp->flag |= ISSET|INTEGER;
switch (c) {
@ -450,7 +457,6 @@ str_val(struct tbl *vp)
int
setstr(struct tbl *vq, const char *s, int error_ok)
{
char *salloc = NULL;
bool no_ro_check = tobool(error_ok & 0x4);
error_ok &= ~0x4;
@ -462,28 +468,33 @@ setstr(struct tbl *vq, const char *s, int error_ok)
}
if (!(vq->flag&INTEGER)) {
/* string dest */
char *salloc = NULL;
size_t cursz;
if ((vq->flag&ALLOC)) {
cursz = strlen(vq->val.s) + 1;
#ifndef MKSH_SMALL
/* debugging */
if (s >= vq->val.s &&
s <= strnul(vq->val.s)) {
if (s >= vq->val.s && s < (vq->val.s + cursz)) {
internal_errorf(
"setstr: %s=%s: assigning to self",
vq->name, s);
}
#endif
afree(vq->val.s, vq->areap);
}
vq->flag &= ~(ISSET|ALLOC);
vq->type = 0;
} else
cursz = 0;
if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
s = salloc = formatstr(vq, s);
if ((vq->flag&EXPORT))
exportprep(vq, s);
exportprep(vq, s, cursz);
else {
strdupx(vq->val.s, s, vq->areap);
size_t n = strlen(s) + 1;
vq->val.s = aresizeif(cursz, (vq->flag & ALLOC) ?
vq->val.s : NULL, n, vq->areap);
memcpy(vq->val.s, s, n);
vq->flag |= ALLOC;
vq->type = 0;
}
afree(salloc, ATEMP);
} else {
/* integer dest */
if (!v_evaluate(vq, s, error_ok, true))
@ -492,7 +503,6 @@ setstr(struct tbl *vq, const char *s, int error_ok)
vq->flag |= ISSET;
if ((vq->flag&SPECIAL))
setspec(vq);
afree(salloc, ATEMP);
return (1);
}
@ -728,25 +738,19 @@ formatstr(struct tbl *vp, const char *s)
* make vp->val.s be "name=value" for quick exporting.
*/
static void
exportprep(struct tbl *vp, const char *val)
exportprep(struct tbl *vp, const char *val, size_t cursz)
{
char *xp;
char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
size_t namelen, vallen;
namelen = strlen(vp->name);
vallen = strlen(val) + 1;
char *cp = (vp->flag & ALLOC) ? vp->val.s : NULL;
size_t namelen = strlen(vp->name);
size_t vallen = strlen(val) + 1;
vp->flag |= ALLOC;
vp->type = namelen + 1;
/* since name+val are both in memory this can go unchecked */
xp = alloc(namelen + 1 + vallen, vp->areap);
memcpy(vp->val.s = xp, vp->name, namelen);
xp += namelen;
*xp++ = '=';
/* offset to value */
vp->type = xp - vp->val.s;
memcpy(xp, val, vallen);
afree(op, vp->areap);
vp->val.s = aresizeif(cursz, cp, vp->type + vallen, vp->areap);
memmove(vp->val.s + vp->type, val == cp ? vp->val.s : val, vallen);
memcpy(vp->val.s, vp->name, namelen);
vp->val.s[namelen] = '=';
}
/*
@ -756,15 +760,24 @@ exportprep(struct tbl *vp, const char *val)
*/
struct tbl *
typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
{
return (vtypeset(NULL, var, set, clr, field, base));
}
static struct tbl *
vtypeset(int *ep, const char *var, uint32_t set, uint32_t clr,
int field, int base)
{
struct tbl *vp;
struct tbl *vpbase, *t;
char *tvar;
char *tvar, tvarbuf[32];
const char *val;
size_t len;
bool vappend = false;
enum namerefflag new_refflag = SRF_NOP;
if (ep)
*ep = 0;
if ((set & (ARRAY | ASSOC)) == ASSOC) {
new_refflag = SRF_ENABLE;
set &= ~(ARRAY | ASSOC);
@ -782,8 +795,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
}
if (ord(*val) == ORD('[')) {
if (new_refflag != SRF_NOP)
errorf(Tf_sD_s, var,
"reference variable can't be an array");
return (maybe_errorf(ep, 1, Tf_sD_s, var,
"reference variable can't be an array"), NULL);
len = array_ref_len(val);
if (len == 0)
return (NULL);
@ -804,13 +817,19 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
val += len;
}
if (ord(val[0]) == ORD('=')) {
strndupx(tvar, var, val - var, ATEMP);
len = val - var;
tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
memcpy(tvar, var, len);
tvar[len] = '\0';
++val;
} else if (set & IMPORT) {
/* environment invalid variable name or no assignment */
return (NULL);
} else if (ord(val[0]) == ORD('+') && ord(val[1]) == ORD('=')) {
strndupx(tvar, var, val - var, ATEMP);
len = val - var;
tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
memcpy(tvar, var, len);
tvar[len] = '\0';
val += 2;
vappend = true;
} else if (val[0] != '\0') {
@ -818,10 +837,12 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
return (NULL);
} else {
/* just varname with no value part nor equals sign */
strdupx(tvar, var, ATEMP);
len = strlen(var);
tvar = len < sizeof(tvarbuf) ? tvarbuf : alloc(len + 1, ATEMP);
memcpy(tvar, var, len);
tvar[len] = '\0';
val = NULL;
/* handle foo[*] => foo (whole array) mapping for R39b */
len = strlen(tvar);
if (len > 3 && ord(tvar[len - 3]) == ORD('[') &&
ord(tvar[len - 2]) == ORD('*') &&
ord(tvar[len - 1]) == ORD(']'))
@ -833,7 +854,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
/* bail out on 'nameref foo+=bar' */
if (vappend)
errorf("appending not allowed for nameref");
return (maybe_errorf(ep, 1,
"appending not allowed for nameref"), NULL);
/* find value if variable already exists */
if ((qval = val) == NULL) {
varsearch(e->loc, &vp, tvar, hash(tvar));
@ -859,7 +881,8 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
goto nameref_rhs_checked;
}
nameref_empty:
errorf(Tf_sD_s, var, "empty nameref target");
return (maybe_errorf(ep, 1, Tf_sD_s, var,
"empty nameref target"), NULL);
}
len = (ord(*ccp) == ORD('[')) ? array_ref_len(ccp) : 0;
if (ccp[len]) {
@ -868,15 +891,15 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
* junk after it" and "invalid array"; in the
* latter case, len is also 0 and points to '['
*/
errorf(Tf_sD_s, qval,
"nameref target not a valid parameter name");
return (maybe_errorf(ep, 1, Tf_sD_s, qval,
"nameref target not a valid parameter name"), NULL);
}
nameref_rhs_checked:
/* prevent nameref loops */
while (qval) {
if (!strcmp(qval, tvar))
errorf(Tf_sD_s, qval,
"expression recurses on parameter");
return (maybe_errorf(ep, 1, Tf_sD_s, qval,
"expression recurses on parameter"), NULL);
varsearch(e->loc, &vp, qval, hash(qval));
qval = NULL;
if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
@ -886,8 +909,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
/* prevent typeset from creating a local PATH/ENV/SHELL */
if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
errorf(Tf_sD_s, tvar, "restricted");
strcmp(tvar, TENV) == 0 || strcmp(tvar, TSHELL) == 0))
return (maybe_errorf(ep, 1, Tf_sD_s,
tvar, "restricted"), NULL);
innermost_refflag = new_refflag;
vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
@ -923,9 +947,9 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
*/
if ((vpbase->flag & RDONLY) &&
(val || clr || (set & ~(EXPORT | RDONLY))))
/* XXX check calls - is error here ok by POSIX? */
errorfx(2, Tf_ro, tvar);
afree(tvar, ATEMP);
return (maybe_errorf(ep, 2, Tf_ro, tvar), NULL);
if (tvar != tvarbuf)
afree(tvar, ATEMP);
/* most calls are with set/clr == 0 */
if (set | clr) {
@ -990,18 +1014,24 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
}
}
if (!ok)
errorfz();
return (maybe_errorf(ep, 1, NULL), NULL);
}
if (val != NULL) {
char *tval;
if (vappend) {
tval = shf_smprintf(Tf_ss, str_val(vp), val);
val = tval;
} else
tval = NULL;
if (vappend) {
size_t tlen;
if ((vp->flag & (ISSET|ALLOC|SPECIAL|INTEGER|UCASEV_AL|LCASEV|LJUST|RJUST)) != (ISSET|ALLOC)) {
/* cannot special-case this */
strdup2x(tvar, str_val(vp), val);
val = tvar;
goto vassign;
}
/* trivial string appending */
len = strlen(vp->val.s);
tlen = strlen(val) + 1;
vp->val.s = aresize(vp->val.s, len + tlen, vp->areap);
memcpy(vp->val.s + len, val, tlen);
} else if (val != NULL) {
vassign:
if (vp->flag&INTEGER) {
/* do not zero base before assignment */
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
@ -1012,13 +1042,16 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
/* setstr can't fail (readonly check already done) */
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
afree(tval, ATEMP);
/* came here from vappend? need to free temp val */
if (vappend)
afree(tvar, ATEMP);
}
/* only x[0] is ever exported, so use vpbase */
if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
if ((vpbase->flag & (EXPORT|INTEGER)) == EXPORT &&
vpbase->type == 0)
exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
exportprep(vpbase, (vpbase->flag & ISSET) ?
vpbase->val.s : null, 0);
return (vp);
}
@ -1829,7 +1862,7 @@ record_match(const char *istr)
setstr(vp, istr, 0x4);
}
/* typeset, global(deprecated), export, and readonly */
/* typeset, export and readonly */
int
c_typeset(const char **wp)
{
@ -2026,7 +2059,7 @@ c_typeset(const char **wp)
if (wp[builtin_opt.optind] &&
/* not "typeset -p varname" */
!(!func && pflag && !(fset | fclr))) {
int rv = 0;
int rv = 0, x;
struct tbl *f;
if (localv && !func)
@ -2049,7 +2082,10 @@ c_typeset(const char **wp)
wp[i], f->val.t);
shf_putc('\n', shl_stdout);
}
} else if (!typeset(wp[i], fset, fclr, field, base)) {
} else if (!vtypeset(&x, wp[i], fset, fclr,
field, base)) {
if (x)
return (x);
bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
return (1);
}