diff -Nru mksh-50e/Build.sh mksh-52c/Build.sh --- mksh-50e/Build.sh 2015-03-01 16:43:15.000000000 +0100 +++ mksh-52c/Build.sh 2016-03-04 19:29:05.000000000 +0100 @@ -1,9 +1,9 @@ #!/bin/sh -srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.669.2.2 2015/03/01 15:42:50 tg Exp $' +srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.697 2016/03/04 18:28:39 tg Exp $' #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013, 2014 -# Thorsten Glaser +# 2011, 2012, 2013, 2014, 2015, 2016 +# mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission @@ -100,6 +100,7 @@ srcfile=$1 test -f "$srcfile" || genopt_die Source file \$srcfile not set. bn=`basename "$srcfile" | sed 's/.opt$//'` + o_hdr='/* +++ GENERATED FILE +++ DO NOT EDIT +++ */' o_gen= o_str= o_sym= @@ -128,6 +129,9 @@ ;; *:@@*) genopt_die ;; + 0:/\*-|0:\ \**|0:) + o_hdr=$o_hdr$nl$line + ;; 0:@*|1:@*) # begin of a definition block sym=`echo "$line" | sed 's/^@//'` @@ -177,6 +181,7 @@ echo "\"$opts\"" test -n "$cond" && echo "#endif" done | { + echo "$o_hdr" echo "#ifndef $o_sym$o_gen" echo "#else" cat @@ -314,6 +319,7 @@ vv ']' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN conftest.c $LIBS $ccpr" test $tcfn = no && test -f a.out && tcfn=a.out test $tcfn = no && test -f a.exe && tcfn=a.exe + test $tcfn = no && test -f conftest.exe && tcfn=conftest.exe test $tcfn = no && test -f conftest && tcfn=conftest if test -f $tcfn; then test 1 = $fr || fv=1 @@ -577,8 +583,8 @@ echo "$me: Error: ./$tfn is a directory!" >&2 exit 1 fi -rmf a.exe* a.out* conftest.c *core core.* lft ${tfn}* no *.bc *.ll *.o *.gen \ - Rebuild.sh signames.inc test.sh x vv.out +rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \ + *.ll *.o *.gen Rebuild.sh lft no signames.inc test.sh x vv.out SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c" SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c" @@ -682,14 +688,14 @@ # Configuration depending on OS name case $TARGET_OS in 386BSD) - : ${HAVE_CAN_OTWO=0} + : "${HAVE_CAN_OTWO=0}" add_cppflags -DMKSH_NO_SIGSETJMP add_cppflags -DMKSH_TYPEDEF_SIG_ATOMIC_T=int add_cppflags -DMKSH_CONSERVATIVE_FDS ;; AIX) add_cppflags -D_ALL_SOURCE - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; BeOS) case $KSH_VERSION in @@ -708,7 +714,7 @@ add_cppflags -DMKSH__NO_SETEUGID ;; BSD/OS) - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; Coherent) oswarn="; it has major issues" @@ -719,7 +725,7 @@ add_cppflags -DMKSH_DISABLE_TTY_WARNING ;; CYGWIN*) - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; Darwin) add_cppflags -D_DARWIN_C_SOURCE @@ -732,13 +738,14 @@ oswarn="; it has minor issues" add_cppflags -D_GNU_SOURCE add_cppflags -DMKSH_CONSERVATIVE_FDS - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; GNU) case $CC in *tendracc*) ;; *) add_cppflags -D_GNU_SOURCE ;; esac + add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN # define MKSH__NO_PATH_MAX to use Hurd-only functions add_cppflags -DMKSH__NO_PATH_MAX ;; @@ -747,6 +754,7 @@ *tendracc*) ;; *) add_cppflags -D_GNU_SOURCE ;; esac + add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN ;; Haiku) add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1 @@ -757,11 +765,11 @@ ccpc='-X ' ccpl='-Y ' add_cppflags -D_ALL_SOURCE - : ${LIBS='-lcrypt'} - : ${HAVE_SETLOCALE_CTYPE=0} + : "${LIBS=-lcrypt}" + : "${HAVE_SETLOCALE_CTYPE=0}" ;; IRIX*) - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; Linux) case $CC in @@ -769,7 +777,7 @@ *) add_cppflags -D_GNU_SOURCE ;; esac add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN - : ${HAVE_REVOKE=0} + : "${HAVE_REVOKE=0}" ;; LynxOS) oswarn="; it has minor issues" @@ -782,7 +790,7 @@ add_cppflags -DMKSH_CONSERVATIVE_FDS add_cppflags -D_MINIX_SOURCE oldish_ed=no-stderr-ed # no /bin/ed, maybe see below - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; Minix3) add_cppflags -DMKSH_UNEMPLOYED @@ -790,23 +798,24 @@ add_cppflags -DMKSH_NO_LIMITS add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX oldish_ed=no-stderr-ed # /usr/bin/ed(!) is broken - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; MirBSD) ;; MSYS_*) add_cppflags -DMKSH_ASSUME_UTF8=0; HAVE_ISSET_MKSH_ASSUME_UTF8=1 # almost same as CYGWIN* (from RT|Chatzilla) - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" # broken on this OE (from ir0nh34d) - : ${HAVE_STDINT_H=0} + : "${HAVE_STDINT_H=0}" ;; NetBSD) ;; NEXTSTEP) add_cppflags -D_NEXT_SOURCE add_cppflags -D_POSIX_SOURCE - : ${AWK=gawk} ${CC=cc -posix} + : "${AWK=gawk}" + : "${CC=cc -posix}" add_cppflags -DMKSH_NO_SIGSETJMP # NeXTstep cannot get a controlling tty add_cppflags -DMKSH_UNEMPLOYED @@ -827,7 +836,17 @@ oswarn="; it has unknown issues" ;; OpenBSD) - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" + ;; +OS/2) + HAVE_TERMIOS_H=0 + HAVE_MKNOD=0 # setmode() incompatible + oswarn="; it is currently being ported" + check_categories="$check_categories nosymlink" + : "${CC=gcc}" + : "${SIZE=: size}" + add_cppflags -DMKSH_UNEMPLOYED + add_cppflags -DMKSH_NOPROSPECTOFWORK ;; OSF1) HAVE_SIG_T=0 # incompatible @@ -835,7 +854,7 @@ add_cppflags -D_POSIX_C_SOURCE=200112L add_cppflags -D_XOPEN_SOURCE=600 add_cppflags -D_XOPEN_SOURCE_EXTENDED - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; Plan9) add_cppflags -D_POSIX_SOURCE @@ -853,7 +872,7 @@ PW32*) HAVE_SIG_T=0 # incompatible oswarn=' and will currently not work' - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; QNX) add_cppflags -D__NO_EXT_QNX @@ -863,7 +882,7 @@ oldish_ed=no-stderr-ed # oldish /bin/ed is broken ;; esac - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; SCO_SV) case $TARGET_OSREV in @@ -880,7 +899,7 @@ ;; esac add_cppflags -DMKSH_CONSERVATIVE_FDS - : ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0} + : "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}" ;; skyos) oswarn="; it has minor issues" @@ -895,15 +914,15 @@ oswarn=' and will currently not work' ;; ULTRIX) - : ${CC=cc -YPOSIX} + : "${CC=cc -YPOSIX}" add_cppflags -DMKSH_TYPEDEF_SSIZE_T=int add_cppflags -DMKSH_CONSERVATIVE_FDS - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; UnixWare|UNIX_SV) # SCO UnixWare add_cppflags -DMKSH_CONSERVATIVE_FDS - : ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0} + : "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}" ;; UWIN*) ccpc='-Yc,' @@ -911,7 +930,7 @@ tsts=" 3<>/dev/tty" oswarn="; it will compile, but the target" oswarn="$oswarn${nl}platform itself is very flakey/unreliable" - : ${HAVE_SETLOCALE_CTYPE=0} + : "${HAVE_SETLOCALE_CTYPE=0}" ;; _svr4) # generic target for SVR4 Unix with uname -s = uname -n @@ -925,11 +944,11 @@ ;; esac -: ${HAVE_MKNOD=0} +: "${HAVE_MKNOD=0}" -: ${AWK=awk} ${CC=cc} ${NROFF=nroff} ${SIZE=size} +: "${AWK=awk}${CC=cc}${NROFF=nroff}${SIZE=size}" test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \ - NROFF="$NROFF -c" + echo | $NROFF -c >/dev/null 2>&1 && NROFF="$NROFF -c" # this aids me in tracing FTBFSen without access to the buildd $e "Hi from$ao $bi$srcversion$ao on:" @@ -1050,7 +1069,7 @@ #endif ; const char * -#if defined(__KLIBC__) +#if defined(__KLIBC__) && !defined(__OS2__) et="klibc" #else et="unknown" @@ -1183,7 +1202,7 @@ ;; tendra) vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V 2>&1 | \ - fgrep -i -e version -e release" + grep -F -i -e version -e release" ;; ucode) vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V" @@ -1194,7 +1213,7 @@ SCO_SV:3.2*) # SCO OpenServer 5 CFLAGS="$CFLAGS -g" - : ${HAVE_CAN_OTWO=0} ${HAVE_CAN_OPTIMISE=0} + : "${HAVE_CAN_OTWO=0}${HAVE_CAN_OPTIMISE=0}" ;; esac vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS" @@ -1220,20 +1239,21 @@ vv '|' "llc -version" ;; esac +etd=" on $et" case $et in klibc) add_cppflags -DMKSH_NO_LIMITS ;; unknown) # nothing special detected, don’t worry - unset et + etd= ;; *) # huh? ;; esac -$e "$bi==> which compiler seems to be used...$ao $ui$ct${et+ on $et}$ao" -rmf conftest.c conftest.o conftest a.out* a.exe* vv.out +$e "$bi==> which compiler seems to be used...$ao $ui$ct$etd$ao" +rmf conftest.c conftest.o conftest a.out* a.exe* conftest.exe* vv.out # # Compiler: works as-is, with -Wno-error and -Werror @@ -1248,7 +1268,7 @@ if ac_ifcpp 'if 0' compiler_fails '' \ 'if the compiler does not fail correctly'; then save_CFLAGS=$CFLAGS - : ${HAVE_CAN_DELEXE=x} + : "${HAVE_CAN_DELEXE=x}" case $ct in dec) CFLAGS="$CFLAGS ${ccpl}-non_shared" @@ -1610,8 +1630,8 @@ # if ac_ifcpp 'ifdef MKSH_SMALL' isset_MKSH_SMALL '' \ "if a reduced-feature mksh is requested"; then - : ${HAVE_NICE=0} - : ${HAVE_PERSISTENT_HISTORY=0} + : "${HAVE_NICE=0}" + : "${HAVE_PERSISTENT_HISTORY=0}" check_categories="$check_categories smksh" HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1 # from sh.h fi @@ -1625,7 +1645,7 @@ "if mksh will be built without job signals" && \ check_categories="$check_categories arge nojsig" ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \ - 'if the default UTF-8 mode is specified' && : ${HAVE_SETLOCALE_CTYPE=0} + 'if the default UTF-8 mode is specified' && : "${HAVE_SETLOCALE_CTYPE=0}" ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \ 'if traditional/conservative fd use is requested' && \ check_categories="$check_categories convfds" @@ -1662,6 +1682,7 @@ ac_header sys/sysmacros.h ac_header bstring.h ac_header grp.h sys/types.h +ac_header io.h ac_header libgen.h ac_header libutil.h sys/types.h ac_header paths.h @@ -1692,6 +1713,7 @@ ac_testn can_lfs_aix '!' can_lfs 0 "... with -D_LARGE_FILES=1" conftest.c <<-'EOF' + cat >conftest.c <<-EOF #define EXTERN #define MKSH_INCLUDES_ONLY #include "sh.h" - __RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.669.2.2 2015/03/01 15:42:50 tg Exp $"); - int main(void) { printf("Hello, World!\n"); return (isatty(0)); } + __RCSID("$srcversion"); + int main(void) { printf("Hello, World!\\n"); return (isatty(0)); } EOF case $cm in llvm) @@ -1904,11 +1926,6 @@ int main(void) { struct timeval tv; return (gettimeofday(&tv, NULL)); } EOF -ac_test issetugid <<-'EOF' - #include - int main(void) { return (issetugid()); } -EOF - ac_test killpg <<-'EOF' #include int main(int ac, char *av[]) { return (av[0][killpg(123, ac)]); } @@ -2150,9 +2167,9 @@ cta(uari_wrap_32_bit, (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) > (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4)); -#define NUM 21 +#define NUM 22 #else -#define NUM 15 +#define NUM 16 #endif /* these are always required */ cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0); @@ -2165,6 +2182,7 @@ cta(sizet_funcptr_same_size, sizeof(size_t) == sizeof(void (*)(void))); /* our formatting routines assume this */ cta(ptr_fits_in_long, sizeof(size_t) <= sizeof(long)); +cta(ari_fits_in_long, sizeof(mksh_ari_t) <= sizeof(long)); /* for struct alignment people */ char padding[64 - NUM]; }; @@ -2246,20 +2264,22 @@ sigseenone=: sigseentwo=: echo '#include -#ifndef NSIG -#if defined(_NSIG) -#define NSIG _NSIG +#if defined(NSIG_MAX) +#define cfg_NSIG NSIG_MAX +#elif defined(NSIG) +#define cfg_NSIG NSIG +#elif defined(_NSIG) +#define cfg_NSIG _NSIG #elif defined(SIGMAX) -#define NSIG (SIGMAX+1) +#define cfg_NSIG (SIGMAX + 1) #elif defined(_SIGMAX) -#define NSIG (_SIGMAX+1) +#define cfg_NSIG (_SIGMAX + 1) #else -/* XXX better error out, see sh.h */ -#define NSIG 64 -#endif +/*XXX better error out, see sh.h */ +#define cfg_NSIG 64 #endif int -mksh_cfg= NSIG +mksh_cfg= cfg_NSIG ;' >conftest.c # GNU sed 2.03 segfaults when optimising this to sed -n NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \ @@ -2281,8 +2301,8 @@ $printf "NSIG=$NSIG ... " sigs="ABRT FPE ILL INT SEGV TERM ALRM BUS CHLD CONT HUP KILL PIPE QUIT" sigs="$sigs STOP TSTP TTIN TTOU USR1 USR2 POLL PROF SYS TRAP URG VTALRM" - sigs="$sigs XCPU XFSZ INFO WINCH EMT IO DIL LOST PWR SAK CLD IOT RESV" - sigs="$sigs STKFLT UNUSED" + sigs="$sigs XCPU XFSZ INFO WINCH EMT IO DIL LOST PWR SAK CLD IOT STKFLT" + sigs="$sigs ABND DCE DUMP IOERR TRACE DANGER THCONT THSTOP RESV UNUSED" test 1 = $HAVE_CPP_DD && test $NSIG -gt 1 && sigs="$sigs "`vq \ "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c" | \ grep '[ ]SIG[A-Z0-9][A-Z0-9]*[ ]' | \ @@ -2308,7 +2328,7 @@ sed 's/^ *mksh_cfg *=[ ]*\([0-9][0-9x]*\).*$/:\1 '$name/ done | sed -n '/^:[^ ]/s/^://p' | while read nr name; do test $printf = echo || nr=`printf %d "$nr" 2>/dev/null` - test $nr -gt 0 && test $nr -le $NSIG || continue + test $nr -gt 0 && test $nr -lt $NSIG || continue case $sigseentwo in *:$nr:*) ;; *) echo " { \"$name\", $nr }," @@ -2325,7 +2345,7 @@ addsrcs USE_PRINTF_BUILTIN printf.c test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose" -add_cppflags -DMKSH_BUILD_R=505 +add_cppflags -DMKSH_BUILD_R=523 $e $bi$me: Finished configuration testing, now producing output.$ao @@ -2333,8 +2353,13 @@ objs= sp= case $tcfn in -a.exe) mkshexe=$tfn.exe ;; -*) mkshexe=$tfn ;; +a.exe|conftest.exe) + mkshexe=$tfn.exe + add_cppflags -DMKSH_EXE_EXT + ;; +*) + mkshexe=$tfn + ;; esac case $curdir in *\ *) mkshshebang="#!./$mkshexe" ;; @@ -2392,7 +2417,7 @@ args[\${#args[*]}]=\$TMPDIR fi print Testing mksh for conformance: - fgrep -e MirOS: -e MIRBSD "\$sflag" + grep -F -e Mir''OS: -e MIRBSD "\$sflag" print "This shell is actually:\\n\\t\$KSH_VERSION" print 'test.sh built for mksh $dstversion' cstr='\$os = defined \$^O ? \$^O : "unknown";' @@ -2641,7 +2666,6 @@ MKSH_NOPWNAM skip PAM calls, for -static on glibc or Solaris MKSH_NO_CMDLINE_EDITING disable command line editing code entirely MKSH_NO_DEPRECATED_WARNING omit warning when deprecated stuff is run -MKSH_NO_EXTERNAL_CAT omit hack to skip cat builtin when flags passed MKSH_NO_LIMITS omit ulimit code MKSH_NO_SIGSETJMP define if sigsetjmp is broken or not available MKSH_NO_SIGSUSPEND use sigprocmask+pause instead of sigsuspend @@ -2668,4 +2692,7 @@ http://anonscm.debian.org/cgit/collab-maint/mksh.git/plain/debian/.mkshrc and put dot.mkshrc as /etc/mkshrc so users need not keep up their HOME. +You may also want to install the lksh binary (also as /bin/sh) built by: +$ CPPFLAGS="$CPPFLAGS -DMKSH_BINSHPOSIX" sh Build.sh -L -r -c lto + EOD diff -Nru mksh-50e/check.pl mksh-52c/check.pl --- mksh-50e/check.pl 2014-08-19 09:43:57.000000000 +0200 +++ mksh-52c/check.pl 2015-11-29 18:05:26.000000000 +0100 @@ -1,9 +1,9 @@ -# $MirOS: src/bin/mksh/check.pl,v 1.37 2014/08/19 07:43:32 tg Exp $ +# $MirOS: src/bin/mksh/check.pl,v 1.42 2015/11/29 17:05:00 tg Exp $ # $OpenBSD: th,v 1.1 2013/12/02 20:39:44 millert Exp $ #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, -# 2012, 2013, 2014 -# Thorsten Glaser +# 2012, 2013, 2014, 2015 +# mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission @@ -73,11 +73,12 @@ # the following minimal environment: # HOME, LD_LIBRARY_PATH, LOCPATH, # LOGNAME, PATH, SHELL, UNIXMODE, -# USER +# UNIXROOT, USER # (values taken from the environment of # the test harness). # CYGWIN is set to nodosfilewarning. # ENV is set to /nonexistant. +# PATHSEP is set to either : or ;. # __progname is set to the -p argument. # __perlname is set to $^X (perlexe). # file-setup mps Used to create files, directories @@ -275,11 +276,12 @@ # Set up a very minimal environment %new_env = (); foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME', - 'PATH', 'SHELL', 'UNIXMODE', 'USER')) { + 'PATH', 'SHELL', 'UNIXMODE', 'UNIXROOT', 'USER')) { $new_env{$env} = $ENV{$env} if defined $ENV{$env}; } $new_env{'CYGWIN'} = 'nodosfilewarning'; $new_env{'ENV'} = '/nonexistant'; +$new_env{'PATHSEP'} = $os eq 'os2' ? ';' : ':'; if (($os eq 'VMS') || ($Config{perlpath} =~ m/$Config{_exe}$/i)) { $new_env{'__perlname'} = $Config{perlpath}; } else { @@ -312,9 +314,10 @@ die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd); if (!$program_kludge) { - $test_prog = "$pwd/$test_prog" if substr($test_prog, 0, 1) ne '/'; + $test_prog = "$pwd/$test_prog" if (substr($test_prog, 0, 1) ne '/') && + ($os ne 'os2' || substr($test_prog, 1, 1) ne ':'); die "$prog: $test_prog is not executable - bye\n" - if (! -x $test_prog && $os ne 'os2'); + if (! -x $test_prog && $os ne 'os2'); } @trap_sigs = ('TERM', 'QUIT', 'INT', 'PIPE', 'HUP'); @@ -568,7 +571,7 @@ } push(@argv, $temps) if defined $test{'script'}; - #XXX realpathise, use which/whence -p, or sth. like that + #XXX realpathise, use command -v/whence -p/which, or sth. like that #XXX if !$program_kludge, we get by with not doing it for now tho $new_env{'__progname'} = $argv[0]; @@ -1165,7 +1168,7 @@ print STDERR "$prog:$test{':long-name'}: expected-exit value $val not in 0..255\n"; return undef; } - } elsif ($val !~ /^([\s<>+-=*%\/&|!()]|\b[wse]\b|\bSIG[A-Z][A-Z0-9]*\b)+$/) { + } elsif ($val !~ /^([\s\d<>+=*%\/&|!()-]|\b[wse]\b|\bSIG[A-Z][A-Z0-9]*\b)+$/) { print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $val\n"; return undef; } diff -Nru mksh-50e/check.t mksh-52c/check.t --- mksh-50e/check.t 2015-03-01 16:43:16.000000000 +0100 +++ mksh-52c/check.t 2016-03-05 16:40:02.000000000 +0100 @@ -1,9 +1,9 @@ -# $MirOS: src/bin/mksh/check.t,v 1.667.2.3 2015/03/01 15:42:51 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.728 2016/03/05 15:39:36 tg Exp $ # -*- mode: sh -*- #- # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013, 2014, 2015 -# Thorsten Glaser +# 2011, 2012, 2013, 2014, 2015, 2016 +# mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission @@ -30,7 +30,7 @@ # (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date expected-stdout: - @(#)MIRBSD KSH R50 2015/03/01 + @(#)MIRBSD KSH R52 2016/03/04 description: Check version of shell. stdin: @@ -39,7 +39,7 @@ category: shell:legacy-no --- expected-stdout: - @(#)LEGACY KSH R50 2015/03/01 + @(#)LEGACY KSH R52 2016/03/04 description: Check version of legacy shell. stdin: @@ -232,7 +232,7 @@ stdin: print '#!'"$__progname"'\necho tf' >lq chmod +x lq - PATH=$PWD:$PATH + PATH=$PWD$PATHSEP$PATH alias lq=lq lq echo = now @@ -257,6 +257,27 @@ expected-stdout: hello world --- +name: alias-11 +description: + Check that special argument handling still applies with escaped aliases +stdin: + alias local='\typeset' + function foo { + local x=$1 y=z + print -r -- "$x,$y" + } + foo 'bar - baz' +expected-stdout: + bar - baz,z +--- +name: arith-compound +description: + Check that arithmetic expressions are compound constructs +stdin: + { ! (( 0$(cat >&2) )) <<<1; } <<<2 +expected-stderr: + 1 +--- name: arith-lazy-1 description: Check that only one side of ternary operator is evaluated @@ -462,6 +483,9 @@ va[1975973142]=right va[4123456789]=wrong echo x7 ${va[#4123456789%2147483647]} + # make sure multiple calculations don't interfere with each other + let '# mca = -4 % -2' ' mcb = -4 % -2' + echo x8 $mca $mcb expected-stdout: x1 -1 4294967295 x2 -171510507 4123456789 @@ -470,6 +494,7 @@ x5 -171510507 4123456789 x6 1975973142 1975973142 x7 right + x8 -4 0 --- name: arith-limit32-1 description: @@ -1185,7 +1210,7 @@ # the mv command fails on Cygwin # Hurd aborts the testsuite (permission denied) # QNX does not find subdir to cd into -category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!nosymlink +category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!os:os390,!nosymlink file-setup: file 644 "x" mkdir noread noread/target noread/target/subdir ln -s noread link @@ -1651,6 +1676,17 @@ 1=02. 2=02. --- +name: expand-weird-4 +description: + Check that tilde expansion is enabled in ${x#~} + and cases that are modelled after it (${x/~/~}) +stdin: + HOME=/etc + a="~/x" + echo "<${a#~}> <${a#\~}> <${b:-~}> <${b:-\~}> <${c:=~}><$c> <${a/~}> <${a/x/~}> <${a/x/\~}>" +expected-stdout: + <~/x> <~> <\~> <~><~> <~/x> <~//etc> <~/~> +--- name: expand-number-1 description: Check that positional arguments do not overflow @@ -1822,7 +1858,7 @@ [[ -n $BASH_VERSION ]] && shopt -s extglob x=1222321_ab/cde_b/c_1221 y=xyz - echo 1: ${x/2} + echo 1: ${x/2} . ${x/} echo 2: ${x//2} echo 3: ${x/+(2)} echo 4: ${x//+(2)} @@ -1844,15 +1880,17 @@ echo 20: ${x/\//.} echo 21: ${x//\//.} echo 22: ${x///.} - echo 23: ${x//#1/9} - echo 24: ${x//%1/9} - echo 25: ${x//\%1/9} - echo 26: ${x//\\%1/9} - echo 27: ${x//\a/9} - echo 28: ${x//\\a/9} - echo 29: ${x/2/$y} + echo 23: ${x/#1/9} + echo 24: ${x//#1/9} + echo 25: ${x/%1/9} + echo 26: ${x//%1/9} + echo 27: ${x//\%1/9} + echo 28: ${x//\\%1/9} + echo 29: ${x//\a/9} + echo 30: ${x//\\a/9} + echo 31: ${x/2/$y} expected-stdout: - 1: 122321_ab/cde_b/c_1221 + 1: 122321_ab/cde_b/c_1221 . 1222321_ab/cde_b/c_1221 2: 131_ab/cde_b/c_11 3: 1321_ab/cde_b/c_1221 4: 131_ab/cde_b/c_11 @@ -1875,12 +1913,14 @@ 21: 1222321_ab.cde_b.c_1221 22: 1222321_ab/cde_b/c_1221 23: 9222321_ab/cde_b/c_1221 - 24: 1222321_ab/cde_b/c_1229 + 24: 1222321_ab/cde_b/c_1221 25: 1222321_ab/cde_b/c_1229 26: 1222321_ab/cde_b/c_1221 - 27: 1222321_9b/cde_b/c_1221 - 28: 1222321_9b/cde_b/c_1221 - 29: 1xyz22321_ab/cde_b/c_1221 + 27: 1222321_ab/cde_b/c_1221 + 28: 1222321_ab/cde_b/c_1221 + 29: 1222321_9b/cde_b/c_1221 + 30: 1222321_ab/cde_b/c_1221 + 31: 1xyz22321_ab/cde_b/c_1221 --- name: eglob-substrpl-2 description: @@ -1905,6 +1945,7 @@ description: Check substring replacement works with variables and slashes, too stdin: + HOME=/etc pfx=/home/user wd=/home/user/tmp echo "${wd/#$pfx/~}" @@ -1914,9 +1955,9 @@ echo "${wd/#"\$pfx"/~}" echo "${wd/#'\$pfx'/~}" expected-stdout: - ~/tmp + /etc/tmp /home/user/tmp - ~/tmp + /etc/tmp /home/user/tmp /home/user/tmp /home/user/tmp @@ -1925,20 +1966,22 @@ description: More of this, bash fails it (bash4 passes) stdin: + HOME=/etc pfx=/home/user wd=/home/user/tmp echo "${wd/#$(echo /home/user)/~}" echo "${wd/#"$(echo /home/user)"/~}" echo "${wd/#'$(echo /home/user)'/~}" expected-stdout: - ~/tmp - ~/tmp + /etc/tmp + /etc/tmp /home/user/tmp --- name: eglob-substrpl-3c description: Even more weird cases stdin: + HOME=/etc pfx=/home/user wd='$pfx/tmp' echo 1: ${wd/#$pfx/~} @@ -1964,31 +2007,31 @@ echo 17: ${ts/+($tp)/$tr} echo 18: ${ts/+($tp)/c/d} echo 19: ${ts/+($tp)/c\/d} - echo 25: ${ts//a\/b/$tr} - echo 26: ${ts//a\/b/\$tr} - echo 27: ${ts//$tp/$tr} - echo 28: ${ts//$tp/c/d} - echo 29: ${ts//$tp/c\/d} - echo 30: ${ts//+(a\/b)/$tr} - echo 31: ${ts//+(a\/b)/\$tr} - echo 32: ${ts//+($tp)/$tr} - echo 33: ${ts//+($tp)/c/d} - echo 34: ${ts//+($tp)/c\/d} + echo 20: ${ts//a\/b/$tr} + echo 21: ${ts//a\/b/\$tr} + echo 22: ${ts//$tp/$tr} + echo 23: ${ts//$tp/c/d} + echo 24: ${ts//$tp/c\/d} + echo 25: ${ts//+(a\/b)/$tr} + echo 26: ${ts//+(a\/b)/\$tr} + echo 27: ${ts//+($tp)/$tr} + echo 28: ${ts//+($tp)/c/d} + echo 29: ${ts//+($tp)/c\/d} tp="+($tp)" - echo 40: ${ts/$tp/$tr} - echo 41: ${ts//$tp/$tr} + echo 30: ${ts/$tp/$tr} + echo 31: ${ts//$tp/$tr} expected-stdout: 1: $pfx/tmp - 2: ~/tmp + 2: /etc/tmp 3: $pfx/tmp - 4: ~/tmp - 5: ~/tmp - 6: ~/tmp + 4: /etc/tmp + 5: /etc/tmp + 6: $pfx/tmp 7: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp) 8: $tra/b$tp$tp_a/b$tp_*(a/b)_*($tp) 9: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp) 10: a/ba/bc/d$tp_a/b$tp_*(a/b)_*($tp) - 11: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp) + 11: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp) 12: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp) 13: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp) 14: c\/da/b$tp$tp_a/b$tp_*(a/b)_*($tp) @@ -1997,21 +2040,21 @@ 17: c/d$tp$tp_a/b$tp_*(a/b)_*($tp) 18: c/d$tp$tp_a/b$tp_*(a/b)_*($tp) 19: c/d$tp$tp_a/b$tp_*(a/b)_*($tp) - 25: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 26: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp) - 27: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 28: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 29: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 30: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 31: $tr$tp$tp_$tr$tp_*($tr)_*($tp) - 32: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 33: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 34: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) - 40: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp) - 41: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp) + 20: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 21: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp) + 22: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 23: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 24: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 25: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 26: $tr$tp$tp_$tr$tp_*($tr)_*($tp) + 27: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 28: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 29: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) + 30: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp) + 31: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp) # This is what GNU bash does: -# 40: c/d$tp$tp_a/b$tp_*(a/b)_*($tp) -# 41: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) +# 30: c/d$tp$tp_a/b$tp_*(a/b)_*($tp) +# 31: c/d$tp$tp_c/d$tp_*(c/d)_*($tp) --- name: eglob-utf8-1 description: @@ -2062,9 +2105,9 @@ name: glob-bad-2 description: Check that symbolic links aren't stat()'d -# breaks on FreeMiNT (cannot unlink dangling symlinks) -# breaks on MSYS (does not support symlinks) # breaks on Dell UNIX 4.0 R2.2 (SVR4) where unlink also fails +# breaks on FreeMiNT (cannot unlink dangling symlinks) +# breaks on MSYS, OS/2 (do not support symlinks) category: !os:mint,!os:msys,!os:svr4.0,!nosymlink file-setup: dir 755 "dir" file-setup: symlink 644 "dir/abc" @@ -2119,7 +2162,7 @@ # breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition) # breaks on Cygwin 1.7 (files are now UTF-16 or something) # breaks on QNX 6.4.1 (says RT) -category: !os:cygwin,!os:darwin,!os:msys,!os:nto +category: !os:cygwin,!os:darwin,!os:msys,!os:nto,!os:os2 need-pass: no file-setup: file 644 "ac" stdin: @@ -2206,9 +2249,20 @@ hi there --- -name: heredoc-4 +name: heredoc-4a +description: + Check that an error occurs if the heredoc-delimiter is missing. +stdin: ! + cat << EOF + hi + there +expected-exit: e > 0 +expected-stderr-pattern: /.*/ +--- +name: heredoc-4an description: Check that an error occurs if the heredoc-delimiter is missing. +arguments: !-n! stdin: ! cat << EOF hi @@ -2216,6 +2270,23 @@ expected-exit: e > 0 expected-stderr-pattern: /.*/ --- +name: heredoc-4b +description: + Check that an error occurs if the heredoc is missing. +stdin: ! + cat << EOF +expected-exit: e > 0 +expected-stderr-pattern: /.*/ +--- +name: heredoc-4bn +description: + Check that an error occurs if the heredoc is missing. +arguments: !-n! +stdin: ! + cat << EOF +expected-exit: e > 0 +expected-stderr-pattern: /.*/ +--- name: heredoc-5 description: Check that backslash quotes a $, ` and \ and kills a \newline @@ -2312,6 +2383,8 @@ tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$bar tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<-foo tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")" + tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"A $(echo "foo bar") B" + tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$b\$b$bar expected-stdout: sbb sbb @@ -2321,6 +2394,9 @@ $one -sbb sbb one + A sbb one B + $o$oone + onm --- name: heredoc-9b description: @@ -2410,7 +2486,7 @@ print -r -- "| ${v//$'\n'/^} |" expected-stdout: function foo { - vc=<<-EOF + vc=<<-EOF =c $x \x40= EOF @@ -2463,11 +2539,11 @@ print -r -- "| ${v//$'\n'/^} |" expected-stdout: function foo { - vc=<<- + vc=<<- =c $x \x40= << - vd=<<-"" + vd=<<-"" =d $x \x40= @@ -2525,6 +2601,46 @@ 3 baz a b baz 3 bla "a b" bla --- +name: heredoc-14 +description: + Check that using multiple here documents works +stdin: + foo() { + echo "got $(cat) on stdin" + echo "got $(cat <&4) on fd#4" + echo "got $(cat <&5) on fd#5" + } + bar() { + foo 4<<-a <<-b 5<<-c + four + a + zero + b + five + c + } + x=$(typeset -f bar) + eval "$x" + y=$(typeset -f bar) + [[ $x = "$y" ]]; echo $? + typeset -f bar + bar +expected-stdout: + 0 + bar() { + foo 4<<-a <<-b 5<<-c + four + a + zero + b + five + c + + } + got zero on stdin + got four on fd#4 + got five on fd#5 +--- name: heredoc-comsub-1 description: Tests for here documents in COMSUB, taken from Austin ML @@ -2569,6 +2685,32 @@ expected-stdout: = these parens \( ) are a problem = --- +name: heredoc-comsub-5 +description: + Check heredoc and COMSUB mixture in input +stdin: + prefix() { sed -e "s/^/$1:/"; } + XXX() { echo x-en; } + YYY() { echo y-es; } + + prefix A <; showargs 3 $a) + (IFS=: b=\<:\>; showargs 4 $b) + print -r '<\>' | (IFS=\\ read f g; showargs 5 "$f" "$g") + print -r '<\\>' | (IFS=\\ read f g; showargs 6 "$f" "$g") + print '<\\\n>' | (IFS=\\ read f g; showargs 7 "$f" "$g") + print -r '<\>' | (IFS=\\ read f; showargs 8 "$f") + print -r '<\\>' | (IFS=\\ read f; showargs 9 "$f") + print '<\\\n>' | (IFS=\\ read f; showargs 10 "$f") + print -r '<\>' | (IFS=\\ read -r f g; showargs 11 "$f" "$g") + print -r '<\\>' | (IFS=\\ read -r f g; showargs 12 "$f" "$g") + print '<\\\n>' | (IFS=\\ read -r f g; showargs 13 "$f" "$g") + print -r '<\>' | (IFS=\\ read -r f; showargs 14 "$f") + print -r '<\\>' | (IFS=\\ read -r f; showargs 15 "$f") + print '<\\\n>' | (IFS=\\ read -r f; showargs 16 "$f") + print -r '<:>' | (IFS=: read f g; showargs 17 "$f" "$g") + print -r '<::>' | (IFS=: read f g; showargs 18 "$f" "$g") + print '<:\n>' | (IFS=: read f g; showargs 19 "$f" "$g") + print -r '<:>' | (IFS=: read f; showargs 20 "$f") + print -r '<::>' | (IFS=: read f; showargs 21 "$f") + print '<:\n>' | (IFS=: read f; showargs 22 "$f") + print -r '<:>' | (IFS=: read -r f g; showargs 23 "$f" "$g") + print -r '<::>' | (IFS=: read -r f g; showargs 24 "$f" "$g") + print '<:\n>' | (IFS=: read -r f g; showargs 25 "$f" "$g") + print -r '<:>' | (IFS=: read -r f; showargs 26 "$f") + print -r '<::>' | (IFS=: read -r f; showargs 27 "$f") + print '<:\n>' | (IFS=: read -r f; showargs 28 "$f") +expected-stdout: + 3 [<] [>] + 4 [<] [>] + 5 [<] [>] + 6 [<] [>] + 7 [<>] [] + 8 [<>] + 9 [<\>] + 10 [<>] + 11 [<] [>] + 12 [<] [\>] + 13 [<] [] + 14 [<\>] + 15 [<\\>] + 16 [<] + 17 [<] [>] + 18 [<] [:>] + 19 [<] [] + 20 [<:>] + 21 [<::>] + 22 [<] + 23 [<] [>] + 24 [<] [:>] + 25 [<] [] + 26 [<:>] + 27 [<::>] + 28 [<] +--- name: read-ksh-1 description: If no var specified, REPLY is used @@ -4935,8 +5147,8 @@ need to be moved out of the switch to before findcom() is called - I don't know what this will break. stdin: - : ${PWD:-`pwd 2> /dev/null`} - : ${PWD:?"PWD not set - can't do test"} + : "${PWD:-`pwd 2> /dev/null`}" + : "${PWD:?"PWD not set - cannot do test"}" mkdir Y cat > Y/xxxscript << EOF #!/bin/sh @@ -5474,7 +5686,7 @@ stdin: print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \ 'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \ - done >env; chmod +x env; PATH=.:$PATH + done >env; chmod +x env; PATH=.$PATHSEP$PATH foo=bar readonly foo foo=stuff env | grep '^foo' @@ -6180,7 +6392,7 @@ stdin: print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \ 'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \ - done >env; chmod +x env; PATH=.:$PATH + done >env; chmod +x env; PATH=.$PATHSEP$PATH FOO=bar exec env expected-stdout-pattern: /(^|.*\n)FOO=bar\n/ @@ -6192,7 +6404,7 @@ stdin: print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \ 'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \ - done >env; chmod +x env; PATH=.:$PATH + done >env; chmod +x env; PATH=.$PATHSEP$PATH env >bar1 FOO=bar exec; env >bar2 cmp -s bar1 bar2 @@ -6252,6 +6464,41 @@ /bad substitution/ expected-exit: 1 --- +name: xxx-variable-syntax-4 +description: + Not all kinds of trims are currently impossible, check those who do +stdin: + foo() { + echo "<$*> X${*:+ }X" + } + foo a b + foo "" c + foo "" + foo "" "" + IFS=: + foo a b + foo "" c + foo "" + foo "" "" + IFS= + foo a b + foo "" c + foo "" + foo "" "" +expected-stdout: + X X + < c> X X + <> XX + < > X X + X X + <:c> X X + <> XX + <:> X X + X X + X X + <> XX + <> XX +--- name: xxx-substitution-eval-order description: Check order of evaluation of expressions @@ -6377,18 +6624,44 @@ description: Check suppresion of error message with null string. According to POSIX, it shouldn't print the error as 'word' isn't ommitted. - ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error, - that's why the condition is reversed. + ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error. stdin: unset foo x= echo x${foo?$x} expected-exit: 1 -# POSIX -#expected-fail: yes -#expected-stderr-pattern: !/not set/ -# common use -expected-stderr-pattern: /parameter null or not set/ +expected-stderr-pattern: !/not set/ +--- +name: xxx-param-subst-qmark-namespec +description: + Check special names are output correctly +stdin: + doit() { + "$__progname" -c "$@" >o1 2>o2 + rv=$? + echo RETVAL: $rv + sed -e "s^${__progname%.exe}\.*e*x*e*: PROG: " -e 's/^/STDOUT: /g' env print '#!'"$__progname"'\nexit 1' >false chmod +x env false - PATH=.:$PATH + PATH=.$PATHSEP$PATH set -ex env false && echo something echo END @@ -6454,7 +6739,7 @@ print '#!'"$__progname"'\nexit 1' >false print '#!'"$__progname"'\nexit 0' >true chmod +x env false - PATH=.:$PATH + PATH=.$PATHSEP$PATH set -ex if env true; then env false && echo something @@ -6898,6 +7183,131 @@ 2- 1 1 1 = 3- 0 0 0 = --- +name: test-stnze-1 +description: + Check that the short form [ $x ] works +stdin: + i=0 + [ -n $x ] + rv=$?; echo $((++i)) $rv + [ $x ] + rv=$?; echo $((++i)) $rv + [ -n "$x" ] + rv=$?; echo $((++i)) $rv + [ "$x" ] + rv=$?; echo $((++i)) $rv + x=0 + [ -n $x ] + rv=$?; echo $((++i)) $rv + [ $x ] + rv=$?; echo $((++i)) $rv + [ -n "$x" ] + rv=$?; echo $((++i)) $rv + [ "$x" ] + rv=$?; echo $((++i)) $rv + x='1 -a 1 = 2' + [ -n $x ] + rv=$?; echo $((++i)) $rv + [ $x ] + rv=$?; echo $((++i)) $rv + [ -n "$x" ] + rv=$?; echo $((++i)) $rv + [ "$x" ] + rv=$?; echo $((++i)) $rv +expected-stdout: + 1 0 + 2 1 + 3 1 + 4 1 + 5 0 + 6 0 + 7 0 + 8 0 + 9 1 + 10 1 + 11 0 + 12 0 +--- +name: test-stnze-2 +description: + Check that the short form [[ $x ]] works (ksh93 extension) +stdin: + i=0 + [[ -n $x ]] + rv=$?; echo $((++i)) $rv + [[ $x ]] + rv=$?; echo $((++i)) $rv + [[ -n "$x" ]] + rv=$?; echo $((++i)) $rv + [[ "$x" ]] + rv=$?; echo $((++i)) $rv + x=0 + [[ -n $x ]] + rv=$?; echo $((++i)) $rv + [[ $x ]] + rv=$?; echo $((++i)) $rv + [[ -n "$x" ]] + rv=$?; echo $((++i)) $rv + [[ "$x" ]] + rv=$?; echo $((++i)) $rv + x='1 -a 1 = 2' + [[ -n $x ]] + rv=$?; echo $((++i)) $rv + [[ $x ]] + rv=$?; echo $((++i)) $rv + [[ -n "$x" ]] + rv=$?; echo $((++i)) $rv + [[ "$x" ]] + rv=$?; echo $((++i)) $rv +expected-stdout: + 1 1 + 2 1 + 3 1 + 4 1 + 5 0 + 6 0 + 7 0 + 8 0 + 9 0 + 10 0 + 11 0 + 12 0 +--- +name: test-numeq +description: + Check numeric -eq works (R40d regression); spotted by Martijn Dekker +stdin: + tst() { + eval "$2" + case $? in + (0) echo yepp 0 \#"$*" ;; + (1) echo nope 1 \#"$*" ;; + (2) echo terr 2 \#"$*" ;; + (*) echo wtf\? $? \#"$*" ;; + esac + } + tst 1 'test 2 -eq 2' + tst 2 'test 2 -eq 2a' + tst 3 'test 2 -eq 3' + tst 4 'test 2 -ne 2' + tst 5 'test 2 -ne 2a' + tst 6 'test 2 -ne 3' + tst 7 'test \! 2 -eq 2' + tst 8 'test \! 2 -eq 2a' + tst 9 'test \! 2 -eq 3' +expected-stdout: + yepp 0 #1 test 2 -eq 2 + terr 2 #2 test 2 -eq 2a + nope 1 #3 test 2 -eq 3 + nope 1 #4 test 2 -ne 2 + terr 2 #5 test 2 -ne 2a + yepp 0 #6 test 2 -ne 3 + nope 1 #7 test \! 2 -eq 2 + terr 2 #8 test \! 2 -eq 2a + yepp 0 #9 test \! 2 -eq 3 +expected-stderr-pattern: + /bad number/ +--- name: mkshrc-1 description: Check that ~/.mkshrc works correctly. @@ -7198,7 +7608,7 @@ set -A anzahl -- foo/* echo got ${#anzahl[*]} files chmod +x foo/* - export PATH=$(pwd)/foo:$PATH + export PATH=$(pwd)/foo$PATHSEP$PATH "$__progname" -c 'fnord' echo = "$__progname" -c 'fnord; fnord; fnord; fnord' @@ -7368,159 +7778,61 @@ name: aliases-1 description: Check if built-in shell aliases are okay -category: !android,!arge stdin: alias typeset -f expected-stdout: - autoload='typeset -fu' - functions='typeset -f' - hash='alias -t' - history='fc -l' - integer='typeset -i' - local=typeset - login='exec login' - nameref='typeset -n' + autoload='\typeset -fu' + functions='\typeset -f' + hash='\builtin alias -t' + history='\builtin fc -l' + integer='\typeset -i' + local='\typeset' + login='\exec login' + nameref='\typeset -n' nohup='nohup ' - r='fc -e -' - source='PATH=$PATH:. command .' - stop='kill -STOP' - type='whence -v' ---- -name: aliases-1-hartz4 -description: - Check if built-in shell aliases are okay -category: android,arge -stdin: - alias - typeset -f -expected-stdout: - autoload='typeset -fu' - functions='typeset -f' - hash='alias -t' - history='fc -l' - integer='typeset -i' - local=typeset - login='exec login' - nameref='typeset -n' - nohup='nohup ' - r='fc -e -' - source='PATH=$PATH:. command .' - type='whence -v' ---- -name: aliases-2a -description: - Check if “set -o sh” disables built-in aliases (except a few) -category: disabled -arguments: !-o!sh! -stdin: - alias - typeset -f -expected-stdout: - integer='typeset -i' - local=typeset ---- -name: aliases-3a -description: - Check if running as sh disables built-in aliases (except a few) -category: disabled -stdin: - cp "$__progname" sh - ./sh -c 'alias; typeset -f' - rm -f sh -expected-stdout: - integer='typeset -i' - local=typeset + r='\builtin fc -e -' + type='\builtin whence -v' --- name: aliases-2b description: Check if “set -o sh” does not influence built-in aliases -category: !android,!arge arguments: !-o!sh! stdin: alias typeset -f expected-stdout: - autoload='typeset -fu' - functions='typeset -f' - hash='alias -t' - history='fc -l' - integer='typeset -i' - local=typeset - login='exec login' - nameref='typeset -n' + autoload='\typeset -fu' + functions='\typeset -f' + hash='\builtin alias -t' + history='\builtin fc -l' + integer='\typeset -i' + local='\typeset' + login='\exec login' + nameref='\typeset -n' nohup='nohup ' - r='fc -e -' - source='PATH=$PATH:. command .' - stop='kill -STOP' - type='whence -v' + r='\builtin fc -e -' + type='\builtin whence -v' --- name: aliases-3b description: Check if running as sh does not influence built-in aliases -category: !android,!arge -stdin: - cp "$__progname" sh - ./sh -c 'alias; typeset -f' - rm -f sh -expected-stdout: - autoload='typeset -fu' - functions='typeset -f' - hash='alias -t' - history='fc -l' - integer='typeset -i' - local=typeset - login='exec login' - nameref='typeset -n' - nohup='nohup ' - r='fc -e -' - source='PATH=$PATH:. command .' - stop='kill -STOP' - type='whence -v' ---- -name: aliases-2b-hartz4 -description: - Check if “set -o sh” does not influence built-in aliases -category: android,arge -arguments: !-o!sh! -stdin: - alias - typeset -f -expected-stdout: - autoload='typeset -fu' - functions='typeset -f' - hash='alias -t' - history='fc -l' - integer='typeset -i' - local=typeset - login='exec login' - nameref='typeset -n' - nohup='nohup ' - r='fc -e -' - source='PATH=$PATH:. command .' - type='whence -v' ---- -name: aliases-3b-hartz4 -description: - Check if running as sh does not influence built-in aliases -category: android,arge stdin: cp "$__progname" sh ./sh -c 'alias; typeset -f' rm -f sh expected-stdout: - autoload='typeset -fu' - functions='typeset -f' - hash='alias -t' - history='fc -l' - integer='typeset -i' - local=typeset - login='exec login' - nameref='typeset -n' + autoload='\typeset -fu' + functions='\typeset -f' + hash='\builtin alias -t' + history='\builtin fc -l' + integer='\typeset -i' + local='\typeset' + login='\exec login' + nameref='\typeset -n' nohup='nohup ' - r='fc -e -' - source='PATH=$PATH:. command .' - type='whence -v' + r='\builtin fc -e -' + type='\builtin whence -v' --- name: aliases-cmdline description: @@ -7543,7 +7855,7 @@ } foo expected-stdout: - funktion + makro --- name: aliases-funcdef-2 description: @@ -7555,7 +7867,7 @@ } foo expected-stdout: - funktion + makro --- name: aliases-funcdef-3 description: @@ -7577,8 +7889,8 @@ :|| local() { :; } alias local expected-stdout: - local=typeset - local=typeset + local='\typeset' + local='\typeset' --- name: arrays-1 description: @@ -8300,7 +8612,7 @@ name: varexpand-substr-3 description: Check that some things that work in bash fail. - This is by design. And that some things fail in both. + This is by design. Oh and vice versa, nowadays. stdin: export x=abcdefghi n=2 "$__progname" -c 'echo v${x:(n)}x' @@ -8308,12 +8620,13 @@ "$__progname" -c 'echo x${x:n}x' "$__progname" -c 'echo y${x:}x' "$__progname" -c 'echo z${x}x' + # next fails only in bash "$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $? expected-stdout: vcdefghix wcdefghix zabcdefghix - 1 + 0 expected-stderr-pattern: /x:n.*bad substitution.*\n.*bad substitution/ --- @@ -8445,6 +8758,7 @@ Ensure concatenating behaviour matches other shells stdin: showargs() { for s_arg in "$@"; do echo -n "<$s_arg> "; done; echo .; } + showargs 0 ""$@ x=; showargs 1 "$x"$@ set A; showargs 2 "${@:+}" n() { echo "$#"; } @@ -8464,6 +8778,7 @@ n "$@" n "$@""$e" expected-stdout: + <0> <> . <1> <> . <2> <> . 2 @@ -8479,13 +8794,36 @@ 0 1 --- +name: varexpand-funny-chars +description: + Check some characters + XXX \uEF80 is asymmetric, possibly buggy so we don’t check this +stdin: + x=$'<\x00>'; typeset -p x + x=$'<\x01>'; typeset -p x + x=$'<\u0000>'; typeset -p x + x=$'<\u0001>'; typeset -p x +expected-stdout: + typeset x='<' + typeset x=$'<\001>' + typeset x='<' + typeset x=$'<\001>' +--- name: print-funny-chars description: Check print builtin's capability to output designated characters stdin: print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>' + print '<\x00>' + print '<\x01>' + print '<\u0000>' + print '<\u0001>' expected-stdout: + <> + <> + <> + <> --- name: print-bksl-c description: @@ -8517,6 +8855,56 @@ {220->> Bitte keine Werbung einwerfen! << } {220 Who do you wanna pretend to be today? } --- +name: print-crlf +description: + Check that CR+LF is shown and read as-is +stdin: + cat >foo <<-'EOF' + x='bar + ' # + echo .${#x} # + if test x"$KSH_VERSION" = x""; then # + printf '<%s>' "$x" # + else # + print -nr -- "<$x>" # + fi # + EOF + echo "[$("$__progname" foo)]" + "$__progname" foo | while IFS= read -r line; do + print -r -- "{$line}" + done +expected-stdout: + [.5 + ] + {.5} + {foo <<-'EOF' + x='bar + ' # + echo .${#x} # + if test x"$KSH_VERSION" = x""; then # + printf '<%s>' "$x" # + else # + print -nr -- "<$x>" # + fi # + EOF + echo "[$("$__progname" foo)]" + "$__progname" foo | while IFS= read -r line; do + print -r -- "{$line}" + done +expected-stdout: + [.4 + ] + {.4} + {/dev/null || echo rab + # alias overrides ksh function alias korn='echo bar' + korn function korn { echo baz } - foo korn - unset -f foo - foo 2>/dev/null || echo rab + # alias temporarily overrides POSIX function + bla() { + echo bfn + } + bla + alias bla='echo bal' + bla + unalias bla + bla expected-stdout: - baz bar - rab + bar + bar + bar + bar + bfn + bal + bfn --- name: bash-function-parens description: @@ -8746,24 +9150,21 @@ echo "$1 {" echo ' echo "bar='\''$0'\'\" echo '}' - echo ${2:-foo} + print -r -- "${2:-foo}" } mk 'function foo' >f-korn mk 'foo ()' >f-dash mk 'function foo ()' >f-bash - mk 'function stop ()' stop >f-stop print '#!'"$__progname"'\nprint -r -- "${0%/f-argh}"' >f-argh chmod +x f-* u=$(./f-argh) x="korn: $(./f-korn)"; echo "${x/@("$u")/.}" x="dash: $(./f-dash)"; echo "${x/@("$u")/.}" x="bash: $(./f-bash)"; echo "${x/@("$u")/.}" - x="stop: $(./f-stop)"; echo "${x/@("$u")/.}" expected-stdout: korn: bar='foo' dash: bar='./f-dash' bash: bar='./f-bash' - stop: bar='./f-stop' --- name: integer-base-one-1 description: @@ -9708,7 +10109,7 @@ stdin: print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \ 'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \ - done >env; chmod +x env; PATH=.:$PATH + done >env; chmod +x env; PATH=.$PATHSEP$PATH function k { if [ x$FOO != xbar ]; then echo 1 @@ -9770,25 +10171,33 @@ Verify that file descriptors > 2 are private for Korn shells AT&T ksh93 does this still, which means we must keep it as well category: shell:legacy-no -file-setup: file 644 "test.sh" - echo >&3 Fowl stdin: - exec 3>&1 - "$__progname" test.sh + cat >cld <<-EOF + #!$__perlname + open(my \$fh, ">&", 9) or die "E: open \$!"; + syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!"; + EOF + chmod +x cld + exec 9>&1 + ./cld expected-exit: e != 0 expected-stderr-pattern: - /bad file descriptor/ + /E: open / --- name: fd-cloexec-2 description: Verify that file descriptors > 2 are not private for POSIX shells See Debian Bug #154540, Closes: #499139 -file-setup: file 644 "test.sh" - echo >&3 Fowl stdin: - test -n "$POSH_VERSION" || set -o sh - exec 3>&1 - "$__progname" test.sh + cat >cld <<-EOF + #!$__perlname + open(my \$fh, ">&", 9) or die "E: open \$!"; + syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!"; + EOF + chmod +x cld + test -n "$POSH_VERSION" || set -o posix + exec 9>&1 + ./cld expected-stdout: Fowl --- @@ -9796,11 +10205,15 @@ description: Verify that file descriptors > 2 are not private for LEGACY KSH category: shell:legacy-yes -file-setup: file 644 "test.sh" - echo >&3 Fowl stdin: - exec 3>&1 - "$__progname" test.sh + cat >cld <<-EOF + #!$__perlname + open(my \$fh, ">&", 9) or die "E: open \$!"; + syswrite(\$fh, "Fowl\\n", 5) or die "E: write \$!"; + EOF + chmod +x cld + exec 9>&1 + ./cld expected-stdout: Fowl --- @@ -9868,7 +10281,7 @@ is a must (a non-recursive parser cannot pass all three of these test cases, especially the ‘#’ is difficult) stdin: - print '#!'"$__progname"'\necho 1234' >id; chmod +x id; PATH=.:$PATH + print '#!'"$__progname"'\necho 1234' >id; chmod +x id; PATH=.$PATHSEP$PATH echo $(typeset -i10 x=16#20; echo $x) echo $(typeset -Uui16 x=16#$(id -u) ) . @@ -10180,7 +10593,9 @@ } inline_TWHILE() { i=1 - while let] " i < 10 " + while { + \let] " i < 10 " + } do echo $i let ++i @@ -10190,20 +10605,22 @@ 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 @@ -10212,13 +10629,13 @@ 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 @@ -10290,7 +10707,7 @@ EOFN } inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() { - cat >|bar <<"EOFN" + cat >|bar <<"EOFN" foo EOFN @@ -10301,7 +10718,7 @@ EOFN ); } function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP { - x=$(cat >|bar <<"EOFN" + x=$(cat >|bar <<"EOFN" foo EOFN ) @@ -10312,7 +10729,7 @@ EOFN )|tr u x); } function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP { - x=$(( cat >|bar <<"EOFN" + x=$(( cat >|bar <<"EOFN" foo EOFN ) | tr u x ) @@ -10323,7 +10740,7 @@ EOFI } inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() { - cat >bar <<-EOFI + cat >bar <<-EOFI foo EOFI @@ -10334,7 +10751,7 @@ EOFI ); } function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP { - x=$(cat >bar <<-EOFI + x=$(cat >bar <<-EOFI foo EOFI ) @@ -10345,7 +10762,7 @@ EOFI )|tr u x); } function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP { - x=$(( cat >bar <<-EOFI + x=$(( cat >bar <<-EOFI foo EOFI ) | tr u x ) @@ -10436,7 +10853,7 @@ EOFN); echo $x } inline_heredoc_closed() { - x=$(cat <&1 <<-EOF + [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF 1,/^\$/d 0a $x @@ -10548,7 +10965,7 @@ fi ); } function comsub_patch_motd { - x=$(x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF + x=$(x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF 1,/^\$/d 0a $x @@ -10575,7 +10992,7 @@ fi )|tr u x); } function reread_patch_motd { - x=$(( x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF + x=$(( x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF 1,/^\$/d 0a $x @@ -10594,7 +11011,7 @@ case x in (x) a+=b - set -A c+ -- d e + \set -A c+ -- d e ;; esac } @@ -10604,7 +11021,7 @@ esac ); } function comsub_wdarrassign { - x=$(case x in (x) a+=b ; set -A c+ -- d e ;; esac ) + x=$(case x in (x) a+=b ; \set -A c+ -- d e ;; esac ) } function reread_wdarrassign { x=$(( case x in @@ -10612,7 +11029,7 @@ esac )|tr u x); } function reread_wdarrassign { - x=$(( case x in (x) a+=b ; set -A c+ -- d e ;; esac ) | tr u x ) + x=$(( case x in (x) a+=b ; \set -A c+ -- d e ;; esac ) | tr u x ) } --- name: comsub-torture-io @@ -10832,7 +11249,9 @@ } inline_TWHILE() { i=1 - while let] " i < 10 " >&3 + while { + \let] " i < 10 " + } >&3 do echo $i let ++i @@ -10842,20 +11261,22 @@ 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 @@ -10864,13 +11285,13 @@ 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 @@ -11002,96 +11423,6 @@ typeset t=$'foo\n\n' this used to segfault. --- -name: test-stnze-1 -description: - Check that the short form [ $x ] works -stdin: - i=0 - [ -n $x ] - rv=$?; echo $((++i)) $rv - [ $x ] - rv=$?; echo $((++i)) $rv - [ -n "$x" ] - rv=$?; echo $((++i)) $rv - [ "$x" ] - rv=$?; echo $((++i)) $rv - x=0 - [ -n $x ] - rv=$?; echo $((++i)) $rv - [ $x ] - rv=$?; echo $((++i)) $rv - [ -n "$x" ] - rv=$?; echo $((++i)) $rv - [ "$x" ] - rv=$?; echo $((++i)) $rv - x='1 -a 1 = 2' - [ -n $x ] - rv=$?; echo $((++i)) $rv - [ $x ] - rv=$?; echo $((++i)) $rv - [ -n "$x" ] - rv=$?; echo $((++i)) $rv - [ "$x" ] - rv=$?; echo $((++i)) $rv -expected-stdout: - 1 0 - 2 1 - 3 1 - 4 1 - 5 0 - 6 0 - 7 0 - 8 0 - 9 1 - 10 1 - 11 0 - 12 0 ---- -name: test-stnze-2 -description: - Check that the short form [[ $x ]] works (ksh93 extension) -stdin: - i=0 - [[ -n $x ]] - rv=$?; echo $((++i)) $rv - [[ $x ]] - rv=$?; echo $((++i)) $rv - [[ -n "$x" ]] - rv=$?; echo $((++i)) $rv - [[ "$x" ]] - rv=$?; echo $((++i)) $rv - x=0 - [[ -n $x ]] - rv=$?; echo $((++i)) $rv - [[ $x ]] - rv=$?; echo $((++i)) $rv - [[ -n "$x" ]] - rv=$?; echo $((++i)) $rv - [[ "$x" ]] - rv=$?; echo $((++i)) $rv - x='1 -a 1 = 2' - [[ -n $x ]] - rv=$?; echo $((++i)) $rv - [[ $x ]] - rv=$?; echo $((++i)) $rv - [[ -n "$x" ]] - rv=$?; echo $((++i)) $rv - [[ "$x" ]] - rv=$?; echo $((++i)) $rv -expected-stdout: - 1 1 - 2 1 - 3 1 - 4 1 - 5 0 - 6 0 - 7 0 - 8 0 - 9 0 - 10 0 - 11 0 - 12 0 ---- name: event-subst-3 description: Check that '!' substitution in noninteractive mode is ignored @@ -11103,7 +11434,7 @@ #! /bin/sh echo si stdin: - export PATH=.:$PATH + export PATH=.$PATHSEP$PATH falsetto echo yeap !false @@ -11133,7 +11464,7 @@ #! /bin/sh echo si stdin: - export PATH=.:$PATH + export PATH=.$PATHSEP$PATH falsetto echo yeap !false @@ -11457,6 +11788,16 @@ expected-stdout: fxbar 0 --- +name: better-parens-5 +description: + Another corner case +stdin: + ( (echo 'fo o$bar' "baz\$bla\"" m\$eh) | tr a A) + ((echo 'fo o$bar' "baz\$bla\"" m\$eh) | tr a A) +expected-stdout: + fo o$bAr bAz$blA" m$eh + fo o$bAr bAz$blA" m$eh +--- name: echo-test-1 description: Test what the echo builtin does (mksh) @@ -11855,6 +12196,77 @@ after 0='swc' 1='二' 2='' = done --- +name: command-pvV-posix-priorities +description: + For POSIX compatibility, command -v should find aliases and reserved + words, and command -p[vV] should find aliases, reserved words, and + builtins over external commands. +stdin: + PATH=/bin:/usr/bin + alias foo="bar baz" + bar() { :; } + for word in 'if' 'foo' 'bar' 'set' 'true'; do + command -v "$word" + command -pv "$word" + command -V "$word" + command -pV "$word" + done +expected-stdout: + if + if + if is a reserved word + if is a reserved word + alias foo='bar baz' + alias foo='bar baz' + foo is an alias for 'bar baz' + foo is an alias for 'bar baz' + bar + bar + bar is a function + bar is a function + set + set + set is a special shell builtin + set is a special shell builtin + true + true + true is a shell builtin + true is a shell builtin +--- +name: whence-preserve-tradition +description: + This regression test is to ensure that the POSIX compatibility + changes for 'command' (see previous test) do not affect traditional + 'whence' behaviour. +category: os:mirbsd +stdin: + PATH=/bin:/usr/bin + alias foo="bar baz" + bar() { :; } + for word in 'if' 'foo' 'bar' 'set' 'true'; do + whence "$word" + whence -p "$word" + whence -v "$word" + whence -pv "$word" + done +expected-stdout: + if + if is a reserved word + if not found + 'bar baz' + foo is an alias for 'bar baz' + foo not found + bar + bar is a function + bar not found + set + set is a special shell builtin + set not found + true + /bin/true + true is a shell builtin + true is a tracked alias for /bin/true +--- name: duffs-device description: Check that the compiler did not optimise-break them @@ -11927,7 +12339,7 @@ Copyright (C) 2002 Free Software Foundation, Inc.' EOF chmod +x bash - "$__progname" -xc 'foo=$(./bash --version 2>&1 | head -1); echo "=$foo="' + "$__progname" -xc 'foo=$(./bash --version 2>&1 | sed q); echo "=$foo="' expected-stdout: =GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)= expected-stderr-pattern: @@ -11956,3 +12368,62 @@ [(p:sh)(f1:sh)(f2:sh)] print '(o1:shx)' [(p:sh)(f1:sh)(f2:sh)] set +x --- +name: fksh-flags +description: + Check that FKSH functions have their own shell flags +category: shell:legacy-no +stdin: + [[ $KSH_VERSION = Version* ]] && set +B + function foo { + set +f + set -e + echo 2 "${-/s}" . + } + set -fh + echo 1 "${-/s}" . + foo + echo 3 "${-/s}" . +expected-stdout: + 1 fh . + 2 eh . + 3 fh . +--- +name: fksh-flags-legacy +description: + Check that even FKSH functions share the shell flags +category: shell:legacy-yes +stdin: + [[ $KSH_VERSION = Version* ]] && set +B + foo() { + set +f + set -e + echo 2 "${-/s}" . + } + set -fh + echo 1 "${-/s}" . + foo + echo 3 "${-/s}" . +expected-stdout: + 1 fh . + 2 eh . + 3 eh . +--- +name: fsh-flags +description: + Check that !FKSH functions share the shell flags +stdin: + [[ $KSH_VERSION = Version* ]] && set +B + foo() { + set +f + set -e + echo 2 "${-/s}" . + } + set -fh + echo 1 "${-/s}" . + foo + echo 3 "${-/s}" . +expected-stdout: + 1 fh . + 2 eh . + 3 eh . +--- diff -Nru mksh-50e/debian/.mkshrc mksh-52c/debian/.mkshrc --- mksh-50e/debian/.mkshrc 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/.mkshrc 2016-03-21 12:53:58.000000000 +0100 @@ -1,13 +1,14 @@ +# Debian mksh # Skeleton ~/.mkshrc file adding a level of indirection # check if this is really mksh # {(( -case $KSH_VERSION in +case ${KSH_VERSION:-} in *MIRBSD\ KSH*) ;; *) return 0 ;; esac # } # source the system-wide mkshrc file -[[ -s /etc/mkshrc ]] && . /etc/mkshrc +[[ -s /etc/mkshrc ]] && \. /etc/mkshrc # prepend $debian_chroot support to PS1 p=$'\001' @@ -20,24 +21,29 @@ [[ -z ${debian_chroot:-} && -r /etc/debian_chroot ]] && \ debian_chroot=$( (does not require subscription to post) archived at http://news.gmane.org/gmane.os.miros.mksh and others + (note: several freemail providers have issues with the list; + use GMane (NNTP or web interface) to post in those cases) * https://launchpad.net/mksh @@ -27,38 +32,34 @@ * After installation, it is strongly recommended to copy the file /etc/skel/.mkshrc into your home directory to get a nicer prompt, - pushd/popd/dirs functionality, some aliases, etc. (you can then - customise it; reading the manual page also often helps). - Note that /etc/skel/.mkshrc sources /etc/mkshrc (which is the - upstream-provided file) by default and contains some (commented - out) examples to set the locale and editor. Change the file once - copied into the home if you do not want this. + pushd/popd/dirs/setenv/enable functionality, some aliases, etc. + (you can then customise it; reading the manual page also often + helps). Note that /etc/skel/.mkshrc sources /etc/mkshrc (which + is the upstream-provided file) by default and contains some more + (commented out) examples to set the locale and editor, etc. Change + the file to suit your needs once copied into your home directory. * The MirBSD Korn Shell provides a workable /bin/sh for Debian; installation however is not automatic due to Debian #540512 (a - bug in dash). It is strongly recommended to use its “legacy” - flavour for this, as it uses the host “long” C type for integer - arithmetics, following POSIX, and contains some workarounds for - broken maintainer scripts in Debian packages. Use these commands: + wontfix RC bug in dash). It is strongly recommended to use its + “legacy” flavour for this, as it uses the host “long” C type for + integer arithmetics, enables POSIX mode if called as /bin/sh, and + contains some workarounds for broken Debian maintainer scripts. + First dpkg-reconfigure -plow dash to disable it sitting on /bin/sh, + then use these commands: $ sudo ln -sf lksh /bin/sh - $ sudo ln -sf mksh.1.gz /usr/share/man/man1/sh.1.gz - This will install the mksh manpage for sh(1) since it explains - better what the shell is actually about; lksh(1) contains only - a short summary of differences. + $ sudo ln -sf lksh.1.gz /usr/share/man/man1/sh.1.gz Please note that lksh, as opposed to mksh, is not suited for interactive use (but no regression from dash which also isn’t). * The /bin/lksh binary is used to run legacy ksh88 and pdksh scripts. It is not intended for interactive use, but has a printf(1) builtin. - It can be used as /bin/sh (see above). It however is used to provide - pdksh as transitional package. It will not be registered with the - menu system or set up as login shell. Also, do not use the pdksh - compatibility symlink as login shell. + It is, however, usable as /bin/sh on Debian systems. In contrast, + /bin/mksh-static should be used on initrd/initramfs as shell. * /bin/mksh is built with the following options: - extra hardening flags: +all - glibc (dynamically linked) - - calling as sh or -sh invokes "set -o posix -o sh" * /bin/mksh-static is built with the following options: - extra hardening flags: +all but no PIE @@ -69,7 +70,7 @@ - note that the stack protector is disabled on dietlibc, klibc due to limitations in those operating environments - calling as sh or -sh invokes "set -o posix -o sh" - - -DMKSH_SMALL + - -DMKSH_SMALL -DMKSH_SMALL_BUT_FAST - no locale support; uses getenv on LANG/LC_CTYPE/LC_ALL * /bin/lksh is built with the following options: @@ -84,12 +85,6 @@ network, are harmless and can be ignored: - utf8bom-2 (mksh-full only) - heredoc-tmpfile-8 (on slow machines) - Failures in dollar-quoted-strings or history-subst-4 (#523086) - are errors in dietlibc on the respective architectures. These - have been fixed past Debian squeeze and *buntu oneiric but may - still occur on backports; except history-subst-4 [hppa], they - are tested for, and on failure rebuilt against glibc, on non- - cross non-nocheck builds. Details (generated during package build) can be found below: diff -Nru mksh-50e/debian/changelog mksh-52c/debian/changelog --- mksh-50e/debian/changelog 2015-03-08 00:42:41.000000000 +0100 +++ mksh-52c/debian/changelog 2016-03-21 12:57:26.000000000 +0100 @@ -1,3 +1,208 @@ +mksh (52c-1ubuntu1) xenial; urgency=high + + * Merge from Debian (LP: #1560008), remaining changes: + - Omit dietlibc builds on Ubuntu, where it is not in main + - Maintainer change for Ubuntu + + -- Thorsten Glaser Mon, 21 Mar 2016 12:56:45 +0100 + +mksh (52c-1) unstable; urgency=low + + * New upstream bugfix-only release: + - [tg] Shave 200 bytes off .text by revisiting string pooling + - [tg, J�rg] Fix manpage for ditroff on Schillix + - [tg, wbx] Use sed 1q instead of unportable head(1) + - [tg] Implement underrun debugging tool for area-based memory allocator + - [tg] Fix history underrun when first interactive command is entered + - [tg, bef0rd] Do not misinterpret “${0/}” as “${0//”, fixes segfault + - [tg, Stéphane Chazelas] Fix display problems with special parameters + - [tg, Stéphane Chazelas] Catch attempt to trim $* and $@ with ?, fixes + segfault (millert@ did this in 2004 for ${x[*]} already, so just sync) + - [Martijn Dekker] Fix “command -p” with -Vv to behave as POSIX requires + - [tg, jilles, Oleg Bulatov] Fix recusive parser with active heredocs + - [tg] Flush even syntax-failing or interrupted commands to history + - [tg, fmunozs] Fix invalid memory access for “'\0'” in arithmetics + - [tg] Explicitly reserve SIGEXIT and SIGERR for ksh + - [tg, izabera] Catch missing here documents at EOF even under “set -n” + - [kre, tg] Document Austin#1015 handling (not considered a violation) + - [tg, fmunozs] Fix buffer overread for empty nameref targets + - [tg] Fix warnings pointed out by latest Debian gcc-snapshot + - [tg, Martijn Dekker] Document upcoming set +o changes + - [Martijn Dekker] Expand testsuite for command/whence + * Remove now-irrelevant comment from debian/control + * Update manpage from CVS incorporating lintian spelling fixes + * Bump S-V (no changes needed); use SSL URIs for VCS (lintian) + + -- Thorsten Glaser Sun, 06 Mar 2016 22:03:50 +0100 + +mksh (52b-1) unstable; urgency=high + + * New upstream bugfix-only release: + - [tg] Recognise ksh93 compiled scripts and LZIP compressed files + as binary (i.e. to not run as mksh plaintext script) + - [tg] Document that we will implement locale tracking later + - [tg] Add EEXIST to failback strerror(3) + - [jilles] Make set -C; :>foo race-free + - [tg] Don’t use unset in portable build script + - [tg] Plug warning on GNU/kFreeBSD, GNU/Hurd + - [tg] Document read -a resets the integer base + - [J�rg] Fix manpage: time is not a builtin but a reserved word + - [J�rg, tg] Make exit (and return) eat -1 + - [tg] parse “$( (( … ) … ) … )” correctly (LP#1532621), Jan Palus + - [tg] reduce memory footprint by free(3)ing more aggressively + - [tg] fix buffer overrun (LP#1533394), bugreport by izabera + - [tg] correctly handle nested ADELIM parsing (LP#1453827), Teckids + - [tg] permit “read -A/-a arr[idx]” as long as only one element is + read; fix corruption of array indicēs with this construct + (LP#1533396), discovered by izabera + - [tg] Sanitise OS-provided signal number in even more places + - [tg] As requested by J�rg, be clear manpage advice is for mksh + - [tg] Revert (as it was a regression) POSIX bugfix from R52/2005 + related to accent gravis-style command substitution until POSIX + decides either way (Austin#1015) and upload with high urgency + - [tg] Handle export et al. after command (Austin#351) + - [tg] Catch EPIPE in built-in cat and return as SIGPIPE (LP#1532621) + - [tg] Fix errno in print/echo builtin; optimise that and unbksl + - [tg] Update documentation, point out POSIX violation (Austin#1015) + * Only create /usr/bin/ksh compat symlink if needed (Closes: #807185) + * Add missed changelog entries for upstream to 52-1 entry + * Update debian/copyright and README.Debian + + -- Thorsten Glaser Wed, 20 Jan 2016 23:03:23 +0000 + +mksh (52-1) unstable; urgency=medium + + * Remove parallel=* from DEB_BUILD_OPTIONS for the reproducible people + * Add myself as Maintainer (Closes: #764401) + * Make ed a Recommends (Closes: #803726) + * Update text in README.Debian + * Make .mkshrc alias-safe + * New bugfix upstream release + - [_0bitcount] Move moving external link from mksh(1) to the #ksh + channel homepage linked therein + - [tg] Make setenv “set -u”-safe and fix when invoked with no args + - [tg] Make “typeset -f” output reentrant if name is a reserved word + - [oksh] Zero-pad seconds in “time” output to align columns + - [tg] Check signals and errorlevels from OS to be within bounds + - [komh, tg] Quote and document ‘;’ as PATH separator in some places + - [oksh, tg] Simplify code to call afree() even if arg is NULL + - [tg] Fix tree-printing and reentrancy of multiple here documents + - [tg] Work around LP#1030581 by permitting exactly one space after + - [tg, oksh] Code quality work, cleanups + - [tg] New code for here documents/strings with several bugfixes + - [tg] Stop using issetugid(2) for ±p checks, wrong tool for the job + - [tg] Reintroduce some -o posix changes lost in 2005, plus fixes + - [tg] Make “source” into a built-in command + - [tg] Drop “stop” alias, lksh(1) functionality to auto-unalias + - [tg] Fix \u0000 ignored in $'…' and print + - [tg] Improve portability of Build.sh + - [Jilles Tjoelker] Improve portability of testsuite + - [tg] Fix tilde expansion for some substitutions (izabera, Chet, Geoff) + - [tg] Improve reparsing of ((…) |…) as ( (…) |…) + - [Martijn Dekker] Fix test(1) not returning evaluation errors + - [tg] Fix ${*:+x} constructs (carstenh) + - [tg] Make (( … )) into a compound command (ormaaj) + - [tg] Repair a few parameter substitution expansion mistakes + - [tg] OpenBSD sync: handle integer base out of band like ksh93 does + - [tg] Protect standard code (predefined aliases, internal code, + aliases and functions in dot.mkshrc) from being overridden by + aliases and, in some cases, shell functions (i.e. permit overriding + but ignore it) + - [tg] Implement GNU bash’s enable for dot.mkshrc using magic aliases + to redirect the builtins to external utilities; this differs from + GNU bash in that enable takes precedence over functions + - [tg] Move unaliasing an identifier when defining a POSIX-style + function with the same name into lksh, as compatibility kludge + - [tg] Korn shell style functions now have locally scoped shell options + - [tg, iSKUNK] Change some ASCII-isms to be EBCDIC-aware or pluggable + - [tg, Ypnose] Mention lksh build instructions on manpage and website + - [tg] Overhaul signal handling; support new POSIX NSIG_MAX, add + sysconf(_SC_NSIG) as a later TODO item + - [tg] Fix signal bounds (1 ≤ signum < NSIG) + - [tg] Improve manual pages, especially wrt. standards compliance + - [tg, iSKUNK] Initial EBCDIC work for dot.mkshrc + - [tg, iSKUNK] Add list of z/OS signals to Build.sh + - [tg] Work around the sh(1) backslash-newline problem by moving + the code triggering it out of *.opt and into the consumers + - [colona] Bind another well-known ANSI Del key in the Emacs mode + - [tg] Fix ${foo/*/x} pattern checks, spotted by izabera + - [carstenh] Fix error output of cd function in dot.mkshrc + - [tg] read partial returns in -N and timeout cases + - [tg] Fix $LINENO inside PS1; spotted by carstenh + - [tg] Ensure correct padding of at least 2 spaces in print_columns + - [tg] Note issues with nested complex parameter expansions and + follow-up bugfixes to expect + - [OpenBSD] Some language fixes in documentation; comments + - [tg] Reimplement multi-line command history (Closes: #783978) + fixes + - [Martijn Dekker] Fix command -v for “shell reserved words” + - [tg] In dot.mkshrc make use of latest feature: local options + - [tg] Fix ""$@ to emit a word + - [tg] Change cat(1) hack to look first and not ignore builtin + - [KO Myung-Hun] Begin porting mksh to OS/2 + - [komh, tg] Some generic minor bugfixes from OS/2 porting + - [tg] Document mknod(8) isn’t normally part of mksh(1) + - [tg] Quote arguments to : in build/test scripts as well + - [tg] Add cat(1) hack for printf(1)-as-builtin: always prefer external + - [tg] Explicitly use binary mode for any and all file I/O in stock mksh + - [Ilya Zakharevich] Use termio, not termios(4), on OS/2 + - [tg] Set edchars to sane BSD defaults if any are NUL + - [tg] Implement support for PC scancodes in Vi and Emacs editing mode + - [komh] OS/2 uses ‘;’ as PATH separator plus support drive letters + - [tg] Correct some mistakes in the manual page + - [tg] Fix a bug in the testsuite driver, spotted on EBCDIC systems + * Update “uhr” example + * Bump debhelper as lintian demands + * Update copyright file against code + * Allow build without script(1), despite being Essential, due to #807071 + + -- Thorsten Glaser Sat, 12 Dec 2015 23:42:09 +0000 + +mksh (50f-2) unstable; urgency=medium + + * QA upload. + * Build-depends on libklibc-dev [linux-any] instead of explicitly + listing all linux architectures. + + -- Aurelien Jarno Thu, 03 Dec 2015 22:58:50 +0100 + +mksh (50f-1) unstable; urgency=low + + * QA upload. + * The “adieu not au revoir” upload + * Update to the next release of the R50-stable branch: + - [tg] Add a patch marker for vendor patch versioning to mksh.1 + - [tg] Document some more issues with the current history code + - [tg] Remove some unused code + - [tg] RCSID-only sync with OpenBSD, for bogus and irrelevant changes + - [tg] Also disable field splitting for alias 'local=\typeset' + - [tg] Fix read -n-1 to not be identical to read -N-1 + - [tg] Several fixes and improvements to lksh(1) and mksh(1) manpages + - [tg] More code (int → size_t), comment and testsuite fixes + - [tg] Make dot.mkshrc more robust (LP#1441853) + - [tg] Fix issues with IFS='\' read, found by edualbus + - [enh, tg] Fix integer overflows related to file descriptor parsing, + found by Pawel Wylecial (LP#1440685); reduce memory usage for I/O redirs + - [tg] Document in the manpage how to set ±U according to the current + locale settings via LANG/LC_* parameters (cf. Debian #782225) + - [igli, tg] Some code cleanup and restructuring + - [tg, oksh] Handle number parsing and storing more carefully + * Merge changelogs for forgotten actual uploads: + - mksh (28.0-3) oldstable (etch) + - mksh (40.2-2exp1) experimental + * Merge changelogs for potential uploads: + - mksh (40.9.20120630-8) wheezy + - mksh (50d-6) jessie + * Improve /etc/skel/.mkshrc structure and make it “set -u”-safer + * Remove the pdksh transitional package; document as NEWS entry + * Tell users that only lksh (and mksh-static, for rescue systems, + initramfs, etc.) should be used as sh; document as NEWS entry + * Cosmetic improvements to README, control, build scripts, etc. + * Use -DMKSH_SMALL_BUT_FAST when using -DMKSH_SMALL, as this + benefits almost, if not all, Debian architectures + * Upload to unstable + + -- Thorsten Glaser Wed, 08 Jul 2015 21:27:51 +0200 + mksh (50e-2ubuntu1) vivid; urgency=high * Merge from Debian (LP: #1429469), remaining changes: @@ -62,7 +267,17 @@ -- Thorsten Glaser Sun, 01 Mar 2015 16:38:11 +0000 -mksh (50d-4) unstable; urgency=medium +mksh (50d-6) jessie; urgency=medium + + * QA upload. + * Backport upstream fix: + - [enh, tg] Fix integer overflows related to file descriptor + parsing, found by Pawel Wylecial (LP#1440685) + * Adjust shell version accordingly + + -- Thorsten Glaser Sun, 19 Apr 2015 22:27:16 +0200 + +mksh (50d-5) unstable; urgency=medium * QA upload. * Backport upstream fix: @@ -610,6 +825,16 @@ -- Thorsten Glaser Tue, 30 Oct 2012 21:15:13 +0000 +mksh (40.9.20120630-8) wheezy; urgency=medium + + * Backport upstream fix: + - [tg] SECURITY: make unset HISTFILE actually work + - [enh, tg] Fix integer overflows related to file descriptor + parsing, found by Pawel Wylecial (LP#1440685) + * Adjust shell version accordingly + + -- Thorsten Glaser Sun, 19 Apr 2015 22:22:13 +0200 + mksh (40.9.20120630-7) unstable; urgency=low * The “customer service” upload @@ -650,11 +875,11 @@ -- Thorsten Glaser Sat, 13 Oct 2012 14:56:50 +0000 -mksh (40.9.20120630-3ubuntu1) quantal; urgency=low +mksh (40.9.20120630-3debianderivatethatcannotbenamed1) quantal; urgency=low * The “GNU Chagrin Collection” upload * Merge from Debian unstable. Remaining changes: - - Remove dietlibc-dev build-deps, unnecessary for Ubuntu. + - Remove dietlibc-dev build-deps, unnecessary for Debian derivate that cannot be named. * Move some compile-time checks to run-time (LP#1058035) -- Thorsten Glaser Sat, 29 Sep 2012 22:00:14 +0000 @@ -690,7 +915,7 @@ * Mention /etc/skel/.mkshrc in the pdksh upgrade message * Bump pdksh versioned dep on mksh, to have complete changelog * Rework some more texts and hints - * Say hello to *buntu main, pdksh is gone + * Say hello to debianderivatethatcannotbenamed main, pdksh is gone -- Thorsten Glaser Sat, 30 Jun 2012 17:30:11 +0000 @@ -1051,6 +1276,14 @@ -- Thorsten Glaser Tue, 25 Oct 2011 23:25:17 +0000 +mksh (40.2-2exp1) experimental; urgency=low + + * The “I’ve just had some Kruškovac❣” upload + * Build-Depend on the version of dietlibc in experimental and + upload to experimental, to test the former + + -- Thorsten Glaser Sun, 18 Sep 2011 20:09:57 +0000 + mksh (40.2-2) unstable; urgency=medium * The “Someone bring me Kruškovac❣” upload @@ -2909,6 +3142,13 @@ -- Thorsten Glaser Tue, 21 Nov 2006 21:49:44 +0000 +mksh (28.0-3) oldstable; urgency=high + + * Fix CVE-2008-1845 (unauthenticated local privilege escalation) + using upstream-provided diff + + -- Thorsten Glaser Thu, 17 Apr 2008 21:55:05 +0000 + mksh (28.0-2) unstable; urgency=low * Fix unaligned memory access on IA-64 (same fix was applied diff -Nru mksh-50e/debian/compat mksh-52c/debian/compat --- mksh-50e/debian/compat 2014-10-07 20:40:08.000000000 +0200 +++ mksh-52c/debian/compat 2016-03-21 12:53:58.000000000 +0100 @@ -1 +1 @@ -5 +9 diff -Nru mksh-50e/debian/control mksh-52c/debian/control --- mksh-50e/debian/control 2015-03-08 00:39:05.000000000 +0100 +++ mksh-52c/debian/control 2016-03-21 12:54:22.000000000 +0100 @@ -9,24 +9,25 @@ # paranoid (just before the freeze/release) mode, and whether the testsuite # is run to determine bad builds. Really, do run the testsuite! (Even then, # versioned B-D may be used to remove known bogus versions.) -Build-Depends: bsdmainutils, debhelper (>= 5), ed, - libklibc-dev [alpha amd64 arm64 armel armhf hppa i386 ia64 m68k mips mipsel powerpc powerpcspe ppc64 ppc64el s390 s390x sh4 sparc sparc64 x32], +Build-Depends: bsdmainutils, debhelper (>= 9), ed, +# better to whitelist, but currently klibc is available for linux-any + libklibc-dev [linux-any], # dietlibc-dev (>= 0.33~cvs20111108-5~) [hppa] | dietlibc-doc [hppa], # dietlibc-dev [alpha amd64 arm armeb armel armhf i386 ia64 mips mipsel powerpc powerpcspe ppc64 s390 s390x sparc sparc64], # # also for the testsuite (could switch to C.UTF-8 nowadays, though) locales [!avr32] | belocs-locales-bin [!avr32] # # hard to test against bug Build-Conflicts: dietlibc-dev (<< 0.33~cvs20111108-5~) [hppa] -Standards-Version: 3.9.6 -VCS-git: git://anonscm.debian.org/collab-maint/mksh.git -VCS-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/mksh.git +Standards-Version: 3.9.7 +VCS-git: https://anonscm.debian.org/collab-maint/mksh.git +VCS-Browser: https://anonscm.debian.org/gitweb/?p=collab-maint/mksh.git Package: mksh Architecture: any Multi-Arch: foreign Built-Using: ${mksh:B-U} Depends: ${misc:Depends}, ${shlibs:Depends} -Suggests: ed +Recommends: ed Description: MirBSD Korn Shell mksh is the successor of the Public Domain Korn shell (pdksh), a Bourne/POSIX compatible shell which is largely similar to the @@ -42,44 +43,26 @@ Debian systems (use the /bin/lksh executable) and is a good rescue and initrd shell (consider the /bin/mksh-static executable). . - The mksh binary is a complete, full-featured shell. If called as sh, - it enables a compatibility mode and some more controversial features - from POSIX, but still provides the “consistent across all platforms” - guarantee, using 32-bit integers for arithmetics. + The mksh binary is a complete, full-featured shell. It provides a + “consistent across all platforms” guarantee, using 32-bit integers + for arithmetics, possibly deviating from POSIX. . The mksh-static binary is a version of mksh, linked against klibc or dietlibc (if they exist for that Debian architecture and are usable) and optimised for small code size, for example for use on initrd or - initramfs images, installation or rescue systems, or /bin/sh on slow - architectures. Otherwise, it is similar to the mksh binary. - It omits some leaf features to be even smaller. + initramfs images, installation or rescue systems. Except for omitting + some features to be smaller, it is similar to the mksh binary otherwise. . The lksh binary is a script shell based on mksh intended to run old ksh88 and pdksh scripts, but not for interactive use. When used as /bin/sh it follows POSIX most closely, including use of the host’s “long” C data type for arithmetics. It also contains kludges so it - can run as /bin/sh on Debian, where many packages ship buggy/broken - maintainer scripts, such as including a rudimentary printf(1) builtin. + can run as /bin/sh on Debian beyond what Policy dictates, to work + around bugs in maintainer scripts and LSB init scripts shipped by + many packages, such as including a rudimentary printf(1) builtin, + permitting a shell function to be named stop overriding the default + alias, more loose interpretation of shell extglibs, etc. . A sample ~/.mkshrc is included in /usr/share/doc/mksh/examples and provided as /etc/mkshrc conffile, which is sourced by another file /etc/skel/.mkshrc users are recommended to copy into their home. - -Package: pdksh -Section: oldlibs -Priority: extra -Architecture: all -Multi-Arch: foreign -Depends: ${misc:Depends}, mksh (>= 40.9.20120630) -Description: transitional dummy package to migrate from pdksh to mksh - The Public Domain Korn Shell "pdksh" is a mostly complete AT&T ksh - look-alike, but has been superseded by mksh. This transitional dummy - package exists to fulfil the needs of existing users, dependencies - and build-dependencies by providing pdksh symlinks to the legacy - flavour of mksh. You can safely remove it once you have no remaining - users or scripts depending on pdksh any more (do make sure to check - users' login shells). - . - Note: the mksh legacy flavour is not intended for interactive use. - Please change your login shell to /bin/mksh and copy /etc/skel/.mkshrc - to your home directory. diff -Nru mksh-50e/debian/copyright mksh-52c/debian/copyright --- mksh-50e/debian/copyright 2015-03-08 00:39:05.000000000 +0100 +++ mksh-52c/debian/copyright 2016-03-21 12:53:58.000000000 +0100 @@ -2,7 +2,7 @@ Sat, 28 May 2005 22:02:17 +0000. It was downloaded from: -https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh-R50e.tgz +https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh-R52c.tgz Licence: @@ -11,12 +11,12 @@ is OSI approved, as reproduced below. Binaries of the MirBSD Korn Shell on systems whose libc does -not provide a strlcpy function, as well as mksh-static, will -additionally contain an implementation under the ISC Licence -which is OSI approved and also reproduced below. +not provide a strlcpy function will additionally contain an +implementation under the ISC Licence, which is OSI approved, +and also reproduced below. -All Debian binaries of mksh R39 and up also pull in the UCB code -SLOB printf.c to have a printf(1) builtin (also OSI approved). +The lksh binary includes the UCB printf.c (also OSI approved +and reproduced below). mksh16.xpm is hand-drawn; mksh16lg.xpm and mksh32.xpm are exports of https://www.mirbsd.org/pics/mksh.svg (the mksh logo). @@ -24,15 +24,15 @@ The MirBSD Korn Shell (mksh) is Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012, 2013, 2014, 2015 - Thorsten “mirabilos” Glaser + 2011, 2012, 2013, 2014, 2015, 2016 + mirabilos All rights reserved. The mksh logo is Copyright © 2008, 2009 Lukas U. Copyright © 2008, 2009 - Thorsten “mirabilos” Glaser + mirabilos Provided that these terms and disclaimer and all copyright notices are retained or reproduced in an accompanying document, permission @@ -51,8 +51,8 @@ strlcpy.c is -Copyright (c) 2006, 2008, 2009 - Thorsten Glaser +Copyright (c) 2006, 2008, 2009, 2013 + mirabilos Copyright (c) 1998 Todd C. Miller diff -Nru mksh-50e/debian/meat mksh-52c/debian/meat --- mksh-50e/debian/meat 2015-03-08 00:39:05.000000000 +0100 +++ mksh-52c/debian/meat 2016-03-21 12:53:58.000000000 +0100 @@ -59,7 +59,7 @@ fgrep 'set -A check_categories' builddir/$where/test.sh #echo "From: $startdate" $CC --version 2>&1 | grep ^gcc - echo "Result: $buildinfo (broken&1 | tee utest.log + elif test -z "$(which script 2>/dev/null)"; then + regressed=unattended + echo "WARNING: script(1) not found, results inconclusive." + ./test.sh -v -C regress:no-ctty 2>&1 | tee utest.log else echo "This needs /dev/tty and /dev/ptmx in the chroot." echo >test.wait @@ -187,7 +193,7 @@ cd ../.. return fi - buildinfo=regressed + buildinfo=$regressed resultest=$(grep -a '^[FPT]' utest.log 2>&1 | fgrep -a -v \ -e 'Trying Perl interpreter' \ -e 'Testing mksh for conformance' \ @@ -211,12 +217,23 @@ rm -rf builddir mkdir builddir builddir/full builddir/static builddir/legacy -for where in legacy; do - cp debian/printf.c builddir/$where/ +cp debian/printf.c builddir/legacy/ + +# oh puh-leaze! +DEB_BUILD_OPTIONS_reproducible= +for v in $DEB_BUILD_OPTIONS; do + case $v in + (parallel=*) + ;; + (*) + DEB_BUILD_OPTIONS_reproducible="$DEB_BUILD_OPTIONS_reproducible $v" + ;; + esac done +DEB_BUILD_OPTIONS_reproducible=${DEB_BUILD_OPTIONS_reproducible# } echo "Building the package 'mksh' on '$DEB_BUILD_ARCH' for '$DEB_HOST_ARCH'" \ - "with DEB_BUILD_OPTIONS '$DEB_BUILD_OPTIONS'" + "with DEB_BUILD_OPTIONS '$DEB_BUILD_OPTIONS_reproducible'" echo "Values (not used) from environment: CFLAGS='$CFLAGS' CPPFLAGS='$CPPFLAGS' LDFLAGS='$LDFLAGS'" # parse options @@ -269,8 +286,6 @@ dLDFLAGS="$dLDFLAGS -Wl,--as-needed" # Prevent the build system from adding this, as we provide it already HAVE_CAN_WALL=0; export HAVE_CAN_WALL -# Debian #499139 -dCPPFLAGS="$dCPPFLAGS -DMKSH_BINSHPOSIX -DMKSH_BINSHREDUCED" # avoid needing a Build-Conflicts on libbsd-dev HAVE_LIBUTIL_H=0; export HAVE_LIBUTIL_H @@ -341,7 +356,7 @@ (*) sCFLAGS="$sCFLAGS $x" ;; esac done -sCPPFLAGS=$dCPPFLAGS +sCPPFLAGS="$dCPPFLAGS -DMKSH_BINSHPOSIX -DMKSH_BINSHREDUCED" sLDFLAGS= for x in $dLDFLAGS; do case $x in @@ -350,7 +365,7 @@ esac done sLIBS= -test $small = 0 || sCPPFLAGS="$sCPPFLAGS -DMKSH_SMALL" +test $small = 0 || sCPPFLAGS="$sCPPFLAGS -DMKSH_SMALL -DMKSH_SMALL_BUT_FAST" buildok=0 for x in $static; do echo Building mksh-static with $x; case $x in @@ -426,7 +441,8 @@ # build mksh-legacy lCC=$dCC lCFLAGS=$dCFLAGS -lCPPFLAGS=$dCPPFLAGS +# Debian #499139 +lCPPFLAGS="$dCPPFLAGS -DMKSH_BINSHPOSIX -DMKSH_BINSHREDUCED" lLDFLAGS=$dLDFLAGS lLIBS= # Debian #532343, #539158 @@ -537,7 +553,7 @@ { cat debian/README.Debian echo Build information for mksh R${pkgvsn%%@(-|wtf)*([!-])}: - for v in DEB_BUILD_GNU_TYPE DEB_HOST_GNU_TYPE DEB_BUILD_OPTIONS; do + for v in DEB_BUILD_GNU_TYPE DEB_HOST_GNU_TYPE DEB_BUILD_OPTIONS_reproducible; do eval x=\$$v echo "| $v='$x'" done diff -Nru mksh-50e/debian/mksh.NEWS mksh-52c/debian/mksh.NEWS --- mksh-50e/debian/mksh.NEWS 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/mksh.NEWS 2016-03-21 12:53:58.000000000 +0100 @@ -1,3 +1,15 @@ +mksh (50f-1) unstable; urgency=low + + The pdksh transitional package is gone after two full + releases – pdksh was last in oldoldoldstable. + + The /bin/mksh binary no longer inspects argv[0] to enable + POSIX and kludge modes when called as sh; use mksh-static + (as sh and user shell for initrd) or lksh (as /bin/sh on + general systems) instead. + + -- Thorsten Glaser Sun, 19 Apr 2015 23:08:08 +0200 + mksh (50-1) unstable; urgency=medium The right-hand side of “nameref” (typeset -n) expressions diff -Nru mksh-50e/debian/mksh.postinst mksh-52c/debian/mksh.postinst --- mksh-50e/debian/mksh.postinst 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/mksh.postinst 2016-03-21 12:53:58.000000000 +0100 @@ -33,13 +33,15 @@ case $1 in configure) update-alternatives --install /bin/ksh ksh /bin/mksh 12 \ - --slave /usr/bin/ksh usr.bin.ksh /bin/mksh \ --slave /usr/share/man/man1/ksh.1.gz ksh.1.gz \ /usr/share/man/man1/mksh.1.gz update-alternatives --install /bin/ksh ksh /bin/mksh-static 11 \ - --slave /usr/bin/ksh usr.bin.ksh /bin/mksh-static \ --slave /usr/share/man/man1/ksh.1.gz ksh.1.gz \ /usr/share/man/man1/mksh.1.gz + # create compatibility symlink if necessary + test -e /usr/bin/ksh || test -h /usr/bin/ksh || \ + ln -s /bin/ksh /usr/bin/ksh + add-shell /bin/mksh add-shell /bin/mksh-static ;; diff -Nru mksh-50e/debian/mksh.prerm mksh-52c/debian/mksh.prerm --- mksh-50e/debian/mksh.prerm 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/mksh.prerm 2016-03-21 12:53:58.000000000 +0100 @@ -21,6 +21,9 @@ remove|deconfigure) update-alternatives --remove ksh /bin/mksh update-alternatives --remove ksh /bin/mksh-static + # remove compatibility symlink if broken + test '!' -h /usr/bin/ksh || test -e /usr/bin/ksh || rm -f /usr/bin/ksh + remove-shell /bin/mksh remove-shell /bin/mksh-static ;; diff -Nru mksh-50e/debian/mtest.t mksh-52c/debian/mtest.t --- mksh-50e/debian/mtest.t 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/mtest.t 2016-03-21 12:53:58.000000000 +0100 @@ -34,7 +34,7 @@ name: mtest-brkcontin description: Check that break and continue work; used by test.sh itself - and broken at least once on *buntu + and broken at least once on Debian derivate that cannot be named time-limit: 3 stdin: for x in "echo 1" false "echo 2"; do $x && continue; echo 3; break; done; echo 4 diff -Nru mksh-50e/debian/patches/debian-changes mksh-52c/debian/patches/debian-changes --- mksh-50e/debian/patches/debian-changes 2015-03-08 00:44:26.000000000 +0100 +++ mksh-52c/debian/patches/debian-changes 2016-03-21 13:02:21.000000000 +0100 @@ -1,81 +1,38 @@ Please review changes against upstream code using SCM, see the Vcs-* tags in debian/control for its location. ---- mksh-50e.orig/check.t -+++ mksh-50e/check.t -@@ -30,7 +30,7 @@ - # (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date - - expected-stdout: -- @(#)MIRBSD KSH R50 2015/03/01 -+ @(#)MIRBSD KSH R50 2015/03/01 Debian-2 - description: - Check version of shell. - stdin: -@@ -39,7 +39,7 @@ name: KSH_VERSION - category: shell:legacy-no - --- - expected-stdout: -- @(#)LEGACY KSH R50 2015/03/01 -+ @(#)LEGACY KSH R50 2015/03/01 Debian-2 - description: - Check version of legacy shell. - stdin: ---- mksh-50e.orig/histrap.c -+++ mksh-50e/histrap.c -@@ -563,7 +563,7 @@ sethistfile(const char *name) - return; - - /* if the name is the same as the name we have */ -- if (hname && strcmp(hname, name) == 0) -+ if (hname && name && !strcmp(hname, name)) - return; - - /* -@@ -581,7 +581,8 @@ sethistfile(const char *name) - hist_source->line = 0; - } - -- hist_init(hist_source); -+ if (name) -+ hist_init(hist_source); - } - #endif - -@@ -712,8 +713,10 @@ hist_init(Source *s) - hist_source = s; - - #if HAVE_PERSISTENT_HISTORY -- if ((hname = str_val(global("HISTFILE"))) == NULL) -+ if (((hname = str_val(global("HISTFILE"))) == NULL) || !*hname) { -+ hname = NULL; - return; -+ } - strdupx(hname, hname, APERM); - hs = hist_init_first; - ---- mksh-50e.orig/sh.h -+++ mksh-50e/sh.h -@@ -171,7 +171,7 @@ - #ifdef EXTERN - __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.701.2.4 2015/03/01 15:43:05 tg Exp $"); - #endif --#define MKSH_VERSION "R50 2015/03/01" -+#define MKSH_VERSION "R50 2015/03/01 Debian-2" - - /* arithmetic types: C implementation */ - #if !HAVE_CAN_INTTYPES ---- mksh-50e.orig/var.c -+++ mksh-50e/var.c -@@ -1350,6 +1350,11 @@ unsetspec(struct tbl *vp) - */ - - switch (special(vp->name)) { -+#if HAVE_PERSISTENT_HISTORY -+ case V_HISTFILE: -+ sethistfile(NULL); -+ return; -+#endif - case V_IFS: - setctypes(TC_IFSWS, C_IFS); - ifs0 = ' '; +--- mksh-52c.orig/mksh.1 ++++ mksh-52c/mksh.1 +@@ -1,4 +1,4 @@ +-.\" $MirOS: src/bin/mksh/mksh.1,v 1.392 2016/03/04 18:28:41 tg Exp $ ++.\" $MirOS: src/bin/mksh/mksh.1,v 1.393 2016/03/06 21:01:28 tg Exp $ + .\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $ + .\"- + .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, +@@ -76,7 +76,7 @@ + .\" with -mandoc, it might implement .Mx itself, but we want to + .\" use our own definition. And .Dd must come *first*, always. + .\" +-.Dd $Mdocdate: March 4 2016 $ ++.Dd $Mdocdate: March 6 2016 $ + .\" + .\" Check which macro package we use, and do other -mdoc setup. + .\" +@@ -3097,7 +3097,7 @@ The specified editing command is bound t + .Ar string , + which should consist of a control character + optionally preceded by one of the two prefix characters +-and optionally succeded by a tilde character. ++and optionally succeeded by a tilde character. + Future input of the + .Ar string + will cause the editing command to be immediately invoked. +@@ -3903,7 +3903,7 @@ then leading whitespace will be removed + You might want to use + .Ic while IFS= read \-r foo; do ...; done + for pristine I/O. +-Similarily, when using the ++Similarly, when using the + .Fl a + option, use of the + .Fl r diff -Nru mksh-50e/debian/pdksh.links mksh-52c/debian/pdksh.links --- mksh-50e/debian/pdksh.links 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/pdksh.links 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -bin/lksh bin/pdksh -usr/share/doc/mksh usr/share/doc/pdksh -usr/share/man/man1/lksh.1.gz usr/share/man/man1/pdksh.1.gz diff -Nru mksh-50e/debian/pdksh.lintian mksh-52c/debian/pdksh.lintian --- mksh-50e/debian/pdksh.lintian 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/pdksh.lintian 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -# deliberate, to get pdksh removed from the menus -pdksh: postinst-has-useless-call-to-update-menus diff -Nru mksh-50e/debian/pdksh.postinst mksh-52c/debian/pdksh.postinst --- mksh-50e/debian/pdksh.postinst 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/pdksh.postinst 1970-01-01 01:00:00.000000000 +0100 @@ -1,66 +0,0 @@ -#!/bin/sh - -set -e - -# This maintainer script can be called the following ways: -# -# * new-postinst "configure" [$most_recently_configured_version] -# The package is unpacked; all dependencies are unpacked and, when there -# are no circular dependencies, configured. -# -# * old-postinst "abort-upgrade" $new_version -# * conflictors-postinst "abort-remove" "in-favour" $package -# $new_version -# * postinst "abort-remove" -# * deconfigureds-postinst "abort-deconfigure" "in-favour" -# $failed_install_package $fip_version ["removing" -# $conflicting_package $cp_version] -# The package is unpacked; all dependencies are at least Half-Installed, -# previously been configured, and not removed. In some error situations, -# dependencies may not be even fully unpacked. -# -# * postinst "triggered" "${triggers[*]}" -# For trigger-only calls, i.e. if "configure" is not called. - -docdir=/usr/share/doc/pdksh -move_docdir() { - test -d /usr/share/doc/. || return 0 - test -d $docdir && rmdir --ignore-fail-on-non-empty $docdir - if test -e $docdir; then - echo >&2 "The old $docdir was locally modified." - echo >&2 "Saved as $docdir.dpkg-old" - (mv $docdir $docdir.dpkg-old || :) - fi - if test -e $docdir; then - echo >&2 "FAILED! Remove $docdir manually," - echo >&2 "then retry (dpkg -a --configure)." - exit 1 - fi - ln -sf mksh $docdir -} - -case $1 in -configure) - # convert old docdir into a symlink, dpkg won’t do that for us - test -h $docdir || move_docdir - test -x /usr/bin/update-menus && update-menus - ;; - -abort-upgrade|abort-remove|abort-deconfigure) - ;; - -triggered) - ;; - -*) - echo >&2 "postinst called with unknown subcommand '$1'" - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff -Nru mksh-50e/debian/pdksh.preinst mksh-52c/debian/pdksh.preinst --- mksh-50e/debian/pdksh.preinst 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/pdksh.preinst 1970-01-01 01:00:00.000000000 +0100 @@ -1,54 +0,0 @@ -#!/bin/sh - -set -e - -# This maintainer script can be called the following ways: -# -# * new-preinst "install" [$old_version] -# * new-preinst "upgrade" [$old_version] -# * old-preinst "abort-upgrade" $new_version -# Essential packages and Pre-Depends are available. Pre-Depends have -# been configured once, but may be unpacked or Half-Configured only, -# or, for "abort-upgrade", Half-Installed if their upgrade failed. - -case $1 in -install|upgrade) - (update-alternatives --remove ksh /bin/pdksh || :) - (remove-shell /bin/pdksh || :) - ;; - -abort-upgrade) - ;; - -*) - echo >&2 "preinst called with unknown subcommand '$1'" - exit 1 - ;; -esac - -# -# Due to switching the source package from pdksh to mksh, we cannot -# use apt-listchanges with a NEWS.Debian file here; neither can we -# use debconf as that would be considered abuse/nagging. -# -test $1 = upgrade && test -n "$2" && \ - dpkg --compare-versions "$2" lt '40.9.20120628-1~' && \ - echo ' -+===============================================================+ -| Note for the pdksh -> mksh transition | -+---------------------------------------------------------------+ -| If you have any user accounts with /bin/pdksh as login shell, | -| change these to /bin/mksh because pdksh will be replaced by a | -| symlink to lksh, a variant of mksh used to run legacy scripts | -| but not suitable for interactive use. For sane defaults, also | -| have users copy /etc/skel/.mkshrc into their home directories | -| and unset ENV. This warning will only be shown this once. | -+===============================================================+ -' - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff -Nru mksh-50e/debian/rules mksh-52c/debian/rules --- mksh-50e/debian/rules 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/rules 2016-03-21 12:53:58.000000000 +0100 @@ -4,7 +4,7 @@ export LC_ALL # shut up build log checkers that don’t know what they are doing -# Debian #492377, #572252; as well *buntu +# Debian #492377, #572252; as well debianderivatethatcannotbenamed _shutup:= 2>&1 | sed \ -e 's!conftest.c:\([0-9]*\(:[0-9]*\)*\): error:!cE(\1) -!g' \ -e 's!conftest.c:\([0-9]*\(:[0-9]*\)*\): warning:!cW(\1) -!g' \ @@ -26,21 +26,6 @@ -rm -rf debian/.*_stamp binary-indep: build-indep - dh_testdir - if test -x "$$(which dh_prep)"; then dh_prep -i; else dh_clean -i -k; fi - dh_installchangelogs -i - dh_install -i - # will be replaced by links to mksh - rm -rf debian/pdksh/usr/share/doc/pdksh - mkdir -p debian/pdksh/usr/share/lintian/overrides - cp -a debian/pdksh.lintian \ - debian/pdksh/usr/share/lintian/overrides/pdksh - dh_link -i - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i binary-arch: build-arch dh_testdir diff -Nru mksh-50e/debian/uhr mksh-52c/debian/uhr --- mksh-50e/debian/uhr 2014-10-07 18:28:41.000000000 +0200 +++ mksh-52c/debian/uhr 2016-03-21 12:53:58.000000000 +0100 @@ -1,8 +1,8 @@ #!/bin/mksh -# $MirOS: contrib/hosted/tg/uhr,v 1.15 2014/08/21 20:08:21 tg Exp $ +# $MirOS: contrib/hosted/tg/uhr,v 1.17 2015/11/29 21:34:06 tg Exp $ #- -# Copyright © 2012, 2013 -# Thorsten Glaser +# Copyright © 2012, 2013, 2015 +# mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission @@ -33,17 +33,95 @@ bcopt= bc --help >/dev/null 2>&1 && bcopt=-q +# global variables used by progress bar +_cnt_progress_bar=0 +_cur_progress_bar=0 +isin_progress_bar=0 +nlin_progress_bar=0 + +# args: $1 = number of draw_progress_bar calls to make up 100% +function init_progress_bar { + global -i _cnt_progress_bar=$1 _cur_progress_bar=0 + global -i nlin_progress_bar=$LINES isin_progress_bar=1 + + trap 'done_progress_bar' EXIT + # newline; up one line (to ensure we are not in the last line); + # save position; set scrolling region; restore position + print -n "\\n\\e[A\\e7\\e[1;$((# nlin_progress_bar - 1))r\\e8" +} + +function sigwinch_uhr { + got_sigwinch=1 + (( isin_progress_bar )) || return 0 + + # get new terminal size + nlin_progress_bar=$LINES + # newline; up one line (to ensure we are not in the last line); + # save position; set scrolling region; restore position + print -n "\\n\\e[A\\e7\\e[1;$((# nlin_progress_bar - 1))r\\e8" +} + +function done_progress_bar { + (( isin_progress_bar )) || return 0 + # save position; clear scrolling region; + # go to last line; delete line; restore position + print "\\e7\\e[0;0r\\e[$nlin_progress_bar;0H\\e[M\\e8" + isin_progress_bar=0 + trap - EXIT +} + +function draw_progress_bar { + local bar num w=$COLUMNS + + ((# num = (++_cur_progress_bar * w * 8) / _cnt_progress_bar )) + while ((# num >= 8 )); do + bar+=█ + ((# num -= 8 )) + done + case $num { + (7) bar+=▉ ;; + (6) bar+=▊ ;; + (5) bar+=▋ ;; + (4) bar+=▌ ;; + (3) bar+=▍ ;; + (2) bar+=▎ ;; + (1) bar+=▏ ;; + } + # fill complete line, right-align completion percentage display + local -R$w spc="$((# _cur_progress_bar * 100 / _cnt_progress_bar))%" + # elide percentage when it stops fitting + ((# (_cur_progress_bar * w / _cnt_progress_bar) > (w - 4) )) && spc= + # save position; go to last line; set colours; + # output a line full of spaces (and completion percentage); + # jump to first column; output bar (line præfix); restore position + print -n -- "\\e7\\e[$nlin_progress_bar;0H\\e[0;1;33;44m$spc\\r$bar\\e8" +} + function graceful { print -n '\033[;H\033[J' exit 0 } trap graceful INT TERM HUP -trap got_sigwinch=1 WINCH +trap sigwinch_uhr WINCH while :; do got_sigwinch=0 -print "\e[0m\nPregenerating arrays, please wait..." +init_progress_bar 135 +draw_progress_bar +S='Pregenerating arrays, please wait...' +if (( (r = (COLUMNS - ${%S}) / 2 - 2) < 1 )); then + d="\\e[0m\\n$S" +else + d= + (( n = ${%S} + 2 )) + while (( n-- )); do + d+=─ + done + d="\\e[0m\\e[$((LINES / 2 - 1));${r}H\\e7┌$d┐\\e8\\e[B│ $S │\\e8\\e[2B└$d┘" +fi +print "$d" + (( r = LINES * 2 )) (( r = (r > COLUMNS ? COLUMNS : r) / 2 - 1)) (( n = 2 * r + 1 )) @@ -113,6 +191,8 @@ px+=("${_px[@]}") } +draw_progress_bar + # precalculate all lines’ endpoints with bc and paths with Bresenham integer x y dx sx dy sy e f bc -l $bcopt |& @@ -128,6 +208,7 @@ # minutes and seconds – full length, 60 items i=-1 while (( ++i < 60 )); do + draw_progress_bar eval set -A lms$i print -p "r * c(p($i * 6))" read -p S; [[ $S = ?(-).* ]] && S=0 @@ -158,6 +239,7 @@ print -p 'r = o * 2 / 3' i=-1 while (( ++i < 60 )); do + draw_progress_bar eval set -A lh$i print -p "r * c(p($i * 6))" read -p S; [[ $S = ?(-).* ]] && S=0 @@ -190,6 +272,7 @@ set -A mkx set -A mky while (( ++i < 12 )); do + draw_progress_bar print -p "r * c(p($i * 30))" read -p S; [[ $S = ?(-).* ]] && S=0 mkx[i]=${S%%.*} @@ -199,6 +282,7 @@ done exec 3>&p; exec 3>&- +draw_progress_bar (( L = LINES >= (COLUMNS / 2) ? (COLUMNS / 2) : LINES )) # fine-tuning of roman numeral position via screen size (( ++mkx[7] )) @@ -241,6 +325,7 @@ ;; } (( mky[0] += 2 * (L & 1) )) +done_progress_bar # clear framebuffer and screen set -A fb diff -Nru mksh-50e/dot.mkshrc mksh-52c/dot.mkshrc --- mksh-50e/dot.mkshrc 2015-01-11 23:40:09.000000000 +0100 +++ mksh-52c/dot.mkshrc 2015-12-31 22:00:38.000000000 +0100 @@ -1,9 +1,9 @@ # $Id$ -# $MirOS: src/bin/mksh/dot.mkshrc,v 1.89.2.1 2015/01/11 22:39:44 tg Exp $ +# $MirOS: src/bin/mksh/dot.mkshrc,v 1.104 2015/12/31 21:00:12 tg Exp $ #- # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013, 2014, 2015 -# Thorsten Glaser +# mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission @@ -22,267 +22,276 @@ #- # ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells -# catch non-mksh (including lksh) trying to shell this file -case $KSH_VERSION in +# catch non-mksh (including lksh) trying to run 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} -: ${MKSH:=$(whence -p mksh)}; PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${| - local e=$? +PS1='#'; (( USER_ID )) && PS1='$'; \: "${TERM:=vt100}${HOSTNAME:=$(\ulimit -c \ + 0; hostname 2>/dev/null)}${EDITOR:=/bin/ed}${USER:=$(\ulimit -c 0; id -un \ + 2>/dev/null || \echo \?)}${MKSH:=$(\builtin whence -p mksh)}" +HOSTNAME=${HOSTNAME%%*([ ]).*}; HOSTNAME=${HOSTNAME##*([ ])} +[[ $HOSTNAME = ?(ip6-)localhost?(6) ]] && HOSTNAME= +\: "${HOSTNAME:=nil}${MKSH:=/bin/mksh}"; \export EDITOR HOSTNAME MKSH TERM USER +PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${| + \typeset e=$? (( e )) && REPLY+="$e|" - REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)} - REPLY+=@${HOSTNAME%%.*}: + REPLY+=${USER}@${HOSTNAME%%.*}: - local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~} - local m=${%d} n p=...; (( m > 0 )) || m=${#d} + \typeset d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/\~} + \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d} (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p= REPLY+=$p$d - return $e -} '"$PS1 "; export EDITOR HOSTNAME MKSH TERM USER -alias ls=ls -unalias ls -alias l='ls -F' -alias la='l -a' -alias ll='l -l' -alias lo='l -alo' -alias doch='sudo mksh -c "$(fc -ln -1)"' -whence -p rot13 >/dev/null || alias rot13='tr \ + \return $e +} '"$PS1 " +\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 "$(\builtin fc -ln -1)"' +\command -v rot13 >/dev/null || \alias rot13='tr \ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM' -if whence -p hd >/dev/null; then :; elif whence -p hexdump >/dev/null; then +if \command -v hd >/dev/null; then \:; elif \command -v 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 + \typeset -Uui16 -Z11 pos=0 + \typeset -Uui16 -Z5 hv=2147483647 + \typeset dasc line i + \set +U - cat "$@" | { set +U; if read -arN -1 line; then - typeset -i1 'line[*]' + \cat "$@" | 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#} " + (( pos )) && \ + \builtin print -r -- "$dasc|" + \builtin print -n "${pos#16#} " dasc=' |' fi - print -n "${hv#16#} " + \builtin print -n "${hv#16#} " + #XXX EBCDIC, but we need [[:print:]] to fix this if (( (hv < 32) || (hv > 126) )); then dasc+=. else dasc+=${line[i-1]#1#} fi - (( (pos++ & 15) == 7 )) && print -n -- '- ' + (( (pos++ & 15) == 7 )) && \ + \builtin print -n -- '- ' done while (( pos & 15 )); do - print -n ' ' - (( (pos++ & 15) == 7 )) && print -n -- '- ' + \builtin print -n ' ' + (( (pos++ & 15) == 7 )) && \ + \builtin print -n -- '- ' done - (( hv == 2147483647 )) || print -r -- "$dasc|" - fi; } + (( hv == 2147483647 )) || \builtin print -r -- "$dasc|" + fi } fi # Berkeley C shell compatible dirs, popd, and pushd functions # Z shell compatible chpwd() hook, used to update DIRSTACK[0] -DIRSTACKBASE=$(realpath ~/. 2>/dev/null || print -nr -- "${HOME:-/}") +DIRSTACKBASE=$(\builtin realpath ~/. 2>/dev/null || \ + \builtin print -nr -- "${HOME:-/}") set -A DIRSTACK function chpwd { - DIRSTACK[0]=$(realpath . 2>/dev/null || print -r -- "$PWD") + DIRSTACK[0]=$(\builtin realpath . 2>/dev/null || \ + \builtin print -r -- "$PWD") [[ $DIRSTACKBASE = ?(*/) ]] || \ - DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/~} - : + DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/\~} + \: } -chpwd . -function cd { - builtin cd "$@" || return $? - chpwd "$@" +\chpwd . +cd() { + \builtin cd "$@" || \return $? + \chpwd "$@" } function cd_csh { - local d t=${1/#~/$DIRSTACKBASE} + \typeset d t=${1/#\~/$DIRSTACKBASE} - if ! d=$(builtin cd "$t" 2>&1); then - print -u2 "${1}: ${d##*cd: $t: }." - return 1 + if ! d=$(\builtin cd "$t" 2>&1); then + \builtin print -u2 "${1}: ${d##*cd: $t: }." + \return 1 fi - cd "$t" + \cd "$t" } function dirs { - local d dwidth - local -i fl=0 fv=0 fn=0 cpos=0 + \typeset d dwidth + \typeset -i fl=0 fv=0 fn=0 cpos=0 - while getopts ":lvn" d; do + while \getopts ":lvn" d; do case $d { (l) fl=1 ;; (v) fv=1 ;; (n) fn=1 ;; - (*) print -u2 'Usage: dirs [-lvn].' - return 1 ;; + (*) \builtin print -u2 'Usage: dirs [-lvn].' + \return 1 ;; } done - shift $((OPTIND - 1)) + \shift $((OPTIND - 1)) if (( $# > 0 )); then - print -u2 'Usage: dirs [-lvn].' - return 1 + \builtin print -u2 'Usage: dirs [-lvn].' + \return 1 fi if (( fv )); then fv=0 while (( fv < ${#DIRSTACK[*]} )); do d=${DIRSTACK[fv]} - (( fl )) && d=${d/#~/$DIRSTACKBASE} - print -r -- "$fv $d" - let fv++ + (( fl )) && d=${d/#\~/$DIRSTACKBASE} + \builtin print -r -- "$fv $d" + \builtin let fv++ done else fv=0 while (( fv < ${#DIRSTACK[*]} )); do d=${DIRSTACK[fv]} - (( fl )) && d=${d/#~/$DIRSTACKBASE} + (( fl )) && d=${d/#\~/$DIRSTACKBASE} (( dwidth = (${%d} > 0 ? ${%d} : ${#d}) )) if (( fn && (cpos += dwidth + 1) >= 79 && \ dwidth < 80 )); then - print + \builtin print (( cpos = dwidth + 1 )) fi - print -nr -- "$d " - let fv++ + \builtin print -nr -- "$d " + \builtin let fv++ done - print + \builtin print fi - return 0 + \return 0 } function popd { - local d fa - local -i n=1 + \typeset d fa + \typeset -i n=1 - while getopts ":0123456789lvn" d; do + while \getopts ":0123456789lvn" d; do case $d { (l|v|n) fa+=" -$d" ;; (+*) n=2 - break ;; - (*) print -u2 'Usage: popd [-lvn] [+].' - return 1 ;; + \break ;; + (*) \builtin print -u2 'Usage: popd [-lvn] [+].' + \return 1 ;; } done - shift $((OPTIND - n)) + \shift $((OPTIND - n)) n=0 if (( $# > 1 )); then - print -u2 popd: Too many arguments. - return 1 + \builtin print -u2 popd: Too many arguments. + \return 1 elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then - print -u2 popd: Directory stack not that deep. - return 1 + \builtin print -u2 popd: Directory stack not that deep. + \return 1 fi elif [[ -n $1 ]]; then - print -u2 popd: Bad directory. - return 1 + \builtin print -u2 popd: Bad directory. + \return 1 fi if (( ${#DIRSTACK[*]} < 2 )); then - print -u2 popd: Directory stack empty. - return 1 + \builtin print -u2 popd: Directory stack empty. + \return 1 fi - unset DIRSTACK[n] - set -A DIRSTACK -- "${DIRSTACK[@]}" - cd_csh "${DIRSTACK[0]}" || return 1 - dirs $fa + \unset DIRSTACK[n] + \set -A DIRSTACK -- "${DIRSTACK[@]}" + \cd_csh "${DIRSTACK[0]}" || \return 1 + \dirs $fa } function pushd { - local d fa - local -i n=1 + \typeset d fa + \typeset -i n=1 - while getopts ":0123456789lvn" d; do + while \getopts ":0123456789lvn" d; do case $d { (l|v|n) fa+=" -$d" ;; (+*) n=2 - break ;; - (*) print -u2 'Usage: pushd [-lvn] [|+].' - return 1 ;; + \break ;; + (*) \builtin print -u2 'Usage: pushd [-lvn] [|+].' + \return 1 ;; } done - shift $((OPTIND - n)) + \shift $((OPTIND - n)) if (( $# == 0 )); then if (( ${#DIRSTACK[*]} < 2 )); then - print -u2 pushd: No other directory. - return 1 + \builtin print -u2 pushd: No other directory. + \return 1 fi d=${DIRSTACK[1]} DIRSTACK[1]=${DIRSTACK[0]} - cd_csh "$d" || return 1 + \cd_csh "$d" || \return 1 elif (( $# > 1 )); then - print -u2 pushd: Too many arguments. - return 1 + \builtin print -u2 pushd: Too many arguments. + \return 1 elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then - print -u2 pushd: Directory stack not that deep. - return 1 + \builtin print -u2 pushd: Directory stack not that deep. + \return 1 fi while (( n-- )); do d=${DIRSTACK[0]} - unset DIRSTACK[0] - set -A DIRSTACK -- "${DIRSTACK[@]}" "$d" + \unset DIRSTACK[0] + \set -A DIRSTACK -- "${DIRSTACK[@]}" "$d" done - cd_csh "${DIRSTACK[0]}" || return 1 + \cd_csh "${DIRSTACK[0]}" || \return 1 else - set -A DIRSTACK -- placeholder "${DIRSTACK[@]}" - cd_csh "$1" || return 1 + \set -A DIRSTACK -- placeholder "${DIRSTACK[@]}" + \cd_csh "$1" || \return 1 fi - dirs $fa + \dirs $fa } # pager (not control character safe) function smores { ( - set +m - cat "$@" |& - trap "rv=\$?; kill $! >/dev/null 2>&1; exit \$rv" EXIT - while IFS= read -pr line; do + \set +m + \cat "$@" |& + \trap "rv=\$?; 'kill' $! >/dev/null 2>&1; 'exit' \$rv" EXIT + while IFS= \read -pr line; do llen=${%line} (( llen == -1 )) && llen=${#line} (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 )) if (( (curlin += llen) >= LINES )); then - print -n -- '\033[7m--more--\033[0m' - read -u1 || exit $? - [[ $REPLY = [Qq]* ]] && exit 0 + \builtin print -n -- '\e[7m--more--\e[0m' + \read -u1 || \exit $? + [[ $REPLY = [Qq]* ]] && \exit 0 curlin=$llen fi - print -r -- "$line" + \builtin print -r -- "$line" done ) } -# base64 encoder and decoder, RFC compliant, NUL safe +# base64 encoder and decoder, RFC compliant, NUL safe, not EBCDIC safe function Lb64decode { - [[ -o utf8-mode ]]; local u=$? c s="$*" t - set +U - [[ -n $s ]] || { s=$(cat; print x); s=${s%x}; } - local -i i=0 j=0 n=${#s} p=0 v x - local -i16 o + \set +U + \typeset c s="$*" t + [[ -n $s ]] || { s=$(\cat; \builtin print x); s=${s%x}; } + \typeset -i i=0 j=0 n=${#s} p=0 v x + \typeset -i16 o while (( i < n )); do c=${s:(i++):1} case $c { - (=) break ;; + (=) \break ;; ([A-Z]) (( v = 1#$c - 65 )) ;; ([a-z]) (( v = 1#$c - 71 )) ;; ([0-9]) (( v = 1#$c + 4 )) ;; (+) v=62 ;; (/) v=63 ;; - (*) continue ;; + (*) \continue ;; } (( x = (x << 6) | v )) case $((p++)) { - (0) continue ;; + (0) \continue ;; (1) (( o = (x >> 4) & 255 )) ;; (2) (( o = (x >> 2) & 255 )) ;; (3) (( o = x & 255 )) @@ -290,26 +299,25 @@ ;; } t+=\\x${o#16#} - (( ++j & 4095 )) && continue - print -n $t + (( ++j & 4095 )) && \continue + \builtin print -n $t t= done - print -n $t - (( u )) || set -U + \builtin print -n $t } -set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ +\set -A Lb64encode_tbl -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + / function Lb64encode { - [[ -o utf8-mode ]]; local u=$? c s t - set +U + \set +U + \typeset c s t if (( $# )); then - read -raN-1 s <<<"$*" - unset s[${#s[*]}-1] + \read -raN-1 s <<<"$*" + \unset s[${#s[*]}-1] else - read -raN-1 s + \read -raN-1 s fi - local -i i=0 n=${#s[*]} j v + \typeset -i i=0 n=${#s[*]} j v while (( i < n )); do (( v = s[i++] << 16 )) @@ -317,86 +325,280 @@ (( v |= j << 8 )) (( j = i < n ? s[i++] : 0 )) (( v |= j )) - t+=${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]} - c=${Lb64encode_code[v >> 6 & 63]} + t+=${Lb64encode_tbl[v >> 18]}${Lb64encode_tbl[v >> 12 & 63]} + c=${Lb64encode_tbl[v >> 6 & 63]} if (( i <= n )); then - t+=$c${Lb64encode_code[v & 63]} + t+=$c${Lb64encode_tbl[v & 63]} elif (( i == n + 1 )); then t+=$c= else t+=== fi if (( ${#t} == 76 || i >= n )); then - print $t + \builtin print $t t= fi done - (( u )) || set -U } # Better Avalanche for the Jenkins Hash -typeset -Z11 -Uui16 Lbafh_v +\typeset -Z11 -Uui16 Lbafh_v function Lbafh_init { Lbafh_v=0 } function Lbafh_add { - [[ -o utf8-mode ]]; local u=$? s - set +U + \set +U + \typeset s if (( $# )); then - read -raN-1 s <<<"$*" - unset s[${#s[*]}-1] + \read -raN-1 s <<<"$*" + \unset s[${#s[*]}-1] else - read -raN-1 s + \read -raN-1 s fi - local -i i=0 n=${#s[*]} + \typeset -i i=0 n=${#s[*]} while (( i < n )); do ((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 )) ((# Lbafh_v ^= Lbafh_v >> 6 )) done - - (( u )) || set -U } function Lbafh_finish { - local -Ui t + \typeset -Ui t ((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \ ((Lbafh_v << 1) & 0xFEFEFEFE) )) ((# Lbafh_v = t ^ (t >>> 8) ^ (Lbafh_v >>> 8) ^ \ (Lbafh_v >>> 16) ^ (Lbafh_v >>> 24) )) - : + \: } # strip comments (and leading/trailing whitespace if IFS is set) from # any file(s) given as argument, or stdin if none, and spew to stdout function Lstripcom { - cat "$@" | { set -o noglob; while read _line; do + \set -o noglob + \cat "$@" | while \read _line; do _line=${_line%%#*} - [[ -n $_line ]] && print -r -- $_line - done; } + [[ -n $_line ]] && \builtin print -r -- $_line + done } # give MidnightBSD's laffer1 a bit of csh feeling function setenv { - eval export "\"$1\""'="$2"' + if (( $# )); then + \eval '\export "$1"="${2:-}"' + else + \typeset -x + fi +} + +# toggle built-in aliases and utilities, and aliases and functions from mkshrc +function enable { + \typeset doprnt=0 mode=1 x y z rv=0 + \typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all + \set -A b_alias + \set -A i_alias + \set -A i_func + + # accumulate mksh built-in aliases, in ASCIIbetical order + i_alias[nalias]=autoload; b_alias[nalias++]='\typeset -fu' + i_alias[nalias]=functions; b_alias[nalias++]='\typeset -f' + i_alias[nalias]=hash; b_alias[nalias++]='\builtin alias -t' + i_alias[nalias]=history; b_alias[nalias++]='\builtin fc -l' + i_alias[nalias]=integer; b_alias[nalias++]='\typeset -i' + i_alias[nalias]=local; b_alias[nalias++]='\typeset' + i_alias[nalias]=login; b_alias[nalias++]='\exec login' + i_alias[nalias]=nameref; b_alias[nalias++]='\typeset -n' + i_alias[nalias]=nohup; b_alias[nalias++]='nohup ' + i_alias[nalias]=r; b_alias[nalias++]='\builtin fc -e -' + i_alias[nalias]=type; b_alias[nalias++]='\builtin whence -v' + + # accumulate mksh built-in utilities, in definition order, even ifndef + i_func[nfunc++]=. + i_func[nfunc++]=: + i_func[nfunc++]='[' + i_func[nfunc++]=alias + i_func[nfunc++]=break + i_func[nfunc++]=builtin + i_func[nfunc++]=cat + i_func[nfunc++]=cd + i_func[nfunc++]=chdir + i_func[nfunc++]=command + i_func[nfunc++]=continue + i_func[nfunc++]=echo + i_func[nfunc++]=eval + i_func[nfunc++]=exec + i_func[nfunc++]=exit + i_func[nfunc++]=export + i_func[nfunc++]=false + i_func[nfunc++]=fc + i_func[nfunc++]=getopts + i_func[nfunc++]=global + i_func[nfunc++]=jobs + i_func[nfunc++]=kill + i_func[nfunc++]=let + i_func[nfunc++]='let]' + i_func[nfunc++]=print + i_func[nfunc++]=pwd + i_func[nfunc++]=read + i_func[nfunc++]=readonly + i_func[nfunc++]=realpath + i_func[nfunc++]=rename + i_func[nfunc++]=return + i_func[nfunc++]=set + i_func[nfunc++]=shift + i_func[nfunc++]=source + i_func[nfunc++]=suspend + i_func[nfunc++]=test + i_func[nfunc++]=times + i_func[nfunc++]=trap + i_func[nfunc++]=true + i_func[nfunc++]=typeset + i_func[nfunc++]=ulimit + i_func[nfunc++]=umask + i_func[nfunc++]=unalias + i_func[nfunc++]=unset + i_func[nfunc++]=wait + i_func[nfunc++]=whence + i_func[nfunc++]=bg + i_func[nfunc++]=fg + i_func[nfunc++]=bind + i_func[nfunc++]=mknod + i_func[nfunc++]=printf + i_func[nfunc++]=sleep + i_func[nfunc++]=domainname + i_func[nfunc++]=extproc + + # accumulate aliases from dot.mkshrc, in definition order + i_alias[nalias]=l; b_alias[nalias++]='ls -F' + i_alias[nalias]=la; b_alias[nalias++]='l -a' + i_alias[nalias]=ll; b_alias[nalias++]='l -l' + i_alias[nalias]=lo; b_alias[nalias++]='l -alo' + i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\builtin fc -ln -1)"' + i_alias[nalias]=rot13; b_alias[nalias++]='tr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM' + i_alias[nalias]=cls; b_alias[nalias++]='\builtin print -n \\ec' + + # accumulate functions from dot.mkshrc, in definition order + i_func[nfunc++]=hd + i_func[nfunc++]=chpwd + i_func[nfunc++]=cd_csh + i_func[nfunc++]=dirs + i_func[nfunc++]=popd + i_func[nfunc++]=pushd + i_func[nfunc++]=smores + i_func[nfunc++]=Lb64decode + i_func[nfunc++]=Lb64encode + i_func[nfunc++]=Lbafh_init + i_func[nfunc++]=Lbafh_add + i_func[nfunc++]=Lbafh_finish + i_func[nfunc++]=Lstripcom + i_func[nfunc++]=setenv + i_func[nfunc++]=enable + + # collect all identifiers, sorted ASCIIbetically + \set -sA i_all -- "${i_alias[@]}" "${i_func[@]}" + + # handle options, we don't do dynamic loading + while \getopts "adf:nps" x; do + case $x { + (a) + mode=-1 + ;; + (d) + # deliberately causing an error, like bash-static + ;| + (f) + \builtin print -u2 enable: dynamic loading not available + \return 2 + ;; + (n) + mode=0 + ;; + (p) + doprnt=1 + ;; + (s) + \set -sA i_all -- . : break continue eval exec exit \ + export readonly return set shift times trap unset + ;; + (*) + \builtin print -u2 enable: usage: \ + "enable [-adnps] [-f filename] [name ...]" + return 2 + ;; + } + done + \shift $((OPTIND - 1)) + + # display builtins enabled/disabled/all/special? + if (( doprnt || ($# == 0) )); then + for x in "${i_all[@]}"; do + y=$(\alias "$x") || y= + [[ $y = "$x='\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)'" ]]; z=$? + case $mode:$z { + (-1:0|0:0) + \print -r -- "enable -n $x" + ;; + (-1:1|1:1) + \print -r -- "enable $x" + ;; + } + done + \return 0 + fi + + for x in "$@"; do + z=0 + for y in "${i_alias[@]}" "${i_func[@]}"; do + [[ $x = "$y" ]] || \continue + z=1 + \break + done + if (( !z )); then + \builtin print -ru2 enable: "$x": not a shell builtin + rv=1 + \continue + fi + if (( !mode )); then + # disable this + \alias "$x=\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)" + else + # find out if this is an alias or not, first + z=0 + y=-1 + while (( ++y < nalias )); do + [[ $x = "${i_alias[y]}" ]] || \continue + z=1 + \break + done + if (( z )); then + # re-enable the original alias body + \alias "$x=${b_alias[y]}" + else + # re-enable the original utility/function + \unalias "$x" + fi + fi + done + \return $rv } -: place customisations below this line +\: place customisations below this line for p in ~/.etc/bin ~/bin; do - [[ -d $p/. ]] || continue + [[ -d $p/. ]] || \continue + #XXX OS/2 [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH done -export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=- -alias cls='print -n \\033c' +\export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=- +\alias cls='\builtin print -n \\ec' -#unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \ +#\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \ # LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME #p=en_GB.UTF-8 -#set -U -#export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p +#\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p +#\set -U -unset p +\unset p -: place customisations above this line +\: place customisations above this line diff -Nru mksh-50e/edit.c mksh-52c/edit.c --- mksh-50e/edit.c 2015-03-01 16:43:21.000000000 +0100 +++ mksh-52c/edit.c 2016-03-04 15:26:38.000000000 +0100 @@ -1,12 +1,12 @@ -/* $OpenBSD: edit.c,v 1.39 2013/12/17 16:37:05 deraadt Exp $ */ +/* $OpenBSD: edit.c,v 1.41 2015/09/01 13:12:31 tedu Exp $ */ /* $OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $ */ -/* $OpenBSD: emacs.c,v 1.49 2015/02/16 01:44:41 tedu Exp $ */ -/* $OpenBSD: vi.c,v 1.28 2013/12/18 16:45:46 deraadt Exp $ */ +/* $OpenBSD: emacs.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */ +/* $OpenBSD: vi.c,v 1.30 2015/09/10 22:48:58 nicm Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -28,7 +28,7 @@ #ifndef MKSH_NO_CMDLINE_EDITING -__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.276.2.3 2015/03/01 15:42:56 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.294 2016/03/04 14:26:12 tg Exp $"); /* * in later versions we might use libtermcap for this, but since external @@ -381,7 +381,7 @@ source = s; if (yylex(ONEWORD | LQCHAR) != LWORD) { source = sold; - internal_warningf("%s: %s", "fileglob", "bad substitution"); + internal_warningf(Tfg_badsubst); return (0); } source = sold; @@ -771,7 +771,7 @@ Xinit(xs, xp, patlen + 128, ATEMP); while (sp) { xp = Xstring(xs, xp); - if (!(p = cstrchr(sp, ':'))) + if (!(p = cstrchr(sp, MKSH_PATHSEPC))) p = sp + strlen(sp); pathlen = p - sp; if (pathlen) { @@ -887,7 +887,7 @@ /* Separator for motion */ #define is_mfs(c) (!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80))) -#define X_NTABS 3 /* normal, meta1, meta2 */ +#define X_NTABS 4 /* normal, meta1, meta2, pc */ #define X_TABSZ 256 /* size of keydef tables etc */ /*- @@ -1099,8 +1099,28 @@ { XFUNC_mv_end | 0x80, 2, '8' }, { XFUNC_mv_end, 2, 'F' }, { XFUNC_del_char | 0x80, 2, '3' }, + { XFUNC_del_char, 2, 'P' }, { XFUNC_search_hist_up | 0x80, 2, '5' }, { XFUNC_search_hist_dn | 0x80, 2, '6' }, +#endif + /* PC scancodes */ +#if !defined(MKSH_SMALL) || defined(__OS2__) + { XFUNC_meta3, 0, 0 }, + { XFUNC_mv_begin, 3, 71 }, + { XFUNC_prev_com, 3, 72 }, +#ifndef MKSH_SMALL + { XFUNC_search_hist_up, 3, 73 }, +#endif + { XFUNC_mv_back, 3, 75 }, + { XFUNC_mv_forw, 3, 77 }, + { XFUNC_mv_end, 3, 79 }, + { XFUNC_next_com, 3, 80 }, +#ifndef MKSH_SMALL + { XFUNC_search_hist_dn, 3, 81 }, +#endif + { XFUNC_del_char, 3, 83 }, +#endif +#ifndef MKSH_SMALL /* more non-standard ones */ { XFUNC_edit_line, 2, 'e' } #endif @@ -2215,6 +2235,13 @@ } static int +x_meta3(int c MKSH_A_UNUSED) +{ + x_curprefix = 3; + return (KSTD); +} + +static int x_kill(int c MKSH_A_UNUSED) { size_t col = xcp - xbuf; @@ -2235,12 +2262,8 @@ static void x_push(int nchars) { - char *cp; - - strndupx(cp, xcp, nchars, AEDIT); - if (killstack[killsp]) - afree(killstack[killsp], AEDIT); - killstack[killsp] = cp; + afree(killstack[killsp], AEDIT); + strndupx(killstack[killsp], xcp, nchars, AEDIT); killsp = (killsp + 1) % KILLSIZE; } @@ -2412,8 +2435,8 @@ if (prefix) /* prefix == 1 || prefix == 2 */ - shf_puts(x_mapout(prefix == 1 ? - CTRL('[') : CTRL('X')), shl_stdout); + shf_puts(x_mapout(prefix == 1 ? CTRL('[') : + prefix == 2 ? CTRL('X') : 0), shl_stdout); #ifdef MKSH_SMALL shprintf("%s = ", x_mapout(key)); #else @@ -2478,6 +2501,8 @@ prefix = 1; else if (f == XFUNC_meta2) prefix = 2; + else if (f == XFUNC_meta3) + prefix = 3; else break; } @@ -2491,7 +2516,7 @@ m1 = msg; while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3) x_mapout2(*c++, &m1); - bi_errorf("%s: %s", "too long key sequence", msg); + bi_errorf("too long key sequence: %s", msg); return (1); } #ifndef MKSH_SMALL @@ -2515,7 +2540,7 @@ if (!strcmp(x_ftab[f].xf_name, a2)) break; if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) { - bi_errorf("%s: %s %s", a2, "no such", Tfunction); + bi_errorf("%s: no such function", a2); return (1); } } @@ -2966,7 +2991,7 @@ /* strip command prefix */ c &= 255; while (c >= 0 && ksh_isdigit(c)) { - n = n * 10 + (c - '0'); + n = n * 10 + ksh_numdig(c); if (n > LINE) /* upper bound for repeat */ goto x_set_arg_too_big; @@ -3053,7 +3078,7 @@ } if (modified) { *xep = '\0'; - histsave(&source->line, xbuf, true, true); + histsave(&source->line, xbuf, HIST_STORE, true); x_arg = 0; } else x_arg = source->line - (histptr - x_histp); @@ -3290,8 +3315,23 @@ edchars.eof = tty_state.c_cc[VEOF]; #ifdef VWERASE edchars.werase = tty_state.c_cc[VWERASE]; +#else + edchars.werase = 0; #endif + if (!edchars.erase) + edchars.erase = CTRL('H'); + if (!edchars.kill) + edchars.kill = CTRL('U'); + if (!edchars.intr) + edchars.intr = CTRL('C'); + if (!edchars.quit) + edchars.quit = CTRL('\\'); + if (!edchars.eof) + edchars.eof = CTRL('D'); + if (!edchars.werase) + edchars.werase = CTRL('W'); + #ifdef _POSIX_VDISABLE /* Convert unset values to internal 'unset' value */ if (edchars.erase == _POSIX_VDISABLE) @@ -3602,6 +3642,18 @@ switch (state) { case VNORMAL: + /* PC scancodes */ + if (!ch) switch (cmdlen = 0, (ch = x_getc())) { + case 71: ch = '0'; goto pseudo_vi_command; + case 72: ch = 'k'; goto pseudo_vi_command; + case 73: ch = 'A'; goto vi_xfunc_search_up; + case 75: ch = 'h'; goto pseudo_vi_command; + case 77: ch = 'l'; goto pseudo_vi_command; + case 79: ch = '$'; goto pseudo_vi_command; + case 80: ch = 'j'; goto pseudo_vi_command; + case 83: ch = 'x'; goto pseudo_vi_command; + default: ch = 0; goto vi_insert_failed; + } if (insert != 0) { if (ch == CTRL('v')) { state = VLIT; @@ -3609,6 +3661,7 @@ } switch (vi_insert(ch)) { case -1: + vi_insert_failed: vi_error(); state = VNORMAL; break; @@ -3627,10 +3680,11 @@ return (1); cmdlen = 0; argc1 = 0; - if (ch >= '1' && ch <= '9') { - argc1 = ch - '0'; + if (ch >= ord('1') && ch <= ord('9')) { + argc1 = ksh_numdig(ch); state = VARG1; } else { + pseudo_vi_command: curcmd[cmdlen++] = ch; state = nextstate(ch); if (state == VSEARCH) { @@ -3672,7 +3726,7 @@ case VARG1: if (ksh_isdigit(ch)) - argc1 = argc1 * 10 + ch - '0'; + argc1 = argc1 * 10 + ksh_numdig(ch); else { curcmd[cmdlen++] = ch; state = nextstate(ch); @@ -3681,8 +3735,8 @@ case VEXTCMD: argc2 = 0; - if (ch >= '1' && ch <= '9') { - argc2 = ch - '0'; + if (ch >= ord('1') && ch <= ord('9')) { + argc2 = ksh_numdig(ch); state = VARG2; return (0); } else { @@ -3698,7 +3752,7 @@ case VARG2: if (ksh_isdigit(ch)) - argc2 = argc2 * 10 + ch - '0'; + argc2 = argc2 * 10 + ksh_numdig(ch); else { if (argc1 == 0) argc1 = argc2; @@ -3799,6 +3853,7 @@ break; case VPREFIX2: + vi_xfunc_search_up: state = VFAIL; switch (ch) { case 'A': @@ -4310,8 +4365,8 @@ return (-1); if (modified) { es->cbuf[es->linelen] = '\0'; - histsave(&source->line, es->cbuf, true, - true); + histsave(&source->line, es->cbuf, + HIST_STORE, true); } else argcnt = source->line + 1 - (hlast - hnum); @@ -4542,8 +4597,8 @@ static int domove(int argcnt, const char *cmd, int sub) { - int bcount, i = 0, t; - int ncursor = 0; + int ncursor = 0, i = 0, t; + unsigned int bcount; switch (*cmd) { case 'b': @@ -4996,7 +5051,7 @@ } (void)histnum(n); if ((hptr = *histpos()) == NULL) { - internal_warningf("%s: %s", "grabhist", "bad history array"); + internal_warningf("grabhist: bad history array"); return (-1); } if (save) diff -Nru mksh-50e/emacsfn.h mksh-52c/emacsfn.h --- mksh-50e/emacsfn.h 2010-07-18 00:09:57.000000000 +0200 +++ mksh-52c/emacsfn.h 2015-12-12 22:09:10.000000000 +0100 @@ -1,5 +1,25 @@ +/*- + * Copyright (c) 2009, 2010, 2015 + * mirabilos + * + * Provided that these terms and disclaimer and all copyright notices + * are retained or reproduced in an accompanying document, permission + * is granted to deal in this work without restriction, including un- + * limited rights to use, publicly perform, distribute, sell, modify, + * merge, give away, or sublicence. + * + * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to + * the utmost extent permitted by applicable law, neither express nor + * implied; without malicious intent or gross negligence. In no event + * may a licensor, author or contributor be held liable for indirect, + * direct, other damage, loss, or other issues arising in any way out + * of dealing in the work, even if advised of the possibility of such + * damage or existence of a defect, except proven that it results out + * of said person's immediate fault when using the work as intended. + */ + #if defined(EMACSFN_DEFNS) -__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.5 2010/07/17 22:09:33 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.7 2015/12/12 21:08:44 tg Exp $"); #define FN(cname,sname,flags) static int x_##cname(int); #elif defined(EMACSFN_ENUMS) #define FN(cname,sname,flags) XFUNC_##cname, @@ -52,6 +72,7 @@ FN(literal, "quote", 0) FN(meta1, "prefix-1", XF_PREFIX) FN(meta2, "prefix-2", XF_PREFIX) +FN(meta3, "prefix-3", XF_PREFIX) FN(meta_yank, "yank-pop", 0) FN(mv_back, "backward-char", XF_ARG) FN(mv_begin, "beginning-of-line", 0) diff -Nru mksh-50e/eval.c mksh-52c/eval.c --- mksh-50e/eval.c 2015-03-01 16:43:23.000000000 +0100 +++ mksh-52c/eval.c 2016-02-26 20:05:47.000000000 +0100 @@ -2,8 +2,8 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.158.2.4 2015/03/01 15:42:58 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.185 2016/02/26 19:05:21 tg Exp $"); /* * string expansion @@ -120,7 +120,7 @@ s->start = s->str = cp; source = s; if (yylex(ONEWORD) != LWORD) - internal_errorf("bad substitution"); + internal_errorf(Tbadsubst); source = sold; afree(s, ATEMP); return (evalstr(yylval.cp, f)); @@ -291,21 +291,14 @@ c = *sp++; break; case OQUOTE: - switch (word) { - case IFS_QUOTE: - /* """something */ - word = IFS_WORD; - break; - case IFS_WORD: - break; - default: + if (word != IFS_WORD) word = IFS_QUOTE; - break; - } tilde_ok = 0; quote = 1; continue; case CQUOTE: + if (word == IFS_QUOTE) + word = IFS_WORD; quote = st->quotew; continue; case COMSUB: @@ -382,14 +375,15 @@ unwind_substsyn: /* restore sp */ sp = varname - 2; - end = (beg = wdcopy(sp, ATEMP)) + - (wdscan(sp, CSUBST) - sp); + beg = wdcopy(sp, ATEMP); + end = (wdscan(cstrchr(sp, '\0') + 1, + CSUBST) - sp) + beg; /* ({) the } or x is already skipped */ if (end < wdscan(beg, EOS)) *end = EOS; str = snptreef(NULL, 64, "%S", beg); afree(beg, ATEMP); - errorf("%s: %s", str, "bad substitution"); + errorf("%s: %s", str, Tbadsubst); } if (f & DOBLANK) doblank++; @@ -410,8 +404,8 @@ st->stype = stype; st->base = Xsavepos(ds, dp); st->f = f; - if (x.var == &vtemp) { - st->var = tempvar(); + if (x.var == vtemp) { + st->var = tempvar(vtemp->name); st->var->flag &= ~INTEGER; /* can't fail here */ setstr(st->var, @@ -444,8 +438,6 @@ beg = wdcopy(sp, ATEMP); mid = beg + (wdscan(sp, ADELIM) - sp); stg = beg + (wdscan(sp, CSUBST) - sp); - if (mid >= stg) - goto unwind_substsyn; mid[-2] = EOS; if (mid[-1] == /*{*/'}') { sp += mid - beg - 1; @@ -453,9 +445,8 @@ } else { end = mid + (wdscan(mid, ADELIM) - mid); - if (end >= stg || - /* more than max delimiters */ - end[-1] != /*{*/ '}') + if (end[-1] != /*{*/ '}') + /* more than max delimiters */ goto unwind_substsyn; end[-2] = EOS; sp += end - beg - 1; @@ -490,93 +481,57 @@ case '/': { char *s, *p, *d, *sbeg, *end; char *pat, *rrep; - char *tpat0, *tpat1, *tpat2; + char fpat = 0, *tpat1, *tpat2; s = wdcopy(sp, ATEMP); p = s + (wdscan(sp, ADELIM) - sp); d = s + (wdscan(sp, CSUBST) - sp); - if (p >= d) - goto unwind_substsyn; p[-2] = EOS; if (p[-1] == /*{*/'}') d = NULL; else d[-2] = EOS; sp += (d ? d : p) - s - 1; - tpat0 = wdstrip(s, - WDS_KEEPQ | WDS_MAGIC); - pat = substitute(tpat0, 0); - if (d) { - d = wdstrip(p, WDS_KEEPQ); - rrep = substitute(d, 0); - afree(d, ATEMP); - } else - rrep = null; + if (!(stype & 0x80) && + s[0] == CHAR && + (s[1] == '#' || s[1] == '%')) + fpat = s[1]; + pat = evalstr(s + (fpat ? 2 : 0), + DOTILDE | DOSCALAR | DOPAT); + rrep = d ? evalstr(p, + DOTILDE | DOSCALAR) : null; afree(s, ATEMP); - s = d = pat; - while (*s) - if (*s != '\\' || - s[1] == '%' || - s[1] == '#' || - s[1] == '\0' || - /* XXX really? */ s[1] == '\\' || - s[1] == '/') - *d++ = *s++; - else - s++; - *d = '\0'; - afree(tpat0, ATEMP); /* check for special cases */ - d = str_val(st->var); - switch (*pat) { - case '#': - /* anchor at begin */ - tpat0 = pat + 1; - tpat1 = rrep; - tpat2 = d; - break; - case '%': - /* anchor at end */ - tpat0 = pat + 1; - tpat1 = d; - tpat2 = rrep; - break; - case '\0': - /* empty pattern */ + if (!*pat && !fpat) { + /* + * empty unanchored + * pattern => reject + */ goto no_repl; - default: - tpat0 = pat; - /* silence gcc */ - tpat1 = tpat2 = NULL; } - if (gmatchx(null, tpat0, false)) { + if ((stype & 0x80) && + gmatchx(null, pat, false)) { /* - * pattern matches - * the empty string + * pattern matches empty + * string => don't loop */ - if (tpat0 == pat) - goto no_repl; - /* but is anchored */ - s = shf_smprintf("%s%s", - tpat1, tpat2); - goto do_repl; + stype &= ~0x80; } /* prepare string on which to work */ - strdupx(s, d, ATEMP); + strdupx(s, str_val(st->var), ATEMP); sbeg = s; /* first see if we have any match at all */ - tpat0 = pat; - if (*pat == '#') { + if (fpat == '#') { /* anchor at the beginning */ - tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC); + tpat1 = shf_smprintf("%s%c*", pat, MAGIC); tpat2 = tpat1; - } else if (*pat == '%') { + } else if (fpat == '%') { /* anchor at the end */ - tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0); - tpat2 = tpat0; + tpat1 = shf_smprintf("%c*%s", MAGIC, pat); + tpat2 = pat; } else { /* float */ tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC); @@ -591,7 +546,7 @@ goto end_repl; end = strnul(s); /* now anchor the beginning of the match */ - if (*pat != '#') + if (fpat != '#') while (sbeg <= end) { if (gmatchx(sbeg, tpat2, false)) break; @@ -600,13 +555,13 @@ } /* now anchor the end of the match */ p = end; - if (*pat != '%') + if (fpat != '%') while (p >= sbeg) { bool gotmatch; c = *p; *p = '\0'; - gotmatch = tobool(gmatchx(sbeg, tpat0, false)); + gotmatch = tobool(gmatchx(sbeg, pat, false)); *p = c; if (gotmatch) break; @@ -622,7 +577,6 @@ goto again_repl; end_repl: afree(tpat1, ATEMP); - do_repl: x.str = s; no_repl: afree(pat, ATEMP); @@ -632,9 +586,11 @@ } case '#': case '%': - /* ! DOBLANK,DOBRACE,DOTILDE */ + /* ! DOBLANK,DOBRACE */ f = (f & DONTRUNCOMMAND) | - DOPAT | DOTEMP | DOSCALAR; + DOPAT | DOTILDE | + DOTEMP | DOSCALAR; + tilde_ok = 1; st->quotew = quote = 0; /* * Prepend open pattern (so | @@ -672,6 +628,9 @@ tilde_ok = 1; break; case '?': + if (*sp == CSUBST) + errorf("%s: parameter null or not set", + st->var->name); f &= ~DOBLANK; f |= DOTEMP; /* FALLTHROUGH */ @@ -767,14 +726,12 @@ st = st->prev; word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS; continue; - case '?': { - char *s = Xrestpos(ds, dp, st->base); + case '?': + dp = Xrestpos(ds, dp, st->base); errorf("%s: %s", st->var->name, - dp == s ? - "parameter null or not set" : - (debunk(s, s, strlen(s) + 1), s)); - } + debunk(dp, dp, strlen(dp) + 1)); + break; case '0': case '/': case 0x100 | '#': @@ -943,6 +900,8 @@ (word == IFS_IWS || word == IFS_NWS) && !ctype(c, C_IFSWS))) { emit_word: + if (f & DOHERESTR) + *dp++ = '\n'; *dp++ = '\0'; cp = Xclose(ds, dp); if (fdo & DOBRACE) @@ -1023,9 +982,8 @@ break; case '=': /* Note first unquoted = for ~ */ - if (!(f & DOTEMP) && !saw_eq && - (Flag(FBRACEEXPAND) || - (f & DOASNTILDE))) { + if (!(f & DOTEMP) && (!Flag(FPOSIX) || + (f & DOASNTILDE)) && !saw_eq) { saw_eq = true; tilde_ok = 1; } @@ -1081,6 +1039,17 @@ } } +static bool +hasnonempty(const char **strv) +{ + size_t i = 0; + + while (strv[i]) + if (*strv[i++]) + return (true); + return (false); +} + /* * Prepare to generate the string returned by ${} substitution. */ @@ -1165,7 +1134,7 @@ } } if (Flag(FNOUNSET) && c == 0 && !zero_ok) - errorf("%s: %s", sp, "parameter not set"); + errorf("%s: parameter not set", sp); /* unqualified variable/string substitution */ *stypep = 0; xp->str = shf_smprintf("%d", c); @@ -1183,7 +1152,7 @@ if (!stype && c == '/') { slen += 2; stype = c; - if (word[slen] == ADELIM) { + if (word[slen] == ADELIM && word[slen + 1] == c) { slen += 2; stype |= 0x80; } @@ -1224,6 +1193,7 @@ /* can't trim a vector (yet) */ case '%': case '#': + case '?': case '0': case '/': case 0x100 | '#': @@ -1309,7 +1279,9 @@ c = stype & 0x7F; /* test the compiler's code generator */ if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' || - (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ + (((stype & 0x80) ? *xp->str == '\0' : xp->str == null) && + (state != XARG || (ifs0 || xp->split ? + (xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ? c == '=' || c == '-' || c == '?' : c == '+'))) || stype == (0x80 | '0') || stype == (0x100 | '#') || stype == (0x100 | 'Q')) @@ -1317,7 +1289,7 @@ state = XBASE; if (Flag(FNOUNSET) && xp->str == null && !zero_ok && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) - errorf("%s: %s", sp, "parameter not set"); + errorf("%s: parameter not set", sp); return (state); } @@ -1353,11 +1325,11 @@ struct ioword *io = *t->ioact; char *name; - if ((io->flag & IOTYPE) != IOREAD) - errorf("%s: %s", "funny $() command", + if ((io->ioflag & IOTYPE) != IOREAD) + errorf("%s: %s", T_funny_command, snptreef(NULL, 32, "%R", io)); - shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, - SHF_MAPHI|SHF_CLEXEC); + shf = shf_open(name = evalstr(io->ioname, DOTILDE), O_RDONLY, + 0, SHF_MAPHI | SHF_CLEXEC); if (shf == NULL) warningf(!Flag(FTALKING), "%s: %s %s: %s", name, "can't open", "$(<...) input", cstrerror(errno)); @@ -1734,7 +1706,7 @@ dp = str_val(global("HOME")); else if (cp[0] == '+' && cp[1] == '\0') dp = str_val(global("PWD")); - else if (cp[0] == '-' && cp[1] == '\0') + else if (ksh_isdash(cp)) dp = str_val(global("OLDPWD")); #ifndef MKSH_NOPWNAM else diff -Nru mksh-50e/exec.c mksh-52c/exec.c --- mksh-50e/exec.c 2015-03-01 16:43:24.000000000 +0100 +++ mksh-52c/exec.c 2016-03-01 19:30:30.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: exec.c,v 1.50 2013/06/10 21:09:27 millert Exp $ */ +/* $OpenBSD: exec.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,10 +23,10 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.137.2.2 2015/03/01 15:42:59 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.172 2016/03/01 18:30:04 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL -#define MKSH_DEFAULT_EXECSHELL "/bin/sh" +#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh" #endif static int comexec(struct op *, struct tbl * volatile, const char **, @@ -39,10 +39,9 @@ static Test_op dbteste_isa(Test_env *, Test_meta); static const char *dbteste_getopnd(Test_env *, Test_op, bool); static void dbteste_error(Test_env *, int, const char *); -static int search_access(const char *, int); /* XXX: horrible kludge to fit within the framework */ -static char *plain_fmt_entry(char *, size_t, unsigned int, const void *); -static char *select_fmt_entry(char *, size_t, unsigned int, const void *); +static void plain_fmt_entry(char *, size_t, unsigned int, const void *); +static void select_fmt_entry(char *, size_t, unsigned int, const void *); /* * execute command tree @@ -92,7 +91,7 @@ t->ioact != NULL && t->ioact[0] != NULL && t->ioact[1] == NULL && /* of type "here document" (or "here string") */ - (t->ioact[0]->flag & IOTYPE) == IOHERE && + (t->ioact[0]->ioflag & IOTYPE) == IOHERE && /* the variable assignment begins with a valid varname */ (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && /* and has no right-hand side (i.e. "varname=") */ @@ -551,6 +550,12 @@ } if ((tp = findcom(cp, FC_BI)) == NULL) errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin"); + if (tp->type == CSHELL && (tp->val.f == c_cat +#ifdef MKSH_PRINTF_BUILTIN + || tp->val.f == c_printf +#endif + )) + break; continue; } else if (tp->val.f == c_exec) { if (ap[1] == NULL) @@ -607,24 +612,28 @@ subst_exstat = 0; break; } -#ifndef MKSH_NO_EXTERNAL_CAT } else if (tp->val.f == c_cat) { - /* - * if we have any flags, do not use the builtin - * in theory, we could allow -u, but that would - * mean to use ksh_getopt here and possibly ad- - * ded complexity and more code and isn't worth - * additional hassle (and the builtin must call - * ksh_getopt already but can't come back here) - */ + /* if we have any flags, do not use the builtin */ if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' && /* argument, begins with -, is not - or -- */ - (ap[1][1] != '-' || ap[1][2] != '\0')) - /* don't look for builtins or functions */ - fcflags = FC_PATH; - else - /* go on, use the builtin */ - break; + (ap[1][1] != '-' || ap[1][2] != '\0')) { + struct tbl *ext_cat; + + ext_cat = findcom(Tcat, FC_PATH | FC_FUNC); + if (ext_cat && (ext_cat->type != CTALIAS || + (ext_cat->flag & ISSET))) + tp = ext_cat; + } + break; +#ifdef MKSH_PRINTF_BUILTIN + } else if (tp->val.f == c_printf) { + struct tbl *ext_printf; + + ext_printf = findcom(Tprintf, FC_PATH | FC_FUNC); + if (ext_printf && (ext_printf->type != CTALIAS || + (ext_printf->flag & ISSET))) + tp = ext_printf; + break; #endif } else if (tp->val.f == c_trap) { t->u.evalflags &= ~DOTCOMEXEC; @@ -705,6 +714,7 @@ /* shell built-in */ case CSHELL: + do_call_builtin: rv = call_builtin(tp, (const char **)ap, null, resetspec); if (resetspec && tp->val.f == c_shift) { l_expand->argc = l_assign->argc; @@ -714,9 +724,9 @@ /* function call */ case CFUNC: { - volatile unsigned char old_xflag; volatile uint32_t old_inuse; const char * volatile old_kshname; + volatile uint8_t old_flags[FNFLAGS]; if (!(tp->flag & ISSET)) { struct tbl *ftp; @@ -729,6 +739,18 @@ break; } if (include(tp->u.fpath, 0, NULL, false) < 0) { + if (!strcmp(cp, Tcat)) { + no_cat_in_FPATH: + tp = findcom(Tcat, FC_BI); + goto do_call_builtin; + } +#ifdef MKSH_PRINTF_BUILTIN + if (!strcmp(cp, Tprintf)) { + no_printf_in_FPATH: + tp = findcom(Tprintf, FC_BI); + goto do_call_builtin; + } +#endif warningf(true, "%s: %s %s %s: %s", cp, "can't open", "function definition file", tp->u.fpath, cstrerror(errno)); @@ -737,6 +759,12 @@ } if (!(ftp = findfunc(cp, hash(cp), false)) || !(ftp->flag & ISSET)) { + if (!strcmp(cp, Tcat)) + goto no_cat_in_FPATH; +#ifdef MKSH_PRINTF_BUILTIN + if (!strcmp(cp, Tprintf)) + goto no_printf_in_FPATH; +#endif warningf(true, "%s: %s %s", cp, "function not defined by", tp->u.fpath); rv = 127; @@ -768,8 +796,9 @@ getopts_reset(1); } - old_xflag = Flag(FXTRACE) ? 1 : 0; - change_xtrace((Flag(FXTRACEREC) ? old_xflag : 0) | + for (type_flags = 0; type_flags < FNFLAGS; ++type_flags) + old_flags[type_flags] = shell_flags[type_flags]; + change_xtrace((Flag(FXTRACEREC) ? Flag(FXTRACE) : 0) | ((tp->flag & TRACE) ? 1 : 0), false); old_inuse = tp->flag & FINUSE; tp->flag |= FINUSE; @@ -781,13 +810,20 @@ } kshname = old_kshname; - change_xtrace(old_xflag, false); + change_xtrace(old_flags[(int)FXTRACE], false); +#ifndef MKSH_LEGACY_MODE + if (tp->flag & FKSH) { + /* Korn style functions restore Flags on return */ + old_flags[(int)FXTRACE] = Flag(FXTRACE); + for (type_flags = 0; type_flags < FNFLAGS; ++type_flags) + shell_flags[type_flags] = old_flags[type_flags]; + } +#endif tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* * Were we deleted while executing? If so, free the - * execution tree. TODO: Unfortunately, the table entry - * is never re-used until the lookup table is expanded. + * execution tree. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { @@ -888,7 +924,7 @@ *tp->args-- = tp->str; #ifndef MKSH_SMALL - if ((fd = open(tp->str, O_RDONLY | O_BINARY)) >= 0) { + if ((fd = binopen2(tp->str, O_RDONLY)) >= 0) { unsigned char *cp; unsigned short m; ssize_t n; @@ -918,10 +954,15 @@ /* restore begin of shebang position (buf+0 or buf+3) */ cp = buf + n; /* bail out if no shebang magic found */ - if ((cp[0] != '#') || (cp[1] != '!')) + if (cp[0] == '#' && cp[1] == '!') + cp += 2; +#ifdef __OS2__ + else if (!strncmp(cp, Textproc, 7) && + (cp[7] == ' ' || cp[7] == '\t')) + cp += 8; +#endif + else goto noshebang; - - cp += 2; /* skip whitespace before shell name */ while (*cp == ' ' || *cp == '\t') ++cp; @@ -956,7 +997,11 @@ (m == /* ECOFF_I386 */ 0x4C01) || (m == /* ECOFF_M68K */ 0x0150 || m == 0x5001) || (m == /* ECOFF_SH */ 0x0500 || m == 0x0005) || - (m == /* "MZ" */ 0x4D5A) || + (m == /* bzip */ 0x425A) || (m == /* "MZ" */ 0x4D5A) || + (m == /* "NE" */ 0x4E45) || (m == /* "LX" */ 0x4C58) || + (m == /* ksh93 */ 0x0B13) || (m == /* LZIP */ 0x4C5A) || + (m == /* xz */ 0xFD37 && buf[2] == 'z' && buf[3] == 'X' && + buf[4] == 'Z') || (m == /* 7zip */ 0x377A) || (m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D)) errorf("%s: not executable: magic %04X", tp->str, m); nomagic: @@ -1020,12 +1065,6 @@ nhash = hash(name); - if (t != NULL && !tobool(t->u.ksh_func)) { - /* drop same-name aliases for POSIX functions */ - if ((tp = ktsearch(&aliases, name, nhash))) - ktdelete(tp); - } - while (/* CONSTCOND */ 1) { tp = findfunc(name, nhash, true); @@ -1200,7 +1239,7 @@ struct tstate ts; for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; ) - if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) { + if ((tp->flag&ISSET) && (all || !mksh_abspath(tp->val.s))) { if (tp->flag&ALLOC) { tp->flag &= ~(ALLOC|ISSET); afree(tp->val.s, APERM); @@ -1210,7 +1249,7 @@ } /* check if path is something we want to find */ -static int +int search_access(const char *fn, int mode) { struct stat sb; @@ -1219,9 +1258,13 @@ /* file does not exist */ return (ENOENT); /* LINTED use of access */ - if (access(fn, mode) < 0) + if (access(fn, mode) < 0) { /* file exists, but we can't access it */ - return (errno); + int eno; + + eno = errno; + return (eno ? eno : EACCES); + } if (mode == X_OK && (!S_ISREG(sb.st_mode) || !(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) /* access(2) may say root can execute everything */ @@ -1261,7 +1304,7 @@ sp = lpath; while (sp != NULL) { xp = Xstring(xs, xp); - if (!(p = cstrchr(sp, ':'))) + if (!(p = cstrchr(sp, MKSH_PATHSEPC))) p = sp + strlen(sp); if (p != sp) { XcheckN(xs, xp, p - sp); @@ -1297,7 +1340,9 @@ if (!tp) internal_errorf("%s: %s", where, wp[0]); builtin_argv0 = wp[0]; - builtin_spec = tobool(!resetspec && (tp->flag & SPEC_BI)); + builtin_spec = tobool(!resetspec && + /*XXX odd use of KEEPASN */ + ((tp->flag & SPEC_BI) || (Flag(FPOSIX) && (tp->flag & KEEPASN)))); shf_reopen(1, SHF_WR, shl_stdout); shl_stdout_ok = true; ksh_getopt_reset(&builtin_opt, GF_ERROR); @@ -1316,9 +1361,9 @@ iosetup(struct ioword *iop, struct tbl *tp) { int u = -1; - char *cp = iop->name; - int iotype = iop->flag & IOTYPE; - bool do_open = true, do_close = false; + char *cp = iop->ioname; + int iotype = iop->ioflag & IOTYPE; + bool do_open = true, do_close = false, do_fstat = false; int flags = 0; struct ioword iotmp; struct stat statb; @@ -1328,8 +1373,8 @@ /* Used for tracing and error messages to print expanded cp */ iotmp = *iop; - iotmp.name = (iotype == IOHERE) ? NULL : cp; - iotmp.flag |= IONAMEXP; + iotmp.ioname = (iotype == IOHERE) ? NULL : cp; + iotmp.ioflag |= IONAMEXP; if (Flag(FXTRACE)) { change_xtrace(2, false); @@ -1347,14 +1392,27 @@ break; case IOWRITE: - flags = O_WRONLY | O_CREAT | O_TRUNC; - /* - * The stat() is here to allow redirections to - * things like /dev/null without error. - */ - if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) && - (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode))) - flags |= O_EXCL; + if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB)) { + /* >file under set -C */ + if (stat(cp, &statb)) { + /* nonexistent file */ + flags = O_WRONLY | O_CREAT | O_EXCL; + } else if (S_ISREG(statb.st_mode)) { + /* regular file, refuse clobbering */ + goto clobber_refused; + } else { + /* + * allow redirections to things + * like /dev/null without error + */ + flags = O_WRONLY; + /* but check again after opening */ + do_fstat = true; + } + } else { + /* >|file or set +C */ + flags = O_WRONLY | O_CREAT | O_TRUNC; + } break; case IORDWR: @@ -1372,12 +1430,12 @@ const char *emsg; do_open = false; - if (*cp == '-' && !cp[1]) { + if (ksh_isdash(cp)) { /* prevent error return below */ u = 1009; do_close = true; } else if ((u = check_fd(cp, - X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK), + X_OK | ((iop->ioflag & IORDUP) ? R_OK : W_OK), &emsg)) < 0) { char *sp; @@ -1386,7 +1444,7 @@ afree(sp, ATEMP); return (-1); } - if (u == iop->unit) + if (u == (int)iop->unit) /* "dup from" == "dup to" */ return (0); break; @@ -1398,7 +1456,16 @@ warningf(true, "%s: %s", cp, "restricted"); return (-1); } - u = open(cp, flags | O_BINARY, 0666); + u = binopen3(cp, flags, 0666); + if (do_fstat && u >= 0) { + /* prevent race conditions */ + if (fstat(u, &statb) || S_ISREG(statb.st_mode)) { + close(u); + clobber_refused: + u = -1; + errno = EEXIST; + } + } } if (u < 0) { /* herein() may already have printed message */ @@ -1414,7 +1481,7 @@ /* Do not save if it has already been redirected (i.e. "cat >x >y"). */ if (e->savefd[iop->unit] == 0) { /* If these are the same, it means unit was previously closed */ - if (u == iop->unit) + if (u == (int)iop->unit) e->savefd[iop->unit] = -1; else /* @@ -1429,13 +1496,13 @@ if (do_close) close(iop->unit); - else if (u != iop->unit) { + else if (u != (int)iop->unit) { if (ksh_dup2(u, iop->unit, true) < 0) { int eno; char *sp; eno = errno; - warningf(true, "%s %s %s", + warningf(true, "%s %s: %s", "can't finish (dup) redirection", (sp = snptreef(NULL, 32, "%R", &iotmp)), cstrerror(eno)); @@ -1451,7 +1518,7 @@ * causes the shell to close its copies */ else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { - if (iop->flag & IORDUP) + if (iop->ioflag & IORDUP) /* possible exec <&p */ coproc_read_close(u); else @@ -1471,9 +1538,9 @@ * unquoted, the string is expanded first. */ static int -hereinval(const char *content, int sub, char **resbuf, struct shf *shf) +hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf) { - const char * volatile ccp = content; + const char * volatile ccp = iop->heredoc; struct source *s, *osource; osource = source; @@ -1484,13 +1551,15 @@ /* special to iosetup(): don't print error */ return (-2); } - if (sub) { + if (iop->ioflag & IOHERESTR) { + ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC); + } else if (sub) { /* do substitutions on the content of heredoc */ s = pushs(SSTRING, ATEMP); s->start = s->str = ccp; source = s; if (yylex(sub) != LWORD) - internal_errorf("%s: %s", "herein", "yylex"); + internal_errorf("herein: yylex"); source = osource; ccp = evalstr(yylval.cp, DOSCALAR | DOHEREDOC); } @@ -1512,26 +1581,19 @@ struct temp *h; int i; - /* ksh -c 'cat <heredoc == NULL) { - warningf(true, "%s missing", "here document"); - /* special to iosetup(): don't print error */ - return (-2); - } - /* lexer substitution flags */ - i = (iop->flag & IOEVAL) ? (ONEWORD | HEREDOC) : 0; + i = (iop->ioflag & IOEVAL) ? (ONEWORD | HEREDOC) : 0; /* skip all the fd setup if we just want the value */ if (resbuf != NULL) - return (hereinval(iop->heredoc, i, resbuf, NULL)); + return (hereinval(iop, i, resbuf, NULL)); /* * Create temp file to hold content (done before newenv * so temp doesn't get removed too soon). */ h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps); - if (!(shf = h->shf) || (fd = open(h->tffn, O_RDONLY | O_BINARY, 0)) < 0) { + if (!(shf = h->shf) || (fd = binopen3(h->tffn, O_RDONLY, 0)) < 0) { i = errno; warningf(true, "can't %s temporary file %s: %s", !shf ? "create" : "open", h->tffn, cstrerror(i)); @@ -1541,7 +1603,7 @@ return (-2); } - if (hereinval(iop->heredoc, i, NULL, shf) == -2) { + if (hereinval(iop, i, NULL, shf) == -2) { close(fd); /* special to iosetup(): don't print error */ return (-2); @@ -1587,9 +1649,9 @@ if (call_builtin(findcom("read", FC_BI), read_args, Tselect, false)) return (NULL); - s = str_val(global("REPLY")); - if (*s && getn(s, &i)) - return ((i >= 1 && i <= argct) ? ap[i - 1] : null); + if (*(s = str_val(global("REPLY")))) + return ((getn(s, &i) && i >= 1 && i <= argct) ? + ap[i - 1] : null); print_menu = true; } } @@ -1600,7 +1662,7 @@ }; /* format a single select menu item */ -static char * +static void select_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) { const struct select_menu_info *smi = @@ -1608,7 +1670,6 @@ shf_snprintf(buf, buflen, "%*u) %s", smi->num_width, i + 1, smi->args[i]); - return (buf); } /* @@ -1654,11 +1715,10 @@ true); } -static char * +static void plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) { strlcpy(buf, ((const char * const *)arg)[i], buflen); - return (buf); } void diff -Nru mksh-50e/expr.c mksh-52c/expr.c --- mksh-50e/expr.c 2015-01-25 16:44:30.000000000 +0100 +++ mksh-52c/expr.c 2016-03-01 19:30:04.000000000 +0100 @@ -2,8 +2,8 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.76.2.1 2015/01/25 15:44:05 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.83 2016/03/01 18:29:38 tg Exp $"); /* the order of these enums is constrained by the order of opinfo[] */ enum token { @@ -227,7 +227,7 @@ exprtoken(es); if (es->tok == END) { es->tok = LIT; - es->val = tempvar(); + es->val = tempvar(""); } v = intvar(es, evalexpr(es, MAX_PREC)); @@ -649,7 +649,7 @@ cp += len; } if (es->noassign) { - es->val = tempvar(); + es->val = tempvar(""); es->val->flag |= EXPRLVALUE; } else { strndupx(tvar, es->tokp, cp - es->tokp, ATEMP); @@ -659,12 +659,16 @@ es->tok = VAR; } else if (c == '1' && cp[1] == '#') { cp += 2; - cp += utf_ptradj(cp); + if (*cp) + cp += utf_ptradj(cp); strndupx(tvar, es->tokp, cp - es->tokp, ATEMP); goto process_tvar; #ifndef MKSH_SMALL } else if (c == '\'') { - ++cp; + if (*++cp == '\0') { + es->tok = END; + evalerr(es, ET_UNEXPECTED, NULL); + } cp += utf_ptradj(cp); if (*cp++ != '\'') evalerr(es, ET_STR, @@ -683,7 +687,7 @@ c = *cp++; strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP); process_tvar: - es->val = tempvar(); + es->val = tempvar(""); es->val->flag &= ~INTEGER; es->val->type = 0; es->val->val.s = tvar; @@ -718,17 +722,19 @@ } struct tbl * -tempvar(void) +tempvar(const char *vname) { struct tbl *vp; + size_t vsize; - vp = alloc(sizeof(struct tbl), ATEMP); + vsize = strlen(vname) + 1; + vp = alloc(offsetof(struct tbl, name[0]) + vsize, ATEMP); + memcpy(vp->name, vname, vsize); vp->flag = ISSET|INTEGER; vp->type = 0; vp->areap = ATEMP; vp->ua.hval = 0; vp->val.i = 0; - vp->name[0] = '\0'; return (vp); } @@ -743,7 +749,7 @@ (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER)) return (vp); - vq = tempvar(); + vq = tempvar(""); if (setint_v(vq, vp, es->arith) == NULL) { if (vp->flag & EXPRINEVAL) evalerr(es, ET_RECURSIVE, vp->name); @@ -916,6 +922,7 @@ return (rv); } +#ifndef MIRBSD_BOOTFLOPPY /* From: X11/xc/programs/xterm/wcwidth.c,v 1.8 2014/06/24 19:53:53 tg Exp $ */ struct mb_ucsrange { @@ -1195,3 +1202,4 @@ return (2); return (1); } +#endif diff -Nru mksh-50e/funcs.c mksh-52c/funcs.c --- mksh-50e/funcs.c 2015-01-25 16:36:09.000000000 +0100 +++ mksh-52c/funcs.c 2016-02-26 21:57:09.000000000 +0100 @@ -1,12 +1,12 @@ -/* $OpenBSD: c_ksh.c,v 1.34 2013/12/17 16:37:05 deraadt Exp $ */ -/* $OpenBSD: c_sh.c,v 1.45 2014/08/27 08:26:04 jmc Exp $ */ +/* $OpenBSD: c_ksh.c,v 1.37 2015/09/10 22:48:58 nicm Exp $ */ +/* $OpenBSD: c_sh.c,v 1.46 2015/07/20 20:46:24 guenther Exp $ */ /* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */ /* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */ /*- * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - * 2010, 2011, 2012, 2013, 2014, 2015 - * Thorsten Glaser + * 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -38,7 +38,7 @@ #endif #endif -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.259.2.2 2015/01/25 15:35:44 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.295 2016/02/26 20:56:43 tg Exp $"); #if HAVE_KILLPG /* @@ -64,6 +64,8 @@ static int c_suspend(const char **); #endif +static int do_whence(const char **, int, bool, bool); + /* getn() that prints error */ static int bi_getn(const char *as, int *ai) @@ -99,11 +101,11 @@ {Talias, c_alias}, {"*=break", c_brkcont}, {Tgbuiltin, c_builtin}, - {"cat", c_cat}, + {Tcat, c_cat}, {"cd", c_cd}, /* dash compatibility hack */ {"chdir", c_cd}, - {"command", c_command}, + {Tcommand, c_command}, {"*=continue", c_brkcont}, {"echo", c_print}, {"*=eval", c_eval}, @@ -127,6 +129,7 @@ {"*=return", c_exitreturn}, {Tsgset, c_set}, {"*=shift", c_shift}, + {"=source", c_dot}, #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID {"suspend", c_suspend}, #endif @@ -138,7 +141,7 @@ {"ulimit", c_ulimit}, {"umask", c_umask}, {Tunalias, c_unalias}, - {Tsgunset, c_unset}, + {"*=unset", c_unset}, {"=wait", c_wait}, {"whence", c_whence}, #ifndef MKSH_UNEMPLOYED @@ -152,7 +155,7 @@ {"mknod", c_mknod}, #endif #ifdef MKSH_PRINTF_BUILTIN - {"printf", c_printf}, + {Tprintf, c_printf}, #endif #if HAVE_SELECT {"sleep", c_sleep}, @@ -161,6 +164,9 @@ /* alias to "true" for historical reasons */ {"domainname", c_true}, #endif +#ifdef __OS2__ + {Textproc, c_true}, +#endif {NULL, (int (*)(const char **))NULL} }; @@ -224,7 +230,7 @@ static Test_op ptest_isa(Test_env *, Test_meta); static const char *ptest_getopnd(Test_env *, Test_op, bool); static void ptest_error(Test_env *, int, const char *); -static char *kill_fmt_entry(char *, size_t, unsigned int, const void *); +static void kill_fmt_entry(char *, size_t, unsigned int, const void *); static void p_time(struct shf *, bool, long, int, int, const char *, const char *); @@ -274,20 +280,18 @@ int c_print(const char **wp) { -#define PO_NL BIT(0) /* print newline */ -#define PO_EXPAND BIT(1) /* expand backslash sequences */ -#define PO_PMINUSMINUS BIT(2) /* print a -- argument */ -#define PO_HIST BIT(3) /* print to history instead of stdout */ -#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */ int fd = 1, c; - int flags = PO_EXPAND | PO_NL; - const char *s, *emsg; + const char *s; XString xs; char *xp; + /* print newline; expand backslash sequences */ + bool po_nl = true, po_exp = true; + /* print to history instead of file descriptor / stdout */ + bool po_hist = false; if (wp[0][0] == 'e') { - /* echo builtin */ - wp++; + /* "echo" builtin */ + ++wp; #ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT if (Flag(FSH)) { /* @@ -295,7 +299,7 @@ * one that supports -e but does not enable it by * default */ - flags = PO_NL; + po_exp = false; } #endif if (Flag(FPOSIX) || @@ -305,14 +309,14 @@ Flag(FAS_BUILTIN)) { /* Debian Policy 10.4 compliant "echo" builtin */ if (*wp && !strcmp(*wp, "-n")) { - /* we recognise "-n" only as the first arg */ - flags = 0; - wp++; - } else - /* otherwise, we print everything as-is */ - flags = PO_NL; + /* recognise "-n" only as the first arg */ + po_nl = false; + ++wp; + } + /* print everything as-is */ + po_exp = false; } else { - int nflags = flags; + bool new_exp = po_exp, new_nl = po_nl; /** * a compromise between sysV and BSD echo commands: @@ -325,62 +329,65 @@ * quences are enabled by default. */ - while ((s = *wp) && *s == '-' && s[1]) { - while (*++s) - if (*s == 'n') - nflags &= ~PO_NL; - else if (*s == 'e') - nflags |= PO_EXPAND; - else if (*s == 'E') - nflags &= ~PO_EXPAND; - else - /* - * bad option: don't use - * nflags, print argument - */ - break; - - if (*s) - break; - wp++; - flags = nflags; + print_tradparse_arg: + if ((s = *wp) && *s++ == '-' && *s) { + print_tradparse_ch: + switch ((c = *s++)) { + case 'E': + new_exp = false; + goto print_tradparse_ch; + case 'e': + new_exp = true; + goto print_tradparse_ch; + case 'n': + new_nl = false; + goto print_tradparse_ch; + case '\0': + po_exp = new_exp; + po_nl = new_nl; + ++wp; + goto print_tradparse_arg; + } } } } else { - int optc; - const char *opts = "Rnprsu,"; + /* "print" builtin */ + const char *opts = "npRrsu,"; + const char *emsg; + /* print a "--" argument */ + bool po_pminusminus = false; - while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) - switch (optc) { - case 'R': - /* fake BSD echo command */ - flags |= PO_PMINUSMINUS; - flags &= ~PO_EXPAND; - opts = "ne"; - break; + while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1) + switch (c) { case 'e': - flags |= PO_EXPAND; + po_exp = true; break; case 'n': - flags &= ~PO_NL; + po_nl = false; break; case 'p': if ((fd = coproc_getfd(W_OK, &emsg)) < 0) { - bi_errorf("%s: %s", "-p", emsg); + bi_errorf("-p: %s", emsg); return (1); } break; + case 'R': + /* fake BSD echo command */ + po_pminusminus = true; + po_exp = false; + opts = "en"; + break; case 'r': - flags &= ~PO_EXPAND; + po_exp = false; break; case 's': - flags |= PO_HIST; + po_hist = true; break; case 'u': if (!*(s = builtin_opt.optarg)) fd = 0; else if ((fd = check_fd(s, W_OK, &emsg)) < 0) { - bi_errorf("%s: %s: %s", "-u", s, emsg); + bi_errorf("-u%s: %s", s, emsg); return (1); } break; @@ -389,22 +396,23 @@ } if (!(builtin_opt.info & GI_MINUSMINUS)) { - /* treat a lone - like -- */ + /* treat a lone "-" like "--" */ if (wp[builtin_opt.optind] && ksh_isdash(wp[builtin_opt.optind])) builtin_opt.optind++; - } else if (flags & PO_PMINUSMINUS) - builtin_opt.optind--; + } else if (po_pminusminus) + builtin_opt.optind--; wp += builtin_opt.optind; } Xinit(xs, xp, 128, ATEMP); - while (*wp != NULL) { + if (*wp != NULL) { + print_read_arg: s = *wp; while ((c = *s++) != '\0') { Xcheck(xs, xp); - if ((flags & PO_EXPAND) && c == '\\') { + if (po_exp && c == '\\') { s_ptr = s; c = unbksl(false, s_get, s_put); s = s_ptr; @@ -412,11 +420,11 @@ /* rejected by generic function */ switch ((c = *s++)) { case 'c': - flags &= ~PO_NL; + po_nl = false; /* AT&T brain damage */ continue; case '\0': - s--; + --s; c = '\\'; break; default: @@ -427,25 +435,31 @@ char ts[4]; ts[utf_wctomb(ts, c - 0x100)] = 0; - for (c = 0; ts[c]; ++c) + c = 0; + do { Xput(xs, xp, ts[c]); + } while (ts[++c]); continue; } } Xput(xs, xp, c); } - if (*++wp != NULL) + if (*++wp != NULL) { Xput(xs, xp, ' '); + goto print_read_arg; + } } - if (flags & PO_NL) + if (po_nl) Xput(xs, xp, '\n'); - if (flags & PO_HIST) { + c = 0; + if (po_hist) { Xput(xs, xp, '\0'); - histsave(&source->line, Xstring(xs, xp), true, false); + histsave(&source->line, Xstring(xs, xp), HIST_STORE, false); Xfree(xs, xp); } else { - int len = Xlength(xs, xp); + size_t len = Xlength(xs, xp); + bool po_coproc = false; int opipe = 0; /* @@ -455,30 +469,36 @@ * not enough). */ if (coproc.write >= 0 && coproc.write == fd) { - flags |= PO_COPROC; + po_coproc = true; opipe = block_pipe(); } - for (s = Xstring(xs, xp); len > 0; ) { - if ((c = write(fd, s, len)) < 0) { - if (flags & PO_COPROC) - restore_pipe(opipe); + + s = Xstring(xs, xp); + while (len > 0) { + ssize_t nwritten; + + if ((nwritten = write(fd, s, len)) < 0) { if (errno == EINTR) { - /* allow user to ^C out */ + if (po_coproc) + restore_pipe(opipe); + /* give the user a chance to ^C out */ intrcheck(); - if (flags & PO_COPROC) + /* interrupted, try again */ + if (po_coproc) opipe = block_pipe(); continue; } - return (1); + c = 1; + break; } - s += c; - len -= c; + s += nwritten; + len -= nwritten; } - if (flags & PO_COPROC) + if (po_coproc) restore_pipe(opipe); } - return (0); + return (c); } static int @@ -496,14 +516,10 @@ int c_whence(const char **wp) { - struct tbl *tp; - const char *id; - bool pflag = false, vflag = false, Vflag = false; - int rv = 0, optc, fcflags; - bool iam_whence = wp[0][0] == 'w'; - const char *opts = iam_whence ? "pv" : "pvV"; + int optc; + bool pflag = false, vflag = false; - while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) + while ((optc = ksh_getopt(wp, &builtin_opt, "pv")) != -1) switch (optc) { case 'p': pflag = true; @@ -511,80 +527,82 @@ case 'v': vflag = true; break; + case '?': + return (1); + } + wp += builtin_opt.optind; + + return (do_whence(wp, pflag ? FC_PATH : + FC_BI | FC_FUNC | FC_PATH | FC_WHENCE, vflag, false)); +} + +/* note: command without -vV is dealt with in comexec() */ +int +c_command(const char **wp) +{ + int optc, fcflags = FC_BI | FC_FUNC | FC_PATH | FC_WHENCE; + bool vflag = false; + + /* options not sorted to facilitate string pooling */ + while ((optc = ksh_getopt(wp, &builtin_opt, "Vpv")) != -1) + switch (optc) { + case 'p': + fcflags |= FC_DEFPATH; + break; case 'V': - Vflag = true; + vflag = true; + break; + case 'v': + vflag = false; break; case '?': return (1); } wp += builtin_opt.optind; - fcflags = FC_BI | FC_PATH | FC_FUNC; - if (!iam_whence) { - /* Note that -p on its own is deal with in comexec() */ - if (pflag) - fcflags |= FC_DEFPATH; - /* - * Convert command options to whence options - note that - * command -pV uses a different path search than whence -v - * or whence -pv. This should be considered a feature. - */ - vflag = Vflag; - } - if (pflag) - fcflags &= ~(FC_BI | FC_FUNC); + return (do_whence(wp, fcflags, vflag, true)); +} - while ((vflag || rv == 0) && (id = *wp++) != NULL) { - uint32_t h = 0; +static int +do_whence(const char **wp, int fcflags, bool vflag, bool iscommand) +{ + uint32_t h; + int rv = 0; + struct tbl *tp; + const char *id; + while ((vflag || rv == 0) && (id = *wp++) != NULL) { + h = hash(id); tp = NULL; - if ((iam_whence || vflag) && !pflag) - tp = ktsearch(&keywords, id, h = hash(id)); - if (!tp && !pflag) { - tp = ktsearch(&aliases, id, h ? h : hash(id)); + + if (fcflags & FC_WHENCE) + tp = ktsearch(&keywords, id, h); + if (!tp && (fcflags & FC_WHENCE)) { + tp = ktsearch(&aliases, id, h); if (tp && !(tp->flag & ISSET)) tp = NULL; } if (!tp) tp = findcom(id, fcflags); - if (vflag || (tp->type != CALIAS && tp->type != CEXEC && - tp->type != CTALIAS)) - shf_puts(id, shl_stdout); - if (vflag) { - switch (tp->type) { - case CKEYWD: - case CALIAS: - case CFUNC: - case CSHELL: - shf_puts(" is a", shl_stdout); - break; - } - switch (tp->type) { - case CKEYWD: - case CSHELL: - case CTALIAS: - case CEXEC: - shf_putc(' ', shl_stdout); - break; - } - } switch (tp->type) { + case CSHELL: + case CFUNC: case CKEYWD: - if (vflag) - shf_puts("reserved word", shl_stdout); + shf_puts(id, shl_stdout); break; - case CALIAS: + } + + switch (tp->type) { + case CSHELL: if (vflag) - shprintf("n %s%s for ", - (tp->flag & EXPORT) ? "exported " : null, - Talias); - if (!iam_whence && !vflag) - shprintf("%s %s=", Talias, id); - print_value_quoted(shl_stdout, tp->val.s); + shprintf(" is a %sshell %s", + (tp->flag & SPEC_BI) ? "special " : "", + Tbuiltin); break; case CFUNC: if (vflag) { + shf_puts(" is a", shl_stdout); if (tp->flag & EXPORT) shf_puts("n exported", shl_stdout); if (tp->flag & TRACE) @@ -598,34 +616,42 @@ shf_puts(T_function, shl_stdout); } break; - case CSHELL: - if (vflag) { - if (tp->flag & SPEC_BI) - shf_puts("special ", shl_stdout); - shprintf("%s %s", "shell", Tbuiltin); - } - break; - case CTALIAS: case CEXEC: + case CTALIAS: if (tp->flag & ISSET) { if (vflag) { - shf_puts("is ", shl_stdout); + shprintf("%s is ", id); if (tp->type == CTALIAS) shprintf("a tracked %s%s for ", (tp->flag & EXPORT) ? - "exported " : null, + "exported " : "", Talias); } shf_puts(tp->val.s, shl_stdout); } else { if (vflag) - shf_puts("not found", shl_stdout); + shprintf("%s not found", id); rv = 1; } break; - default: - shf_puts(" is *GOK*", shl_stdout); + case CALIAS: + if (vflag) { + shprintf("%s is an %s%s for ", id, + (tp->flag & EXPORT) ? "exported " : "", + Talias); + } else if (iscommand) + shprintf("%s %s=", Talias, id); + print_value_quoted(shl_stdout, tp->val.s); break; + case CKEYWD: + if (vflag) + shf_puts(" is a reserved word", shl_stdout); + break; +#ifndef MKSH_SMALL + default: + bi_errorf("%s is of unknown type %d", id, tp->type); + return (1); +#endif } if (vflag || !rv) shf_putc('\n', shl_stdout); @@ -633,17 +659,6 @@ return (rv); } -/* Deal with command -vV - command -p dealt with in comexec() */ -int -c_command(const char **wp) -{ - /* - * Let c_whence do the work. Note that c_command() must be - * a distinct function from c_whence() (tested in comexec()). - */ - return (c_whence(wp)); -} - /* typeset, global, export, and readonly */ static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool); static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool, @@ -778,9 +793,13 @@ if (fieldstr && !bi_getn(fieldstr, &field)) return (1); - if (basestr && (!bi_getn(basestr, &base) || base < 1 || base > 36)) { - bi_errorf("%s: %s", "bad integer base", basestr); - return (1); + if (basestr) { + if (!getn(basestr, &base)) { + bi_errorf("%s: %s", "bad integer base", basestr); + return (1); + } + if (base < 1 || base > 36) + base = 10; } if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] && @@ -966,27 +985,27 @@ */ shprintf("%s %s", Ttypeset, ""); if (((vp->flag & (ARRAY | ASSOC)) == ASSOC)) - shprintf("%s ", "-n"); + shprintf("-%c ", 'n'); if ((vp->flag & INTEGER)) - shprintf("%s ", "-i"); + shprintf("-%c ", 'i'); if ((vp->flag & EXPORT)) - shprintf("%s ", "-x"); + shprintf("-%c ", 'x'); if ((vp->flag & RDONLY)) - shprintf("%s ", "-r"); + shprintf("-%c ", 'r'); if ((vp->flag & TRACE)) - shprintf("%s ", "-t"); + shprintf("-%c ", 't'); if ((vp->flag & LJUST)) shprintf("-L%d ", vp->u2.field); if ((vp->flag & RJUST)) shprintf("-R%d ", vp->u2.field); if ((vp->flag & ZEROFIL)) - shprintf("%s ", "-Z"); + shprintf("-%c ", 'Z'); if ((vp->flag & LCASEV)) - shprintf("%s ", "-l"); + shprintf("-%c ", 'l'); if ((vp->flag & UCASEV_AL)) - shprintf("%s ", "-u"); + shprintf("-%c ", 'u'); if ((vp->flag & INT_U)) - shprintf("%s ", "-U"); + shprintf("-%c ", 'U'); } else if (pflag) { shprintf("%s %s", istset ? Ttypeset : (flag & EXPORT) ? Texport : Treadonly, ""); @@ -1298,12 +1317,13 @@ rv = j_resume(*wp, bg); else rv = j_resume("%%", bg); - return (bg ? 0 : rv); + /* fg returns $? of the job unless POSIX */ + return ((bg | Flag(FPOSIX)) ? 0 : rv); } #endif /* format a single kill item */ -static char * +static void kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) { const struct kill_info *ki = (const struct kill_info *)arg; @@ -1313,7 +1333,6 @@ ki->num_width, i, ki->name_width, sigtraps[i].name, sigtraps[i].mess); - return (buf); } int @@ -1327,7 +1346,7 @@ /* assume old style options if -digits or -UPPERCASE */ if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) || ksh_isupper(p[1]))) { - if (!(t = gettrap(p + 1, false))) { + if (!(t = gettrap(p + 1, false, false))) { bi_errorf("bad signal '%s'", p + 1); return (1); } @@ -1341,7 +1360,8 @@ lflag = true; break; case 's': - if (!(t = gettrap(builtin_opt.optarg, true))) { + if (!(t = gettrap(builtin_opt.optarg, + true, false))) { bi_errorf("bad signal '%s'", builtin_opt.optarg); return (1); @@ -1367,24 +1387,32 @@ for (; wp[i]; i++) { if (!bi_getn(wp[i], &n)) return (1); -#if (NSIG < 128) - if (n > 128 && n < 128 + NSIG) +#if (ksh_NSIG <= 128) + if (n > 128 && n < 128 + ksh_NSIG) n -= 128; #endif - if (n > 0 && n < NSIG) + if (n > 0 && n < ksh_NSIG) shprintf("%s\n", sigtraps[n].name); else shprintf("%d\n", n); } + } else if (Flag(FPOSIX)) { + n = 1; + while (n < ksh_NSIG) { + shf_puts(sigtraps[n].name, shl_stdout); + shf_putc(++n == ksh_NSIG ? '\n' : ' ', + shl_stdout); + } } else { - ssize_t w, mess_cols, mess_octs; - int j; - struct kill_info ki; + ssize_t w, mess_cols = 0, mess_octs = 0; + int j = ksh_NSIG - 1; + struct kill_info ki = { 0, 0 }; - for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10) + do { ki.num_width++; - ki.name_width = mess_cols = mess_octs = 0; - for (j = 0; j < NSIG; j++) { + } while ((j /= 10)); + + for (j = 1; j < ksh_NSIG; j++) { w = strlen(sigtraps[j].name); if (w > ki.name_width) ki.name_width = w; @@ -1396,7 +1424,7 @@ mess_cols = w; } - print_columns(shl_stdout, (unsigned int)(NSIG - 1), + print_columns(shl_stdout, (unsigned int)(ksh_NSIG - 1), kill_fmt_entry, (void *)&ki, ki.num_width + 1 + ki.name_width + 1 + mess_octs, ki.num_width + 1 + ki.name_width + 1 + mess_cols, @@ -1428,7 +1456,8 @@ getopts_reset(int val) { if (val >= 1) { - ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT); + ksh_getopt_reset(&user_opt, GF_NONAME | + (Flag(FPOSIX) ? 0 : GF_PLUSOPT)); user_opt.optind = user_opt.uoptind = val; } } @@ -1662,8 +1691,11 @@ mode_t new_umask; if (ksh_isdigit(*cp)) { - for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++) - new_umask = new_umask * 8 + (*cp - '0'); + new_umask = 0; + while (*cp >= ord('0') && *cp <= ord('7')) { + new_umask = new_umask * 8 + ksh_numdig(*cp); + ++cp; + } if (*cp) { bi_errorf("bad number"); return (1); @@ -1766,7 +1798,11 @@ bi_errorf("missing argument"); return (1); } - if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) { + file = search_path(cp, path, R_OK, &errcode); + if (!file && errcode == ENOENT && wp[0][0] == 's' && + search_access(cp, R_OK) == 0) + file = cp; + if (!file) { bi_errorf("%s: %s", cp, cstrerror(errcode)); return (1); } @@ -1817,19 +1853,22 @@ c_read(const char **wp) { #define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS)) - int c, fd = 0, rv = 0, lastparm = 0; + int c, fd = 0, rv = 0; bool savehist = false, intoarray = false, aschars = false; bool rawmode = false, expanding = false; + bool lastparmmode = false, lastparmused = false; enum { LINES, BYTES, UPTO, READALL } readmode = LINES; char delim = '\n'; size_t bytesleft = 128, bytesread; - struct tbl *vp /* FU gcc */ = NULL, *vq; + struct tbl *vp /* FU gcc */ = NULL, *vq = NULL; char *cp, *allocd = NULL, *xp; const char *ccp; XString xs; size_t xsave = 0; mksh_ttyst tios; bool restore_tios = false; + /* to catch read -aN2 foo[i] */ + bool subarray = false; #if HAVE_SELECT bool hastimeout = false; struct timeval tv, tvlim; @@ -1855,14 +1894,14 @@ if (!bi_getn(builtin_opt.optarg, &c)) return (2); if (c == -1) { - readmode = READALL; + readmode = readmode == BYTES ? READALL : UPTO; bytesleft = 1024; } else bytesleft = (unsigned int)c; break; case 'p': if ((fd = coproc_getfd(R_OK, &ccp)) < 0) { - bi_errorf("%s: %s", "-p", ccp); + bi_errorf("-p: %s", ccp); return (2); } break; @@ -1954,8 +1993,9 @@ break; case 0: /* timeout expired for this call */ - rv = 1; - goto c_read_out; + bytesread = 0; + /* fake EOF read; all cases return 1 */ + goto c_read_didread; default: bi_errorf("%s: %s", Tselect, cstrerror(errno)); rv = 2; @@ -1980,6 +2020,7 @@ goto c_read_readloop; } + c_read_didread: switch (readmode) { case READALL: if (bytesread == 0) { @@ -2002,7 +2043,7 @@ if (bytesread == 0) { /* end of file reached */ rv = 1; - xp = Xstring(xs, xp); + /* may be partial read: $? = 1, but content */ goto c_read_readdone; } xp += bytesread; @@ -2065,13 +2106,14 @@ } if (savehist) - histsave(&source->line, Xstring(xs, xp), true, false); + histsave(&source->line, Xstring(xs, xp), HIST_STORE, false); ccp = cp = Xclose(xs, xp); expanding = false; XinitN(xs, 128, ATEMP); if (intoarray) { vp = global(*wp); + subarray = last_lookup_was_array; if (vp->flag & RDONLY) { c_read_splitro: bi_errorf("read-only: %s", *wp); @@ -2080,10 +2122,10 @@ afree(cp, ATEMP); goto c_read_out; } - /* exporting an array is currently pointless */ - unset(vp, 1); /* counter for array index */ - c = 0; + c = subarray ? arrayindex(vp) : 0; + /* exporting an array is currently pointless */ + unset(vp, subarray ? 0 : 1); } if (!aschars) { /* skip initial IFS whitespace */ @@ -2123,7 +2165,7 @@ } if (!intoarray && wp[1] == NULL) - lastparm = 1; + lastparmmode = true; c_read_splitlast: /* copy until IFS character */ @@ -2148,16 +2190,23 @@ } xsave = Xsavepos(xs, xp); /* copy word delimiter: IFSWS+IFS,IFSWS */ + expanding = false; while (bytesread) { char ch; ch = *ccp; if (!ctype(ch, C_IFS)) break; - Xcheck(xs, xp); - Xput(xs, xp, ch); + if (lastparmmode && !expanding && !rawmode && ch == '\\') { + expanding = true; + } else { + Xcheck(xs, xp); + Xput(xs, xp, ch); + } ++ccp; --bytesread; + if (expanding) + continue; if (!ctype(ch, C_IFSWS)) break; } @@ -2168,17 +2217,28 @@ --bytesread; } /* if no more parameters, rinse and repeat */ - if (lastparm && bytesread) { - ++lastparm; + if (lastparmmode && bytesread) { + lastparmused = true; goto c_read_splitlast; } /* get rid of the delimiter unless we pack the rest */ - if (lastparm < 2) + if (!lastparmused) xp = Xrestpos(xs, xp, xsave); c_read_gotword: Xput(xs, xp, '\0'); if (intoarray) { - vq = arraysearch(vp, c++); + if (subarray) { + /* array element passed, accept first read */ + if (vq) { + bi_errorf("nested arrays not yet supported"); + goto c_read_spliterr; + } + vq = vp; + if (c) + /* [0] doesn't */ + vq->flag |= AINDEX; + } else + vq = arraysearch(vp, c++); } else { vq = global(*wp); /* must be checked before exporting */ @@ -2271,41 +2331,44 @@ int c_trap(const char **wp) { - int i; + Trap *p = sigtraps; + int i = ksh_NSIG; const char *s; - Trap *p; if (ksh_getopt(wp, &builtin_opt, null) == '?') return (1); wp += builtin_opt.optind; if (*wp == NULL) { - for (p = sigtraps, i = NSIG + 1; --i >= 0; p++) - if (p->trap != NULL) { + do { + if (p->trap) { shf_puts("trap -- ", shl_stdout); print_value_quoted(shl_stdout, p->trap); shprintf(" %s\n", p->name); } + ++p; + } while (i--); return (0); } - /* - * Use case sensitive lookup for first arg so the - * command 'exit' isn't confused with the pseudo-signal - * 'EXIT'. - */ - /* get command */ - s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; - if (s != NULL && s[0] == '-' && s[1] == '\0') + if (getn(*wp, &i)) { + /* first argument is a signal number, reset them all */ s = NULL; + } else { + /* first argument must be a command, then */ + s = *wp++; + /* reset traps? */ + if (ksh_isdash(s)) + s = NULL; + } - /* set/clear traps */ + /* set/clear the traps */ i = 0; - while (*wp != NULL) - if ((p = gettrap(*wp++, true)) == NULL) { - warningf(true, "%s: %s '%s'", builtin_argv0, - "bad signal", wp[-1]); - ++i; + while (*wp) + if (!(p = gettrap(*wp++, true, true))) { + warningf(true, "%s: bad signal '%s'", + builtin_argv0, wp[-1]); + i = 1; } else settrap(p, s); return (i); @@ -2315,20 +2378,14 @@ c_exitreturn(const char **wp) { int n, how = LEXIT; - const char *arg; - - if (ksh_getopt(wp, &builtin_opt, null) == '?') - goto c_exitreturn_err; - arg = wp[builtin_opt.optind]; - if (arg) { - if (!getn(arg, &n)) { - exstat = 1; - warningf(true, "%s: %s", arg, "bad number"); - } else - exstat = n & 0xFF; + if (wp[1]) { + if (wp[2]) + goto c_exitreturn_err; + exstat = bi_getn(wp[1], &n) ? (n & 0xFF) : 1; } else if (trap_exstat != -1) exstat = trap_exstat; + if (wp[0][0] == 'r') { /* return */ struct env *ep; @@ -2349,12 +2406,13 @@ how = LSHELL; } - /* get rid of any i/o redirections */ + /* get rid of any I/O redirections */ quitenv(NULL); unwind(how); /* NOTREACHED */ c_exitreturn_err: + bi_errorf("too many arguments"); return (1); } @@ -2376,7 +2434,7 @@ goto c_brkcont_err; if (n <= 0) { /* AT&T ksh does this for non-interactive shells only - weird */ - bi_errorf("%s: %s", arg, "bad value"); + bi_errorf("%s: bad value", arg); goto c_brkcont_err; } quit = (unsigned int)n; @@ -2397,7 +2455,7 @@ * scripts, but don't generate an error (ie, keep going). */ if ((unsigned int)n == quit) { - warningf(true, "%s: %s %s", wp[0], "can't", wp[0]); + warningf(true, "%s: can't %s", wp[0], wp[0]); return (0); } /* @@ -2522,7 +2580,7 @@ shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width, tv_sec, tv_usec, suffix); else - shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width, + shf_fprintf(shf, "%s%*ldm%02d.%02ds%s", prefix, width, tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix); } @@ -2760,8 +2818,8 @@ umask(oldmode); return (rv); c_mknod_usage: - bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor"); - bi_errorf("%s: %s", "usage", "mknod [-m mode] name p"); + bi_errorf("usage: mknod [-m mode] name %s", "b|c major minor"); + bi_errorf("usage: mknod [-m mode] name %s", "p"); return (1); } #endif @@ -2820,7 +2878,7 @@ /* * Attempt to conform to POSIX special cases. This is pretty - * dumb code straight-forward from the 2008 spec, but unless + * dumb code straight-forward from the 2008 spec, but unlike * the old pdksh code doesn't live from so many assumptions. * It does, though, inline some calls to '(*te.funcname)()'. */ @@ -2841,6 +2899,8 @@ ptest_unary: rv = test_eval(&te, op, *te.pos.wp++, NULL, true); ptest_out: + if (te.flags & TEF_ERROR) + return (T_ERR_EXIT); return ((invert & 1) ? rv : !rv); } /* let the parser deal with anything else */ @@ -3408,7 +3468,27 @@ # error nonsensical v ulimit #endif +struct limits { + /* limit resource */ + int resource; + /* multiply by to get rlim_{cur,max} values */ + unsigned int factor; + /* getopts char */ + char optchar; + /* limit name */ + char name[1]; +}; + #define RLIMITS_DEFNS +#define FN(lname,lid,lfac,lopt) \ + static const struct { \ + int resource; \ + unsigned int factor; \ + char optchar; \ + char name[sizeof(lname)]; \ + } rlimits_ ## lid = { \ + lid, lfac, lopt, lname \ + }; #include "rlimits.gen" static void print_ulimit(const struct limits *, int); @@ -3598,10 +3678,11 @@ int c_cat(const char **wp) { - int fd = STDIN_FILENO, rv, eno; + int fd = STDIN_FILENO, rv; ssize_t n, w; const char *fn = ""; char *buf, *cp; + int opipe = 0; #define MKSH_CAT_BUFSIZ 4096 /* parse options: POSIX demands we support "-u" as no-op */ @@ -3623,54 +3704,64 @@ return (1); } + /* catch SIGPIPE */ + opipe = block_pipe(); + do { if (*wp) { fn = *wp++; - if (fn[0] == '-' && fn[1] == '\0') + if (ksh_isdash(fn)) fd = STDIN_FILENO; - else if ((fd = open(fn, O_RDONLY | O_BINARY)) < 0) { - eno = errno; - bi_errorf("%s: %s", fn, cstrerror(eno)); + else if ((fd = binopen2(fn, O_RDONLY)) < 0) { + bi_errorf("%s: %s", fn, cstrerror(errno)); rv = 1; continue; } } while (/* CONSTCOND */ 1) { - n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ); - eno = errno; - /* give the user a chance to ^C out */ - intrcheck(); - if (n == -1) { - if (eno == EINTR) { + if ((n = blocking_read(fd, (cp = buf), + MKSH_CAT_BUFSIZ)) == -1) { + if (errno == EINTR) { + restore_pipe(opipe); + /* give the user a chance to ^C out */ + intrcheck(); /* interrupted, try again */ + opipe = block_pipe(); continue; } /* an error occured during reading */ - bi_errorf("%s: %s", fn, cstrerror(eno)); + bi_errorf("%s: %s", fn, cstrerror(errno)); rv = 1; break; } else if (n == 0) /* end of file reached */ break; while (n) { - w = write(STDOUT_FILENO, cp, n); - eno = errno; - /* give the user a chance to ^C out */ - intrcheck(); - if (w == -1) { - if (eno == EINTR) - /* interrupted, try again */ - continue; + if ((w = write(STDOUT_FILENO, cp, n)) != -1) { + n -= w; + cp += w; + continue; + } + if (errno == EINTR) { + restore_pipe(opipe); + /* give the user a chance to ^C out */ + intrcheck(); + /* interrupted, try again */ + opipe = block_pipe(); + continue; + } + if (errno == EPIPE) { + /* fake receiving signel */ + rv = ksh_sigmask(SIGPIPE); + } else { /* an error occured during writing */ bi_errorf("%s: %s", "", - cstrerror(eno)); + cstrerror(errno)); rv = 1; - if (fd != STDIN_FILENO) - close(fd); - goto out; } - n -= w; - cp += w; + if (fd != STDIN_FILENO) + close(fd); + goto out; } } if (fd != STDIN_FILENO) @@ -3678,6 +3769,7 @@ } while (*wp); out: + restore_pipe(opipe); free_osfunc(buf); return (rv); } diff -Nru mksh-50e/histrap.c mksh-52c/histrap.c --- mksh-50e/histrap.c 2015-03-01 16:43:25.000000000 +0100 +++ mksh-52c/histrap.c 2016-03-04 15:26:39.000000000 +0100 @@ -1,10 +1,10 @@ -/* $OpenBSD: history.c,v 1.40 2014/11/20 15:22:39 tedu Exp $ */ +/* $OpenBSD: history.c,v 1.41 2015/09/01 13:12:31 tedu Exp $ */ /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2014, 2015 - * Thorsten Glaser + * 2011, 2012, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -27,9 +27,9 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.134.2.3 2015/03/01 15:43:00 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.156 2016/03/04 14:26:13 tg Exp $"); -Trap sigtraps[NSIG + 1]; +Trap sigtraps[ksh_NSIG + 1]; static struct sigaction Sigact_ign; #if HAVE_PERSISTENT_HISTORY @@ -38,7 +38,7 @@ static void writehistfile(int, const char *); #endif -static int hist_execute(char *); +static int hist_execute(char *, Area *); static char **hist_get(const char *, bool, bool); static char **hist_get_oldest(void); @@ -84,6 +84,9 @@ /* maximum considered size of persistent history file */ #define MKSH_MAXHISTFSIZE ((off_t)1048576 * 96) +/* hidden option */ +#define HIST_DISCARD 5 + int c_fc(const char **wp) { @@ -214,7 +217,7 @@ xp += rep_len; } if (!any_subst) { - bi_errorf("bad substitution"); + bi_errorf(Tbadsubst); return (1); } len = strlen(s) + 1; @@ -223,7 +226,7 @@ xp += len; line = Xclose(xs, xp); } - return (hist_execute(line)); + return (hist_execute(line, ATEMP)); } if (editor && (lflag || nflag)) { @@ -275,8 +278,9 @@ for (hp = rflag ? hlast : hfirst; hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) { if (!nflag) - shf_fprintf(shl_stdout, "%d", - hist_source->line - (int)(histptr - hp)); + shf_fprintf(shl_stdout, "%lu", + (unsigned long)hist_source->line - + (unsigned long)(histptr - hp)); shf_putc('\t', shl_stdout); /* print multi-line commands correctly */ s = *hp; @@ -359,18 +363,17 @@ shf_close(shf); *xp = '\0'; strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); - return (hist_execute(Xstring(xs, xp))); + return (hist_execute(Xstring(xs, xp), hist_source->areap)); } } -/* Save cmd in history, execute cmd (cmd gets trashed) */ +/* save cmd in history, execute cmd (cmd gets afree’d) */ static int -hist_execute(char *cmd) +hist_execute(char *cmd, Area *areap) { static int last_line = -1; Source *sold; int ret; - char *p, *q; /* Back up over last histsave */ if (histptr >= history && last_line != hist_source->line) { @@ -380,22 +383,12 @@ last_line = hist_source->line; } - for (p = cmd; p; p = q) { - if ((q = strchr(p, '\n'))) { - /* kill the newline */ - *q++ = '\0'; - if (!*q) - /* ignore trailing newline */ - q = NULL; - } - histsave(&hist_source->line, p, true, true); - - /* POSIX doesn't say this is done... */ - shellf("%s\n", p); - if (q) - /* restore \n (trailing \n not restored) */ - q[-1] = '\n'; - } + histsave(&hist_source->line, cmd, HIST_STORE, true); + /* now *histptr == cmd without all trailing newlines */ + afree(cmd, areap); + cmd = *histptr; + /* pdksh says POSIX doesn’t say this is done, testsuite needs it */ + shellf("%s\n", cmd); /*- * Commands are executed here instead of pushing them onto the @@ -563,7 +556,7 @@ return; /* if the name is the same as the name we have */ - if (hname && strcmp(hname, name) == 0) + if (hname && name && !strcmp(hname, name)) return; /* @@ -577,11 +570,13 @@ afree(hname, APERM); hname = NULL; /* let's reset the history */ + histsave(NULL, NULL, HIST_DISCARD, true); histptr = history - 1; hist_source->line = 0; } - hist_init(hist_source); + if (name) + hist_init(hist_source); } #endif @@ -610,6 +605,8 @@ { bool changed = false; + /* called by histsave(), may not HIST_DISCARD, caller should flush */ + if (histfd != -1) { int lno = hist_source->line; @@ -629,30 +626,76 @@ * save command in history */ void -histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups) +histsave(int *lnp, const char *cmd, int svmode, bool ignoredups) { - char **hp; - char *c, *cp; + static char *enqueued = NULL; + char **hp, *c; + const char *ccp; + + if (svmode == HIST_DISCARD) { + afree(enqueued, APERM); + enqueued = NULL; + return; + } + + if (svmode == HIST_APPEND) { + if (!enqueued) + svmode = HIST_STORE; + } else if (enqueued) { + c = enqueued; + enqueued = NULL; + --*lnp; + histsave(lnp, c, HIST_STORE, true); + afree(c, APERM); + } - strdupx(c, cmd, APERM); - if ((cp = strchr(c, '\n')) != NULL) - *cp = '\0'; + if (svmode == HIST_FLUSH) + return; - if (ignoredups && !strcmp(c, *histptr) + ccp = cmd + strlen(cmd); + while (ccp > cmd && ccp[-1] == '\n') + --ccp; + strndupx(c, cmd, ccp - cmd, APERM); + + if (svmode != HIST_APPEND) { + if (ignoredups && + histptr >= history && + !strcmp(c, *histptr) #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY - && !histsync() + && !histsync() #endif - ) { - afree(c, APERM); - return; + ) { + afree(c, APERM); + return; + } + ++*lnp; } - ++*lnp; #if HAVE_PERSISTENT_HISTORY - if (dowrite && histfd != -1) + if (svmode == HIST_STORE && histfd != -1) writehistfile(*lnp, c); #endif + if (svmode == HIST_QUEUE || svmode == HIST_APPEND) { + size_t nenq, ncmd; + + if (!enqueued) { + if (*c) + enqueued = c; + else + afree(c, APERM); + return; + } + + nenq = strlen(enqueued); + ncmd = strlen(c); + enqueued = aresize(enqueued, nenq + 1 + ncmd + 1, APERM); + enqueued[nenq] = '\n'; + memcpy(enqueued + nenq + 1, c, ncmd + 1); + afree(c, APERM); + return; + } + hp = histptr; if (++hp >= history + histsize) { @@ -705,6 +748,8 @@ enum { hist_init_first, hist_init_retry, hist_init_restore } hs; #endif + histsave(NULL, NULL, HIST_DISCARD, true); + if (Flag(FTALKING) == 0) return; @@ -712,15 +757,16 @@ hist_source = s; #if HAVE_PERSISTENT_HISTORY - if ((hname = str_val(global("HISTFILE"))) == NULL) + if (((hname = str_val(global("HISTFILE"))) == NULL) || !*hname) { + hname = NULL; return; + } strdupx(hname, hname, APERM); hs = hist_init_first; retry: /* we have a file and are interactive */ - if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND | O_BINARY, - 0600)) < 0) + if ((fd = binopen3(hname, O_RDWR | O_CREAT | O_APPEND, 0600)) < 0) return; histfd = savefd(fd); @@ -755,8 +801,8 @@ /* create temporary file */ nhname = shf_smprintf("%s.%d", hname, (int)procpid); - if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC | - O_EXCL | O_BINARY, 0600)) < 0) { + if ((fd = binopen3(nhname, O_RDWR | O_CREAT | O_TRUNC | + O_EXCL, 0600)) < 0) { /* just don't truncate then, meh. */ goto hist_trunc_dont; } @@ -854,13 +900,12 @@ if (lno >= s->line - (histptr - history) && lno <= s->line) { hp = &histptr[lno - s->line]; - if (*hp) - afree(*hp, APERM); + afree(*hp, APERM); strdupx(*hp, (char *)(base + 4), APERM); } } else { s->line = lno--; - histsave(&lno, (char *)(base + 4), false, false); + histsave(&lno, (char *)(base + 4), HIST_NOTE, false); } /* advance base pointer past NUL */ bytes -= ++cp - base; @@ -972,59 +1017,71 @@ { int i; const char *cs; +#if !HAVE_SYS_SIGNAME + const struct mksh_sigpair *pair; +#endif trap_exstat = -1; - /* Populate sigtraps based on sys_signame and sys_siglist. */ - /*XXX this is idiotic, use a multi-key/value hashtable! */ - for (i = 0; i <= NSIG; i++) { + /* populate sigtraps based on sys_signame and sys_siglist */ + for (i = 1; i < ksh_NSIG; i++) { sigtraps[i].signal = i; - if (i == ksh_SIGERR) { - sigtraps[i].name = "ERR"; - sigtraps[i].mess = "Error handler"; - } else { #if HAVE_SYS_SIGNAME - cs = sys_signame[i]; + cs = sys_signame[i]; #else - const struct mksh_sigpair *pair = mksh_sigpairs; - while ((pair->nr != i) && (pair->name != NULL)) - ++pair; - cs = pair->name; -#endif - if ((cs == NULL) || - (cs[0] == '\0')) - sigtraps[i].name = shf_smprintf("%d", i); - else { - char *s; + pair = mksh_sigpairs; + while ((pair->nr != i) && (pair->name != NULL)) + ++pair; + cs = pair->name; +#endif + if ((cs == NULL) || + (cs[0] == '\0')) + sigtraps[i].name = null; + else { + char *s; - /* this is not optimal, what about SIGSIG1? */ - if ((cs[0] & 0xDF) == 'S' && - (cs[1] & 0xDF) == 'I' && - (cs[2] & 0xDF) == 'G' && - cs[3] != '\0') { - /* skip leading "SIG" */ - cs += 3; - } - strdupx(s, cs, APERM); - sigtraps[i].name = s; - while ((*s = ksh_toupper(*s))) - ++s; + /* this is not optimal, what about SIGSIG1? */ + if (ksh_eq(cs[0], 'S', 's') && + ksh_eq(cs[1], 'I', 'i') && + ksh_eq(cs[2], 'G', 'g') && + cs[3] != '\0') { + /* skip leading "SIG" */ + cs += 3; + } + strdupx(s, cs, APERM); + sigtraps[i].name = s; + while ((*s = ksh_toupper(*s))) + ++s; + /* check for reserved names */ + if (!strcmp(sigtraps[i].name, "EXIT") || + !strcmp(sigtraps[i].name, "ERR")) { +#ifndef MKSH_SMALL + internal_warningf("ignoring invalid signal name %s", + sigtraps[i].name); +#endif + sigtraps[i].name = null; } + } + if (sigtraps[i].name == null) + sigtraps[i].name = shf_smprintf("%d", i); #if HAVE_SYS_SIGLIST - sigtraps[i].mess = sys_siglist[i]; + sigtraps[i].mess = sys_siglist[i]; #elif HAVE_STRSIGNAL - sigtraps[i].mess = strsignal(i); + sigtraps[i].mess = strsignal(i); #else - sigtraps[i].mess = NULL; + sigtraps[i].mess = NULL; #endif - if ((sigtraps[i].mess == NULL) || - (sigtraps[i].mess[0] == '\0')) - sigtraps[i].mess = shf_smprintf("%s %d", - "Signal", i); - } + if ((sigtraps[i].mess == NULL) || + (sigtraps[i].mess[0] == '\0')) + sigtraps[i].mess = shf_smprintf("%s %d", + "Signal", i); } - /* our name for signal 0 */ + sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT; sigtraps[ksh_SIGEXIT].name = "EXIT"; + sigtraps[ksh_SIGEXIT].mess = "Exit trap"; + sigtraps[ksh_SIGERR].signal = ksh_SIGERR; + sigtraps[ksh_SIGERR].name = "ERR"; + sigtraps[ksh_SIGERR].mess = "Error handler"; (void)sigemptyset(&Sigact_ign.sa_mask); Sigact_ign.sa_flags = 0; /* interruptible */ @@ -1072,21 +1129,24 @@ } Trap * -gettrap(const char *cs, bool igncase) +gettrap(const char *cs, bool igncase, bool allsigs) { int i; Trap *p; char *as; - if (ksh_isdigit(*cs)) { - return ((getn(cs, &i) && 0 <= i && i < NSIG) ? + /* signal number (1..ksh_NSIG) or 0? */ + + if (ksh_isdigit(*cs)) + return ((getn(cs, &i) && 0 <= i && i < ksh_NSIG) ? (&sigtraps[i]) : NULL); - } + + /* do a lookup by name then */ /* this breaks SIGSIG1, but we do that above anyway */ - if ((cs[0] & 0xDF) == 'S' && - (cs[1] & 0xDF) == 'I' && - (cs[2] & 0xDF) == 'G' && + if (ksh_eq(cs[0], 'S', 's') && + ksh_eq(cs[1], 'I', 'i') && + ksh_eq(cs[2], 'G', 'g') && cs[3] != '\0') { /* skip leading "SIG" */ cs += 3; @@ -1101,14 +1161,24 @@ } else as = NULL; + /* this is idiotic, we really want a hashtable here */ + p = sigtraps; - for (i = 0; i <= NSIG; i++) { + i = ksh_NSIG + 1; + do { if (!strcmp(p->name, cs)) goto found; ++p; - } - p = NULL; + } while (--i); + goto notfound; + found: + if (!allsigs) { + if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) { + notfound: + p = NULL; + } + } afree(as, ATEMP); return (p); } @@ -1152,14 +1222,16 @@ int fatal_trap_check(void) { - int i; - Trap *p; + Trap *p = sigtraps; + int i = ksh_NSIG + 1; /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ - for (p = sigtraps, i = NSIG+1; --i >= 0; p++) + do { if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) /* return value is used as an exit code */ - return (128 + p->signal); + return (ksh_sigmask(p->signal)); + ++p; + } while (--i); return (0); } @@ -1171,13 +1243,15 @@ int trap_pending(void) { - int i; - Trap *p; + Trap *p = sigtraps; + int i = ksh_NSIG + 1; - for (p = sigtraps, i = NSIG+1; --i >= 0; p++) + do { if (p->set && ((p->trap && p->trap[0]) || ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) return (p->signal); + ++p; + } while (--i); return (0); } @@ -1188,8 +1262,8 @@ void runtraps(int flag) { - int i; - Trap *p; + Trap *p = sigtraps; + int i = ksh_NSIG + 1; if (ksh_tmout_state == TMOUT_LEAVING) { ksh_tmout_state = TMOUT_EXECUTING; @@ -1208,10 +1282,12 @@ if (flag & TF_FATAL) fatal_trap = 0; ++trap_nested; - for (p = sigtraps, i = NSIG+1; --i >= 0; p++) + do { if (p->set && (!flag || ((p->flags & flag) && p->trap == NULL))) runtrap(p, false); + ++p; + } while (--i); if (!--trap_nested) runtrap(NULL, true); } @@ -1230,16 +1306,17 @@ p->set = 0; if (trapstr == NULL) { /* SIG_DFL */ - if (p->flags & TF_FATAL) { - /* eg, SIGHUP */ - exstat = (int)ksh_min(128U + (unsigned)i, 255U); - unwind(LLEAVE); + if (p->flags & (TF_FATAL | TF_DFL_INTR)) { + exstat = (int)(128U + (unsigned)i); + if ((unsigned)exstat > 255U) + exstat = 255; } - if (p->flags & TF_DFL_INTR) { - /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ - exstat = (int)ksh_min(128U + (unsigned)i, 255U); + /* e.g. SIGHUP */ + if (p->flags & TF_FATAL) + unwind(LLEAVE); + /* e.g. SIGINT, SIGQUIT, SIGTERM, etc. */ + if (p->flags & TF_DFL_INTR) unwind(LINTR); - } goto donetrap; } if (trapstr[0] == '\0') @@ -1279,30 +1356,34 @@ void cleartraps(void) { - int i; - Trap *p; + Trap *p = sigtraps; + int i = ksh_NSIG + 1; trap = 0; intrsig = 0; fatal_trap = 0; - for (i = NSIG+1, p = sigtraps; --i >= 0; p++) { + + do { p->set = 0; if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) settrap(p, NULL); - } + ++p; + } while (--i); } /* restore signals just before an exec(2) */ void restoresigs(void) { - int i; - Trap *p; + Trap *p = sigtraps; + int i = ksh_NSIG + 1; - for (i = NSIG+1, p = sigtraps; --i >= 0; p++) + do { if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, SS_RESTORE_CURR|SS_FORCE); + ++p; + } while (--i); } void @@ -1310,8 +1391,7 @@ { sig_t f; - if (p->trap) - afree(p->trap, APERM); + afree(p->trap, APERM); /* handles s == NULL */ strdupx(p->trap, s, APERM); p->flags |= TF_CHANGED; diff -Nru mksh-50e/jobs.c mksh-52c/jobs.c --- mksh-50e/jobs.c 2015-01-25 16:44:31.000000000 +0100 +++ mksh-52c/jobs.c 2016-03-04 15:26:39.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: jobs.c,v 1.40 2013/09/04 15:49:18 millert Exp $ */ +/* $OpenBSD: jobs.c,v 1.43 2015/09/10 22:48:58 nicm Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, - * 2012, 2013, 2014 - * Thorsten Glaser + * 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.105.2.1 2015/01/25 15:44:06 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.120 2016/03/04 14:26:13 tg Exp $"); #if HAVE_KILLPG #define mksh_killpg killpg @@ -45,8 +45,8 @@ int state; int status; /* wait status */ /* process command string from vistree */ - char command[256 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) + - 2 * sizeof(int))]; + char command[256 - (ALLOC_OVERHEAD + sizeof(Proc *) + + sizeof(pid_t) + 2 * sizeof(int))]; }; /* Notify/print flag - j_print() argument */ @@ -86,7 +86,7 @@ int flags; /* see JF_* */ volatile int state; /* job state */ int status; /* exit status of last process */ - int32_t age; /* number of jobs started */ + int age; /* number of jobs started */ Coproc_id coproc_id; /* 0 or id of coprocess output pipe */ #ifndef MKSH_UNEMPLOYED mksh_ttyst ttystat; /* saved tty state for stopped jobs */ @@ -118,7 +118,7 @@ static pid_t async_pid; static int nzombie; /* # of zombies owned by this process */ -static int32_t njobs; /* # of jobs started */ +static int njobs; /* # of jobs started */ #ifndef CHILD_MAX #define CHILD_MAX 25 @@ -217,9 +217,9 @@ { switch (p->state) { case PEXITED: - return (WEXITSTATUS(p->status)); + return ((WEXITSTATUS(p->status)) & 255); case PSIGNALLED: - return (128 + WTERMSIG(p->status)); + return (ksh_sigmask(WTERMSIG(p->status))); default: return (0); } @@ -238,11 +238,11 @@ mksh_tcset(tty_fd, &tty_state); if (restore_ttypgrp >= 0) { if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) { - warningf(false, "%s: %s %s: %s", "j_suspend", - "tcsetpgrp", "failed", cstrerror(errno)); + warningf(false, "%s: %s failed: %s", + "j_suspend", "tcsetpgrp", cstrerror(errno)); } else if (setpgid(0, restore_ttypgrp) < 0) { - warningf(false, "%s: %s %s: %s", "j_suspend", - "setpgid", "failed", cstrerror(errno)); + warningf(false, "%s: %s failed: %s", + "j_suspend", "setpgid", cstrerror(errno)); } } } @@ -259,12 +259,12 @@ if (ttypgrp_ok) { if (restore_ttypgrp >= 0) { if (setpgid(0, kshpid) < 0) { - warningf(false, "%s: %s %s: %s", "j_suspend", - "setpgid", "failed", cstrerror(errno)); + warningf(false, "%s: %s failed: %s", + "j_suspend", "setpgid", cstrerror(errno)); ttypgrp_ok = false; } else if (tcsetpgrp(tty_fd, kshpid) < 0) { - warningf(false, "%s: %s %s: %s", "j_suspend", - "tcsetpgrp", "failed", cstrerror(errno)); + warningf(false, "%s: %s failed: %s", + "j_suspend", "tcsetpgrp", cstrerror(errno)); ttypgrp_ok = false; } } @@ -349,8 +349,8 @@ pid_t ttypgrp; if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) { - warningf(false, "%s: %s %s: %s", - "j_init", "tcgetpgrp", "failed", + warningf(false, "%s: %s failed: %s", + "j_init", "tcgetpgrp", cstrerror(errno)); ttypgrp_ok = false; break; @@ -365,13 +365,13 @@ SS_RESTORE_DFL|SS_FORCE); if (ttypgrp_ok && kshpgrp != kshpid) { if (setpgid(0, kshpid) < 0) { - warningf(false, "%s: %s %s: %s", "j_init", - "setpgid", "failed", cstrerror(errno)); + warningf(false, "%s: %s failed: %s", + "j_init", "setpgid", cstrerror(errno)); ttypgrp_ok = false; } else { if (tcsetpgrp(tty_fd, kshpid) < 0) { - warningf(false, "%s: %s %s: %s", - "j_init", "tcsetpgrp", "failed", + warningf(false, "%s: %s failed: %s", + "j_init", "tcsetpgrp", cstrerror(errno)); ttypgrp_ok = false; } else @@ -467,8 +467,7 @@ if (flags & XPIPEI) { /* continuing with a pipe */ if (!last_job) - internal_errorf("%s %d", - "exchild: XPIPEI and no last_job - pid", + internal_errorf("exchild: XPIPEI and no last_job - pid %d", (int)procpid); j = last_job; if (last_proc) @@ -601,7 +600,7 @@ #ifndef MKSH_SMALL if (t->type == TPIPE) unwind(LLEAVE); - internal_warningf("%s: %s", "exchild", "execute() returned"); + internal_warningf("%s: execute() returned", "exchild"); fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n", "exchild", t); shf_flush(shl_out); @@ -753,7 +752,7 @@ if (rv < 0) /* we were interrupted */ - *sigp = 128 + -rv; + *sigp = ksh_sigmask(-rv); return (rv); } @@ -862,10 +861,10 @@ if (j->flags & JF_SAVEDTTY) mksh_tcset(tty_fd, &tty_state); sigprocmask(SIG_SETMASK, &omask, NULL); - bi_errorf("%s %s(%d, %ld) %s: %s", - "1st", "tcsetpgrp", tty_fd, + bi_errorf("%s %s(%d, %ld) failed: %s", + "fg: 1st", "tcsetpgrp", tty_fd, (long)((j->flags & JF_SAVEDTTYPGRP) ? - j->saved_ttypgrp : j->pgrp), "failed", + j->saved_ttypgrp : j->pgrp), cstrerror(rv)); return (1); } @@ -884,12 +883,12 @@ if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) mksh_tcset(tty_fd, &tty_state); if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0) - warningf(true, "%s %s(%d, %ld) %s: %s", + warningf(true, "%s %s(%d, %ld) failed: %s", "fg: 2nd", "tcsetpgrp", tty_fd, - (long)kshpgrp, "failed", cstrerror(errno)); + (long)kshpgrp, cstrerror(errno)); } sigprocmask(SIG_SETMASK, &omask, NULL); - bi_errorf("%s %s %s", "can't continue job", + bi_errorf("%s %s: %s", "can't continue job", cp, cstrerror(eno)); return (1); } @@ -1111,6 +1110,7 @@ int flags, const char *where) { + Proc *p; int rv; #ifdef MKSH_NO_SIGSUSPEND sigset_t omask; @@ -1175,9 +1175,9 @@ (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0) j->flags |= JF_SAVEDTTYPGRP; if (tcsetpgrp(tty_fd, kshpgrp) < 0) - warningf(true, "%s %s(%d, %ld) %s: %s", + warningf(true, "%s %s(%d, %ld) failed: %s", "j_waitj:", "tcsetpgrp", tty_fd, - (long)kshpgrp, "failed", cstrerror(errno)); + (long)kshpgrp, cstrerror(errno)); if (j->state == PSTOPPED) { j->flags |= JF_SAVEDTTY; mksh_tcget(tty_fd, &j->ttystat); @@ -1222,14 +1222,14 @@ * even when not monitoring, but this doesn't make sense since * a tty generated ^C goes to the whole process group) */ - { - int status; - - status = j->last_proc->status; - if (Flag(FMONITOR) && j->state == PSIGNALLED && - WIFSIGNALED(status) && - (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR)) - trapsig(WTERMSIG(status)); + if (Flag(FMONITOR) && j->state == PSIGNALLED && + WIFSIGNALED(j->last_proc->status)) { + int termsig; + + if ((termsig = WTERMSIG(j->last_proc->status)) > 0 && + termsig < ksh_NSIG && + (sigtraps[termsig].flags & TF_TTY_INTR)) + trapsig(termsig); } #endif } @@ -1238,9 +1238,10 @@ j_systime = j->systime; rv = j->status; - if ((flags & JW_PIPEST) && (j->proc_list != NULL)) { + if (!(p = j->proc_list)) { + ; /* nothing */ + } else if (flags & JW_PIPEST) { uint32_t num = 0; - Proc *p = j->proc_list; struct tbl *vp; unset(vp_pipest, 1); @@ -1270,15 +1271,13 @@ rv = vp->val.i; p = p->next; } - } else if (Flag(FPIPEFAIL) && (j->proc_list != NULL)) { - Proc *p = j->proc_list; - int i; + } else if (Flag(FPIPEFAIL)) { + do { + const int i = proc_errorlevel(p); - while (p != NULL) { - if ((i = proc_errorlevel(p))) + if (i) rv = i; - p = p->next; - } + } while ((p = p->next)); } if (!(flags & JW_ASYNCNOTIFY) @@ -1426,8 +1425,8 @@ /* XXX debugging (nasty - interrupt routine using shl_out) */ if (!(j->flags & JF_STARTED)) { - internal_warningf("check_job: job started (flags 0x%x)", - j->flags); + internal_warningf("check_job: job started (flags 0x%X)", + (unsigned int)j->flags); return; } @@ -1527,7 +1526,7 @@ Proc *p; int state; int status; - int coredumped; + bool coredumped; char jobchar = ' '; char buf[64]; const char *filler; @@ -1551,41 +1550,49 @@ jobchar = '-'; for (p = j->proc_list; p != NULL;) { - coredumped = 0; + coredumped = false; switch (p->state) { case PRUNNING: memcpy(buf, "Running", 8); break; - case PSTOPPED: - strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess, - sizeof(buf)); + case PSTOPPED: { + int stopsig = WSTOPSIG(p->status); + + strlcpy(buf, stopsig > 0 && stopsig < ksh_NSIG ? + sigtraps[stopsig].mess : "Stopped", sizeof(buf)); break; - case PEXITED: + } + case PEXITED: { + int exitstatus = (WEXITSTATUS(p->status)) & 255; + if (how == JP_SHORT) buf[0] = '\0'; - else if (WEXITSTATUS(p->status) == 0) + else if (exitstatus == 0) memcpy(buf, "Done", 5); else shf_snprintf(buf, sizeof(buf), "Done (%d)", - WEXITSTATUS(p->status)); + exitstatus); break; - case PSIGNALLED: + } + case PSIGNALLED: { + int termsig = WTERMSIG(p->status); #ifdef WCOREDUMP if (WCOREDUMP(p->status)) - coredumped = 1; + coredumped = true; #endif /* * kludge for not reporting 'normal termination * signals' (i.e. SIGINT, SIGPIPE) */ if (how == JP_SHORT && !coredumped && - (WTERMSIG(p->status) == SIGINT || - WTERMSIG(p->status) == SIGPIPE)) { + (termsig == SIGINT || termsig == SIGPIPE)) { buf[0] = '\0'; } else - strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess, + strlcpy(buf, termsig > 0 && termsig < ksh_NSIG ? + sigtraps[termsig].mess : "Signalled", sizeof(buf)); break; + } default: buf[0] = '\0'; } @@ -1644,8 +1651,7 @@ size_t len; int job = 0; - if (ksh_isdigit(*cp)) { - getn(cp, &job); + if (ksh_isdigit(*cp) && getn(cp, &job)) { /* Look for last_proc->pid (what $! returns) first... */ for (j = job_list; j != NULL; j = j->next) if (j->last_proc && j->last_proc->pid == job) @@ -1657,11 +1663,10 @@ for (j = job_list; j != NULL; j = j->next) if (j->pgrp && j->pgrp == job) return (j); - if (ecodep) - *ecodep = JL_NOSUCH; - return (NULL); + goto j_lookup_nosuch; } if (*cp != '%') { + j_lookup_invalid: if (ecodep) *ecodep = JL_INVALID; return (NULL); @@ -1681,7 +1686,8 @@ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - getn(cp, &job); + if (!getn(cp, &job)) + goto j_lookup_invalid; for (j = job_list; j != NULL; j = j->next) if (j->job == job) return (j); @@ -1721,6 +1727,7 @@ return (last_match); break; } + j_lookup_nosuch: if (ecodep) *ecodep = JL_NOSUCH; return (NULL); @@ -1896,8 +1903,8 @@ #endif break; case 3: - warningf(false, "%s: %s %s: %s", "j_ttyinit", - "dup of tty fd", "failed", cstrerror(errno)); + warningf(false, "%s: %s failed: %s", "j_ttyinit", + "dup of tty fd", cstrerror(errno)); break; case 4: warningf(false, "%s: %s: %s", "j_ttyinit", diff -Nru mksh-50e/lalloc.c mksh-52c/lalloc.c --- mksh-50e/lalloc.c 2015-01-25 16:36:12.000000000 +0100 +++ mksh-52c/lalloc.c 2016-02-26 22:54:02.000000000 +0100 @@ -1,6 +1,6 @@ /*- - * Copyright (c) 2009, 2010, 2011, 2013, 2014 - * Thorsten Glaser + * Copyright (c) 2009, 2010, 2011, 2013, 2014, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -19,8 +19,11 @@ */ #include "sh.h" +#ifdef MKSH_ALLOC_CATCH_UNDERRUNS +#include +#endif -__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.20.2.1 2015/01/25 15:35:47 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.26 2016/02/26 21:53:36 tg Exp $"); /* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */ #if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0) @@ -29,19 +32,72 @@ #define remalloc(p,n) realloc_osi((p), (n)) #endif -#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % ALLOC_SIZE) -static ALLOC_ITEM *findptr(ALLOC_ITEM **, char *, Area *); +static struct lalloc_common *findptr(struct lalloc_common **, char *, Area *); + +#ifndef MKSH_ALLOC_CATCH_UNDERRUNS +#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) % sizeof(struct lalloc_common)) +#else +#define ALLOC_ISUNALIGNED(p) (((size_t)(p)) & 4095) +#undef remalloc +#undef free_osimalloc + +static void +free_osimalloc(void *ptr) +{ + struct lalloc_item *lp = ptr; + + if (munmap(lp, lp->len)) + err(1, "free_osimalloc"); +} + +static void * +remalloc(void *ptr, size_t size) +{ + struct lalloc_item *lp, *lold = ptr; + + size = (size + 4095) & ~(size_t)4095; + + if (lold && lold->len >= size) + return (ptr); + + if ((lp = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, (off_t)0)) == MAP_FAILED) + err(1, "remalloc: mmap(%zu)", size); + if (ALLOC_ISUNALIGNED(lp)) + errx(1, "remalloc: unaligned(%p)", lp); + if (mprotect(((char *)lp) + 4096, 4096, PROT_NONE)) + err(1, "remalloc: mprotect"); + lp->len = size; + + if (lold) { + memcpy(((char *)lp) + 8192, ((char *)lold) + 8192, + lold->len - 8192); + if (munmap(lold, lold->len)) + err(1, "remalloc: munmap"); + } + + return (lp); +} +#endif void ainit(Area *ap) { - /* area pointer is an ALLOC_ITEM, just the head of the list */ +#ifdef MKSH_ALLOC_CATCH_UNDERRUNS + if (sysconf(_SC_PAGESIZE) != 4096) { + fprintf(stderr, "mksh: fatal: pagesize %lu not 4096!\n", + sysconf(_SC_PAGESIZE)); + fflush(stderr); + abort(); + } +#endif + /* area pointer and items share struct lalloc_common */ ap->next = NULL; } -static ALLOC_ITEM * -findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap) +static struct lalloc_common * +findptr(struct lalloc_common **lpp, char *ptr, Area *ap) { void *lp; @@ -51,10 +107,10 @@ #endif /* get address of ALLOC_ITEM from user item */ /* - * note: the alignment of "ptr" to ALLOC_SIZE is checked + * note: the alignment of "ptr" to ALLOC_ITEM is checked * above; the "void *" gets us rid of a gcc 2.95 warning */ - *lpp = (lp = ptr - ALLOC_SIZE); + *lpp = (lp = ptr - sizeof(ALLOC_ITEM)); /* search for allocation item in group list */ while (ap->next != lp) if ((ap = ap->next) == NULL) { @@ -84,35 +140,35 @@ void * aresize(void *ptr, size_t numb, Area *ap) { - ALLOC_ITEM *lp = NULL; + struct lalloc_common *lp = NULL; /* resizing (true) or newly allocating? */ if (ptr != NULL) { - ALLOC_ITEM *pp; + struct lalloc_common *pp; pp = findptr(&lp, ptr, ap); pp->next = lp->next; } - if (notoktoadd(numb, ALLOC_SIZE) || - (lp = remalloc(lp, numb + ALLOC_SIZE)) == NULL + if (notoktoadd(numb, sizeof(ALLOC_ITEM)) || + (lp = remalloc(lp, numb + sizeof(ALLOC_ITEM))) == NULL #ifndef MKSH_SMALL || ALLOC_ISUNALIGNED(lp) #endif ) internal_errorf(Toomem, numb); - /* this only works because Area is an ALLOC_ITEM */ + /* area pointer and items share struct lalloc_common */ lp->next = ap->next; ap->next = lp; /* return user item address */ - return ((char *)lp + ALLOC_SIZE); + return ((char *)lp + sizeof(ALLOC_ITEM)); } void afree(void *ptr, Area *ap) { if (ptr != NULL) { - ALLOC_ITEM *lp, *pp; + struct lalloc_common *lp, *pp; pp = findptr(&lp, ptr, ap); /* unhook */ @@ -125,7 +181,7 @@ void afreeall(Area *ap) { - ALLOC_ITEM *lp; + struct lalloc_common *lp; /* traverse group (linked list) */ while ((lp = ap->next) != NULL) { diff -Nru mksh-50e/lex.c mksh-52c/lex.c --- mksh-50e/lex.c 2015-01-11 23:40:15.000000000 +0100 +++ mksh-52c/lex.c 2016-03-01 20:22:57.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: lex.c,v 1.49 2013/12/17 16:37:06 deraadt Exp $ */ +/* $OpenBSD: lex.c,v 1.51 2015/09/10 22:48:58 nicm Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193.2.1 2015/01/11 22:39:50 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.222 2016/03/01 19:22:31 tg Exp $"); /* * states while lexing word @@ -38,8 +38,8 @@ #define SQBRACE 7 /* inside "${}" */ #define SBQUOTE 8 /* inside `` */ #define SASPAREN 9 /* inside $(( )) */ -#define SHEREDELIM 10 /* parsing <<,<<-,<<< delimiter */ -#define SHEREDQUOTE 11 /* parsing " in <<,<<-,<<< delimiter */ +#define SHEREDELIM 10 /* parsing << or <<- delimiter */ +#define SHEREDQUOTE 11 /* parsing " in << or <<- delimiter */ #define SPATTERN 12 /* parsing *(...|...) pattern (*+?@!) */ #define SADELIM 13 /* like SBASE, looking for delimiter */ #define STBRACEKORN 14 /* parsing ${...[#%]...} !FSH */ @@ -61,7 +61,7 @@ /* point to the next state block */ struct lex_state *base; /* marks start of state output in output string */ - int start; + size_t start; /* SBQUOTE: true if in double quotes: "`...`" */ /* SEQUOTE: got NUL, ignore rest of string */ bool abool; @@ -94,11 +94,10 @@ static int getsc_uu(void); static void getsc_line(Source *); static int getsc_bn(void); -static int s_get(void); -static void s_put(int); +static int getsc_i(void); static char *get_brace_var(XString *, char *); static bool arraysub(char **); -static void gethere(bool); +static void gethere(void); static Lex_state *push_state_i(State_info *, Lex_state *); static Lex_state *pop_state_i(State_info *, Lex_state *); @@ -112,7 +111,7 @@ #define o_getsc_u() ((*source->str != '\0') ? *source->str++ : getsc_uu()) /* retrace helper */ -#define o_getsc_r(carg) { \ +#define o_getsc_r(carg) \ int cev = (carg); \ struct sretrace_info *rp = retrace_info; \ \ @@ -122,17 +121,17 @@ rp = rp->next; \ } \ \ - return (cev); \ -} - -#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST) -static int getsc(void); + return (cev); +/* callback */ static int -getsc(void) +getsc_i(void) { o_getsc_r(o_getsc()); } + +#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST) +#define getsc getsc_i #else static int getsc_r(int); @@ -234,26 +233,14 @@ if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ source->flags &= ~SF_ALIAS; - cf |= ALIAS; + /* POSIX: trailing space only counts if parsing simple cmd */ + if (!Flag(FPOSIX) || (cf & CMDWORD)) + cf |= ALIAS; } /* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */ statep->type = state; - /* check for here string */ - if (state == SHEREDELIM) { - c = getsc(); - if (c == '<') { - state = SHEREDELIM; - while ((c = getsc()) == ' ' || c == '\t') - ; - ungetsc(c); - c = '<'; - goto accept_nonword; - } - ungetsc(c); - } - /* collect non-special or quoted characters to form word */ while (!((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { @@ -262,7 +249,6 @@ c == /*{*/ '}') /* possibly end ${ :;} */ break; - accept_nonword: Xcheck(ws, wp); switch (state) { case SADELIM: @@ -282,7 +268,7 @@ } /* FALLTHROUGH */ case SBASE: - if (c == '[' && (cf & (VARASN|ARRAYVAR))) { + if (c == '[' && (cf & CMDASN)) { /* temporary */ *wp = EOS; if (is_wdvarname(Xstring(ws, wp), false)) { @@ -539,25 +525,12 @@ PUSH_STATE(SBQUOTE); *wp++ = COMSUB; /* - * Need to know if we are inside double quotes - * since sh/AT&T-ksh translate the \" to " in - * "`...\"...`". - * This is not done in POSIX mode (section - * 3.2.3, Double Quotes: "The backquote shall - * retain its special meaning introducing the - * other form of command substitution (see - * 3.6.3). The portion of the quoted string - * from the initial backquote and the - * characters up to the next backquote that - * is not preceded by a backslash (having - * escape characters removed) defines that - * command whose output replaces `...` when - * the word is expanded." - * Section 3.6.3, Command Substitution: - * "Within the backquoted style of command - * substitution, backslash shall retain its - * literal meaning, except when followed by - * $ ` \."). + * We need to know whether we are within double + * quotes in order to translate \" to " within + * "…`…\"…`…" because, unlike for COMSUBs, the + * outer double quoteing changes the backslash + * meaning for the inside. For more details: + * http://austingroupbugs.net/view.php?id=1015 */ statep->ls_bool = false; s2 = statep; @@ -596,8 +569,8 @@ *wp++ = CQUOTE; ignore_backslash_newline--; } else if (c == '\\') { - if ((c2 = unbksl(true, s_get, s_put)) == -1) - c2 = s_get(); + if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1) + c2 = getsc(); if (c2 == 0) statep->ls_bool = true; if (!statep->ls_bool) { @@ -609,10 +582,11 @@ } else { cz = utf_wctomb(ts, c2 - 0x100); ts[cz] = 0; - for (cz = 0; ts[cz]; ++cz) { + cz = 0; + do { *wp++ = QCHAR; *wp++ = ts[cz]; - } + } while (ts[++cz]); } } } else if (!statep->ls_bool) { @@ -747,8 +721,9 @@ case 0: /* trailing \ is lost */ break; + case '$': + case '`': case '\\': - case '$': case '`': *wp++ = c; break; case '"': @@ -783,6 +758,7 @@ Source *s; ungetsc(c2); + ungetsc(c); /* * mismatched parenthesis - * assume we were really @@ -790,11 +766,12 @@ */ *wp = EOS; sp = Xstring(ws, wp); - dp = wdstrip(sp, WDS_KEEPQ); + dp = wdstrip(sp + 1, WDS_TPUTS); s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = dp; s->next = source; source = s; + ungetsc('('/*)*/); return ('('/*)*/); } } else if (c == '(') @@ -806,7 +783,7 @@ ++statep->nparen; goto Sbase2; - /* <<, <<-, <<< delimiter */ + /* << or <<- delimiter */ case SHEREDELIM: /* * here delimiters need a special case since @@ -844,7 +821,7 @@ } break; - /* " in <<, <<-, <<< delimiter */ + /* " in << or <<- delimiter */ case SHEREDQUOTE: if (c != '"') goto Subst; @@ -920,48 +897,45 @@ goto no_iop; if (!ksh_isdigit(dp[c2 + 1])) goto no_iop; - iop->unit = (iop->unit * 10) + dp[c2 + 1] - '0'; + iop->unit = iop->unit * 10 + ksh_numdig(dp[c2 + 1]); + if (iop->unit >= FDBASE) + goto no_iop; } - if (iop->unit >= FDBASE) - goto no_iop; - if (c == '&') { if ((c2 = getsc()) != '>') { ungetsc(c2); goto no_iop; } c = c2; - iop->flag = IOBASH; + iop->ioflag = IOBASH; } else - iop->flag = 0; + iop->ioflag = 0; c2 = getsc(); /* <<, >>, <> are ok, >< is not */ if (c == c2 || (c == '<' && c2 == '>')) { - iop->flag |= c == c2 ? + iop->ioflag |= c == c2 ? (c == '>' ? IOCAT : IOHERE) : IORDWR; - if (iop->flag == IOHERE) { - if ((c2 = getsc()) == '-') { - iop->flag |= IOSKIP; - c2 = getsc(); - } else if (c2 == '<') - iop->flag |= IOHERESTR; - ungetsc(c2); - if (c2 == '\n') - iop->flag |= IONDELIM; + if (iop->ioflag == IOHERE) { + if ((c2 = getsc()) == '-') + iop->ioflag |= IOSKIP; + else if (c2 == '<') + iop->ioflag |= IOHERESTR; + else + ungetsc(c2); } } else if (c2 == '&') - iop->flag |= IODUP | (c == '<' ? IORDUP : 0); + iop->ioflag |= IODUP | (c == '<' ? IORDUP : 0); else { - iop->flag |= c == '>' ? IOWRITE : IOREAD; + iop->ioflag |= c == '>' ? IOWRITE : IOREAD; if (c == '>' && c2 == '|') - iop->flag |= IOCLOB; + iop->ioflag |= IOCLOB; else ungetsc(c2); } - iop->name = NULL; + iop->ioname = NULL; iop->delim = NULL; iop->heredoc = NULL; /* free word */ @@ -999,12 +973,24 @@ } #endif } else if (c == '\n') { - gethere(false); - if (cf & CONTIN) - goto Again; - } else if (c == '\0') - /* need here strings at EOF */ - gethere(true); + if (cf & HEREDELIM) + ungetsc(c); + else { + gethere(); + if (cf & CONTIN) + goto Again; + } + } else if (c == '\0' && !(cf & HEREDELIM)) { + struct ioword **p = heres; + + while (p < herep) + if ((*p)->ioflag & IOHERESTR) + ++p; + else + /* ksh -c 'cat <delim, 0)); + } return (c); } @@ -1027,30 +1013,18 @@ /* copy word to unprefixed string ident */ sp = yylval.cp; dp = ident; - if ((cf & HEREDELIM) && (sp[1] == '<')) { - herestringloop: - switch ((c = *sp++)) { - case CHAR: - ++sp; - /* FALLTHROUGH */ - case OQUOTE: - case CQUOTE: - goto herestringloop; - default: - break; - } - /* dummy value */ - *dp++ = 'x'; - } else - while ((dp - ident) < IDENT && (c = *sp++) == CHAR) - *dp++ = *sp++; - /* make sure the ident array stays NUL padded */ - memset(dp, 0, (ident + IDENT) - dp + 1); + while ((dp - ident) < IDENT && (c = *sp++) == CHAR) + *dp++ = *sp++; if (c != EOS) /* word is not unquoted */ - *ident = '\0'; + dp = ident; + /* make sure the ident array stays NUL padded */ + memset(dp, 0, (ident + IDENT) - dp + 1); - if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) { + if (!(cf & (KEYWORD | ALIAS))) + return (LWORD); + + if (*ident != '\0') { struct tbl *p; uint32_t h = hash(ident); @@ -1077,8 +1051,7 @@ * pushed into an SREREAD) which is what * we want here anyway: find out whether * the alias name is followed by a POSIX - * function definition (only the opening - * parenthesis is checked though) + * function definition */ ++cp; /* prefer functions over aliases */ @@ -1106,21 +1079,23 @@ goto Again; } } + } else if (cf & ALIAS) { + /* retain typeset et al. even when quoted */ + if (assign_command((dp = wdstrip(yylval.cp, 0)), true)) + strlcpy(ident, dp, sizeof(ident)); + afree(dp, ATEMP); } return (LWORD); } static void -gethere(bool iseof) +gethere(void) { struct ioword **p; for (p = heres; p < herep; p++) - if (iseof && !((*p)->flag & IOHERESTR)) - /* only here strings at EOF */ - return; - else + if (!((*p)->ioflag & IOHERESTR)) readhere(*p); herep = heres; } @@ -1136,20 +1111,11 @@ const char *eof, *eofp; XString xs; char *xp; - int xpos; - - if (iop->flag & IOHERESTR) { - /* process the here string */ - iop->heredoc = xp = evalstr(iop->delim, DOBLANK); - xpos = strlen(xp) - 1; - memmove(xp, xp + 1, xpos); - xp[xpos] = '\n'; - return; - } + size_t xpos; - eof = iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0); + eof = evalstr(iop->delim, 0); - if (!(iop->flag & IOEVAL)) + if (!(iop->ioflag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); @@ -1158,10 +1124,10 @@ /* beginning of line */ eofp = eof; xpos = Xsavepos(xs, xp); - if (iop->flag & IOSKIP) { + if (iop->ioflag & IOSKIP) { /* skip over leading tabs */ while ((c = getsc()) == '\t') - /* nothing */; + ; /* nothing */ goto heredoc_parse_char; } heredoc_read_char: @@ -1197,7 +1163,7 @@ while (c != '\n') { if (!c) /* oops, reached EOF */ - yyerror("%s '%s' unclosed\n", "here document", eof); + yyerror("here document '%s' unclosed\n", eof); /* store character */ Xcheck(xs, xp); Xput(xs, xp, c); @@ -1220,7 +1186,7 @@ Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); - if (!(iop->flag & IOEVAL)) + if (!(iop->ioflag & IOEVAL)) ignore_backslash_newline--; } @@ -1383,8 +1349,10 @@ ksh_tmout_state = TMOUT_READING; alarm(ksh_tmout); } - if (interactive) + if (interactive) { + histsave(&s->line, NULL, HIST_FLUSH, true); change_winsz(); + } #ifndef MKSH_NO_CMDLINE_EDITING if (have_tty && ( #if !MKSH_S_NOVI @@ -1455,16 +1423,23 @@ if (s->type == SFILE) shf_fdclose(s->u.shf); s->str = NULL; - } else if (interactive && *s->str && - (cur_prompt != PS1 || !ctype(*s->str, C_IFS | C_IFSWS))) { - histsave(&s->line, s->str, true, true); + } else if (interactive && *s->str) { + if (cur_prompt != PS1) + histsave(&s->line, s->str, HIST_APPEND, true); + else if (!ctype(*s->str, C_IFS | C_IFSWS)) + histsave(&s->line, s->str, HIST_QUEUE, true); #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY + else + goto check_for_sole_return; } else if (interactive && cur_prompt == PS1) { + check_for_sole_return: cp = Xstring(s->xs, xp); while (*cp && ctype(*cp, C_IFSWS)) ++cp; - if (!*cp) + if (!*cp) { + histsave(&s->line, NULL, HIST_FLUSH, true); histsync(); + } #endif } if (interactive) @@ -1474,7 +1449,7 @@ void set_prompt(int to, Source *s) { - cur_prompt = to; + cur_prompt = (uint8_t)to; switch (to) { /* command */ @@ -1489,6 +1464,7 @@ struct shf *shf; char * volatile ps1; Area *saved_atemp; + int saved_lineno; ps1 = str_val(global("PS1")); shf = shf_sopen(NULL, strlen(ps1) * 2, @@ -1497,9 +1473,12 @@ if (*ps1 != '!' || *++ps1 == '!') shf_putchar(*ps1++, shf); else - shf_fprintf(shf, "%d", - s ? s->line + 1 : 0); + shf_fprintf(shf, "%lu", s ? + (unsigned long)s->line + 1 : 0UL); ps1 = shf_sclose(shf); + saved_lineno = current_lineno; + if (s) + current_lineno = s->line + 1; saved_atemp = ATEMP; newenv(E_ERRH); if (kshsetjmp(e->jbuf)) { @@ -1515,6 +1494,7 @@ char *cp = substitute(ps1, 0); strdupx(prompt, cp, saved_atemp); } + current_lineno = saved_lineno; quitenv(NULL); } break; @@ -1798,15 +1778,3 @@ return (si->base + STATE_BSIZE - 1); } - -static int -s_get(void) -{ - return (getsc()); -} - -static void -s_put(int c) -{ - ungetsc(c); -} diff -Nru mksh-50e/lksh.1 mksh-52c/lksh.1 --- mksh-50e/lksh.1 2013-05-22 20:18:31.000000000 +0200 +++ mksh-52c/lksh.1 2016-02-11 21:12:35.000000000 +0100 @@ -1,7 +1,7 @@ -.\" $MirOS: src/bin/mksh/lksh.1,v 1.5 2013/05/22 18:18:06 tg Exp $ +.\" $MirOS: src/bin/mksh/lksh.1,v 1.17 2016/02/11 20:12:09 tg Exp $ .\"- -.\" Copyright (c) 2008, 2009, 2010, 2012, 2013 -.\" Thorsten “mirabilos” Glaser +.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016 +.\" mirabilos .\" .\" Provided that these terms and disclaimer and all copyright notices .\" are retained or reproduced in an accompanying document, permission @@ -27,7 +27,9 @@ .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba -.\" Also make sure to use \& especially with two-letter words. +.\" Also make sure to use \& *before* a punctuation char that is to not +.\" be interpreted as punctuation, and especially with two-letter words +.\" but also (after) a period that does not end a sentence (“e.g.\&”). .\" The section after the "doc" macropackage has been loaded contains .\" additional code to convene between the UCB mdoc macropackage (and .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. @@ -72,7 +74,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: May 22 2013 $ +.Dd $Mdocdate: February 11 2016 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -173,12 +175,27 @@ .Nm mksh instead of relying on legacy or idiotic POSIX-mandated behaviour, since the MirBSD Korn Shell scripting language is much more consistent. +.Pp +Note that it's strongly recommended to invoke +.Nm +with at least the +.Fl o Ic posix +option, if not both that +.Em and Fl o Ic sh , +to fully enjoy better compatibility to the +.Tn POSIX +standard (which is probably why you use +.Nm +over +.Nm mksh +in the first place) or legacy scripts, respectively. .Sh LEGACY MODE .Nm -has the following differences from +currently has the following differences from .Nm mksh : .Bl -bullet .It +.\"XXX TODO: remove (some systems may wish to have lksh as ksh) There is no explicit support for interactive use, nor any command line editing or history code. Hence, @@ -195,35 +212,51 @@ .Dq LEGACY KSH instead of .Dq MIRBSD KSH . -.It -.Nm -only offers the traditional ten file descriptors to scripts. +Note that the rest of the version string is identical between +the two shell flavours, and the behaviour and differences can +change between versions; see the accompanying manual page +.Xr mksh 1 +for the versions this document applies to. .It .Nm uses .Tn POSIX arithmetics, which has quite a few implications: -The data type for arithmetics is the host ISO C +The data type for arithmetics is the host +.Tn ISO +C .Vt long data type. -Signed integer wraparound is Undefined Behaviour. +Signed integer wraparound is Undefined Behaviour; this means that... +.Bd -literal -offset indent +$ echo $((2147483647 + 1)) +.Ed +.Pp +\&... is permitted to, e.g. delete all files on your system +(the figure differs for non-32-bit systems, the rule doesn't). The sign of the result of a modulo operation with at least one negative operand is unspecified. Shift operations on negative numbers are unspecified. Division of the largest negative number by \-1 is Undefined Behaviour. The compiler is permitted to delete all data and crash the system -if Undefined Behaviour occurs. +if Undefined Behaviour occurs (see above for an example). +.It +.Nm +only offers the traditional ten file descriptors to scripts. .It +.\"XXX TODO: move this to FPOSIX 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 +.\"XXX TODO: move this to FPOSIX The .Tn GNU .Nm bash extension &\*(Gt to redirect stdout and stderr in one go is not parsed. .It +.\"XXX TODO: drop along with allowing interactivity The .Nm mksh command line option @@ -245,11 +278,24 @@ .Xr getopt 1 command. .It -.Nm lksh , -unlike +.\"XXX TODO: move to FPOSIX/FSH +Unlike .At .Nm ksh , -does not keep file descriptors \*(Gt 2 private. +.Nm mksh +in +.Fl o Ic posix +or +.Fl o Ic sh +mode and +.Nm lksh +do not keep file descriptors \*(Gt 2 private from sub-processes. +.It +Functions defined with the +.Ic function +reserved word share the shell options +.Pq Ic set -o +instead of locally scoping them. .El .Sh SEE ALSO .Xr mksh 1 @@ -258,13 +304,30 @@ .Pp .Pa https://www.mirbsd.org/ksh\-chan.htm .Sh CAVEATS +The distinction between the shell variants +.Pq Nm lksh / Nm mksh +and shell flags +.Pq Fl o Ic posix / Ic sh +will be reworked for an upcoming release. +.Pp To use .Nm as .Pa /bin/sh , compilation to enable .Ic set -o posix -by default is highly recommended for better standards compliance. +by default if called as +.Nm sh +is highly recommended for better standards compliance. +For better compatibility with legacy scripts, such as many +.Tn Debian +maintainer scripts, Upstart and SYSV init scripts, and other +unfixed scripts, using the compile-time options for enabling +.Em both +.Ic set -o posix -o sh +when the shell is run as +.Nm sh +is recommended. .Pp .Nm tries to make a cross between a legacy bourne/posix compatibl-ish @@ -274,7 +337,7 @@ .Pp The .Ic set -built-in command does not have all options one would expect +built-in command does not currently have all options one would expect from a full-blown .Nm mksh or diff -Nru mksh-50e/main.c mksh-52c/main.c --- mksh-50e/main.c 2015-03-01 16:43:26.000000000 +0100 +++ mksh-52c/main.c 2016-02-26 22:54:02.000000000 +0100 @@ -1,12 +1,12 @@ -/* $OpenBSD: main.c,v 1.55 2015/02/09 09:09:30 jsg Exp $ */ +/* $OpenBSD: main.c,v 1.57 2015/09/10 22:48:58 nicm Exp $ */ /* $OpenBSD: tty.c,v 1.10 2014/08/10 02:44:26 guenther Exp $ */ -/* $OpenBSD: io.c,v 1.25 2014/08/11 20:28:47 guenther Exp $ */ -/* $OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $ */ +/* $OpenBSD: io.c,v 1.26 2015/09/11 08:00:27 guenther Exp $ */ +/* $OpenBSD: table.c,v 1.16 2015/09/01 13:12:31 tedu Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -34,7 +34,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/main.c,v 1.285.2.2 2015/03/01 15:43:01 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/main.c,v 1.310 2016/02/26 21:53:36 tg Exp $"); extern char **environ; @@ -43,7 +43,7 @@ #endif #ifndef MKSH_DEFAULT_TMPDIR -#define MKSH_DEFAULT_TMPDIR "/tmp" +#define MKSH_DEFAULT_TMPDIR MKSH_UNIXROOT "/tmp" #endif static uint8_t isuc(const char *); @@ -66,28 +66,22 @@ Ttypeset, "-x", "HOME", "PATH", "SHELL", NULL, Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL, Talias, - "integer=typeset -i", - Tlocal_typeset, + "integer=\\typeset -i", + "local=\\typeset", /* not "alias -t --": hash -r needs to work */ - "hash=alias -t", - "type=whence -v", -#if !defined(ANDROID) && !defined(MKSH_UNEMPLOYED) - /* not in Android for political reasons */ - /* not in ARGE mksh due to no job control */ - "stop=kill -STOP", -#endif - "autoload=typeset -fu", - "functions=typeset -f", - "history=fc -l", - "nameref=typeset -n", + "hash=\\builtin alias -t", + "type=\\builtin whence -v", + "autoload=\\typeset -fu", + "functions=\\typeset -f", + "history=\\builtin fc -l", + "nameref=\\typeset -n", "nohup=nohup ", - Tr_fc_e_dash, - "source=PATH=$PATH:. command .", - "login=exec login", + "r=\\builtin fc -e -", + "login=\\exec login", NULL, /* this is what AT&T ksh seems to track, with the addition of emacs */ Talias, "-tU", - "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls", + Tcat, "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL, NULL }; @@ -116,13 +110,13 @@ } *bufptr; char *cp; - cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM); + cp = alloc(sizeof(*bufptr) - sizeof(ALLOC_ITEM), APERM); #ifdef DEBUG /* clear the allocated space, for valgrind */ - memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE); + memset(cp, 0, sizeof(*bufptr) - sizeof(ALLOC_ITEM)); #endif /* undo what alloc() did to the malloc result address */ - bufptr = (void *)(cp - ALLOC_SIZE); + bufptr = (void *)(cp - sizeof(ALLOC_ITEM)); /* PIE or something similar provides us with deltas here */ bufptr->dataptr = &rndsetupstate; /* ASLR in at least Windows, Linux, some BSDs */ @@ -136,6 +130,9 @@ /* introduce variation (and yes, second arg MBZ for portability) */ mksh_TIME(bufptr->tv); +#ifdef MKSH_ALLOC_CATCH_UNDERRUNS + mprotect(((char *)bufptr) + 4096, 4096, PROT_READ | PROT_WRITE); +#endif h = chvt_rndsetup(bufptr, sizeof(*bufptr)); afree(cp, APERM); @@ -193,10 +190,16 @@ ssize_t k; #endif +#ifdef __OS2__ + for (i = 0; i < 3; ++i) + if (!isatty(i)) + setmode(i, O_BINARY); +#endif + /* do things like getpgrp() et al. */ chvt_reinit(); - /* make sure argv[] is sane */ + /* make sure argv[] is sane, for weird OSes */ if (!*argv) { argv = empty_argv; argc = 1; @@ -205,6 +208,8 @@ /* initialise permanent Area */ ainit(&aperm); + /* max. name length: -2147483648 = 11 (+ NUL) */ + vtemp = alloc(offsetof(struct tbl, name[0]) + 12, APERM); /* set up base environment */ env.type = E_NONE; @@ -249,7 +254,7 @@ /* define built-in commands and see if we were called as one */ ktinit(APERM, &builtins, - /* currently up to 51 builtins: 75% of 128 = 2^7 */ + /* currently up to 54 builtins: 75% of 128 = 2^7 */ 7); for (i = 0; mkshbuiltins[i].name != NULL; i++) if (!strcmp(ccp, builtin(mkshbuiltins[i].name, @@ -264,7 +269,7 @@ #if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED) /* are we called as -sh or /bin/sh or so? */ - if (!strcmp(ccp, "sh")) { + if (!strcmp(ccp, "sh" MKSH_EXE_EXT)) { /* either also turns off braceexpand */ #ifdef MKSH_BINSHPOSIX /* enable better POSIX conformance */ @@ -318,7 +323,10 @@ * "keeping a regular /usr"; this is supposed * to be a sane 'basic' default PATH */ - def_path = "/bin:/usr/bin:/sbin:/usr/sbin"; + def_path = MKSH_UNIXROOT "/bin" MKSH_PATHSEPS + MKSH_UNIXROOT "/usr/bin" MKSH_PATHSEPS + MKSH_UNIXROOT "/sbin" MKSH_PATHSEPS + MKSH_UNIXROOT "/usr/sbin"; #endif /* @@ -361,7 +369,7 @@ vp = global("PWD"); cp = str_val(vp); /* Try to use existing $PWD if it is valid */ - set_current_wd((cp[0] == '/' && test_eval(NULL, TO_FILEQ, cp, ".", + set_current_wd((mksh_abspath(cp) && test_eval(NULL, TO_FILEQ, cp, ".", true)) ? cp : NULL); if (current_wd[0]) simplify_path(current_wd); @@ -407,11 +415,7 @@ setint_n((vp_pipest = global("PIPESTATUS")), 0, 10); /* Set this before parsing arguments */ - Flag(FPRIVILEGED) = ( -#if HAVE_ISSETUGID - issetugid() || -#endif - kshuid != ksheuid || kshgid != kshegid) ? 2 : 0; + Flag(FPRIVILEGED) = (kshuid != ksheuid || kshgid != kshegid) ? 2 : 0; /* this to note if monitor is set on command line (see below) */ #ifndef MKSH_UNEMPLOYED @@ -436,7 +440,7 @@ } else if (Flag(FCOMMAND)) { s = pushs(SSTRINGCMDLINE, ATEMP); if (!(s->start = s->str = argv[argi++])) - errorf("%s %s", "-c", "requires an argument"); + errorf("-c %s", "requires an argument"); while (*s->str) { if (*s->str != ' ' && ctype(*s->str, C_QUOTE)) break; @@ -454,7 +458,19 @@ kshname = argv[argi++]; } else if (argi < argc && !Flag(FSTDIN)) { s = pushs(SFILE, ATEMP); +#ifdef __OS2__ + /* + * A bug in OS/2 extproc (like shebang) handling makes + * it not pass the full pathname of a script, so we need + * to search for it. This changes the behaviour of a + * simple "mksh foo", but can't be helped. + */ + s->file = search_path(argv[argi++], path, X_OK, NULL); + if (!s->file || !*s->file) + s->file = argv[argi - 1]; +#else s->file = argv[argi++]; +#endif s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC); if (s->u.shf == NULL) { @@ -693,7 +709,7 @@ unwind(i); /* NOTREACHED */ default: - internal_errorf("%s %d", "include", i); + internal_errorf("include %d", i); /* NOTREACHED */ } } @@ -785,7 +801,7 @@ default: source = old_source; quitenv(NULL); - internal_errorf("%s %d", "shell", i); + internal_errorf("shell %d", i); /* NOTREACHED */ } while (/* CONSTCOND */ 1) { @@ -803,6 +819,8 @@ set_prompt(PS1, s); } t = compile(s, sfirst); + if (interactive) + histsave(&s->line, NULL, HIST_FLUSH, true); sfirst = false; if (!t) goto source_no_tree; @@ -913,9 +931,9 @@ * struct env includes ALLOC_ITEM for alignment constraints * so first get the actually used memory, then assign it */ - cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP); + cp = alloc(sizeof(struct env) - sizeof(ALLOC_ITEM), ATEMP); /* undo what alloc() did to the malloc result address */ - ep = (void *)(cp - ALLOC_SIZE); + ep = (void *)(cp - sizeof(ALLOC_ITEM)); /* initialise public members of struct env (not the ALLOC_ITEM) */ ainit(&ep->area); ep->oenv = e; @@ -1011,7 +1029,7 @@ /* free the struct env - tricky due to the ALLOC_ITEM inside */ cp = (void *)ep; - afree(cp + ALLOC_SIZE, ATEMP); + afree(cp + sizeof(ALLOC_ITEM), ATEMP); } /* Called after a fork to cleanup stuff left over from parents environment */ @@ -1246,8 +1264,7 @@ /* * POSIX special builtins and ksh special builtins cause - * non-interactive shells to exit. - * XXX odd use of KEEPASN; also may not want LERROR here + * non-interactive shells to exit. XXX may not want LERROR here */ if (builtin_spec) { builtin_argv0 = NULL; @@ -1286,8 +1303,9 @@ strcmp(source->file, kshname) != 0) shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-')); if (fileline && source && source->file != NULL) { - shf_fprintf(shl_out, "%s[%d]: ", source->file, - source->errline > 0 ? source->errline : source->line); + shf_fprintf(shl_out, "%s[%lu]: ", source->file, + (unsigned long)(source->errline ? + source->errline : source->line)); source->errline = 0; } } @@ -1351,7 +1369,7 @@ shf_fdopen(2, SHF_WR, shl_xtrace); #ifdef DF if ((lfp = getenv("SDMKSH_PATH")) == NULL) { - if ((lfp = getenv("HOME")) == NULL || *lfp != '/') + if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp)) errorf("cannot get home directory"); lfp = shf_smprintf("%s/mksh-dbg.txt", lfp); } @@ -1401,7 +1419,7 @@ int nfd = fd; if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 && - errno == EBADF) + (errno == EBADF || errno == EPERM)) return (-1); if (nfd < 0 || nfd > SHRT_MAX) errorf("too many files open in shell"); @@ -1453,13 +1471,20 @@ int check_fd(const char *name, int mode, const char **emsgp) { - int fd, fl; + int fd = 0, fl; if (name[0] == 'p' && !name[1]) return (coproc_getfd(mode, emsgp)); - for (fd = 0; ksh_isdigit(*name); ++name) - fd = (fd * 10) + *name - '0'; - if (*name || fd >= FDBASE) { + while (ksh_isdigit(*name)) { + fd = fd * 10 + ksh_numdig(*name); + if (fd >= FDBASE) { + if (emsgp) + *emsgp = "file descriptor too large"; + return (-1); + } + ++name; + } + if (*name) { if (emsgp) *emsgp = "illegal file descriptor name"; return (-1); @@ -1605,28 +1630,20 @@ memcpy(cp, "/shXXXXXX.tmp", 14); /* point to the first of six Xes */ cp += 3; - /* generate random part of filename */ - len = -1; - do { - i = rndget() % 36; - cp[++len] = i < 26 ? 'a' + i : '0' + i - 26; - } while (len < 5); /* cyclically attempt to open a temporary file */ - while ((i = open(tp->tffn, O_CREAT | O_EXCL | O_RDWR | O_BINARY, - 0600)) < 0) { - if (errno != EEXIST) + do { + /* generate random part of filename */ + len = 0; + do { + cp[len++] = digits_lc[rndget() % 36]; + } while (len < 6); + + /* check if this one works */ + if ((i = binopen3(tp->tffn, O_CREAT | O_EXCL | O_RDWR, + 0600)) < 0 && errno != EEXIST) goto maketemp_out; - /* count down from z to a then from 9 to 0 */ - while (cp[len] == '0') - if (!len--) - goto maketemp_out; - if (cp[len] == 'a') - cp[len] = '9'; - else - --cp[len]; - /* do another cycle */ - } + } while (i < 0); if (type == TT_FUNSUB) { /* map us high and mark as close-on-exec */ diff -Nru mksh-50e/mirhash.h mksh-52c/mirhash.h --- mksh-50e/mirhash.h 2014-10-02 21:34:31.000000000 +0200 +++ mksh-52c/mirhash.h 2015-11-29 18:05:28.000000000 +0100 @@ -1,6 +1,6 @@ /*- - * Copyright © 2011, 2014 - * Thorsten Glaser + * Copyright © 2011, 2014, 2015 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -44,7 +44,7 @@ #include -__RCSID("$MirOS: src/bin/mksh/mirhash.h,v 1.3 2014/10/02 19:34:06 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/mirhash.h,v 1.6 2015/11/29 17:05:02 tg Exp $"); /*- * BAFH itself is defined by the following primitives: @@ -61,7 +61,8 @@ * the context is (still) zero, adding a NUL byte is not ignored. * * • BAFHror(eax,cl) evaluates to the unsigned 32-bit integer “eax”, - * rotated right by “cl” ∈ [0;31]; no casting, be careful! + * rotated right by “cl” ∈ [0; 31] (no casting, be careful!) where + * “eax” must be uint32_t and “cl” an in-range integer. * * • BAFHFinish(ctx) avalanches the context around so every sub-byte * depends on all input octets; afterwards, the context variable’s @@ -88,7 +89,7 @@ * • BAFHHostStr(ctx,buf) does the same for C strings. * * All macros may use ctx multiple times in their expansion, but all - * other arguments are always evaluated at most once. + * other arguments are always evaluated at most once except BAFHror. * * To stay portable, never use the BAFHHost*() macros (these are for * host-local entropy shuffling), and encode numbers using ULEB128. @@ -206,6 +207,7 @@ } BAFHHost_v; \ \ BAFHUpdate_s = (const void *)(s); \ + BAFHHost_v.as_u32 = 0; \ if ((BAFHHost_v.as_u8[0] = *BAFHUpdate_s) != 0) \ ++BAFHUpdate_s; \ if ((BAFHHost_v.as_u8[1] = *BAFHUpdate_s) != 0) \ diff -Nru mksh-50e/misc.c mksh-52c/misc.c --- mksh-50e/misc.c 2015-03-01 16:43:27.000000000 +0100 +++ mksh-52c/misc.c 2016-03-04 15:26:39.000000000 +0100 @@ -1,10 +1,10 @@ -/* $OpenBSD: misc.c,v 1.39 2015/01/16 06:39:32 deraadt Exp $ */ -/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */ +/* $OpenBSD: misc.c,v 1.41 2015/09/10 22:48:58 nicm Exp $ */ +/* $OpenBSD: path.c,v 1.13 2015/09/05 09:47:08 jsg Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -30,7 +30,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.219.2.2 2015/03/01 15:43:02 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.242 2016/03/04 14:26:13 tg Exp $"); #define KSH_CHVT_FLAG #ifdef MKSH_SMALL @@ -52,7 +52,7 @@ const unsigned char *, bool) MKSH_A_PURE; static int do_gmatch(const unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *) MKSH_A_PURE; -static const unsigned char *cclass(const unsigned char *, unsigned char) +static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char) MKSH_A_PURE; #ifdef KSH_CHVT_CODE static void chvt(const Getopt *); @@ -93,12 +93,8 @@ void initctypes(void) { - int c; - - for (c = 'a'; c <= 'z'; c++) - chtypes[c] |= C_ALPHA; - for (c = 'A'; c <= 'Z'; c++) - chtypes[c] |= C_ALPHA; + setctypes(letters_uc, C_ALPHA); + setctypes(letters_lc, C_ALPHA); chtypes['_'] |= C_ALPHA; setctypes("0123456789", C_DIGIT); /* \0 added automatically */ @@ -126,6 +122,17 @@ #define SHFLAGS_DEFNS +#define FN(sname,cname,flags,ochar) \ + static const struct { \ + /* character flag (if any) */ \ + char c; \ + /* OF_* */ \ + unsigned char optflags; \ + /* long name of option */ \ + char name[sizeof(sname)]; \ + } shoptione_ ## cname = { \ + ochar, flags, sname \ + }; #include "sh_flags.gen" #define OFC(i) (options[i][-2]) @@ -166,11 +173,11 @@ int opts[NELEM(options)]; }; -static char *options_fmt_entry(char *, size_t, unsigned int, const void *); +static void options_fmt_entry(char *, size_t, unsigned int, const void *); static void printoptions(bool); /* format a single select menu item */ -static char * +static void options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) { const struct options_info *oi = (const struct options_info *)arg; @@ -178,7 +185,6 @@ shf_snprintf(buf, buflen, "%-*s %s", oi->opt_width, OFN(oi->opts[i]), Flag(oi->opts[i]) ? "on" : "off"); - return (buf); } static void @@ -545,7 +551,7 @@ if (num.u > 214748364U) /* overflow on multiplication */ return (0); - num.u = num.u * 10U + (unsigned int)(c - '0'); + num.u = num.u * 10U + (unsigned int)ksh_numdig(c); /* now: num.u <= 2147483649U */ } while ((c = *s++)); @@ -776,7 +782,7 @@ } switch (*p++) { case '[': - if (sc == 0 || (p = cclass(p, sc)) == NULL) + if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL) return (0); break; @@ -889,7 +895,7 @@ } static const unsigned char * -cclass(const unsigned char *p, unsigned char sub) +gmatch_cclass(const unsigned char *p, unsigned char sub) { unsigned char c, d; bool notp, found = false; @@ -1007,7 +1013,7 @@ const char *arg = argv[go->optind], flag = arg ? *arg : '\0'; go->p = 1; - if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { + if (flag == '-' && ksh_isdash(arg + 1)) { go->optind++; go->p = 0; go->info |= GI_MINUSMINUS; @@ -1220,7 +1226,7 @@ */ void print_columns(struct shf *shf, unsigned int n, - char *(*func)(char *, size_t, unsigned int, const void *), + void (*func)(char *, size_t, unsigned int, const void *), const void *arg, size_t max_oct, size_t max_colz, bool prefcol) { unsigned int i, r, c, rows, cols, nspace, max_col; @@ -1249,17 +1255,20 @@ str = alloc(max_oct, ATEMP); /* - * We use (max_col + 1) to consider the space separator. - * Note that no space is printed after the last column - * to avoid problems with terminals that have auto-wrap. + * We use (max_col + 2) to consider the separator space. + * Note that no spaces are printed after the last column + * to avoid problems with terminals that have auto-wrap, + * but we need to also take this into account in x_cols. */ - cols = x_cols / (max_col + 1); + cols = (x_cols + 1) / (max_col + 2); /* if we can only print one column anyway, skip the goo */ if (cols < 2) { - for (i = 0; i < n; ++i) - shf_fprintf(shf, "%s\n", - (*func)(str, max_oct, i, arg)); + for (i = 0; i < n; ++i) { + (*func)(str, max_oct, i, arg); + shf_puts(str, shf); + shf_putc('\n', shf); + } goto out; } @@ -1270,18 +1279,19 @@ } nspace = (x_cols - max_col * cols) / cols; + if (nspace < 2) + nspace = 2; max_col = -max_col; - if (nspace <= 0) - nspace = 1; for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { - i = c * rows + r; - if (i < n) { - shf_fprintf(shf, "%*s", max_col, - (*func)(str, max_oct, i, arg)); - if (c + 1 < cols) - shf_fprintf(shf, "%*s", nspace, null); - } + if ((i = c * rows + r) >= n) + break; + (*func)(str, max_oct, i, arg); + if (i + rows >= n) + shf_puts(str, shf); + else + shf_fprintf(shf, "%*s%*s", + (int)max_col, str, (int)nspace, null); } shf_putchar('\n', shf); } @@ -1289,32 +1299,27 @@ afree(str, ATEMP); } -/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ +/* strip all NUL bytes from buf; output is NUL-terminated if stripped */ void -strip_nuls(char *buf, int nbytes) +strip_nuls(char *buf, size_t len) { - char *dst; + char *cp, *dp, *ep; - /* - * nbytes check because some systems (older FreeBSDs) have a - * buggy memchr() - */ - if (nbytes && (dst = memchr(buf, '\0', nbytes))) { - char *end = buf + nbytes; - char *p, *q; - - for (p = dst; p < end; p = q) { - /* skip a block of nulls */ - while (++p < end && *p == '\0') - ; - /* find end of non-null block */ - if (!(q = memchr(p, '\0', end - p))) - q = end; - memmove(dst, p, q - p); - dst += q - p; - } - *dst = '\0'; - } + if (!len || !(dp = memchr(buf, '\0', len))) + return; + + ep = buf + len; + cp = dp; + + cp_has_nul_byte: + while (cp++ < ep && *cp == '\0') + ; /* nothing */ + while (cp < ep && *cp != '\0') + *dp++ = *cp++; + if (cp < ep) + goto cp_has_nul_byte; + + *dp = '\0'; } /* @@ -1407,12 +1412,12 @@ /* max. recursion depth */ int symlinks = 32; - if (upath[0] == '/') { + if (mksh_abspath(upath)) { /* upath is an absolute pathname */ strdupx(ipath, upath, ATEMP); } else { /* upath is a relative pathname, prepend cwd */ - if ((tp = ksh_get_wd()) == NULL || tp[0] != '/') + if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp)) return (NULL); ipath = shf_smprintf("%s%s%s", tp, "/", upath); afree(tp, ATEMP); @@ -1515,7 +1520,7 @@ tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip); afree(ipath, ATEMP); ip = ipath = tp; - if (ldest[0] != '/') { + if (!mksh_abspath(ldest)) { /* symlink target is a relative path */ xp = Xrestpos(xs, xp, pos); } else @@ -1565,16 +1570,14 @@ } /* return target path */ - if (ldest != NULL) - afree(ldest, ATEMP); + afree(ldest, ATEMP); afree(ipath, ATEMP); return (Xclose(xs, xp)); notfound: /* save; freeing memory might trash it */ llen = errno; - if (ldest != NULL) - afree(ldest, ATEMP); + afree(ldest, ATEMP); afree(ipath, ATEMP); Xfree(xs, xp); errno = llen; @@ -1615,7 +1618,7 @@ if (!file) file = null; - if (file[0] == '/') { + if (mksh_abspath(file)) { *phys_pathp = 0; use_cdpath = false; } else { @@ -1632,15 +1635,15 @@ if (!plist) use_cdpath = false; else if (use_cdpath) { - char *pend; + char *pend = plist; - for (pend = plist; *pend && *pend != ':'; pend++) - ; + while (*pend && *pend != MKSH_PATHSEPC) + ++pend; plen = pend - plist; *cdpathp = *pend ? pend + 1 : NULL; } - if ((!use_cdpath || !plen || plist[0] != '/') && + if ((!use_cdpath || !plen || !mksh_abspath(plist)) && (cwd && *cwd)) { len = strlen(cwd); XcheckN(*xsp, xp, len); @@ -1726,7 +1729,7 @@ continue; else if (len == 2 && tp[1] == '.') { /* parent level, but how? */ - if (*p == '/') + if (mksh_abspath(p)) /* absolute path, only one way */ goto strip_last_component; else if (dp > sp) { @@ -1858,7 +1861,7 @@ * we don't */ if ((cp = strstr(current_wd, wp[0])) == NULL) { - bi_errorf("bad substitution"); + bi_errorf(Tbadsubst); return (2); } /*- @@ -1926,7 +1929,7 @@ /* Ignore failure (happens if readonly or integer) */ setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR); - if (Xstring(xs, xp)[0] != '/') { + if (!mksh_abspath(Xstring(xs, xp))) { pwd = NULL; } else if (!physical) { goto norealpath_PWD; @@ -2004,9 +2007,9 @@ #endif } } - if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) { + if ((fd = binopen2(dv, O_RDWR)) < 0) { sleep(1); - if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) { + if ((fd = binopen2(dv, O_RDWR)) < 0) { errorf("%s: %s %s", "chvt", "can't open", dv); } } @@ -2199,8 +2202,8 @@ wc = 0; i = 3; while (i--) - if ((c = (*fg)()) >= '0' && c <= '7') - wc = (wc << 3) + (c - '0'); + if ((c = (*fg)()) >= ord('0') && c <= ord('7')) + wc = (wc << 3) + ksh_numdig(c); else { (*fp)(c); break; @@ -2209,13 +2212,13 @@ case 'U': i = 8; if (/* CONSTCOND */ 0) - /* FALLTHROUGH */ + /* FALLTHROUGH */ case 'u': - i = 4; + i = 4; if (/* CONSTCOND */ 0) - /* FALLTHROUGH */ + /* FALLTHROUGH */ case 'x': - i = cstyle ? -1 : 2; + i = cstyle ? -1 : 2; /** * x: look for a hexadecimal number with up to * two (C style: arbitrary) digits; convert @@ -2226,12 +2229,12 @@ wc = 0; while (i--) { wc <<= 4; - if ((c = (*fg)()) >= '0' && c <= '9') - wc += c - '0'; - else if (c >= 'A' && c <= 'F') - wc += c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - wc += c - 'a' + 10; + if ((c = (*fg)()) >= ord('0') && c <= ord('9')) + wc += ksh_numdig(c); + else if (c >= ord('A') && c <= ord('F')) + wc += ksh_numuc(c) + 10; + else if (c >= ord('a') && c <= ord('f')) + wc += ksh_numlc(c) + 10; else { wc >>= 4; (*fp)(c); diff -Nru mksh-50e/mksh.1 mksh-52c/mksh.1 --- mksh-50e/mksh.1 2015-03-01 16:43:28.000000000 +0100 +++ mksh-52c/mksh.1 2016-03-04 19:29:07.000000000 +0100 @@ -1,9 +1,9 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.344.2.3 2015/03/01 15:43:03 tg Exp $ -.\" $OpenBSD: ksh.1,v 1.156 2015/01/16 15:32:32 schwarze Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.392 2016/03/04 18:28:41 tg Exp $ +.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, -.\" 2010, 2011, 2012, 2013, 2014, 2015 -.\" Thorsten “mirabilos” Glaser +.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016 +.\" mirabilos .\" .\" Provided that these terms and disclaimer and all copyright notices .\" are retained or reproduced in an accompanying document, permission @@ -29,7 +29,9 @@ .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba -.\" Also make sure to use \& especially with two-letter words. +.\" Also make sure to use \& *before* a punctuation char that is to not +.\" be interpreted as punctuation, and especially with two-letter words +.\" but also (after) a period that does not end a sentence (“e.g.\&”). .\" The section after the "doc" macropackage has been loaded contains .\" additional code to convene between the UCB mdoc macropackage (and .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. @@ -74,7 +76,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: March 1 2015 $ +.Dd $Mdocdate: March 4 2016 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" @@ -179,6 +181,11 @@ Its command language is a superset of the .Xr sh C shell language and largely compatible to the original Korn shell. +At times, this manual page may give scripting advice; while it +sometimes does take portable shell scripting or various standards +into account all information is first and foremost presented with +.Nm +in mind and should be taken as such. .Ss I'm an Android user, so what's mksh? .Nm mksh is a @@ -426,7 +433,7 @@ .Ql \&(( .. )) is used in arithmetic expressions; and lastly, -.Ql \&( .. )\& +.Ql \&( .. \&) is used to create subshells. .Pp Whitespace and meta-characters can be quoted individually using a backslash @@ -434,7 +441,7 @@ or in groups using double .Pq Sq \&" or single -.Pq Sq \*(aq +.Pq Dq \*(aq quotes. Note that the following characters are also treated specially by the shell and must be quoted if they are to represent themselves: @@ -476,7 +483,7 @@ .Ql } delimit .Xr csh 1 Ns -style -alterations (see +alternations (see .Sx Brace expansion below); and finally, @@ -914,7 +921,7 @@ .Sx Arithmetic expressions and the .Ic let -command, below). +command, below) in a compound construct. .It Bq Bq Ar \ \&expression\ \& Similar to the .Ic test @@ -988,7 +995,7 @@ .Ql \e and the newline are stripped. Second, a single quote -.Pq Sq \*(aq +.Pq Dq \*(aq quotes everything up to the next single quote (this may span lines). Third, a double quote .Pq Sq \&" @@ -997,13 +1004,14 @@ .Ql \` and .Ql \e , -up to the next unquoted double quote. +up to the next unescaped double quote. .Ql $ and .Ql \` -inside double quotes have their usual meaning (i.e. parameter, command, or -arithmetic substitution) except no field splitting is carried out on the -results of double-quoted substitutions. +inside double quotes have their usual meaning (i.e. parameter, arithmetic, +or command substitution) except no field splitting is carried out on the +results of double-quoted substitutions, and the old-style form of command +substitution has backslash-quoting for double quotes enabled. If a .Ql \e inside a double-quoted string is followed by @@ -1026,7 +1034,9 @@ the expanded result is treated as any other single-quoted string. If a double-quoted string is preceded by an unquoted .Ql $ , -the latter is ignored. +the +.Ql $ +is simply ignored. .Ss Backslash expansion In places where backslashes are expanded, certain C and .At @@ -1120,18 +1130,17 @@ .Pp The following command aliases are defined automatically by the shell: .Bd -literal -offset indent -autoload=\*(aqtypeset \-fu\*(aq -functions=\*(aqtypeset \-f\*(aq -hash=\*(aqalias \-t\*(aq -history=\*(aqfc \-l\*(aq -integer=\*(aqtypeset \-i\*(aq -local=\*(aqtypeset\*(aq -login=\*(aqexec login\*(aq -nameref=\*(aqtypeset \-n\*(aq +autoload=\*(aq\etypeset \-fu\*(aq +functions=\*(aq\etypeset \-f\*(aq +hash=\*(aq\ebuiltin alias \-t\*(aq +history=\*(aq\ebuiltin fc \-l\*(aq +integer=\*(aq\etypeset \-i\*(aq +local=\*(aq\etypeset\*(aq +login=\*(aq\eexec login\*(aq +nameref=\*(aq\etypeset \-n\*(aq nohup=\*(aqnohup \*(aq -r=\*(aqfc \-e \-\*(aq -stop=\*(aqkill \-STOP\*(aq -type=\*(aqwhence \-v\*(aq +r=\*(aq\ebuiltin fc \-e \-\*(aq +type=\*(aq\ebuiltin whence \-v\*(aq .Ed .Pp Tracked aliases allow the shell to remember where it found a particular @@ -1142,16 +1151,16 @@ time the command is executed, the shell checks the saved path to see that it is still valid, and if so, avoids repeating the path search. Tracked aliases can be listed and created using -.Ic alias \-t . +.Ic alias Fl t . Note that changing the .Ev PATH parameter clears the saved paths for all tracked aliases. If the .Ic trackall option is set (i.e.\& -.Ic set \-o Ic trackall +.Ic set Fl o Ic trackall or -.Ic set \-h ) , +.Ic set Fl h ) , the shell tracks all commands. This option is set automatically for non-interactive shells. For interactive shells, only the following commands are @@ -1203,7 +1212,7 @@ .Ic return work, and in that .Ic exit -terminates the parent shell. +terminates the parent shell; shell options are shared. .Pp Another variant of substitution are the valsubs (value substitutions) .Pf ${\*(Ba\& Ns Ar command Ns \&;} @@ -1212,7 +1221,7 @@ the, initially empty, expression-local variable .Ev REPLY is set to within the -.Ar command Ns No s . +.Ar command Ns s . .Pp If a substitution appears outside of double quotes, the results of the substitution are generally subject to word or field splitting according to @@ -1254,10 +1263,8 @@ .Sq D . Note that if the .Ev IFS -parameter is set to the -.Dv NULL -string, no field splitting is done; if the parameter is unset, the default -value of space, tab, and newline is used. +parameter is set to the empty string, no field splitting is done; +if it is unset, the default value of space, tab, and newline is used. .Pp Also, note that the field splitting applies only to the immediate result of the substitution. @@ -1289,6 +1296,8 @@ For .Pf $( Ns Ar command Ns \&) and +.Pf ${\*(Ba\& Ns Ar command Ns \&;} +and .Pf ${\ \& Ar command Ns \&;} substitutions, normal quoting rules are used when .Ar command @@ -1301,9 +1310,11 @@ .Ql \` , or .Ql \e -is stripped (a +is stripped (as is +.Ql \&" +when the substitution is part of a double-quoted string); a backslash .Ql \e -followed by any other character is unchanged). +followed by any other character is unchanged. As a special case in command substitutions, a command of the form .Pf \*(Lt Ar file is interpreted to mean substitute the contents of @@ -1427,7 +1438,7 @@ .Ic getopts , .Ic read , and -.Ic set \-A +.Ic set Fl A commands. Lastly, parameters can be assigned values using assignment operators inside arithmetic expressions (see @@ -1539,7 +1550,9 @@ .Pp The following forms of parameter substitution can also be used (if .Ar name -is an array, its element #0 will be substituted in a scalar context): +is an array, the element with the key +.Dq 0 +will be substituted in scalar context): .Pp .Bl -tag -width Ds -compact .It Pf ${# Ns Ar name Ns \&} @@ -1626,31 +1639,40 @@ .Xc .It Xo .Pf ${ Ar name +.Pf /# Ar pattern / Ar string No } +.Xc +.It Xo +.Pf ${ Ar name +.Pf /% Ar pattern / Ar string No } +.Xc +.It Xo +.Pf ${ Ar name .Pf // Ar pattern / Ar string No } .Xc .Sm on -Like ${..#..} substitution, but it replaces the longest match of -.Ar pattern , -anchored anywhere in the value, with -.Ar string . -If +The longest match of .Ar pattern -begins with -.Ql # , -it is anchored at the beginning of the value; if it begins with -.Ql % , -it is anchored at the end. -Patterns that are empty or consist only of wildcards are invalid. -A single -.Ql / -replaces the first occurence of the search -.Ar pattern , -and two of them replace all occurences. -If -.Pf / Ar string -is omitted, the +in the value of parameter +.Ar name +is replaced with +.Ar string +(deleted if +.Ar string +is empty; the trailing slash +.Pq Ql / +may be omitted in that case). +A leading slash followed by +.Ql # +or +.Ql % +causes the pattern to be anchored at the beginning or end of +the value, respectively; empty unanchored +.Ar pattern Ns s +cause no replacement; a single leading slash or use of a .Ar pattern -is replaced by the empty string, i.e. deleted. +that matches the empty string causes the replacement to +happen only once; two leading slashes cause all occurrences +of matches in the value to be replaced. Cannot be applied to a vector. Inefficiently implemented, may be slow. .Pp @@ -1736,7 +1758,7 @@ The exit status of the last non-asynchronous command executed. If the last command was killed by a signal, .Ic $?\& -is set to 128 plus the signal number. +is set to 128 plus the signal number, but at most 255. .It Ev 0 The name of the shell, determined as follows: the first argument to @@ -1795,21 +1817,16 @@ .It Ev BASHPID The PID of the shell or subshell. .It Ev CDPATH -Search path for the +Like +.Ev PATH , +but used to resolve the argument to the .Ic cd built-in command. -It works the same way as -.Ev PATH -for those directories not beginning with -.Ql / -in -.Ic cd -commands. Note that if .Ev CDPATH is set and does not contain .Sq \&. -or contains an empty path, the current directory is not searched. +or an empty string element, the current directory is not searched. Also, the .Ic cd built-in command will display the resulting directory when a match is found @@ -1823,9 +1840,9 @@ .Ev LINES . This parameter is used by the interactive line editing modes, and by the .Ic select , -.Ic set \-o , +.Ic set Fl o , and -.Ic kill \-l +.Ic kill Fl l commands to format information columns. Importing from the environment or unsetting this parameter removes the binding to the actual terminal size in favour of the provided value. @@ -1862,7 +1879,9 @@ below for more information. .It Ev HISTFILE The name of the file used to store command history. -When assigned to, history is loaded from the specified file. +When assigned to or unset, the file is opened, history is truncated +then loaded from the file; subsequent new commands (possibly consisting +of several lines) are appended once they successfully compiled. Also, several invocations of the shell will share history if their .Ev HISTFILE parameters all point to the same file. @@ -1870,7 +1889,7 @@ .Sy Note : If .Ev HISTFILE -isn't set, no history file is used. +is unset or empty, no history file is used. This is different from .At .Nm ksh . @@ -1927,7 +1946,7 @@ formatted as decimal .Va tv_sec followed by a dot -.Pq Sq .\& +.Pq Sq \&. and .Va tv_usec padded to exactly six decimal digits. @@ -1948,8 +1967,8 @@ .Ic getopts to process arguments from the beginning the next time it is invoked. .It Ev PATH -A colon separated list of directories that are searched when looking for -commands and files sourced using the +A colon (semicolon on OS/2) separated list of directories that are +searched when looking for commands and files sourced using the .Sq \&. command (see below). An empty string resulting from a leading or trailing @@ -2015,7 +2034,7 @@ sequences (such as escape codes) by prefixing your prompt with a character (such as Ctrl-A) followed by a carriage return and then delimiting the escape codes with this character. -Any occurences of that character in the prompt are not printed. +Any occurrences of that character in the prompt are not printed. By the way, don't blame me for this hack; it's derived from the original .Xr ksh88 1 , @@ -2145,10 +2164,10 @@ .Pp The home directory of previously expanded login names are cached and re-used. The -.Ic alias \-d +.Ic alias Fl d command may be used to list, change, and add to this cache (e.g.\& .Ic alias \-d fac=/usr/local/facilities; cd \*(TIfac/bin ) . -.Ss Brace expansion (alteration) +.Ss Brace expansion (alternation) Brace expressions take the following form: .Bd -unfilled -offset indent .Sm off @@ -2338,7 +2357,7 @@ .Pa /dev/null , and commands for which any of the following redirections have been specified: .Bl -tag -width XXxxmarker -.It \*(Gt Ar file +.It \*(Gt Ns Ar file Standard output is redirected to .Ar file . If @@ -2354,13 +2373,13 @@ .Ar cmd gets a chance to actually read .Ar foo . -.It \*(Gt\*(Ba Ar file +.It \*(Gt\*(Ba Ns Ar file Same as .Ic \*(Gt , except the file is truncated, even if the .Ic noclobber option is set. -.It \*(Gt\*(Gt Ar file +.It \*(Gt\*(Gt Ns Ar file Same as .Ic \*(Gt , except if @@ -2369,15 +2388,15 @@ Also, the file is opened in append mode, so writes always go to the end of the file (see .Xr open 2 ) . -.It \*(Lt Ar file +.It \*(Lt Ns Ar file Standard input is redirected from .Ar file , which is opened for reading. -.It \*(Lt\*(Gt Ar file +.It \*(Lt\*(Gt Ns Ar file Same as .Ic \*(Lt , except the file is opened for reading and writing. -.It \*(Lt\*(Lt Ar marker +.It \*(Lt\*(Lt Ns Ar marker After reading the command line containing this kind of redirection (called a .Dq here document ) , the shell copies lines from the command source into a temporary file until a @@ -2417,11 +2436,11 @@ .Sq \&"" quotes with nothing in between, the here document ends at the next empty line and substitution will not be performed. -.It \*(Lt\*(Lt\- Ar marker +.It \*(Lt\*(Lt\- Ns Ar marker Same as .Ic \*(Lt\*(Lt , except leading tabs are stripped from lines in the here document. -.It \*(Lt\*(Lt\*(Lt Ar word +.It \*(Lt\*(Lt\*(Lt Ns Ar word Same as .Ic \*(Lt\*(Lt , except that @@ -2429,7 +2448,7 @@ .Em is the here document. This is called a here string. -.It \*(Lt& Ar fd +.It \*(Lt& Ns Ar fd Standard input is duplicated from file descriptor .Ar fd . .Ar fd @@ -2443,42 +2462,35 @@ Note that .Ar fd is limited to a single digit in most shell implementations. -.It \*(Gt& Ar fd +.It \*(Gt& Ns Ar fd Same as .Ic \*(Lt& , except the operation is done on standard output. -.It &\*(Gt Ar file +.It &\*(Gt Ns Ar file Same as -.Ic \*(Gt Ar file 2\*(Gt&1 . -This is a GNU +.Ic \*(Gt Ns Ar file 2\*(Gt&1 . +This is a deprecated (legacy) GNU .Nm bash extension supported by .Nm which also supports the preceding explicit fd number, for example, -.Ic 3&\*(Gt Ar file +.Ic 3&\*(Gt Ns Ar file is the same as -.Ic 3\*(Gt Ar file 2\*(Gt&3 +.Ic 3\*(Gt Ns Ar file 2\*(Gt&3 in .Nm but a syntax error in GNU .Nm bash . -Setting the -.Fl o Ar posix -or -.Fl o Ar sh -shell options disable parsing of this redirection; -it's a compatibility feature to legacy scripts, to -not be used when writing new shell code. -.It Xo -.No &\*(Gt\*(Ba Ar file , -.No &\*(Gt\*(Gt Ar file , -.No &\*(Gt& Ar fd +.It Xo +.No &\*(Gt\*(Ba Ns Ar file , +.No &\*(Gt\*(Gt Ns Ar file , +.No &\*(Gt& Ns Ar fd .Xc Same as -.Ic \*(Gt\*(Ba Ar file , -.Ic \*(Gt\*(Gt Ar file , +.Ic \*(Gt\*(Ba Ns Ar file , +.Ic \*(Gt\*(Gt Ns Ar file , or -.Ic \*(Gt& Ar fd , +.Ic \*(Gt& Ns Ar fd , followed by .Ic 2\*(Gt&1 , as above. @@ -2518,12 +2530,7 @@ .Pp .D1 $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t .Pp -File descriptors created by input/output redirections are private to the -Korn shell, but passed to sub-processes if -.Fl o Ic posix -or -.Fl o Ic sh -is set. +File descriptors created by I/O redirections are private to the shell. .Ss Arithmetic expressions Integer arithmetic expressions can be used with the .Ic let @@ -2599,12 +2606,8 @@ built-in command. Prefixing numbers with a sole digit zero .Pq Sq 0 -leads to the shell interpreting it as base-8 (octal) integer in -.Ic posix -mode -.Em only ; -historically, (pd)ksh has never done so either anyway, -and it's unsafe to do that, but POSIX demands it nowadays. +does not cause interpretation as octal, as that's unsafe to do. +.Pp As a special .Nm mksh extension, numbers to the base of one are treated as either (8-bit @@ -2628,7 +2631,9 @@ behaviour is undefined (usually, the shell aborts with a parse error, but rarely, it succeeds, e.g. on the sequence C2 20). That's why you should always use ASCII mode unless you know that the -input is well-formed UTF-8 in the range of 0000..FFFD. +input is well-formed UTF-8 in the range of 0000..FFFD if you use this +feature, as opposed to +.Ic read Fl a . .Pp The operators are evaluated as follows: .Bl -tag -width Ds -offset indent @@ -2748,9 +2753,9 @@ A co-process (which is a pipeline created with the .Sq \*(Ba& operator) is an asynchronous process that the shell can both write to (using -.Ic print \-p ) +.Ic print Fl p ) and read from (using -.Ic read \-p ) . +.Ic read Fl p ) . The input and output of the co-process can also be manipulated using .Ic \*(Gt&p and @@ -2788,13 +2793,13 @@ portion of the co-process output when the most recently started co-process (instead of when all sharing co-processes) exits. .It -.Ic print \-p +.Ic print Fl p will ignore .Dv SIGPIPE signals during writes if the signal is not being trapped or ignored; the same is true if the co-process input has been duplicated to another file descriptor and -.Ic print \-u Ns Ar n +.Ic print Fl u Ns Ar n is used. .El .Ss Functions @@ -2823,11 +2828,11 @@ A list of functions can be obtained using .Ic typeset +f and the function definitions can be listed using -.Ic typeset \-f . +.Ic typeset Fl f . The .Ic autoload command (which is an alias for -.Ic typeset \-fu ) +.Ic typeset Fl fu ) may be used to create undefined functions: when an undefined function is executed, the shell searches the path specified in the .Ev FPATH @@ -2848,9 +2853,9 @@ and .Dq export , which can be set with -.Ic typeset \-ft +.Ic typeset Fl ft and -.Ic typeset \-fx , +.Ic typeset Fl fx , respectively. When a traced function is executed, the shell's .Ic xtrace @@ -2909,9 +2914,9 @@ .Ic getopts outside the function). .It -Bourne-style function definitions take precedence over alias dereferences -and remove alias definitions upon encounter, while aliases take precedence -over Korn-style functions. +Shell options +.Pq Ic set Fl o +have local scope, i.e. changes inside a function are reset upon its exit. .El .Pp In the future, the following differences may also be added: @@ -2925,10 +2930,6 @@ .It The EXIT trap, if set in a function, will be executed after the function returns. -.It -Shell options -.Pq Ic set Fl o -have local scope, i.e. changes inside a function are reset upon its exit. .El .Ss Command execution After evaluation of command-line arguments, redirections, and parameter @@ -2962,23 +2963,24 @@ .Nm commands keeping assignments: .Pp -.Ic builtin , global , typeset , wait +.Ic builtin , global , source , typeset , +.Ic wait .Pp Builtins that are not special: .Pp .Ic [ , alias , bg , bind , .Ic cat , cd , command , echo , .Ic false , fc , fg , getopts , -.Ic jobs , kill , let , mknod , -.Ic print , pwd , read , realpath , -.Ic rename , sleep , suspend , test , -.Ic true , ulimit , umask , unalias , -.Ic whence +.Ic jobs , kill , let , print , +.Ic pwd , read , realpath , rename , +.Ic sleep , suspend , 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. .Pp -The following describes the special and regular built-in commands: +The following describes the special and regular built-in commands and +builtin-like reserved words: .Pp .Bl -tag -width false -compact .It Ic \&. Ar file Op Ar arg ... @@ -3167,15 +3169,13 @@ is a single dash .Pq Sq - or absent, read from standard input. -Unless compiled with -.Dv MKSH_NO_EXTERNAL_CAT , -if any options are given, an external -.Xr cat 1 -utility is invoked instead if called from the shell. For direct builtin calls, the .Tn POSIX .Fl u option is supported as a no-op. +For calls from shell, if any options are given, an external +.Xr cat 1 +utility is preferred over the builtin. .Pp .It Xo .Ic cd @@ -3303,9 +3303,9 @@ .Ar cmd , information about what would be executed is given (and the same is done for .Ar arg ... ) . -For special and regular built-in commands and functions, their names are simply -printed; for aliases, a command that defines them is printed; and for commands -found by searching the +For builtins, functions and keywords, their names are simply printed; +for aliases, a command that defines them is printed; +for utilities found by searching the .Ev PATH parameter, the full path of the command is printed. If no command is found @@ -3657,7 +3657,7 @@ Since expressions may need to be quoted, .No \&(( Ar expr No )) is syntactic sugar for -.No let \&" Ns Ar expr Ns \&" . +.No "{ let '" Ns Ar expr Ns "'; }" . .Pp .It Ic let] Internally used alias for @@ -3695,10 +3695,9 @@ and .Ar minor (minor device number). -.Pp -See -.Xr mknod 8 -for further information. +This is not normally part of +.Nm mksh ; +however, distributors may have added this as builtin as a speed hack. .Pp .It Xo .Ic print @@ -3759,8 +3758,9 @@ .Xr printf 1 , utility, except it uses the same .Sx Backslash expansion -and I/O code and does hot handle floating point as the rest of +and I/O code and does not handle floating point as the rest of .Nm mksh . +An external utility is preferred over the builtin. This is not normally part of .Nm mksh ; however, distributors may have added this as builtin as a speed hack. @@ -3830,7 +3830,8 @@ .Ev REPLY ) as array of characters (wide characters if the .Ic utf8\-mode -option is enacted, octets otherwise). +option is enacted, octets otherwise); the codepoints are +encoded as decimal numbers by default. .It Fl d Ar x Use the first byte of .Ar x , @@ -3839,7 +3840,8 @@ .It Fl N Ar z Instead of reading till end-of-line, read exactly .Ar z -bytes; less if EOF or a timeout occurs. +bytes. +If EOF or a timeout occurs, a partial read is returned with exit status 1. .It Fl n Ar z Instead of reading till end-of-line, read up to .Ar z @@ -3858,6 +3860,9 @@ Interrupt reading after .Ar n seconds (specified as positive decimal value with an optional fractional part). +The exit status of +.Nm read +is 1 if the timeout occurred, but partial reads may still be returned. .It Fl r Normally, the ASCII backslash character escapes the special meaning of the following character and is stripped from the input; @@ -3904,8 +3909,8 @@ .Fl r option might be prudent; the same applies for: .Bd -literal -offset indent -find . \-type f \-print0 \*(Ba \e - while IFS= read \-d \*(aq\*(aq \-r filename; do +find . \-type f \-print0 \*(Ba& \e + while IFS= read \-d \*(aq\*(aq \-pr filename; do print \-r \-\- "found \*(Lt${filename#./}\*(Gt" done .Ed @@ -3977,14 +3982,14 @@ .Pp .It Ic return Op Ar status Returns from a function or -.Ic .\& +.Ic \&. script, with exit status .Ar status . If no .Ar status is given, the exit status of the last executed command is used. If used outside of a function or -.Ic .\& +.Ic \&. script, it has the same effect as .Ic exit . Note that @@ -3992,9 +3997,9 @@ treats both profile and .Ev ENV files as -.Ic .\& +.Ic \&. scripts, while the original Korn shell only treats profiles as -.Ic .\& +.Ic \&. scripts. .Pp .It Xo @@ -4045,23 +4050,6 @@ .Nm ksh93 is: .Ic foo=(a b c); foo+=(d e) -.Pp -Another -.At -.Nm ksh93 -and -.Tn GNU -.Nm bash -extension allows specifying the indices used for -.Ar arg ... -.Pq from the above example, Ic a b c -like this: -.Ic set \-A foo \-\- [0]=a [1]=b [2]=c -or -.Ic foo=([0]=a [1]=b [2]=c) -which can also be written -.Ic foo=([0]=a b c) -because indices are incremented automatically. .It Fl a \*(Ba Fl o Ic allexport All new parameters are created with the export attribute. .It Fl b \*(Ba Fl o Ic notify @@ -4089,7 +4077,7 @@ .Ic until , .Ic while , or -.Ic !\& +.Ic \&! statements. For .Ic && @@ -4166,6 +4154,12 @@ case-insensitively; for direct builtin calls depending on the aforementioned environment variables; or for stdin or scripts, if the input begins with a UTF-8 Byte Order Mark. +.Pp +In near future, locale tracking will be implemented, which means that +.Ic set Fl +U +is changed whenever one of the +.Tn POSIX +locale-related environment variables changes. .It Fl u \*(Ba Fl o Ic nounset Referencing of an unset parameter, other than .Dq $@ @@ -4258,19 +4252,25 @@ Make the exit status of a pipeline (before logically complementing) the rightmost non-zero errorlevel, or zero if all commands exited with zero. .It Fl o Ic posix -Enable a somewhat more -.Px -ish mode. +Behave closer to the standards +(see +.Sx POSIX mode +for details). +Automatically enabled if the basename of the shell invocation begins with +.Dq sh +and this autodetection feature is compiled in +.Pq not in MirBSD . As a side effect, setting this flag turns off .Ic braceexpand mode, which can be turned back on manually, and .Ic sh -mode. +mode (unless both are enabled at the same time). .It Fl o Ic sh Enable .Pa /bin/sh .Pq kludge -mode. +mode (see +.Sx SH mode ) . Automatically enabled if the basename of the shell invocation begins with .Dq sh and this autodetection feature is compiled in @@ -4279,7 +4279,7 @@ .Ic braceexpand mode, which can be turned back on manually, and .Ic posix -mode. +mode (unless both are enabled at the same time). .It Fl o Ic vi Enable .Xr vi 1 Ns -like @@ -4313,6 +4313,11 @@ with no option name will list all the options and whether each is on or off; .Ic set +o will print the long names of all options that are currently on. +In a future version, +.Ic set +o +will behave +.Tn POSIX +compliant and print commands to restore the current options instead. .Pp Remaining arguments, if any, are positional parameters and are assigned, in order, to the positional parameters (i.e. $1, $2, etc.). @@ -4349,16 +4354,9 @@ Like .Ic \&. Po Do dot Dc Pc , except that the current working directory is appended to the -.Ev PATH -in GNU +search path (GNU .Nm bash -and -.Nm mksh . -In -.Nm ksh93 -and -.Nm mksh , -this is implemented as a shell alias instead of a builtin. +extension). .Pp .It Ic suspend Stops the shell as if it had received the suspend character from @@ -4657,54 +4655,60 @@ 0m0.00s 0m0.00s .Ed .Pp +.It Ic trap Ar n Op Ar signal ... +If the first operand is a decimal unsigned integer, this resets all +specified signals to the default action, i.e. is the same as calling +.Ic trap +with a minus sign +.Pq Sq \- +as +.Ar handler , +followed by the arguments +.Pq Ar n Op Ar signal ... , +all of which are treated as signals. +.Pp .It Ic trap Op Ar handler signal ... -Sets a trap handler that is to be executed when any of the specified signals are -received. +Sets a trap handler that is to be executed when any of the specified +.Ar signal Ns s +are received. .Ar handler -is either a -.Dv NULL -string, indicating the signals are to be ignored, a minus sign +is either an empty string, indicating the signals are to be ignored, +a minus sign .Pq Sq \- , -indicating that the default action is to be taken for the signals (see -.Xr signal 3 ) , -or a string containing shell commands to be evaluated and executed at the first -opportunity (i.e. when the current command completes, or before printing the -next +indicating that the default action is to be taken for the signals +.Pq see Xr signal 3 , +or a string containing shell commands to be executed at the first opportunity +(i.e. when the current command completes, or before printing the next .Ev PS1 prompt) after receipt of one of the signals. .Ar signal -is the name of a signal (e.g.\& -.Dv PIPE -or -.Dv ALRM ) +is the name of a signal +.Pq e.g.\& Dv PIPE or Dv ALRM or the number of the signal (see the -.Ic kill \-l +.Ic kill Fl l command above). .Pp There are two special signals: .Dv EXIT -(also known as 0) which is executed when the shell is about to exit, and +.Pq also known as 0 , +which is executed when the shell is about to exit, and .Dv ERR , -which is executed after an error occurs (an error is something that would cause -the shell to exit if the -.Fl e +which is executed after an error occurs; an error is something +that would cause the shell to exit if the +.Ic set Fl e or -.Ic errexit -option were set \*(en see the -.Ic set -command above). +.Ic set Fl o Ic errexit +option were set. .Dv EXIT handlers are executed in the environment of the last executed command. -Note -that for non-interactive shells, the trap handler cannot be changed for signals -that were ignored when the shell started. .Pp -With no arguments, -.Ic trap -lists, as a series of +Note that, for non-interactive shells, the trap handler cannot be changed +for signals that were ignored when the shell started. +.Pp +With no arguments, the current state of the traps that have been set since +the shell started is shown as a series of .Ic trap -commands, the current state of the traps that have been set since the shell -started. +commands. Note that the output of .Ic trap cannot be usefully piped to another process (an artifact of the fact that @@ -5114,7 +5118,7 @@ .Ic wait is that of the last specified job; if the last job is killed by a signal, the exit status is 128 + the number of the signal (see -.Ic kill \-l Ar exit-status +.Ic kill Fl l Ar exit-status above); if the last specified job can't be found (because it never existed, or had already finished), the exit status of .Ic wait @@ -5143,38 +5147,20 @@ .Op Fl pv .Op Ar name ... .Xc -For each -.Ar name , -the type of command is listed (reserved word, built-in, alias, -function, tracked alias, or executable). -If the -.Fl p -option is used, a path search is performed even if -.Ar name -is a reserved word, alias, etc. Without the .Fl v -option, -.Ic whence -is similar to -.Ic command Fl v -except that -.Ic whence -will find reserved words and won't print aliases as alias commands. +option, it is the same as +.Ic command Fl v , +except aliases are not printed as alias command. With the .Fl v -option, -.Ic whence -is the same as +option, it is exactly the same as .Ic command Fl V . -Note that for -.Ic whence , -the +In either case, the .Fl p -option does not affect the search path used, as it does for -.Ic command . -If the type of one or more of the names could not be determined, the exit -status is non-zero. +option differs: the search path is not affected in +.Ic whence , +but the search is restricted to the path. .El .Ss Job control Job control refers to the shell's ability to monitor and control jobs which @@ -5185,9 +5171,9 @@ .Ic jobs commands. If job control is fully enabled (using -.Ic set \-m +.Ic set Fl m or -.Ic set \-o monitor ) , +.Ic set Fl o Ic monitor ) , as it is for interactive shells, the processes of a job are placed in their own process group. Foreground jobs can be stopped by typing the suspend @@ -5282,7 +5268,7 @@ .Dv SIGTSTP ) . .It Ar signal-description Op Dq core dumped The job was killed by a signal (e.g. memory fault, hangup); use -.Ic kill \-l +.Ic kill Fl l for a list of signal descriptions. The .Dq core dumped @@ -5312,6 +5298,69 @@ is immediately made to exit the shell, the running jobs are sent a .Dv SIGHUP signal and the shell exits. +.Ss POSIX mode +Entering +.Ic set Fl o Ic posix +mode will cause +.Nm +to behave even more +.Tn POSIX +compliant in places where the defaults or opinions differ. +Note that +.Nm mksh +will still operate with unsigned 32-bit arithmetics; use +.Nm lksh +if arithmetics on the host +.Vt long +data type, complete with ISO C Undefined Behaviour, are required; +refer to the +.Xr lksh 1 +manual page for details. +Most other historic, +.At +.Nm ksh Ns -compatible , +or opinionated differences can be disabled by using this mode; these are: +.Bl -bullet +.It +The GNU +.Nm bash +I/O redirection +.Ic &\*(Gt Ns Ar file +is no longer supported. +.It +File descriptors created by I/O redirections are inherited by +child processes. +.It +Numbers with a leading digit zero are interpreted as octal. +.It +The +.Nm echo +builtin does not interpret backslashes and only supports the exact option +.Dq Fl n . +.It +\&... (list is incomplete and may change for R53) +.El +.Ss SH mode +Compatibility mode; intended for use with legacy scripts that +cannot easily be fixed; the changes are as follows: +.Bl -bullet +.It +The GNU +.Nm bash +I/O redirection +.Ic &\*(Gt Ns Ar file +is no longer supported. +.It +File descriptors created by I/O redirections are inherited by +child processes. +.It +The +.Nm echo +builtin does not interpret backslashes and only supports the exact option +.Dq Fl n . +.It +\&... (list is incomplete and may change for R53) +.El .Ss Interactive input line editing The shell supports three modes of reading command lines from a .Xr tty 4 @@ -5409,7 +5458,7 @@ Most ordinary characters are bound to this. .It Xo backward\-char: .Op Ar n -.No \*(haB , \*(haXD , ANSI-CurLeft +.No \*(haB , \*(haXD , ANSI-CurLeft , PC-CurLeft .Xc Moves the cursor backward .Ar n @@ -5426,7 +5475,7 @@ characters. .It beginning\-of\-history: \*(ha[\*(Lt Moves to the beginning of the history. -.It beginning\-of\-line: \*(haA, ANSI-Home +.It beginning\-of\-line: \*(haA, ANSI-Home, PC-Home Moves the cursor to the beginning of the edited input line. .It Xo capitalise\-word: .Op Ar n @@ -5482,7 +5531,7 @@ characters before the cursor. .It Xo delete\-char\-forward: .Op Ar n -.No ANSI-Del +.No ANSI-Del , PC-Del .Xc Deletes .Ar n @@ -5503,7 +5552,7 @@ words. .It Xo down\-history: .Op Ar n -.No \*(haN , \*(haXB , ANSI-CurDown +.No \*(haN , \*(haXB , ANSI-CurDown , PC-CurDown .Xc Scrolls the history buffer forward .Ar n @@ -5535,7 +5584,7 @@ .Ic fc \-e ${VISUAL:\-${EDITOR:\-vi}} Ar n . .It end\-of\-history: \*(ha[\*(Gt Moves to the end of the history. -.It end\-of\-line: \*(haE, ANSI-End +.It end\-of\-line: \*(haE, ANSI-End, PC-End Moves the cursor to the end of the input line. .It eot: \*(ha_ Acts as an end-of-file; this is useful because edit-mode input disables @@ -5560,7 +5609,7 @@ If no files match the pattern, the bell is rung. .It Xo forward\-char: .Op Ar n -.No \*(haF , \*(haXC , ANSI-CurRight +.No \*(haF , \*(haXC , ANSI-CurRight , PC-CurRight .Xc Moves the cursor forward .Ar n @@ -5674,12 +5723,12 @@ pattern. The history buffer retains only a finite number of lines; the oldest are discarded as necessary. -.It search\-history\-up: ANSI-PgUp +.It search\-history\-up: ANSI-PgUp, PC-PgUp Search backwards through the history buffer for commands whose beginning match the portion of the input line before the cursor. When used on an empty line, this has the same effect as .Ic up\-history . -.It search\-history\-down: ANSI-PgDn +.It search\-history\-down: ANSI-PgDn, PC-PgDn Search forwards through the history buffer for commands whose beginning match the portion of the input line before the cursor. When used on an empty line, this has the same effect as @@ -5699,7 +5748,7 @@ character to the right. .It Xo up\-history: .Op Ar n -.No \*(haP , \*(haXA , ANSI-CurUp +.No \*(haP , \*(haXA , ANSI-CurUp , PC-CurUp .Xc Scrolls the history buffer backward .Ar n @@ -5811,7 +5860,7 @@ Optional file name and command completion (see .Ic \*(haF above), enabled with -.Ic set \-o vi\-tabcomplete . +.Ic set Fl o Ic vi\-tabcomplete . .El .Pp In command mode, each character is interpreted as a command. @@ -5924,7 +5973,7 @@ is only recognised if the .Ic vi\-esccomplete option is set (see -.Ic set \-o ) . +.Ic set Fl o ) . If .Ar n is specified, the @@ -6127,7 +6176,7 @@ .Ar n Ns th occurrence of the last search string; the direction of the search is the opposite of the last search. -.It Ar ANSI-CurUp +.It Ar ANSI-CurUp , PC-PgUp Take the characters from the beginning of the line to the current cursor position as search string and do a backwards history search for lines beginning with this string; keep the cursor position. @@ -6278,6 +6327,8 @@ Undo the last edit command. .It U Undo all changes that have been made to the current line. +.It PC Home, End, Del, and cursor keys +They move as expected, both in insert and command mode. .It Ar intr No and Ar quit The interrupt and quit terminal characters cause the current line to be deleted and a new prompt to be printed. @@ -6312,6 +6363,7 @@ .Xr cat 1 , .Xr ed 1 , .Xr getopt 1 , +.Xr lksh 1 , .Xr sed 1 , .Xr sh 1 , .Xr stty 1 , @@ -6337,8 +6389,6 @@ .Xr utf\-8 7 , .Xr mknod 8 .Pp -.Pa http://docsrv.sco.com:507/en/man/html.C/sh.C.html -.Pp .Pa https://www.mirbsd.org/ksh\-chan.htm .Rs .%A Morris Bolsky @@ -6405,8 +6455,8 @@ .An -nosplit .Nm "The MirBSD Korn Shell" is developed by -.An Thorsten Glaser Aq tg@mirbsd.org -and currently maintained as part of The MirOS Project. +.An mirabilos Aq Mt m@mirbsd.org +as part of The MirOS Project. This shell is based on the public domain 7th edition Bourne shell clone by .An Charles Forsyth , who kindly agreed to, in countries where the Public Domain status of the work @@ -6425,10 +6475,10 @@ was created by .An Eric Gisin , and it was subsequently maintained by -.An John R. MacMillan Aq Mt change!john@sq.sq.com , -.An Simon J. Gerraty Aq Mt sjg@zen.void.oz.au , +.An John R. MacMillan , +.An Simon J. Gerraty , and -.An Michael Rendell Aq Mt michael@cs.mun.ca . +.An Michael Rendell . The effort of several projects, such as Debian and OpenBSD, and other contributors including our users, to improve the shell is appreciated. See the documentation, CVS, and web site for details. @@ -6438,10 +6488,11 @@ .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). +.\" or shortcut for mksh or mksh/Win32 or OS/2; distro patches are ok +.\" (but we request they amend $KSH_VERSION when modifying mksh). +.\" Authors are Marshall Kirk McKusick (UCB), Rick Collette (ekkoBSD), +.\" mirabilos, Benny Siegert (MirBSD), Michael Langguth (mksh/Win32), +.\" KO Myung-Hun (mksh for OS/2). .\" .\" As far as MirBSD is concerned, the files themselves are free .\" to modification and distribution under BSD/MirOS Licence, the @@ -6450,9 +6501,6 @@ .\" .Sh CAVEATS .Nm -only supports the Unicode BMP (Basic Multilingual Plane). -.Pp -.Nm has a different scope model from .At .Nm ksh , @@ -6473,23 +6521,47 @@ .Nm mksh provides a consistent set of 32-bit integer arithmetics, both signed and unsigned, with defined wraparound and sign of the result of a -remainder operation, even (defying POSIX) on 64-bit systems. -If you require 64-bit integer arithmetics, use -.Nm lksh Pq legacy mksh -instead, but be aware that, in POSIX, it's legal for the OS to make -.Li print $((2147483647 + 1)) -delete all files on your system, as it's Undefined Behaviour. +remainder operation, even (defying POSIX) on 36-bit and 64-bit systems. .Pp .Nm mksh provides a consistent, clear interface normally. -This may deviate from POSIX in optional or opinionated places, such -as whether leading-digit-zero numbers should be interpreted as octal. -.Ic set \-o posix -will cause the shell (either +This may deviate from POSIX in historic or opinionated places. +.Ic set Fl o Ic posix +(see +.Sx POSIX mode +for details) +will cause the shell to behave more conformant. +.Pp +For the purpose of +.Tn POSIX , .Nm mksh -or -.Nm lksh ) -to behave more like the standard expects. +supports only the +.Dq C +locale. +.Nm mksh Ns 's +.Ic utf8\-mode +only supports the Unicode BMP (Basic Multilingual Plane) and maps +raw octets into the U+EF80..U+EFFF wide character range; compare +.Sx Arithmetic expressions . +The following +.Tn POSIX +.Nm sh +code toggles the +.Ic utf8\-mode +option dependent on the current +.Tn POSIX +locale for mksh to allow using the UTF-8 mode, within the constraints +outlined above, in code portable across various shell implementations: +.Bd -literal -offset indent +case ${KSH_VERSION:\-} in +*MIRBSD\ KSH*\*(Ba*LEGACY\ KSH*) + case ${LC_ALL:\-${LC_CTYPE:\-${LANG:\-}}} in + *[Uu][Tt][Ff]8*\*(Ba*[Uu][Tt][Ff]\-8*) set \-U ;; + *) set +U ;; + esac ;; +esac +.Ed +In near future, (Unicode) locale tracking will be implemented though. .Sh BUGS Suspending (using \*(haZ) pipelines like the one below will only suspend the currently running part of the pipeline; in this example, @@ -6500,17 +6572,30 @@ $ /bin/sleep 666 && echo fubar .Ed .Pp +The truncation process involved when changing +.Ev HISTFILE +does not free old history entries (leaks memory) and leaks +old entries into the new history if their line numbers are +not overwritten by same-numer entries from the persistent +history file; truncating the on-disc file to +.Ev HISTSIZE +lines has always been broken and prone to history file corruption +when multiple shells are accessing the file; the rollover process +for the in-memory portion of the history is slow, should use +.Xr memmove 3 . +.Pp This document attempts to describe -.Nm mksh\ R50e +.Nm mksh\ R52c and up, +.\" with vendor patches from insert-your-name-here, compiled without any options impacting functionality, such as .Dv MKSH_SMALL , when not called as .Pa /bin/sh which, on some systems only, enables -.Ic set \-o posix +.Ic set Fl o Ic posix or -.Ic set \-o sh +.Ic set Fl o Ic sh automatically (whose behaviour differs across targets), for an operating environment supporting all of its advanced needs. .Pp @@ -6519,7 +6604,7 @@ to the .Mx mailing list at -.Aq miros\-mksh@mirbsd.org +.Aq Mt miros\-mksh@mirbsd.org or in the .Li \&#\&!/bin/mksh .Pq or Li \&#ksh diff -Nru mksh-50e/rlimits.opt mksh-52c/rlimits.opt --- mksh-50e/rlimits.opt 2015-03-01 16:48:21.000000000 +0100 +++ mksh-52c/rlimits.opt 2015-12-12 22:09:10.000000000 +0100 @@ -1,18 +1,27 @@ +/*- + * Copyright (c) 2013, 2015 + * mirabilos + * + * Provided that these terms and disclaimer and all copyright notices + * are retained or reproduced in an accompanying document, permission + * is granted to deal in this work without restriction, including un- + * limited rights to use, publicly perform, distribute, sell, modify, + * merge, give away, or sublicence. + * + * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to + * the utmost extent permitted by applicable law, neither express nor + * implied; without malicious intent or gross negligence. In no event + * may a licensor, author or contributor be held liable for indirect, + * direct, other damage, loss, or other issues arising in any way out + * of dealing in the work, even if advised of the possibility of such + * damage or existence of a defect, except proven that it results out + * of said person's immediate fault when using the work as intended. + */ + @RLIMITS_DEFNS -__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.1 2013/11/17 22:21:18 tg Exp $"); -struct limits { - /* limit resource */ - int resource; - /* multiply by to get rlim_{cur,max} values */ - unsigned int factor; - /* getopts char */ - char optchar; - /* limit name */ - char name[1]; -}; -#define FN(lname,lid,lfac,lopt) static const struct { int resource; unsigned int factor; char optchar; char name[sizeof(lname)]; } rlimits_ ## lid = { lid, lfac, lopt, lname }; +__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.3 2015/12/12 21:08:44 tg Exp $"); @RLIMITS_ITEMS -#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid), +#define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid), @@ /* generic options for the ulimit builtin */ diff -Nru mksh-50e/sh.h mksh-52c/sh.h --- mksh-50e/sh.h 2015-03-01 16:43:30.000000000 +0100 +++ mksh-52c/sh.h 2016-03-04 19:29:08.000000000 +0100 @@ -1,8 +1,8 @@ -/* $OpenBSD: sh.h,v 1.33 2013/12/18 13:53:12 millert Exp $ */ +/* $OpenBSD: sh.h,v 1.35 2015/09/10 22:48:58 nicm Exp $ */ /* $OpenBSD: shf.h,v 1.6 2005/12/11 18:53:51 deraadt Exp $ */ /* $OpenBSD: table.h,v 1.8 2012/02/19 07:52:30 otto Exp $ */ /* $OpenBSD: tree.h,v 1.10 2005/03/28 21:28:22 deraadt Exp $ */ -/* $OpenBSD: expand.h,v 1.6 2005/03/30 17:16:37 deraadt Exp $ */ +/* $OpenBSD: expand.h,v 1.7 2015/09/01 13:12:31 tedu Exp $ */ /* $OpenBSD: lex.h,v 1.13 2013/03/03 19:11:34 guenther Exp $ */ /* $OpenBSD: proto.h,v 1.35 2013/09/04 15:49:19 millert Exp $ */ /* $OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $ */ @@ -10,8 +10,8 @@ /*- * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -64,6 +64,9 @@ #include #include #include +#if HAVE_IO_H +#include +#endif #if HAVE_LIBGEN_H #include #endif @@ -105,6 +108,9 @@ #if HAVE_VALUES_H #include #endif +#ifdef MIRBSD_BOOTFLOPPY +#include +#endif #undef __attribute__ #if HAVE_ATTRIBUTE_BOUNDED @@ -169,9 +175,9 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.701.2.4 2015/03/01 15:43:05 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.768 2016/03/04 18:28:42 tg Exp $"); #endif -#define MKSH_VERSION "R50 2015/03/01" +#define MKSH_VERSION "R52 2016/03/04" /* arithmetic types: C implementation */ #if !HAVE_CAN_INTTYPES @@ -253,7 +259,8 @@ /* extra types */ -#if !HAVE_GETRUSAGE +/* getrusage does not exist on OS/2 kLIBC */ +#if !HAVE_GETRUSAGE && !defined(__OS2__) #undef rusage #undef RUSAGE_SELF #undef RUSAGE_CHILDREN @@ -298,16 +305,6 @@ } while (/* CONSTCOND */ 0) #endif -#define ksh_isdigit(c) (((c) >= '0') && ((c) <= '9')) -#define ksh_islower(c) (((c) >= 'a') && ((c) <= 'z')) -#define ksh_isupper(c) (((c) >= 'A') && ((c) <= 'Z')) -#define ksh_tolower(c) (((c) >= 'A') && ((c) <= 'Z') ? (c) - 'A' + 'a' : (c)) -#define ksh_toupper(c) (((c) >= 'a') && ((c) <= 'z') ? (c) - 'a' + 'A' : (c)) -#define ksh_isdash(s) (((s)[0] == '-') && ((s)[1] == '\0')) -#define ksh_isspace(c) ((((c) >= 0x09) && ((c) <= 0x0D)) || ((c) == 0x20)) -#define ksh_min(x,y) ((x) < (y) ? (x) : (y)) -#define ksh_max(x,y) ((x) > (y) ? (x) : (y)) - #ifdef MKSH__NO_PATH_MAX #undef PATH_MAX #else @@ -339,29 +336,71 @@ #define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) #endif -#ifndef NSIG -#if defined(_NSIG) -#define NSIG _NSIG + +/* determine ksh_NSIG: first, use the traditional definitions */ +#undef ksh_NSIG +#if defined(NSIG) +#define ksh_NSIG (NSIG) +#elif defined(_NSIG) +#define ksh_NSIG (_NSIG) #elif defined(SIGMAX) -#define NSIG (SIGMAX+1) +#define ksh_NSIG (SIGMAX + 1) #elif defined(_SIGMAX) -#define NSIG (_SIGMAX+1) +#define ksh_NSIG (_SIGMAX + 1) +#elif defined(NSIG_MAX) +#define ksh_NSIG (NSIG_MAX) #else # error Please have your platform define NSIG. -#define NSIG 64 -#endif #endif - -/* get rid of this (and awk/printf(1) in Build.sh) later */ -#if (NSIG < 1) +/* range-check them */ +#if (ksh_NSIG < 1) # error Your NSIG value is not positive. -#unset NSIG -#define NSIG 64 +#undef ksh_NSIG +#endif +/* second, see if the new POSIX definition is available */ +#ifdef NSIG_MAX +#if (NSIG_MAX < 2) +/* and usable */ +# error Your NSIG_MAX value is too small. +#undef NSIG_MAX +#elif (ksh_NSIG > NSIG_MAX) +/* and realistic */ +# error Your NSIG value is larger than your NSIG_MAX value. +#undef NSIG_MAX +#else +/* since it’s usable, prefer it */ +#undef ksh_NSIG +#define ksh_NSIG (NSIG_MAX) +#endif +/* if NSIG_MAX is now still defined, use sysconf(_SC_NSIG) at runtime */ +#endif +/* third, for cpp without the error directive, default */ +#ifndef ksh_NSIG +#define ksh_NSIG 64 #endif +#define ksh_sigmask(sig) (((sig) < 1 || (sig) > 127) ? 255 : 128 + (sig)) + /* OS-dependent additions (functions, variables, by OS) */ +#ifdef MKSH_EXE_EXT +#undef MKSH_EXE_EXT +#define MKSH_EXE_EXT ".exe" +#else +#define MKSH_EXE_EXT "" +#endif + +#ifdef __OS2__ +#define MKSH_PATHSEPS ";" +#define MKSH_PATHSEPC ';' +#define MKSH_UNIXROOT "/@unixroot" +#else +#define MKSH_PATHSEPS ":" +#define MKSH_PATHSEPC ':' +#define MKSH_UNIXROOT "" +#endif + #if !HAVE_FLOCK_DECL extern int flock(int, int); #endif @@ -476,6 +515,8 @@ EXTERN const char digits_uc[] E_INIT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); EXTERN const char digits_lc[] E_INIT("0123456789abcdefghijklmnopqrstuvwxyz"); +#define letters_uc (digits_uc + 10) +#define letters_lc (digits_lc + 10) /* * Evil hack for const correctness due to API brokenness @@ -537,7 +578,7 @@ #define mkssert(e) do { } while (/* CONSTCOND */ 0) #endif -#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 505) +#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 523) #error Must run Build.sh to compile this. extern void thiswillneverbedefinedIhope(void); int @@ -658,16 +699,29 @@ /* 1. internal structure */ -struct lalloc { - struct lalloc *next; +struct lalloc_common { + struct lalloc_common *next; +}; + +#ifdef MKSH_ALLOC_CATCH_UNDERRUNS +struct lalloc_item { + struct lalloc_common *next; + size_t len; + char dummy[8192 - sizeof(struct lalloc_common *) - sizeof(size_t)]; }; +#endif /* 2. sizes */ -#define ALLOC_ITEM struct lalloc -#define ALLOC_SIZE (sizeof(ALLOC_ITEM)) +#ifdef MKSH_ALLOC_CATCH_UNDERRUNS +#define ALLOC_ITEM struct lalloc_item +#define ALLOC_OVERHEAD 0 +#else +#define ALLOC_ITEM struct lalloc_common +#define ALLOC_OVERHEAD (sizeof(ALLOC_ITEM)) +#endif -/* 3. group structure (only the same for lalloc.c) */ -typedef struct lalloc Area; +/* 3. group structure */ +typedef struct lalloc_common Area; EXTERN Area aperm; /* permanent object space */ @@ -790,30 +844,35 @@ #define OF_FIRSTTIME 0x10 /* as early as possible, once */ #define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL) +/* trust GCC to have string pooling; -Wformat bitches otherwise */ +/*XXX TODO: make this with a .gen file plus not imake-style */ + /* null value for variable; comparison pointer for unset */ EXTERN char null[] E_INIT(""); /* helpers for string pooling */ -EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented"); -EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes"); #if defined(__GNUC__) -/* trust this to have string pooling; -Wformat bitches otherwise */ #define Tsynerr "syntax error" +#define Tintovfl "integer overflow %zu %c %zu prevented" +#define Toomem "can't allocate %zu data bytes" #else +EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented"); +EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes"); EXTERN const char Tsynerr[] E_INIT("syntax error"); #endif EXTERN const char Tselect[] E_INIT("select"); -EXTERN const char Tr_fc_e_dash[] E_INIT("r=fc -e -"); -#define Tfc_e_dash (Tr_fc_e_dash + 2) /* "fc -e -" */ -#define Zfc_e_dash 7 /* strlen(Tfc_e_dash) */ -EXTERN const char Tlocal_typeset[] E_INIT("local=typeset"); -#define T_typeset (Tlocal_typeset + 5) /* "=typeset" */ -#define Ttypeset (Tlocal_typeset + 6) /* "typeset" */ +EXTERN const char T_typeset[] E_INIT("=typeset"); +#define Ttypeset (T_typeset + 1) /* "typeset" */ EXTERN const char Talias[] E_INIT("alias"); EXTERN const char Tunalias[] E_INIT("unalias"); +EXTERN const char Tcat[] E_INIT("cat"); +#ifdef __OS2__ +EXTERN const char Textproc[] E_INIT("extproc"); +#endif +#ifdef MKSH_PRINTF_BUILTIN +EXTERN const char Tprintf[] E_INIT("printf"); +#endif EXTERN const char Tsgset[] E_INIT("*=set"); #define Tset (Tsgset + 2) /* "set" */ -EXTERN const char Tsgunset[] E_INIT("*=unset"); -#define Tunset (Tsgunset + 2) /* "unset" */ EXTERN const char Tsgexport[] E_INIT("*=export"); #define Texport (Tsgexport + 2) /* "export" */ EXTERN const char Tsgreadonly[] E_INIT("*=readonly"); @@ -822,6 +881,14 @@ #define Tbuiltin (Tgbuiltin + 1) /* "builtin" */ EXTERN const char T_function[] E_INIT(" function"); #define Tfunction (T_function + 1) /* "function" */ +EXTERN const char T_funny_command[] E_INIT("funny $() command"); +#define Tcommand (T_funny_command + 10) /* "command" */ +#if defined(__GNUC__) +#define Tfg_badsubst "fileglob: bad substitution" +#else +EXTERN const char Tfg_badsubst[] E_INIT("fileglob: bad substitution"); +#endif +#define Tbadsubst (Tfg_badsubst + 10) /* "bad substitution" */ EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n"); #define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */ @@ -892,13 +959,13 @@ #define SS_USER BIT(4) /* user is doing the set (ie, trap command) */ #define SS_SHTRAP BIT(5) /* trap for internal use (ALRM, CHLD, WINCH) */ -#define ksh_SIGEXIT 0 /* for trap EXIT */ -#define ksh_SIGERR NSIG /* for trap ERR */ +#define ksh_SIGEXIT 0 /* for trap EXIT */ +#define ksh_SIGERR ksh_NSIG /* for trap ERR */ EXTERN volatile sig_atomic_t trap; /* traps pending? */ EXTERN volatile sig_atomic_t intrsig; /* pending trap interrupts command */ EXTERN volatile sig_atomic_t fatal_trap; /* received a fatal signal */ -extern Trap sigtraps[NSIG+1]; +extern Trap sigtraps[ksh_NSIG + 1]; /* got_winch = 1 when we need to re-adjust the window size */ #ifdef SIGWINCH @@ -940,8 +1007,20 @@ #define ctype(c, t) tobool( ((t) == C_SUBOP2) ? \ (((c) == '#' || (c) == '%') ? 1 : 0) : \ (chtypes[(unsigned char)(c)] & (t)) ) +#define ord(c) ((int)(unsigned char)(c)) #define ksh_isalphx(c) ctype((c), C_ALPHA) #define ksh_isalnux(c) ctype((c), C_ALPHA | C_DIGIT) +#define ksh_isdigit(c) (((c) >= '0') && ((c) <= '9')) +#define ksh_islower(c) (((c) >= 'a') && ((c) <= 'z')) +#define ksh_isupper(c) (((c) >= 'A') && ((c) <= 'Z')) +#define ksh_tolower(c) (ksh_isupper(c) ? (c) - 'A' + 'a' : (c)) +#define ksh_toupper(c) (ksh_islower(c) ? (c) - 'a' + 'A' : (c)) +#define ksh_isdash(s) (((s)[0] == '-') && ((s)[1] == '\0')) +#define ksh_isspace(c) ((((c) >= 0x09) && ((c) <= 0x0D)) || ((c) == 0x20)) +#define ksh_eq(c,u,l) (((c) | 0x20) == (l)) +#define ksh_numdig(c) ((c) - ord('0')) +#define ksh_numuc(c) ((c) - ord('A')) +#define ksh_numlc(c) ((c) - ord('a')) EXTERN int ifs0 E_INIT(' '); /* for "$*" */ @@ -977,7 +1056,7 @@ /* This for co-processes */ /* something that won't (realisticly) wrap */ -typedef int32_t Coproc_id; +typedef int Coproc_id; struct coproc { void *job; /* 0 or job of co-process using input pipe */ @@ -996,14 +1075,14 @@ /* name of called builtin function (used by error functions) */ EXTERN const char *builtin_argv0; -/* is called builtin SPEC_BI? */ +/* is called builtin SPEC_BI? (also KEEPASN, odd use though) */ EXTERN bool builtin_spec; /* current working directory */ EXTERN char *current_wd; /* input line size */ -#define LINE (4096 - ALLOC_SIZE) +#define LINE (4096 - ALLOC_OVERHEAD) /* * Minimum required space to work with on a line - if the prompt leaves * less space than this on a line, the prompt is truncated. @@ -1021,12 +1100,8 @@ /* Determine the location of the system (common) profile */ #ifndef MKSH_DEFAULT_PROFILEDIR -#if defined(ANDROID) -#define MKSH_DEFAULT_PROFILEDIR "/system/etc" -#else #define MKSH_DEFAULT_PROFILEDIR "/etc" #endif -#endif #define MKSH_SYSTEM_PROFILE MKSH_DEFAULT_PROFILEDIR "/profile" #define MKSH_SUID_PROFILE MKSH_DEFAULT_PROFILEDIR "/suid_profile" @@ -1137,7 +1212,9 @@ char name[4]; }; -EXTERN struct tbl vtemp; +EXTERN struct tbl *vtemp; +/* set by global() and local() */ +EXTERN bool last_lookup_was_array; /* common flag bits */ #define ALLOC BIT(0) /* val.s has been allocated */ @@ -1204,7 +1281,7 @@ #define FC_BI (FC_SPECBI | FC_NORMBI) #define FC_PATH BIT(3) /* do path search */ #define FC_DEFPATH BIT(4) /* use default path in path search */ - +#define FC_WHENCE BIT(5) /* for use by command and whence */ #define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */ #define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */ @@ -1270,7 +1347,7 @@ EXTERN const char *def_path; /* path to use if PATH not set */ EXTERN char *tmpdir; /* TMPDIR value */ EXTERN const char *prompt; -EXTERN int cur_prompt; /* PS1 or PS2 */ +EXTERN uint8_t cur_prompt; /* PS1 or PS2 */ EXTERN int current_lineno; /* LINENO value */ /* @@ -1347,11 +1424,11 @@ * IO redirection */ struct ioword { - int unit; /* unit affected */ - int flag; /* action (below) */ - char *name; /* file name (unused if heredoc) */ - char *delim; /* delimiter for <<,<<- */ - char *heredoc; /* content of heredoc */ + char *ioname; /* filename (unused if heredoc) */ + char *delim; /* delimiter for <<, <<- */ + char *heredoc; /* content of heredoc */ + unsigned short ioflag; /* action (below) */ + short unit; /* unit (fd) affected */ }; /* ioword.flag - type of redirection */ @@ -1403,6 +1480,7 @@ #define DOTCOMEXEC BIT(11) /* not an eval flag, used by sh -c hack */ #define DOSCALAR BIT(12) /* change field handling to non-list context */ #define DOHEREDOC BIT(13) /* change scalar handling to heredoc body */ +#define DOHERESTR BIT(14) /* append a newline char */ #define X_EXTRA 20 /* this many extra bytes in X string */ @@ -1579,13 +1657,12 @@ #define ALIAS BIT(2) /* recognise alias */ #define KEYWORD BIT(3) /* recognise keywords */ #define LETEXPR BIT(4) /* get expression inside (( )) */ -#define VARASN BIT(5) /* check for var=word */ -#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */ +#define CMDASN BIT(5) /* parse x[1 & 2] as one word, for typeset */ +#define HEREDOC BIT(6) /* parsing a here document body */ #define ESACONLY BIT(7) /* only accept esac keyword */ #define CMDWORD BIT(8) /* parsing simple command (alias related) */ #define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */ #define LQCHAR BIT(10) /* source string contains QCHAR */ -#define HEREDOC BIT(11) /* parsing a here document body */ #define HERES 10 /* max number of << in line */ @@ -1605,6 +1682,13 @@ EXTERN char **histptr; /* last history item */ EXTERN mksh_ari_t histsize; /* history size */ +/* flags to histsave */ +#define HIST_FLUSH 0 +#define HIST_QUEUE 1 +#define HIST_APPEND 2 +#define HIST_STORE 3 +#define HIST_NOTE 4 + /* user and system time of last j_waitjed job */ EXTERN struct timeval j_usrtime, j_systime; @@ -1660,6 +1744,7 @@ const char *builtin(const char *, int (*)(const char **)); struct tbl *findcom(const char *, int); void flushcom(bool); +int search_access(const char *, int); const char *search_path(const char *, const char *, int, int *); void pr_menu(const char * const *); void pr_list(char * const *); @@ -1673,9 +1758,13 @@ size_t utf_mbswidth(const char *) MKSH_A_PURE; const char *utf_skipcols(const char *, int) MKSH_A_PURE; size_t utf_ptradj(const char *) MKSH_A_PURE; +#ifdef MIRBSD_BOOTFLOPPY +#define utf_wcwidth(i) wcwidth((wchar_t)(i)) +#else int utf_wcwidth(unsigned int) MKSH_A_PURE; +#endif int ksh_access(const char *, int); -struct tbl *tempvar(void); +struct tbl *tempvar(const char *); /* funcs.c */ int c_hash(const char **); int c_pwd(const char **); @@ -1731,7 +1820,7 @@ #if HAVE_PERSISTENT_HISTORY void hist_finish(void); #endif -void histsave(int *, const char *, bool, bool); +void histsave(int *, const char *, int, bool); #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY bool histsync(void); #endif @@ -1748,7 +1837,7 @@ char **hist_get_newest(bool); void inittraps(void); void alarm_init(void); -Trap *gettrap(const char *, bool); +Trap *gettrap(const char *, bool, bool); void trapsig(int); void intrcheck(void); int fatal_trap_check(void); @@ -1874,9 +1963,10 @@ void print_value_quoted(struct shf *, const char *); char *quote_value(const char *); void print_columns(struct shf *, unsigned int, - char *(*)(char *, size_t, unsigned int, const void *), + void (*)(char *, size_t, unsigned int, const void *), const void *, size_t, size_t, bool); -void strip_nuls(char *, int); +void strip_nuls(char *, size_t) + MKSH_A_BOUNDED(__string__, 1, 2); ssize_t blocking_read(int, char *, size_t) MKSH_A_BOUNDED(__buffer__, 2, 3); int reset_nonblock(int); @@ -1923,6 +2013,7 @@ ssize_t shf_vfprintf(struct shf *, const char *, va_list) MKSH_A_FORMAT(__printf__, 2, 0); /* syn.c */ +int assign_command(const char *, bool) MKSH_A_PURE; void initkeywords(void); struct op *compile(Source *, bool); bool parse_usec(const char *, struct timeval *); @@ -1935,8 +2026,6 @@ char *wdcopy(const char *, Area *); const char *wdscan(const char *, int); #define WDS_TPUTS BIT(0) /* tputS (dumpwdvar) mode */ -#define WDS_KEEPQ BIT(1) /* keep quote characters */ -#define WDS_MAGIC BIT(2) /* make MAGIC */ char *wdstrip(const char *, int); void tfree(struct op *, Area *); void dumpchar(struct shf *, int); @@ -2037,6 +2126,33 @@ extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */ +#ifdef __OS2__ +#ifndef __GNUC__ +# error oops? +#endif +#define binopen2(path,flags) __extension__({ \ + int binopen2_fd = open((path), (flags) | O_BINARY); \ + if (binopen2_fd >= 0) \ + setmode(binopen2_fd, O_BINARY); \ + (binopen2_fd); \ +}) +#define binopen3(path,flags,mode) __extension__({ \ + int binopen3_fd = open((path), (flags) | O_BINARY, (mode)); \ + if (binopen3_fd >= 0) \ + setmode(binopen3_fd, O_BINARY); \ + (binopen3_fd); \ +}) +#define mksh_abspath(s) __extension__({ \ + const char *mksh_abspath_s = (s); \ + (mksh_abspath_s[0] == '/' || (ksh_isalphx(mksh_abspath_s[0]) && \ + mksh_abspath_s[1] == ':')); \ +}) +#else +#define binopen2(path,flags) open((path), (flags) | O_BINARY) +#define binopen3(path,flags,mode) open((path), (flags) | O_BINARY, (mode)) +#define mksh_abspath(s) ((s)[0] == '/') +#endif + /* be sure not to interfere with anyone else's idea about EXTERN */ #ifdef EXTERN_DEFINED # undef EXTERN_DEFINED diff -Nru mksh-50e/sh_flags.opt mksh-52c/sh_flags.opt --- mksh-50e/sh_flags.opt 2015-03-01 16:48:23.000000000 +0100 +++ mksh-52c/sh_flags.opt 2015-12-12 22:09:10.000000000 +0100 @@ -1,11 +1,30 @@ +/*- + * Copyright (c) 2013, 2014, 2015 + * mirabilos + * + * Provided that these terms and disclaimer and all copyright notices + * are retained or reproduced in an accompanying document, permission + * is granted to deal in this work without restriction, including un- + * limited rights to use, publicly perform, distribute, sell, modify, + * merge, give away, or sublicence. + * + * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to + * the utmost extent permitted by applicable law, neither express nor + * implied; without malicious intent or gross negligence. In no event + * may a licensor, author or contributor be held liable for indirect, + * direct, other damage, loss, or other issues arising in any way out + * of dealing in the work, even if advised of the possibility of such + * damage or existence of a defect, except proven that it results out + * of said person's immediate fault when using the work as intended. + */ + @SHFLAGS_DEFNS -__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.2 2014/06/09 12:28:19 tg Exp $"); -#define FN(sname,cname,flags,ochar) static const struct { /* character flag (if any) */ char c; /* OF_* */ unsigned char optflags; /* long name of option */ char name[sizeof(sname)]; } shoptione_ ## cname = { ochar, flags, sname }; +__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.4 2015/12/12 21:08:44 tg Exp $"); @SHFLAGS_ENUMS #define FN(sname,cname,flags,ochar) cname, #define F0(sname,cname,flags,ochar) cname = 0, @SHFLAGS_ITEMS -#define FN(sname,cname,flags,ochar) ((const char *)(&shoptione_ ## cname)) + 2, +#define FN(sname,cname,flags,ochar) ((const char *)(&shoptione_ ## cname)) + 2, @@ /* special cases */ diff -Nru mksh-50e/shf.c mksh-52c/shf.c --- mksh-50e/shf.c 2015-03-01 16:43:32.000000000 +0100 +++ mksh-52c/shf.c 2016-03-04 15:26:42.000000000 +0100 @@ -2,8 +2,8 @@ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, - * 2012, 2013, 2015 - * Thorsten Glaser + * 2012, 2013, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -25,7 +25,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.62.2.2 2015/03/01 15:43:07 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.70 2016/03/04 14:26:16 tg Exp $"); /* flags to shf_emptybuf() */ #define EB_READSW 0x01 /* about to switch to reading */ @@ -62,7 +62,7 @@ shf->flags = SHF_ALLOCS; /* Rest filled in by reopen. */ - fd = open(name, oflags | O_BINARY, mode); + fd = binopen3(name, oflags, mode); if (fd < 0) { eno = errno; afree(shf, shf->areap); @@ -197,7 +197,8 @@ { /* can't have a read+write string */ if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR))) - internal_errorf("%s: flags 0x%X", "shf_sopen", sflags); + internal_errorf("%s: flags 0x%X", "shf_sopen", + (unsigned int)sflags); if (!shf) { shf = alloc(sizeof(struct shf), ATEMP); @@ -452,7 +453,8 @@ ssize_t ncopy, orig_bsize = bsize; if (!(shf->flags & SHF_RD)) - internal_errorf("%s: flags 0x%X", "shf_read", shf->flags); + internal_errorf("%s: flags 0x%X", "shf_read", + (unsigned int)shf->flags); if (bsize <= 0) internal_errorf("%s: %s %zd", "shf_write", "bsize", bsize); @@ -489,7 +491,8 @@ char *orig_buf = buf; if (!(shf->flags & SHF_RD)) - internal_errorf("%s: flags 0x%X", "shf_getse", shf->flags); + internal_errorf("%s: flags 0x%X", "shf_getse", + (unsigned int)shf->flags); if (bsize <= 0) return (NULL); @@ -525,7 +528,8 @@ shf_getchar(struct shf *shf) { if (!(shf->flags & SHF_RD)) - internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags); + internal_errorf("%s: flags 0x%X", "shf_getchar", + (unsigned int)shf->flags); if (shf->rnleft == 0 && (shf_fillbuf(shf) == -1 || shf->rnleft == 0)) return (-1); @@ -541,7 +545,8 @@ shf_ungetc(int c, struct shf *shf) { if (!(shf->flags & SHF_RD)) - internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags); + internal_errorf("%s: flags 0x%X", "shf_ungetc", + (unsigned int)shf->flags); if ((shf->flags & SHF_ERROR) || c == -1 || (shf->rp == shf->buf && shf->rnleft)) @@ -578,7 +583,8 @@ shf_putchar(int c, struct shf *shf) { if (!(shf->flags & SHF_WR)) - internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags); + internal_errorf("%s: flags 0x%X", "shf_putchar", + (unsigned int)shf->flags); if (c == -1) return (-1); @@ -633,7 +639,8 @@ ssize_t n, ncopy, orig_nbytes = nbytes; if (!(shf->flags & SHF_WR)) - internal_errorf("%s: flags 0x%X", "shf_write", shf->flags); + internal_errorf("%s: flags 0x%X", "shf_write", + (unsigned int)shf->flags); if (nbytes < 0) internal_errorf("%s: %s %zd", "shf_write", "nbytes", nbytes); @@ -764,7 +771,7 @@ const char *s; char c, *cp; int tmp = 0, flags; - ssize_t field, precision, len; + size_t field, precision, len; unsigned long lnum; /* %#o produces the longest output */ char numbuf[(8 * sizeof(long) + 2) / 3 + 1]; @@ -791,7 +798,7 @@ */ flags = 0; field = precision = 0; - for ( ; (c = *fmt++) ; ) { + while ((c = *fmt++)) { switch (c) { case '#': flags |= FL_HASH; @@ -821,12 +828,17 @@ case '*': tmp = VA(int); - if (flags & FL_DOT) - precision = tmp; - else if ((field = tmp) < 0) { - field = -field; - flags |= FL_RIGHT; - } + if (tmp < 0) { + if (flags & FL_DOT) + precision = 0; + else { + field = (unsigned int)-tmp; + flags |= FL_RIGHT; + } + } else if (flags & FL_DOT) + precision = (unsigned int)tmp; + else + field = (unsigned int)tmp; continue; case 'l': @@ -847,32 +859,29 @@ if (ksh_isdigit(c)) { bool overflowed = false; - tmp = c - '0'; - while (c = *fmt++, ksh_isdigit(c)) { + tmp = ksh_numdig(c); + while (c = *fmt++, ksh_isdigit(c)) if (notok2mul(2147483647, tmp, 10)) overflowed = true; - tmp = tmp * 10 + c - '0'; - } + else + tmp = tmp * 10 + ksh_numdig(c); --fmt; if (overflowed) tmp = 0; if (flags & FL_DOT) - precision = tmp; + precision = (unsigned int)tmp; else - field = tmp; + field = (unsigned int)tmp; continue; } break; } - if (precision < 0) - precision = 0; - if (!c) /* nasty format */ break; - if (c >= 'A' && c <= 'Z') { + if (ksh_isupper(c)) { flags |= FL_UPPER; c = ksh_tolower(c); } @@ -917,7 +926,7 @@ /* FALLTHROUGH */ case 'u': do { - *--cp = lnum % 10 + '0'; + *--cp = digits_lc[lnum % 10]; lnum /= 10; } while (lnum); @@ -933,7 +942,7 @@ case 'o': do { - *--cp = (lnum & 0x7) + '0'; + *--cp = digits_lc[lnum & 0x7]; lnum >>= 3; } while (lnum); @@ -945,7 +954,7 @@ const char *digits = (flags & FL_UPPER) ? digits_uc : digits_lc; do { - *--cp = digits[lnum & 0xf]; + *--cp = digits[lnum & 0xF]; lnum >>= 4; } while (lnum); @@ -953,7 +962,7 @@ *--cp = (flags & FL_UPPER) ? 'X' : 'x'; *--cp = '0'; } - } + } } len = numbuf + sizeof(numbuf) - (s = cp); if (flags & FL_DOT) { @@ -999,7 +1008,6 @@ if (field > precision) { field -= precision; if (!(flags & FL_RIGHT)) { - field = -field; /* skip past sign or 0x when padding with 0 */ if ((flags & FL_ZERO) && (flags & FL_NUMBER)) { if (*s == '+' || *s == '-' || @@ -1012,8 +1020,8 @@ shf_putc(*s, shf); s++; nwritten++; - if (--precision > 0 && - (*s | 0x20) == 'x') { + if (--precision && + ksh_eq(*s, 'X', 'x')) { shf_putc(*s, shf); s++; precision--; @@ -1023,17 +1031,16 @@ c = '0'; } else c = flags & FL_ZERO ? '0' : ' '; - if (field < 0) { - nwritten += -field; - for ( ; field < 0 ; field++) - shf_putc(c, shf); - } + nwritten += field; + while (field--) + shf_putc(c, shf); + field = 0; } else c = ' '; } else field = 0; - if (precision > 0) { + if (precision) { const char *q; nwritten += precision; @@ -1042,11 +1049,9 @@ shf_putc(*s, shf); } while (++s < q); } - if (field > 0) { - nwritten += field; - for ( ; field > 0 ; --field) - shf_putc(c, shf); - } + nwritten += field; + while (field--) + shf_putc(c, shf); } return (shf_error(shf) ? -1 : nwritten); @@ -1097,14 +1102,10 @@ switch (errnum) { case 0: return ("Undefined error: 0"); -#ifdef EPERM case EPERM: return ("Operation not permitted"); -#endif -#ifdef ENOENT case ENOENT: return ("No such file or directory"); -#endif #ifdef ESRCH case ESRCH: return ("No such process"); @@ -1113,22 +1114,20 @@ case E2BIG: return ("Argument list too long"); #endif -#ifdef ENOEXEC case ENOEXEC: return ("Exec format error"); -#endif + case EBADF: + return ("Bad file descriptor"); #ifdef ENOMEM case ENOMEM: return ("Cannot allocate memory"); #endif -#ifdef EACCES case EACCES: return ("Permission denied"); -#endif -#ifdef ENOTDIR + case EEXIST: + return ("File exists"); case ENOTDIR: return ("Not a directory"); -#endif #ifdef EINVAL case EINVAL: return ("Invalid argument"); diff -Nru mksh-50e/strlcpy.c mksh-52c/strlcpy.c --- mksh-50e/strlcpy.c 2013-11-05 23:10:40.000000000 +0100 +++ mksh-52c/strlcpy.c 2015-11-29 18:05:28.000000000 +0100 @@ -1,6 +1,6 @@ /*- * Copyright (c) 2006, 2008, 2009, 2013 - * Thorsten Glaser + * mirabilos * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -18,7 +18,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/strlcpy.c,v 1.8 2013/11/05 22:10:15 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/strlcpy.c,v 1.10 2015/11/29 17:05:02 tg Exp $"); /* * Copy src to string dst of size siz. At most siz-1 characters diff -Nru mksh-50e/syn.c mksh-52c/syn.c --- mksh-50e/syn.c 2015-01-25 16:36:19.000000000 +0100 +++ mksh-52c/syn.c 2016-02-26 22:25:24.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: syn.c,v 1.29 2013/06/03 18:40:05 jca Exp $ */ +/* $OpenBSD: syn.c,v 1.30 2015/09/01 13:12:31 tedu Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, - * 2011, 2012, 2013, 2014 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.94.2.1 2015/01/25 15:35:54 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.111 2016/02/26 21:24:58 tg Exp $"); struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ @@ -31,6 +31,7 @@ }; struct yyrecursive_state { + struct ioword *old_heres[HERES]; struct yyrecursive_state *next; struct ioword **old_herep; int old_symbol; @@ -58,7 +59,6 @@ static void syntaxerr(const char *) MKSH_A_NORETURN; static void nesting_push(struct nesting_state *, int); static void nesting_pop(struct nesting_state *); -static int assign_command(const char *); static int inalias(struct source *) MKSH_A_PURE; static Test_op dbtestp_isa(Test_env *, Test_meta); static const char *dbtestp_getopnd(Test_env *, Test_op, bool); @@ -173,6 +173,8 @@ return (t); } +static const char IONDELIM_delim[] = { CHAR, '<', CHAR, '<', EOS }; + static struct ioword * synio(int cf) { @@ -190,40 +192,48 @@ return (NULL); ACCEPT; iop = yylval.iop; - if (iop->flag & IONDELIM) - goto gotnulldelim; - ishere = (iop->flag & IOTYPE) == IOHERE; - musthave(LWORD, ishere ? HEREDELIM : 0); + ishere = (iop->ioflag & IOTYPE) == IOHERE; + if (iop->ioflag & IOHERESTR) { + musthave(LWORD, 0); + } else if (ishere && tpeek(HEREDELIM) == '\n') { + ACCEPT; + yylval.cp = wdcopy(IONDELIM_delim, ATEMP); + iop->ioflag |= IOEVAL | IONDELIM; + } else + musthave(LWORD, ishere ? HEREDELIM : 0); if (ishere) { iop->delim = yylval.cp; - if (*ident != 0) { + if (*ident != 0 && !(iop->ioflag & IOHERESTR)) { /* unquoted */ - gotnulldelim: - iop->flag |= IOEVAL; + iop->ioflag |= IOEVAL; } if (herep > &heres[HERES - 1]) yyerror("too many %ss\n", "<<"); *herep++ = iop; } else - iop->name = yylval.cp; + iop->ioname = yylval.cp; - if (iop->flag & IOBASH) { + if (iop->ioflag & IOBASH) { char *cp; nextiop = alloc(sizeof(*iop), ATEMP); - nextiop->name = cp = alloc(5, ATEMP); +#ifdef MKSH_CONSERVATIVE_FDS + nextiop->ioname = cp = alloc(3, ATEMP); +#else + nextiop->ioname = cp = alloc(5, ATEMP); if (iop->unit > 9) { *cp++ = CHAR; - *cp++ = '0' + (iop->unit / 10); + *cp++ = digits_lc[iop->unit / 10]; } +#endif *cp++ = CHAR; - *cp++ = '0' + (iop->unit % 10); + *cp++ = digits_lc[iop->unit % 10]; *cp = EOS; - iop->flag &= ~IOBASH; + iop->ioflag &= ~IOBASH; nextiop->unit = 2; - nextiop->flag = IODUP; + nextiop->ioflag = IODUP; nextiop->delim = NULL; nextiop->heredoc = NULL; } @@ -244,10 +254,10 @@ } static const char let_cmd[] = { - CHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS + QCHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS }; static const char setA_cmd0[] = { - CHAR, 's', CHAR, 'e', CHAR, 't', EOS + QCHAR, 's', CHAR, 'e', CHAR, 't', EOS }; static const char setA_cmd1[] = { CHAR, '-', CHAR, 'A', EOS @@ -263,7 +273,6 @@ int c, iopn = 0, syniocf, lno; struct ioword *iop, **iops; XPtrV args, vars; - char *tcp; struct nesting_state old_nesting; /* NUFILE is small enough to leave this addition unchecked */ @@ -272,7 +281,7 @@ XPinit(vars, 16); syniocf = KEYWORD|sALIAS; - switch (c = token(cf|KEYWORD|sALIAS|VARASN)) { + switch (c = token(cf|KEYWORD|sALIAS|CMDASN)) { default: REJECT; afree(iops, ATEMP); @@ -287,9 +296,18 @@ syniocf &= ~(KEYWORD|sALIAS); t = newtp(TCOM); t->lineno = source->line; + goto get_command_begin; while (/* CONSTCOND */ 1) { - cf = (t->u.evalflags ? ARRAYVAR : 0) | - (XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD); + bool check_assign_cmd; + + if (XPsize(args) == 0) { + get_command_begin: + check_assign_cmd = true; + cf = sALIAS | CMDASN; + } else if (t->u.evalflags) + cf = CMDWORD | CMDASN; + else + cf = CMDWORD; switch (tpeek(cf)) { case REDIR: while ((iop = synio(cf)) != NULL) { @@ -307,9 +325,12 @@ * dubious but AT&T ksh acts this way */ if (iopn == 0 && XPsize(vars) == 0 && - XPsize(args) == 0 && - assign_command(ident)) - t->u.evalflags = DOVACHECK; + check_assign_cmd) { + if (assign_command(ident, false)) + t->u.evalflags = DOVACHECK; + else if (strcmp(ident, Tcommand) != 0) + check_assign_cmd = false; + } if ((XPsize(args) == 0 || Flag(FKEYWORD)) && is_wdvarassign(yylval.cp)) XPput(vars, yylval.cp); @@ -320,6 +341,8 @@ case '(' /*)*/: if (XPsize(args) == 0 && XPsize(vars) == 1 && is_wdvarassign(yylval.cp)) { + char *tcp; + /* wdarrassign: foo=(bar) */ ACCEPT; @@ -391,6 +414,7 @@ case LWORD: break; case '(': /*)*/ + c = '('; goto Subshell; default: syntaxerr(NULL); @@ -422,10 +446,10 @@ case FOR: case SELECT: t = newtp((c == FOR) ? TFOR : TSELECT); - musthave(LWORD, ARRAYVAR); + musthave(LWORD, CMDASN); if (!is_wdvarname(yylval.cp, true)) - yyerror("%s: %s\n", c == FOR ? "for" : Tselect, - "bad identifier"); + yyerror("%s: bad identifier\n", + c == FOR ? "for" : Tselect); strdupx(t->str, ident, ATEMP); nesting_push(&old_nesting, c); t->vars = wordlist(); @@ -511,6 +535,12 @@ XPfree(vars); } + if (c == MDPAREN) { + t = block(TBRACE, t, NULL); + t->ioact = t->left->ioact; + t->left->ioact = NULL; + } + return (t); } @@ -557,7 +587,7 @@ { struct op *t; - switch (token(KEYWORD|sALIAS|VARASN)) { + switch (token(KEYWORD|sALIAS|CMDASN)) { case ELSE: if ((t = c_list(true)) == NULL) syntaxerr(NULL); @@ -676,7 +706,7 @@ */ for (p = sname; *p; p++) if (ctype(*p, C_QUOTE)) - yyerror("%s: %s\n", sname, "invalid function name"); + yyerror("%s: invalid function name\n", sname); /* * Note that POSIX allows only compound statements after foo(), @@ -713,7 +743,7 @@ /* (2 * sizeof(char *)) is small enough */ t->left->args = alloc(2 * sizeof(char *), ATEMP); t->left->args[0] = tv = alloc(3, ATEMP); - tv[0] = CHAR; + tv[0] = QCHAR; tv[1] = ':'; tv[2] = EOS; t->left->args[1] = NULL; @@ -823,8 +853,8 @@ static void syntaxerr(const char *what) { - /* 2<<- is the longest redirection, I think */ - char redir[6]; + /* 23<<- is the longest redirection, I think */ + char redir[8]; const char *s; struct tokeninfo const *tt; int c; @@ -843,7 +873,7 @@ goto Again; } /* don't quote the EOF */ - yyerror("%s: %s %s\n", Tsynerr, "unexpected", "EOF"); + yyerror("%s: unexpected EOF\n", Tsynerr); /* NOTREACHED */ case LWORD: @@ -927,14 +957,15 @@ * a=a * $ */ -static int -assign_command(const char *s) +int +assign_command(const char *s, bool docommand) { if (!*s) return (0); return ((strcmp(s, Talias) == 0) || (strcmp(s, Texport) == 0) || (strcmp(s, Treadonly) == 0) || + (docommand && (strcmp(s, Tcommand) == 0)) || (strcmp(s, Ttypeset) == 0)); } @@ -976,7 +1007,7 @@ static Test_op dbtestp_isa(Test_env *te, Test_meta meta) { - int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); + int c = tpeek(CMDASN | (meta == TM_BINOP ? 0 : CONTIN)); bool uqword; char *save = NULL; Test_op ret = TO_NONOP; @@ -997,9 +1028,10 @@ ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP; else if (meta == TM_UNOP || meta == TM_BINOP) { if (meta == TM_BINOP && c == REDIR && - (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { + (yylval.iop->ioflag == IOREAD || + yylval.iop->ioflag == IOWRITE)) { ret = TO_NONNULL; - save = wdcopy(yylval.iop->flag == IOREAD ? + save = wdcopy(yylval.iop->ioflag == IOREAD ? db_lthan : db_gthan, ATEMP); } else if (uqword && (ret = test_isop(meta, ident))) save = yylval.cp; @@ -1021,7 +1053,7 @@ dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED, bool do_eval MKSH_A_UNUSED) { - int c = tpeek(ARRAYVAR); + int c = tpeek(CMDASN); if (c != LWORD) return (NULL); @@ -1074,7 +1106,8 @@ tv->tv_sec = 0; /* parse integral part */ while (ksh_isdigit(*s)) { - tt.tv_sec = tv->tv_sec * 10 + (*s++ - '0'); + tt.tv_sec = tv->tv_sec * 10 + ksh_numdig(*s++); + /*XXX this overflow check maybe UB */ if (tt.tv_sec / 10 != tv->tv_sec) { errno = EOVERFLOW; return (true); @@ -1095,7 +1128,7 @@ /* parse decimal fraction */ i = 100000; while (ksh_isdigit(*s)) { - tv->tv_usec += i * (*s++ - '0'); + tv->tv_usec += i * ksh_numdig(*s++); if (i == 1) break; i /= 10; @@ -1143,7 +1176,9 @@ ys->old_reject = reject; ys->old_symbol = symbol; ACCEPT; + memcpy(ys->old_heres, heres, sizeof(heres)); ys->old_herep = herep; + herep = heres; ys->old_salias = sALIAS; sALIAS = 0; ys->next = e->yyrecursive_statep; @@ -1170,6 +1205,7 @@ e->yyrecursive_statep = ys->next; sALIAS = ys->old_salias; + memcpy(heres, ys->old_heres, sizeof(heres)); herep = ys->old_herep; reject = ys->old_reject; symbol = ys->old_symbol; diff -Nru mksh-50e/tree.c mksh-52c/tree.c --- mksh-50e/tree.c 2013-09-24 22:20:10.000000000 +0200 +++ mksh-52c/tree.c 2016-03-04 15:26:42.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: tree.c,v 1.20 2012/06/27 07:17:19 otto Exp $ */ +/* $OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013 - * Thorsten Glaser + * 2011, 2012, 2013, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.72 2013/09/24 20:19:45 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.83 2016/03/04 14:26:16 tg Exp $"); #define INDENT 8 @@ -69,7 +69,7 @@ t->ioact != NULL && t->ioact[0] != NULL && t->ioact[1] == NULL && /* of type "here document" (or "here string") */ - (t->ioact[0]->flag & IOTYPE) == IOHERE) { + (t->ioact[0]->ioflag & IOTYPE) == IOHERE) { fptreef(shf, indent, "%S", t->vars[0]); break; } @@ -221,12 +221,11 @@ struct ioword *iop = *ioact++; /* heredoc is NULL when tracing (set -x) */ - if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE && + if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE && iop->heredoc) { shf_putc('\n', shf); shf_puts(iop->heredoc, shf); fptreef(shf, indent, "%s", - iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0)); need_nl = true; } @@ -246,16 +245,16 @@ static void pioact(struct shf *shf, struct ioword *iop) { - int flag = iop->flag; - int type = flag & IOTYPE; - int expected; + unsigned short flag = iop->ioflag; + unsigned short type = flag & IOTYPE; + short expected; expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 : (type == IOCAT || type == IOWRITE) ? 1 : (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit : iop->unit + 1; if (iop->unit != expected) - shf_fprintf(shf, "%d", iop->unit); + shf_fprintf(shf, "%d", (int)iop->unit); switch (type) { case IOREAD: @@ -265,6 +264,8 @@ shf_puts("<<", shf); if (flag & IOSKIP) shf_putc('-', shf); + else if (flag & IOHERESTR) + shf_putc('<', shf); break; case IOCAT: shf_puts(">>", shf); @@ -283,17 +284,15 @@ } /* name/delim are NULL when printing syntax errors */ if (type == IOHERE) { - if (iop->delim) + if (iop->delim && !(iop->ioflag & IONDELIM)) wdvarput(shf, iop->delim, 0, WDS_TPUTS); - if (iop->flag & IOHERESTR) - shf_putc(' ', shf); - } else if (iop->name) { - if (iop->flag & IONAMEXP) - print_value_quoted(shf, iop->name); + } else if (iop->ioname) { + if (flag & IONAMEXP) + print_value_quoted(shf, iop->ioname); else - wdvarput(shf, iop->name, 0, WDS_TPUTS); - shf_putc(' ', shf); + wdvarput(shf, iop->ioname, 0, WDS_TPUTS); } + shf_putc(' ', shf); prevent_semicolon = false; } @@ -309,7 +308,7 @@ * `...` -> $(...) * 'foo' -> "foo" * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS - * x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ + * x${foo:-'hi'} -> x${foo:-hi} * could change encoding to: * OQUOTE ["'] ... CQUOTE ["'] * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) @@ -319,12 +318,12 @@ case EOS: return (--wp); case ADELIM: + if (*wp == /*{*/'}') { + ++wp; + goto wdvarput_csubst; + } case CHAR: c = *wp++; - if ((opmode & WDS_MAGIC) && - (ISMAGIC(c) || c == '[' || c == '!' || - c == '-' || c == ']' || c == '*' || c == '?')) - shf_putc(MAGIC, shf); shf_putc(c, shf); break; case QCHAR: { @@ -336,8 +335,7 @@ if (quotelevel == 0) doq = true; } else { - if (!(opmode & WDS_KEEPQ)) - doq = false; + doq = false; } if (doq) shf_putc('\\', shf); @@ -389,25 +387,20 @@ wp = wdvarput(shf, wp, 0, opmode); break; case CSUBST: - if (*wp++ == '}') + if (*wp++ == '}') { + wdvarput_csubst: shf_putc('}', shf); + } return (wp); case OPAT: - if (opmode & WDS_MAGIC) { - shf_putc(MAGIC, shf); - shf_putchar(*wp++ | 0x80, shf); - } else { - shf_putchar(*wp++, shf); - shf_putc('(', shf); - } + shf_putchar(*wp++, shf); + shf_putc('(', shf); break; case SPAT: c = '|'; if (0) case CPAT: c = /*(*/ ')'; - if (opmode & WDS_MAGIC) - shf_putc(MAGIC, shf); shf_putc(c, shf); break; } @@ -594,8 +587,10 @@ case EOS: return (wp); case ADELIM: - if (c == ADELIM) + if (c == ADELIM && nest == 0) return (wp + 1); + if (*wp == /*{*/'}') + goto wdscan_csubst; /* FALLTHROUGH */ case CHAR: case QCHAR: @@ -617,6 +612,7 @@ ; break; case CSUBST: + wdscan_csubst: wp++; if (c == CSUBST && nest == 0) return (wp); @@ -635,8 +631,8 @@ break; default: internal_warningf( - "wdscan: unknown char 0x%x (carrying on)", - wp[-1]); + "wdscan: unknown char 0x%X (carrying on)", + (unsigned char)wp[-1]); } } @@ -673,8 +669,8 @@ q = alloc(sizeof(struct ioword), ap); ior[i] = q; *q = *p; - if (p->name != NULL) - q->name = wdcopy(p->name, ap); + if (p->ioname != NULL) + q->ioname = wdcopy(p->ioname, ap); if (p->delim != NULL) q->delim = wdcopy(p->delim, ap); if (p->heredoc != NULL) @@ -696,8 +692,7 @@ if (t == NULL) return; - if (t->str != NULL) - afree(t->str, ap); + afree(t->str, ap); if (t->vars != NULL) { for (w = t->vars; *w != NULL; w++) @@ -732,12 +727,9 @@ iop = iow; while ((p = *iop++) != NULL) { - if (p->name != NULL) - afree(p->name, ap); - if (p->delim != NULL) - afree(p->delim, ap); - if (p->heredoc != NULL) - afree(p->heredoc, ap); + afree(p->ioname, ap); + afree(p->delim, ap); + afree(p->heredoc, ap); afree(p, ap); } afree(iow, ap); @@ -748,6 +740,8 @@ { if (isksh) fptreef(shf, i, "%s %s %T", Tfunction, k, v); + else if (ktsearch(&keywords, k, hash(k))) + fptreef(shf, i, "%s %s() %T", Tfunction, k, v); else fptreef(shf, i, "%s() %T", k, v); } @@ -822,6 +816,10 @@ shf_puts("EOS", shf); return (--wp); case ADELIM: + if (*wp == /*{*/'}') { + shf_puts(/*{*/ "]ADELIM(})", shf); + return (wp + 1); + } shf_puts("ADELIM=", shf); if (0) case CHAR: @@ -854,10 +852,10 @@ shf_puts("EXPRSUB<", shf); goto dumpsub; case OQUOTE: - shf_fprintf(shf, "OQUOTE{%d", ++quotelevel); + shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel); break; case CQUOTE: - shf_fprintf(shf, "%d}CQUOTE", quotelevel); + shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel); if (quotelevel) quotelevel--; else @@ -910,9 +908,9 @@ shf_puts("{IOACT", shf); while ((iop = *ioact++) != NULL) { - int type = iop->flag & IOTYPE; + unsigned short type = iop->ioflag & IOTYPE; #define DT(x) case x: shf_puts(#x, shf); break; -#define DB(x) if (iop->flag & x) shf_puts("|" #x, shf); +#define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf); shf_putc(';', shf); switch (type) { @@ -933,19 +931,19 @@ DB(IOBASH) DB(IOHERESTR) DB(IONDELIM) - shf_fprintf(shf, ",unit=%d", iop->unit); - if (iop->delim) { + shf_fprintf(shf, ",unit=%d", (int)iop->unit); + if (iop->delim && !(iop->ioflag & IONDELIM)) { shf_puts(",delim<", shf); dumpwdvar(shf, iop->delim); shf_putc('>', shf); } - if (iop->name) { - if (iop->flag & IONAMEXP) { + if (iop->ioname) { + if (iop->ioflag & IONAMEXP) { shf_puts(",name=", shf); - print_value_quoted(shf, iop->name); + print_value_quoted(shf, iop->ioname); } else { shf_puts(",name<", shf); - dumpwdvar(shf, iop->name); + dumpwdvar(shf, iop->ioname); shf_putc('>', shf); } } @@ -1097,7 +1095,7 @@ goto dumpleftandout; OPEN(TFUNCT) shf_fprintf(shf, " str<%s> ksh<%s>", t->str, - t->u.ksh_func ? "yes" : "no"); + t->u.ksh_func ? "true" : "false"); goto dumpleftandout; OPEN(TTIME) goto dumpleftandout; diff -Nru mksh-50e/var.c mksh-52c/var.c --- mksh-50e/var.c 2015-03-01 16:43:32.000000000 +0100 +++ mksh-52c/var.c 2016-03-01 21:28:59.000000000 +0100 @@ -1,9 +1,9 @@ -/* $OpenBSD: var.c,v 1.38 2013/12/20 17:53:09 zhuk Exp $ */ +/* $OpenBSD: var.c,v 1.44 2015/09/10 11:37:42 jca Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - * 2011, 2012, 2013, 2014, 2015 - * Thorsten Glaser + * 2011, 2012, 2013, 2014, 2015, 2016 + * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission @@ -28,7 +28,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/var.c,v 1.183.2.2 2015/03/01 15:43:07 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/var.c,v 1.201 2016/03/01 20:28:33 tg Exp $"); /*- * Variables @@ -218,14 +218,16 @@ return (n); } +#define vn vname.ro /* * Search for variable, if not found create globally. */ struct tbl * global(const char *n) { - struct block *l = e->loc; struct tbl *vp; + union mksh_cchack vname; + struct block *l = e->loc; int c; bool array; uint32_t h, val; @@ -234,27 +236,35 @@ * check to see if this is an array; * dereference namerefs; must come first */ - n = array_index_calc(n, &array, &val); - h = hash(n); - c = (unsigned char)n[0]; + vn = array_index_calc(n, &array, &val); + h = hash(vn); + c = (unsigned char)vn[0]; if (!ksh_isalphx(c)) { if (array) - errorf("bad substitution"); - vp = &vtemp; + errorf(Tbadsubst); + vp = vtemp; vp->flag = DEFINED; vp->type = 0; vp->areap = ATEMP; - *vp->name = c; if (ksh_isdigit(c)) { - if (getn(n, &c) && (c <= l->argc)) - /* setstr can't fail here */ - setstr(vp, l->argv[c], KSH_RETURN_ERROR); + if (getn(vn, &c)) { + /* main.c:main_init() says 12 */ + shf_snprintf(vp->name, 12, "%d", c); + if (c <= l->argc) { + /* setstr can't fail here */ + setstr(vp, l->argv[c], + KSH_RETURN_ERROR); + } + } else + vp->name[0] = '\0'; vp->flag |= RDONLY; - return (vp); + goto out; } + vp->name[0] = c; + vp->name[1] = '\0'; vp->flag |= RDONLY; - if (n[1] != '\0') - return (vp); + if (vn[1] != '\0') + goto out; vp->flag |= ISSET|INTEGER; switch (c) { case '$': @@ -278,17 +288,24 @@ default: vp->flag &= ~(ISSET|INTEGER); } - return (vp); + goto out; } - l = varsearch(e->loc, &vp, n, h); - if (vp != NULL) - return (array ? arraysearch(vp, val) : vp); - vp = ktenter(&l->vars, n, h); + l = varsearch(e->loc, &vp, vn, h); + if (vp != NULL) { + if (array) + vp = arraysearch(vp, val); + goto out; + } + vp = ktenter(&l->vars, vn, h); if (array) vp = arraysearch(vp, val); vp->flag |= DEFINED; - if (special(n)) + if (special(vn)) vp->flag |= SPECIAL; + out: + last_lookup_was_array = array; + if (vn != n) + afree(vname.rw, ATEMP); return (vp); } @@ -298,8 +315,9 @@ struct tbl * local(const char *n, bool copy) { - struct block *l = e->loc; struct tbl *vp; + union mksh_cchack vname; + struct block *l = e->loc; bool array; uint32_t h, val; @@ -307,20 +325,20 @@ * check to see if this is an array; * dereference namerefs; must come first */ - n = array_index_calc(n, &array, &val); - h = hash(n); - if (!ksh_isalphx(*n)) { - vp = &vtemp; + vn = array_index_calc(n, &array, &val); + h = hash(vn); + if (!ksh_isalphx(*vn)) { + vp = vtemp; vp->flag = DEFINED|RDONLY; vp->type = 0; vp->areap = ATEMP; - return (vp); + goto out; } - vp = ktenter(&l->vars, n, h); + vp = ktenter(&l->vars, vn, h); if (copy && !(vp->flag & DEFINED)) { struct tbl *vq; - varsearch(l->next, &vq, n, h); + varsearch(l->next, &vq, vn, h); if (vq != NULL) { vp->flag |= vq->flag & (EXPORT | INTEGER | RDONLY | LJUST | RJUST | @@ -333,10 +351,15 @@ if (array) vp = arraysearch(vp, val); vp->flag |= DEFINED; - if (special(n)) + if (special(vn)) vp->flag |= SPECIAL; + out: + last_lookup_was_array = array; + if (vn != n) + afree(vname.rw, ATEMP); return (vp); } +#undef vn /* get variable string value */ char * @@ -464,13 +487,12 @@ setint(struct tbl *vq, mksh_ari_t n) { if (!(vq->flag&INTEGER)) { - struct tbl *vp = &vtemp; - vp->flag = (ISSET|INTEGER); - vp->type = 0; - vp->areap = ATEMP; - vp->val.i = n; + vtemp->flag = (ISSET|INTEGER); + vtemp->type = 0; + vtemp->areap = ATEMP; + vtemp->val.i = n; /* setstr can't fail here */ - setstr(vq, str_val(vp), KSH_RETURN_ERROR); + setstr(vq, str_val(vtemp), KSH_RETURN_ERROR); } else vq->val.i = n; vq->flag |= ISSET; @@ -481,41 +503,57 @@ static int getint(struct tbl *vp, mksh_ari_u *nump, bool arith) { - mksh_uari_t c, num, base; + mksh_uari_t c, num = 0, base = 10; const char *s; bool have_base = false, neg = false; - if (vp->flag&SPECIAL) + if (vp->flag & SPECIAL) getspec(vp); /* XXX is it possible for ISSET to be set and val.s to be NULL? */ - if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL)) + if (!(vp->flag & ISSET) || (!(vp->flag & INTEGER) && vp->val.s == NULL)) return (-1); - if (vp->flag&INTEGER) { + if (vp->flag & INTEGER) { nump->i = vp->val.i; return (vp->type); } s = vp->val.s + vp->type; - base = 10; - num = 0; - if (arith && s[0] == '0' && (s[1] | 0x20) == 'x') { - s += 2; - base = 16; - have_base = true; - } - if (Flag(FPOSIX) && arith && s[0] == '0' && ksh_isdigit(s[1]) && - !(vp->flag & ZEROFIL)) { - /* interpret as octal (deprecated) */ - base = 8; - have_base = true; - } - while ((c = (unsigned char)*s++)) { - if (c == '-') { - neg = true; - continue; - } else if (c == '#') { - if (have_base || num < 1 || num > 36) + + do { + c = (unsigned char)*s++; + } while (ksh_isspace(c)); + + switch (c) { + case '-': + neg = true; + /* FALLTHROUGH */ + case '+': + c = (unsigned char)*s++; + break; + } + + if (c == '0' && arith) { + if (ksh_eq(s[0], 'X', 'x')) { + /* interpret as hexadecimal */ + base = 16; + ++s; + goto getint_c_style_base; + } else if (Flag(FPOSIX) && ksh_isdigit(s[0]) && + !(vp->flag & ZEROFIL)) { + /* interpret as octal (deprecated) */ + base = 8; + getint_c_style_base: + have_base = true; + c = (unsigned char)*s++; + } + } + + do { + if (c == '#') { + /* ksh-style base determination */ + if (have_base || num < 1) return (-1); if ((base = num) == 1) { + /* mksh-specific extension */ unsigned int wc; if (!UTFMODE) @@ -530,22 +568,26 @@ wc = 0xEF00 + *(const unsigned char *)s; nump->u = (mksh_uari_t)wc; return (1); - } + } else if (base > 36) + base = 10; num = 0; have_base = true; continue; - } else if (ksh_isdigit(c)) - c -= '0'; - else if (ksh_islower(c)) - c -= 'a' - 10; + } + if (ksh_isdigit(c)) + c = ksh_numdig(c); else if (ksh_isupper(c)) - c -= 'A' - 10; + c = ksh_numuc(c) + 10; + else if (ksh_islower(c)) + c = ksh_numlc(c) + 10; else return (-1); if (c >= base) return (-1); + /* handle overflow as truncation */ num = num * base + c; - } + } while ((c = (unsigned char)*s++)); + if (neg) num = -num; nump->u = num; @@ -690,8 +732,7 @@ /* offset to value */ vp->type = xp - vp->val.s; memcpy(xp, val, vallen); - if (op != NULL) - afree(op, vp->areap); + afree(op, vp->areap); } /* @@ -788,12 +829,13 @@ /* check target value for being a valid variable name */ ccp = skip_varname(qval, false); if (ccp == qval) { - if (ksh_isdigit(qval[0])) { - int c; + int c; - if (getn(qval, &c)) - goto nameref_rhs_checked; - } else if (qval[1] == '\0') switch (qval[0]) { + if (!(c = (unsigned char)qval[0])) + goto nameref_empty; + else if (ksh_isdigit(c) && getn(qval, &c)) + goto nameref_rhs_checked; + else if (qval[1] == '\0') switch (c) { case '$': case '!': case '?': @@ -929,8 +971,7 @@ t->type = 0; } } - if (free_me) - afree(free_me, t->areap); + afree(free_me, t->areap); } } if (!ok) @@ -956,8 +997,7 @@ /* setstr can't fail (readonly check already done) */ setstr(vp, val, KSH_RETURN_ERROR | 0x4); - if (tval != NULL) - afree(tval, ATEMP); + afree(tval, ATEMP); } /* only x[0] is ever exported, so use vpbase */ @@ -1144,7 +1184,7 @@ } static time_t seconds; /* time SECONDS last set */ -static int user_lineno; /* what user set $LINENO to */ +static mksh_uari_t user_lineno; /* what user set $LINENO to */ static void getspec(struct tbl *vp) @@ -1178,7 +1218,7 @@ num.i = histsize; break; case V_LINENO: - num.i = current_lineno + user_lineno; + num.u = (mksh_uari_t)current_lineno + user_lineno; break; case V_LINES: num.i = x_lins; @@ -1240,18 +1280,15 @@ ifs0 = *s; return; case V_PATH: - if (path) - afree(path, APERM); + afree(path, APERM); s = str_val(vp); strdupx(path, s, APERM); /* clear tracked aliases */ flushcom(true); return; case V_TMPDIR: - if (tmpdir) { - afree(tmpdir, APERM); - tmpdir = NULL; - } + afree(tmpdir, APERM); + tmpdir = NULL; /* * Use tmpdir iff it is an absolute path, is writable * and searchable and is a directory... @@ -1261,7 +1298,7 @@ s = str_val(vp); /* LINTED use of access */ - if (s[0] == '/' && access(s, W_OK|X_OK) == 0 && + if (mksh_abspath(s) && access(s, W_OK|X_OK) == 0 && stat(s, &statb) == 0 && S_ISDIR(statb.st_mode)) strdupx(tmpdir, s, APERM); } @@ -1308,7 +1345,7 @@ break; case V_LINENO: /* The -1 is because line numbering starts at 1. */ - user_lineno = num.u - current_lineno - 1; + user_lineno = num.u - (mksh_uari_t)current_lineno - 1; break; case V_LINES: if (num.i >= MIN_LINS) @@ -1350,13 +1387,17 @@ */ switch (special(vp->name)) { +#if HAVE_PERSISTENT_HISTORY + case V_HISTFILE: + sethistfile(NULL); + return; +#endif case V_IFS: setctypes(TC_IFSWS, C_IFS); ifs0 = ' '; break; case V_PATH: - if (path) - afree(path, APERM); + afree(path, APERM); strdupx(path, def_path, APERM); /* clear tracked aliases */ flushcom(true); @@ -1455,7 +1496,7 @@ const char *p; char *rv; - if ((p = cstrchr(str, '[')) == 0) + if (!(p = cstrchr(str, '['))) /* Shouldn't happen, but why worry? */ strdupx(rv, str, ATEMP); else diff -Nru mksh-50e/var_spec.h mksh-52c/var_spec.h --- mksh-50e/var_spec.h 2012-11-30 17:45:50.000000000 +0100 +++ mksh-52c/var_spec.h 2015-12-12 22:09:10.000000000 +0100 @@ -1,5 +1,25 @@ +/*- + * Copyright (c) 2009, 2011, 2012 + * mirabilos + * + * Provided that these terms and disclaimer and all copyright notices + * are retained or reproduced in an accompanying document, permission + * is granted to deal in this work without restriction, including un- + * limited rights to use, publicly perform, distribute, sell, modify, + * merge, give away, or sublicence. + * + * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to + * the utmost extent permitted by applicable law, neither express nor + * implied; without malicious intent or gross negligence. In no event + * may a licensor, author or contributor be held liable for indirect, + * direct, other damage, loss, or other issues arising in any way out + * of dealing in the work, even if advised of the possibility of such + * damage or existence of a defect, except proven that it results out + * of said person's immediate fault when using the work as intended. + */ + #if defined(VARSPEC_DEFNS) -__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.6 2012/11/30 16:45:25 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.7 2015/12/12 21:08:44 tg Exp $"); #define FN(name) /* nothing */ #elif defined(VARSPEC_ENUMS) #define FN(name) V_##name,