From 811a575c0f6a5ef00a921d14c1830ef5ae1bd796 Mon Sep 17 00:00:00 2001 From: Thorsten Glaser Date: Thu, 25 Jul 2013 14:24:45 +0000 Subject: [PATCH] Update to mksh R48 Change-Id: I4d1bef9bf8ddc7899cfb32a6f2fa9e6f632bc53f --- Android.mk | 6 +- mkmf.sh | 15 +- mkshrc | 40 ++- src/Build.sh | 383 +++++++++++++------------ src/check.pl | 38 ++- src/check.t | 622 +++++++++++++++++++++++++++++++++------- src/dot.mkshrc | 73 +++-- src/edit.c | 340 ++++++++++++---------- src/eval.c | 225 +++++++++------ src/exec.c | 111 ++++---- src/expr.c | 881 +++++++++++++++++++++++++++++++++++---------------------- src/funcs.c | 90 +++--- src/jobs.c | 39 ++- src/lalloc.c | 6 +- src/lex.c | 63 ++--- src/lksh.1 | 297 +++++++++++++++++++ src/main.c | 72 +++-- src/misc.c | 252 +++++++++++------ src/mksh.1 | 204 +++++++++---- src/mksh.ico | Bin 0 -> 14166 bytes src/sh.h | 66 ++--- src/sh_flags.h | 33 ++- src/shf.c | 19 +- src/syn.c | 47 ++- src/tree.c | 49 +++- src/var.c | 111 ++++---- 26 files changed, 2709 insertions(+), 1373 deletions(-) create mode 100644 src/lksh.1 create mode 100644 src/mksh.ico diff --git a/Android.mk b/Android.mk index 0d8807e..3b1c9fd 100644 --- a/Android.mk +++ b/Android.mk @@ -37,6 +37,7 @@ LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src LOCAL_CFLAGS:= -DMKSHRC_PATH=\"/system/etc/mkshrc\" \ -DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\" \ -DMKSH_DEFAULT_TMPDIR=\"/data/local\" \ + -Wno-deprecated-declarations \ -fno-asynchronous-unwind-tables -fwrapv \ -DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 -DMKSH_CONSERVATIVE_FDS \ -DMKSH_DONT_EMIT_IDSTRING -DMKSH_NOPWNAM -DMKSH_BUILDSH \ @@ -62,9 +63,8 @@ LOCAL_CFLAGS:= -DMKSHRC_PATH=\"/system/etc/mkshrc\" \ -DHAVE_SETGROUPS=1 -DHAVE_STRERROR=1 -DHAVE_STRSIGNAL=0 \ -DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \ -DHAVE_SYS_ERRLIST_DECL=0 -DHAVE_SYS_SIGLIST_DECL=1 \ - -DHAVE_PERSISTENT_HISTORY=0 -DHAVE_SILENT_IDIVWRAPV=0 \ - -DMKSH_BUILD_R=431 + -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=481 -# check categories: shell:legacy-no int:32 android convfds no-histfile +# check_categories= shell:legacy-no int:32 android convfds no-histfile include $(BUILD_EXECUTABLE) diff --git a/mkmf.sh b/mkmf.sh index 6e2517c..f48db92 100644 --- a/mkmf.sh +++ b/mkmf.sh @@ -64,7 +64,6 @@ addvar CPPFLAGS \ -isystem $aospdir/frameworks/native/opengl/include \ -isystem $aospdir/frameworks/av/include \ -isystem $aospdir/frameworks/base/include \ - -isystem $aospdir/frameworks/base/opengl/include \ -isystem $aospdir/external/skia/include \ -isystem $aospdir/out/target/product/generic/obj/include \ -isystem $aospdir/bionic/libc/arch-arm/include \ @@ -75,11 +74,10 @@ addvar CPPFLAGS \ -isystem $aospdir/bionic/libm/include \ -isystem $aospdir/bionic/libm/include/arm \ -isystem $aospdir/bionic/libthread_db/include \ - -D_FORTIFY_SOURCE=1 \ + -D_FORTIFY_SOURCE=2 \ -include $aospdir/build/core/combo/include/arch/linux-arm/AndroidConfig.h \ -I$aospdir/build/core/combo/include/arch/linux-arm/ \ -DANDROID -DNDEBUG -UDEBUG -# who would have thought the AOSP devs are funny? -fno-builtin-sin addvar CFLAGS \ -fno-exceptions \ -Wno-multichar \ @@ -123,6 +121,7 @@ addvar CFLAGS \ addvar LDFLAGS \ -nostdlib \ -Bdynamic \ + -fPIE \ -pie \ -Wl,-dynamic-linker,/system/bin/linker \ -Wl,--gc-sections \ @@ -131,6 +130,7 @@ addvar LDFLAGS \ -Wl,-z,relro \ -Wl,-z,now \ -Wl,--warn-shared-textrel \ + -Wl,--fatal-warnings \ -Wl,--icf=safe \ -Wl,--fix-cortex-a8 \ -Wl,--no-undefined \ @@ -138,9 +138,9 @@ addvar LDFLAGS \ addvar LIBS \ -L$aospdir/out/target/product/generic/obj/lib \ -Wl,-rpath-link=$aospdir/out/target/product/generic/obj/lib \ - -lc \ -Wl,--no-whole-archive \ - $aospdir/out/target/product/generic/obj/STATIC_LIBRARIES/libcompiler-rt-extras_intermediates/libcompiler-rt-extras.a \ + $aospdir/out/target/product/generic/obj/STATIC_LIBRARIES/libcompiler_rt-extras_intermediates/libcompiler_rt-extras.a \ + -lc \ $aospdir/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7/bin/../lib/gcc/arm-linux-androideabi/4.7/armv7-a/libgcc.a \ $aospdir/out/target/product/generic/obj/lib/crtend_android.o @@ -181,11 +181,6 @@ export HAVE_CAN_FNOSTRICTALIASING HAVE_CAN_FSTACKPROTECTORALL HAVE_CAN_WALL # even the idea of persistent history on a phone is funny HAVE_PERSISTENT_HISTORY=0; export HAVE_PERSISTENT_HISTORY -# this is a run-time check and dependent on the target CPU -# architecture (at _least_!) and cannot be auto-detected, -# so always include the safety check even if unnecessary -HAVE_SILENT_IDIVWRAPV=0; export HAVE_SILENT_IDIVWRAPV - # ... and run it! export CC CPPFLAGS CFLAGS LDFLAGS LIBS TARGET_OS sh ../src/Build.sh $args diff --git a/mkshrc b/mkshrc index 2951595..6d135a3 100644 --- a/mkshrc +++ b/mkshrc @@ -9,12 +9,13 @@ : ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${HOSTNAME:=$(getprop ro.product.device)} : ${SHELL:=$MKSH} ${USER:=$(typeset x=$(id); x=${x#*\(}; print -r -- ${x%%\)*})} ${HOSTNAME:=android} if (( USER_ID )); then PS1='$'; else PS1='#'; fi -function precmd { - typeset e=$? +PS4='[$EPOCHREALTIME] '; PS1='${| + local e=$? - (( e )) && print -n "$e|" -} -PS1='$(precmd)$USER@$HOSTNAME:${PWD:-?} '"$PS1 " + (( e )) && REPLY+="$e|" + + return $e +}$USER@$HOSTNAME:${PWD:-?} '"$PS1 " export HOME HOSTNAME MKSH SHELL TERM USER alias l='ls' alias la='l -a' @@ -22,7 +23,34 @@ alias ll='l -l' alias lo='l -a -l' function hd { - cat "$@" | command hd /proc/self/fd/0 + local -Uui16 -Z11 pos=0 + local -Uui16 -Z5 hv=2147483647 + local dasc line i + + cat "$@" | { set +U; if read -arN -1 line; then + typeset -i1 line + i=0 + while (( i < ${#line[*]} )); do + hv=${line[i++]} + if (( (pos & 15) == 0 )); then + (( pos )) && print -r -- "$dasc|" + print -n "${pos#16#} " + dasc=' |' + fi + print -n "${hv#16#} " + if (( (hv < 32) || (hv > 126) )); then + dasc+=. + else + dasc+=${line[i-1]#1#} + fi + (( (pos++ & 15) == 7 )) && print -n -- '- ' + done + while (( pos & 15 )); do + print -n ' ' + (( (pos++ & 15) == 7 )) && print -n -- '- ' + done + (( hv == 2147483647 )) || print -r -- "$dasc|" + fi; } } function more { diff --git a/src/Build.sh b/src/Build.sh index 31c559d..45af9dd 100644 --- a/src/Build.sh +++ b/src/Build.sh @@ -1,5 +1,5 @@ #!/bin/sh -srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.622 2013/02/19 18:45:15 tg Exp $' +srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.645 2013/08/10 13:44:25 tg Exp $' #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 @@ -28,6 +28,9 @@ srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.622 2013/02/19 18:45:15 tg Exp $' LC_ALL=C export LC_ALL +echo "For the build logs, demonstrate that /dev/null and /dev/tty exist:" +ls -l /dev/null /dev/tty + case $ZSH_VERSION:$VERSION in :zsh*) ZSH_VERSION=2 ;; esac @@ -63,7 +66,7 @@ vq() { rmf() { for _f in "$@"; do case $_f in - Build.sh|check.pl|check.t|dot.mkshrc|*.c|*.h|mksh.1) ;; + Build.sh|check.pl|check.t|dot.mkshrc|*.c|*.h|*.ico|*.1) ;; *) rm -f "$_f" ;; esac done @@ -190,6 +193,7 @@ ac_testn() { ac_ifcpp() { expr=$1; shift ac_testn "$@" <<-EOF + extern int thiswillneverbedefinedIhope(void); int main(void) { return ( #$expr 0 @@ -458,7 +462,7 @@ oswarn= ccpc=-Wc, ccpl=-Wl, tsts= -ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.c|*.h|mksh.1) ;; *) rm -f "$_f" ;; esac; done' +ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.c|*.h|*.ico|*.1) ;; *) rm -f "$_f" ;; esac; done' # Evil hack if test x"$TARGET_OS" = x"Android"; then @@ -686,8 +690,11 @@ Plan9) add_cppflags -D_SUSV2_SOURCE add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1 add_cppflags -DMKSH_NO_CMDLINE_EDITING + add_cppflags -DMKSH__NO_SETEUGID oswarn=' and will currently not work' add_cppflags -DMKSH_UNEMPLOYED + # this is for detecting kencc + add_cppflags -DMKSH_MAYBE_KENCC ;; PW32*) HAVE_SIG_T=0 # incompatible @@ -766,7 +773,7 @@ esac : ${HAVE_MKNOD=0} -: ${AWK=awk} ${CC=cc} ${NROFF=nroff} +: ${AWK=awk} ${CC=cc} ${NROFF=nroff} ${SIZE=size} test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \ NROFF="$NROFF -c" @@ -876,6 +883,9 @@ ct="ucode" ct="uslc" #elif defined(__LCC__) ct="lcc" +#elif defined(MKSH_MAYBE_KENCC) +/* and none of the above matches */ +ct="kencc" #else ct="unknown" #endif @@ -952,6 +962,9 @@ iar) icc) vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V" ;; +kencc) + vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" + ;; lcc) vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" add_cppflags -D__inline__=__inline @@ -1074,19 +1087,23 @@ if ac_ifcpp 'if 0' compiler_fails '' \ 'if the compiler does not fail correctly'; then save_CFLAGS=$CFLAGS : ${HAVE_CAN_DELEXE=x} - if test $ct = dmc; then - CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE" - ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF - int main(void) { return (0); } - EOF - elif test $ct = dec; then + case $ct in + dec) CFLAGS="$CFLAGS ${ccpl}-non_shared" ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF int main(void) { return (0); } EOF - else + ;; + dmc) + CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE" + ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF + int main(void) { return (0); } + EOF + ;; + *) exit 1 - fi + ;; + esac test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF EOF @@ -1099,49 +1116,65 @@ if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \ HAVE_COMPILER_KNOWN=1 fi -if test $ct = sunpro; then - test x"$save_NOWARN" = x"" && save_NOWARN='-errwarn=%none' - ac_flags 0 errwarnnone "$save_NOWARN" - test 1 = $HAVE_CAN_ERRWARNNONE || save_NOWARN= - ac_flags 0 errwarnall "-errwarn=%all" - test 1 = $HAVE_CAN_ERRWARNALL && DOWARN="-errwarn=%all" -elif test $ct = hpcc; then +case $ct in +bcc) + save_NOWARN="${ccpc}-w" + DOWARN="${ccpc}-w!" + ;; +dec) + # -msg_* flags not used yet, or is -w2 correct? + ;; +dmc) + save_NOWARN="${ccpc}-w" + DOWARN="${ccpc}-wx" + ;; +hpcc) save_NOWARN= DOWARN=+We -elif test $ct = mipspro; then + ;; +kencc) + save_NOWARN= + DOWARN= + ;; +mipspro) save_NOWARN= DOWARN="-diag_error 1-10000" -elif test $ct = msc; then + ;; +msc) save_NOWARN="${ccpc}/w" DOWARN="${ccpc}/WX" -elif test $ct = dmc; then - save_NOWARN="${ccpc}-w" - DOWARN="${ccpc}-wx" -elif test $ct = bcc; then - save_NOWARN="${ccpc}-w" - DOWARN="${ccpc}-w!" -elif test $ct = dec; then - : -msg_* flags not used yet, or is -w2 correct? -elif test $ct = xlc; then - save_NOWARN=-qflag=i:e - DOWARN=-qflag=i:i -elif test $ct = tendra; then + ;; +sunpro) + test x"$save_NOWARN" = x"" && save_NOWARN='-errwarn=%none' + ac_flags 0 errwarnnone "$save_NOWARN" + test 1 = $HAVE_CAN_ERRWARNNONE || save_NOWARN= + ac_flags 0 errwarnall "-errwarn=%all" + test 1 = $HAVE_CAN_ERRWARNALL && DOWARN="-errwarn=%all" + ;; +tendra) save_NOWARN=-w -elif test $ct = ucode; then + ;; +ucode) save_NOWARN= DOWARN=-w2 -elif test $ct = watcom; then + ;; +watcom) save_NOWARN= DOWARN=-Wc,-we -else + ;; +xlc) + save_NOWARN=-qflag=i:e + DOWARN=-qflag=i:i + ;; +*) test x"$save_NOWARN" = x"" && save_NOWARN=-Wno-error ac_flags 0 wnoerror "$save_NOWARN" test 1 = $HAVE_CAN_WNOERROR || save_NOWARN= ac_flags 0 werror -Werror test 1 = $HAVE_CAN_WERROR && DOWARN=-Werror -fi - -test $ct = icc && DOWARN="$DOWARN -wd1419" + test $ct = icc && DOWARN="$DOWARN -wd1419" + ;; +esac NOWARN=$save_NOWARN # @@ -1149,7 +1182,16 @@ 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"" && if test $ct = sunpro; then +test x"$i" = x"" && case $ct in +hpcc) + phase=u + ac_flags 1 otwo +O2 + phase=x + ;; +kencc|tcc|tendra) + # no special optimisation + ;; +sunpro) cat >x <<-'EOF' int main(void) { return (0); } #define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p @@ -1159,25 +1201,37 @@ test x"$i" = x"" && if test $ct = sunpro; then yes pad | head -n 256 >>x ac_flags - 1 otwo -xO2 x ac_flags - 1 stackon "${ccpc}/GZ" 'if stack checks can be enabled' #include #include - int main(void) { return ((int)(ptrdiff_t)(sig_t)(ptrdiff_t)kill(0,0)); } + volatile sig_t foo = (sig_t)0; + int main(void) { return (foo == (sig_t)0); } EOF ac_testn sighandler_t '!' sig_t 0 <<-'EOF' #include #include #include - int main(void) { return ((int)(ptrdiff_t)(sighandler_t)(ptrdiff_t)kill(0,0)); } + volatile sighandler_t foo = (sighandler_t)0; + int main(void) { return (foo == (sighandler_t)0); } EOF if test 1 = $HAVE_SIGHANDLER_T; then add_cppflags -Dsig_t=sighandler_t @@ -1509,7 +1574,8 @@ ac_testn __sighandler_t '!' sig_t 0 <<-'EOF' #include #include #include - int main(void) { return ((int)(ptrdiff_t)(__sighandler_t)(ptrdiff_t)kill(0,0)); } + volatile __sighandler_t foo = (__sighandler_t)0; + int main(void) { return (foo == (__sighandler_t)0); } EOF if test 1 = $HAVE___SIGHANDLER_T; then add_cppflags -Dsig_t=__sighandler_t @@ -1532,7 +1598,7 @@ else #define EXTERN #define MKSH_INCLUDES_ONLY #include "sh.h" - __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.622 2013/02/19 18:45:15 tg Exp $"); + __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.645 2013/08/10 13:44:25 tg Exp $"); int main(void) { printf("Hello, World!\n"); return (0); } EOF case $cm in @@ -1748,7 +1814,7 @@ EOF ac_test setresugid <<-'EOF' #include #include - int main(void) { setresuid(0,0,0); return (setresgid(0,0,0)); } + int main(void) { return (setresuid(0,0,0) + setresgid(0,0,0)); } EOF ac_test setgroups setresugid 0 <<-'EOF' @@ -1848,7 +1914,6 @@ ac_testdone ac_cppflags save_CFLAGS=$CFLAGS -test x1 = x$HAVE_CAN_WNOOVERFLOW && CFLAGS="$CFLAGS -Wno-overflow" ac_testn compile_time_asserts_$$ '' 'whether compile-time assertions pass' <<-'EOF' #define MKSH_INCLUDES_ONLY #include "sh.h" @@ -1875,13 +1940,8 @@ cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long)); #ifndef MKSH_LEGACY_MODE /* the next assertion is probably not really needed */ cta(ari_is_4_char, sizeof(mksh_ari_t) == 4); -/* but the next two are; we REQUIRE signed integer wraparound */ +/* but this is */ cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1)); -#ifndef MKSH_GCC55009 -cta(ari_sign_32_bit_and_wrap, - (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1) > - (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 2)); -#endif /* the next assertion is probably not really needed */ cta(uari_is_4_char, sizeof(mksh_uari_t) == 4); /* but the next three are; we REQUIRE unsigned integer wraparound */ @@ -1890,10 +1950,15 @@ cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 cta(uari_wrap_32_bit, (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) > (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4)); +#define NUM 22 +#else +#define NUM 16 #endif /* these are always required */ cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0); cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0); +/* we require these to have the precisely same size and assume 2s complement */ +cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t)); cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t)); cta(ptrdifft_sizet_same_size, sizeof(ptrdiff_t) == sizeof(size_t)); @@ -1901,17 +1966,10 @@ cta(ptrdifft_voidptr_same_size, sizeof(ptrdiff_t) == sizeof(void *)); cta(ptrdifft_funcptr_same_size, sizeof(ptrdiff_t) == sizeof(void (*)(void))); /* our formatting routines assume this */ cta(ptr_fits_in_long, sizeof(ptrdiff_t) <= sizeof(long)); +/* for struct alignment people */ + char padding[64 - NUM]; }; -#ifndef MKSH_LEGACY_MODE -#ifndef MKSH_GCC55009 -#define NUM 22 -#else -#define NUM 21 -#endif -#else -#define NUM 15 -#endif -char ctasserts_dblcheck[sizeof(struct ctasserts) == NUM ? 1 : -1]; +char ctasserts_dblcheck[sizeof(struct ctasserts) == 64 ? 1 : -1]; int main(void) { return (sizeof(ctasserts_dblcheck)); } EOF CFLAGS=$save_CFLAGS @@ -1957,71 +2015,6 @@ EOF fi # -# runtime checks -# once this is more than one, check if we can do runtime -# checks (not cross-compiling) first to save on warnings -# -$e "${bi}run-time checks follow$ao, please ignore any weird errors" - -if ac_testnnd silent_idivwrapv '' '(run-time) whether signed integer division overflows wrap silently' <<-'EOF' - #define MKSH_INCLUDES_ONLY - #include "sh.h" - #if !defined(MKSH_LEGACY_MODE) || HAVE_LONG_32BIT - #define IDIVWRAPV_VL (mksh_uari_t)0x80000000UL - #elif HAVE_LONG_64BIT - #define IDIVWRAPV_VL (mksh_uari_t)0x8000000000000000UL - #else - # error "cannot check this" - #endif - #ifdef SIGFPE - static void fpe_catcher(int) MKSH_A_NORETURN; - #endif - int main(int ac, char **av) { - mksh_ari_t o1, o2, r1, r2; - - #ifdef SIGFPE - signal(SIGFPE, fpe_catcher); - #endif - o1 = (mksh_ari_t)IDIVWRAPV_VL; - o2 = -ac; - r1 = o1 / o2; - r2 = o1 % o2; - if (r1 == o1 && r2 == 0) { - printf("si"); - return (0); - } - printf("no %d %d %d %d %s", (int)o1, (int)o2, (int)r1, - (int)r2, av[0]); - return (1); - } - #ifdef SIGFPE - static const char fpe_msg[] = "no, got SIGFPE, what were they smoking?"; - #define fpe_msglen (sizeof(fpe_msg) - 1) - static void fpe_catcher(int sig MKSH_A_UNUSED) { - _exit(write(1, fpe_msg, fpe_msglen) == fpe_msglen ? 2 : 3); - } - #endif -EOF -then - if test $fv = 0; then - echo "| hrm, compiling this failed, but we will just failback" - else - echo "| running test programme; this will fail if cross-compiling" - echo "| in which case we will gracefully degrade to the default" - ./$tcfn >vv.out 2>&1 - rv=$? - echo "| result: `cat vv.out`" - fv=0 - test $rv = 0 && test x"`cat vv.out`" = x"si" && fv=1 - fi - rmf conftest.c conftest.o ${tcfn}* vv.out - ac_testdone -fi -ac_cppflags - -$e "${bi}end of run-time checks$ao" - -# # Compiler: Praeprocessor (only if needed) # test 0 = $HAVE_SYS_SIGNAME && if ac_testinit cpp_dd '' \ @@ -2120,7 +2113,7 @@ addsrcs USE_PRINTF_BUILTIN printf.c test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose" test -n "$LDSTATIC" && add_cppflags -DMKSH_OPTSTATIC -add_cppflags -DMKSH_BUILD_R=431 +add_cppflags -DMKSH_BUILD_R=481 $e $bi$me: Finished configuration testing, now producing output.$ao @@ -2219,13 +2212,17 @@ cat >test.sh <<-EOF exit \$rv EOF chmod 755 test.sh -if test $cm = llvm; then - emitbc="-emit-llvm -c" -elif test $cm = dragonegg; then +case $cm in +dragonegg) emitbc="-S -flto" -else + ;; +llvm) + emitbc="-emit-llvm -c" + ;; +*) emitbc=-c -fi + ;; +esac echo ": # work around NeXTstep bug" >Rebuild.sh echo set -x >>Rebuild.sh for file in $SRCS; do @@ -2254,7 +2251,7 @@ dragonegg|llvm) esac echo tcfn=$mkshexe >>Rebuild.sh echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh -echo 'test -f $tcfn || exit 1; size $tcfn' >>Rebuild.sh +echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh if test $cm = makefile; then extras='emacsfn.h sh.h sh_flags.h var_spec.h' test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc" @@ -2335,15 +2332,17 @@ test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr" test -f $tcfn || exit 1 test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >$tfn.cat1" || \ rmf $tfn.cat1 -test 0 = $eq && v size $tcfn +test 0 = $eq && v $SIZE $tcfn i=install test -f /usr/ucb/$i && i=/usr/ucb/$i test 1 = $eq && e=: $e $e Installing the shell: $e "# $i -c -s -o root -g bin -m 555 $tfn /bin/$tfn" -$e "# grep -x /bin/$tfn /etc/shells >/dev/null || echo /bin/$tfn >>/etc/shells" -$e "# $i -c -o root -g bin -m 444 dot.mkshrc /usr/share/doc/mksh/examples/" +if test $legacy = 0; then + $e "# grep -x /bin/$tfn /etc/shells >/dev/null || echo /bin/$tfn >>/etc/shells" + $e "# $i -c -o root -g bin -m 444 dot.mkshrc /usr/share/doc/mksh/examples/" +fi $e $e Installing the manual: if test -f $tfn.cat1; then @@ -2351,7 +2350,7 @@ if test -f $tfn.cat1; then "/usr/share/man/cat1/$tfn.0" $e or fi -$e "# $i -c -o root -g bin -m 444 mksh.1 /usr/share/man/man1/$tfn.1" +$e "# $i -c -o root -g bin -m 444 $tfn.1 /usr/share/man/man1/$tfn.1" $e $e Run the regression test suite: ./test.sh $e Please also read the sample file dot.mkshrc and the fine manual. @@ -2389,6 +2388,7 @@ DEBUG_LEAKS enable freeing resources before exiting MKSHRC_PATH "~/.mkshrc" (do not change) MKSH_A4PB force use of arc4random_pushb MKSH_ASSUME_UTF8 (0=disabled, 1=enabled; default: unset) +MKSH_BINSHPOSIX if */sh or */-sh, enable set -o posix MKSH_BINSHREDUCED if */sh or */-sh, enable set -o sh MKSH_CLRTOEOL_STRING "\033[K" MKSH_CLS_STRING "\033[;H\033[J" @@ -2400,7 +2400,6 @@ MKSH_DISABLE_DEPRECATED disable code paths scheduled for later removal MKSH_DISABLE_EXPERIMENTAL disable code not yet comfy for (LTS) snapshots MKSH_DISABLE_TTY_WARNING shut up warning about ctty if OS cant be fixed MKSH_DONT_EMIT_IDSTRING omit RCS IDs from binary -MKSH_GCC55009 DANGER! see http://www.mirbsd.org/mksh.htm#p41 MKSH_MIDNIGHTBSD01ASH_COMPAT set -o sh: additional compatibility quirk MKSH_NOPROSPECTOFWORK disable jobs, co-processes, etc. (do not use) MKSH_NOPWNAM skip PAM calls, for -static on eglibc, Solaris diff --git a/src/check.pl b/src/check.pl index 5e55cd4..ecd8f8d 100644 --- a/src/check.pl +++ b/src/check.pl @@ -1,7 +1,8 @@ -# $MirOS: src/bin/mksh/check.pl,v 1.31 2012/04/06 12:22:14 tg Exp $ -# $OpenBSD: th,v 1.13 2006/05/18 21:27:23 miod Exp $ +# $MirOS: src/bin/mksh/check.pl,v 1.32 2013/07/21 18:35:56 tg Exp $ +# $OpenBSD: th,v 1.16 2013/06/14 20:52:08 millert Exp $ #- -# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 +# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, +# 2012, 2013 # Thorsten Glaser # # Provided that these terms and disclaimer and all copyright notices @@ -171,13 +172,15 @@ BEGIN { use Getopt::Std; use Config; +use File::Temp qw/ :mktemp /; $os = defined $^O ? $^O : 'unknown'; ($prog = $0) =~ s#.*/##; $Usage = < 0): $opt_t\n" if $opt_t !~ /^\d+$/ || $opt_t <= 0; @@ -297,8 +296,6 @@ if (defined $opt_e) { } %old_env = %ENV; -die "$prog: couldn't make directory $tempdir - $!\n" if !mkdir($tempdir, 0777); - chop($pwd = `pwd 2>/dev/null`); die "$prog: couldn't get current working directory\n" if $pwd eq ''; die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd); @@ -316,6 +313,17 @@ $SIG{'ALRM'} = 'catch_sigalrm'; $| = 1; +# Create temp files +($fh, $temps) = mkstemp("${temp_dir}/rts.XXXXXXXX"); +close($fh); +($fh, $tempi) = mkstemp("${temp_dir}/rti.XXXXXXXX"); +close($fh); +($fh, $tempo) = mkstemp("${temp_dir}/rto.XXXXXXXX"); +close($fh); +($fh, $tempe) = mkstemp("${temp_dir}/rte.XXXXXXXX"); +close($fh); +$tempdir = mkdtemp("${temp_dir}/rtd.XXXXXXXX"); + if (-d $test_set) { $file_prefix_skip = length($test_set) + 1; $ret = &process_test_dir($test_set); @@ -433,6 +441,8 @@ run_test local(*test) = @_; local($name) = $test{':full-name'}; + return undef if !&scrub_dir($tempdir); + if (defined $test{'stdin'}) { return undef if !&write_file($tempi, $test{'stdin'}); $ifile = $tempi; @@ -444,8 +454,6 @@ run_test return undef if !&write_file($temps, $test{'script'}); } - return undef if !&scrub_dir($tempdir); - if (!chdir($tempdir)) { print STDERR "$prog: couldn't cd to $tempdir - $!\n"; return undef; diff --git a/src/check.t b/src/check.t index d33febd..8df826f 100644 --- a/src/check.t +++ b/src/check.t @@ -1,7 +1,9 @@ -# $MirOS: src/bin/mksh/check.t,v 1.597 2013/02/19 18:45:17 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.629 2013/08/14 20:26:15 tg Exp $ # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ +# $OpenBSD: regress.t,v 1.15 2013/07/01 17:25:27 jca Exp $ +# $OpenBSD: obsd-regress.t,v 1.5 2013/07/01 17:25:27 jca Exp $ #- # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 @@ -29,7 +31,7 @@ # http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD expected-stdout: - @(#)MIRBSD KSH R43 2013/02/19 + @(#)MIRBSD KSH R48 2013/08/14 description: Check version of shell. stdin: @@ -38,7 +40,7 @@ name: KSH_VERSION category: shell:legacy-no --- expected-stdout: - @(#)LEGACY KSH R43 2013/02/19 + @(#)LEGACY KSH R48 2013/08/14 description: Check version of legacy shell. stdin: @@ -290,6 +292,22 @@ stdin: expected-stdout: = 4 2 = --- +name: arith-lazy-4 +description: + Check that preun/postun not done on non-evaluated side of ternary + operator +stdin: + (( m = n = 0, 1 ? n++ : m++ ? 2 : 3 )) + echo "($n, $m)" + m=0; echo $(( 0 ? ++m : 2 )); echo $m + m=0; echo $(( 0 ? m++ : 2 )); echo $m +expected-stdout: + (1, 0) + 2 + 0 + 2 + 0 +--- name: arith-ternary-prec-1 description: Check precedence of ternary operator vs assignment @@ -363,30 +381,35 @@ expected-stdout: --- name: arith-mandatory description: - If MKSH_GCC55009 is set when compiling, passing of - this test is *mandatory* for a valid mksh executable! + Passing of this test is *mandatory* for a valid mksh executable! category: shell:legacy-no stdin: typeset -i sari=0 typeset -Ui uari=0 typeset -i x=0 - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #0 let --sari --uari - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #1 sari=2147483647 uari=2147483647 - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #2 let ++sari ++uari - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #3 let --sari --uari let 'sari *= 2' 'uari *= 2' let ++sari ++uari - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #4 let ++sari ++uari - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #5 sari=-2147483648 uari=-2147483648 - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #6 let --sari --uari - print -r -- $((x++)):$sari=$uari. + print -r -- $((x++)):$sari=$uari. #7 + (( sari = -5 >> 1 )) + ((# uari = -5 >> 1 )) + print -r -- $((x++)):$sari=$uari. #8 + (( sari = -2 )) + ((# uari = sari )) + print -r -- $((x++)):$sari=$uari. #9 expected-stdout: 0:0=0. 1:-1=4294967295. @@ -396,6 +419,8 @@ expected-stdout: 5:0=0. 6:-2147483648=2147483648. 7:2147483647=2147483647. + 8:-3=2147483645. + 9:-2=4294967294. --- name: arith-unsigned-1 description: @@ -2530,30 +2555,6 @@ expected-stdout: \END end --- -name: heredoc-quoting-unsubst -description: - Check for correct handling of quoted characters in - here documents without substitution (marker is quoted). -stdin: - foo=bar - cat <<-'EOF' - x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x - EOF -expected-stdout: - x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x ---- -name: heredoc-quoting-subst -description: - Check for correct handling of quoted characters in - here documents with substitution (marker is not quoted). -stdin: - foo=bar - cat <<-EOF - x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x - EOF -expected-stdout: - x " \" \ \ $ $ baz `echo baz` bar $foo x ---- name: heredoc-tmpfile-1 description: Check that heredoc temp files aren't removed too soon or too late. @@ -2736,6 +2737,131 @@ expected-stdout: hi Left overs: * --- +name: heredoc-quoting-unsubst +description: + Check for correct handling of quoted characters in + here documents without substitution (marker is quoted). +stdin: + foo=bar + cat <<-'EOF' + x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x + EOF +expected-stdout: + x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x +--- +name: heredoc-quoting-subst +description: + Check for correct handling of quoted characters in + here documents with substitution (marker is not quoted). +stdin: + foo=bar + cat <<-EOF + x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x + EOF +expected-stdout: + x " \" \ \ $ $ baz `echo baz` bar $foo x +--- +name: single-quotes-in-braces +description: + Check that single quotes inside unquoted {} are treated as quotes +stdin: + foo=1 + echo ${foo:+'blah $foo'} +expected-stdout: + blah $foo +--- +name: single-quotes-in-quoted-braces +description: + Check that single quotes inside quoted {} are treated as + normal char +stdin: + foo=1 + echo "${foo:+'blah $foo'}" +expected-stdout: + 'blah 1' +--- +name: single-quotes-in-braces-nested +description: + Check that single quotes inside unquoted {} are treated as quotes, + even if that's inside a double-quoted command expansion +stdin: + foo=1 + echo "$( echo ${foo:+'blah $foo'})" +expected-stdout: + blah $foo +--- +name: single-quotes-in-brace-pattern +description: + Check that single quotes inside {} pattern are treated as quotes +stdin: + foo=1234 + echo ${foo%'2'*} "${foo%'2'*}" ${foo%2'*'} "${foo%2'*'}" +expected-stdout: + 1 1 1234 1234 +--- +name: single-quotes-in-heredoc-braces +description: + Check that single quotes inside {} in heredoc are treated + as normal char +stdin: + foo=1 + cat < + <1> <2> - <3> - <4> + <3> + <4> --- name: IFS-space-colon-1 description: @@ -3772,22 +3898,17 @@ expected-stdout: --- name: integer-base-check-flat description: - Check behaviour does not match POSuX, because a not type-safe - scripting language has *no* business interpreting "010" as octal -category: shell:legacy-no + Check behaviour does not match POSuX (except if set -o posix), + because a not type-safe scripting language has *no* business + interpreting the string "010" as octal numer eight (dangerous). stdin: - echo :$((10)).$((010)).$((0x10)). + echo 1 "$("$__progname" -c 'echo :$((10))/$((010)),$((0x10)):')" . + echo 2 "$("$__progname" -o posix -c 'echo :$((10))/$((010)),$((0x10)):')" . + echo 3 "$("$__progname" -o sh -c 'echo :$((10))/$((010)),$((0x10)):')" . expected-stdout: - :10.10.16. ---- -name: integer-base-check-flat-legacy -description: - Check behaviour matches POSuX for LEGACY KSH -category: shell:legacy-yes -stdin: - echo :$((10)).$((010)).$((0x10)). -expected-stdout: - :10.8.16. + 1 :10/10,16: . + 2 :10/8,16: . + 3 :10/10,16: . --- name: integer-base-check-numeric-from description: @@ -3913,6 +4034,13 @@ expected-stdout: s:-9223372036854775808.-1.0. u:9223372036854775808.18446744073709551615.0. --- +name: integer-size-FAIL-to-detect +description: + Notify the user that their ints are not 32 or 64 bit +category: int:u +stdin: + : +--- name: lineno-stdin description: See if $LINENO is updated and can be modified. @@ -4245,7 +4373,7 @@ description: should print 0 according to POSIX (dash, bash, ksh93, posh) but not 0 according to the getopt(1) manual page, ksh88, and Bourne sh (such as /bin/sh on Solaris). - In mksh R39b, we honour POSIX except when -o sh is set. + We honour POSIX except when -o sh is set. category: shell:legacy-no stdin: showf() { @@ -4265,10 +4393,15 @@ stdin: showf set -- `false` echo rv=$? + set -o posix -o sh + showf + set -- `false` + echo rv=$? expected-stdout: FPOSIX=0 FSH=0 rv=0 FPOSIX=0 FSH=1 rv=1 FPOSIX=1 FSH=0 rv=0 + FPOSIX=1 FSH=1 rv=0 --- name: regression-10-legacy description: @@ -4297,10 +4430,15 @@ stdin: showf set -- `false` echo rv=$? + set -o posix -o sh + showf + set -- `false` + echo rv=$? expected-stdout: FPOSIX=0 FSH=0 rv=1 FPOSIX=0 FSH=1 rv=1 - FPOSIX=1 FSH=0 rv=1 + FPOSIX=1 FSH=0 rv=0 + FPOSIX=1 FSH=1 rv=0 --- name: regression-11 description: @@ -4663,18 +4801,16 @@ expected-stdout: --- name: regression-39 description: - set -e: errors in command substitutions aren't ignored - Not clear if they should be or not... bash passes here - this may actually be required for make, so changed the - test to make this an mksh feature, not a bug -arguments: !-e! + Only posh and oksh(2013-07) say “hi” below; FreeBSD sh, + GNU bash in POSIX mode, dash, ksh93, mksh don’t. All of + them exit 0. The POSIX behaviour is needed by BSD make. stdin: + set -e echo `false; echo hi` -#expected-fail: yes -#expected-stdout: -# hi + echo $? expected-stdout: + 0 --- name: regression-40 description: @@ -5984,6 +6120,27 @@ expected-stdout: EXtrap = noeval-undef 1 . --- +name: exit-trap-interactive +description: + Check that interactive shell doesn't exit via EXIT trap on syntax error +arguments: !-i! +stdin: + trap -- EXIT + echo Syntax error < + echo 'After error 1' + trap 'echo Exit trap' EXIT + echo Syntax error < + echo 'After error 2' + trap 'echo Exit trap' EXIT + exit + echo 'After exit' +expected-stdout: + After error 1 + After error 2 + Exit trap +expected-stderr-pattern: + /syntax error: 'newline' unexpected/ +--- name: test-stlt-1 description: Check that test also can handle string1 < string2 etc. @@ -6178,7 +6335,7 @@ expected-stdout: --- name: sh-mode-2a description: - Check that sh mode is *not* automatically turned on + Check that posix or sh mode is *not* automatically turned on category: !binsh stdin: ln -s "$__progname" ksh || cp "$__progname" ksh @@ -6187,7 +6344,7 @@ stdin: ln -s "$__progname" ./-sh || cp "$__progname" ./-sh for shell in {,-}{,k}sh; do print -- $shell $(./$shell +l -c \ - '[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh') + '[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh') done expected-stdout: sh nosh @@ -6197,7 +6354,7 @@ expected-stdout: --- name: sh-mode-2b description: - Check that sh mode *is* automatically turned on + Check that posix or sh mode *is* automatically turned on category: binsh stdin: ln -s "$__progname" ksh || cp "$__progname" ksh @@ -6206,7 +6363,7 @@ stdin: ln -s "$__progname" ./-sh || cp "$__progname" ./-sh for shell in {,-}{,k}sh; do print -- $shell $(./$shell +l -c \ - '[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh') + '[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh') done expected-stdout: sh sh @@ -6275,6 +6432,28 @@ expected-stdout: PIPESTATUS[0]=0 8 PIPESTATUS[0]=0 PIPESTATUS[1]=0 . --- +name: pipeline-4 +description: + Check that "set -o pipefail" does what it's supposed to +stdin: + echo 1 "$("$__progname" -c '(exit 12) | (exit 23) | (exit 42); echo $?')" . + echo 2 "$("$__progname" -c '! (exit 12) | (exit 23) | (exit 42); echo $?')" . + echo 3 "$("$__progname" -o pipefail -c '(exit 12) | (exit 23) | (exit 42); echo $?')" . + echo 4 "$("$__progname" -o pipefail -c '! (exit 12) | (exit 23) | (exit 42); echo $?')" . + echo 5 "$("$__progname" -c '(exit 23) | (exit 42) | :; echo $?')" . + echo 6 "$("$__progname" -c '! (exit 23) | (exit 42) | :; echo $?')" . + echo 7 "$("$__progname" -o pipefail -c '(exit 23) | (exit 42) | :; echo $?')" . + echo 8 "$("$__progname" -o pipefail -c '! (exit 23) | (exit 42) | :; echo $?')" . +expected-stdout: + 1 42 . + 2 0 . + 3 42 . + 4 0 . + 5 0 . + 6 1 . + 7 42 . + 8 0 . +--- name: persist-history-1 description: Check if persistent history saving works @@ -7551,6 +7730,28 @@ stdin: expected-stdout: ab --- +name: print-cr +description: + Check that CR+LF is not collapsed into LF as some MSYS shells wrongly do +stdin: + echo '#!'"$__progname" >foo + cat >>foo <<-'EOF' + print -n -- '220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT\r\n220->> Bitte keine Werbung einwerfen! <<\r\r\n220 Who do you wanna pretend to be today' + print \? + EOF + chmod +x foo + echo "[$(./foo)]" + ./foo | while IFS= read -r line; do + print -r -- "{$line}" + done +expected-stdout: + [220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT + 220->> Bitte keine Werbung einwerfen! << + 220 Who do you wanna pretend to be today? ] + {220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT } + {220->> Bitte keine Werbung einwerfen! << } + {220 Who do you wanna pretend to be today? } +--- name: print-nul-chars description: Check handling of NUL characters for print and COMSUB @@ -8363,6 +8564,7 @@ name: bashiop-1 description: Check if GNU bash-like I/O redirection works Part 1: this is also supported by GNU bash +category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -8383,6 +8585,7 @@ name: bashiop-2a description: Check if GNU bash-like I/O redirection works Part 2: this is *not* supported by GNU bash +category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -8403,6 +8606,7 @@ name: bashiop-2b description: Check if GNU bash-like I/O redirection works Part 2: this is *not* supported by GNU bash +category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -8423,6 +8627,7 @@ name: bashiop-2c description: Check if GNU bash-like I/O redirection works Part 2: this is supported by GNU bash 4 only +category: shell:legacy-no stdin: echo mir >foo set -o noclobber @@ -8446,6 +8651,7 @@ name: bashiop-3a description: Check if GNU bash-like I/O redirection fails correctly Part 1: this is also supported by GNU bash +category: shell:legacy-no stdin: echo mir >foo set -o noclobber @@ -8467,6 +8673,7 @@ name: bashiop-3b description: Check if GNU bash-like I/O redirection fails correctly Part 2: this is *not* supported by GNU bash +category: shell:legacy-no stdin: echo mir >foo set -o noclobber @@ -8490,6 +8697,7 @@ description: Check if GNU bash-like I/O redirection works Part 4: this is also supported by GNU bash, but failed in some mksh versions +category: shell:legacy-no stdin: exec 3>&1 function threeout { @@ -8511,6 +8719,34 @@ expected-stdout: ras dwa --- +name: bashiop-5-normal +description: + Check if GNU bash-like I/O redirection is only supported + in !POSIX !sh mode as it breaks existing scripts' syntax +category: shell:legacy-no +stdin: + :>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(/dev/console 0<&1 2>&1 - #COMSUB_EXPRSUB - echo $(true) $((1+ 2)) + #COMSUB_EXPRSUB_FUNSUB_VALSUB + echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} #QCHAR_OQUOTE_CQUOTE echo fo\ob\"a\`r\'b\$az echo "fo\ob\"a\`r\'b\$az" @@ -9041,7 +9417,7 @@ expected-stdout: } inline_TWHILE() { i=1 - while let " i < 10 " + while let] " i < 10 " do echo $i let ++i @@ -9051,20 +9427,20 @@ expected-stdout: i=1; while (( i < 10 )); do echo $i; let ++i; done ); } function comsub_TWHILE { - x=$(i=1 ; while let " i < 10 " ; do echo $i ; let ++i ; done ) + x=$(i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) } function reread_TWHILE { x=$(( i=1; while (( i < 10 )); do echo $i; let ++i; done )|tr u x); } function reread_TWHILE { - x=$(( i=1 ; while let " i < 10 " ; do echo $i ; let ++i ; done ) | tr u x ) + x=$(( i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) | tr u x ) } inline_TUNTIL() { i=10; until (( !--i )) ; do echo $i; done } inline_TUNTIL() { i=10 - until let " !--i " + until let] " !--i " do echo $i done @@ -9073,13 +9449,13 @@ expected-stdout: i=10; until (( !--i )) ; do echo $i; done ); } function comsub_TUNTIL { - x=$(i=10 ; until let " !--i " ; do echo $i ; done ) + x=$(i=10 ; until let] " !--i " ; do echo $i ; done ) } function reread_TUNTIL { x=$(( i=10; until (( !--i )) ; do echo $i; done )|tr u x); } function reread_TUNTIL { - x=$(( i=10 ; until let " !--i " ; do echo $i ; done ) | tr u x ) + x=$(( i=10 ; until let] " !--i " ; do echo $i ; done ) | tr u x ) } inline_TCOPROC() { cat * |& ls @@ -9229,23 +9605,23 @@ expected-stdout: function reread_IORDWR_IODUP { x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x ) } - inline_COMSUB_EXPRSUB() { - echo $(true) $((1+ 2)) + inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() { + echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} } - inline_COMSUB_EXPRSUB() { - echo $(true ) $((1+ 2)) + inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() { + echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} } - function comsub_COMSUB_EXPRSUB { x=$( - echo $(true) $((1+ 2)) + function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$( + echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} ); } - function comsub_COMSUB_EXPRSUB { - x=$(echo $(true ) $((1+ 2)) ) + function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { + x=$(echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) } - function reread_COMSUB_EXPRSUB { x=$(( - echo $(true) $((1+ 2)) + function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$(( + echo $(true) $((1+ 2)) ${ :;} ${| REPLY=x;} )|tr u x); } - function reread_COMSUB_EXPRSUB { - x=$(( echo $(true ) $((1+ 2)) ) | tr u x ) + function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { + x=$(( echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | tr u x ) } inline_QCHAR_OQUOTE_CQUOTE() { echo fo\ob\"a\`r\'b\$az @@ -9693,7 +10069,7 @@ expected-stdout: } inline_TWHILE() { i=1 - while let " i < 10 " >&3 + while let] " i < 10 " >&3 do echo $i let ++i @@ -9703,20 +10079,20 @@ expected-stdout: i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3 ); } function comsub_TWHILE { - x=$(i=1 ; while let " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) + x=$(i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) } function reread_TWHILE { x=$(( i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3 )|tr u x); } function reread_TWHILE { - x=$(( i=1 ; while let " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) | tr u x ) + x=$(( i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) | tr u x ) } inline_TUNTIL() { i=10; until (( !--i )) >&3 ; do echo $i; done >&3 } inline_TUNTIL() { i=10 - until let " !--i " >&3 + until let] " !--i " >&3 do echo $i done >&3 @@ -9725,13 +10101,13 @@ expected-stdout: i=10; until (( !--i )) >&3 ; do echo $i; done >&3 ); } function comsub_TUNTIL { - x=$(i=10 ; until let " !--i " >&3 ; do echo $i ; done >&3 ) + x=$(i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) } function reread_TUNTIL { x=$(( i=10; until (( !--i )) >&3 ; do echo $i; done >&3 )|tr u x); } function reread_TUNTIL { - x=$(( i=10 ; until let " !--i " >&3 ; do echo $i ; done >&3 ) | tr u x ) + x=$(( i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) | tr u x ) } inline_TCOPROC() { cat * >&3 |& >&3 ls @@ -9831,6 +10207,38 @@ expected-stdout: 2:ya x2,1,0. 3:ya,1,3. --- +name: valsub-1 +description: + Check that "value substitutions" work as advertised +stdin: + x=1 + y=2 + z=3 + REPLY=4 + echo "before: x<$x> y<$y> z<$z> R<$REPLY>" + x=${| + local y + echo "begin: x<$x> y<$y> z<$z> R<$REPLY>" + x=5 + y=6 + z=7 + REPLY=8 + echo "end: x<$x> y<$y> z<$z> R<$REPLY>" + } + echo "after: x<$x> y<$y> z<$z> R<$REPLY>" + # ensure trailing newlines are kept + t=${|REPLY=$'foo\n\n';} + typeset -p t + echo -n this used to segfault + echo ${|true;}$(true). +expected-stdout: + before: x<1> y<2> z<3> R<4> + begin: x<1> y<> z<3> R<> + end: x<5> y<6> z<7> R<8> + after: x<8> y<2> z<7> R<4> + typeset t=$'foo\n\n' + this used to segfault. +--- name: test-stnze-1 description: Check that the short form [ $x ] works @@ -10156,7 +10564,7 @@ description: time-limit: 3 stdin: baz() { - typeset -n foo=foo + typeset -n foo=fnord fnord=foo foo[0]=bar } set -A foo bad @@ -10165,7 +10573,9 @@ stdin: echo blah $foo . expected-stdout: sind bad . - blah bar . + blah bad . +expected-stderr-pattern: + /fnord: expression recurses on parameter/ --- name: better-parens-1a description: @@ -10743,3 +11153,19 @@ stdin: done Lb64decode $s >/dev/null --- +name: xtrace-1 +description: + Check that "set -x" doesn't redirect too quickly +stdin: + print '#!'"$__progname" >bash + cat >>bash <<'EOF' + echo 'GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10) + Copyright (C) 2002 Free Software Foundation, Inc.' + EOF + chmod +x bash + "$__progname" -xc 'foo=$(./bash --version 2>&1 | head -1); echo "=$foo="' +expected-stdout: + =GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)= +expected-stderr-pattern: + /.*/ +--- diff --git a/src/dot.mkshrc b/src/dot.mkshrc index 5ea4b91..cbd13ed 100644 --- a/src/dot.mkshrc +++ b/src/dot.mkshrc @@ -1,5 +1,5 @@ # $Id$ -# $MirOS: src/bin/mksh/dot.mkshrc,v 1.77 2013/02/17 15:58:26 tg Exp $ +# $MirOS: src/bin/mksh/dot.mkshrc,v 1.84 2013/08/10 13:43:50 tg Exp $ #- # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 @@ -22,35 +22,76 @@ #- # ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells +# catch non-mksh (including lksh) trying to shell this file +case $KSH_VERSION in +*MIRBSD\ KSH*) ;; +*) return 0 ;; +esac + PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \ 2>/dev/null)} = *([ ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \ 2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100} -function precmd { +: ${MKSH:=$(whence -p mksh)}; PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${| local e=$? - (( e )) && print -n "$e|" - # precmd is required to retain the errorlevel when ${ …;} is used + (( e )) && REPLY+="$e|" + REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)} + REPLY+=@${HOSTNAME%%.*}: + + local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~} + local m=${%d} n p=...; (( m > 0 )) || m=${#d} + (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p= + REPLY+=$p$d + return $e -} -PS1=$'\001\r''${ precmd;}${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \? - )}@${HOSTNAME%%.*}:${ local e=$? d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || \ - d=${d/#$p/~}; local m=${%d} n p=...; (( m > 0 )) || m=${#d} - (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || \ - p=; print -nr -- "$p$d"; return $e;} '"$PS1 " -: ${MKSH:=$(whence -p mksh)}; export EDITOR HOSTNAME MKSH TERM USER +} '"$PS1 "; export EDITOR HOSTNAME MKSH TERM USER alias ls=ls unalias ls alias l='ls -F' alias la='l -a' alias ll='l -l' alias lo='l -alo' +alias doch='sudo mksh -c "$(fc -ln -1)"' whence -p rot13 >/dev/null || alias rot13='tr \ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM' -whence -p hd >/dev/null || function hd { - hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \ - -e '" |" "%_p"' -e '"|\n"' "$@" -} +if whence -p hd >/dev/null; then :; elif whence -p hexdump >/dev/null; then + function hd { + hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \ + -e '" |" "%_p"' -e '"|\n"' "$@" + } +else + function hd { + local -Uui16 -Z11 pos=0 + local -Uui16 -Z5 hv=2147483647 + local dasc line i + + cat "$@" | { set +U; if read -arN -1 line; then + typeset -i1 line + i=0 + while (( i < ${#line[*]} )); do + hv=${line[i++]} + if (( (pos & 15) == 0 )); then + (( pos )) && print -r -- "$dasc|" + print -n "${pos#16#} " + dasc=' |' + fi + print -n "${hv#16#} " + if (( (hv < 32) || (hv > 126) )); then + dasc+=. + else + dasc+=${line[i-1]#1#} + fi + (( (pos++ & 15) == 7 )) && print -n -- '- ' + done + while (( pos & 15 )); do + print -n ' ' + (( (pos++ & 15) == 7 )) && print -n -- '- ' + done + (( hv == 2147483647 )) || print -r -- "$dasc|" + fi; } + } +fi # Berkeley C shell compatible dirs, popd, and pushd functions # Z shell compatible chpwd() hook, used to update DIRSTACK[0] @@ -222,7 +263,7 @@ function Lb64decode { [[ -o utf8-mode ]]; local u=$? set +U local c s="$*" t= - [[ -n $s ]] || { s=$(cat;print x); s=${s%x}; } + [[ -n $s ]] || { s=$(cat; print x); s=${s%x}; } local -i i=0 j=0 n=${#s} p=0 v x local -i16 o diff --git a/src/edit.c b/src/edit.c index 12fb4eb..675e7ed 100644 --- a/src/edit.c +++ b/src/edit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: edit.c,v 1.37 2013/01/21 10:13:24 halex Exp $ */ +/* $OpenBSD: edit.c,v 1.38 2013/06/03 15:41:59 tedu Exp $ */ /* $OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $ */ /* $OpenBSD: emacs.c,v 1.44 2011/09/05 04:50:33 marco Exp $ */ /* $OpenBSD: vi.c,v 1.26 2009/06/29 22:50:19 martynas Exp $ */ @@ -28,7 +28,7 @@ #ifndef MKSH_NO_CMDLINE_EDITING -__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.265 2013/02/10 19:05:36 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.270 2013/08/14 20:26:17 tg Exp $"); /* * in later versions we might use libtermcap for this, but since external @@ -66,7 +66,7 @@ static X_chars edchars; static char editmode; static int xx_cols; /* for Emacs mode */ static int modified; /* buffer has been "modified" */ -static char holdbuf[LINE]; /* place to hold last edit buffer */ +static char *holdbufp; /* place to hold last edit buffer */ static int x_getc(void); static void x_putcf(int); @@ -81,10 +81,10 @@ static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool); static int x_basename(const char *, const char *); static void x_free_words(int, char **); static int x_escape(const char *, size_t, int (*)(const char *, size_t)); -static int x_emacs(char *, size_t); +static int x_emacs(char *); static void x_init_prompt(void); #if !MKSH_S_NOVI -static int x_vi(char *, size_t); +static int x_vi(char *); #endif #define x_flush() shf_flush(shl_out) @@ -110,17 +110,17 @@ static int x_e_rebuildline(const char *); * read an edited command line */ int -x_read(char *buf, size_t len) +x_read(char *buf) { int i; x_mode(true); modified = 1; if (Flag(FEMACS) || Flag(FGMACS)) - i = x_emacs(buf, len); + i = x_emacs(buf); #if !MKSH_S_NOVI else if (Flag(FVI)) - i = x_vi(buf, len); + i = x_vi(buf); #endif else /* internal error */ @@ -919,7 +919,6 @@ static bool x_adj_ok; */ static int x_adj_done; /* is incremented by x_adjust() */ -static int x_col; static int x_displen; static int x_arg; /* general purpose arg */ static bool x_arg_defaulted; /* x_arg not explicitly set; defaulted to 1 */ @@ -944,9 +943,6 @@ static int x_curprefix; static char *macroptr; /* bind key macro active? */ #endif #if !MKSH_S_NOVI -static int cur_col; /* current column on line */ -static int pwidth; /* width of prompt */ -static int prompt_trunc; /* how much of prompt to truncate */ static int winwidth; /* width of window */ static char *wbuf[2]; /* window buffers */ static int wbuf_len; /* length of window buffers (x_cols - 3) */ @@ -955,13 +951,16 @@ static char morec; /* more character at right of window */ static int lastref; /* argument to last refresh() */ static int holdlen; /* length of holdbuf */ #endif -static bool prompt_redraw; /* false if newline forced after prompt */ +static int pwidth; /* width of prompt */ +static int prompt_trunc; /* how much of prompt to truncate or -1 */ +static int x_col; /* current column on line */ static int x_ins(const char *); static void x_delete(size_t, bool); static size_t x_bword(void); static size_t x_fword(bool); static void x_goto(char *); +static char *x_bs0(char *, char *); static void x_bs3(char **); static int x_size_str(char *); static int x_size2(char *, char **); @@ -1170,30 +1169,25 @@ x_e_getmbc(char *sbuf) static void x_init_prompt(void) { - x_col = promptlen(prompt); - x_adj_ok = true; - prompt_redraw = true; - if (x_col >= xx_cols) - x_col %= xx_cols; - x_displen = xx_cols - 2 - x_col; - x_adj_done = 0; - - pprompt(prompt, 0); - if (x_displen < 1) { - x_col = 0; - x_displen = xx_cols - 2; + prompt_trunc = pprompt(prompt, 0); + pwidth = prompt_trunc % x_cols; + prompt_trunc -= pwidth; + if ((mksh_uari_t)pwidth > ((mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE)) { + /* force newline after prompt */ + prompt_trunc = -1; + pwidth = 0; x_e_putc2('\n'); - prompt_redraw = false; } } static int -x_emacs(char *buf, size_t len) +x_emacs(char *buf) { int c, i; unsigned char f; - xbp = xbuf = buf; xend = buf + len; + xbp = xbuf = buf; + xend = buf + LINE; xlp = xcp = xep = buf; *xcp = 0; xlp_valid = true; @@ -1202,8 +1196,10 @@ x_emacs(char *buf, size_t len) x_histp = histptr + 1; x_last_command = XFUNC_error; - xx_cols = x_cols; x_init_prompt(); + x_displen = (xx_cols = x_cols) - 2 - (x_col = pwidth); + x_adj_done = 0; + x_adj_ok = true; x_histncp = NULL; if (x_nextcmd >= 0) { @@ -1561,11 +1557,7 @@ x_fword(bool move) static void x_goto(char *cp) { - if (cp >= xep) - cp = xep; - else if (UTFMODE) - while ((cp > xbuf) && ((*cp & 0xC0) == 0x80)) - --cp; + cp = cp >= xep ? xep : x_bs0(cp, xbuf); if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) { /* we are heading off screen */ xcp = cp; @@ -1581,16 +1573,22 @@ x_goto(char *cp) } } +static char * +x_bs0(char *cp, char *lower_bound) +{ + if (UTFMODE) + while ((!lower_bound || (cp > lower_bound)) && + ((*(unsigned char *)cp & 0xC0) == 0x80)) + --cp; + return (cp); +} + static void x_bs3(char **p) { int i; - (*p)--; - if (UTFMODE) - while (((unsigned char)**p & 0xC0) == 0x80) - (*p)--; - + *p = x_bs0((*p) - 1, NULL); i = x_size2(*p, NULL); while (i--) x_e_putc2('\b'); @@ -1824,7 +1822,7 @@ x_load_hist(char **hp) char *sp = NULL; if (hp == histptr + 1) { - sp = holdbuf; + sp = holdbufp; modified = 0; } else if (hp < history || hp > histptr) { x_e_putc2(7); @@ -1835,7 +1833,7 @@ x_load_hist(char **hp) x_histp = hp; oldsize = x_size_str(xbuf); if (modified) - strlcpy(holdbuf, xbuf, sizeof(holdbuf)); + strlcpy(holdbufp, xbuf, LINE); strlcpy(xbuf, sp, xend - xbuf); xbp = xbuf; xep = xcp = xbuf + strlen(xbuf); @@ -2080,7 +2078,7 @@ x_cls(int c MKSH_A_UNUSED) static void x_redraw(int limit) { - int i, j, x_trunc = 0; + int i, j; char *cp; x_adj_ok = false; @@ -2090,19 +2088,11 @@ x_redraw(int limit) x_e_putc2('\r'); x_flush(); if (xbp == xbuf) { - x_col = promptlen(prompt); - if (x_col >= xx_cols) - x_trunc = (x_col / xx_cols) * xx_cols; - if (prompt_redraw) - pprompt(prompt, x_trunc); - } - if (x_col >= xx_cols) - x_col %= xx_cols; - x_displen = xx_cols - 2 - x_col; - if (x_displen < 1) { - x_col = 0; - x_displen = xx_cols - 2; + if (prompt_trunc != -1) + pprompt(prompt, prompt_trunc); + x_col = pwidth; } + x_displen = xx_cols - 2 - x_col; xlp_valid = false; x_zots(xbp); if (xbp != xbuf || xep > xlp) @@ -2816,16 +2806,42 @@ do_complete( static void x_adjust(void) { - /* flag the fact that we were called. */ + int col_left, n; + + /* flag the fact that we were called */ x_adj_done++; + /* - * we had a problem if the prompt length > xx_cols / 2 + * calculate the amount of columns we need to "go back" + * from xcp to set xbp to (but never < xbuf) to 2/3 of + * the display width; take care of pwidth though */ - if ((xbp = xcp - (x_displen / 2)) < xbuf) - xbp = xbuf; - if (UTFMODE) - while ((xbp > xbuf) && ((*xbp & 0xC0) == 0x80)) - --xbp; + if ((col_left = xx_cols * 2 / 3) < MIN_EDIT_SPACE) { + /* + * cowardly refuse to do anything + * if the available space is too small; + * fall back to dumb pdksh code + */ + if ((xbp = xcp - (x_displen / 2)) < xbuf) + xbp = xbuf; + /* elide UTF-8 fixup as penalty */ + goto x_adjust_out; + } + + /* fix up xbp to just past a character end first */ + xbp = xcp >= xep ? xep : x_bs0(xcp, xbuf); + /* walk backwards */ + while (xbp > xbuf && col_left > 0) { + xbp = x_bs0(xbp - 1, xbuf); + col_left -= (n = x_size2(xbp, NULL)); + } + /* check if we hit the prompt */ + if (xbp == xbuf && xcp != xbuf && col_left >= 0 && col_left < pwidth) { + /* so we did; force scrolling occurs */ + xbp += utf_ptradj(xbp); + } + + x_adjust_out: xlp_valid = false; x_redraw(xx_cols); x_flush(); @@ -3345,9 +3361,9 @@ static void yank_range(int, int); static int bracktype(int); static void save_cbuf(void); static void restore_cbuf(void); -static int putbuf(const char *, ssize_t, int); +static int putbuf(const char *, ssize_t, bool); static void del_range(int, int); -static int findch(int, int, int, int); +static int findch(int, int, bool, bool); static int forwword(int); static int backword(int); static int endword(int); @@ -3411,11 +3427,11 @@ static const unsigned char classify[128] = { /* 8 @ A B C D E F G */ vC|vX, vC, vM, vC, vC, vM, vM|vX, vC|vU|vZ, /* 9 H I J K L M N O */ - 0, vC, 0, 0, 0, 0, vC|vU, 0, + 0, vC, 0, 0, 0, 0, vC|vU, vU, /* A P Q R S T U V W */ vC, 0, vC, vC, vM|vX, vC, 0, vM, /* B X Y Z [ \ ] ^ _ */ - vC, vC|vU, 0, 0, vC|vZ, 0, vM, vC|vZ, + vC, vC|vU, 0, vU, vC|vZ, 0, vM, vC|vZ, /* C ` a b c d e f g */ 0, vC, vM, vE, vE, vM, vM|vX, vC|vZ, /* D h i j k l m n o */ @@ -3443,25 +3459,24 @@ static const unsigned char classify[128] = { #define VLIT 8 /* ^V */ #define VSEARCH 9 /* /, ? */ #define VVERSION 10 /* ^V */ - -static char undocbuf[LINE]; +#define VPREFIX2 11 /* ^[[ and ^[O in insert mode */ static struct edstate *save_edstate(struct edstate *old); static void restore_edstate(struct edstate *old, struct edstate *news); static void free_edstate(struct edstate *old); static struct edstate ebuf; -static struct edstate undobuf = { undocbuf, 0, LINE, 0, 0 }; +static struct edstate undobuf; -static struct edstate *es; /* current editor state */ +static struct edstate *es; /* current editor state */ static struct edstate *undo; -static char ibuf[LINE]; /* input buffer */ -static int first_insert; /* set when starting in insert mode */ +static char *ibuf; /* input buffer */ +static bool first_insert; /* set when starting in insert mode */ static int saved_inslen; /* saved inslen for first insert */ static int inslen; /* length of input buffer */ static int srchlen; /* length of current search pattern */ -static char ybuf[LINE]; /* yank buffer */ +static char *ybuf; /* yank buffer */ static int yanklen; /* length of yank buffer */ static int fsavecmd = ' '; /* last find command */ static int fsavech; /* character to find */ @@ -3469,7 +3484,7 @@ static char lastcmd[MAXVICMD]; /* last non-move command */ static int lastac; /* argcnt for lastcmd */ static int lastsearch = ' '; /* last search command */ static char srchpat[SRCHLEN]; /* last search pattern */ -static int insert; /* non-zero in insert mode */ +static int insert; /* <>0 in insert mode */ static int hnum; /* position in history */ static int ohnum; /* history line copied (after mod) */ static int hlast; /* 1 past last position in history */ @@ -3495,7 +3510,7 @@ static enum expand_mode { } expanded; static int -x_vi(char *buf, size_t len) +x_vi(char *buf) { int c; @@ -3503,36 +3518,29 @@ x_vi(char *buf, size_t len) ohnum = hnum = hlast = histnum(-1) + 1; insert = INSERT; saved_inslen = inslen; - first_insert = 1; + first_insert = true; inslen = 0; vi_macro_reset(); + ebuf.cbuf = buf; + if (undobuf.cbuf == NULL) { + ibuf = alloc(LINE, AEDIT); + ybuf = alloc(LINE, AEDIT); + undobuf.cbuf = alloc(LINE, AEDIT); + } + undobuf.cbufsize = ebuf.cbufsize = LINE; + undobuf.linelen = ebuf.linelen = 0; + undobuf.cursor = ebuf.cursor = 0; + undobuf.winleft = ebuf.winleft = 0; es = &ebuf; - es->cbuf = buf; undo = &undobuf; - undo->cbufsize = es->cbufsize = len > LINE ? LINE : len; - - es->linelen = undo->linelen = 0; - es->cursor = undo->cursor = 0; - es->winleft = undo->winleft = 0; - cur_col = promptlen(prompt); - prompt_trunc = (cur_col / x_cols) * x_cols; - cur_col -= prompt_trunc; - - pprompt(prompt, 0); - if ((mksh_uari_t)cur_col > (mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE) { - prompt_redraw = false; - cur_col = 0; - x_putc('\n'); - } else - prompt_redraw = true; - pwidth = cur_col; + x_init_prompt(); + x_col = pwidth; - if (!wbuf_len || wbuf_len != x_cols - 3) { - wbuf_len = x_cols - 3; - wbuf[0] = aresize(wbuf[0], wbuf_len, APERM); - wbuf[1] = aresize(wbuf[1], wbuf_len, APERM); + if (wbuf_len != x_cols - 3 && ((wbuf_len = x_cols - 3))) { + wbuf[0] = aresize(wbuf[0], wbuf_len, AEDIT); + wbuf[1] = aresize(wbuf[1], wbuf_len, AEDIT); } if (wbuf_len) { memset(wbuf[0], ' ', wbuf_len); @@ -3589,11 +3597,11 @@ x_vi(char *buf, size_t len) x_putc('\n'); x_flush(); - if (c == -1 || (ssize_t)len <= es->linelen) + if (c == -1 || (ssize_t)LINE <= es->linelen) return (-1); if (es->cbuf != buf) - memmove(buf, es->cbuf, es->linelen); + memcpy(buf, es->cbuf, es->linelen); buf[es->linelen++] = '\n'; @@ -3644,10 +3652,8 @@ vi_hook(int ch) save_cbuf(); es->cursor = 0; es->linelen = 0; - if (ch == '/') { - if (putbuf("/", 1, 0) != 0) - return (-1); - } else if (putbuf("?", 1, 0) != 0) + if (putbuf(ch == '/' ? "/" : "?", 1, + false) != 0) return (-1); refresh(0); } @@ -3656,7 +3662,7 @@ vi_hook(int ch) es->cursor = 0; es->linelen = 0; putbuf(KSH_VERSION, - strlen(KSH_VERSION), 0); + strlen(KSH_VERSION), false); refresh(0); } } @@ -3805,10 +3811,35 @@ vi_hook(int ch) return (0); } break; + + case VPREFIX2: + state = VFAIL; + switch (ch) { + case 'A': + /* the cursor may not be at the BOL */ + if (!es->cursor) + break; + /* nor further in the line than we can search for */ + if ((size_t)es->cursor >= sizeof(srchpat) - 1) + es->cursor = sizeof(srchpat) - 2; + /* anchor the search pattern */ + srchpat[0] = '^'; + /* take the current line up to the cursor */ + memmove(srchpat + 1, es->cbuf, es->cursor); + srchpat[es->cursor + 1] = '\0'; + /* set a magic flag */ + argc1 = 2 + (int)es->cursor; + /* and emulate a backwards history search */ + lastsearch = '/'; + *curcmd = 'n'; + goto pseudo_VCMD; + } + break; } switch (state) { case VCMD: + pseudo_VCMD: state = VNORMAL; switch (vi_cmd(argc1, curcmd)) { case -1: @@ -3962,7 +3993,7 @@ vi_insert(int ch) case Ctrl('['): expanded = NONE; if (first_insert) { - first_insert = 0; + first_insert = false; if (inslen == 0) { inslen = saved_inslen; return (redo_insert(0)); @@ -4074,12 +4105,12 @@ vi_cmd(int argcnt, const char *cmd) * at this point, it's fairly reasonable that * nlen + olen + 2 doesn't overflow */ - nbuf = alloc(nlen + 1 + olen, APERM); + nbuf = alloc(nlen + 1 + olen, AEDIT); memcpy(nbuf, ap->val.s, nlen); nbuf[nlen++] = cmd[1]; if (macro.p) { memcpy(nbuf + nlen, macro.p, olen); - afree(macro.buf, APERM); + afree(macro.buf, AEDIT); nlen += olen; } else { nbuf[nlen++] = '\0'; @@ -4164,7 +4195,8 @@ vi_cmd(int argcnt, const char *cmd) hnum = hlast; if (es->linelen != 0) es->cursor++; - while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0) + while (putbuf(ybuf, yanklen, false) == 0 && + --argcnt > 0) ; if (es->cursor != 0) es->cursor--; @@ -4176,7 +4208,8 @@ vi_cmd(int argcnt, const char *cmd) modified = 1; hnum = hlast; any = 0; - while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0) + while (putbuf(ybuf, yanklen, false) == 0 && + --argcnt > 0) any = 1; if (any && es->cursor != 0) es->cursor--; @@ -4378,6 +4411,11 @@ vi_cmd(int argcnt, const char *cmd) hnum = c2; ohnum = hnum; } + if (argcnt >= 2) { + /* flag from cursor-up command */ + es->cursor = argcnt - 2; + return (0); + } break; case '_': { @@ -4422,8 +4460,8 @@ vi_cmd(int argcnt, const char *cmd) argcnt++; p++; } - if (putbuf(" ", 1, 0) != 0 || - putbuf(sp, argcnt, 0) != 0) { + if (putbuf(" ", 1, false) != 0 || + putbuf(sp, argcnt, false) != 0) { if (es->cursor != 0) es->cursor--; return (-1); @@ -4498,6 +4536,16 @@ vi_cmd(int argcnt, const char *cmd) case Ctrl('x'): expand_word(1); break; + + + /* mksh: cursor movement */ + case '[': + case 'O': + state = VPREFIX2; + if (es->linelen != 0) + es->cursor++; + insert = INSERT; + return (0); } if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen) es->cursor--; @@ -4556,7 +4604,8 @@ domove(int argcnt, const char *cmd, int sub) t = fsavecmd > 'a'; if (*cmd == ',') t = !t; - if ((ncursor = findch(fsavech, argcnt, t, i)) < 0) + if ((ncursor = findch(fsavech, argcnt, tobool(t), + tobool(i))) < 0) return (-1); if (sub && t) ncursor++; @@ -4656,7 +4705,7 @@ static int redo_insert(int count) { while (count-- > 0) - if (putbuf(ibuf, inslen, insert == REPLACE) != 0) + if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0) return (-1); if (es->cursor > 0) es->cursor--; @@ -4707,9 +4756,9 @@ bracktype(int ch) static void save_cbuf(void) { - memmove(holdbuf, es->cbuf, es->linelen); + memmove(holdbufp, es->cbuf, es->linelen); holdlen = es->linelen; - holdbuf[holdlen] = '\0'; + holdbufp[holdlen] = '\0'; } static void @@ -4717,7 +4766,7 @@ restore_cbuf(void) { es->cursor = 0; es->linelen = holdlen; - memmove(es->cbuf, holdbuf, holdlen); + memmove(es->cbuf, holdbufp, holdlen); } /* return a new edstate */ @@ -4726,8 +4775,8 @@ save_edstate(struct edstate *old) { struct edstate *news; - news = alloc(sizeof(struct edstate), APERM); - news->cbuf = alloc(old->cbufsize, APERM); + news = alloc(sizeof(struct edstate), AEDIT); + news->cbuf = alloc(old->cbufsize, AEDIT); memcpy(news->cbuf, old->cbuf, old->linelen); news->cbufsize = old->cbufsize; news->linelen = old->linelen; @@ -4749,8 +4798,8 @@ restore_edstate(struct edstate *news, struct edstate *old) static void free_edstate(struct edstate *old) { - afree(old->cbuf, APERM); - afree(old, APERM); + afree(old->cbuf, AEDIT); + afree(old, AEDIT); } /* @@ -4759,11 +4808,11 @@ free_edstate(struct edstate *old) static int x_vi_putbuf(const char *s, size_t len) { - return (putbuf(s, len, 0)); + return (putbuf(s, len, false)); } static int -putbuf(const char *buf, ssize_t len, int repl) +putbuf(const char *buf, ssize_t len, bool repl) { if (len == 0) return (0); @@ -4793,7 +4842,7 @@ del_range(int a, int b) } static int -findch(int ch, int cnt, int forw, int incl) +findch(int ch, int cnt, bool forw, bool incl) { int ncursor; @@ -4989,8 +5038,8 @@ grabsearch(int save, int start, int fwd, const char *pat) start--; anchored = *pat == '^' ? (++pat, 1) : 0; if ((hist = findhist(start, fwd, pat, anchored)) < 0) { - /* (start != 0 && fwd && match(holdbuf, pat) >= 0) */ - if (start != 0 && fwd && strcmp(holdbuf, pat) >= 0) { + /* (start != 0 && fwd && match(holdbufp, pat) >= 0) */ + if (start != 0 && fwd && strcmp(holdbufp, pat) >= 0) { restore_cbuf(); return (0); } else @@ -5016,9 +5065,9 @@ redraw_line(bool newl) x_putc('\r'); x_putc('\n'); } - if (prompt_redraw) + if (prompt_trunc != -1) pprompt(prompt, prompt_trunc); - cur_col = pwidth; + x_col = pwidth; morec = ' '; } @@ -5136,10 +5185,10 @@ display(char *wb1, char *wb2, int leftside) twb2 = wb2; while (cnt--) { if (*twb1 != *twb2) { - if (cur_col != col) + if (x_col != col) ed_mov_opt(col, wb1); x_putc(*twb1); - cur_col++; + x_col++; } twb1++; twb2++; @@ -5160,34 +5209,34 @@ display(char *wb1, char *wb2, int leftside) if (mc != morec) { ed_mov_opt(pwidth + winwidth + 1, wb1); x_putc(mc); - cur_col++; + x_col++; morec = mc; } - if (cur_col != ncol) + if (x_col != ncol) ed_mov_opt(ncol, wb1); } static void ed_mov_opt(int col, char *wb) { - if (col < cur_col) { - if (col + 1 < cur_col - col) { + if (col < x_col) { + if (col + 1 < x_col - col) { x_putc('\r'); - if (prompt_redraw) + if (prompt_trunc != -1) pprompt(prompt, prompt_trunc); - cur_col = pwidth; - while (cur_col++ < col) + x_col = pwidth; + while (x_col++ < col) x_putcf(*wb++); } else { - while (cur_col-- > col) + while (x_col-- > col) x_putc('\b'); } } else { - wb = &wb[cur_col - pwidth]; - while (cur_col++ < col) + wb = &wb[x_col - pwidth]; + while (x_col++ < col) x_putcf(*wb++); } - cur_col = col; + x_col = col; } @@ -5229,7 +5278,7 @@ expand_word(int cmd) rval = -1; break; } - if (++i < nwords && putbuf(" ", 1, 0) != 0) { + if (++i < nwords && putbuf(" ", 1, false) != 0) { rval = -1; break; } @@ -5347,7 +5396,7 @@ complete_word(int cmd, int count) */ if (match_len > 0 && match[match_len - 1] != '/' && !(flags & XCF_IS_NOSPACE)) - rval = putbuf(" ", 1, 0); + rval = putbuf(" ", 1, false); } x_free_words(nwords, words); @@ -5404,7 +5453,7 @@ static void vi_macro_reset(void) { if (macro.p) { - afree(macro.buf, APERM); + afree(macro.buf, AEDIT); memset((char *)¯o, 0, sizeof(macro)); } } @@ -5425,8 +5474,11 @@ x_init(void) /* ^W */ edchars.werase = 027; - /* initialise Emacs command line editing mode */ + /* command line editing specific memory allocation */ ainit(AEDIT); + holdbufp = alloc(LINE, AEDIT); + + /* initialise Emacs command line editing mode */ x_nextcmd = -1; x_tab = alloc2(X_NTABS, sizeof(*x_tab), AEDIT); diff --git a/src/eval.c b/src/eval.c index 4b1f5a0..b6ff1dc 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,4 +1,4 @@ -/* $OpenBSD: eval.c,v 1.37 2011/10/11 14:32:43 otto Exp $ */ +/* $OpenBSD: eval.c,v 1.39 2013/07/01 17:25:27 jca Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.136 2013/02/10 23:43:59 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.142 2013/07/24 18:03:57 tg Exp $"); /* * string expansion @@ -33,15 +33,21 @@ __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.136 2013/02/10 23:43:59 tg Exp $"); */ /* expansion generator state */ -typedef struct Expand { - /* int type; */ /* see expand() */ - const char *str; /* string */ +typedef struct { + /* not including an "int type;" member, see expand() */ + /* string */ + const char *str; + /* source */ union { - const char **strv; /* string[] */ - struct shf *shf; /* file */ - } u; /* source */ - struct tbl *var; /* variable in ${var..} */ - bool split; /* split "$@" / call waitlast $() */ + /* string[] */ + const char **strv; + /* file */ + struct shf *shf; + } u; + /* variable in ${var...} */ + struct tbl *var; + /* split "$@" / call waitlast in $() */ + bool split; } Expand; #define XBASE 0 /* scanning original */ @@ -59,7 +65,7 @@ typedef struct Expand { static int varsub(Expand *, const char *, const char *, int *, int *); static int comsub(Expand *, const char *, int); -static void funsub(struct op *); +static char *valsub(struct op *, Area *); static char *trimsub(char *, char *, int); static void glob(char *, XPtrV *, bool); static void globit(XString *, char **, char *, XPtrV *, int); @@ -198,33 +204,46 @@ typedef struct SubType { } SubType; void -expand(const char *cp, /* input word */ - XPtrV *wp, /* output words */ - int f) /* DO* flags */ +expand( + /* input word */ + const char *ccp, + /* output words */ + XPtrV *wp, + /* DO* flags */ + int f) { int c = 0; - int type; /* expansion type */ - int quote = 0; /* quoted */ - XString ds; /* destination string */ - char *dp; /* destination */ - const char *sp; /* source */ - int fdo, word; /* second pass flags; have word */ - int doblank; /* field splitting of parameter/command subst */ + /* expansion type */ + int type; + /* quoted */ + int quote = 0; + /* destination string and live pointer */ + XString ds; + char *dp; + /* source */ + const char *sp; + /* second pass flags */ + int fdo; + /* have word */ + int word; + /* field splitting of parameter/command substitution */ + int doblank; + /* expansion variables */ Expand x = { - /* expansion variables */ NULL, { NULL }, NULL, 0 }; SubType st_head, *st; - /* For trailing newlines in COMSUB */ + /* record number of trailing newlines in COMSUB */ int newlines = 0; bool saw_eq, make_magic; int tilde_ok; size_t len; + char *cp; - if (cp == NULL) + if (ccp == NULL) internal_errorf("expand(NULL)"); /* for alias, readonly, set, typeset commands */ - if ((f & DOVACHECK) && is_wdvarassign(cp)) { + if ((f & DOVACHECK) && is_wdvarassign(ccp)) { f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); f |= DOASNTILDE; } @@ -238,7 +257,7 @@ expand(const char *cp, /* input word */ /* init destination string */ Xinit(ds, dp, 128, ATEMP); type = XBASE; - sp = cp; + sp = ccp; fdo = 0; saw_eq = false; /* must be 1/0 */ @@ -279,27 +298,26 @@ expand(const char *cp, /* input word */ continue; case COMSUB: case FUNSUB: + case VALSUB: tilde_ok = 0; if (f & DONTRUNCOMMAND) { word = IFS_WORD; *dp++ = '$'; - if (c == FUNSUB) { - *dp++ = '{'; - *dp++ = ' '; - } else - *dp++ = '('; + *dp++ = c == COMSUB ? '(' : '{'; + if (c != COMSUB) + *dp++ = c == FUNSUB ? ' ' : '|'; while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } - if (c == FUNSUB) { + if (c != COMSUB) { *dp++ = ';'; *dp++ = '}'; } else *dp++ = ')'; } else { type = comsub(&x, sp, c); - if (type == XCOM && (f&DOBLANK)) + if (type != XBASE && (f & DOBLANK)) doblank++; sp = strnul(sp) + 1; newlines = 0; @@ -317,7 +335,6 @@ expand(const char *cp, /* input word */ *dp++ = ')'; *dp++ = ')'; } else { struct tbl v; - char *p; v.flag = DEFINED|ISSET|INTEGER; /* not default */ @@ -326,9 +343,10 @@ expand(const char *cp, /* input word */ v_evaluate(&v, substitute(sp, 0), KSH_UNWIND_ERROR, true); sp = strnul(sp) + 1; - for (p = str_val(&v); *p; ) { + cp = str_val(&v); + while (*cp) { Xcheck(ds, dp); - *dp++ = *p++; + *dp++ = *cp++; } } continue; @@ -394,8 +412,7 @@ expand(const char *cp, /* input word */ if (stype) sp += slen; switch (stype & 0x17F) { - case 0x100 | '#': - { + case 0x100 | '#': { char *beg, *end; mksh_ari_t seed; register uint32_t h; @@ -416,8 +433,7 @@ expand(const char *cp, /* input word */ (unsigned int)h); break; } - case 0x100 | 'Q': - { + case 0x100 | 'Q': { struct shf shf; shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf); @@ -839,7 +855,10 @@ expand(const char *cp, /* input word */ if (c == 0) { if (quote && !x.split) continue; + /* this is so we don't terminate */ c = ' '; + /* now force-emit a word */ + goto emit_word; } if (quote && x.split) { /* terminate word for "$@" */ @@ -850,14 +869,19 @@ expand(const char *cp, /* input word */ break; case XCOM: - if (newlines) { - /* Spit out saved NLs */ + if (x.u.shf == NULL) { + /* $(<...) failed */ + subst_exstat = 1; + /* fake EOF */ + c = EOF; + } else if (newlines) { + /* spit out saved NLs */ c = '\n'; --newlines; } else { while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') if (c == '\n') - /* Save newlines */ + /* save newlines */ newlines++; if (newlines && c != EOF) { shf_ungetc(c, x.u.shf); @@ -867,7 +891,8 @@ expand(const char *cp, /* input word */ } if (c == EOF) { newlines = 0; - shf_close(x.u.shf); + if (x.u.shf) + shf_close(x.u.shf); if (x.split) subst_exstat = waitlast(); type = XBASE; @@ -895,21 +920,21 @@ expand(const char *cp, /* input word */ */ if (word == IFS_WORD || (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) { - char *p; - + emit_word: *dp++ = '\0'; - p = Xclose(ds, dp); + cp = Xclose(ds, dp); if (fdo & DOBRACE) /* also does globbing */ - alt_expand(wp, p, p, - p + Xlength(ds, (dp - 1)), + alt_expand(wp, cp, cp, + cp + Xlength(ds, (dp - 1)), fdo | (f & DOMARKDIRS)); else if (fdo & DOGLOB) - glob(p, wp, tobool(f & DOMARKDIRS)); + glob(cp, wp, tobool(f & DOMARKDIRS)); else if ((f & DOPAT) || !(fdo & DOMAGIC)) - XPput(*wp, p); + XPput(*wp, cp); else - XPput(*wp, debunk(p, p, strlen(p) + 1)); + XPput(*wp, debunk(cp, cp, + strlen(cp) + 1)); fdo = 0; saw_eq = false; tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; @@ -920,10 +945,8 @@ expand(const char *cp, /* input word */ return; } else if (type == XSUB && ctype(c, C_IFS) && !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) { - char *p; - - *(p = alloc(1, ATEMP)) = '\0'; - XPput(*wp, p); + *(cp = alloc(1, ATEMP)) = '\0'; + XPput(*wp, cp); type = XSUBMID; } if (word != IFS_NWS) @@ -932,10 +955,8 @@ expand(const char *cp, /* input word */ if (type == XSUB) { if (word == IFS_NWS && Xlength(ds, dp) == 0) { - char *p; - - *(p = alloc(1, ATEMP)) = '\0'; - XPput(*wp, p); + *(cp = alloc(1, ATEMP)) = '\0'; + XPput(*wp, cp); } type = XSUBMID; } @@ -1002,18 +1023,17 @@ expand(const char *cp, /* input word */ if (type == XBASE && (f & (DOTILDE|DOASNTILDE)) && (tilde_ok & 2)) { - const char *p; - char *dp_x; + const char *tcp; + char *tdp = dp; - dp_x = dp; - p = maybe_expand_tilde(sp, - &ds, &dp_x, + tcp = maybe_expand_tilde(sp, + &ds, &tdp, f & DOASNTILDE); - if (p) { - if (dp != dp_x) + if (tcp) { + if (dp != tdp) word = IFS_WORD; - dp = dp_x; - sp = p; + dp = tdp; + sp = tcp; continue; } } @@ -1176,8 +1196,10 @@ varsub(Expand *xp, const char *sp, const char *word, c = sp[0]; if (c == '*' || c == '@') { switch (stype & 0x17F) { - case '=': /* can't assign to a vector */ - case '%': /* can't trim a vector (yet) */ + /* can't assign to a vector */ + case '=': + /* can't trim a vector (yet) */ + case '%': case '#': case '0': case '/': @@ -1204,8 +1226,10 @@ varsub(Expand *xp, const char *sp, const char *word, XPtrV wv; switch (stype & 0x17F) { - case '=': /* can't assign to a vector */ - case '%': /* can't trim a vector (yet) */ + /* can't assign to a vector */ + case '=': + /* can't trim a vector (yet) */ + case '%': case '#': case '?': case '0': @@ -1317,33 +1341,41 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED) shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); if (shf == NULL) - errorf("%s: %s %s", name, "can't open", "$() input"); + warningf(!Flag(FTALKING), "%s: %s %s: %s", name, + "can't open", "$(<...) input", cstrerror(errno)); } else if (fn == FUNSUB) { int ofd1; struct temp *tf = NULL; - /* create a temporary file, open for writing */ + /* + * create a temporary file, open for reading and writing, + * with an shf open for reading (buffered) but yet unused + */ maketemp(ATEMP, TT_FUNSUB, &tf); if (!tf->shf) { errorf("can't %s temporary file %s: %s", "create", tf->tffn, cstrerror(errno)); } - /* save stdout and make the temporary file it */ + /* extract shf from temporary file, unlink and free it */ + shf = tf->shf; + unlink(tf->tffn); + afree(tf, ATEMP); + /* save stdout and let it point to the tempfile */ ofd1 = savefd(1); - ksh_dup2(shf_fileno(tf->shf), 1, false); + ksh_dup2(shf_fileno(shf), 1, false); /* * run tree, with output thrown into the tempfile, * in a new function block */ - funsub(t); + valsub(t, NULL); subst_exstat = exstat & 0xFF; - /* close the tempfile and restore regular stdout */ - shf_close(tf->shf); + /* rewind the tempfile and restore regular stdout */ + lseek(shf_fileno(shf), (off_t)0, SEEK_SET); restfd(1, ofd1); - /* now open, unlink and free the tempfile for reading */ - shf = shf_open(tf->tffn, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC); - unlink(tf->tffn); - afree(tf, ATEMP); + } else if (fn == VALSUB) { + xp->str = valsub(t, ATEMP); + subst_exstat = exstat & 0xFF; + return (XSUB); } else { int ofd1, pv[2]; @@ -1495,11 +1527,11 @@ globit(XString *xs, /* dest string */ */ if ((check & GF_EXCHECK) || ((check & GF_MARKDIR) && (check & GF_GLOBBED))) { -#define stat_check() (stat_done ? stat_done : \ - (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \ - ? -1 : 1)) +#define stat_check() (stat_done ? stat_done : (stat_done = \ + stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1)) struct stat lstatb, statb; - int stat_done = 0; /* -1: failed, 1 ok */ + /* -1: failed, 1 ok, 0 not yet done */ + int stat_done = 0; if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0) return; @@ -1801,12 +1833,21 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo) } /* helper function due to setjmp/longjmp woes */ -static void -funsub(struct op *t) +static char * +valsub(struct op *t, Area *ap) { + char * volatile cp = NULL; + struct tbl * volatile vp = NULL; + + newenv(E_FUNC); newblock(); - e->type = E_FUNC; + if (ap) + vp = local("REPLY", false); if (!kshsetjmp(e->jbuf)) execute(t, XXCOM | XERROK, NULL); - popblock(); + if (vp) + strdupx(cp, str_val(vp), ap); + quitenv(NULL); + + return (cp); } diff --git a/src/exec.c b/src/exec.c index 9a384db..50ec9ca 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec.c,v 1.49 2009/01/29 23:27:26 jaredy Exp $ */ +/* $OpenBSD: exec.c,v 1.50 2013/06/10 21:09:27 millert Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.116 2013/02/17 05:40:15 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.125 2013/07/21 20:44:44 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL "/bin/sh" @@ -106,7 +106,7 @@ execute(struct op * volatile t, /* set variable to its expanded value */ z = strlen(cp) + 1; if (notoktomul(z, 2) || notoktoadd(z * 2, n)) - internal_errorf(Toomem, (unsigned long)-1); + internal_errorf(Toomem, (size_t)-1); dp = alloc(z * 2 + n, ATEMP); memcpy(dp, t->vars[0], n); t->vars[0] = dp; @@ -138,14 +138,6 @@ execute(struct op * volatile t, /* Allow option parsing (bizarre, but POSIX) */ timex_hook(t, &up); ap = (const char **)up; - if (Flag(FXTRACE) && ap[0]) { - shf_puts(substitute(str_val(global("PS4")), 0), - shl_out); - for (i = 0; ap[i]; i++) - shf_fprintf(shl_out, "%s%c", ap[i], - ap[i + 1] ? ' ' : '\n'); - shf_flush(shl_out); - } if (ap[0]) tp = findcom(ap[0], FC_BI|FC_FUNC); } @@ -305,10 +297,12 @@ execute(struct op * volatile t, case TAND: rv = execute(t->left, XERROK, xerrok); if ((rv == 0) == (t->type == TAND)) - rv = execute(t->right, XERROK, xerrok); - flags |= XERROK; - if (xerrok) - *xerrok = 1; + rv = execute(t->right, flags & XERROK, xerrok); + else { + flags |= XERROK; + if (xerrok) + *xerrok = 1; + } break; case TBANG: @@ -335,6 +329,7 @@ execute(struct op * volatile t, case TFOR: case TSELECT: { volatile bool is_first = true; + ap = (t->vars == NULL) ? e->loc->argv + 1 : (const char **)eval((const char **)t->vars, DOBLANK | DOGLOB | DOTILDE); @@ -639,27 +634,44 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap, l_assign = e->loc; if (Flag(FEXPORT)) type_flags |= EXPORT; + if (Flag(FXTRACE)) + change_xtrace(2, false); for (i = 0; t->vars[i]; i++) { /* do NOT lookup in the new var/fn block just created */ e->loc = l_expand; cp = evalstr(t->vars[i], DOASNTILDE); e->loc = l_assign; - /* but assign in there as usual */ - if (Flag(FXTRACE)) { - if (i == 0) - shf_puts(substitute(str_val(global("PS4")), 0), - shl_out); - shf_fprintf(shl_out, "%s%c", cp, - t->vars[i + 1] ? ' ' : '\n'); - if (!t->vars[i + 1]) - shf_flush(shl_out); + const char *ccp; + + ccp = skip_varname(cp, true); + if (*ccp == '+') + ++ccp; + if (*ccp == '=') + ++ccp; + shf_write(cp, ccp - cp, shl_xtrace); + print_value_quoted(shl_xtrace, ccp); + shf_putc(' ', shl_xtrace); } + /* but assign in there as usual */ typeset(cp, type_flags, 0, 0, 0); if (bourne_function_call && !(type_flags & EXPORT)) typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0); } + if (Flag(FXTRACE)) { + change_xtrace(2, false); + if (ap[rv = 0]) { + xtrace_ap_loop: + print_value_quoted(shl_xtrace, ap[rv]); + if (ap[++rv]) { + shf_putc(' ', shl_xtrace); + goto xtrace_ap_loop; + } + } + change_xtrace(1, false); + } + if ((cp = *ap) == NULL) { rv = subst_exstat; goto Leave; @@ -700,10 +712,9 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap, break; } if (include(tp->u.fpath, 0, NULL, false) < 0) { - rv = errno; warningf(true, "%s: %s %s %s: %s", cp, "can't open", "function definition file", - tp->u.fpath, cstrerror(rv)); + tp->u.fpath, cstrerror(errno)); rv = 127; break; } @@ -740,9 +751,9 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap, getopts_reset(1); } - old_xflag = Flag(FXTRACE); - Flag(FXTRACE) |= tp->flag & TRACE ? 1 : 0; - + old_xflag = Flag(FXTRACE) ? 1 : 0; + change_xtrace((Flag(FXTRACEREC) ? old_xflag : 0) | + ((tp->flag & TRACE) ? 1 : 0), false); old_inuse = tp->flag & FINUSE; tp->flag |= FINUSE; @@ -751,9 +762,11 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap, execute(tp->val.t, flags & XERROK, NULL); i = LRETURN; } + kshname = old_kshname; - Flag(FXTRACE) = old_xflag; + change_xtrace(old_xflag, false); tp->flag = (tp->flag & ~FINUSE) | old_inuse; + /* * Were we deleted while executing? If so, free the * execution tree. TODO: Unfortunately, the table entry @@ -1033,25 +1046,23 @@ const char * builtin(const char *name, int (*func) (const char **)) { struct tbl *tp; - uint32_t flag; + uint32_t flag = DEFINED; /* see if any flags should be set for this builtin */ - for (flag = 0; ; name++) { + while (1) { if (*name == '=') /* command does variable assignment */ flag |= KEEPASN; else if (*name == '*') /* POSIX special builtin */ flag |= SPEC_BI; - else if (*name == '+') - /* POSIX regular builtin */ - flag |= REG_BI; else break; + name++; } tp = ktenter(&builtins, name, hash(name)); - tp->flag = DEFINED | flag; + tp->flag = flag; tp->type = CSHELL; tp->val.f = func; @@ -1083,7 +1094,7 @@ findcom(const char *name, int flags) tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL; /* * POSIX says special builtins first, then functions, then - * POSIX regular builtins, then search path... + * regular builtins, then search path... */ if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI)) tp = tbi; @@ -1098,9 +1109,7 @@ findcom(const char *name, int flags) &tp->u2.errnov); } } - if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) - tp = tbi; - if (!tp && (flags & FC_UNREGBI) && tbi) + if (!tp && (flags & FC_NORMBI) && tbi) tp = tbi; if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { tp = ktsearch(&taliases, name, h); @@ -1298,10 +1307,11 @@ iosetup(struct ioword *iop, struct tbl *tp) iotmp.name = (iotype == IOHERE) ? NULL : cp; iotmp.flag |= IONAMEXP; - if (Flag(FXTRACE)) - shellf("%s%s\n", - substitute(str_val(global("PS4")), 0), - snptreef(NULL, 32, "%R", &iotmp)); + if (Flag(FXTRACE)) { + change_xtrace(2, false); + fptreef(shl_xtrace, 0, "%R", &iotmp); + change_xtrace(1, false); + } switch (iotype) { case IOREAD: @@ -1345,8 +1355,11 @@ iosetup(struct ioword *iop, struct tbl *tp) } else if ((u = check_fd(cp, X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK), &emsg)) < 0) { + char *sp; + warningf(true, "%s: %s", - snptreef(NULL, 32, "%R", &iotmp), emsg); + (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg); + afree(sp, ATEMP); return (-1); } if (u == iop->unit) @@ -1395,12 +1408,14 @@ iosetup(struct ioword *iop, struct tbl *tp) else if (u != iop->unit) { if (ksh_dup2(u, iop->unit, true) < 0) { int eno; + char *sp; eno = errno; warningf(true, "%s %s %s", "can't finish (dup) redirection", - snptreef(NULL, 32, "%R", &iotmp), + (sp = snptreef(NULL, 32, "%R", &iotmp)), cstrerror(eno)); + afree(sp, ATEMP); if (iotype != IODUP) close(u); return (-1); @@ -1548,10 +1563,8 @@ do_selectargs(const char **ap, bool print_menu) if (call_builtin(findcom("read", FC_BI), read_args, Tselect)) return (NULL); s = str_val(global("REPLY")); - if (*s) { - getn(s, &i); + if (*s && getn(s, &i)) return ((i >= 1 && i <= argct) ? ap[i - 1] : null); - } print_menu = true; } } diff --git a/src/expr.c b/src/expr.c index 321c31e..e91c0da 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: expr.c,v 1.21 2009/06/01 19:00:57 deraadt Exp $ */ +/* $OpenBSD: expr.c,v 1.22 2013/03/28 08:39:28 nicm Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -23,23 +23,9 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.61 2013/02/15 18:36:48 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.72 2013/07/21 18:38:56 tg Exp $"); -#if !HAVE_SILENT_IDIVWRAPV -#if !defined(MKSH_LEGACY_MODE) || HAVE_LONG_32BIT -#define IDIVWRAPV_VL (mksh_uari_t)0x80000000UL -#define IDIVWRAPV_VR (mksh_uari_t)0xFFFFFFFFUL -#elif HAVE_LONG_64BIT -#define IDIVWRAPV_VL (mksh_uari_t)0x8000000000000000UL -#define IDIVWRAPV_VR (mksh_uari_t)0xFFFFFFFFFFFFFFFFUL -#else -# warning "cannot guarantee integer division wraparound" -#undef HAVE_SILENT_IDIVWRAPV -#define HAVE_SILENT_IDIVWRAPV 1 -#endif -#endif - -/* The order of these enums is constrained by the order of opinfo[] */ +/* the order of these enums is constrained by the order of opinfo[] */ enum token { /* some (long) unary operators */ O_PLUSPLUS = 0, O_MINUSMINUS, @@ -47,7 +33,14 @@ enum token { O_EQ, O_NE, /* assignments are assumed to be in range O_ASN .. O_BORASN */ O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN, +#ifndef MKSH_LEGACY_MODE + O_ROLASN, O_RORASN, +#endif O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN, + /* binary non-assignment operators */ +#ifndef MKSH_LEGACY_MODE + O_ROL, O_ROR, +#endif O_LSHIFT, O_RSHIFT, O_LE, O_GE, O_LT, O_GT, O_LAND, @@ -67,14 +60,13 @@ enum token { /* things that don't appear in the opinfo[] table */ VAR, LIT, END, BAD }; -#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA) #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN) /* precisions; used to be enum prec but we do arithmetics on it */ -#define P_PRIMARY 0 /* VAR, LIT, (), ~ ! - + */ +#define P_PRIMARY 0 /* VAR, LIT, (), ! ~ ++ -- */ #define P_MULT 1 /* * / % */ #define P_ADD 2 /* + - */ -#define P_SHIFT 3 /* << >> */ +#define P_SHIFT 3 /* <<< >>> << >> */ #define P_RELATION 4 /* < <= > >= */ #define P_EQUALITY 5 /* == != */ #define P_BAND 6 /* & */ @@ -83,60 +75,72 @@ enum token { #define P_LAND 9 /* && */ #define P_LOR 10 /* || */ #define P_TERN 11 /* ?: */ -#define P_ASSIGN 12 /* = *= /= %= += -= <<= >>= &= ^= |= */ + /* = += -= *= /= %= <<<= >>>= <<= >>= &= ^= |= */ +#define P_ASSIGN 12 #define P_COMMA 13 /* , */ #define MAX_PREC P_COMMA struct opinfo { - char name[4]; - int len; /* name length */ - int prec; /* precedence: lower is higher */ + char name[5]; + /* name length */ + uint8_t len; + /* precedence: lower is higher */ + uint8_t prec; }; -/* Tokens in this table must be ordered so the longest are first +/* + * Tokens in this table must be ordered so the longest are first * (eg, += before +). If you change something, change the order * of enum token too. */ static const struct opinfo opinfo[] = { - { "++", 2, P_PRIMARY }, /* before + */ - { "--", 2, P_PRIMARY }, /* before - */ - { "==", 2, P_EQUALITY }, /* before = */ - { "!=", 2, P_EQUALITY }, /* before ! */ - { "=", 1, P_ASSIGN }, /* keep assigns in a block */ - { "*=", 2, P_ASSIGN }, - { "/=", 2, P_ASSIGN }, - { "%=", 2, P_ASSIGN }, - { "+=", 2, P_ASSIGN }, - { "-=", 2, P_ASSIGN }, - { "<<=", 3, P_ASSIGN }, - { ">>=", 3, P_ASSIGN }, - { "&=", 2, P_ASSIGN }, - { "^=", 2, P_ASSIGN }, - { "|=", 2, P_ASSIGN }, - { "<<", 2, P_SHIFT }, - { ">>", 2, P_SHIFT }, - { "<=", 2, P_RELATION }, - { ">=", 2, P_RELATION }, - { "<", 1, P_RELATION }, - { ">", 1, P_RELATION }, - { "&&", 2, P_LAND }, - { "||", 2, P_LOR }, - { "*", 1, P_MULT }, - { "/", 1, P_MULT }, - { "%", 1, P_MULT }, - { "+", 1, P_ADD }, - { "-", 1, P_ADD }, - { "&", 1, P_BAND }, - { "^", 1, P_BXOR }, - { "|", 1, P_BOR }, - { "?", 1, P_TERN }, - { ",", 1, P_COMMA }, - { "~", 1, P_PRIMARY }, - { "!", 1, P_PRIMARY }, - { "(", 1, P_PRIMARY }, - { ")", 1, P_PRIMARY }, - { ":", 1, P_PRIMARY }, - { "", 0, P_PRIMARY } + { "++", 2, P_PRIMARY }, /* before + */ + { "--", 2, P_PRIMARY }, /* before - */ + { "==", 2, P_EQUALITY }, /* before = */ + { "!=", 2, P_EQUALITY }, /* before ! */ + { "=", 1, P_ASSIGN }, /* keep assigns in a block */ + { "*=", 2, P_ASSIGN }, + { "/=", 2, P_ASSIGN }, + { "%=", 2, P_ASSIGN }, + { "+=", 2, P_ASSIGN }, + { "-=", 2, P_ASSIGN }, +#ifndef MKSH_LEGACY_MODE + { "<<<=", 4, P_ASSIGN }, /* before <<< */ + { ">>>=", 4, P_ASSIGN }, /* before >>> */ +#endif + { "<<=", 3, P_ASSIGN }, + { ">>=", 3, P_ASSIGN }, + { "&=", 2, P_ASSIGN }, + { "^=", 2, P_ASSIGN }, + { "|=", 2, P_ASSIGN }, +#ifndef MKSH_LEGACY_MODE + { "<<<", 3, P_SHIFT }, /* before << */ + { ">>>", 3, P_SHIFT }, /* before >> */ +#endif + { "<<", 2, P_SHIFT }, + { ">>", 2, P_SHIFT }, + { "<=", 2, P_RELATION }, + { ">=", 2, P_RELATION }, + { "<", 1, P_RELATION }, + { ">", 1, P_RELATION }, + { "&&", 2, P_LAND }, + { "||", 2, P_LOR }, + { "*", 1, P_MULT }, + { "/", 1, P_MULT }, + { "%", 1, P_MULT }, + { "+", 1, P_ADD }, + { "-", 1, P_ADD }, + { "&", 1, P_BAND }, + { "^", 1, P_BXOR }, + { "|", 1, P_BOR }, + { "?", 1, P_TERN }, + { ",", 1, P_COMMA }, + { "~", 1, P_PRIMARY }, + { "!", 1, P_PRIMARY }, + { "(", 1, P_PRIMARY }, + { ")", 1, P_PRIMARY }, + { ":", 1, P_PRIMARY }, + { "", 0, P_PRIMARY } }; typedef struct expr_state { @@ -151,18 +155,13 @@ typedef struct expr_state { /* token from token() */ enum token tok; /* don't do assignments (for ?:, &&, ||) */ - short noassign; + uint8_t noassign; /* evaluating an $(()) expression? */ bool arith; /* unsigned arithmetic calculation */ bool natural; } Expr_state; -#define bivui(x, op, y) (es->natural ? \ - (mksh_uari_t)((x)->val.u op (y)->val.u) : \ - (mksh_uari_t)((x)->val.i op (y)->val.i) \ -) - enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE, ET_LVALUE, ET_RDONLY, ET_STR @@ -170,7 +169,7 @@ enum error_type { static void evalerr(Expr_state *, enum error_type, const char *) MKSH_A_NORETURN; -static struct tbl *evalexpr(Expr_state *, int); +static struct tbl *evalexpr(Expr_state *, unsigned int); static void exprtoken(Expr_state *); static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool); static void assign_check(Expr_state *, enum token, struct tbl *); @@ -185,7 +184,7 @@ evaluate(const char *expr, mksh_ari_t *rval, int error_ok, bool arith) struct tbl v; int ret; - v.flag = DEFINED|INTEGER; + v.flag = DEFINED | INTEGER; v.type = 0; ret = v_evaluate(&v, expr, error_ok, arith); *rval = v.val.i; @@ -307,52 +306,103 @@ evalerr(Expr_state *es, enum error_type type, const char *str) unwind(LAEXPR); } +/* do a ++ or -- operation */ static struct tbl * -evalexpr(Expr_state *es, int prec) +do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix) +{ + struct tbl *vl; + mksh_uari_t oval; + + assign_check(es, op, vasn); + + vl = intvar(es, vasn); + oval = vl->val.u; + if (op == O_PLUSPLUS) + ++vl->val.u; + else + --vl->val.u; + if (!es->noassign) { + if (vasn->flag & INTEGER) + setint_v(vasn, vl, es->arith); + else + setint(vasn, vl->val.i); + } + if (!is_prefix) + /* undo the increment/decrement */ + vl->val.u = oval; + + return (vl); +} + +static struct tbl * +evalexpr(Expr_state *es, unsigned int prec) { struct tbl *vl, *vr = NULL, *vasn; enum token op; - mksh_uari_t res = 0; + mksh_uari_t res = 0, t1, t2, t3; if (prec == P_PRIMARY) { - op = es->tok; - if (op == O_BNOT || op == O_LNOT || op == O_MINUS || - op == O_PLUS) { + switch ((int)(op = es->tok)) { + case O_BNOT: + case O_LNOT: + case O_MINUS: + case O_PLUS: exprtoken(es); vl = intvar(es, evalexpr(es, P_PRIMARY)); - if (op == O_BNOT) - vl->val.i = ~vl->val.i; - else if (op == O_LNOT) - vl->val.i = !vl->val.i; - else if (op == O_MINUS) - vl->val.i = -vl->val.i; - /* op == O_PLUS is a no-op */ - } else if (op == OPEN_PAREN) { + switch ((int)op) { + case O_BNOT: + vl->val.u = ~vl->val.u; + break; + case O_LNOT: + vl->val.u = !vl->val.u; + break; + case O_MINUS: + vl->val.u = -vl->val.u; + break; + case O_PLUS: + /* nop */ + break; + } + break; + + case OPEN_PAREN: exprtoken(es); vl = evalexpr(es, MAX_PREC); if (es->tok != CLOSE_PAREN) evalerr(es, ET_STR, "missing )"); exprtoken(es); - } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) { + break; + + case O_PLUSPLUS: + case O_MINUSMINUS: exprtoken(es); vl = do_ppmm(es, op, es->val, true); exprtoken(es); - } else if (op == VAR || op == LIT) { + break; + + case VAR: + case LIT: vl = es->val; exprtoken(es); - } else { + break; + + default: evalerr(es, ET_UNEXPECTED, NULL); /* NOTREACHED */ } + if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) { vl = do_ppmm(es, es->tok, vl, false); exprtoken(es); } + return (vl); + /* prec == P_PRIMARY */ } + vl = evalexpr(es, prec - 1); - for (op = es->tok; IS_BINOP(op) && opinfo[(int)op].prec == prec; - op = es->tok) { + while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA && + opinfo[(int)op].prec == prec) { exprtoken(es); vasn = vl; if (op != O_ASN) @@ -362,152 +412,204 @@ evalexpr(Expr_state *es, int prec) if (!es->noassign) assign_check(es, op, vasn); vr = intvar(es, evalexpr(es, P_ASSIGN)); - } else if (op != O_TERN && op != O_LAND && op != O_LOR) + } else if (op == O_TERN) { + bool ev = vl->val.u != 0; + + if (!ev) + es->noassign++; + vl = evalexpr(es, MAX_PREC); + if (!ev) + es->noassign--; + if (es->tok != CTERN) + evalerr(es, ET_STR, "missing :"); + exprtoken(es); + if (ev) + es->noassign++; + vr = evalexpr(es, P_TERN); + if (ev) + es->noassign--; + vl = ev ? vl : vr; + continue; + } else if (op != O_LAND && op != O_LOR) vr = intvar(es, evalexpr(es, prec - 1)); - if ((op == O_DIV || op == O_MOD || op == O_DIVASN || - op == O_MODASN) && vr->val.i == 0) { - if (es->noassign) - vr->val.i = 1; - else - evalerr(es, ET_STR, "zero divisor"); + + /* common ops setup */ + switch ((int)op) { + case O_DIV: + case O_DIVASN: + case O_MOD: + case O_MODASN: + if (vr->val.u == 0) { + if (!es->noassign) + evalerr(es, ET_STR, "zero divisor"); + vr->val.u = 1; + } + /* calculate the absolute values */ + t1 = vl->val.i < 0 ? -vl->val.u : vl->val.u; + t2 = vr->val.i < 0 ? -vr->val.u : vr->val.u; + break; +#ifndef MKSH_LEGACY_MODE + case O_LSHIFT: + case O_LSHIFTASN: + case O_RSHIFT: + case O_RSHIFTASN: + case O_ROL: + case O_ROLASN: + case O_ROR: + case O_RORASN: + t1 = vl->val.u; + t2 = vr->val.u & 31; + break; +#endif + case O_LAND: + case O_LOR: + t1 = vl->val.u; + t2 = 0; /* gcc */ + break; + default: + t1 = vl->val.u; + t2 = vr->val.u; + break; } + +#define cmpop(op) (es->natural ? \ + (mksh_uari_t)(vl->val.u op vr->val.u) : \ + (mksh_uari_t)(vl->val.i op vr->val.i) \ +) + + /* op calculation */ switch ((int)op) { case O_TIMES: case O_TIMESASN: - res = bivui(vl, *, vr); + res = t1 * t2; break; + case O_MOD: + case O_MODASN: + if (es->natural) { + res = vl->val.u % vr->val.u; + break; + } + goto signed_division; case O_DIV: case O_DIVASN: -#if !HAVE_SILENT_IDIVWRAPV + if (es->natural) { + res = vl->val.u / vr->val.u; + break; + } + signed_division: /* - * we are doing the comparisons here for the - * signed arithmetics (!es->natural) case, - * but the exact value checks and the bypass - * case assignments are done unsignedly as - * several compilers bitch around otherwise + * a / b = abs(a) / abs(b) * sgn((u)a^(u)b) */ - if (!es->natural && - vl->val.u == IDIVWRAPV_VL && - vr->val.u == IDIVWRAPV_VR) { - /* -2147483648 / -1 = 2147483648 */ - /* this ^ is really (1 << 31) though */ - res = IDIVWRAPV_VL; - } else -#endif - res = bivui(vl, /, vr); - break; - case O_MOD: - case O_MODASN: -#if !HAVE_SILENT_IDIVWRAPV - /* see O_DIV / O_DIVASN for the reason behind this */ - if (!es->natural && - vl->val.u == IDIVWRAPV_VL && - vr->val.u == IDIVWRAPV_VR) { - /* -2147483648 % -1 = 0 */ - res = 0; - } else + t3 = t1 / t2; +#ifndef MKSH_LEGACY_MODE + res = ((vl->val.u ^ vr->val.u) & 0x80000000) ? -t3 : t3; +#else + res = ((t1 == vl->val.u ? 0 : 1) ^ + (t2 == vr->val.u ? 0 : 1)) ? -t3 : t3; #endif - res = bivui(vl, %, vr); + if (op == O_MOD || op == O_MODASN) { + /* + * primitive modulo, to get the sign of + * the result correct: + * (a % b) = a - ((a / b) * b) + * the subtraction and multiplication + * are, amazingly enough, sign ignorant + */ + res = vl->val.u - (res * vr->val.u); + } break; case O_PLUS: case O_PLUSASN: - res = bivui(vl, +, vr); + res = t1 + t2; break; case O_MINUS: case O_MINUSASN: - res = bivui(vl, -, vr); + res = t1 - t2; + break; +#ifndef MKSH_LEGACY_MODE + case O_ROL: + case O_ROLASN: + res = (t1 << t2) | (t1 >> (32 - t2)); break; + case O_ROR: + case O_RORASN: + res = (t1 >> t2) | (t1 << (32 - t2)); + break; +#endif case O_LSHIFT: case O_LSHIFTASN: - res = bivui(vl, <<, vr); + res = t1 << t2; break; case O_RSHIFT: case O_RSHIFTASN: - res = bivui(vl, >>, vr); + res = es->natural || vl->val.i >= 0 ? + t1 >> t2 : + ~(~t1 >> t2); break; case O_LT: - res = bivui(vl, <, vr); + res = cmpop(<); break; case O_LE: - res = bivui(vl, <=, vr); + res = cmpop(<=); break; case O_GT: - res = bivui(vl, >, vr); + res = cmpop(>); break; case O_GE: - res = bivui(vl, >=, vr); + res = cmpop(>=); break; case O_EQ: - res = bivui(vl, ==, vr); + res = t1 == t2; break; case O_NE: - res = bivui(vl, !=, vr); + res = t1 != t2; break; case O_BAND: case O_BANDASN: - res = bivui(vl, &, vr); + res = t1 & t2; break; case O_BXOR: case O_BXORASN: - res = bivui(vl, ^, vr); + res = t1 ^ t2; break; case O_BOR: case O_BORASN: - res = bivui(vl, |, vr); + res = t1 | t2; break; case O_LAND: - if (!vl->val.i) + if (!t1) es->noassign++; vr = intvar(es, evalexpr(es, prec - 1)); - res = bivui(vl, &&, vr); - if (!vl->val.i) + res = t1 && vr->val.u; + if (!t1) es->noassign--; break; case O_LOR: - if (vl->val.i) + if (t1) es->noassign++; vr = intvar(es, evalexpr(es, prec - 1)); - res = bivui(vl, ||, vr); - if (vl->val.i) + res = t1 || vr->val.u; + if (t1) es->noassign--; break; - case O_TERN: - { - bool ev = vl->val.i != 0; - - if (!ev) - es->noassign++; - vl = evalexpr(es, MAX_PREC); - if (!ev) - es->noassign--; - if (es->tok != CTERN) - evalerr(es, ET_STR, "missing :"); - exprtoken(es); - if (ev) - es->noassign++; - vr = evalexpr(es, P_TERN); - if (ev) - es->noassign--; - vl = ev ? vl : vr; - } - break; case O_ASN: - res = vr->val.u; - break; case O_COMMA: - res = vr->val.u; + res = t2; break; } + +#undef cmpop + if (IS_ASSIGNOP(op)) { vr->val.u = res; if (!es->noassign) { if (vasn->flag & INTEGER) setint_v(vasn, vr, es->arith); else - setint(vasn, (mksh_ari_t)res); + setint(vasn, vr->val.i); } vl = vr; - } else if (op != O_TERN) + } else vl->val.u = res; } return (vl); @@ -520,13 +622,14 @@ exprtoken(Expr_state *es) int c; char *tvar; - /* skip white space */ + /* skip whitespace */ skip_spaces: while ((c = *cp), ksh_isspace(c)) ++cp; if (es->tokp == es->expression && c == '#') { /* expression begins with # */ - es->natural = true; /* switch to unsigned */ + /* switch to unsigned */ + es->natural = true; ++cp; goto skip_spaces; } @@ -544,12 +647,6 @@ exprtoken(Expr_state *es) if (len == 0) evalerr(es, ET_STR, "missing ]"); cp += len; - } else if (c == '(' /*)*/ ) { - /* todo: add math functions (all take single argument): - * abs acos asin atan cos cosh exp int log sin sinh sqrt - * tan tanh - */ - ; } if (es->noassign) { es->val = tempvar(); @@ -610,39 +707,6 @@ exprtoken(Expr_state *es) es->tokp = cp; } -/* Do a ++ or -- operation */ -static struct tbl * -do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix) -{ - struct tbl *vl; - mksh_ari_t oval; - - assign_check(es, op, vasn); - - vl = intvar(es, vasn); - oval = vl->val.i; - if (op == O_PLUSPLUS) { - if (es->natural) - ++vl->val.u; - else - ++vl->val.i; - } else { - if (es->natural) - --vl->val.u; - else - --vl->val.i; - } - if (vasn->flag & INTEGER) - setint_v(vasn, vl, es->arith); - else - setint(vasn, vl->val.i); - if (!is_prefix) - /* undo the increment/decrement */ - vl->val.i = oval; - - return (vl); -} - static void assign_check(Expr_state *es, enum token op, struct tbl *vasn) { @@ -833,129 +897,6 @@ utf_wctomb(char *dst, unsigned int wc) return ((char *)d - dst); } - -#ifndef MKSH_mirbsd_wcwidth -/* --- begin of wcwidth.c excerpt --- */ -/*- - * Markus Kuhn -- 2007-05-26 (Unicode 5.0) - * - * Permission to use, copy, modify, and distribute this software - * for any purpose and without fee is hereby granted. The author - * disclaims all warranties with regard to this software. - */ - -__RCSID("$miros: src/lib/libc/i18n/wcwidth.c,v 1.11 2012/09/01 23:46:43 tg Exp $"); - -int -utf_wcwidth(unsigned int c) -{ - static const struct cbset { - unsigned short first; - unsigned short last; - } comb[] = { - /* Unicode 6.1.0 BMP */ - { 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD }, - { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, - { 0x05C7, 0x05C7 }, { 0x0600, 0x0604 }, { 0x0610, 0x061A }, - { 0x064B, 0x065F }, { 0x0670, 0x0670 }, { 0x06D6, 0x06DD }, - { 0x06DF, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, - { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, - { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0816, 0x0819 }, - { 0x081B, 0x0823 }, { 0x0825, 0x0827 }, { 0x0829, 0x082D }, - { 0x0859, 0x085B }, { 0x08E4, 0x08FE }, { 0x0900, 0x0902 }, - { 0x093A, 0x093A }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, - { 0x094D, 0x094D }, { 0x0951, 0x0957 }, { 0x0962, 0x0963 }, - { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, - { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, - { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, - { 0x0A4B, 0x0A4D }, { 0x0A51, 0x0A51 }, { 0x0A70, 0x0A71 }, - { 0x0A75, 0x0A75 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, - { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, - { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, - { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B44 }, { 0x0B4D, 0x0B4D }, - { 0x0B56, 0x0B56 }, { 0x0B62, 0x0B63 }, { 0x0B82, 0x0B82 }, - { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, - { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, - { 0x0C62, 0x0C63 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, - { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, - { 0x0D41, 0x0D44 }, { 0x0D4D, 0x0D4D }, { 0x0D62, 0x0D63 }, - { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, - { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, - { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, - { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, - { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, - { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F8D, 0x0F97 }, - { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, - { 0x1032, 0x1037 }, { 0x1039, 0x103A }, { 0x103D, 0x103E }, - { 0x1058, 0x1059 }, { 0x105E, 0x1060 }, { 0x1071, 0x1074 }, - { 0x1082, 0x1082 }, { 0x1085, 0x1086 }, { 0x108D, 0x108D }, - { 0x109D, 0x109D }, { 0x1160, 0x11FF }, { 0x135D, 0x135F }, - { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, - { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, - { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, - { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, - { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, - { 0x1A17, 0x1A18 }, { 0x1A56, 0x1A56 }, { 0x1A58, 0x1A5E }, - { 0x1A60, 0x1A60 }, { 0x1A62, 0x1A62 }, { 0x1A65, 0x1A6C }, - { 0x1A73, 0x1A7C }, { 0x1A7F, 0x1A7F }, { 0x1B00, 0x1B03 }, - { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, - { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1B80, 0x1B81 }, - { 0x1BA2, 0x1BA5 }, { 0x1BA8, 0x1BA9 }, { 0x1BAB, 0x1BAB }, - { 0x1BE6, 0x1BE6 }, { 0x1BE8, 0x1BE9 }, { 0x1BED, 0x1BED }, - { 0x1BEF, 0x1BF1 }, { 0x1C2C, 0x1C33 }, { 0x1C36, 0x1C37 }, - { 0x1CD0, 0x1CD2 }, { 0x1CD4, 0x1CE0 }, { 0x1CE2, 0x1CE8 }, - { 0x1CED, 0x1CED }, { 0x1CF4, 0x1CF4 }, { 0x1DC0, 0x1DE6 }, - { 0x1DFC, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, - { 0x2060, 0x2064 }, { 0x206A, 0x206F }, { 0x20D0, 0x20F0 }, - { 0x2CEF, 0x2CF1 }, { 0x2D7F, 0x2D7F }, { 0x2DE0, 0x2DFF }, - { 0x302A, 0x302D }, { 0x3099, 0x309A }, { 0xA66F, 0xA672 }, - { 0xA674, 0xA67D }, { 0xA69F, 0xA69F }, { 0xA6F0, 0xA6F1 }, - { 0xA802, 0xA802 }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, - { 0xA825, 0xA826 }, { 0xA8C4, 0xA8C4 }, { 0xA8E0, 0xA8F1 }, - { 0xA926, 0xA92D }, { 0xA947, 0xA951 }, { 0xA980, 0xA982 }, - { 0xA9B3, 0xA9B3 }, { 0xA9B6, 0xA9B9 }, { 0xA9BC, 0xA9BC }, - { 0xAA29, 0xAA2E }, { 0xAA31, 0xAA32 }, { 0xAA35, 0xAA36 }, - { 0xAA43, 0xAA43 }, { 0xAA4C, 0xAA4C }, { 0xAAB0, 0xAAB0 }, - { 0xAAB2, 0xAAB4 }, { 0xAAB7, 0xAAB8 }, { 0xAABE, 0xAABF }, - { 0xAAC1, 0xAAC1 }, { 0xAAEC, 0xAAED }, { 0xAAF6, 0xAAF6 }, - { 0xABE5, 0xABE5 }, { 0xABE8, 0xABE8 }, { 0xABED, 0xABED }, - { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE26 }, - { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB } - }; - size_t min = 0, mid, max = NELEM(comb) - 1; - - /* test for 8-bit control characters */ - if (c < 32 || (c >= 0x7F && c < 0xA0)) - return (c ? -1 : 0); - - /* binary search in table of non-spacing characters */ - if (c >= comb[0].first && c <= comb[max].last) - while (max >= min) { - mid = (min + max) / 2; - if (c > comb[mid].last) - min = mid + 1; - else if (c < comb[mid].first) - max = mid - 1; - else - return (0); - } - - /* if we arrive here, c is not a combining or C0/C1 control char */ - - return ((c >= 0x1100 && ( - c <= 0x115F || /* Hangul Jamo init. consonants */ - c == 0x2329 || c == 0x232A || - (c >= 0x2E80 && c <= 0xA4CF && c != 0x303F) || /* CJK ... Yi */ - (c >= 0xAC00 && c <= 0xD7A3) || /* Hangul Syllables */ - (c >= 0xF900 && c <= 0xFAFF) || /* CJK Compatibility Ideographs */ - (c >= 0xFE10 && c <= 0xFE19) || /* Vertical forms */ - (c >= 0xFE30 && c <= 0xFE6F) || /* CJK Compatibility Forms */ - (c >= 0xFF00 && c <= 0xFF60) || /* Fullwidth Forms */ - (c >= 0xFFE0 && c <= 0xFFE6))) ? 2 : 1); -} -/* --- end of wcwidth.c excerpt --- */ -#endif - /* * Wrapper around access(2) because it says root can execute everything * on some operating systems. Does not set errno, no user needs it. Use @@ -974,3 +915,277 @@ ksh_access(const char *fn, int mode) return (rv); } + +#ifndef MKSH_mirbsd_wcwidth +/* From: X11/xc/programs/xterm/wcwidth.c,v 1.6 2013/05/31 23:27:09 tg Exp $ */ + +struct mb_ucsrange { + unsigned short beg; + unsigned short end; +}; + +static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems, + unsigned int val); + +/* + * Generated by MirOS: contrib/code/Snippets/eawparse,v 1.1 2013/05/31 23:27:16 tg Exp $ + * from Unicode 6.2.0 + */ + +static const struct mb_ucsrange mb_ucs_combining[] = { + { 0x0300, 0x036F }, + { 0x0483, 0x0489 }, + { 0x0591, 0x05BD }, + { 0x05BF, 0x05BF }, + { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, + { 0x05C7, 0x05C7 }, + { 0x0600, 0x0604 }, + { 0x0610, 0x061A }, + { 0x064B, 0x065F }, + { 0x0670, 0x0670 }, + { 0x06D6, 0x06DD }, + { 0x06DF, 0x06E4 }, + { 0x06E7, 0x06E8 }, + { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, + { 0x0711, 0x0711 }, + { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, + { 0x07EB, 0x07F3 }, + { 0x0816, 0x0819 }, + { 0x081B, 0x0823 }, + { 0x0825, 0x0827 }, + { 0x0829, 0x082D }, + { 0x0859, 0x085B }, + { 0x08E4, 0x08FE }, + { 0x0900, 0x0902 }, + { 0x093A, 0x093A }, + { 0x093C, 0x093C }, + { 0x0941, 0x0948 }, + { 0x094D, 0x094D }, + { 0x0951, 0x0957 }, + { 0x0962, 0x0963 }, + { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, + { 0x09C1, 0x09C4 }, + { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, + { 0x0A01, 0x0A02 }, + { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, + { 0x0A47, 0x0A48 }, + { 0x0A4B, 0x0A4D }, + { 0x0A51, 0x0A51 }, + { 0x0A70, 0x0A71 }, + { 0x0A75, 0x0A75 }, + { 0x0A81, 0x0A82 }, + { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, + { 0x0AC7, 0x0AC8 }, + { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, + { 0x0B01, 0x0B01 }, + { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, + { 0x0B41, 0x0B44 }, + { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, + { 0x0B62, 0x0B63 }, + { 0x0B82, 0x0B82 }, + { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, + { 0x0C3E, 0x0C40 }, + { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, + { 0x0C55, 0x0C56 }, + { 0x0C62, 0x0C63 }, + { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, + { 0x0CC6, 0x0CC6 }, + { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, + { 0x0D41, 0x0D44 }, + { 0x0D4D, 0x0D4D }, + { 0x0D62, 0x0D63 }, + { 0x0DCA, 0x0DCA }, + { 0x0DD2, 0x0DD4 }, + { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, + { 0x0E34, 0x0E3A }, + { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, + { 0x0EB4, 0x0EB9 }, + { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, + { 0x0F18, 0x0F19 }, + { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, + { 0x0F39, 0x0F39 }, + { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, + { 0x0F86, 0x0F87 }, + { 0x0F8D, 0x0F97 }, + { 0x0F99, 0x0FBC }, + { 0x0FC6, 0x0FC6 }, + { 0x102D, 0x1030 }, + { 0x1032, 0x1037 }, + { 0x1039, 0x103A }, + { 0x103D, 0x103E }, + { 0x1058, 0x1059 }, + { 0x105E, 0x1060 }, + { 0x1071, 0x1074 }, + { 0x1082, 0x1082 }, + { 0x1085, 0x1086 }, + { 0x108D, 0x108D }, + { 0x109D, 0x109D }, + { 0x1160, 0x11FF }, + { 0x135D, 0x135F }, + { 0x1712, 0x1714 }, + { 0x1732, 0x1734 }, + { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, + { 0x17B4, 0x17B5 }, + { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, + { 0x17C9, 0x17D3 }, + { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, + { 0x18A9, 0x18A9 }, + { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, + { 0x1932, 0x1932 }, + { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, + { 0x1A56, 0x1A56 }, + { 0x1A58, 0x1A5E }, + { 0x1A60, 0x1A60 }, + { 0x1A62, 0x1A62 }, + { 0x1A65, 0x1A6C }, + { 0x1A73, 0x1A7C }, + { 0x1A7F, 0x1A7F }, + { 0x1B00, 0x1B03 }, + { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, + { 0x1B3C, 0x1B3C }, + { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, + { 0x1B80, 0x1B81 }, + { 0x1BA2, 0x1BA5 }, + { 0x1BA8, 0x1BA9 }, + { 0x1BAB, 0x1BAB }, + { 0x1BE6, 0x1BE6 }, + { 0x1BE8, 0x1BE9 }, + { 0x1BED, 0x1BED }, + { 0x1BEF, 0x1BF1 }, + { 0x1C2C, 0x1C33 }, + { 0x1C36, 0x1C37 }, + { 0x1CD0, 0x1CD2 }, + { 0x1CD4, 0x1CE0 }, + { 0x1CE2, 0x1CE8 }, + { 0x1CED, 0x1CED }, + { 0x1CF4, 0x1CF4 }, + { 0x1DC0, 0x1DE6 }, + { 0x1DFC, 0x1DFF }, + { 0x200B, 0x200F }, + { 0x202A, 0x202E }, + { 0x2060, 0x2064 }, + { 0x206A, 0x206F }, + { 0x20D0, 0x20F0 }, + { 0x2CEF, 0x2CF1 }, + { 0x2D7F, 0x2D7F }, + { 0x2DE0, 0x2DFF }, + { 0x302A, 0x302D }, + { 0x3099, 0x309A }, + { 0xA66F, 0xA672 }, + { 0xA674, 0xA67D }, + { 0xA69F, 0xA69F }, + { 0xA6F0, 0xA6F1 }, + { 0xA802, 0xA802 }, + { 0xA806, 0xA806 }, + { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, + { 0xA8C4, 0xA8C4 }, + { 0xA8E0, 0xA8F1 }, + { 0xA926, 0xA92D }, + { 0xA947, 0xA951 }, + { 0xA980, 0xA982 }, + { 0xA9B3, 0xA9B3 }, + { 0xA9B6, 0xA9B9 }, + { 0xA9BC, 0xA9BC }, + { 0xAA29, 0xAA2E }, + { 0xAA31, 0xAA32 }, + { 0xAA35, 0xAA36 }, + { 0xAA43, 0xAA43 }, + { 0xAA4C, 0xAA4C }, + { 0xAAB0, 0xAAB0 }, + { 0xAAB2, 0xAAB4 }, + { 0xAAB7, 0xAAB8 }, + { 0xAABE, 0xAABF }, + { 0xAAC1, 0xAAC1 }, + { 0xAAEC, 0xAAED }, + { 0xAAF6, 0xAAF6 }, + { 0xABE5, 0xABE5 }, + { 0xABE8, 0xABE8 }, + { 0xABED, 0xABED }, + { 0xFB1E, 0xFB1E }, + { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE26 }, + { 0xFEFF, 0xFEFF }, + { 0xFFF9, 0xFFFB } +}; + +static const struct mb_ucsrange mb_ucs_fullwidth[] = { + { 0x1100, 0x115F }, + { 0x2329, 0x232A }, + { 0x2E80, 0x303E }, + { 0x3040, 0xA4CF }, + { 0xA960, 0xA97F }, + { 0xAC00, 0xD7A3 }, + { 0xF900, 0xFAFF }, + { 0xFE10, 0xFE19 }, + { 0xFE30, 0xFE6F }, + { 0xFF00, 0xFF60 }, + { 0xFFE0, 0xFFE6 } +}; + +/* simple binary search in ranges, with bounds optimisation */ +static int +mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems, unsigned int val) +{ + size_t min = 0, mid, max = elems; + + if (val < arr[min].beg || val > arr[max - 1].end) + return (0); + + while (min < max) { + mid = (min + max) / 2; + + if (val < arr[mid].beg) + max = mid; + else if (val > arr[mid].end) + min = mid + 1; + else + return (1); + } + return (0); +} + +/* Unix column width of a wide character (Unicode code point, really) */ +int +utf_wcwidth(unsigned int wc) +{ + /* except NUL, C0/C1 control characters and DEL yield -1 */ + if (wc < 0x20 || (wc >= 0x7F && wc < 0xA0)) + return (wc ? -1 : 0); + + /* combining characters use 0 screen columns */ + if (mb_ucsbsearch(mb_ucs_combining, NELEM(mb_ucs_combining), wc)) + return (0); + + /* all others use 1 or 2 screen columns */ + if (mb_ucsbsearch(mb_ucs_fullwidth, NELEM(mb_ucs_fullwidth), wc)) + return (2); + return (1); +} +#endif diff --git a/src/funcs.c b/src/funcs.c index e23ec8b..6698f5e 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -1,5 +1,5 @@ /* $OpenBSD: c_ksh.c,v 1.33 2009/02/07 14:03:24 kili Exp $ */ -/* $OpenBSD: c_sh.c,v 1.41 2010/03/27 09:10:01 jmc Exp $ */ +/* $OpenBSD: c_sh.c,v 1.43 2013/04/19 17:39:45 deraadt Exp $ */ /* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */ /* $OpenBSD: c_ulimit.c,v 1.17 2008/03/21 12:51:19 millert Exp $ */ @@ -38,7 +38,7 @@ #endif #endif -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.238 2013/02/18 22:47:32 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.244 2013/06/03 22:28:32 tg Exp $"); #if HAVE_KILLPG /* @@ -84,70 +84,69 @@ c_false(const char **wp MKSH_A_UNUSED) } /* - * A leading = means assignments before command are kept; - * a leading * means a POSIX special builtin; - * a leading + means a POSIX regular builtin - * (* and + should not be combined). + * A leading = means assignments before command are kept. + * A leading * means a POSIX special builtin. */ const struct builtin mkshbuiltins[] = { {"*=.", c_dot}, {"*=:", c_true}, {"[", c_test}, + /* no =: AT&T manual wrong */ + {Talias, c_alias}, {"*=break", c_brkcont}, {Tgbuiltin, c_builtin}, + {"cat", c_cat}, + {"cd", c_cd}, + /* dash compatibility hack */ + {"chdir", c_cd}, + {"command", c_command}, {"*=continue", c_brkcont}, + {"echo", c_print}, {"*=eval", c_eval}, {"*=exec", c_exec}, {"*=exit", c_exitreturn}, - {"+false", c_false}, - {"*=return", c_exitreturn}, - {Tsgset, c_set}, - {"*=shift", c_shift}, - {"=times", c_times}, - {"*=trap", c_trap}, - {"+=wait", c_wait}, - {"+read", c_read}, - {"test", c_test}, - {"+true", c_true}, - {"ulimit", c_ulimit}, - {"+umask", c_umask}, - {Tsgunset, c_unset}, - /* no =: AT&T manual wrong */ - {Tpalias, c_alias}, - {"+cd", c_cd}, - /* dash compatibility hack */ - {"chdir", c_cd}, - {"+command", c_command}, - {"echo", c_print}, {Tsgexport, c_typeset}, - {"+fc", c_fc}, - {"+getopts", c_getopts}, + {"false", c_false}, + {"fc", c_fc}, + {"getopts", c_getopts}, {"=global", c_typeset}, - {"+jobs", c_jobs}, - {"+kill", c_kill}, + {"jobs", c_jobs}, + {"kill", c_kill}, {"let", c_let}, + {"let]", c_let}, {"print", c_print}, -#ifdef MKSH_PRINTF_BUILTIN - {"printf", c_printf}, -#endif {"pwd", c_pwd}, + {"read", c_read}, {Tsgreadonly, c_typeset}, + {"realpath", c_realpath}, + {"rename", c_rename}, + {"*=return", c_exitreturn}, + {Tsgset, c_set}, + {"*=shift", c_shift}, + {"test", c_test}, + {"*=times", c_times}, + {"*=trap", c_trap}, + {"true", c_true}, {T_typeset, c_typeset}, - {Tpunalias, c_unalias}, + {"ulimit", c_ulimit}, + {"umask", c_umask}, + {Tunalias, c_unalias}, + {Tsgunset, c_unset}, + {"=wait", c_wait}, {"whence", c_whence}, #ifndef MKSH_UNEMPLOYED - {"+bg", c_fgbg}, - {"+fg", c_fgbg}, + {"bg", c_fgbg}, + {"fg", c_fgbg}, #endif #ifndef MKSH_NO_CMDLINE_EDITING {"bind", c_bind}, #endif - {"cat", c_cat}, #if HAVE_MKNOD {"mknod", c_mknod}, #endif - {"realpath", c_realpath}, - {"rename", c_rename}, +#ifdef MKSH_PRINTF_BUILTIN + {"printf", c_printf}, +#endif #if HAVE_SELECT {"sleep", c_sleep}, #endif @@ -836,7 +835,7 @@ c_typeset(const char **wp) shf_putc('\n', shl_stdout); } } else if (!typeset(wp[i], fset, fclr, field, base)) { - bi_errorf("%s: %s", wp[i], "not identifier"); + bi_errorf("%s: %s", wp[i], "is not an identifier"); goto errout; } } @@ -2253,7 +2252,7 @@ c_trap(const char **wp) wp += builtin_opt.optind; if (*wp == NULL) { - for (p = sigtraps, i = NSIG+1; --i >= 0; p++) + for (p = sigtraps, i = NSIG + 1; --i >= 0; p++) if (p->trap != NULL) { shf_puts("trap -- ", shl_stdout); print_value_quoted(shl_stdout, p->trap); @@ -2427,12 +2426,13 @@ c_set(const char **wp) * which assumes the exit value set will be that of the $() * (subst_exstat is cleared in execute() so that it will be 0 * if there are no command substitutions). - * Switched ksh (!posix !sh) to POSIX in mksh R39b. */ #ifdef MKSH_LEGACY_MODE - return (subst_exstat); + /* traditional behaviour, unless set -o posix */ + return (Flag(FPOSIX) ? 0 : subst_exstat); #else - return (Flag(FSH) ? subst_exstat : 0); + /* conformant behaviour, unless set -o sh +o posix */ + return (Flag(FSH) && !Flag(FPOSIX) ? subst_exstat : 0); #endif } @@ -3726,7 +3726,7 @@ c_cat(const char **wp) rv = 0; if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) { - bi_errorf(Toomem, (unsigned long)MKSH_CAT_BUFSIZ); + bi_errorf(Toomem, (size_t)MKSH_CAT_BUFSIZ); return (1); } diff --git a/src/jobs.c b/src/jobs.c index 4be7356..3277c78 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -1,7 +1,8 @@ -/* $OpenBSD: jobs.c,v 1.38 2009/12/12 04:28:44 deraadt Exp $ */ +/* $OpenBSD: jobs.c,v 1.39 2009/12/13 04:36:48 deraadt Exp $ */ /*- - * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 + * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, + * 2012, 2013 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -22,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.94 2012/12/28 02:28:36 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.100 2013/07/26 20:33:23 tg Exp $"); #if HAVE_KILLPG #define mksh_killpg killpg @@ -44,12 +45,11 @@ struct proc { int state; int status; /* wait status */ /* process command string from vistree */ - char command[64 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) + + char command[256 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) + 2 * sizeof(int))]; }; /* Notify/print flag - j_print() argument */ -#define JP_NONE 0 /* don't print anything */ #define JP_SHORT 1 /* print signals processes were killed by */ #define JP_MEDIUM 2 /* print [job-num] -/+ command */ #define JP_LONG 3 /* print [job-num] -/+ pid command */ @@ -102,17 +102,14 @@ struct job { #define JW_PIPEST 0x08 /* want PIPESTATUS */ /* Error codes for j_lookup() */ -#define JL_OK 0 -#define JL_NOSUCH 1 /* no such job */ -#define JL_AMBIG 2 /* %foo or %?foo is ambiguous */ -#define JL_INVALID 3 /* non-pid, non-% job id */ +#define JL_NOSUCH 0 /* no such job */ +#define JL_AMBIG 1 /* %foo or %?foo is ambiguous */ +#define JL_INVALID 2 /* non-pid, non-% job id */ static const char * const lookup_msgs[] = { - null, "no such job", "ambiguous", - "argument must be %job or process id", - NULL + "argument must be %job or process id" }; static Job *job_list; /* job list */ @@ -463,7 +460,7 @@ exchild(struct op *t, int flags, forksleep <<= 1; } /* ensure $RANDOM changes between parent and child */ - rndset((long)cldpid); + rndset((unsigned long)cldpid); /* fork failed? */ if (cldpid < 0) { kill_job(j, SIGKILL); @@ -506,9 +503,6 @@ exchild(struct op *t, int flags, /* Do this before restoring signal */ if (flags & XCOPROC) coproc_cleanup(false); -#ifndef MKSH_NOPROSPECTOFWORK - sigprocmask(SIG_SETMASK, &omask, NULL); -#endif cleanup_parents_env(); #ifndef MKSH_UNEMPLOYED /* @@ -543,6 +537,10 @@ exchild(struct op *t, int flags, } /* in case of $(jobs) command */ remove_job(j, "child"); +#ifndef MKSH_NOPROSPECTOFWORK + /* remove_job needs SIGCHLD blocked still */ + sigprocmask(SIG_SETMASK, &omask, NULL); +#endif nzombie = 0; #ifndef MKSH_UNEMPLOYED ttypgrp_ok = false; @@ -906,7 +904,7 @@ j_jobs(const char *cp, int slp, zflag = 1; } if (cp) { - int ecode; + int ecode; if ((j = j_lookup(cp, &ecode)) == NULL) { #ifndef MKSH_NOPROSPECTOFWORK @@ -1220,6 +1218,8 @@ j_waitj(Job *j, ARRAY | INT_U | AINDEX; got_array: vp->val.i = proc_errorlevel(p); + if (Flag(FPIPEFAIL) && vp->val.i) + rv = vp->val.i; p = p->next; } } @@ -1251,8 +1251,7 @@ j_waitj(Job *j, static void j_sigchld(int sig MKSH_A_UNUSED) { - /* this runs inside interrupt context, with errno saved */ - + int saved_errno = errno; Job *j; Proc *p = NULL; pid_t pid; @@ -1340,7 +1339,7 @@ j_sigchld(int sig MKSH_A_UNUSED) #ifdef MKSH_NO_SIGSUSPEND sigprocmask(SIG_SETMASK, &omask, NULL); #endif - /* nothing */; + errno = saved_errno; } /* diff --git a/src/lalloc.c b/src/lalloc.c index daaee57..f5dc830 100644 --- a/src/lalloc.c +++ b/src/lalloc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009, 2010, 2011 + * Copyright (c) 2009, 2010, 2011, 2013 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -20,7 +20,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.19 2011/09/07 15:24:16 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.20 2013/06/03 22:28:33 tg Exp $"); /* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */ #if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0) @@ -100,7 +100,7 @@ aresize(void *ptr, size_t numb, Area *ap) || ALLOC_ISUNALIGNED(lp) #endif ) - internal_errorf(Toomem, (unsigned long)numb); + internal_errorf(Toomem, numb); /* this only works because Area is an ALLOC_ITEM */ lp->next = ap->next; ap->next = lp; diff --git a/src/lex.c b/src/lex.c index 3f60742..e58d8b8 100644 --- a/src/lex.c +++ b/src/lex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lex.c,v 1.46 2013/01/20 14:47:46 stsp Exp $ */ +/* $OpenBSD: lex.c,v 1.47 2013/03/03 19:11:34 guenther Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.182 2013/02/19 18:45:20 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.188 2013/08/10 13:44:31 tg Exp $"); /* * states while lexing word @@ -102,8 +102,6 @@ static void gethere(bool); static Lex_state *push_state_i(State_info *, Lex_state *); static Lex_state *pop_state_i(State_info *, Lex_state *); -static int dopprompt(const char *, int, bool); - static int backslash_skip; static int ignore_backslash_newline; @@ -338,7 +336,9 @@ yylex(int cf) } break; case '\'': - open_ssquote: + open_ssquote_unless_heredoc: + if ((cf & HEREDOC)) + goto store_char; *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SSQUOTE); @@ -421,8 +421,14 @@ yylex(int cf) wp += cz; } } else if (c == '{') /*}*/ { - c = getsc(); - if (ctype(c, C_IFSWS)) { + if ((c = getsc()) == '|') { + /* + * non-subenvironment + * value substitution + */ + c = VALSUB; + goto subst_command2; + } else if (ctype(c, C_IFSWS)) { /* * non-subenvironment * "command" substitution @@ -495,7 +501,8 @@ yylex(int cf) PUSH_STATE(STBRACEKORN); } else { ungetsc(c); - if (state == SDQUOTE) + if (state == SDQUOTE || + state == SQBRACE) PUSH_STATE(SQBRACE); else PUSH_STATE(SBRACE); @@ -616,6 +623,8 @@ yylex(int cf) case SSQUOTE: if (c == '\'') { POP_STATE(); + if ((cf & HEREDOC) || state == SQBRACE) + goto store_char; *wp++ = CQUOTE; ignore_backslash_newline--; } else { @@ -693,7 +702,7 @@ yylex(int cf) case SBRACE: if (c == '\'') - goto open_ssquote; + goto open_ssquote_unless_heredoc; else if (c == '\\') goto getsc_qchar; common_SQBRACE: @@ -812,7 +821,7 @@ yylex(int cf) } break; case '\'': - goto open_ssquote; + goto open_ssquote_unless_heredoc; case '$': if ((c2 = getsc()) == '\'') { open_sequote: @@ -898,7 +907,11 @@ yylex(int cf) state = SBASE; dp = Xstring(ws, wp); - if ((c == '<' || c == '>' || c == '&') && state == SBASE) { + if (state == SBASE && ( +#ifndef MKSH_LEGACY_MODE + (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) || +#endif + c == '<' || c == '>')) { struct ioword *iop = alloc(sizeof(struct ioword), ATEMP); if (Xlength(ws, wp) == 0) @@ -1374,7 +1387,7 @@ getsc_line(Source *s) Flag(FEMACS) || Flag(FGMACS))) { int nread; - nread = x_read(xp, LINE); + nread = x_read(xp); if (nread < 0) /* read error */ nread = 0; @@ -1505,8 +1518,8 @@ set_prompt(int to, Source *s) } } -static int -dopprompt(const char *cp, int ntruncate, bool doprint) +int +pprompt(const char *cp, int ntruncate) { int columns = 0, lines = 0; bool indelimit = false; @@ -1539,35 +1552,21 @@ dopprompt(const char *cp, int ntruncate, bool doprint) else if (UTFMODE && ((unsigned char)*cp > 0x7F)) { const char *cp2; columns += utf_widthadj(cp, &cp2); - if (doprint && (indelimit || - (ntruncate < (x_cols * lines + columns)))) + if (indelimit || + (ntruncate < (x_cols * lines + columns))) shf_write(cp, cp2 - cp, shl_out); cp = cp2 - /* loop increment */ 1; continue; } else columns++; - if (doprint && (*cp != delimiter) && + if ((*cp != delimiter) && (indelimit || (ntruncate < (x_cols * lines + columns)))) shf_putc(*cp, shl_out); } - if (doprint) - shf_flush(shl_out); + shf_flush(shl_out); return (x_cols * lines + columns); } - -void -pprompt(const char *cp, int ntruncate) -{ - dopprompt(cp, ntruncate, true); -} - -int -promptlen(const char *cp) -{ - return (dopprompt(cp, 0, false)); -} - /* * Read the variable part of a ${...} expression (i.e. up to but not * including the :[-+?=#%] or close-brace). diff --git a/src/lksh.1 b/src/lksh.1 new file mode 100644 index 0000000..31dc6ff --- /dev/null +++ b/src/lksh.1 @@ -0,0 +1,297 @@ +.\" $MirOS: src/bin/mksh/lksh.1,v 1.5 2013/05/22 18:18:06 tg Exp $ +.\"- +.\" Copyright (c) 2008, 2009, 2010, 2012, 2013 +.\" Thorsten “mirabilos” Glaser +.\" +.\" Provided that these terms and disclaimer and all copyright notices +.\" are retained or reproduced in an accompanying document, permission +.\" is granted to deal in this work without restriction, including un‐ +.\" limited rights to use, publicly perform, distribute, sell, modify, +.\" merge, give away, or sublicence. +.\" +.\" This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to +.\" the utmost extent permitted by applicable law, neither express nor +.\" implied; without malicious intent or gross negligence. In no event +.\" may a licensor, author or contributor be held liable for indirect, +.\" direct, other damage, loss, or other issues arising in any way out +.\" of dealing in the work, even if advised of the possibility of such +.\" damage or existence of a defect, except proven that it results out +.\" of said person’s immediate fault when using the work as intended. +.\"- +.\" Try to make GNU groff and AT&T nroff more compatible +.\" * ` generates ‘ in gnroff, so use \` +.\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq +.\" * - generates ‐ in gnroff, \- generates −, so .tr it to - +.\" thus use - for hyphens and \- for minus signs and option dashes +.\" * ~ is size-reduced and placed atop in groff, so use \*(TI +.\" * ^ is size-reduced and placed atop in groff, so use \*(ha +.\" * \(en does not work in nroff, so use \*(en +.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba +.\" Also make sure to use \& especially with two-letter words. +.\" The section after the "doc" macropackage has been loaded contains +.\" additional code to convene between the UCB mdoc macropackage (and +.\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. +.\" +.ie \n(.g \{\ +. if \*[.T]ascii .tr \-\N'45' +. if \*[.T]latin1 .tr \-\N'45' +. if \*[.T]utf8 .tr \-\N'45' +. ds <= \[<=] +. ds >= \[>=] +. ds Rq \[rq] +. ds Lq \[lq] +. ds sL \(aq +. ds sR \(aq +. if \*[.T]utf8 .ds sL ` +. if \*[.T]ps .ds sL ` +. if \*[.T]utf8 .ds sR ' +. if \*[.T]ps .ds sR ' +. ds aq \(aq +. ds TI \(ti +. ds ha \(ha +. ds en \(en +.\} +.el \{\ +. ds aq ' +. ds TI ~ +. ds ha ^ +. ds en \(em +.\} +.\" +.\" Implement .Dd with the Mdocdate RCS keyword +.\" +.rn Dd xD +.de Dd +.ie \\$1$Mdocdate: \{\ +. xD \\$2 \\$3, \\$4 +.\} +.el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 +.. +.\" +.\" .Dd must come before definition of .Mx, because when called +.\" with -mandoc, it might implement .Mx itself, but we want to +.\" use our own definition. And .Dd must come *first*, always. +.\" +.Dd $Mdocdate: May 22 2013 $ +.\" +.\" Check which macro package we use, and do other -mdoc setup. +.\" +.ie \n(.g \{\ +. if \*[.T]utf8 .tr \[la]\*(Lt +. if \*[.T]utf8 .tr \[ra]\*(Gt +. ie d volume-ds-1 .ds tT gnu +. el .ds tT bsd +.\} +.el .ds tT ucb +.\" +.\" Implement .Mx (MirBSD) +.\" +.ie "\*(tT"gnu" \{\ +. eo +. de Mx +. nr curr-font \n[.f] +. nr curr-size \n[.ps] +. ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] +. ds str-Mx1 \*[Tn-font-size]\%MirOS\*[str-Mx] +. if !\n[arg-limit] \ +. if \n[.$] \{\ +. ds macro-name Mx +. parse-args \$@ +. \} +. if (\n[arg-limit] > \n[arg-ptr]) \{\ +. nr arg-ptr +1 +. ie (\n[type\n[arg-ptr]] == 2) \ +. as str-Mx1 \~\*[arg\n[arg-ptr]] +. el \ +. nr arg-ptr -1 +. \} +. ds arg\n[arg-ptr] "\*[str-Mx1] +. nr type\n[arg-ptr] 2 +. ds space\n[arg-ptr] "\*[space] +. nr num-args (\n[arg-limit] - \n[arg-ptr]) +. nr arg-limit \n[arg-ptr] +. if \n[num-args] \ +. parse-space-vector +. print-recursive +.. +. ec +. ds sP \s0 +. ds tN \*[Tn-font-size] +.\} +.el \{\ +. de Mx +. nr cF \\n(.f +. nr cZ \\n(.s +. ds aa \&\f\\n(cF\s\\n(cZ +. if \\n(aC==0 \{\ +. ie \\n(.$==0 \&MirOS\\*(aa +. el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +. \} +. if \\n(aC>\\n(aP \{\ +. nr aP \\n(aP+1 +. ie \\n(C\\n(aP==2 \{\ +. as b1 \&MirOS\ #\&\\*(A\\n(aP\\*(aa +. ie \\n(aC>\\n(aP \{\ +. nr aP \\n(aP+1 +. nR +. \} +. el .aZ +. \} +. el \{\ +. as b1 \&MirOS\\*(aa +. nR +. \} +. \} +.. +.\} +.\"- +.Dt LKSH 1 +.Os MirBSD +.Sh NAME +.Nm lksh +.Nd Legacy Korn shell built on mksh +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl +abCefhiklmnprUuvXx +.Op Fl +o Ar opt +.Oo +.Fl c Ar string \*(Ba +.Fl s \*(Ba +.Ar file +.Op Ar args ... +.Oc +.Ek +.Sh DESCRIPTION +.Nm +is a command interpreter intended exclusively for running legacy +shell scripts. +It is built on +.Nm mksh ; +refer to its manual page for details on the scripting language. +It is recommended to port scripts to +.Nm mksh +instead of relying on legacy or idiotic POSIX-mandated behaviour, +since the MirBSD Korn Shell scripting language is much more consistent. +.Sh LEGACY MODE +.Nm +has the following differences from +.Nm mksh : +.Bl -bullet +.It +There is no explicit support for interactive use, +nor any command line editing or history code. +Hence, +.Nm +is not suitable as a user's login shell, either; use +.Nm mksh +instead. +.It +The +.Ev KSH_VERSION +string identifies +.Nm +as +.Dq LEGACY KSH +instead of +.Dq MIRBSD KSH . +.It +.Nm +only offers the traditional ten file descriptors to scripts. +.It +.Nm +uses +.Tn POSIX +arithmetics, which has quite a few implications: +The data type for arithmetics is the host ISO C +.Vt long +data type. +Signed integer wraparound is Undefined Behaviour. +The sign of the result of a modulo operation with at least one +negative operand is unspecified. +Shift operations on negative numbers are unspecified. +Division of the largest negative number by \-1 is Undefined Behaviour. +The compiler is permitted to delete all data and crash the system +if Undefined Behaviour occurs. +.It +The rotation arithmetic operators are not available. +.It +The shift arithmetic operators take all bits of the second operand into +account; if they exceed permitted precision, the result is unspecified. +.It +The +.Tn GNU +.Nm bash +extension &\*(Gt to redirect stdout and stderr in one go is not parsed. +.It +The +.Nm mksh +command line option +.Fl T +is not available. +.It +Unless +.Ic set -o posix +is active, +.Nm +always uses traditional mode for constructs like: +.Bd -literal -offset indent +$ set -- $(getopt ab:c "$@") +$ echo $? +.Ed +.Pp +POSIX mandates this to show 0, but traditional mode +passes through the errorlevel from the +.Xr getopt 1 +command. +.It +.Nm lksh , +unlike +.At +.Nm ksh , +does not keep file descriptors \*(Gt 2 private. +.El +.Sh SEE ALSO +.Xr mksh 1 +.Pp +.Pa https://www.mirbsd.org/mksh.htm +.Pp +.Pa https://www.mirbsd.org/ksh\-chan.htm +.Sh CAVEATS +To use +.Nm +as +.Pa /bin/sh , +compilation to enable +.Ic set -o posix +by default is highly recommended for better standards compliance. +.Pp +.Nm +tries to make a cross between a legacy bourne/posix compatibl-ish +shell and a legacy pdksh-alike but +.Dq legacy +is not exactly specified. +.Pp +The +.Ic set +built-in command does not have all options one would expect +from a full-blown +.Nm mksh +or +.Nm pdksh . +.Pp +Talk to the +.Mx +development team using the mailing list at +.Aq miros\-mksh@mirbsd.org +or the +.Li \&#\&!/bin/mksh +.Pq or Li \&#ksh +IRC channel at +.Pa irc.freenode.net +.Pq Port 6697 SSL, 6667 unencrypted +if you need any further quirks or assistance, +and consider migrating your legacy scripts to work with +.Nm mksh +instead of requiring +.Nm . diff --git a/src/main.c b/src/main.c index 61b09e2..291ab40 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.51 2012/09/10 01:25:30 tedu Exp $ */ +/* $OpenBSD: main.c,v 1.52 2013/06/15 17:25:19 millert Exp $ */ /* $OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $ */ /* $OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $ */ /* $OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $ */ @@ -34,7 +34,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/main.c,v 1.260 2013/02/10 21:42:16 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/main.c,v 1.269 2013/07/25 18:07:46 tg Exp $"); extern char **environ; @@ -48,6 +48,7 @@ extern char **environ; static uint8_t isuc(const char *); static int main_init(int, const char *[], Source **, struct block **); +uint32_t chvt_rndsetup(const void *, size_t); void chvt_reinit(void); static void reclaim(void); static void remove_temps(struct temp *); @@ -137,15 +138,25 @@ rndsetup(void) /* introduce variation (and yes, second arg MBZ for portability) */ mksh_TIME(bufptr->tv); + h = chvt_rndsetup(bufptr, sizeof(*bufptr)); + + afree(cp, APERM); + return ((mksh_uari_t)h); +} + +uint32_t +chvt_rndsetup(const void *bp, size_t sz) +{ + register uint32_t h; + NZATInit(h); /* variation through pid, ppid, and the works */ NZATUpdateMem(h, &rndsetupstate, sizeof(rndsetupstate)); /* some variation, some possibly entropy, depending on OE */ - NZATUpdateMem(h, bufptr, sizeof(*bufptr)); + NZATUpdateMem(h, bp, sz); NZAATFinish(h); - afree(cp, APERM); - return ((mksh_uari_t)h); + return (h); } void @@ -238,7 +249,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 50 builtins: 75% of 128 = 2^7 */ + /* currently up to 51 builtins: 75% of 128 = 2^7 */ 7); for (i = 0; mkshbuiltins[i].name != NULL; i++) if (!strcmp(ccp, builtin(mkshbuiltins[i].name, @@ -251,11 +262,20 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) if (argi < 0) return (1); +#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED) + /* are we called as -sh or /bin/sh or so? */ + if (!strcmp(ccp, "sh")) { + /* either also turns off braceexpand */ +#ifdef MKSH_BINSHPOSIX + /* enable better POSIX conformance */ + change_flag(FPOSIX, OF_FIRSTTIME, true); +#endif #ifdef MKSH_BINSHREDUCED - /* set FSH if we're called as -sh or /bin/sh or so */ - if (!strcmp(ccp, "sh")) + /* enable kludge/compat mode */ change_flag(FSH, OF_FIRSTTIME, true); #endif + } +#endif } initvar(); @@ -323,6 +343,11 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) */ Flag(FBRACEEXPAND) = 1; + /* + * Turn on "set -x" inheritance by default. + */ + Flag(FXTRACEREC) = 1; + #ifndef MKSH_NO_CMDLINE_EDITING /* * Set edit mode to emacs by default, may be overridden @@ -411,7 +436,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) return (1); } -#ifdef DEBUG +#if defined(DEBUG) && !defined(MKSH_LEGACY_MODE) /* test wraparound of arithmetic types */ { volatile long xl; @@ -442,7 +467,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) xc = 0; --xc; if ((xua2 != 2147483648UL) || - (xl != -2147483648L) || (xul != 2147483648UL) || + (xl != (-2147483647L-1)) || (xul != 2147483648UL) || (xi != -1) || (xui != 4294967295U) || (xa != 0) || (xua != 0) || (xc != 255)) errorf("integer wraparound test failed"); @@ -889,8 +914,9 @@ unwind(int i) } /* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */ - if (i == LEXIT || - ((i == LERROR || i == LINTR) && sigtraps[ksh_SIGEXIT].trap)) { + if (i == LEXIT || ((i == LERROR || i == LINTR) && + sigtraps[ksh_SIGEXIT].trap && + (!Flag(FTALKING) || Flag(FERREXIT)))) { ++trap_nested; runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1); --trap_nested; @@ -1006,6 +1032,10 @@ quitenv(struct shf *shf) #ifndef MKSH_NO_CMDLINE_EDITING x_done(); #endif +#ifndef MKSH_NOPROSPECTOFWORK + /* block at least SIGCHLD during/after afreeall */ + sigprocmask(SIG_BLOCK, &sm_sigchld, NULL); +#endif afreeall(APERM); for (fd = 3; fd < NUFILE; fd++) if ((i = fcntl(fd, F_GETFD, 0)) != -1 && @@ -1364,7 +1394,7 @@ initio(void) /* force buffer allocation */ shf_fdopen(1, SHF_WR, shl_stdout); shf_fdopen(2, SHF_WR, shl_out); - shf_fdopen(2, SHF_WR, shl_spare); + shf_fdopen(2, SHF_WR, shl_xtrace); #ifdef DF if ((lfp = getenv("SDMKSH_PATH")) == NULL) { if ((lfp = getenv("HOME")) == NULL || *lfp != '/') @@ -1594,7 +1624,7 @@ maketemp(Area *ap, Temp_type type, struct temp **tlist) { char *cp; size_t len; - int i; + int i, j; struct temp *tp; const char *dir; struct stat sb; @@ -1644,17 +1674,19 @@ maketemp(Area *ap, Temp_type type, struct temp **tlist) } if (type == TT_FUNSUB) { - int nfd; - /* map us high and mark as close-on-exec */ - if ((nfd = savefd(i)) != i) { + if ((j = savefd(i)) != i) { close(i); - i = nfd; + i = j; } - } + + /* operation mode for the shf */ + j = SHF_RD; + } else + j = SHF_WR; /* shf_fdopen cannot fail, so no fd leak */ - tp->shf = shf_fdopen(i, SHF_WR, NULL); + tp->shf = shf_fdopen(i, j, NULL); maketemp_out: tp->next = *tlist; diff --git a/src/misc.c b/src/misc.c index 988f98c..adf4bc4 100644 --- a/src/misc.c +++ b/src/misc.c @@ -3,7 +3,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012 + * 2011, 2012, 2013 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -30,7 +30,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.205 2012/12/17 23:18:08 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.214 2013/08/11 14:57:09 tg Exp $"); #define KSH_CHVT_FLAG #ifdef MKSH_SMALL @@ -54,7 +54,7 @@ static int do_gmatch(const unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); static const unsigned char *cclass(const unsigned char *, unsigned char); #ifdef KSH_CHVT_CODE -static void chvt(const char *); +static void chvt(const Getopt *); #endif /*XXX this should go away */ @@ -123,10 +123,15 @@ Xcheck_grow(XString *xsp, const char *xp, size_t more) return (xsp->beg + (xp - old_beg)); } + #define SHFLAGS_DEFNS #include "sh_flags.h" -const struct shoption options[] = { +#define OFC(i) (options[i][-2]) +#define OFF(i) (((const unsigned char *)options[i])[-1]) +#define OFN(i) (options[i]) + +const char * const options[] = { #define SHFLAGS_ITEMS #include "sh_flags.h" }; @@ -137,15 +142,20 @@ const struct shoption options[] = { size_t option(const char *n) { - size_t i; + size_t i = 0; - if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2]) { - for (i = 0; i < NELEM(options); i++) - if (options[i].c == n[1]) + if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2]) + while (i < NELEM(options)) { + if (OFC(i) == n[1]) + return (i); + ++i; + } + else + while (i < NELEM(options)) { + if (!strcmp(OFN(i), n)) return (i); - } else for (i = 0; i < NELEM(options); i++) - if (options[i].name && strcmp(options[i].name, n) == 0) - return (i); + ++i; + } return ((size_t)-1); } @@ -165,7 +175,7 @@ options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) const struct options_info *oi = (const struct options_info *)arg; shf_snprintf(buf, buflen, "%-*s %s", - oi->opt_width, options[oi->opts[i]].name, + oi->opt_width, OFN(oi->opts[i]), Flag(oi->opts[i]) ? "on" : "off"); return (buf); } @@ -184,12 +194,11 @@ printoptions(bool verbose) oi.opt_width = 0; while (i < NELEM(options)) { - if (options[i].name) { + if ((len = strlen(OFN(i)))) { oi.opts[n++] = i; - len = strlen(options[i].name); if (len > octs) octs = len; - len = utf_mbswidth(options[i].name); + len = utf_mbswidth(OFN(i)); if ((int)len > oi.opt_width) oi.opt_width = (int)len; } @@ -200,10 +209,9 @@ printoptions(bool verbose) } else { /* short version like AT&T ksh93 */ shf_puts(Tset, shl_stdout); - while (i < (int)NELEM(options)) { - if (Flag(i) && options[i].name) - shprintf("%s %s %s", null, "-o", - options[i].name); + while (i < NELEM(options)) { + if (Flag(i) && OFN(i)[0]) + shprintf(" -o %s", OFN(i)); ++i; } shf_putc('\n', shl_stdout); @@ -213,13 +221,15 @@ printoptions(bool verbose) char * getoptions(void) { - size_t i; - char m[(int)FNFLAGS + 1]; + size_t i = 0; + char c, m[(int)FNFLAGS + 1]; char *cp = m; - for (i = 0; i < NELEM(options); i++) - if (options[i].c && Flag(i)) - *cp++ = options[i].c; + while (i < NELEM(options)) { + if ((c = OFC(i)) && Flag(i)) + *cp++ = c; + ++i; + } strndupx(cp, m, cp - m, ATEMP); return (cp); } @@ -229,8 +239,12 @@ void change_flag(enum sh_flag f, int what, bool newset) { unsigned char oldval; - unsigned char newval; + 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 @@ -277,16 +291,46 @@ change_flag(enum sh_flag f, int what, bool newset) setgid(kshegid); #endif } else if ((f == FPOSIX || f == FSH) && newval) { - Flag(FPOSIX) = Flag(FSH) = Flag(FBRACEEXPAND) = 0; - Flag(f) = newval; - } - /* Changing interactive flag? */ - if (f == FTALKING) { + /* Turning on -o posix or -o sh? */ + Flag(FBRACEEXPAND) = 0; + } else if (f == FTALKING) { + /* Changing interactive flag? */ if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) Flag(FTALKING_I) = newval; } } +void +change_xtrace(unsigned char newval, bool dosnapshot) +{ + if (!dosnapshot && newval == Flag(FXTRACE)) + return; + + if (Flag(FXTRACE) == 2) { + shf_putc('\n', shl_xtrace); + Flag(FXTRACE) = 1; + shf_flush(shl_xtrace); + } + + if (!dosnapshot && Flag(FXTRACE) == 1) + switch (newval) { + case 1: + return; + case 2: + goto changed_xtrace; + } + + shf_flush(shl_xtrace); + if (shl_xtrace->fd != 2) + close(shl_xtrace->fd); + if (!newval || (shl_xtrace->fd = savefd(2)) == -1) + shl_xtrace->fd = 2; + + changed_xtrace: + if ((Flag(FXTRACE) = newval) == 2) + shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace); +} + /* * Parse command line and set command arguments. Returns the index of * non-option arguments, -1 if there is an error. @@ -306,10 +350,11 @@ parse_args(const char **argv, size_t i; int optc, arrayset = 0; bool sortargs = false; + bool fcompatseen = false; /* First call? Build option strings... */ if (cmd_opts[0] == '\0') { - char *p = cmd_opts, *q = set_opts; + char ch, *p = cmd_opts, *q = set_opts; /* see cmd_opts[] declaration */ *p++ = 'o'; @@ -326,11 +371,11 @@ parse_args(const char **argv, *q++ = 's'; for (i = 0; i < NELEM(options); i++) { - if (options[i].c) { - if (options[i].flags & OF_CMDLINE) - *p++ = options[i].c; - if (options[i].flags & OF_SET) - *q++ = options[i].c; + if ((ch = OFC(i))) { + if (OFF(i) & OF_CMDLINE) + *p++ = ch; + if (OFF(i) & OF_SET) + *q++ = ch; } } *p = '\0'; @@ -379,6 +424,17 @@ parse_args(const char **argv, break; } i = option(go.optarg); + if ((i == FPOSIX || i == FSH) && set && !fcompatseen) { + /* + * If running 'set -o posix' or + * 'set -o sh', turn off the other; + * if running 'set -o posix -o sh' + * allow both to be set though. + */ + Flag(FPOSIX) = 0; + Flag(FSH) = 0; + fcompatseen = true; + } if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i)) /* * Don't check the context if the flag @@ -387,7 +443,7 @@ parse_args(const char **argv, * if the output of "set +o" is to be used. */ ; - else if ((i != (size_t)-1) && (options[i].flags & what)) + else if ((i != (size_t)-1) && (OFF(i) & what)) change_flag((enum sh_flag)i, what, set); else { bi_errorf("%s: %s", go.optarg, "bad option"); @@ -403,7 +459,7 @@ parse_args(const char **argv, errorf("no TIOCSCTTY ioctl"); #else change_flag(FTALKING, OF_CMDLINE, true); - chvt(go.optarg); + chvt(&go); break; #endif #endif @@ -420,8 +476,8 @@ parse_args(const char **argv, break; } for (i = 0; i < NELEM(options); i++) - if (optc == options[i].c && - (what & options[i].flags)) { + if (optc == OFC(i) && + (what & OFF(i))) { change_flag((enum sh_flag)i, what, set); break; } @@ -433,8 +489,10 @@ parse_args(const char **argv, (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && argv[go.optind][1] == '\0') { /* lone - clears -v and -x flags */ - if (argv[go.optind][0] == '-') - Flag(FVERBOSE) = Flag(FXTRACE) = 0; + if (argv[go.optind][0] == '-') { + Flag(FVERBOSE) = 0; + change_xtrace(0, false); + } /* set skips lone - or + option */ go.optind++; } @@ -472,9 +530,11 @@ int getn(const char *s, int *ai) { char c; - unsigned int i = 0; + mksh_ari_u num; bool neg = false; + num.u = 0; + do { c = *s++; } while (ksh_isspace(c)); @@ -492,18 +552,20 @@ getn(const char *s, int *ai) if (!ksh_isdigit(c)) /* not numeric */ return (0); - if (i > 214748364U) + if (num.u > 214748364U) /* overflow on multiplication */ return (0); - i = i * 10U + (unsigned int)(c - '0'); - /* now: i <= 2147483649U */ + num.u = num.u * 10U + (unsigned int)(c - '0'); + /* now: num.u <= 2147483649U */ } while ((c = *s++)); - if (i > (neg ? 2147483648U : 2147483647U)) + if (num.u > (neg ? 2147483648U : 2147483647U)) /* overflow for signed 32-bit int */ return (0); - *ai = neg ? -(int)i : (int)i; + if (neg) + num.u = -num.u; + *ai = num.i; return (1); } @@ -1206,20 +1268,19 @@ print_columns(struct shf *shf, unsigned int n, /* if we can only print one column anyway, skip the goo */ if (cols < 2) { for (i = 0; i < n; ++i) - shf_fprintf(shf, "%s \n", + shf_fprintf(shf, "%s\n", (*func)(str, max_oct, i, arg)); goto out; } rows = (n + cols - 1) / cols; if (prefcol && cols > rows) { - i = rows; - rows = cols > n ? n : cols; - cols = i; + cols = rows; + rows = (n + cols - 1) / cols; } + nspace = (x_cols - max_col * cols) / cols; max_col = -max_col; - nspace = (x_cols + max_col * cols) / cols; if (nspace <= 0) nspace = 1; for (r = 0; r < rows; r++) { @@ -1911,59 +1972,69 @@ c_cd(const char **wp) #ifdef KSH_CHVT_CODE +extern uint32_t chvt_rndsetup(const void *, size_t); extern void chvt_reinit(void); static void -chvt(const char *fn) +chvt(const Getopt *go) { - char dv[20]; - struct stat sb; + const char *dv = go->optarg; + char *cp = NULL; int fd; - if (*fn == '-') { - memcpy(dv, "-/dev/null", sizeof("-/dev/null")); - fn = dv + 1; - } else { - if (stat(fn, &sb)) { - memcpy(dv, "/dev/ttyC", 9); - strlcpy(dv + 9, fn, sizeof(dv) - 9); + switch (*dv) { + case '-': + dv = "/dev/null"; + break; + case '!': + ++dv; + /* FALLTHROUGH */ + default: { + struct stat sb; + + if (stat(dv, &sb)) { + cp = shf_smprintf("/dev/ttyC%s", dv); + dv = cp; if (stat(dv, &sb)) { - strlcpy(dv + 8, fn, sizeof(dv) - 8); - if (stat(dv, &sb)) - errorf("%s: %s %s", "chvt", - "can't find tty", fn); + memmove(cp + 1, cp, /* /dev/tty */ 8); + dv = cp + 1; + if (stat(dv, &sb)) { + errorf("%s: %s: %s", "chvt", + "can't find tty", go->optarg); + } } - fn = dv; } if (!(sb.st_mode & S_IFCHR)) - errorf("%s %s %s", "chvt: not a char", "device", fn); - if ((sb.st_uid != 0) && chown(fn, 0, 0)) - warningf(false, "%s: %s %s", "chvt", "can't chown root", fn); - if (((sb.st_mode & 07777) != 0600) && chmod(fn, (mode_t)0600)) - warningf(false, "%s: %s %s", "chvt", "can't chmod 0600", fn); + errorf("%s: %s: %s", "chvt", "not a char device", dv); +#ifndef MKSH_DISABLE_REVOKE_WARNING #if HAVE_REVOKE - if (revoke(fn)) + if (revoke(dv)) #endif warningf(false, "%s: %s %s", "chvt", "new shell is potentially insecure, can't revoke", - fn); + dv); +#endif + } } - if ((fd = open(fn, O_RDWR)) < 0) { + if ((fd = open(dv, O_RDWR)) < 0) { sleep(1); - if ((fd = open(fn, O_RDWR)) < 0) - errorf("%s: %s %s", "chvt", "can't open", fn); + if ((fd = open(dv, O_RDWR)) < 0) { + errorf("%s: %s %s", "chvt", "can't open", dv); + } } - switch (fork()) { - case -1: - errorf("%s: %s %s", "chvt", "fork", "failed"); - case 0: - break; - default: - exit(0); + if (go->optarg[0] != '!') { + switch (fork()) { + case -1: + errorf("%s: %s %s", "chvt", "fork", "failed"); + case 0: + break; + default: + exit(0); + } } if (setsid() == -1) errorf("%s: %s %s", "chvt", "setsid", "failed"); - if (fn != dv + 1) { + if (go->optarg[0] != '-') { if (ioctl(fd, TIOCSCTTY, NULL) == -1) errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed"); if (tcflush(fd, TCIOFLUSH)) @@ -1974,14 +2045,7 @@ chvt(const char *fn) ksh_dup2(fd, 2, false); if (fd > 2) close(fd); - { - register uint32_t h; - - NZATInit(h); - NZATUpdateMem(h, &rndsetupstate, sizeof(rndsetupstate)); - NZAATFinish(h); - rndset((long)h); - } + rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt))); chvt_reinit(); } #endif diff --git a/src/mksh.1 b/src/mksh.1 index 51800fc..621aa97 100644 --- a/src/mksh.1 +++ b/src/mksh.1 @@ -1,5 +1,5 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.305 2013/02/19 18:45:20 tg Exp $ -.\" $OpenBSD: ksh.1,v 1.145 2013/01/17 21:20:25 jmc Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.320 2013/08/10 14:11:39 tg Exp $ +.\" $OpenBSD: ksh.1,v 1.147 2013/06/13 19:43:09 millert Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" 2010, 2011, 2012, 2013 @@ -74,7 +74,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: February 19 2013 $ +.Dd $Mdocdate: August 10 2013 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -157,7 +157,11 @@ .Nm .Bk -words .Op Fl +abCefhiklmnprUuvXx -.Op Fl T Ar /dev/ttyCn \*(Ba \- +.Oo +.Fl T Oo Ar \&! Oc Ns Ar tty +\*(Ba +.Ar \&\- +.Oc .Op Fl +o Ar option .Oo .Fl c Ar string \*(Ba @@ -295,16 +299,28 @@ Redirections that create files can't be used (i.e.\& .It Fl s The shell reads commands from standard input; all non-option arguments are positional parameters. -.It Fl T Ar tty +.It Fl T Ar name Spawn .Nm on the .Xr tty 4 device given. -Superuser only. +The paths +.Ar name , +.Pa /dev/ttyC Ns Ar name +and +.Pa /dev/tty Ns Ar name +are attempted in order. +Unless +.Ar name +begins with an exclamation mark +.Pq Sq \&! , +this is done in a subshell and returns immediately. If -.Ar tty -is a dash, detach from controlling terminal (daemonise) instead. +.Ar name +is a dash +.Pq Sq \&\- , +detach from controlling terminal (daemonise) instead. .El .Pp In addition to the above, the options described in the @@ -514,7 +530,9 @@ token to form pipelines, in which the standard output of each command but the last is piped (see .Xr pipe 2 ) to the standard input of the following command. -The exit status of a pipeline is that of its last command. +The exit status of a pipeline is that of its last command, unless the +.Ic pipefail +option is set (see there). All commands of a pipeline are executed in separate subshells; this is allowed by POSIX but differs from both variants of .At @@ -1180,6 +1198,15 @@ work, and in that .Ic exit terminates the parent shell. .Pp +Another variant of substitution are the valsubs (value substitutions) +.Pf ${\*(Ba\& Ns Ar command Ns \&;} +which are also executed in the current environment, like funsubs, but +share their I/O with the parent; instead, they evaluate to whatever +the, initially empty, expression-local variable +.Ev REPLY +is set to within the +.Ar command Ns No s . +.Pp If a substitution appears outside of double quotes, the results of the substitution are generally subject to word or field splitting according to the current value of the @@ -2037,6 +2064,9 @@ Parameter, command, and arithmetic substitutions are performed before it is printed. The default is .Sq +\ \& . +You may want to set it to +.Sq \&[$EPOCHREALTIME]\ \& +instead, to include timestamps. .It Ev PWD The current working directory. May be unset or @@ -2431,6 +2461,13 @@ in .Nm but a syntax error in GNU .Nm bash . +Setting the +.Fl o Ar posix +or +.Fl o Ar sh +shell options disable parsing of this redirection; +it's a compatibility feature to legacy scripts, to +not be used when writing new shell code. .It Xo .No &\*(Gt\*(Ba Ar file , .No &\*(Gt\*(Gt Ar file , @@ -2515,15 +2552,15 @@ Unary operators: Binary operators: .Bd -literal -offset indent , -= *= /= %= += \-= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba= += += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba= \*(Ba\*(Ba && \*(Ba \*(ha & == != -\*(Lt \*(Lt= \*(Gt= \*(Gt -\*(Lt\*(Lt \*(Gt\*(Gt +\*(Lt \*(Lt= \*(Gt \*(Gt= +\*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt \*(Lt\*(Lt \*(Gt\*(Gt + \- * / % .Ed @@ -2553,9 +2590,14 @@ Additionally, base-16 integers may be specified by prefixing them with in all forms of arithmetic expressions, except as numeric arguments to the .Ic test built-in command. -It is discouraged to prefix numbers with a sole zero -.Pq Sq 0 , -because some shells may interpret them as base-8 integers. +Prefixing numbers with a sole digit zero +.Pq Sq 0 +leads to the shell interpreting it as base-8 integer in +.Ic posix +mode +.Em only ; +historically, (pd)ksh has never done so either anyway, +and it's unsafe to do that, but POSIX demands it nowadays. As a special .Nm mksh extension, numbers to the base of one are treated as either (8-bit @@ -2609,8 +2651,8 @@ The result is the value of the expression on the right-hand side. .It = Assignment; the variable on the left is set to the value on the right. .It Xo -.No *= /= += \-= \*(Lt\*(Lt= -.No \*(Gt\*(Gt= &= \*(ha= \*(Ba= +.No += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= +.No \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba= .Xc Assignment operators. .Sm off @@ -2656,10 +2698,15 @@ Not equal; the result is 0 if both arguments are equal, 1 if not. .It \*(Lt Less than; the result is 1 if the left argument is less than the right, 0 if not. -.It \*(Lt= \*(Gt= \*(Gt +.It \*(Lt= \*(Gt \*(Gt= Less than or equal, greater than or equal, greater than. See .Ic \*(Lt . +.It \*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt +Rotate left (right); the result is similar to shift (see +.Ic \*(Lt\*(Lt ) +except that the bits shifted out at one end are shifted in +at the other end, instead of zero or sign bits. .It \*(Lt\*(Lt \*(Gt\*(Gt Shift left (right); the result is the left argument with its bits shifted left (right) by the amount given in the right argument. @@ -2668,7 +2715,6 @@ Addition, subtraction, multiplication, and division. .It % Remainder; the result is the remainder of the division of the left argument by the right. -The sign of the result is unspecified if either argument is negative. .It Xo .Sm off .Aq Ar arg1 ? @@ -2682,6 +2728,7 @@ is non-zero, the result is .Aq Ar arg2 ; otherwise the result is .Aq Ar arg3 . +The non-result argument is not evaluated. .El .Ss Co-processes A co-process (which is a pipeline created with the @@ -2753,8 +2800,7 @@ However, unlike shell arguments (i.e. positional parameters $1, $2, etc.)\& are never visible inside them. When the shell is determining the location of a command, functions -are searched after special built-in commands, before regular and -non-regular built-ins, and before the +are searched after special built-in commands, before builtins and the .Ev PATH is searched. .Pp @@ -2868,8 +2914,8 @@ returns. .El .Ss Command execution After evaluation of command-line arguments, redirections, and parameter -assignments, the type of command is determined: a special built-in, a -function, a regular built-in, or the name of a file to execute found using the +assignments, the type of command is determined: a special built-in command, +a function, a normal builtin, or the name of a file to execute found using the .Ev PATH parameter. The checks are made in the above order. @@ -2885,46 +2931,30 @@ parameter is not used to find them. The original .Nm ksh and POSIX differ somewhat in which commands are considered -special or regular: +special or regular. .Pp -POSIX special commands +POSIX special built-in utilities: .Pp .Ic \&. , \&: , break , continue , .Ic eval , exec , exit , export , .Ic readonly , return , set , shift , -.Ic trap , unset , wait +.Ic times , trap , unset .Pp Additional .Nm -special commands -.Pp -.Ic builtin , global , times , typeset -.Pp -Very special commands -.Pq non-POSIX +commands keeping assignments: .Pp -.Ic alias , readonly , set , typeset +.Ic builtin , global , typeset , wait .Pp -POSIX regular commands +Builtins that are not special: .Pp -.Ic alias , bg , cd , command , +.Ic [ , alias , bg , bind , +.Ic cat , cd , command , echo , .Ic false , fc , fg , getopts , -.Ic jobs , kill , read , true , -.Ic umask , unalias -.Pp -Additional -.Nm -regular commands -.Pp -.Ic \&[ , chdir , bind , cat , -.Ic echo , let , mknod , print , -.Ic pwd , realpath , rename , sleep , -.Ic test , ulimit , whence -.Pp -In the future, the additional -.Nm -special and regular commands may be treated -differently from the POSIX special and regular commands. +.Ic jobs , kill , let , mknod , +.Ic print , pwd , read , realpath , +.Ic rename , sleep , test , true , +.Ic ulimit , umask , unalias , whence .Pp Once the type of command has been determined, any command-line parameter assignments are performed and exported for the duration of the command. @@ -2952,6 +2982,10 @@ those of the environment the command is used in. The null command. Exit status is set to zero. .Pp +.It Ic \&[ Ar expression Ic \&] +See +.Ic test . +.Pp .It Xo Ic alias .Oo Fl d \*(Ba t Oo Fl r Oc \*(Ba .Cm +\-x Oc @@ -3506,6 +3540,10 @@ resetting .Ev OPTIND , may lead to unexpected results. .Pp +.It global Ar ... +See +.Ic typeset . +.Pp .It Xo .Ic hash .Op Fl r @@ -3585,6 +3623,10 @@ Since expressions may need to be quoted, is syntactic sugar for .No let \&" Ns Ar expr Ns \&" . .Pp +.It let] +Internally used alias for +.Ic let . +.Pp .It Xo .Ic mknod .Op Fl m Ar mode @@ -4004,11 +4046,14 @@ explicitly tested by a shell construct such as .Ic if , .Ic until , .Ic while , -.Ic && , -.Ic \*(Ba\*(Ba , or .Ic !\& statements. +For +.Ic && +or +.Ic \*(Ba\*(Ba , +only the status of the last command is tested. .It Fl f \*(Ba Fl o Ic noglob Do not expand file name patterns. .It Fl h \*(Ba Fl o Ic trackall @@ -4097,7 +4142,7 @@ Mark directories with a trailing .Ql / during file name generation. .It Fl x \*(Ba Fl o Ic xtrace -Print commands and parameter assignments when they are executed, preceded by +Print command trees when they are executed, preceded by the value of .Ev PS4 . .It Fl o Ic bgnice @@ -4120,6 +4165,11 @@ must be used. To avoid infinite loops, the shell will exit if .Dv EOF is read 13 times in a row. +.It Fl o Ic inherit\-xtrace +Do not reset +.Fl o Ic xtrace +upon entering functions. +This is enabled by default. .It Fl o Ic nohup Do not kill running jobs with a .Dv SIGHUP @@ -4162,6 +4212,9 @@ See the and .Ic pwd commands above for more details. +.It Fl o Ic pipefail +Make the exit status of a pipeline (before logically complementing) the +rightmost non-zero errorlevel, or zero if all commands exited with zero. .It Fl o Ic posix Enable a somewhat more .Px @@ -6020,6 +6073,11 @@ Search for the .Ar n Ns th occurrence of the last search string; the direction of the search is the opposite of the last search. +.It Ar ANSI-CurUp +Take the characters from the beginning of the line to the current +cursor position as search string and do a backwards history search +for lines beginning with this string; keep the cursor position. +This works only in insert mode and keeps it enabled. .El .Pp Edit commands @@ -6309,21 +6367,53 @@ all contributors, such as the Debian and OpenBSD projects. .\" Open Source licence. .\" See the documentation, CVS, and web site for details. +.Pp +The BSD daemon is Copyright \(co Marshall Kirk McKusick. +The complete legalese is at: +.Pa https://www.mirbsd.org/TaC\-mksh.txt +.\" +.\" This boils down to: feel free to use mksh.ico as application icon +.\" or shortcut for mksh or mksh/Win32; distro patches are ok (but we +.\" request they amend $KSH_VERSION when modifying mksh). Authors are +.\" Marshall Kirk McKusick (UCB), Rick Collette (ekkoBSD), Thorsten +.\" Glaser, Benny Siegert (MirBSD), Michael Langguth (mksh/Win32). +.\" +.\" As far as MirBSD is concerned, the files themselves are free +.\" to modification and distribution under BSD/MirOS Licence, the +.\" restriction on use stems only from trademark law's requirement +.\" to protect it or lose it, which McKusick almost did. +.\" .Sh CAVEATS .Nm only supports the Unicode BMP (Basic Multilingual Plane). -It has a different scope model from +.Pp +.Nm +has a different scope model from .At .Nm ksh , which leads to subtile differences in semantics for identical builtins. +This can cause issues with a +.Ic nameref +to suddenly point to a local variable by accident; fixing this is hard. .Pp The parts of a pipeline, like below, are executed in subshells. -Thus, variable assignments inside them fail. +Thus, variable assignments inside them are not visible in the +surrounding execution environment. Use co-processes instead. .Bd -literal -offset indent foo \*(Ba bar \*(Ba read baz # will not change $baz foo \*(Ba bar \*(Ba& read \-p baz # will, however, do so .Ed +.Pp +.Nm mksh +provides a consistent set of 32-bit integer arithmetics, both signed +and unsigned, with defined wraparound and sign of the result of a modulo +operation, even (defying POSIX) on 64-bit systems. +If you require 64-bit integer arithmetics, use +.Nm lksh Pq legacy mksh +instead, but be aware that, in POSIX, it's legal for the OS to make +.Li print $((2147483647 + 1)) +delete all files on your system, as it's Undefined Behaviour. .Sh BUGS Suspending (using \*(haZ) pipelines like the one below will only suspend the currently running part of the pipeline; in this example, @@ -6335,7 +6425,7 @@ $ /bin/sleep 666 && echo fubar .Ed .Pp This document attempts to describe -.Nm mksh\ R43 +.Nm mksh\ R48 and up, compiled without any options impacting functionality, such as .Dv MKSH_SMALL , diff --git a/src/mksh.ico b/src/mksh.ico new file mode 100644 index 0000000000000000000000000000000000000000..731c2b6dbd16d9d01f3167cc66b76e38a7103db7 GIT binary patch literal 14166 zcmeHtcU)A*7w?I}1_ELUf}+6gvJF^zS;{Vb=}n~DQ0WMYbVRVwtBO*kD@9Qe8+L3V zD#lo&v3Eg(1siJEckWUI<1fj3zxV!m@14)?-kCe6&D^;&-*aXGz5^0Wm@omaBEb3% zAP@lSfORnl76r)1dt~xJ`_nN1??^;_puJHH;I=q`rsiN>3Lt$FfSK7qoh1XHP61#M zI`NBQs8q>Qf2~;MgxK1=N%8S`vrpjwj<{on_m#uU(V!Q6bQPix;F z6W529M%$E`o-Zow+?3cU&@O(U-I1&#=sjpEFe*gH|4SM@PB^%KL&P}Il<1&Dp#gas z1t`<0K%>&Iu7vmTsz3!LMZ8B=0;TB#<4GeEJVd(^q@0Za$2=2AxfuqD7egTHw|H23 zFc89;=RwJ#b+GNlCh+NShVUDHuyE%rSaBsD<{a{XfVO!sce4+)KRyhnU;YF)-d_jP zNG^mQ34u8^KH!z_4jnH}fp?t;c-6Q=(Jvcd$J6al`)du%Jv0XbcLsoGktfW^o&na; z){xMg0EfE|!?9<_fT7F)Dvb&hCG?k01DhkZU|h(BnVUVJ;aVd&?{ov*B^+=`ngPMJ zp^(s;1i^)&;GY)=))!nsH{A$qce;X+7Z)<>vcS4+2AEdcfPNGg+%|Yae9LmMsdEF5 zHM7BGg$KCA%mkwn8?dN$0h0u4Fpaf=h3i7VX|WqYeK-ljR!n8b8K@4Zl(k1%;$ots}5K!asW@9t9n)jpc%*ozpN18&$0)d zR2$G0KD0JU}`9XX0#=!g_?jypb2n7`Jm=*fb&}uSVn4~Y@h}VT@_GL zXMl;51!$q4YCcB5M*o-=8lY^Y1$v&Qpu|xIU2_9qyKsPRt`2lFb}u}rK3a&mIe(a~X;mXplUHz>|N*!mMz#cDDm(_1#}3}2L*yGx9y zXN81TZ>mdLI)By1pON8INw9ZSo3HacKQFskSRc1_hFe?90t=oRTUQUOwXQk^n>O!Q zW<^zEDRhjhw3c6ZWZSlF2lks&rpk>kjd7qSHm_N;W}mH75Y6~RVVtU2eI8PimS=;= z@srD^Sngh5k-uU67EO=#2_(@;lb0~aK6@GyjxxE2$9I?-naF6kt1GK0YEQRc{X@sh z6%jwmmPH55oha>+5h zZ^y4J{{b&QWGo(s53b{Hmc@V7M69!9{F>qS-|R<96e^JA6(Hc$e6T5Uf^9cjfD^|D zwl%(k_sjM#B^@1YT&@LzgFWH-PygJKbL-ZA zs`d_npfa%MS*OV#H`;P;-?~;M5EutWJUeL8t)tCdSJI;)5cKBtAUzPguDEskDb@u# zO^16TUTF089NbSO6bh(1pCZl+dY=s%z&pL^s|CHcHVRvbpO4#7K0kZ7{<-+YmL2(J zTZV2l=iTW%QyE{1?g*&~>z!MBvd$0IKNIbp=ep{&sX~(<%e$Jl_QXBA)s|T*Wa*sM z+|YCD@q8J6oUq=})qFZOFfh>HYZ+1R^E}Ym+4xvS#%oy?QBMnJ<6f&qu(x6LL$qxt zQiHWW-;1ahd)N{6)Py}AMEmD`p0iJvmX<|ly)Gx@KVBdg?0;~uGujy84Rt}{(+3qO=^=Nl1FDol62P+?9I0Y=5J=U{g zJlw5p;)(j}Q-?CFd~yzYS}h}d5ePD=XZh=zJELv(4~*Aw0nG@yL=z0FnctVN55Aui?5HpY!$U+=JQ&a+~0!4@=hH!); zz6)e|c~HbJ6~Q1`K>?=WnoHmcim)z^Ycdt6R0?86MX0^I0%F@1K<3pbNH`Y^yPq{e z;BW45^3f59y%q$)Tjqhs347>#^D|f#^C0|mAmpCUfXch2u=Qy@+<12#g7z|oYjA8^l|2{kvWfLqB0&q7ZK-Wm+y72!~Fz67W$RA8tu zV7kI|VBuauXVAgB-4|@mI0C=k4kC6(!t(tqpy}pT&@be{>b4B%>3;#-az5xK7=t6? z58Hef;GdZZ#>tjozsv;;e7P_y+Yfl{E})lf4n}MFV7tK$=9L72vI!d^8{=_bWP4DV}M^H7_fY6eqU{K-!>M=afiZ=s|MO@Gf z;(~#n85p?maIdAq{PYlzrz(K%aw{-I?8LBB2e!Qq@WY*f&DRF4Ok2>)aRA)}K4{HC zjBKY3&Z}qPp3ef^m54tQ5Pw9XFJTs-WXT2<8*NZFR)?ikDWIya4q8bzh*!)&&CLK9 zR){UEwLt~-(z$F>(s3IOwbK-!iogOHt;hsH& zHxL8Raqni}-mNl^2O9nsz;M(7HBVzubw;d$7)C)4@r0>5>Qxuw5Q>osuq?HKfp(bM zh*c1aFcBM&by;BGX$8^6t3cPpK!`z9W|;_a2!TQ999)alSVAnaG%E?FFcpPsGEU6%LsD$AkwC zVL`w!HU!^g@sLni4hEJMpy}-a7Bk&I)y5jJr7x^+YQ*_81D0$ohk%@H@J~yHb#42= zF(?Rh=lMZQX&Hp&LUh28~^!?qJ9Ws+A$dqERh0#^-=g+;*-u@aABK7v; zo$D`eUwVG)?%VF}yFb-VBHE>T-hF=e=-SU)&+NVOs${~r3FF6Y*);jwPd9%%Dr^@> zJ?Vb-`_-#AA6+|teiBV}8965ZC?zF0eff7{;U>v;ZVi(al^O7!kRPB)6EEF(B8p|n{h^tjU$>%M$@=C! zMqDm;a>t6r+%bP;@T^(V+-_)Scyil{%IO zeqkypN{jPt--&cAc>apiqGI9beQED+NQ((N-?TnVR7_k%L}Hkuv_8fo z=S1W2U=J&YaE~BgU#GdTSt)hH?fs2gW^UcybZm*axwEr5hsx(OG_Adb`!K;n>fhpM zp5xiHbAuy?%4V}I=-Os(4go%c@}&M6J8h@qO-)TZk45UUS!@Q~4|gMaMg-B3al@5~RCQ=vluU8K4LAlsVJoHPkH^6=V?YBvMYarKxK6 zzBVBp_A?7#?xUnWW6`qJsYLsjhJt|E4BeIu19ZrdyfJso72Ph#S5RS5l!PMyy^;%k) z`|677_O-QW(Khyvc)${4l;o$SswmRPPT8%IL5mM*DK6v=Oin^OX{?Q!HBVD+_EH{A zS(&Cto-@sUL6Hd2B&4x(?PrxQP@JM$mKWt_yFhu`G|i}})ZuoChN!@%wfP!~cCBkG z*6gsGBImWLpkl4iK|+2~;W`nChSer6S>mTWi+6d;n=Gv;GTc5qJ^G6!NCNQy`(@vg zNRuZ05bI)})F_xV!hQ6VB(W@EyKt&VoJ#asBR=5RmtRuJ+J-@gBpQ~j9rg`5{wt92 zzd@M5DVRb6Kl~DTr@te@T_p;}d^tpWel~1=R1U?R`7rOYD+HeN zf`|+AAo)rp#GP6ME}a(O^{WF!{NxYoZf1k)aa(X}a)8{+8L;h1BbeyJa?n9t;Nlc|I&Zv>ZyV6$4p~4AZF7ggAxHq6_mN4BQI{ ztYUT68n|U9V3flJuQnfWX>f&z)+La6G7Iw06hPtG^IT*0#39!&E2;9Tqh(JhIH(oIuN;3yS(QFr06W_(ThI3+=&Rog?VyAr^_T0QLDspci2QDo)zKcGCw9PeV|# z(1f6(C{WGd17{uPbg)kZZsEk)0n=RvG<=PLX|IKO4t-ETe8Tm%!n}qm?(>*;!2O+t zISM+P32I4v&|GAJc*7JJF4~AwctGYUV;^jR!AERksU^fUDprVb%(cLC*?h!C)}R@T z`5AXZP z?qPEfuV6j~v5AVcK4K)qMLKL@-i2YU3p9*DwOkOlnIoRUHag}~hI1}Bhm`cyAfX@) z=uXB!Lo7r^Jf)(g4t#%ah^oxP+zT5#mj=Q7716*p)J0pWpyF){3H8OG5#kN=5KB2M zUkt&;*`T7Q4gPD=5Oa8fL&!qVvowR&^QWQx`elrLmw>6eE2LGIgSP){e3mBQl@tq^ zJGMY{RT&uAS_4Z{1H#IS!F5SEsCyuOv9|?vt}zrI*pGVG!0c74fTfOOgt;8!uy9DO zYZ%5U?brLI-@e^<@8!#`2VF1Q#h>33`?9`$qxA9K+ilPLKJ@kW_WtqbpRc5zzm*V> z#K(;vKgoE~B&qS^1{YA0^q}mU=Wjo}{_wtU@(fyzTm<@9Dz_FCRU6+4ZpRj|Z0@ zJd!!N%V^M$#Kk{fym)fw+3P$m$iAK$(G{^tSXlE3w&UAnYYT5|k=yim!PAClO3 zNuvjkAMf7PFrZEHw;GNS>HDu#e-Xfv_z%~goS!hrF}~||Wy{fXqZq!fNKZcU*%53!88N@rBZuD#M?gm4i_rnsg#vtepe)4AbIeH6FgQ+-HPzHj25_68ws4oX~8 zxvP5DiT2TK!?phGx~lx(;>{-qsC{-~;=;1j>JUTTFw3Zdbi?k#`~}1OC+l|wB_^(_ zt~*-)X%yd3MLN5#u-rFzUxQG8Lw(b^eS2zx*X;_a=Y6{e=Q67+=DTmpM*jULvlqw5 zm6a8^#Z@&Q-u%5Nl9-s-@U#}%__QUYI(Al2+kT|BGp_lLGf|1v8jdC zn_7l^8MvO?U)MT6xa|N^+k)Ugx9Bzbc5c?H8XBtlIu`a0QKbc=`FYtbmA(sv{MqsD zmR3$qYI<7wd@J?u)RlEure>j0qxeZiA?tkgR@W2y4`f&R>Z?xIS5;Bf*J9~t`=`V# z3M^YalAm;L_XJR&=X{f8Js!y>&UrI_>uErCjAtD)7<(W=%3O{-F>>1&QkUH$kP}fjbnZnkz56vxz zORXN_7Lbgp^Guo3d=Bp=l4nEI7G$ois!C=vZBU>zzTyji|E@fHO-;FoJ!gh^@p|Tv z-HiO1+KNm~dwcu2$)#mQL;9uDSNhw}wWmbv+53e;_RQCbuK4Lttb?Yhe=g>^R}PI2 zsbO_Va&8zyYvYcVVX6&Tuj5_5VN-+Hn7Y8^Y-#rmnWO?h76Aq35QC488Isagy{{{Y*&_-ue8;kS+`@JoJnNS=y!^6XKb8_&C;c8 zEthFgrAz0SlgV^CSy7R?E}G8v^jgXL+Q8V|Ha5AX$#cl$DYkJX$;+3_@$#Cp%v^3R zoxNmfWXajD42U<)^^8wlof~O0Ws26RvVzjw+>+!H7db^a_LAh1+}x_s{LWZq-4w35eipG*iM=MHgbZrifm2PUt%xr0i z#w4UyazRDb{`&UOi;wi~I)nfu-u_%?k0=reqfG3d$Rt2lKqMkUVW zi8FaBo+B~ncxH)lDpipT4C369jQK~*pVO##Hjnvp2IkGD%T2}eBP?S~z{0#~*}WAI z{i_$O`8^SmuZDu(d3)IWv;<<#27~JnK15tx09W5!0=v^ZaP6>#;FErEyypnm?6d&i z(=)+;|2#-M69apmw8Hbx&%k#l>N=kfb$4pO_OcD|JNaO;nFrc_+OYIkB!stxL;2Nm z%(-G*ql&p(%$<{|WKg7F%#3HVL|p3J<_fl(?7*|m3+8Q`4=dVNf zt*`*&b!OmD;0j)av%s{~4mkOyV3leQ7H4OG`4MN(PcQ+KXe%&oum`G zJf^!g82RJ5o}(@>F-BuxEUTa61bVA5UQMzEEzF&3g=6j&V@fp-1Hg?H^W^H7Q>KG& zioY-pC1OxDT@BE|{5;D`A7d^pP{&+6+X>@UI~~lq;}>)43?82CV?4>ibN8&B8-#JE z#yr&PZ43;1ZOp-ATkMZbGEJ`+21s2A$nqDTrw$%rxWpjYRVS}O?O?ZAebpFrwFoxM#;lj9-_;1Zn z-d({8<4w%X+lP4p4dYlA2hSahwSl6FXYLr668{AtVpAoaIxKH05XPl!j5U?eH!7Y3 z=vWwGjEmAJHb)N}<0HX4B>|Mod7x@*g|WFdENiHN6pT&P+?-)yb~?@te7@>xu(Wz3 zlpQ{R{SSdX7k>q#h((auvK;~o3qfPfTrl(T#TXUO6bAo)Fu1Nu4X^M2ONF|+(K`(1 ze?m*Afs~;C{%cH$3k0Iy_e)_x^zg24#%ABpTE`JN{qs1H(=YE&9H+0ZpLFhkzP>>8 z%!BX$Wsf>L&WwL>e^Ti4PtP8_>+0%$_3BmkAEdgMuZMPAQBeUA6MVz8@Rro!=TB>2 zK0p5QV;`OzeE$6KUEhn|aT7m1mk@|biHS=}>YY71QBO}_QheAU(NVZS*sp8xF!m)>=CcXxMny?gk;Uz{{<91gQo?V(Z5NZkH# zw*@w{AV@dwZ|%{Uynv z;kokm#igrPPhPK;l>DagVMS6TaY_BKOSj%X-g~vXVaR{UJ3X8HCB#W2k#F>TSazr& zMUwR2`}qBVA?tX8bm`WHoi&~R{+$$=u$TF0!mu&%legAJZd|H6WZ|E$qWZb-tA@KO z>i=rTveL@hf0i5M=&w0Y|Nf3-Kc}A{4u^E*XhBBvsJG^Cbs3y0-*oG!n}9P&6Zant z-Vpk?A|o67Lz^-a8*?K$y0x{!&%~2Q7dO?^3LW?tUT|9XqzBD8TFTKGq_bC!ZjNkj ztqH9;T=zG~deDc?Lt*9Vt6b(L>C_C;p;Fi2^+n~SrIF1w|7^dreOqbzs=3SEYT5^> z=D^;gjf*!IW|oK5ef92-?Dt^VptWhkX!-Vlow1nW87n3t^38R^%Pg<{YE|M?Zud1;QzM&}RgIRf zt~zI>xnQ-sC3(s;b894@n7I73?3AziU%hUng@v^>S#y2WsZms2HJk3SE`961Wi!py z%za|xZ)x)=N(s?4UX@l{-YOWI5@hca9aoT1oS9J^ z88%YBGdndeK7S@}s>zY+jiae*0h-UquGZ}r6&q7qjC|XRQx4VC93COxU)`{FUEwTc ziqW!N$)m-a&)LYutt-tLSpG!B1YyMunb|3w1Lx&~I_g?V%bT3FqjPgql zedBc*p=6BuWLX)9rQ3pxWo3;Mt1E;F&9=&IZIx}OX30zoFaG9gHLPHa=d>vbvX++< zX|jC2EZ6zsFZhq)Q_XuWY;9?2o+YCguvX`5r*kN;3zMr3?tfl!>ll*W$N5kpU@z2q6veT!_I$!jV zRpRq~ivM%}rN-K;^21lg1SrWVN*iy^i;0d3a3oGM92XhN%tUd=fB?UO!6T!QZV#5l z672Z#1*vgS{OQt@-k;RTv zrYP{E;!@L_s#&GrsY&8+yl z$)j5{q$}Oq)-mA=Mi!Mwm}krOj?d8kw&g*wp%+P1EO6euyqYia0= 0x08A8) && !defined(MKSH_OPTSTATIC) +#if defined(MirBSD) && (MirBSD >= 0x0AB3) && !defined(MKSH_OPTSTATIC) #define MKSH_mirbsd_wcwidth #define utf_wcwidth(i) wcwidth((__WCHAR_TYPE__)i) extern int wcwidth(__WCHAR_TYPE__); @@ -443,8 +444,6 @@ extern int wcwidth(__WCHAR_TYPE__); #define MAGIC (7) /* prefix for *?[!{,} during expand */ #define ISMAGIC(c) ((unsigned char)(c) == MAGIC) -#define LINE 4096 /* input line size */ - EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */ #ifdef MKSH_LEGACY_MODE @@ -471,6 +470,14 @@ union mksh_ccphack { const char **ro; }; +/* + * Evil hack since casting uint to sint is implementation-defined + */ +typedef union { + mksh_ari_t i; + mksh_uari_t u; +} mksh_ari_u; + /* for const debugging */ #if defined(DEBUG) && defined(__GNUC__) && !defined(__ICC) && \ !defined(__INTEL_COMPILER) && !defined(__SUNPRO_C) @@ -511,8 +518,9 @@ char *ucstrstr(char *, const char *); #define mkssert(e) do { } while (/* CONSTCOND */ 0) #endif -#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 431) +#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 481) #error Must run Build.sh to compile this. +extern void thiswillneverbedefinedIhope(void); int im_sorry_dave(void) { @@ -763,18 +771,11 @@ EXTERN struct { #define OF_FIRSTTIME 0x10 /* as early as possible, once */ #define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL) -struct shoption { - const char *name; /* long name of option */ - char c; /* character flag (if any) */ - unsigned char flags; /* OF_* */ -}; -extern const struct shoption options[]; - /* null value for variable; comparison pointer for unset */ EXTERN char null[] E_INIT(""); /* helpers for string pooling */ EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented"); -EXTERN const char Toomem[] E_INIT("can't allocate %lu data bytes"); +EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes"); #if defined(__GNUC__) /* trust this to have string pooling; -Wformat bitches otherwise */ #define Tsynerr "syntax error" @@ -788,10 +789,8 @@ EXTERN const char Tr_fc_e_dash[] E_INIT("r=fc -e -"); EXTERN const char Tlocal_typeset[] E_INIT("local=typeset"); #define T_typeset (Tlocal_typeset + 5) /* "=typeset" */ #define Ttypeset (Tlocal_typeset + 6) /* "typeset" */ -EXTERN const char Tpalias[] E_INIT("+alias"); -#define Talias (Tpalias + 1) /* "alias" */ -EXTERN const char Tpunalias[] E_INIT("+unalias"); -#define Tunalias (Tpunalias + 1) /* "unalias" */ +EXTERN const char Talias[] E_INIT("alias"); +EXTERN const char Tunalias[] E_INIT("unalias"); EXTERN const char Tsgset[] E_INIT("*=set"); #define Tset (Tsgset + 2) /* "set" */ EXTERN const char Tsgunset[] E_INIT("*=unset"); @@ -830,7 +829,7 @@ struct temp { * stdio and our IO routines */ -#define shl_spare (&shf_iob[0]) /* for c_read()/c_print() */ +#define shl_xtrace (&shf_iob[0]) /* for set -x */ #define shl_stdout (&shf_iob[1]) #define shl_out (&shf_iob[2]) #ifdef DF @@ -984,6 +983,8 @@ EXTERN uint32_t builtin_flag; /* current working directory */ EXTERN char *current_wd; +/* input line size */ +#define LINE (4096 - ALLOC_SIZE) /* * Minimum required space to work with on a line - if the prompt leaves * less space than this on a line, the prompt is truncated. @@ -1151,7 +1152,6 @@ EXTERN struct tbl vtemp; #define FDELETE BIT(10) /* function deleted while it was executing */ #define FKSH BIT(11) /* function defined with function x (vs x()) */ #define SPEC_BI BIT(12) /* a POSIX special builtin */ -#define REG_BI BIT(13) /* a POSIX regular builtin */ /* * 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 @@ -1180,12 +1180,11 @@ EXTERN enum { /* Flags for findcom()/comexec() */ #define FC_SPECBI BIT(0) /* special builtin */ -#define FC_FUNC BIT(1) /* function builtin */ -#define FC_REGBI BIT(2) /* regular builtin */ -#define FC_UNREGBI BIT(3) /* un-regular builtin (!special,!regular) */ -#define FC_BI (FC_SPECBI|FC_REGBI|FC_UNREGBI) -#define FC_PATH BIT(4) /* do path search */ -#define FC_DEFPATH BIT(5) /* use default path in path search */ +#define FC_FUNC BIT(1) /* function */ +#define FC_NORMBI BIT(2) /* not special builtin */ +#define FC_BI (FC_SPECBI | FC_NORMBI) +#define FC_PATH BIT(3) /* do path search */ +#define FC_DEFPATH BIT(4) /* use default path in path search */ #define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */ @@ -1254,10 +1253,6 @@ EXTERN const char *prompt; EXTERN int cur_prompt; /* PS1 or PS2 */ EXTERN int current_lineno; /* LINENO value */ -#define NOBLOCK ((struct op *)NULL) -#define NOWORD ((char *)NULL) -#define NOWORDS ((char **)NULL) - /* * Description of a command or an operation on commands. */ @@ -1326,6 +1321,7 @@ struct op { #define CPAT 11 /* close pattern: ) */ #define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */ #define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */ +#define VALSUB 15 /* ${|foo;} substitution (NUL terminated) */ /* * IO redirection @@ -1680,7 +1676,7 @@ void x_init(void); #ifdef DEBUG_LEAKS void x_done(void); #endif -int x_read(char *, size_t); +int x_read(char *); #endif void x_mkraw(int, mksh_ttyst *, bool); /* eval.c */ @@ -1834,8 +1830,7 @@ void yyerror(const char *, ...) MKSH_A_FORMAT(__printf__, 1, 2); Source *pushs(int, Area *); void set_prompt(int, Source *); -void pprompt(const char *, int); -int promptlen(const char *); +int pprompt(const char *, int); /* main.c */ int include(const char *, int, const char **, bool); int command(const char *, int); @@ -1903,6 +1898,7 @@ void initctypes(void); size_t option(const char *); char *getoptions(void); void change_flag(enum sh_flag, int, bool); +void change_xtrace(unsigned char, bool); int parse_args(const char **, int, bool *); int getn(const char *, int *); int gmatchx(const char *, const char *, bool); @@ -2011,7 +2007,7 @@ char *arrayname(const char *); mksh_uari_t set_array(const char *, bool, const char **); uint32_t hash(const void *); mksh_ari_t rndget(void); -void rndset(long); +void rndset(unsigned long); enum Test_op { /* non-operator */ diff --git a/src/sh_flags.h b/src/sh_flags.h index 1c8a30e..3e4cf56 100644 --- a/src/sh_flags.h +++ b/src/sh_flags.h @@ -1,11 +1,22 @@ #if defined(SHFLAGS_DEFNS) -__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.12 2012/06/28 20:14:17 tg Exp $"); -#define FN(sname,cname,ochar,flags) /* nothing */ +__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.16 2013/08/11 14:57:11 tg Exp $"); +#define FN(sname,cname,ochar,flags) \ + static const struct { \ + /* character flag (if any) */ \ + char c; \ + /* OF_* */ \ + unsigned char optflags; \ + /* long name of option */ \ + char name[sizeof(sname)]; \ + } shoptione_ ## cname = { \ + ochar, flags, sname \ + }; #elif defined(SHFLAGS_ENUMS) #define FN(sname,cname,ochar,flags) cname, #define F0(sname,cname,ochar,flags) cname = 0, #elif defined(SHFLAGS_ITEMS) -#define FN(sname,cname,ochar,flags) { sname, ochar, flags }, +#define FN(sname,cname,ochar,flags) \ + ((const char *)(&shoptione_ ## cname)) + 2, #endif #ifndef F0 @@ -45,6 +56,9 @@ FN("gmacs", FGMACS, 0, OF_ANY) /* ./. reading EOF does not exit */ FN("ignoreeof", FIGNOREEOF, 0, OF_ANY) +/* ./. inherit -x flag */ +FN("inherit-xtrace", FXTRACEREC, 0, OF_ANY) + /* -i interactive shell */ FN("interactive", FTALKING, 'i', OF_CMDLINE) @@ -88,7 +102,10 @@ FN("nounset", FNOUNSET, 'u', OF_ANY) /* ./. don't do logical cds/pwds (non-standard) */ FN("physical", FPHYSICAL, 0, OF_ANY) -/* ./. pdksh compat: somewhat more POSIXish mode (non-standard) */ +/* ./. errorlevel of a pipeline is the rightmost nonzero value */ +FN("pipefail", FPIPEFAIL, 0, OF_ANY) + +/* ./. adhere more closely to POSIX even when undesirable */ FN("posix", FPOSIX, 0, OF_ANY) /* -p use suid_profile; privileged shell */ @@ -97,7 +114,7 @@ FN("privileged", FPRIVILEGED, 'p', OF_ANY) /* -r restricted shell */ FN("restricted", FRESTRICTED, 'r', OF_CMDLINE) -/* ./. pdksh compat: called as sh not mksh; kludge mode (non-standard) */ +/* ./. kludge mode for better compat with traditional sh (OS-specific) */ FN("sh", FSH, 0, OF_ANY) /* -s (invocation) parse stdin (pseudo non-standard) */ @@ -130,17 +147,17 @@ FN("viraw", FVIRAW, 0, OF_ANY) FN("xtrace", FXTRACE, 'x', OF_ANY) /* -c (invocation) execute specified command */ -FN(NULL, FCOMMAND, 'c', OF_CMDLINE) +FN("", FCOMMAND, 'c', OF_CMDLINE) /* * anonymous flags: used internally by shell only (not visible to user) */ /* ./. direct builtin call (divined from argv[0] multi-call binary) */ -FN(NULL, FAS_BUILTIN, 0, OF_INTERNAL) +FN("", FAS_BUILTIN, 0, OF_INTERNAL) /* ./. (internal) initial shell was interactive */ -FN(NULL, FTALKING_I, 0, OF_INTERNAL) +FN("", FTALKING_I, 0, OF_INTERNAL) #undef FN #undef F0 diff --git a/src/shf.c b/src/shf.c index 82db720..e8ec14a 100644 --- a/src/shf.c +++ b/src/shf.c @@ -1,7 +1,8 @@ -/* $OpenBSD: shf.c,v 1.15 2006/04/02 00:48:33 deraadt Exp $ */ +/* $OpenBSD: shf.c,v 1.16 2013/04/19 17:36:09 millert Exp $ */ /*- - * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 + * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, + * 2012, 2013 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -24,7 +25,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.56 2013/01/01 03:32:44 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.61 2013/07/21 18:36:03 tg Exp $"); /* flags to shf_emptybuf() */ #define EB_READSW 0x01 /* about to switch to reading */ @@ -51,7 +52,7 @@ shf_open(const char *name, int oflags, int mode, int sflags) ssize_t bsize = /* at most 512 */ sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE; - int fd; + int fd, eno; /* Done before open so if alloca fails, fd won't be lost. */ shf = alloc(sizeof(struct shf) + bsize, ATEMP); @@ -63,16 +64,20 @@ shf_open(const char *name, int oflags, int mode, int sflags) fd = open(name, oflags, mode); if (fd < 0) { + eno = errno; afree(shf, shf->areap); + errno = eno; return (NULL); } if ((sflags & SHF_MAPHI) && fd < FDBASE) { int nfd; nfd = fcntl(fd, F_DUPFD, FDBASE); + eno = errno; close(fd); if (nfd < 0) { afree(shf, shf->areap); + errno = eno; return (NULL); } fd = nfd; @@ -740,8 +745,6 @@ shf_smprintf(const char *fmt, ...) return (shf_sclose(&shf)); } -#define BUF_SIZE 128 - #define FL_HASH 0x001 /* '#' seen */ #define FL_PLUS 0x002 /* '+' seen */ #define FL_RIGHT 0x004 /* '-' seen */ @@ -985,6 +988,10 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args) case 's': if ((s = VA(const char *)) == NULL) s = "(null)"; + else if (flags & FL_HASH) { + print_value_quoted(shf, s); + continue; + } len = utf_mbswidth(s); break; diff --git a/src/syn.c b/src/syn.c index bffee7a..5c07d51 100644 --- a/src/syn.c +++ b/src/syn.c @@ -1,8 +1,8 @@ -/* $OpenBSD: syn.c,v 1.28 2008/07/23 16:34:38 jaredy Exp $ */ +/* $OpenBSD: syn.c,v 1.29 2013/06/03 18:40:05 jca Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, - * 2011, 2012 + * 2011, 2012, 2013 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.88 2012/12/28 02:28:39 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.92 2013/06/03 22:28:17 tg Exp $"); struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ @@ -53,7 +53,7 @@ static struct op *caselist(void); static struct op *casepart(int); static struct op *function_body(char *, bool); static char **wordlist(void); -static struct op *block(int, struct op *, struct op *, char **); +static struct op *block(int, struct op *, struct op *); static struct op *newtp(int); static void syntaxerr(const char *) MKSH_A_NORETURN; static void nesting_push(struct nesting_state *, int); @@ -108,9 +108,9 @@ pipeline(int cf) if ((p = get_command(CONTIN)) == NULL) syntaxerr(NULL); if (tl == NULL) - t = tl = block(TPIPE, t, p, NOWORDS); + t = tl = block(TPIPE, t, p); else - tl = tl->right = block(TPIPE, tl->right, p, NOWORDS); + tl = tl->right = block(TPIPE, tl->right, p); } REJECT; } @@ -128,7 +128,7 @@ andor(void) while ((c = token(0)) == LOGAND || c == LOGOR) { if ((p = pipeline(CONTIN)) == NULL) syntaxerr(NULL); - t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); + t = block(c == LOGAND? TAND: TOR, t, p); } REJECT; } @@ -157,16 +157,15 @@ c_list(bool multi) } else if (!p) break; else if (c == '&' || c == COPROC) - p = block(c == '&' ? TASYNC : TCOPROC, - p, NOBLOCK, NOWORDS); + p = block(c == '&' ? TASYNC : TCOPROC, p, NULL); else if (c != ';') have_sep = false; if (!t) t = p; else if (!tl) - t = tl = block(TLIST, t, p, NOWORDS); + t = tl = block(TLIST, t, p); else - tl = tl->right = block(TLIST, tl->right, p, NOWORDS); + tl = tl->right = block(TLIST, tl->right, p); if (!have_sep) break; } @@ -240,11 +239,11 @@ nested(int type, int smark, int emark) t = c_list(true); musthave(emark, KEYWORD|sALIAS); nesting_pop(&old_nesting); - return (block(type, t, NOBLOCK, NOWORDS)); + return (block(type, t, NULL)); } static const char let_cmd[] = { - CHAR, 'l', CHAR, 'e', CHAR, 't', EOS + CHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS }; static const char setA_cmd0[] = { CHAR, 's', CHAR, 'e', CHAR, 't', EOS @@ -465,7 +464,7 @@ get_command(int cf) t = pipeline(0); if (t == NULL) syntaxerr(NULL); - t = block(TBANG, NOBLOCK, t, NOWORDS); + t = block(TBANG, NULL, t); break; case TIME: @@ -477,7 +476,7 @@ get_command(int cf) t->str[0] = '\0'; t->str[1] = '\0'; } - t = block(TTIME, t, NOBLOCK, NOWORDS); + t = block(TTIME, t, NULL); break; case FUNCTION: @@ -505,7 +504,7 @@ get_command(int cf) XPput(args, NULL); t->args = (const char **)XPclose(args); XPput(vars, NULL); - t->vars = (char **) XPclose(vars); + t->vars = (char **)XPclose(vars); } else { XPfree(args); XPfree(vars); @@ -632,7 +631,7 @@ casepart(int endtok) } while (token(0) == '|'); REJECT; XPput(ptns, NULL); - t->vars = (char **) XPclose(ptns); + t->vars = (char **)XPclose(ptns); musthave(')', 0); t->left = c_list(true); @@ -743,13 +742,8 @@ wordlist(void) XPput(args, yylval.cp); if (c != '\n' && c != ';') syntaxerr(NULL); - if (XPsize(args) == 0) { - XPfree(args); - return (NULL); - } else { - XPput(args, NULL); - return ((char **)XPclose(args)); - } + XPput(args, NULL); + return ((char **)XPclose(args)); } /* @@ -757,14 +751,13 @@ wordlist(void) */ static struct op * -block(int type, struct op *t1, struct op *t2, char **wp) +block(int type, struct op *t1, struct op *t2) { struct op *t; t = newtp(type); t->left = t1; t->right = t2; - t->vars = wp; return (t); } @@ -1131,7 +1124,7 @@ yyrecursive(int subtype MKSH_A_UNUSED) struct yyrecursive_state *ys; int stok, etok; - if (subtype == FUNSUB) { + if (subtype != COMSUB) { stok = '{'; etok = '}'; } else { diff --git a/src/tree.c b/src/tree.c index 8015a8d..dcbd7a1 100644 --- a/src/tree.c +++ b/src/tree.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012 + * 2011, 2012, 2013 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -23,12 +23,12 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.67 2012/12/04 01:10:35 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.71 2013/07/26 20:33:24 tg Exp $"); #define INDENT 8 static void ptree(struct op *, int, struct shf *); -static void pioact(struct shf *, int, struct ioword *); +static void pioact(struct shf *, struct ioword *); static const char *wdvarput(struct shf *, const char *, int, int); static void vfptreef(struct shf *, int, const char *, va_list); static struct ioword **iocopy(struct ioword **, Area *); @@ -214,7 +214,7 @@ ptree(struct op *t, int indent, struct shf *shf) bool need_nl = false; while (*ioact != NULL) - pioact(shf, indent, *ioact++); + pioact(shf, *ioact++); /* Print here documents after everything else... */ ioact = t->ioact; while (*ioact != NULL) { @@ -244,7 +244,7 @@ ptree(struct op *t, int indent, struct shf *shf) } static void -pioact(struct shf *shf, int indent, struct ioword *iop) +pioact(struct shf *shf, struct ioword *iop) { int flag = iop->flag; int type = flag & IOTYPE; @@ -259,16 +259,20 @@ pioact(struct shf *shf, int indent, struct ioword *iop) switch (type) { case IOREAD: - shf_puts("<", shf); + shf_putc('<', shf); break; case IOHERE: - shf_puts(flag & IOSKIP ? "<<-" : "<<", shf); + shf_puts("<<", shf); + if (flag & IOSKIP) + shf_putc('-', shf); break; case IOCAT: shf_puts(">>", shf); break; case IOWRITE: - shf_puts(flag & IOCLOB ? ">|" : ">", shf); + shf_putc('>', shf); + if (flag & IOCLOB) + shf_putc('|', shf); break; case IORDWR: shf_puts("<>", shf); @@ -283,9 +287,13 @@ pioact(struct shf *shf, int indent, struct ioword *iop) wdvarput(shf, iop->delim, 0, WDS_TPUTS); if (iop->flag & IOHERESTR) shf_putc(' ', shf); - } else if (iop->name) - fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ", - iop->name); + } else if (iop->name) { + if (iop->flag & IONAMEXP) + print_value_quoted(shf, iop->name); + else + wdvarput(shf, iop->name, 0, WDS_TPUTS); + shf_putc(' ', shf); + } prevent_semicolon = false; } @@ -345,7 +353,14 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) shf_puts(cs, shf); break; case FUNSUB: - shf_puts("${ ", shf); + c = ' '; + if (0) + /* FALLTHROUGH */ + case VALSUB: + c = '|'; + shf_putc('$', shf); + shf_putc('{', shf); + shf_putc(c, shf); cs = ";}"; goto pSUB; case EXPRSUB: @@ -485,7 +500,7 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va) break; case 'R': /* I/O redirection */ - pioact(shf, indent, va_arg(va, struct ioword *)); + pioact(shf, va_arg(va, struct ioword *)); break; default: shf_putc(c, shf); @@ -588,6 +603,7 @@ wdscan(const char *wp, int c) break; case COMSUB: case FUNSUB: + case VALSUB: case EXPRSUB: while (*wp++ != 0) ; @@ -745,8 +761,8 @@ vistree(char *dst, size_t sz, struct op *t) char *cp, *buf; size_t n; - buf = alloc(sz + 8, ATEMP); - snptreef(buf, sz + 8, "%T", t); + buf = alloc(sz + 16, ATEMP); + snptreef(buf, sz + 16, "%T", t); cp = buf; vist_loop: if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) { @@ -830,6 +846,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) case FUNSUB: shf_puts("FUNSUB<", shf); goto dumpsub; + case VALSUB: + shf_puts("VALSUB<", shf); + goto dumpsub; case EXPRSUB: shf_puts("EXPRSUB<", shf); goto dumpsub; diff --git a/src/var.c b/src/var.c index 19527e0..ba8fd82 100644 --- a/src/var.c +++ b/src/var.c @@ -1,4 +1,4 @@ -/* $OpenBSD: var.c,v 1.34 2007/10/15 02:16:35 deraadt Exp $ */ +/* $OpenBSD: var.c,v 1.35 2013/04/05 01:31:30 tedu Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -27,7 +27,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/var.c,v 1.166 2013/02/18 22:24:52 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/var.c,v 1.173 2013/05/31 22:47:14 tg Exp $"); /*- * Variables @@ -49,7 +49,7 @@ static void unspecial(const char *); static void getspec(struct tbl *); static void setspec(struct tbl *); static void unsetspec(struct tbl *); -static int getint(struct tbl *, mksh_ari_t *, bool); +static int getint(struct tbl *, mksh_ari_u *, bool); static const char *array_index_calc(const char *, bool *, uint32_t *); /* @@ -347,7 +347,7 @@ str_val(struct tbl *vp) else { /* integer source */ mksh_uari_t n; - int base; + unsigned int base; /** * worst case number length is when base == 2: * 1 (minus) + 2 (base, up to 36) + 1 ('#') + @@ -361,8 +361,8 @@ str_val(struct tbl *vp) if (vp->flag & INT_U) n = vp->val.u; else - n = (vp->val.i < 0) ? -vp->val.i : vp->val.i; - base = (vp->type == 0) ? 10 : vp->type; + n = (vp->val.i < 0) ? -vp->val.u : vp->val.u; + base = (vp->type == 0) ? 10U : (unsigned int)vp->type; if (base == 1 && n == 0) base = 2; @@ -471,48 +471,43 @@ setint(struct tbl *vq, mksh_ari_t n) } static int -getint(struct tbl *vp, mksh_ari_t *nump, bool arith) +getint(struct tbl *vp, mksh_ari_u *nump, bool arith) { - int c, base, neg; - mksh_uari_t num; + mksh_uari_t c, num, base; const char *s; - bool have_base = false; + bool have_base = false, neg = false; if (vp->flag&SPECIAL) getspec(vp); - /* XXX is it possible for ISSET to be set and val.s to be 0? */ + /* XXX is it possible for ISSET to be set and val.s to be NULL? */ if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL)) return (-1); if (vp->flag&INTEGER) { - *nump = vp->val.i; + nump->i = vp->val.i; return (vp->type); } s = vp->val.s + vp->type; base = 10; num = 0; - neg = 0; if (arith && s[0] == '0' && (s[1] | 0x20) == 'x') { s += 2; base = 16; have_base = true; } -#ifdef MKSH_LEGACY_MODE - if (arith && s[0] == '0' && ksh_isdigit(s[1]) && + if (Flag(FPOSIX) && arith && s[0] == '0' && ksh_isdigit(s[1]) && !(vp->flag & ZEROFIL)) { /* interpret as octal (deprecated) */ base = 8; have_base = true; } -#endif while ((c = *s++)) { if (c == '-') { - neg++; + neg = true; continue; } else if (c == '#') { if (have_base || num < 1 || num > 36) return (-1); - base = (int)num; - if (base == 1) { + if ((base = num) == 1) { unsigned int wc; if (!UTFMODE) @@ -525,7 +520,7 @@ getint(struct tbl *vp, mksh_ari_t *nump, bool arith) * not round-tripping correctly XXX) */ wc = 0xEF00 + *(const unsigned char *)s; - *nump = (mksh_ari_t)wc; + nump->u = (mksh_uari_t)wc; return (1); } num = 0; @@ -539,11 +534,13 @@ getint(struct tbl *vp, mksh_ari_t *nump, bool arith) c -= 'A' - 10; else return (-1); - if (c < 0 || c >= base) + if (c >= base) return (-1); num = num * base + c; } - *nump = neg ? -((mksh_ari_t)num) : (mksh_ari_t)num; + if (neg) + num = -num; + nump->u = num; return (base); } @@ -555,11 +552,11 @@ struct tbl * setint_v(struct tbl *vq, struct tbl *vp, bool arith) { int base; - mksh_ari_t num; + mksh_ari_u num; if ((base = getint(vp, &num, arith)) == -1) return (NULL); - setint_n(vq, num, 0); + setint_n(vq, num.i, 0); if (vq->type == 0) /* default base */ vq->type = base; @@ -708,6 +705,10 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base) /* check for valid variable name, search for value */ val = skip_varname(var, false); + if (val == var) { + /* no variable name given */ + return (NULL); + } if (*val == '[') { if (set_refflag != SRF_NOP) errorf("%s: %s", var, @@ -742,7 +743,6 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base) * must have a = when setting a variable by importing * the original environment, otherwise be empty; we * also end up here when a variable name was invalid - * or none given */ return (NULL); } else { @@ -768,10 +768,15 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base) if (vp != NULL) qval = str_val(vp); } - /* silently ignore 'nameref foo=foo' */ - if (qval != NULL && !strcmp(qval, tvar)) { - afree(tvar, ATEMP); - return (&vtemp); + /* prevent nameref loops */ + while (qval) { + if (!strcmp(qval, tvar)) + errorf("%s: %s", qval, + "expression recurses on parameter"); + varsearch(e->loc, &vp, qval, hash(qval)); + qval = NULL; + if (vp && ((vp->flag & (ARRAY|ASSOC)) == ASSOC)) + qval = str_val(vp); } } @@ -1094,7 +1099,7 @@ static int user_lineno; /* what user set $LINENO to */ static void getspec(struct tbl *vp) { - register mksh_ari_t i; + mksh_ari_u num; int st; struct timeval tv; @@ -1114,19 +1119,19 @@ getspec(struct tbl *vp) } switch (st) { case V_BASHPID: - i = (mksh_ari_t)procpid; + num.u = (mksh_uari_t)procpid; break; case V_COLUMNS: - i = x_cols; + num.i = x_cols; break; case V_HISTSIZE: - i = histsize; + num.i = histsize; break; case V_LINENO: - i = current_lineno + user_lineno; + num.i = current_lineno + user_lineno; break; case V_LINES: - i = x_lins; + num.i = x_lins; break; case V_EPOCHREALTIME: { /* 10(%u) + 1(.) + 6 + NUL */ @@ -1141,10 +1146,10 @@ getspec(struct tbl *vp) return; } case V_OPTIND: - i = user_opt.uoptind; + num.i = user_opt.uoptind; break; case V_RANDOM: - i = rndget(); + num.i = rndget(); break; case V_SECONDS: /* @@ -1154,7 +1159,7 @@ getspec(struct tbl *vp) */ if (vp->flag & ISSET) { mksh_TIME(tv); - i = tv.tv_sec - seconds; + num.i = tv.tv_sec - seconds; } else return; break; @@ -1163,14 +1168,14 @@ getspec(struct tbl *vp) return; } vp->flag &= ~SPECIAL; - setint_n(vp, i, 0); + setint_n(vp, num.i, 0); vp->flag |= SPECIAL; } static void setspec(struct tbl *vp) { - mksh_ari_t i; + mksh_ari_u num; char *s; int st; @@ -1228,11 +1233,11 @@ setspec(struct tbl *vp) case V_SECONDS: case V_TMOUT: vp->flag &= ~SPECIAL; - if (getint(vp, &i, false) == -1) { + if (getint(vp, &num, false) == -1) { s = str_val(vp); if (st != V_RANDOM) errorf("%s: %s: %s", vp->name, "bad number", s); - i = hash(s); + num.u = hash(s); } vp->flag |= SPECIAL; break; @@ -1245,40 +1250,40 @@ setspec(struct tbl *vp) switch (st) { case V_COLUMNS: - if (i >= MIN_COLS) - x_cols = i; + if (num.i >= MIN_COLS) + x_cols = num.i; break; case V_HISTSIZE: - sethistsize(i); + sethistsize(num.i); break; case V_LINENO: /* The -1 is because line numbering starts at 1. */ - user_lineno = (unsigned int)i - current_lineno - 1; + user_lineno = num.u - current_lineno - 1; break; case V_LINES: - if (i >= MIN_LINS) - x_lins = i; + if (num.i >= MIN_LINS) + x_lins = num.i; break; case V_OPTIND: - getopts_reset((int)i); + getopts_reset((int)num.i); break; case V_RANDOM: /* * mksh R39d+ no longer has the traditional repeatability * of $RANDOM sequences, but always retains state */ - rndset((long)i); + rndset((unsigned long)num.u); break; case V_SECONDS: { struct timeval tv; mksh_TIME(tv); - seconds = tv.tv_sec - i; + seconds = tv.tv_sec - num.i; } break; case V_TMOUT: - ksh_tmout = i >= 0 ? i : 0; + ksh_tmout = num.i >= 0 ? num.i : 0; break; } } @@ -1534,7 +1539,7 @@ rndget(void) } void -rndset(long v) +rndset(unsigned long v) { register uint32_t h; -- 2.11.0