# ...and CPPFLAGS.
LOCAL_CFLAGS += \
- -DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 -DMKSH_CONSERVATIVE_FDS \
- -DMKSH_DONT_EMIT_IDSTRING -DMKSH_NOPWNAM -DMKSH_BUILDSH \
+ -DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 \
+ -DMKSH_DONT_EMIT_IDSTRING \
+ -DMKSH_BUILDSH \
-D_GNU_SOURCE -DSETUID_CAN_FAIL_WITH_EAGAIN \
-DHAVE_ATTRIBUTE_BOUNDED=0 -DHAVE_ATTRIBUTE_FORMAT=1 \
-DHAVE_ATTRIBUTE_NORETURN=1 \
-DHAVE_STRINGS_H=1 -DHAVE_TERMIOS_H=1 -DHAVE_ULIMIT_H=0 \
-DHAVE_VALUES_H=0 -DHAVE_CAN_INTTYPES=1 -DHAVE_CAN_UCBINTS=1 \
-DHAVE_CAN_INT8TYPE=1 -DHAVE_CAN_UCBINT8=1 -DHAVE_RLIM_T=1 \
- -DHAVE_SIG_T=1 -DHAVE_SYS_ERRLIST=0 -DHAVE_SYS_SIGNAME=1 \
+ -DHAVE_SIG_T=1 \
+ -DHAVE_STRING_POOLING=1 \
+ -DHAVE_SYS_ERRLIST=0 -DHAVE_SYS_SIGNAME=1 \
-DHAVE_SYS_SIGLIST=1 -DHAVE_FLOCK=1 -DHAVE_LOCK_FCNTL=1 \
-DHAVE_GETRUSAGE=1 \
-DHAVE_GETSID=1 \
-DHAVE_SETGROUPS=1 -DHAVE_STRERROR=1 -DHAVE_STRSIGNAL=0 \
-DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \
-DHAVE_SYS_ERRLIST_DECL=0 -DHAVE_SYS_SIGLIST_DECL=1 \
- -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=521
+ -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=530
include $(BUILD_EXECUTABLE)
+++ /dev/null
-# Copyright © 2010, 2012, 2013
-# Thorsten Glaser <tg@mirbsd.org>
-# This file is provided under the same terms as mksh.
-#-
-# Helper script to let src/Build.sh generate Makefrag.inc
-# which we in turn use in the manual creation of Android.mk
-#
-# This script is supposed to be run from/inside AOSP by the
-# porter of mksh to Android (and only manually).
-
-if test x"$1" = x"-t"; then
- # test compilation
- args=-r
- mkmfmode=1
-else
- # prepare for AOSP
- args=-M
- mkmfmode=0
-fi
-
-cd "$(dirname "$0")"
-srcdir=$(pwd)
-rm -rf tmp
-mkdir tmp
-cd ../..
-aospdir=$(pwd)
-cd $srcdir/tmp
-
-addvar() {
- _vn=$1; shift
-
- eval $_vn=\"\$$_vn '$*"'
-}
-
-CFLAGS=
-CPPFLAGS=
-LDFLAGS=
-LIBS=
-
-# The definitions below were generated by examining the
-# output of the following command:
-# make showcommands out/target/product/generic/system/bin/mksh 2>&1 | tee log
-#
-# They are only used to let Build.sh find the compiler, header
-# files, linker and libraries to generate Makefrag.inc (similar
-# to what GNU autotools’ configure scripts do), and never used
-# during the real build process. We need this to port mksh to
-# the Android platform and it is crucial these are as close as
-# possible to the values used later. (You also must example the
-# results gathered from Makefrag.inc to see they are the same
-# across all Android platforms, or add appropriate ifdefs.)
-# Since we no longer use the NDK, AOSP has to have been
-# built before using this script.
-
-CC=$ANDROID_TOOLCHAIN/*-gcc
-
-target_arch=$(cd $ANDROID_BUILD_TOP; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make --no-print-directory -f build/core/config.mk dumpvar-TARGET_ARCH)
-
-addvar CPPFLAGS \
- -isystem $aospdir/bionic/libc/arch-$target_arch/include \
- -isystem $aospdir/bionic/libc/include \
- -isystem $aospdir/bionic/libc/kernel/uapi \
- -isystem $aospdir/bionic/libc/kernel/uapi/asm-$target_arch \
- -isystem $aospdir/bionic/libm/include \
- -isystem $aospdir/bionic/libm/include/$target_arch \
- -D_FORTIFY_SOURCE=2 \
- -include $aospdir/build/core/combo/include/arch/linux-$target_arch/AndroidConfig.h \
- -I$aospdir/build/core/combo/include/arch/linux-$target_arch/ \
- -DANDROID -DNDEBUG -UDEBUG
-addvar CFLAGS \
- -fno-exceptions \
- -Wno-multichar \
- -fpic \
- -fPIE \
- -ffunction-sections \
- -fdata-sections \
- -funwind-tables \
- -fstack-protector \
- -Wa,--noexecstack \
- -Werror=format-security \
- -fno-short-enums \
- -Wno-unused-but-set-variable \
- -fno-builtin-sin \
- -fno-strict-volatile-bitfields \
- -Wno-psabi \
- -fmessage-length=0 \
- -W \
- -Wall \
- -Wno-unused \
- -Winit-self \
- -Wpointer-arith \
- -Werror=return-type \
- -Werror=non-virtual-dtor \
- -Werror=address \
- -Werror=sequence-point \
- -g \
- -Wstrict-aliasing=2 \
- -fgcse-after-reload \
- -frerun-cse-after-loop \
- -frename-registers \
- -Os \
- -fomit-frame-pointer \
- -fno-strict-aliasing
-addvar LDFLAGS \
- -nostdlib \
- -Bdynamic \
- -fPIE \
- -pie \
- -Wl,-dynamic-linker,/system/bin/linker \
- -Wl,--gc-sections \
- -Wl,-z,nocopyreloc \
- -Wl,-z,noexecstack \
- -Wl,-z,relro \
- -Wl,-z,now \
- -Wl,--warn-shared-textrel \
- -Wl,--fatal-warnings \
- -Wl,--no-undefined \
- $ANDROID_PRODUCT_OUT/obj/lib/crtbegin_dynamic.o
-addvar LIBS \
- -L$ANDROID_PRODUCT_OUT/obj/lib \
- -Wl,-rpath-link=$ANDROID_PRODUCT_OUT/obj/lib \
- -Wl,--no-whole-archive \
- $ANDROID_PRODUCT_OUT/obj/STATIC_LIBRARIES/libcompiler_rt-extras_intermediates/libcompiler_rt-extras.a \
- -lc \
- $ANDROID_PRODUCT_OUT/obj/lib/crtend_android.o
-
-
-### Flags used by test builds
-if test $mkmfmode = 1; then
- addvar CPPFLAGS '-DMKSHRC_PATH=\"/system/etc/mkshrc\"'
- addvar CPPFLAGS '-DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\"'
- addvar CPPFLAGS '-DMKSH_DEFAULT_TMPDIR=\"/data/local\"'
-fi
-
-### Override flags
-# Let the shell free all memory upon exiting
-addvar CPPFLAGS -DDEBUG_LEAKS
-# UTF-8 works nowadays
-addvar CPPFLAGS -DMKSH_ASSUME_UTF8
-# Reduce filedescriptor usage
-addvar CPPFLAGS -DMKSH_CONSERVATIVE_FDS
-# Leave out RCS ID strings from the binary
-addvar CPPFLAGS -DMKSH_DONT_EMIT_IDSTRING
-# No getpwnam() calls (affects "cd ~username/" only)
-addvar CPPFLAGS -DMKSH_NOPWNAM
-# Leave out the ulimit builtin
-#addvar CPPFLAGS -DMKSH_NO_LIMITS
-# Compile an extra small mksh (optional)
-#addvar CPPFLAGS -DMKSH_SMALL
-
-# Set target platform
-TARGET_OS=Android
-
-# Android-x86 does not have helper functions for ProPolice SSP
-# and AOSP adds the flags by itself (same for warning flags)
-HAVE_CAN_FNOSTRICTALIASING=0
-HAVE_CAN_FSTACKPROTECTORALL=0
-HAVE_CAN_WALL=0
-export HAVE_CAN_FNOSTRICTALIASING HAVE_CAN_FSTACKPROTECTORALL HAVE_CAN_WALL
-
-# even the idea of persistent history on a phone is funny
-HAVE_PERSISTENT_HISTORY=0; export HAVE_PERSISTENT_HISTORY
-
-# ... and run it!
-export CC CPPFLAGS CFLAGS LDFLAGS LIBS TARGET_OS
-sh ../src/Build.sh $args
-rv=$?
-test x"$args" = x"-r" && exit $rv
-test x0 = x"$rv" && mv -f Makefrag.inc ../
-cd ..
-rm -rf tmp
-exit $rv
#!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.695 2016/01/02 20:11:31 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.702 2016/08/10 18:20:16 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015, 2016
check_categories="$check_categories shell:legacy-yes"
add_cppflags -DMKSH_LEGACY_MODE
HAVE_PERSISTENT_HISTORY=0
- HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1 # from sh.h
fi
if test x"$srcdir" = x"."; then
: "${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
add_cppflags -DMKSH__NO_SYMLINK
check_categories="$check_categories nosymlink"
add_cppflags -DMKSH__NO_SETEUGID
- add_cppflags -DMKSH_CONSERVATIVE_FDS
add_cppflags -DMKSH_DISABLE_TTY_WARNING
;;
CYGWIN*)
FreeMiNT)
oswarn="; it has minor issues"
add_cppflags -D_GNU_SOURCE
- add_cppflags -DMKSH_CONSERVATIVE_FDS
: "${HAVE_SETLOCALE_CTYPE=0}"
;;
GNU)
Minix-vmd)
add_cppflags -DMKSH__NO_SETEUGID
add_cppflags -DMKSH_UNEMPLOYED
- 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}"
;;
Minix3)
add_cppflags -DMKSH_UNEMPLOYED
- add_cppflags -DMKSH_CONSERVATIVE_FDS
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
oswarn="; it needs libposix.a"
;;
esac
- add_cppflags -DMKSH_CONSERVATIVE_FDS
;;
Ninix3)
# similar to Minix3
add_cppflags -DMKSH_UNEMPLOYED
- add_cppflags -DMKSH_CONSERVATIVE_FDS
add_cppflags -DMKSH_NO_LIMITS
# but no idea what else could be needed
oswarn="; it has unknown issues"
oswarn="$oswarn$nl$TARGET_OS ${TARGET_OSREV}, please tell me what to do"
;;
esac
- add_cppflags -DMKSH_CONSERVATIVE_FDS
: "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}"
;;
skyos)
ULTRIX)
: "${CC=cc -YPOSIX}"
add_cppflags -DMKSH_TYPEDEF_SSIZE_T=int
- add_cppflags -DMKSH_CONSERVATIVE_FDS
: "${HAVE_SETLOCALE_CTYPE=0}"
;;
UnixWare|UNIX_SV)
# SCO UnixWare
- add_cppflags -DMKSH_CONSERVATIVE_FDS
: "${HAVE_SYS_SIGLIST=0}${HAVE__SYS_SIGLIST=0}"
;;
UWIN*)
:*) ;;
*:) CCC_LD=$CCC_CC; export CCC_LD ;;
esac
+ : "${HAVE_STRING_POOLING=i1}"
;;
dec)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
-dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
$LIBS -dumpversion`'
+ : "${HAVE_STRING_POOLING=i2}"
;;
hpcc)
vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
ac_flags 1 fwrapv -fwrapv
fi
+# “on demand” means: GCC version >= 4
+fd='if to rely on compiler for string pooling'
+ac_cache string_pooling || case $HAVE_STRING_POOLING in
+2) fx=' (on demand, cached)' ;;
+i1) fv=1 ;;
+i2) fv=2; fx=' (on demand)' ;;
+esac
+ac_testdone
+test x"$HAVE_STRING_POOLING" = x"0" || ac_cppflags
+
phase=x
# The following tests run with -Werror or similar (all compilers) if possible
NOWARN=$DOWARN
: "${HAVE_NICE=0}"
: "${HAVE_PERSISTENT_HISTORY=0}"
check_categories="$check_categories smksh"
- HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1 # from sh.h
fi
ac_ifcpp 'if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)' \
isset_MKSH_BINSH '' 'if invoking as sh should be handled specially' && \
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}"
-ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
- 'if traditional/conservative fd use is requested' && \
- check_categories="$check_categories convfds"
#ac_ifcpp 'ifdef MKSH_DISABLE_DEPRECATED' isset_MKSH_DISABLE_DEPRECATED '' \
# "if deprecated features are to be omitted" && \
# check_categories="$check_categories nodeprecated"
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=521
+add_cppflags -DMKSH_BUILD_R=530
$e $bi$me: Finished configuration testing, now producing output.$ao
echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh
if test $cm = makefile; then
- extras='emacsfn.h rlimits.opt sh.h sh_flags.opt var_spec.h'
+ extras='emacsfn.h exprtok.h rlimits.opt sh.h sh_flags.opt var_spec.h'
test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
gens= genq=
for file in $optfiles; do
MKSH_ASSUME_UTF8 (0=disabled, 1=enabled; default: unset)
MKSH_BINSHPOSIX if */sh or */-sh, enable set -o posix
MKSH_BINSHREDUCED if */sh or */-sh, enable set -o sh
-MKSH_CLRTOEOL_STRING "\033[K"
MKSH_CLS_STRING "\033[;H\033[J"
-MKSH_CONSERVATIVE_FDS fd 0-9 for scripts, shell only up to 31
MKSH_DEFAULT_EXECSHELL "/bin/sh" (do not change)
MKSH_DEFAULT_PROFILEDIR "/etc" (do not change)
MKSH_DEFAULT_TMPDIR "/tmp" (do not change)
-# $MirOS: src/bin/mksh/check.t,v 1.721 2016/01/20 21:34:09 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.751 2016/08/12 16:48:02 tg Exp $
# -*- mode: sh -*-
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# http://svnweb.freebsd.org/base/head/bin/test/tests/legacy_test.sh?view=co&content-type=text%2Fplain
#
# Integrated testsuites from:
-# (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
+# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout:
- @(#)MIRBSD KSH R52 2016/01/20
+ @(#)MIRBSD KSH R53 2016/08/12
description:
Check version of shell.
stdin:
category: shell:legacy-no
---
expected-stdout:
- @(#)LEGACY KSH R52 2016/01/20
+ @(#)LEGACY KSH R53 2016/08/12
description:
Check version of legacy shell.
stdin:
stdin:
alias X='case '
alias Y=Z
- X Y in 'Y') echo is y ;; Z) echo is z ; esac
+ X Y in 'Y') echo is y ;; Z) echo is z ;; esac
expected-stdout:
is z
---
expected-stdout:
20
---
+name: arith-prec-1
+description:
+ Prove arithmetic expressions with embedded parameter
+ substitutions cannot be parsed ahead of time
+stdin:
+ a='3 + 4'
+ print 1 $((2 * a)) .
+ print 2 $((2 * $a)) .
+expected-stdout:
+ 1 14 .
+ 2 10 .
+---
name: arith-div-assoc-1
description:
Check associativity of division operator
(echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>/dev/null || echo failed in 38
foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>/dev/null || echo failed in 39
foo="a b c"; (echo -n '40 '; ./pfs "${foo#a}"; echo .) 2>/dev/null || echo failed in 40
+ (foo() { return 100; }; foo; echo 41 ${#+${#:+${#?}}\ \}\}\}}) 2>/dev/null || echo failed in 41
expected-stdout:
1 }z
2 ''z}
38 xay / x'a'y .
39 x' / x' .
40 < b c> .
+ 41 3 }}}
---
name: expand-unglob-dblq
description:
(echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>/dev/null || \
echo "$1 QSTN brac -> error"
}
+ : '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
tl_norm 1 -
tl_norm 2 ''
tl_norm 3 x
(echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>/dev/null || \
echo "$1 QSTN brac -> error"
}
+ : '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
tl_norm 1 -
tl_norm 2 ''
tl_norm 3 x
---
name: expand-weird-1
description:
- Check corner case of trim expansion vs. $# vs. ${#var}
+ Check corner cases of trim expansion vs. $# vs. ${#var} vs. ${var?}
stdin:
set 1 2 3 4 5 6 7 8 9 10 11
echo ${#} # value of $#
echo ${##1} # $# trimmed 1
set 1 2 3 4 5 6 7 8 9 10 11 12
echo ${##1}
+ (exit 0)
+ echo $? = ${#?} .
+ (exit 111)
+ echo $? = ${#?} .
expected-stdout:
11
2
1
2
+ 0 = 1 .
+ 111 = 3 .
---
name: expand-weird-2
description:
- Check corner case of ${var?} vs. ${#var}
-stdin:
- (exit 0)
- echo $? = ${#?} .
- (exit 111)
- echo $? = ${#?} .
-expected-stdout:
- 0 = 1 .
- 111 = 3 .
+ Check more substitution and extension corner cases
+stdin:
+ :& set -C; pid=$$; sub=$!; flg=$-; set -- i; exec 3>x.tmp
+ #echo "D: !=$! #=$# \$=$$ -=$- ?=$?"
+ echo >&3 3 = s^${!-word} , ${#-word} , p^${$-word} , f^${--word} , ${?-word} .
+ echo >&3 4 = ${!+word} , ${#+word} , ${$+word} , ${-+word} , ${?+word} .
+ echo >&3 5 = s^${!=word} , ${#=word} , p^${$=word} , f^${-=word} , ${?=word} .
+ echo >&3 6 = s^${!?word} , ${#?word} , p^${$?word} , f^${-?word} , ${??word} .
+ echo >&3 7 = sl^${#!} , ${##} , pl^${#$} , fl^${#-} , ${#?} .
+ echo >&3 8 = sw^${%!} , ${%#} , pw^${%$} , fw^${%-} , ${%?} .
+ echo >&3 9 = ${!!} , s^${!#} , ${!$} , s^${!-} , s^${!?} .
+ echo >&3 10 = s^${!#pattern} , ${##pattern} , p^${$#pattern} , f^${-#pattern} , ${?#pattern} .
+ echo >&3 11 = s^${!%pattern} , ${#%pattern} , p^${$%pattern} , f^${-%pattern} , ${?%pattern} .
+ echo >&3 12 = $# : ${##} , ${##1} .
+ set --
+ echo >&3 14 = $# : ${##} , ${##1} .
+ set -- 1 2 3 4 5
+ echo >&3 16 = $# : ${##} , ${##1} .
+ set -- 1 2 3 4 5 6 7 8 9 a b c d e
+ echo >&3 18 = $# : ${##} , ${##1} .
+ exec 3>&-
+ <x.tmp sed \
+ -e "s/ pl^${#pid} / PID /g" -e "s/ sl^${#sub} / SUB /g" -e "s/ fl^${#flg} / FLG /g" \
+ -e "s/ pw^${%pid} / PID /g" -e "s/ sw^${%sub} / SUB /g" -e "s/ fw^${%flg} / FLG /g" \
+ -e "s/ p^$pid / PID /g" -e "s/ s^$sub / SUB /g" -e "s/ f^$flg / FLG /g"
+expected-stdout:
+ 3 = SUB , 1 , PID , FLG , 0 .
+ 4 = word , word , word , word , word .
+ 5 = SUB , 1 , PID , FLG , 0 .
+ 6 = SUB , 1 , PID , FLG , 0 .
+ 7 = SUB , 1 , PID , FLG , 1 .
+ 8 = SUB , 1 , PID , FLG , 1 .
+ 9 = ! , SUB , $ , SUB , SUB .
+ 10 = SUB , 1 , PID , FLG , 0 .
+ 11 = SUB , 1 , PID , FLG , 0 .
+ 12 = 1 : 1 , .
+ 14 = 0 : 1 , 0 .
+ 16 = 5 : 1 , 5 .
+ 18 = 14 : 2 , 4 .
---
name: expand-weird-3
description:
expected-stdout:
<~/x> </x> <~> <\~> <~><~> <~/x> <~//etc> <~/~>
---
+name: expand-bang-1
+description:
+ Check corner case of ${!?} with ! being var vs. op
+stdin:
+ echo ${!?}
+expected-exit: 1
+expected-stderr-pattern: /not set/
+---
+name: expand-bang-2
+description:
+ Check corner case of ${!var} vs. ${var op} with var=!
+stdin:
+ echo 1 $! .
+ echo 2 ${!#} .
+ echo 3 ${!#[0-9]} .
+ echo 4 ${!-foo} .
+ # get an at least three-digit bg pid
+ while :; do
+ :&
+ x=$!
+ if [[ $x != +([0-9]) ]]; then
+ echo >&2 "cannot test, pid '$x' not numeric"
+ echo >&2 report this with as many details as possible
+ exit 1
+ fi
+ [[ $x = [0-9][0-9][0-9]* ]] && break
+ done
+ y=${x#?}
+ t=$!; [[ $t = $x ]]; echo 5 $? .
+ t=${!#}; [[ $t = $x ]]; echo 6 $? .
+ t=${!#[0-9]}; [[ $t = $y ]]; echo 7 $? .
+ t=${!-foo}; [[ $t = $x ]]; echo 8 $? .
+ t=${!?bar}; [[ $t = $x ]]; echo 9 $? .
+expected-stdout:
+ 1 .
+ 2 .
+ 3 .
+ 4 foo .
+ 5 0 .
+ 6 0 .
+ 7 0 .
+ 8 0 .
+ 9 0 .
+---
name: expand-number-1
description:
Check that positional arguments do not overflow
expected-stdout:
1 .
---
+name: expand-slashes-1
+description:
+ Check that side effects in substring replacement are handled correctly
+stdin:
+ foo=n1n1n1n2n3
+ i=2
+ n=1
+ echo 1 ${foo//n$((n++))/[$((++i))]} .
+ echo 2 $n , $i .
+expected-stdout:
+ 1 [3][3][3]n2n3 .
+ 2 2 , 3 .
+---
+name: expand-slashes-2
+description:
+ Check that side effects in substring replacement are handled correctly
+stdin:
+ foo=n1n1n1n2n3
+ i=2
+ n=1
+ echo 1 ${foo@/n$((n++))/[$((++i))]} .
+ echo 2 $n , $i .
+expected-stdout:
+ 1 [3]n1n1[4][5] .
+ 2 5 , 5 .
+---
+name: expand-slashes-3
+description:
+ Check that we can access the replaced string
+stdin:
+ foo=n1n1n1n2n3
+ echo 1 ${foo@/n[12]/[$KSH_MATCH]} .
+expected-stdout:
+ 1 [n1][n1][n1][n2]n3 .
+---
name: eglob-bad-1
description:
Check that globbing isn't done when glob has syntax error
[[ -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)}
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
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
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
eval "$fnd"
foo
print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
+ x=y
+ foo
+ typeset -f foo
+ print -r -- "| vc={$vc} vd={$vd} |"
# check append
v=<<-
vapp1
} vc={=c u \x40=
} vd={=d $x \x40=
} |
+ function foo {
+ vc=<<-
+ =c $x \x40=
+ <<
+
+ vd=<<-""
+ =d $x \x40=
+
+
+ }
+ | vc={=c y \x40=
+ } vd={=d $x \x40=
+ } |
| vapp1^vapp2^ |
---
name: heredoc-12
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 <<XXX && echo "$(prefix B <<XXX
+ echo line 1
+ XXX
+ echo line 2)" && prefix C <<YYY
+ echo line 3
+ XXX
+ echo line 4)"
+ echo line 5
+ YYY
+ XXX
+expected-stdout:
+ A:echo line 3
+ B:echo line 1
+ line 2
+ C:echo line 4)"
+ C:echo line 5
+ x-en
+---
+name: heredoc-comsub-6
+description:
+ Check here documents and here strings can be used
+ without a specific command, like $(<…) (extension)
+stdin:
+ foo=bar
+ x=$(<<<EO${foo}F)
+ echo "3<$x>"
+ y=$(<<-EOF
+ hi!
+
+ $foo) is not a problem
+
+
+ EOF)
+ echo "7<$y>"
+expected-stdout:
+ 3<EObarF>
+ 7<hi!
+
+ bar) is not a problem>
+---
name: heredoc-subshell-1
description:
Tests for here documents in subshells, taken from Austin ML
expected-stderr-pattern:
/(.*can't unlink HISTFILE.*\n)?X*$/
---
+name: history-multiline
+description:
+ Check correct multiline history, Debian #783978
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!
+file-setup: file 644 "Env"
+ PS1=X
+ PS2=Y
+stdin:
+ for i in A B C
+ do
+ print $i
+ print $i
+ done
+ fc -l
+expected-stdout:
+ A
+ A
+ B
+ B
+ C
+ C
+ 1 for i in A B C
+ do
+ print $i
+ print $i
+ done
+expected-stderr-pattern:
+ /^XYYYYXX$/
+---
name: history-e-minus-1
description:
Check if more recent command is executed
ac_space=' '
ac_newline=$'\n'
---
+name: regression-67
+description:
+ Check that we can both break and use source on the same line
+stdin:
+ for s in s; do break; done; print -s s
+---
+name: regression-68
+description:
+ Check that all common arithmetic operators work as expected
+stdin:
+ echo 1 $(( a = 5 )) .
+ echo 2 $(( ++a )) , $(( a++ )) , $(( a )) .
+ echo 3 $(( --a )) , $(( a-- )) , $(( a )) .
+ echo 4 $(( a == 5 )) , $(( a == 6 )) .
+ echo 5 $(( a != 5 )) , $(( a != 6 )) .
+ echo 6 $(( a *= 3 )) .
+ echo 7 $(( a /= 5 )) .
+ echo 8 $(( a %= 2 )) .
+ echo 9 $(( a += 9 )) .
+ echo 10 $(( a -= 4 )) .
+ echo 11 $(( a <<= 1 )) .
+ echo 12 $(( a >>= 1 )) .
+ echo 13 $(( a &= 4 )) .
+ echo 14 $(( a ^= a )) .
+ echo 15 $(( a |= 5 )) .
+ echo 16 $(( 5 << 1 )) .
+ echo 17 $(( 5 >> 1 )) .
+ echo 18 $(( 5 <= 6 )) , $(( 5 <= 5 )) , $(( 5 <= 4 )) .
+ echo 19 $(( 5 >= 6 )) , $(( 5 >= 5 )) , $(( 5 >= 4 )) .
+ echo 20 $(( 5 < 6 )) , $(( 5 < 5 )) , $(( 5 < 4 )) .
+ echo 21 $(( 5 > 6 )) , $(( 5 > 5 )) , $(( 5 > 4 )) .
+ echo 22 $(( 0 && 0 )) , $(( 0 && 1 )) , $(( 1 && 0 )) , $(( 1 && 1 )) .
+ echo 23 $(( 0 || 0 )) , $(( 0 || 1 )) , $(( 1 || 0 )) , $(( 1 || 1 )) .
+ echo 24 $(( 5 * 3 )) .
+ echo 25 $(( 7 / 2 )) .
+ echo 26 $(( 5 % 5 )) , $(( 5 % 4 )) , $(( 5 % 1 )) , $(( 5 % -1 )) , $(( 5 % -2 )) .
+ echo 27 $(( 5 + 2 )) , $(( 5 + 0 )) , $(( 5 + -2 )) .
+ echo 28 $(( 5 - 2 )) , $(( 5 - 0 )) , $(( 5 - -2 )) .
+ echo 29 $(( 6 & 4 )) , $(( 6 & 8 )) .
+ echo 30 $(( 4 ^ 2 )) , $(( 4 ^ 4 )) .
+ echo 31 $(( 4 | 2 )) , $(( 4 | 4 )) , $(( 4 | 0 )) .
+ echo 32 $(( 0 ? 1 : 2 )) , $(( 3 ? 4 : 5 )) .
+ echo 33 $(( 5 , 2 , 3 )) .
+ echo 34 $(( ~0 )) , $(( ~1 )) , $(( ~~1 )) , $(( ~~2 )) .
+ echo 35 $(( !0 )) , $(( !1 )) , $(( !!1 )) , $(( !!2 )) .
+ echo 36 $(( (5) )) .
+expected-stdout:
+ 1 5 .
+ 2 6 , 6 , 7 .
+ 3 6 , 6 , 5 .
+ 4 1 , 0 .
+ 5 0 , 1 .
+ 6 15 .
+ 7 3 .
+ 8 1 .
+ 9 10 .
+ 10 6 .
+ 11 12 .
+ 12 6 .
+ 13 4 .
+ 14 0 .
+ 15 5 .
+ 16 10 .
+ 17 2 .
+ 18 1 , 1 , 0 .
+ 19 0 , 1 , 1 .
+ 20 1 , 0 , 0 .
+ 21 0 , 0 , 1 .
+ 22 0 , 0 , 0 , 1 .
+ 23 0 , 1 , 1 , 1 .
+ 24 15 .
+ 25 3 .
+ 26 0 , 1 , 0 , 0 , 1 .
+ 27 7 , 5 , 3 .
+ 28 3 , 5 , 7 .
+ 29 4 , 0 .
+ 30 6 , 0 .
+ 31 6 , 4 , 4 .
+ 32 2 , 4 .
+ 33 3 .
+ 34 -1 , -2 , 1 , 2 .
+ 35 1 , 0 , 1 , 1 .
+ 36 5 .
+---
+name: regression-69
+description:
+ Check that all non-lksh arithmetic operators work as expected
+category: shell:legacy-no
+stdin:
+ a=5 b=0x80000005
+ echo 1 $(( a ^<= 1 )) , $(( b ^<= 1 )) .
+ echo 2 $(( a ^>= 2 )) , $(( b ^>= 2 )) .
+ echo 3 $(( 5 ^< 1 )) .
+ echo 4 $(( 5 ^> 1 )) .
+expected-stdout:
+ 1 10 , 11 .
+ 2 -2147483646 , -1073741822 .
+ 3 10 .
+ 4 -2147483646 .
+---
name: readonly-0
description:
Ensure readonly is honoured for assignments and unset
expected-exit: 1
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\ 1^${__progname%.exe}\.*e*x*e*: \ 1PROG: \ 1" -e 's/^/STDOUT: /g' <o1
+ sed -e "s\ 1^${__progname%.exe}\.*e*x*e*: \ 1PROG: \ 1" -e 's/^/STDERR: /g' <o2
+ }
+ doit 'echo ${1x}'
+ doit 'echo "${1x}"'
+ doit 'echo ${1?}'
+ doit 'echo ${19?}'
+ doit 'echo ${!:?}'
+ doit -u 'echo ${*:?}' foo ""
+expected-stdout:
+ RETVAL: 1
+ STDERR: PROG: ${1x}: bad substitution
+ RETVAL: 1
+ STDERR: PROG: ${1x}: bad substitution
+ RETVAL: 1
+ STDERR: PROG: 1: parameter null or not set
+ RETVAL: 1
+ STDERR: PROG: 19: parameter null or not set
+ RETVAL: 1
+ STDERR: PROG: !: parameter null or not set
+ RETVAL: 1
+ STDERR: foo: ${*:?}: bad substitution
+---
name: xxx-param-_-1
# fails due to weirdness of execv stuff
category: !os:uwin-nt
2 off
3 done
---
+name: utf8bug-1
+description:
+ Ensure trailing combining characters are not lost
+stdin:
+ set -U
+ a=a
+ b=$'\u0301'
+ x=$a$b
+ print -r -- "<e$x>"
+ x=$a
+ x+=$b
+ print -r -- "<e$x>"
+ b=$'\u0301'b
+ x=$a
+ x+=$b
+ print -r -- "<e$x>"
+expected-stdout:
+ <eá>
+ <eá>
+ <eáb>
+---
name: aliases-1
description:
Check if built-in shell aliases are okay
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>'
+ {
+ print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
+ print '<\x00>'
+ print '<\x01>'
+ print '<\u0000>'
+ print '<\u0001>'
+ } | {
+ # integer-base-one-3Ar
+ typeset -Uui16 -Z11 pos=0
+ typeset -Uui16 -Z5 hv=2147483647
+ dasc=
+ 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 "$dasc|"
+ print -n "${pos#16#} "
+ dasc=' |'
+ fi
+ print -n "${hv#16#} "
+ if (( (hv < 32) || (hv > 126) )); then
+ dasc=$dasc.
+ else
+ dasc=$dasc${line[i-1]#1#}
+ fi
+ (( (pos++ & 15) == 7 )) && print -n -- '- '
+ done
+ fi
+ while (( pos & 15 )); do
+ print -n ' '
+ (( (pos++ & 15) == 7 )) && print -n -- '- '
+ done
+ (( hv == 2147483647 )) || print "$dasc|"
+ }
expected-stdout:
- <däÛÃ\9bâ\82¬Ã\9b@>
- <\0>
- <\ 1>
- <\0>
- <\ 1>
+ 00000000 3C 64 E4 DB C3 9B E2 82 - AC C3 9B 40 3E 0A 3C 00 |<d.........@>.<.|
+ 00000010 3E 0A 3C 01 3E 0A 3C 00 - 3E 0A 3C 01 3E 0A |>.<.>.<.>.<.>.|
---
name: print-bksl-c
description:
expected-stdout-pattern:
/^4 3 2 <> <\0>$/
---
+name: print-array
+description:
+ Check that print -A works as expected
+stdin:
+ print -An 0x20AC 0xC3 0xBC 8#101
+ set -U
+ print -A 0x20AC 0xC3 0xBC 8#102
+expected-stdout:
+ ¬Ã¼Aâ\82¬Ã\83¼B
+---
name: print-escapes
description:
Check backslash expansion by the print builtin
expected-stderr-pattern:
/\.: missing argument.*\n.*source: missing argument/
---
+name: dot-errorlevel
+description:
+ Ensure dot resets $?
+stdin:
+ :>dotfile
+ (exit 42)
+ . ./dotfile
+ echo 1 $? .
+expected-stdout:
+ 1 0 .
+---
name: alias-function-no-conflict
description:
make aliases not conflict with function definitions
2 = bar .
3 = bar .
---
-name: mkshiop-1
-description:
- Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
- read -u10 foo 10<<< bar
- echo x$foo
-expected-stdout:
- xbar
----
-name: mkshiop-2
-description:
- Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
- exec 12>foo
- print -u12 bar
- echo baz >&12
- cat foo
-expected-stdout:
- bar
- baz
----
name: oksh-eval
description:
Check expansions.
description:
Verify that file descriptors > 2 are private for Korn shells
AT&T ksh93 does this still, which means we must keep it as well
+ XXX fails on some old Perl installations
+need-pass: no
category: shell:legacy-no
stdin:
cat >cld <<-EOF
description:
Verify that file descriptors > 2 are not private for POSIX shells
See Debian Bug #154540, Closes: #499139
+ XXX fails on some old Perl installations
+need-pass: no
stdin:
cat >cld <<-EOF
#!$__perlname
done
expected-stderr-pattern: /.*-x.*option/
---
+name: utilities-getopts-3
+description:
+ Check unsetting OPTARG
+stdin:
+ set -- -x arg -y
+ getopts x:y opt && echo "${OPTARG-unset}"
+ getopts x:y opt && echo "${OPTARG-unset}"
+expected-stdout:
+ arg
+ unset
+---
name: wcswidth-1
description:
Check the new wcswidth feature
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
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:
# $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.104 2015/12/31 21:00:12 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.108 2016/07/26 22:03:41 tg Exp $
#-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015
+# 2011, 2012, 2013, 2014, 2015, 2016
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
(( e )) && REPLY+="$e|"
REPLY+=${USER}@${HOSTNAME%%.*}:
- \typeset d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/\~}
- \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
+ \typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
+ d=${d%/}; \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
REPLY+=$p$d
}
# 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
- llen=${%line}
- (( llen == -1 )) && llen=${#line}
- (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
- if (( (curlin += llen) >= LINES )); then
- \builtin print -n -- '\e[7m--more--\e[0m'
- \read -u1 || \exit $?
- [[ $REPLY = [Qq]* ]] && \exit 0
- curlin=$llen
- fi
- \builtin print -r -- "$line"
- done
- )
-}
+smores() (
+ \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
+ \builtin print -n -- '\e[7m--more--\e[0m'
+ \read -u1 || \exit $?
+ [[ $REPLY = [Qq]* ]] && \exit 0
+ curlin=$llen
+ fi
+ \builtin print -r -- "$line"
+ done
+)
# base64 encoder and decoder, RFC compliant, NUL safe, not EBCDIC safe
function Lb64decode {
((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
((Lbafh_v << 1) & 0xFEFEFEFE) ))
- ((# Lbafh_v = t ^ (t >>> 8) ^ (Lbafh_v >>> 8) ^ \
- (Lbafh_v >>> 16) ^ (Lbafh_v >>> 24) ))
+ ((# Lbafh_v = t ^ (t ^> 8) ^ (Lbafh_v ^> 8) ^ \
+ (Lbafh_v ^> 16) ^ (Lbafh_v ^> 24) ))
\:
}
# accumulate functions from dot.mkshrc, in definition order
i_func[nfunc++]=hd
i_func[nfunc++]=chpwd
+ i_func[nfunc++]=cd
i_func[nfunc++]=cd_csh
i_func[nfunc++]=dirs
i_func[nfunc++]=popd
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
+ * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#ifndef MKSH_NO_CMDLINE_EDITING
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.292 2015/10/09 16:11:13 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.306 2016/08/01 18:42:40 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
#ifndef MKSH_CLS_STRING
#define MKSH_CLS_STRING "\033[;H\033[J"
#endif
-#ifndef MKSH_CLRTOEOL_STRING
-#define MKSH_CLRTOEOL_STRING "\033[K"
-#endif
/* tty driver characters we are interested in */
-typedef struct {
- int erase;
- int kill;
- int werase;
- int intr;
- int quit;
- int eof;
-} X_chars;
-
-static X_chars edchars;
+#define EDCHAR_DISABLED 0xFFFFU
+#define EDCHAR_INITIAL 0xFFFEU
+static struct {
+ unsigned short erase;
+ unsigned short kill;
+ unsigned short werase;
+ unsigned short intr;
+ unsigned short quit;
+ unsigned short eof;
+} edchars;
+
+#define isched(x,e) ((unsigned short)(unsigned char)(x) == (e))
+#define isedchar(x) (!((x) & ~0xFF))
+#ifndef _POSIX_VDISABLE
+#define toedchar(x) ((unsigned short)(unsigned char)(x))
+#else
+#define toedchar(x) (((_POSIX_VDISABLE != -1) && ((x) == _POSIX_VDISABLE)) ? \
+ ((unsigned short)EDCHAR_DISABLED) : \
+ ((unsigned short)(unsigned char)(x)))
+#endif
/* x_cf_glob() flags */
#define XCF_COMMAND BIT(0) /* Do command completion */
static int modified; /* buffer has been "modified" */
static char *holdbufp; /* place to hold last edit buffer */
+/* 0=dumb 1=tmux (for now) */
+static bool x_term_mode;
+
+static void x_adjust(void);
static int x_getc(void);
static void x_putcf(int);
static void x_modified(void);
static size_t x_longest_prefix(int, char * const *);
static void x_glob_hlp_add_qchar(char *);
static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
-static int x_basename(const char *, const char *);
+static size_t x_basename(const char *, const char *);
static void x_free_words(int, char **);
static int x_escape(const char *, size_t, int (*)(const char *, size_t));
static int x_emacs(char *);
static int x_locate_word(const char *, int, int, int *, bool *);
static int x_e_getmbc(char *);
-static int x_e_rebuildline(const char *);
/* +++ generic editing functions +++ */
/* redraw line in Emacs mode */
xx_cols = x_cols;
x_init_prompt(false);
- x_e_rebuildline(MKSH_CLRTOEOL_STRING);
+ x_adjust();
}
}
#endif
x_print_expansions(int nwords, char * const *words, bool is_command)
{
bool use_copy = false;
- int prefix_len;
+ size_t prefix_len;
XPtrV l = { NULL, 0, 0 };
/*
*--cp = '/';
} else {
/* ok, expand and replace */
- cp = shf_smprintf("%s/%s", dp, cp);
+ cp = shf_smprintf(Tf_sSs, dp, cp);
if (magic_flag)
afree(s, ATEMP);
s = cp;
source = s;
if (yylex(ONEWORD | LQCHAR) != LWORD) {
source = sold;
- internal_warningf("%s: %s", "fileglob", "bad substitution");
+ internal_warningf(Tfg_badsubst);
return (0);
}
source = sold;
/* Data structure used in x_command_glob() */
struct path_order_info {
char *word;
- int base;
- int path_order;
+ size_t base;
+ size_t path_order;
};
/* Compare routine used in x_command_glob() */
const struct path_order_info *b = (const struct path_order_info *)bb;
int t;
- t = strcmp(a->word + a->base, b->word + b->base);
- return (t ? t : a->path_order - b->path_order);
+ if ((t = strcmp(a->word + a->base, b->word + b->base)))
+ return (t);
+ if (a->path_order > b->path_order)
+ return (1);
+ if (a->path_order < b->path_order)
+ return (-1);
+ return (0);
}
static int
glob_table(pat, &w, &l->funs);
glob_path(flags, pat, &w, path);
- if ((fpath = str_val(global("FPATH"))) != null)
+ if ((fpath = str_val(global(TFPATH))) != null)
glob_path(flags, pat, &w, fpath);
nwords = XPsize(w);
* /// 2
* 0
*/
-static int
+static size_t
x_basename(const char *s, const char *se)
{
const char *p;
x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
{
size_t add = 0, wlen = len;
- const char *ifs = str_val(local("IFS", 0));
int rval = 0;
while (wlen - add > 0)
if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
- vstrchr(ifs, s[add])) {
+ ctype(s[add], C_IFS)) {
if (putbuf_func(s, add) != 0) {
rval = -1;
break;
static void x_goto(char *);
static char *x_bs0(char *, char *) MKSH_A_PURE;
static void x_bs3(char **);
-static int x_size_str(char *);
static int x_size2(char *, char **);
static void x_zots(char *);
static void x_zotc3(char **);
#endif
static int x_match(char *, char *);
static void x_redraw(int);
-static void x_push(int);
+static void x_push(size_t);
static char *x_mapin(const char *, Area *);
static char *x_mapout(int);
static void x_mapout2(int, char **);
static void x_print(int, int);
-static void x_adjust(void);
static void x_e_ungetc(int);
static int x_e_getc(void);
static void x_e_putc2(int);
static int x_fold_case(int);
#endif
static char *x_lastcp(void);
+static void x_lastpos(void);
static void do_complete(int, Comp_type);
static size_t x_nb2nc(size_t) MKSH_A_PURE;
#endif
#ifndef MKSH_SMALL
/* more non-standard ones */
+ { XFUNC_eval_region, 1, CTRL('E') },
{ XFUNC_edit_line, 2, 'e' }
#endif
};
return (pos);
}
+/*
+ * minimum required space to work with on a line - if the prompt
+ * leaves less space than this on a line, the prompt is truncated
+ */
+#define MIN_EDIT_SPACE 7
+
static void
x_init_prompt(bool doprint)
{
x_lastcp();
x_adj_ok = tobool(xcp >= xlp);
x_zots(cp);
- /* has x_adjust() been called? */
- if (adj == x_adj_done) {
- /* no */
- cp = xlp;
- while (cp > xcp)
- x_bs3(&cp);
- }
- if (xlp == xep - 1)
- x_redraw(xx_cols);
+ if (adj == x_adj_done)
+ /* x_adjust() has not been called */
+ x_lastpos();
x_adj_ok = true;
return (0);
}
/*x_goto(xcp);*/
x_adj_ok = true;
xlp_valid = false;
- cp = x_lastcp();
- while (cp > xcp)
- x_bs3(&cp);
-
+ x_lastpos();
x_modified();
return;
}
x_goto(char *cp)
{
cp = cp >= xep ? xep : x_bs0(cp, xbuf);
- if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
+ if (cp < xbp || cp >= utf_skipcols(xbp, x_displen, NULL)) {
/* we are heading off screen */
xcp = cp;
x_adjust();
}
static int
-x_size_str(char *cp)
-{
- int size = 0;
- while (*cp)
- size += x_size2(cp, &cp);
- return (size);
-}
-
-static int
x_size2(char *cp, char **dcp)
{
uint8_t c = *(unsigned char *)cp;
if (c == '\t') {
/* Kludge, tabs are always four spaces. */
- x_e_puts(" ");
+ x_e_puts(T4spaces);
(*cp)++;
} else if (ISCTRL(c) && /* but not C1 */ c < 0x80) {
x_e_putc2('^');
static int
x_end_of_text(int c MKSH_A_UNUSED)
{
- char tmp = edchars.eof;
- char *cp = &tmp;
+ unsigned char tmp;
+ char *cp = (void *)&tmp;
+ tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
+ (unsigned char)CTRL('D');
x_zotc3(&cp);
x_putc('\r');
x_putc('\n');
static void
x_load_hist(char **hp)
{
- int oldsize;
char *sp = NULL;
if (hp == histptr + 1) {
if (sp == NULL)
sp = *hp;
x_histp = hp;
- oldsize = x_size_str(xbuf);
if (modified)
strlcpy(holdbufp, xbuf, LINE);
strlcpy(xbuf, sp, xend - xbuf);
xbp = xbuf;
xep = xcp = xbuf + strlen(xbuf);
- xlp_valid = false;
- if (xep <= x_lastcp()) {
- x_redraw(oldsize);
- }
- x_goto(xep);
+ x_adjust();
modified = 0;
}
}
}
if (offset < 0)
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
static int
x_del_line(int c MKSH_A_UNUSED)
{
- int i, j;
-
*xep = 0;
- i = xep - xbuf;
- j = x_size_str(xbuf);
- xcp = xbuf;
- x_push(i);
+ x_push(xep - (xcp = xbuf));
xlp = xbp = xep = xbuf;
xlp_valid = true;
*xcp = 0;
xmp = NULL;
- x_redraw(j);
+ x_redraw('\r');
x_modified();
return (KSTD);
}
static int
x_draw_line(int c MKSH_A_UNUSED)
{
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
static int
-x_e_rebuildline(const char *clrstr)
+x_cls(int c MKSH_A_UNUSED)
{
- shf_puts(clrstr, shl_out);
- x_adjust();
+ shf_puts(MKSH_CLS_STRING, shl_out);
+ x_redraw(0);
return (KSTD);
}
-static int
-x_cls(int c MKSH_A_UNUSED)
+/*
+ * clear line from x_col (current cursor position) to xx_cols - 2,
+ * then output lastch, then go back to x_col; if lastch is space,
+ * clear with termcap instead of spaces, or not if line_was_cleared;
+ * lastch MUST be an ASCII character with wcwidth(lastch) == 1
+ */
+static void
+x_clrtoeol(int lastch, bool line_was_cleared)
{
- return (x_e_rebuildline(MKSH_CLS_STRING));
+ int col;
+
+ if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
+ shf_puts("\033[K", shl_out);
+ line_was_cleared = true;
+ }
+ if (lastch == ' ' && line_was_cleared)
+ return;
+
+ col = x_col;
+ while (col < (xx_cols - 2)) {
+ x_putc(' ');
+ ++col;
+ }
+ x_putc(lastch);
+ ++col;
+ while (col > x_col) {
+ x_putc('\b');
+ --col;
+ }
}
-/*
- * Redraw (part of) the line. If limit is < 0, the everything is redrawn
- * on a NEW line, otherwise limit is the screen column up to which needs
- * redrawing.
- */
+/* output the prompt, assuming a line has just been started */
static void
-x_redraw(int limit)
+x_pprompt(void)
{
- int i, j;
- char *cp;
+ if (prompt_trunc != -1)
+ pprompt(prompt, prompt_trunc);
+ x_col = pwidth;
+}
+
+/* output CR, then redraw the line, clearing to EOL if needed (cr ≠ 0, LF) */
+static void
+x_redraw(int cr)
+{
+ int lch;
x_adj_ok = false;
- if (limit == -1)
- x_e_putc2('\n');
- else
- x_e_putc2('\r');
+ /* clear the line */
+ x_e_putc2(cr ? cr : '\r');
x_flush();
- if (xbp == xbuf) {
- if (prompt_trunc != -1)
- pprompt(prompt, prompt_trunc);
- x_col = pwidth;
- }
+ /* display the prompt */
+ if (xbp == xbuf)
+ x_pprompt();
x_displen = xx_cols - 2 - x_col;
+ /* display the line content */
xlp_valid = false;
x_zots(xbp);
- if (xbp != xbuf || xep > xlp)
- limit = xx_cols;
- if (limit >= 0) {
- if (xep > xlp)
- /* we fill the line */
- i = 0;
- else {
- char *cpl = xbp;
-
- i = limit;
- while (cpl < xlp)
- i -= x_size2(cpl, &cpl);
- }
-
- j = 0;
- while ((j < i) || (x_col < (xx_cols - 2))) {
- if (!(x_col < (xx_cols - 2)))
- break;
- x_e_putc2(' ');
- j++;
- }
- i = ' ';
- if (xep > xlp) {
- /* more off screen */
- if (xbp > xbuf)
- i = '*';
- else
- i = '>';
- } else if (xbp > xbuf)
- i = '<';
- x_e_putc2(i);
- j++;
- while (j--)
- x_e_putc2('\b');
- }
- cp = xlp;
- while (cp > xcp)
- x_bs3(&cp);
+ /* check whether there is more off-screen */
+ lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' ';
+ /* clear the rest of the line */
+ x_clrtoeol(lch, !cr || cr == '\n');
+ /* go back to actual cursor position */
+ x_lastpos();
x_adj_ok = true;
- return;
}
static int
}
static void
-x_push(int nchars)
+x_push(size_t nchars)
{
afree(killstack[killsp], AEDIT);
strndupx(killstack[killsp], xcp, nchars, AEDIT);
killtp--;
if (killstack[killtp] == 0) {
x_e_puts("\nnothing to yank");
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
xmp = xcp;
killstack[killtp] == 0) {
killtp = killsp;
x_e_puts("\nyank something first");
- x_redraw(-1);
+ x_redraw('\n');
return (KSTD);
}
len = strlen(killstack[killtp]);
shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
if (XFUNC_VALUE(f) != XFUNC_ins_string)
#endif
- shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
+ shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
#ifndef MKSH_SMALL
else
shprintf("'%s'\n", x_atab[prefix][key]);
if (list) {
for (f = 0; f < NELEM(x_ftab); f++)
if (!(x_ftab[f].xf_flags & XF_NOBIND))
- shprintf("%s\n", x_ftab[f].xf_name);
+ shprintf(Tf_sN, x_ftab[f].xf_name);
return (0);
}
if (a1 == NULL) {
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
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);
}
}
i = 0;
while (i < nwords) {
if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
- (++i < nwords && x_ins(" ") < 0)) {
+ (++i < nwords && x_ins(T1space) < 0)) {
x_e_putc2(7);
return (KSTD);
}
*/
if (nwords == 1 && words[0][nlen - 1] != '/' &&
!(flags & XCF_IS_NOSPACE)) {
- x_ins(" ");
+ x_ins(T1space);
}
x_free_words(nwords, words);
x_adjust_out:
xlp_valid = false;
- x_redraw(xx_cols);
+ x_redraw('\r');
x_flush();
}
static int
x_comment(int c MKSH_A_UNUSED)
{
- int oldsize = x_size_str(xbuf);
ssize_t len = xep - xbuf;
int ret = x_do_comment(xbuf, xend - xbuf, &len);
xep = xbuf + len;
*xep = '\0';
xcp = xbp = xbuf;
- x_redraw(oldsize);
+ x_redraw('\r');
if (ret > 0)
return (x_newline('\n'));
}
{
char *o_xbuf = xbuf, *o_xend = xend;
char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
- int lim = x_lastcp() - xbp;
- size_t vlen;
char *v;
strdupx(v, KSH_VERSION, ATEMP);
xbuf = xbp = xcp = v;
- xend = xep = v + (vlen = strlen(v));
- x_redraw(lim);
+ xend = xep = v + strlen(v);
+ x_redraw('\r');
x_flush();
c = x_e_getc();
xbp = o_xbp;
xep = o_xep;
xcp = o_xcp;
- x_redraw((int)vlen);
+ x_redraw('\r');
if (c < 0)
return (KSTD);
x_arg = source->line - (histptr - x_histp);
}
if (x_arg)
- shf_snprintf(xbuf, xend - xbuf, "%s %d",
+ shf_snprintf(xbuf, xend - xbuf, Tf_sd,
"fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
else
strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
* NAME:
* x_lastcp - last visible char
*
- * SYNOPSIS:
- * x_lastcp()
- *
* DESCRIPTION:
* This function returns a pointer to that char in the
* edit buffer that will be the last displayed on the
- * screen. The sequence:
- *
- * cp = x_lastcp();
- * while (cp > xcp)
- * x_bs3(&cp);
- *
- * Will position the cursor correctly on the screen.
- *
- * RETURN VALUE:
- * cp or NULL
+ * screen.
*/
static char *
x_lastcp(void)
return (xlp);
}
+/* correctly position the cursor on the screen from end of visible area */
+static void
+x_lastpos(void)
+{
+ char *cp = x_lastcp();
+
+ while (cp > xcp)
+ x_bs3(&cp);
+}
+
static void
x_mode(bool onoff)
{
if (onoff) {
x_mkraw(tty_fd, NULL, false);
- edchars.erase = tty_state.c_cc[VERASE];
- edchars.kill = tty_state.c_cc[VKILL];
- edchars.intr = tty_state.c_cc[VINTR];
- edchars.quit = tty_state.c_cc[VQUIT];
- edchars.eof = tty_state.c_cc[VEOF];
+ edchars.erase = toedchar(tty_state.c_cc[VERASE]);
+ edchars.kill = toedchar(tty_state.c_cc[VKILL]);
+ edchars.intr = toedchar(tty_state.c_cc[VINTR]);
+ edchars.quit = toedchar(tty_state.c_cc[VQUIT]);
+ edchars.eof = toedchar(tty_state.c_cc[VEOF]);
#ifdef VWERASE
- edchars.werase = tty_state.c_cc[VWERASE];
+ edchars.werase = toedchar(tty_state.c_cc[VWERASE]);
#else
edchars.werase = 0;
#endif
if (!edchars.werase)
edchars.werase = CTRL('W');
-#ifdef _POSIX_VDISABLE
- /* Convert unset values to internal 'unset' value */
- if (edchars.erase == _POSIX_VDISABLE)
- edchars.erase = -1;
- if (edchars.kill == _POSIX_VDISABLE)
- edchars.kill = -1;
- if (edchars.intr == _POSIX_VDISABLE)
- edchars.intr = -1;
- if (edchars.quit == _POSIX_VDISABLE)
- edchars.quit = -1;
- if (edchars.eof == _POSIX_VDISABLE)
- edchars.eof = -1;
- if (edchars.werase == _POSIX_VDISABLE)
- edchars.werase = -1;
-#endif
-
- if (edchars.erase >= 0) {
+ if (isedchar(edchars.erase)) {
bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
}
- if (edchars.kill >= 0)
+ if (isedchar(edchars.kill))
bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
- if (edchars.werase >= 0)
+ if (isedchar(edchars.werase))
bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
- if (edchars.intr >= 0)
+ if (isedchar(edchars.intr))
bind_if_not_bound(0, edchars.intr, XFUNC_abort);
- if (edchars.quit >= 0)
+ if (isedchar(edchars.quit))
bind_if_not_bound(0, edchars.quit, XFUNC_noop);
} else
mksh_tcset(tty_fd, &tty_state);
if (c == -1)
break;
if (state != VLIT) {
- if (c == edchars.intr || c == edchars.quit) {
+ if (isched(c, edchars.intr) ||
+ isched(c, edchars.quit)) {
/* pretend we got an interrupt */
x_vi_zotc(c);
x_flush();
- trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
+ trapsig(isched(c, edchars.intr) ?
+ SIGINT : SIGQUIT);
x_mode(false);
unwind(LSHELL);
- } else if (c == edchars.eof && state != VVERSION) {
+ } else if (isched(c, edchars.eof) &&
+ state != VVERSION) {
if (es->linelen == 0) {
- x_vi_zotc(edchars.eof);
+ x_vi_zotc(c);
c = -1;
break;
}
memcpy(srchpat, locpat, srchlen + 1);
}
state = VCMD;
- } else if (ch == edchars.erase || ch == CTRL('h')) {
+ } else if (isched(ch, edchars.erase) || ch == CTRL('h')) {
if (srchlen != 0) {
srchlen--;
es->linelen -= char_len(locpat[srchlen]);
restore_cbuf();
state = VNORMAL;
refresh(0);
- } else if (ch == edchars.kill) {
+ } else if (isched(ch, edchars.kill)) {
srchlen = 0;
es->linelen = 1;
es->cursor = 1;
refresh(0);
return (0);
- } else if (ch == edchars.werase) {
+ } else if (isched(ch, edchars.werase)) {
unsigned int i, n;
struct edstate new_es, *save_es;
{
int tcursor;
- if (ch == edchars.erase || ch == CTRL('h')) {
+ if (isched(ch, edchars.erase) || ch == CTRL('h')) {
if (insert == REPLACE) {
if (es->cursor == undo->cursor) {
vi_error();
expanded = NONE;
return (0);
}
- if (ch == edchars.kill) {
+ if (isched(ch, edchars.kill)) {
if (es->cursor != 0) {
inslen = 0;
memmove(es->cbuf, &es->cbuf[es->cursor],
expanded = NONE;
return (0);
}
- if (ch == edchars.werase) {
+ if (isched(ch, edchars.werase)) {
if (es->cursor != 0) {
tcursor = backword(1);
memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
(hlast - hnum);
}
if (argcnt)
- shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
+ shf_snprintf(es->cbuf, es->cbufsize, Tf_sd,
"fc -e ${VISUAL:-${EDITOR:-vi}} --",
argcnt);
else
argcnt++;
p++;
}
- if (putbuf(" ", 1, false) != 0 ||
+ if (putbuf(T1space, 1, false) != 0 ||
putbuf(sp, argcnt, false) != 0) {
if (es->cursor != 0)
es->cursor--;
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':
ncursor = es->cursor;
while (ncursor < es->linelen && argcnt--) {
if (ksh_isalnux(es->cbuf[ncursor]))
- while (ksh_isalnux(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < es->linelen &&
+ ksh_isalnux(es->cbuf[ncursor]))
ncursor++;
else if (!ksh_isspace(es->cbuf[ncursor]))
- while (!ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < es->linelen &&
+ !ksh_isalnux(es->cbuf[ncursor]) &&
+ !ksh_isspace(es->cbuf[ncursor]))
ncursor++;
- while (ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < es->linelen &&
+ ksh_isspace(es->cbuf[ncursor]))
ncursor++;
}
return (ncursor);
ncursor = es->cursor;
while (ncursor < es->linelen && argcnt--) {
- while (!ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < es->linelen &&
+ !ksh_isspace(es->cbuf[ncursor]))
ncursor++;
- while (ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
+ while (ncursor < es->linelen &&
+ ksh_isspace(es->cbuf[ncursor]))
ncursor++;
}
return (ncursor);
}
(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)
x_putc('\r');
x_putc('\n');
}
- if (prompt_trunc != -1)
- pprompt(prompt, prompt_trunc);
- x_col = pwidth;
+ x_pprompt();
morec = ' ';
}
if (col < x_col) {
if (col + 1 < x_col - col) {
x_putc('\r');
- if (prompt_trunc != -1)
- pprompt(prompt, prompt_trunc);
- x_col = pwidth;
+ x_pprompt();
while (x_col++ < col)
x_putcf(*wb++);
} else {
rval = -1;
break;
}
- if (++i < nwords && putbuf(" ", 1, false) != 0) {
+ if (++i < nwords && putbuf(T1space, 1, false) != 0) {
rval = -1;
break;
}
*/
if (match_len > 0 && match[match_len - 1] != '/' &&
!(flags & XCF_IS_NOSPACE))
- rval = putbuf(" ", 1, false);
+ rval = putbuf(T1space, 1, false);
}
x_free_words(nwords, words);
int i, j;
/*
- * Set edchars to -2 to force initial binding, except
- * we need default values for some deficient systems…
+ * set edchars to force initial binding, except we need
+ * default values for ^W for some deficient systems…
*/
edchars.erase = edchars.kill = edchars.intr = edchars.quit =
- edchars.eof = -2;
- /* ^W */
+ edchars.eof = EDCHAR_INITIAL;
edchars.werase = 027;
/* command line editing specific memory allocation */
afreeall(AEDIT);
}
#endif
+
+void
+x_initterm(const char *termtype)
+{
+ /* default must be 0 (bss) */
+ x_term_mode = 0;
+ /* this is what tmux uses, don't ask me about it */
+ if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7))
+ x_term_mode = 1;
+}
+
+#ifndef MKSH_SMALL
+static char *
+x_eval_region_helper(const char *cmd, size_t len)
+{
+ char * volatile cp;
+ newenv(E_ERRH);
+
+ if (!kshsetjmp(e->jbuf)) {
+ char *wds = alloc(len + 3, ATEMP);
+
+ wds[0] = FUNSUB;
+ memcpy(wds + 1, cmd, len);
+ wds[len + 1] = '\0';
+ wds[len + 2] = EOS;
+
+ cp = evalstr(wds, DOSCALAR);
+ strdupx(cp, cp, AEDIT);
+ } else
+ cp = NULL;
+ quitenv(NULL);
+ return (cp);
+}
+
+static int
+x_eval_region(int c MKSH_A_UNUSED)
+{
+ char *evbeg, *evend, *cp;
+ size_t newlen;
+ /* only for LINE overflow checking */
+ size_t restlen;
+
+ if (xmp == NULL) {
+ evbeg = xbuf;
+ evend = xep;
+ } else if (xmp < xcp) {
+ evbeg = xmp;
+ evend = xcp;
+ } else {
+ evbeg = xcp;
+ evend = xmp;
+ }
+
+ x_e_putc2('\r');
+ x_clrtoeol(' ', false);
+ x_flush();
+ x_mode(false);
+ cp = x_eval_region_helper(evbeg, evend - evbeg);
+ x_mode(true);
+
+ if (cp == NULL) {
+ /* command cannot be parsed */
+ x_eval_region_err:
+ x_e_putc2(7);
+ x_redraw('\r');
+ return (KSTD);
+ }
+
+ newlen = strlen(cp);
+ restlen = xep - evend;
+ /* check for LINE overflow, until this is dynamically allocated */
+ if (evbeg + newlen + restlen >= xend)
+ goto x_eval_region_err;
+
+ xmp = evbeg;
+ xcp = evbeg + newlen;
+ xep = xcp + restlen;
+ memmove(xcp, evend, restlen + /* NUL */ 1);
+ memcpy(xmp, cp, newlen);
+ afree(cp, AEDIT);
+ x_adjust();
+ x_modified();
+ return (KSTD);
+}
+#endif /* !MKSH_SMALL */
#endif /* !MKSH_NO_CMDLINE_EDITING */
/*-
- * Copyright (c) 2009, 2010, 2015
+ * Copyright (c) 2009, 2010, 2015, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
*/
#if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.7 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.9 2016/07/26 21:50:30 tg Exp $");
#define FN(cname,sname,flags) static int x_##cname(int);
#elif defined(EMACSFN_ENUMS)
#define FN(cname,sname,flags) XFUNC_##cname,
FN(enumerate, "list", 0)
FN(eot_del, "eot-or-delete", XF_ARG)
FN(error, "error", 0)
+#ifndef MKSH_SMALL
+FN(eval_region, "evaluate-region", 0)
+#endif
FN(expand, "expand-file", 0)
#ifndef MKSH_SMALL
FN(fold_capitalise, "capitalize-word", XF_ARG)
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.180 2016/01/19 23:12:12 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.192 2016/08/01 21:38:01 tg Exp $");
/*
* string expansion
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));
rv = (char *) *XPptrv(w);
break;
default:
- rv = evalstr(cp, f&~DOGLOB);
+ rv = evalstr(cp, f & ~DOGLOB);
break;
}
XPfree(w);
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);
+ str = snptreef(NULL, 64, Tf_S, beg);
afree(beg, ATEMP);
- errorf("%s: %s", str, "bad substitution");
+ errorf(Tf_sD_s, str, Tbadsubst);
}
if (f & DOBLANK)
doblank++;
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,
strndupx(x.str, beg, num, ATEMP);
goto do_CSUBST;
}
+ case 0x100 | '/':
case '/': {
char *s, *p, *d, *sbeg, *end;
- char *pat, *rrep;
+ char *pat = NULL, *rrep = null;
char fpat = 0, *tpat1, *tpat2;
+ char *ws, *wpat, *wrep;
- s = wdcopy(sp, ATEMP);
+ s = ws = wdcopy(sp, ATEMP);
p = s + (wdscan(sp, ADELIM) - sp);
d = s + (wdscan(sp, CSUBST) - sp);
p[-2] = EOS;
else
d[-2] = EOS;
sp += (d ? d : p) - s - 1;
- if (!(stype & 0x80) &&
+ if (!(stype & 0x180) &&
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);
+ wpat = s + (fpat ? 2 : 0);
+ wrep = d ? p : NULL;
+ if (!(stype & 0x100)) {
+ rrep = wrep ? evalstr(wrep,
+ DOTILDE | DOSCALAR) :
+ null;
+ }
+ /* prepare string on which to work */
+ strdupx(s, str_val(st->var), ATEMP);
+ sbeg = s;
+ again_search:
+ pat = evalstr(wpat,
+ DOTILDE | DOSCALAR | DOPAT);
/* check for special cases */
if (!*pat && !fpat) {
/*
*/
goto no_repl;
}
- if ((stype & 0x80) &&
+ if ((stype & 0x180) &&
gmatchx(null, pat, false)) {
/*
* pattern matches empty
* string => don't loop
*/
- stype &= ~0x80;
+ stype &= ~0x180;
}
- /* prepare string on which to work */
- strdupx(s, str_val(st->var), ATEMP);
- sbeg = s;
-
/* first see if we have any match at all */
if (fpat == '#') {
/* anchor at the beginning */
break;
p--;
}
+ strndupx(end, sbeg, p - sbeg, ATEMP);
+ record_match(end);
+ afree(end, ATEMP);
+ if (stype & 0x100) {
+ if (rrep != null)
+ afree(rrep, ATEMP);
+ rrep = wrep ? evalstr(wrep,
+ DOTILDE | DOSCALAR) :
+ null;
+ }
strndupx(end, s, sbeg - s, ATEMP);
- d = shf_smprintf("%s%s%s", end, rrep, p);
+ d = shf_smprintf(Tf_sss, end, rrep, p);
afree(end, ATEMP);
sbeg = d + (sbeg - s) + strlen(rrep);
afree(s, ATEMP);
s = d;
- if (stype & 0x80)
+ if (stype & 0x100) {
+ afree(tpat1, ATEMP);
+ afree(pat, ATEMP);
+ goto again_search;
+ } else if (stype & 0x80)
goto again_repl;
end_repl:
afree(tpat1, ATEMP);
afree(pat, ATEMP);
if (rrep != null)
afree(rrep, ATEMP);
+ afree(ws, ATEMP);
goto do_CSUBST;
}
case '#':
case '?':
dp = Xrestpos(ds, dp, st->base);
- errorf("%s: %s", st->var->name,
+ errorf(Tf_sD_s, st->var->name,
debunk(dp, dp, strlen(dp) + 1));
break;
case '0':
+ case 0x100 | '/':
case '/':
case 0x100 | '#':
case 0x100 | 'Q':
int c;
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
int stype; /* substitution type */
- int slen;
+ int slen = 0;
const char *p;
struct tbl *vp;
bool zero_ok = false;
}
}
if (Flag(FNOUNSET) && c == 0 && !zero_ok)
- errorf("%s: %s", sp, "parameter not set");
+ errorf(Tf_parm, sp);
/* unqualified variable/string substitution */
*stypep = 0;
- xp->str = shf_smprintf("%d", c);
+ xp->str = shf_smprintf(Tf_d, c);
+ return (XSUB);
+ }
+ if (stype == '!' && c != '\0' && *word == CSUBST) {
+ sp++;
+ if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
+ p[2] == ']') {
+ c = '!';
+ stype = 0;
+ goto arraynames;
+ }
+ xp->var = global(sp);
+ xp->str = p ? shf_smprintf("%s[%lu]",
+ xp->var->name, arrayindex(xp->var)) : xp->var->name;
+ *stypep = 0;
return (XSUB);
}
/* Check for qualifiers in word part */
stype = 0;
- c = word[slen = 0] == CHAR ? word[1] : 0;
+ c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
if (c == ':') {
slen += 2;
stype = 0x80;
if (!stype && c == '/') {
slen += 2;
stype = c;
- if (word[slen] == ADELIM) {
+ if (word[slen] == ADELIM && word[slen + 1] == c) {
slen += 2;
stype |= 0x80;
}
return (-1);
if (!stype && *word != CSUBST)
return (-1);
- *stypep = stype;
- *slenp = slen;
c = sp[0];
if (c == '*' || c == '@') {
/* can't trim a vector (yet) */
case '%':
case '#':
+ case '?':
case '0':
+ case 0x100 | '/':
case '/':
case 0x100 | '#':
case 0x100 | 'Q':
}
/* POSIX 2009? */
zero_ok = true;
- } else {
- if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
- p[2] == ']') {
- XPtrV wv;
-
- switch (stype & 0x17F) {
- /* can't assign to a vector */
- case '=':
- /* can't trim a vector (yet) */
- case '%':
- case '#':
- case '?':
- case '0':
- case '/':
- case 0x100 | '#':
- case 0x100 | 'Q':
- return (-1);
- }
- XPinit(wv, 32);
- if ((c = sp[0]) == '!')
- ++sp;
- vp = global(arrayname(sp));
- for (; vp; vp = vp->u.array) {
- if (!(vp->flag&ISSET))
- continue;
- XPput(wv, c == '!' ? shf_smprintf("%lu",
- arrayindex(vp)) :
- str_val(vp));
- }
- if (XPsize(wv) == 0) {
- xp->str = null;
- state = p[1] == '@' ? XNULLSUB : XSUB;
- XPfree(wv);
- } else {
- XPput(wv, 0);
- xp->u.strv = (const char **)XPptrv(wv);
- xp->str = *xp->u.strv++;
- /* ${foo[@]} */
- xp->split = tobool(p[1] == '@');
- state = XARG;
- }
+ } else if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
+ p[2] == ']') {
+ XPtrV wv;
+
+ switch (stype & 0x17F) {
+ /* can't assign to a vector */
+ case '=':
+ /* can't trim a vector (yet) */
+ case '%':
+ case '#':
+ case '?':
+ case '0':
+ case 0x100 | '/':
+ case '/':
+ case 0x100 | '#':
+ case 0x100 | 'Q':
+ return (-1);
+ }
+ c = 0;
+ arraynames:
+ XPinit(wv, 32);
+ vp = global(arrayname(sp));
+ for (; vp; vp = vp->u.array) {
+ if (!(vp->flag&ISSET))
+ continue;
+ XPput(wv, c == '!' ? shf_smprintf(Tf_lu,
+ arrayindex(vp)) :
+ str_val(vp));
+ }
+ if (XPsize(wv) == 0) {
+ xp->str = null;
+ state = p[1] == '@' ? XNULLSUB : XSUB;
+ XPfree(wv);
} else {
- /* Can't assign things like $! or $1 */
- if ((stype & 0x17F) == '=' &&
- ctype(*sp, C_VAR1 | C_DIGIT))
- return (-1);
- if (*sp == '!' && sp[1]) {
- ++sp;
- xp->var = global(sp);
- if (vstrchr(sp, '['))
- xp->str = shf_smprintf("%s[%lu]",
- xp->var->name,
- arrayindex(xp->var));
- else
- xp->str = xp->var->name;
- } else {
- xp->var = global(sp);
- xp->str = str_val(xp->var);
- }
- state = XSUB;
+ XPput(wv, 0);
+ xp->u.strv = (const char **)XPptrv(wv);
+ xp->str = *xp->u.strv++;
+ /* ${foo[@]} */
+ xp->split = tobool(p[1] == '@');
+ state = XARG;
}
+ } else {
+ xp->var = global(sp);
+ xp->str = str_val(xp->var);
+ /* can't assign things like $! or $1 */
+ if ((stype & 0x17F) == '=' && !*xp->str &&
+ ctype(*sp, C_VAR1 | C_DIGIT))
+ return (-1);
+ state = XSUB;
}
c = stype & 0x7F;
/* test the compiler's code generator */
- if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
+ if (((stype < 0x100) && (ctype(c, C_SUBOP2) ||
(((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'))
+ stype == (0x100 | 'Q') || (stype & 0x7F) == '/')
/* expand word instead of variable value */
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(Tf_parm, sp);
+ *stypep = stype;
+ *slenp = slen;
return (state);
}
struct ioword *io = *t->ioact;
char *name;
- if ((io->ioflag & IOTYPE) != IOREAD)
- errorf("%s: %s", T_funny_command,
- snptreef(NULL, 32, "%R", io));
- 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));
+ switch (io->ioflag & IOTYPE) {
+ case IOREAD:
+ shf = shf_open(name = evalstr(io->ioname, DOTILDE),
+ O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
+ if (shf == NULL)
+ warningf(!Flag(FTALKING), Tf_sD_s_sD_s,
+ name, Tcant_open, "$(<...) input",
+ cstrerror(errno));
+ break;
+ case IOHERE:
+ if (!herein(io, &name)) {
+ xp->str = name;
+ /* as $(…) requires, trim trailing newlines */
+ name += strlen(name);
+ while (name > xp->str && name[-1] == '\n')
+ --name;
+ *name = '\0';
+ return (XSUB);
+ }
+ shf = NULL;
+ break;
+ default:
+ errorf(Tf_sD_s, T_funny_command,
+ snptreef(NULL, 32, Tft_R, io));
+ }
} else if (fn == FUNSUB) {
int ofd1;
struct temp *tf = NULL;
*/
maketemp(ATEMP, TT_FUNSUB, &tf);
if (!tf->shf) {
- errorf("can't %s temporary file %s: %s",
- "create", tf->tffn, cstrerror(errno));
+ errorf(Tf_temp,
+ Tcreate, tf->tffn, cstrerror(errno));
}
/* extract shf from temporary file, unlink and free it */
shf = tf->shf;
for (p = str; p <= end; p += utf_ptradj(p)) {
c = *p; *p = '\0';
if (gmatchx(str, pat, false)) {
+ record_match(str);
*p = c;
return (p);
}
for (p = end; p >= str; p--) {
c = *p; *p = '\0';
if (gmatchx(str, pat, false)) {
+ record_match(str);
*p = c;
return (p);
}
for (p = str; p <= end; p++)
if (gmatchx(p, pat, false)) {
trimsub_match:
+ record_match(p);
strndupx(end, str, p - str, ATEMP);
return (end);
}
/* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
*xp = '\0';
prefix_len = Xlength(*xs, xp);
- dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
+ dirp = opendir(prefix_len ? Xstring(*xs, xp) : Tdot);
if (dirp == NULL)
goto Nodir;
while ((d = readdir(dirp)) != NULL) {
*
* based on a version by Arnold Robbins
*/
-
char *
do_tilde(char *cp)
{
char *dp = null;
+#ifndef MKSH_NOPWNAM
+ bool do_simplify = true;
+#endif
if (cp[0] == '\0')
dp = str_val(global("HOME"));
else if (cp[0] == '+' && cp[1] == '\0')
- dp = str_val(global("PWD"));
+ dp = str_val(global(TPWD));
else if (ksh_isdash(cp))
- dp = str_val(global("OLDPWD"));
+ dp = str_val(global(TOLDPWD));
#ifndef MKSH_NOPWNAM
- else
+ else {
dp = homedir(cp);
+ do_simplify = false;
+ }
#endif
- /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
- return (dp == null ? NULL : dp);
+
+ /* if parameters aren't set, don't expand ~ */
+ if (dp == NULL || dp == null)
+ return (NULL);
+
+ /* simplify parameters as if cwd upon entry */
+#ifndef MKSH_NOPWNAM
+ if (do_simplify)
+#endif
+ {
+ strdupx(dp, dp, ATEMP);
+ simplify_path(dp);
+ }
+ return (dp);
}
#ifndef MKSH_NOPWNAM
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
+ * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.170 2015/12/31 21:03:47 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.179 2016/08/01 21:38:02 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
static int call_builtin(struct tbl *, const char **, const char *, bool);
static int iosetup(struct ioword *, struct tbl *);
-static int herein(struct ioword *, char **);
static const char *do_selectargs(const char **, bool);
static Test_op dbteste_isa(Test_env *, Test_meta);
static const char *dbteste_getopnd(Test_env *, Test_op, bool);
const char *s, *ccp;
struct ioword **iowp;
struct tbl *tp = NULL;
- char *cp;
if (t == NULL)
return (0);
/* we want to run an executable, do some variance checks */
if (t->type == TCOM) {
+ /*
+ * Clear subst_exstat before argument expansion. Used by
+ * null commands (see comexec() and c_eval()) and by c_set().
+ */
+ subst_exstat = 0;
+
+ /* for $LINENO */
+ current_lineno = t->lineno;
+
/* check if this is 'var=<<EOF' */
- /*XXX this is broken, don’t use! */
- /*XXX https://bugs.launchpad.net/mksh/+bug/1380389 */
if (
- /* we have zero arguments, i.e. no programme to run */
+ /* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
/* and has no right-hand side (i.e. "varname=") */
ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
/* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
- ccp[3] == '=' && ccp[4] == EOS)) &&
- /* plus we can have a here document content */
- herein(t->ioact[0], &cp) == 0 && cp && *cp) {
- char *sp = cp, *dp;
- size_t n = ccp - t->vars[0] + (ccp[1] == '+' ? 4 : 2);
- size_t z;
-
- /* drop redirection (will be garbage collected) */
- t->ioact = NULL;
-
- /* set variable to its expanded value */
- z = strlen(cp);
- if (notoktomul(z, 2) || notoktoadd(z * 2, n + 1))
- internal_errorf(Toomem, (size_t)-1);
- dp = alloc(z * 2 + n + 1, APERM);
- memcpy(dp, t->vars[0], n);
- t->vars[0] = dp;
- dp += n;
- while (*sp) {
- *dp++ = QCHAR;
- *dp++ = *sp++;
- }
- *dp = EOS;
+ ccp[3] == '=' && ccp[4] == EOS))) {
+ char *cp, *dp;
+
+ if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
+ cp = NULL;
+ dp = shf_smprintf(Tf_ss, evalstr(t->vars[0],
+ DOASNTILDE | DOSCALAR), rv ? null : cp);
+ typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
/* free the expanded value */
afree(cp, APERM);
+ afree(dp, ATEMP);
+ goto Break;
}
/*
- * Clear subst_exstat before argument expansion. Used by
- * null commands (see comexec() and c_eval()) and by c_set().
- */
- subst_exstat = 0;
-
- /* for $LINENO */
- current_lineno = t->lineno;
-
- /*
* POSIX says expand command words first, then redirections,
* and assignments last..
*/
for (ap = (const char **)t->vars; *ap; ap++) {
if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
gmatchx(ccp, s, false))) {
+ record_match(ccp);
rv = execute(t->left, flags & XERROK,
xerrok);
i = 0;
if (rv == ENOEXEC)
scriptexec(t, (const char **)up);
else
- errorf("%s: %s", t->str, cstrerror(rv));
+ errorf(Tf_sD_s, t->str, cstrerror(rv));
}
Break:
exstat = rv & 0xFF;
break;
}
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
- ))
+ errorf(Tf_sD_sD_s, Tbuiltin, cp, Tnot_found);
+ if (tp->type == CSHELL && (tp->flag & LOW_BI))
break;
continue;
} else if (tp->val.f == c_exec) {
fcflags = FC_BI|FC_PATH;
if (saw_p) {
if (Flag(FRESTRICTED)) {
- warningf(true, "%s: %s",
+ warningf(true, Tf_sD_s,
"command -p", "restricted");
rv = 1;
goto Leave;
subst_exstat = 0;
break;
}
- } else if (tp->val.f == c_cat) {
+ } else if (tp->flag & LOW_BI) {
/* if we have any flags, do not use the builtin */
- if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
+ if ((ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
/* argument, begins with -, is not - or -- */
- (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;
+ (ap[1][1] != '-' || ap[1][2] != '\0')) ||
+ /* always prefer the external utility */
+ (tp->flag & LOWER_BI)) {
+ struct tbl *ext_cmd;
+
+ ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC);
+ if (ext_cmd && (ext_cmd->type != CTALIAS ||
+ (ext_cmd->flag & ISSET)))
+ tp = ext_cmd;
}
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;
break;
goto Leave;
} else if (!tp) {
if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
- warningf(true, "%s: %s", cp, "restricted");
+ warningf(true, Tf_sD_s, cp, "restricted");
rv = 1;
goto Leave;
}
struct tbl *ftp;
if (!tp->u.fpath) {
+ fpath_error:
rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
- warningf(true, "%s: %s %s: %s", cp,
- "can't find", "function definition file",
+ warningf(true, Tf_sD_s_sD_s, cp,
+ Tcant_find, Tfile_fd,
cstrerror(tp->u2.errnov));
break;
}
- if (include(tp->u.fpath, 0, NULL, false) < 0) {
- if (!strcmp(cp, Tcat)) {
- no_cat_in_FPATH:
- tp = findcom(Tcat, FC_BI);
+ errno = 0;
+ if (include(tp->u.fpath, 0, NULL, false) < 0 ||
+ !(ftp = findfunc(cp, hash(cp), false)) ||
+ !(ftp->flag & ISSET)) {
+ rv = errno;
+ if ((ftp = findcom(cp, FC_BI)) &&
+ (ftp->type == CSHELL) &&
+ (ftp->flag & LOW_BI)) {
+ tp = ftp;
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;
+ if (rv) {
+ tp->u2.errnov = rv;
+ cp = tp->u.fpath;
+ goto fpath_error;
}
-#endif
- warningf(true, "%s: %s %s %s: %s", cp,
- "can't open", "function definition file",
- tp->u.fpath, cstrerror(errno));
- rv = 127;
- break;
- }
- 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,
+ warningf(true, Tf_sD_s_s, cp,
"function not defined by", tp->u.fpath);
rv = 127;
break;
/* NOTREACHED */
default:
quitenv(NULL);
- internal_errorf("%s %d", "CFUNC", i);
+ internal_errorf(Tf_sd, "CFUNC", i);
}
break;
}
if (!(tp->flag&ISSET)) {
if (tp->u2.errnov == ENOENT) {
rv = 127;
- warningf(true, "%s: %s", cp, "not found");
+ warningf(true, Tf_sD_s, cp, Tnot_found);
} else {
rv = 126;
- warningf(true, "%s: %s: %s", cp, "can't execute",
+ warningf(true, Tf_sD_sD_s, cp, "can't execute",
cstrerror(tp->u2.errnov));
}
break;
#endif
union mksh_ccphack args, cap;
- sh = str_val(global("EXECSHELL"));
+ sh = str_val(global(TEXECSHELL));
if (sh && *sh)
sh = search_path(sh, path, X_OK, NULL);
if (!sh || !*sh)
execve(args.rw[0], args.rw, cap.rw);
/* report both the programme that was run and the bogus interpreter */
- errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
+ errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno));
}
int
}
if (tp->flag & ALLOC) {
- tp->flag &= ~(ISSET|ALLOC);
+ tp->flag &= ~(ISSET|ALLOC|FKSH);
tfree(tp->val.t, tp->areap);
}
uint32_t flag = DEFINED;
/* see if any flags should be set for this builtin */
- while (1) {
- if (*name == '=')
- /* command does variable assignment */
- flag |= KEEPASN;
- else if (*name == '*')
- /* POSIX special builtin */
- flag |= SPEC_BI;
- else
- break;
- name++;
+ flags_loop:
+ switch (*name) {
+ case '=':
+ /* command does variable assignment */
+ flag |= KEEPASN;
+ break;
+ case '*':
+ /* POSIX special builtin */
+ flag |= SPEC_BI;
+ break;
+ case '~':
+ /* external utility overrides built-in utility, always */
+ flag |= LOWER_BI;
+ /* FALLTHROUGH */
+ case '!':
+ /* external utility overrides built-in utility, with flags */
+ flag |= LOW_BI;
+ break;
+ default:
+ goto flags_seen;
}
+ ++name;
+ goto flags_loop;
+ flags_seen:
+ /* enter into the builtins hash table */
tp = ktenter(&builtins, name, hash(name));
tp->flag = flag;
tp->type = CSHELL;
tp->val.f = func;
+ /* return name, for direct builtin call check in main.c */
return (name);
}
if (!tp && (flags & FC_FUNC)) {
tp = findfunc(name, h, false);
if (tp && !(tp->flag & ISSET)) {
- if ((fpath = str_val(global("FPATH"))) == null) {
+ if ((fpath = str_val(global(TFPATH))) == null) {
tp->u.fpath = NULL;
tp->u2.errnov = ENOENT;
} else
afree(npath.rw, ATEMP);
tp->flag |= ISSET|ALLOC;
} else if ((flags & FC_FUNC) &&
- (fpath = str_val(global("FPATH"))) != null &&
+ (fpath = str_val(global(TFPATH))) != null &&
(npath.ro = search_path(name, fpath, R_OK,
&tp->u2.errnov)) != NULL) {
/*
int rv;
if (!tp)
- internal_errorf("%s: %s", where, wp[0]);
+ internal_errorf(Tf_sD_s, where, wp[0]);
builtin_argv0 = wp[0];
builtin_spec = tobool(!resetspec &&
/*XXX odd use of KEEPASN */
if (Flag(FXTRACE)) {
change_xtrace(2, false);
- fptreef(shl_xtrace, 0, "%R", &iotmp);
+ fptreef(shl_xtrace, 0, Tft_R, &iotmp);
change_xtrace(1, false);
}
&emsg)) < 0) {
char *sp;
- warningf(true, "%s: %s",
- (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg);
+ warningf(true, Tf_sD_s,
+ (sp = snptreef(NULL, 32, Tft_R, &iotmp)), emsg);
afree(sp, ATEMP);
return (-1);
}
if (do_open) {
if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
- warningf(true, "%s: %s", cp, "restricted");
+ warningf(true, Tf_sD_s, cp, "restricted");
return (-1);
}
u = binopen3(cp, flags, 0666);
/* herein() may already have printed message */
if (u == -1) {
u = errno;
- warningf(true, "can't %s %s: %s",
+ warningf(true, Tf_cant,
iotype == IODUP ? "dup" :
(iotype == IOREAD || iotype == IOHERE) ?
- "open" : "create", cp, cstrerror(u));
+ Topen : Tcreate, cp, cstrerror(u));
}
return (-1);
}
char *sp;
eno = errno;
- warningf(true, "%s %s: %s",
- "can't finish (dup) redirection",
- (sp = snptreef(NULL, 32, "%R", &iotmp)),
+ warningf(true, Tf_s_sD_s, Tredirection_dup,
+ (sp = snptreef(NULL, 32, Tft_R, &iotmp)),
cstrerror(eno));
afree(sp, ATEMP);
if (iotype != IODUP)
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);
}
return (0);
}
-static int
+int
herein(struct ioword *iop, char **resbuf)
{
int fd = -1;
struct temp *h;
int i;
- /* ksh -c 'cat <<EOF' can cause this... */
- if (iop->heredoc == NULL && !(iop->ioflag & IOHERESTR)) {
- warningf(true, "%s missing", "here document");
- /* special to iosetup(): don't print error */
- return (-2);
- }
-
/* lexer substitution flags */
i = (iop->ioflag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
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));
+ warningf(true, Tf_temp,
+ !shf ? Tcreate : Topen, h->tffn, cstrerror(i));
if (shf)
shf_close(shf);
/* special to iosetup(): don't print error */
if (shf_close(shf) == -1) {
i = errno;
close(fd);
- warningf(true, "can't %s temporary file %s: %s",
- "write", h->tffn, cstrerror(i));
+ warningf(true, Tf_temp,
+ Twrite, h->tffn, cstrerror(i));
/* special to iosetup(): don't print error */
return (-2);
}
do_selectargs(const char **ap, bool print_menu)
{
static const char *read_args[] = {
- "read", "-r", "REPLY", NULL
+ Tread, "-r", "REPLY", NULL
};
char *s;
int i, argct;
*/
if (print_menu || !*str_val(global("REPLY")))
pr_menu(ap);
- shellf("%s", str_val(global("PS3")));
- if (call_builtin(findcom("read", FC_BI), read_args, Tselect,
+ shellf(Tf_s, str_val(global("PS3")));
+ if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect,
false))
return (NULL);
if (*(s = str_val(global("REPLY"))))
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.81 2016/01/14 21:17:50 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.88 2016/07/27 00:55:27 tg Exp $");
-/* the order of these enums is constrained by the order of opinfo[] */
-enum token {
- /* some (long) unary operators */
- O_PLUSPLUS = 0, O_MINUSMINUS,
- /* binary operators */
- O_EQ, O_NE,
- /* assignments are assumed to be in range O_ASN .. O_BORASN */
- O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
-#ifndef MKSH_LEGACY_MODE
- O_ROLASN, O_RORASN,
-#endif
- O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
- /* binary non-assignment operators */
-#ifndef MKSH_LEGACY_MODE
- O_ROL, O_ROR,
-#endif
- O_LSHIFT, O_RSHIFT,
- O_LE, O_GE, O_LT, O_GT,
- O_LAND,
- O_LOR,
- O_TIMES, O_DIV, O_MOD,
- O_PLUS, O_MINUS,
- O_BAND,
- O_BXOR,
- O_BOR,
- O_TERN,
- O_COMMA,
- /* things after this aren't used as binary operators */
- /* unary that are not also binaries */
- O_BNOT, O_LNOT,
- /* misc */
- OPEN_PAREN, CLOSE_PAREN, CTERN,
- /* things that don't appear in the opinfo[] table */
- VAR, LIT, END, BAD
-};
-#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+#define EXPRTOK_DEFNS
+#include "exprtok.h"
/* precisions; used to be enum prec but we do arithmetics on it */
#define P_PRIMARY 0 /* VAR, LIT, (), ! ~ ++ -- */
#define P_MULT 1 /* * / % */
#define P_ADD 2 /* + - */
-#define P_SHIFT 3 /* <<< >>> << >> */
+#define P_SHIFT 3 /* ^< ^> << >> */
#define P_RELATION 4 /* < <= > >= */
#define P_EQUALITY 5 /* == != */
#define P_BAND 6 /* & */
#define P_LAND 9 /* && */
#define P_LOR 10 /* || */
#define P_TERN 11 /* ?: */
- /* = += -= *= /= %= <<<= >>>= <<= >>= &= ^= |= */
+ /* = += -= *= /= %= ^<= ^>= <<= >>= &= ^= |= */
#define P_ASSIGN 12
#define P_COMMA 13 /* , */
#define MAX_PREC P_COMMA
-struct opinfo {
- char name[5];
- /* name length */
- uint8_t len;
- /* precedence: lower is higher */
- uint8_t prec;
+enum token {
+#define EXPRTOK_ENUM
+#include "exprtok.h"
};
-/*
- * Tokens in this table must be ordered so the longest are first
- * (eg, += before +). If you change something, change the order
- * of enum token too.
- */
-static const struct opinfo opinfo[] = {
- { "++", 2, P_PRIMARY }, /* before + */
- { "--", 2, P_PRIMARY }, /* before - */
- { "==", 2, P_EQUALITY }, /* before = */
- { "!=", 2, P_EQUALITY }, /* before ! */
- { "=", 1, P_ASSIGN }, /* keep assigns in a block */
- { "*=", 2, P_ASSIGN },
- { "/=", 2, P_ASSIGN },
- { "%=", 2, P_ASSIGN },
- { "+=", 2, P_ASSIGN },
- { "-=", 2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
- { "<<<=", 4, P_ASSIGN }, /* before <<< */
- { ">>>=", 4, P_ASSIGN }, /* before >>> */
-#endif
- { "<<=", 3, P_ASSIGN },
- { ">>=", 3, P_ASSIGN },
- { "&=", 2, P_ASSIGN },
- { "^=", 2, P_ASSIGN },
- { "|=", 2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
- { "<<<", 3, P_SHIFT }, /* before << */
- { ">>>", 3, P_SHIFT }, /* before >> */
-#endif
- { "<<", 2, P_SHIFT },
- { ">>", 2, P_SHIFT },
- { "<=", 2, P_RELATION },
- { ">=", 2, P_RELATION },
- { "<", 1, P_RELATION },
- { ">", 1, P_RELATION },
- { "&&", 2, P_LAND },
- { "||", 2, P_LOR },
- { "*", 1, P_MULT },
- { "/", 1, P_MULT },
- { "%", 1, P_MULT },
- { "+", 1, P_ADD },
- { "-", 1, P_ADD },
- { "&", 1, P_BAND },
- { "^", 1, P_BXOR },
- { "|", 1, P_BOR },
- { "?", 1, P_TERN },
- { ",", 1, P_COMMA },
- { "~", 1, P_PRIMARY },
- { "!", 1, P_PRIMARY },
- { "(", 1, P_PRIMARY },
- { ")", 1, P_PRIMARY },
- { ":", 1, P_PRIMARY },
- { "", 0, P_PRIMARY }
+static const char opname[][4] = {
+#define EXPRTOK_NAME
+#include "exprtok.h"
+};
+
+static const uint8_t oplen[] = {
+#define EXPRTOK_LEN
+#include "exprtok.h"
+};
+
+static const uint8_t opprec[] = {
+#define EXPRTOK_PREC
+#include "exprtok.h"
};
typedef struct expr_state {
exprtoken(es);
if (es->tok == END) {
es->tok = LIT;
- es->val = tempvar();
+ es->val = tempvar("");
}
v = intvar(es, evalexpr(es, MAX_PREC));
s = tbuf;
break;
default:
- s = opinfo[(int)es->tok].name;
+ s = opname[(int)es->tok];
}
- warningf(true, "%s: %s '%s'", es->expression,
- "unexpected", s);
+ warningf(true, Tf_sD_s_qs, es->expression,
+ Tunexpected, s);
break;
case ET_BADLIT:
- warningf(true, "%s: %s '%s'", es->expression,
+ warningf(true, Tf_sD_s_qs, es->expression,
"bad number", str);
break;
case ET_RECURSIVE:
- warningf(true, "%s: %s '%s'", es->expression,
+ warningf(true, Tf_sD_s_qs, es->expression,
"expression recurses on parameter", str);
break;
case ET_LVALUE:
- warningf(true, "%s: %s %s",
+ warningf(true, Tf_sD_s_s,
es->expression, str, "requires lvalue");
break;
case ET_RDONLY:
- warningf(true, "%s: %s %s",
+ warningf(true, Tf_sD_s_s,
es->expression, str, "applied to read-only variable");
break;
default: /* keep gcc happy */
case ET_STR:
- warningf(true, "%s: %s", es->expression, str);
+ warningf(true, Tf_sD_s, es->expression, str);
break;
}
unwind(LAEXPR);
vl = evalexpr(es, prec - 1);
while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
- opinfo[(int)op].prec == prec) {
+ opprec[(int)op] == prec) {
exprtoken(es);
vasn = vl;
if (op != O_ASN)
cp += len;
}
if (es->noassign) {
- es->val = tempvar();
+ es->val = tempvar("");
es->val->flag |= EXPRLVALUE;
} else {
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,
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;
} else {
int i, n0;
- for (i = 0; (n0 = opinfo[i].name[0]); i++)
- if (c == n0 && strncmp(cp, opinfo[i].name,
- (size_t)opinfo[i].len) == 0) {
+ for (i = 0; (n0 = opname[i][0]); i++)
+ if (c == n0 && strncmp(cp, opname[i],
+ (size_t)oplen[i]) == 0) {
es->tok = (enum token)i;
- cp += opinfo[i].len;
+ cp += oplen[i];
break;
}
if (!n0)
{
if (es->tok == END || !vasn ||
(vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
- evalerr(es, ET_LVALUE, opinfo[(int)op].name);
+ evalerr(es, ET_LVALUE, opname[(int)op]);
else if (vasn->flag & RDONLY)
- evalerr(es, ET_RDONLY, opinfo[(int)op].name);
+ evalerr(es, ET_RDONLY, opname[(int)op]);
}
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);
}
(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);
}
const char *
-utf_skipcols(const char *p, int cols)
+utf_skipcols(const char *p, int cols, int *colp)
{
int c = 0;
+ const char *q;
while (c < cols) {
- if (!*p)
- return (p + cols - c);
+ if (!*p) {
+ /* end of input; special handling for edit.c */
+ if (!colp)
+ return (p + cols - c);
+ *colp = c;
+ return (p);
+ }
c += utf_widthadj(p, &p);
}
+ if (UTFMODE)
+ while (utf_widthadj(p, &q) == 0)
+ p = q;
+ if (colp)
+ *colp = c;
return (p);
}
--- /dev/null
+/*-
+ * Copyright (c) 2016
+ * mirabilos <m@mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#if defined(EXPRTOK_DEFNS)
+__RCSID("$MirOS: src/bin/mksh/exprtok.h,v 1.2 2016/08/12 16:48:05 tg Exp $");
+/* see range comment below */
+#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+#define FN(name, len, prec, enum) /* nothing */
+#define F1(enum) /* nothing */
+#elif defined(EXPRTOK_ENUM)
+#define F0(name, len, prec, enum) enum = 0,
+#define FN(name, len, prec, enum) enum,
+#define F1(enum) enum,
+#define F2(enum) enum,
+#define F9(enum) enum
+#elif defined(EXPRTOK_NAME)
+#define FN(name, len, prec, enum) name,
+#define F1(enum) ""
+#elif defined(EXPRTOK_LEN)
+#define FN(name, len, prec, enum) len,
+#define F1(enum) 0
+#elif defined(EXPRTOK_PREC)
+#define FN(name, len, prec, enum) prec,
+#define F1(enum) P_PRIMARY
+#endif
+
+#ifndef F0
+#define F0 FN
+#endif
+
+#ifndef F2
+#define F2(enum) /* nothing */
+#define F9(enum) /* nothing */
+#endif
+
+/* tokens must be ordered so the longest are first (e.g. += before +) */
+
+/* some (long) unary operators */
+FN("++", 2, P_PRIMARY, O_PLUSPLUS = 0) /* before + */
+FN("--", 2, P_PRIMARY, O_MINUSMINUS) /* before - */
+/* binary operators */
+FN("==", 2, P_EQUALITY, O_EQ) /* before = */
+FN("!=", 2, P_EQUALITY, O_NE) /* before ! */
+/* assignments are assumed to be in range O_ASN .. O_BORASN */
+FN("=", 1, P_ASSIGN, O_ASN)
+FN("*=", 2, P_ASSIGN, O_TIMESASN)
+FN("/=", 2, P_ASSIGN, O_DIVASN)
+FN("%=", 2, P_ASSIGN, O_MODASN)
+FN("+=", 2, P_ASSIGN, O_PLUSASN)
+FN("-=", 2, P_ASSIGN, O_MINUSASN)
+#ifndef MKSH_LEGACY_MODE
+FN("^<=", 3, P_ASSIGN, O_ROLASN) /* before ^< */
+FN("^>=", 3, P_ASSIGN, O_RORASN) /* before ^> */
+#endif
+FN("<<=", 3, P_ASSIGN, O_LSHIFTASN)
+FN(">>=", 3, P_ASSIGN, O_RSHIFTASN)
+FN("&=", 2, P_ASSIGN, O_BANDASN)
+FN("^=", 2, P_ASSIGN, O_BXORASN)
+FN("|=", 2, P_ASSIGN, O_BORASN)
+/* binary non-assignment operators */
+#ifndef MKSH_LEGACY_MODE
+FN("^<", 2, P_SHIFT, O_ROL) /* before ^ */
+FN("^>", 2, P_SHIFT, O_ROR) /* before ^ */
+#endif
+FN("<<", 2, P_SHIFT, O_LSHIFT)
+FN(">>", 2, P_SHIFT, O_RSHIFT)
+FN("<=", 2, P_RELATION, O_LE)
+FN(">=", 2, P_RELATION, O_GE)
+FN("<", 1, P_RELATION, O_LT)
+FN(">", 1, P_RELATION, O_GT)
+FN("&&", 2, P_LAND, O_LAND)
+FN("||", 2, P_LOR, O_LOR)
+FN("*", 1, P_MULT, O_TIMES)
+FN("/", 1, P_MULT, O_DIV)
+FN("%", 1, P_MULT, O_MOD)
+FN("+", 1, P_ADD, O_PLUS)
+FN("-", 1, P_ADD, O_MINUS)
+FN("&", 1, P_BAND, O_BAND)
+FN("^", 1, P_BXOR, O_BXOR)
+FN("|", 1, P_BOR, O_BOR)
+FN("?", 1, P_TERN, O_TERN)
+FN(",", 1, P_COMMA, O_COMMA)
+/* things after this aren't used as binary operators */
+/* unary that are not also binaries */
+FN("~", 1, P_PRIMARY, O_BNOT)
+FN("!", 1, P_PRIMARY, O_LNOT)
+/* misc */
+FN("(", 1, P_PRIMARY, OPEN_PAREN)
+FN(")", 1, P_PRIMARY, CLOSE_PAREN)
+FN(":", 1, P_PRIMARY, CTERN)
+/* things that don't appear in the opinfo[] table */
+F1(VAR) /*XXX should be F2 */
+F2(LIT)
+F2(END)
+F9(BAD)
+
+#undef FN
+#undef F0
+#undef F1
+#undef F2
+#undef F9
+#undef EXPRTOK_DEFNS
+#undef EXPRTOK_ENUM
+#undef EXPRTOK_NAME
+#undef EXPRTOK_LEN
+#undef EXPRTOK_PREC
#endif
#endif
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.293 2016/01/20 21:34:11 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.305 2016/08/01 21:38:02 tg Exp $");
#if HAVE_KILLPG
/*
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)
int rv;
if (!(rv = getn(as, ai)))
- bi_errorf("%s: %s", as, "bad number");
+ bi_errorf(Tf_sD_s, as, "bad number");
return (rv);
}
* A leading * means a POSIX special builtin.
*/
const struct builtin mkshbuiltins[] = {
- {"*=.", c_dot},
+ {Tsgdot, c_dot},
{"*=:", c_true},
- {"[", c_test},
+ {Tbracket, c_test},
/* no =: AT&T manual wrong */
{Talias, c_alias},
{"*=break", c_brkcont},
{Tgbuiltin, c_builtin},
- {Tcat, c_cat},
- {"cd", c_cd},
+ {Tbcat, c_cat},
+ {Tcd, c_cd},
/* dash compatibility hack */
{"chdir", c_cd},
{Tcommand, c_command},
{"*=exec", c_exec},
{"*=exit", c_exitreturn},
{Tsgexport, c_typeset},
- {"false", c_false},
+ {Tfalse, c_false},
{"fc", c_fc},
- {"getopts", c_getopts},
+ {Tgetopts, c_getopts},
{"=global", c_typeset},
- {"jobs", c_jobs},
+ {Tjobs, c_jobs},
{"kill", c_kill},
{"let", c_let},
{"let]", c_let},
{"print", c_print},
{"pwd", c_pwd},
- {"read", c_read},
+ {Tread, c_read},
{Tsgreadonly, c_typeset},
- {"realpath", c_realpath},
- {"rename", c_rename},
+ {"!realpath", c_realpath},
+ {"~rename", c_rename},
{"*=return", c_exitreturn},
{Tsgset, c_set},
{"*=shift", c_shift},
{"=source", c_dot},
#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
- {"suspend", c_suspend},
+ {Tsuspend, c_suspend},
#endif
{"test", c_test},
{"*=times", c_times},
{"*=trap", c_trap},
- {"true", c_true},
- {T_typeset, c_typeset},
+ {Ttrue, c_true},
+ {Tgtypeset, c_typeset},
{"ulimit", c_ulimit},
{"umask", c_umask},
{Tunalias, c_unalias},
{"=wait", c_wait},
{"whence", c_whence},
#ifndef MKSH_UNEMPLOYED
- {"bg", c_fgbg},
- {"fg", c_fgbg},
+ {Tbg, c_fgbg},
+ {Tfg, c_fgbg},
#endif
#ifndef MKSH_NO_CMDLINE_EDITING
{"bind", c_bind},
{"mknod", c_mknod},
#endif
#ifdef MKSH_PRINTF_BUILTIN
- {Tprintf, c_printf},
+ {"~printf", c_printf},
#endif
#if HAVE_SELECT
{"sleep", c_sleep},
wp += builtin_opt.optind;
if (wp[0]) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
if (p && access(p, R_OK) < 0)
p = NULL;
if (!p && !(p = allocd = ksh_get_wd())) {
- bi_errorf("%s: %s", "can't determine current directory",
+ bi_errorf(Tf_sD_s, "can't determine current directory",
cstrerror(errno));
return (1);
}
- shprintf("%s\n", p);
+ shprintf(Tf_sN, p);
afree(allocd, ATEMP);
return (0);
}
bool po_nl = true, po_exp = true;
/* print to history instead of file descriptor / stdout */
bool po_hist = false;
+ /* print characters */
+ bool po_char = false;
+ char ts[4];
if (wp[0][0] == 'e') {
/* "echo" builtin */
}
} else {
/* "print" builtin */
- const char *opts = "npRrsu,";
+ const char *opts = "AnpRrsu,";
const char *emsg;
/* print a "--" argument */
bool po_pminusminus = false;
while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
switch (c) {
+ case 'A':
+ po_char = true;
+ break;
case 'e':
po_exp = true;
break;
break;
case 'p':
if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
- bi_errorf("-p: %s", emsg);
+ bi_errorf(Tf_coproc, emsg);
return (1);
}
break;
Xinit(xs, xp, 128, ATEMP);
- if (*wp != NULL) {
+ if (*wp != NULL && po_char) {
+ mksh_ari_t wc;
+
+ do {
+ if (!evaluate(*wp, &wc, KSH_RETURN_ERROR, true))
+ return (1);
+ Xcheck(xs, xp);
+ if (UTFMODE) {
+ ts[utf_wctomb(ts, wc)] = 0;
+ c = 0;
+ do {
+ Xput(xs, xp, ts[c]);
+ } while (ts[++c]);
+ } else
+ Xput(xs, xp, wc & 0xFF);
+ } while (*++wp);
+ } else if (*wp != NULL) {
print_read_arg:
s = *wp;
while ((c = *s++) != '\0') {
}
} else if ((unsigned int)c > 0xFF) {
/* generic function returned Unicode */
- char ts[4];
-
ts[utf_wctomb(ts, c - 0x100)] = 0;
c = 0;
do {
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, Tpv)) != -1)
switch (optc) {
case 'p':
pflag = true;
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;
+
+ while ((optc = ksh_getopt(wp, &builtin_opt, TpVv)) != -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 (!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)
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(Tnot_found_s, 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);
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,
return (1);
if (basestr) {
if (!getn(basestr, &base)) {
- bi_errorf("%s: %s", "bad integer base", basestr);
+ bi_errorf(Tf_sD_s, "bad integer base", basestr);
return (1);
}
if (base < 1 || base > 36)
shf_putc('\n', shl_stdout);
}
} else if (!typeset(wp[i], fset, fclr, field, base)) {
- bi_errorf("%s: %s", wp[i], "is not an identifier");
+ bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
return (1);
}
}
/* no arguments */
if (!thing && !flag) {
if (any_set == 1) {
- shprintf("%s %s %s\n", Tset, "-A", vp->name);
+ shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
any_set = 2;
}
/*
* leftadj, zerofill, etc., but POSIX says must
* be suitable for re-entry...
*/
- shprintf("%s %s", Ttypeset, "");
+ shprintf(Tf_s_s, Ttypeset, "");
if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
- shprintf("%s ", "-n");
+ shprintf(Tf__c_, 'n');
if ((vp->flag & INTEGER))
- shprintf("%s ", "-i");
+ shprintf(Tf__c_, 'i');
if ((vp->flag & EXPORT))
- shprintf("%s ", "-x");
+ shprintf(Tf__c_, 'x');
if ((vp->flag & RDONLY))
- shprintf("%s ", "-r");
+ shprintf(Tf__c_, 'r');
if ((vp->flag & TRACE))
- shprintf("%s ", "-t");
+ shprintf(Tf__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(Tf__c_, 'Z');
if ((vp->flag & LCASEV))
- shprintf("%s ", "-l");
+ shprintf(Tf__c_, 'l');
if ((vp->flag & UCASEV_AL))
- shprintf("%s ", "-u");
+ shprintf(Tf__c_, 'u');
if ((vp->flag & INT_U))
- shprintf("%s ", "-U");
+ shprintf(Tf__c_, 'U');
} else if (pflag) {
- shprintf("%s %s", istset ? Ttypeset :
+ shprintf(Tf_s_s, istset ? Ttypeset :
(flag & EXPORT) ? Texport : Treadonly, "");
}
if (any_set)
for (p = ktsort(t); (ap = *p++) != NULL; )
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
if (pflag)
- shprintf("%s ", Talias);
+ shprintf(Tf_s_, Talias);
shf_puts(ap->name, shl_stdout);
if (prefix != '+') {
shf_putc('=', shl_stdout);
ap = ktsearch(t, alias, h);
if (ap != NULL && (ap->flag&ISSET)) {
if (pflag)
- shprintf("%s ", Talias);
+ shprintf(Tf_s_, Talias);
shf_puts(ap->name, shl_stdout);
if (prefix != '+') {
shf_putc('=', shl_stdout);
}
shf_putc('\n', shl_stdout);
} else {
- shprintf("%s %s %s\n", alias, Talias,
- "not found");
+ shprintf(Tf_s_s_sN, alias, Talias, Tnot_found);
rv = 1;
}
continue;
if (wp[1] == NULL)
/* AT&T ksh does this */
- bi_errorf("no arguments");
+ bi_errorf(Tno_args);
else
for (wp++; *wp; wp++)
if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
int
c_fgbg(const char **wp)
{
- bool bg = strcmp(*wp, "bg") == 0;
+ bool bg = strcmp(*wp, Tbg) == 0;
int rv = 0;
if (!Flag(FMONITOR)) {
if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
ksh_isupper(p[1]))) {
if (!(t = gettrap(p + 1, false, false))) {
- bi_errorf("bad signal '%s'", p + 1);
+ bi_errorf(Tbad_sig_s, p + 1);
return (1);
}
i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
case 's':
if (!(t = gettrap(builtin_opt.optarg,
true, false))) {
- bi_errorf("bad signal '%s'",
+ bi_errorf(Tbad_sig_s,
builtin_opt.optarg);
return (1);
}
n -= 128;
#endif
if (n > 0 && n < ksh_NSIG)
- shprintf("%s\n", sigtraps[n].name);
+ shprintf(Tf_sN, sigtraps[n].name);
else
- shprintf("%d\n", n);
+ shprintf(Tf_dN, n);
}
} else if (Flag(FPOSIX)) {
n = 1;
if (j_kill(p, sig))
rv = 1;
} else if (!getn(p, &n)) {
- bi_errorf("%s: %s", p,
+ bi_errorf(Tf_sD_s, p,
"arguments must be jobs or process IDs");
rv = 1;
} else {
if (mksh_kill(n, sig) < 0) {
- bi_errorf("%s: %s", p, cstrerror(errno));
+ bi_errorf(Tf_sD_s, p, cstrerror(errno));
rv = 1;
}
}
opts = *wp++;
if (!opts) {
- bi_errorf("missing %s argument", "options");
+ bi_errorf(Tf_sD_s, "options", Tno_args);
return (1);
}
var = *wp++;
if (!var) {
- bi_errorf("missing %s argument", "name");
+ bi_errorf(Tf_sD_s, Tname, Tno_args);
return (1);
}
if (!*var || *skip_varname(var, true)) {
- bi_errorf("%s: %s", var, "is not an identifier");
+ bi_errorf(Tf_sD_s, var, Tnot_ident);
return (1);
}
if (e->loc->next == NULL) {
- internal_warningf("%s: %s", "c_getopts", "no argv");
+ internal_warningf(Tf_sD_s, Tgetopts, Tno_args);
return (1);
}
/* Which arguments are we parsing... */
/* nothing to do */
return (0);
} else if (n < 0) {
- bi_errorf("%s: %s", arg, "bad number");
+ bi_errorf(Tf_sD_s, arg, "bad number");
return (1);
}
if (l->argc < n) {
old_umask = ~old_umask;
p = buf;
for (i = 0; i < 3; i++) {
- *p++ = "ugo"[i];
+ *p++ = Tugo[i];
*p++ = '=';
for (j = 0; j < 3; j++)
if (old_umask & (1 << (8 - (3*i + j))))
*p++ = ',';
}
p[-1] = '\0';
- shprintf("%s\n", buf);
+ shprintf(Tf_sN, buf);
} else
shprintf("%#3.3o\n", (unsigned int)old_umask);
} else {
new_umask = old_umask;
positions = 0;
while (*cp) {
- while (*cp && vstrchr("augo", *cp))
+ while (*cp && vstrchr(Taugo, *cp))
switch (*cp++) {
case 'a':
positions |= 0111;
c_dot(const char **wp)
{
const char *file, *cp, **argv;
- int argc, i, errcode;
+ int argc, rv, errcode;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return (1);
if ((cp = wp[builtin_opt.optind]) == NULL) {
- bi_errorf("missing argument");
+ bi_errorf(Tno_args);
return (1);
}
file = search_path(cp, path, R_OK, &errcode);
search_access(cp, R_OK) == 0)
file = cp;
if (!file) {
- bi_errorf("%s: %s", cp, cstrerror(errcode));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errcode));
return (1);
}
argc = 0;
argv = NULL;
}
- if ((i = include(file, argc, argv, false)) < 0) {
+ /* SUSv4: OR with a high value never written otherwise */
+ exstat |= 0x4000;
+ if ((rv = include(file, argc, argv, false)) < 0) {
/* should not happen */
- bi_errorf("%s: %s", cp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errno));
return (1);
}
- return (i);
+ if (exstat & 0x4000)
+ /* detect old exstat, use 0 in that case */
+ rv = 0;
+ return (rv);
}
int
break;
case 'p':
if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
- bi_errorf("%s: %s", "-p", ccp);
+ bi_errorf(Tf_coproc, ccp);
return (2);
}
break;
#if HAVE_SELECT
case 't':
if (parse_usec(builtin_opt.optarg, &tv)) {
- bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
+ bi_errorf(Tf_sD_s_qs, Tsynerr, cstrerror(errno),
builtin_opt.optarg);
return (2);
}
if (!builtin_opt.optarg[0])
fd = 0;
else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
- bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
+ bi_errorf(Tf_sD_sD_s, "-u", builtin_opt.optarg, ccp);
return (2);
}
break;
*--wp = REPLY;
if (intoarray && wp[1] != NULL) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (2);
}
/* fake EOF read; all cases return 1 */
goto c_read_didread;
default:
- bi_errorf("%s: %s", Tselect, cstrerror(errno));
+ bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
rv = 2;
goto c_read_out;
}
}
#endif
- bytesread = blocking_read(fd, xp, bytesleft);
- if (bytesread == (size_t)-1) {
- /* interrupted */
- if (errno == EINTR && fatal_trap_check()) {
- /*
- * Was the offending signal one that would
- * normally kill a process? If so, pretend
- * the read was killed.
- */
- rv = 2;
- goto c_read_out;
+ if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) {
+ if (errno == EINTR) {
+ /* check whether the signal would normally kill */
+ if (!fatal_trap_check()) {
+ /* no, just ignore the signal */
+ goto c_read_readloop;
+ }
+ /* pretend the read was killed */
+ } else {
+ /* unexpected error */
+ bi_errorf(Tf_s, cstrerror(errno));
}
- /* just ignore the signal */
- goto c_read_readloop;
+ rv = 2;
+ goto c_read_out;
}
c_read_didread:
subarray = last_lookup_was_array;
if (vp->flag & RDONLY) {
c_read_splitro:
- bi_errorf("read-only: %s", *wp);
+ bi_errorf(Tf_ro, *wp);
c_read_spliterr:
rv = 2;
afree(cp, ATEMP);
if (p->trap) {
shf_puts("trap -- ", shl_stdout);
print_value_quoted(shl_stdout, p->trap);
- shprintf(" %s\n", p->name);
+ shprintf(Tf__sN, p->name);
}
++p;
} while (i--);
i = 0;
while (*wp)
if (!(p = gettrap(*wp++, true, true))) {
- warningf(true, "%s: %s '%s'", builtin_argv0,
- "bad signal", wp[-1]);
+ warningf(true, Tbad_sig_ss, builtin_argv0, wp[-1]);
i = 1;
} else
settrap(p, s);
/* NOTREACHED */
c_exitreturn_err:
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
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;
* 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);
}
/*
afree(cp, ATEMP);
if ((vp->flag&RDONLY)) {
- warningf(true, "read-only: %s", vp->name);
+ warningf(true, Tf_ro, vp->name);
rv = 1;
} else
unset(vp, optc);
getrusage(RUSAGE_SELF, &usage);
p_time(shl_stdout, false, usage.ru_utime.tv_sec,
- usage.ru_utime.tv_usec, 0, null, " ");
+ usage.ru_utime.tv_usec, 0, null, T1space);
p_time(shl_stdout, false, usage.ru_stime.tv_sec,
usage.ru_stime.tv_usec, 0, null, "\n");
getrusage(RUSAGE_CHILDREN, &usage);
p_time(shl_stdout, false, usage.ru_utime.tv_sec,
- usage.ru_utime.tv_usec, 0, null, " ");
+ usage.ru_utime.tv_usec, 0, null, T1space);
p_time(shl_stdout, false, usage.ru_stime.tv_sec,
usage.ru_stime.tv_usec, 0, null, "\n");
timersub(&tv1, &tv0, &tv1);
if (tf & TF_POSIX)
p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
- 5, "real ", "\n");
+ 5, Treal_sp1, "\n");
else
p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
- 5, null, " real ");
+ 5, null, Treal_sp2);
}
if (tf & TF_POSIX)
p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
- 5, "user ", "\n");
+ 5, Tuser_sp1, "\n");
else
p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
- 5, null, " user ");
+ 5, null, Tuser_sp2);
if (tf & TF_POSIX)
p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
5, "sys ", "\n");
t->str[0] |= TF_POSIX;
break;
case '?':
- errorf("time: -%s %s", opt.optarg,
- "unknown option");
+ errorf(Tf_optfoo, Ttime, Tcolsp,
+ opt.optarg[0], Tunknown_option);
case ':':
- errorf("time: -%s %s", opt.optarg,
- "requires an argument");
+ errorf(Tf_optfoo, Ttime, Tcolsp,
+ opt.optarg[0], Treq_arg);
}
/* Copy command words down over options. */
if (opt.optind != 0) {
majnum = strtoul(argv[2], &c, 0);
if ((c == argv[2]) || (*c != '\0')) {
- bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
+ bi_errorf(Tf_nonnum, "device", "major", argv[2]);
goto c_mknod_err;
}
minnum = strtoul(argv[3], &c, 0);
if ((c == argv[3]) || (*c != '\0')) {
- bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
+ bi_errorf(Tf_nonnum, "device", "minor", argv[3]);
goto c_mknod_err;
}
dv = makedev(majnum, minnum);
if ((unsigned long)(major(dv)) != majnum) {
- bi_errorf("%s %s too large: %lu", "device", "major", majnum);
+ bi_errorf(Tf_toolarge, "device", "major", majnum);
goto c_mknod_err;
}
if ((unsigned long)(minor(dv)) != minnum) {
- bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
+ bi_errorf(Tf_toolarge, "device", "minor", minnum);
goto c_mknod_err;
}
if (mknod(argv[0], mode, dv))
goto c_mknod_failed;
} else if (mkfifo(argv[0], mode)) {
c_mknod_failed:
- bi_errorf("%s: %s", argv[0], cstrerror(errno));
+ bi_errorf(Tf_sD_s, argv[0], cstrerror(errno));
c_mknod_err:
rv = 1;
}
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
int argc, rv, invert = 0;
Test_env te;
Test_op op;
+ Test_meta tm;
const char *lhs, **swp;
te.flags = 0;
for (argc = 0; wp[argc]; argc++)
;
- if (strcmp(wp[0], "[") == 0) {
+ if (strcmp(wp[0], Tbracket) == 0) {
if (strcmp(wp[--argc], "]") != 0) {
bi_errorf("missing ]");
return (T_ERR_EXIT);
rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
goto ptest_out;
}
+ if (ptest_isa(&te, tm = TM_AND) || ptest_isa(&te, tm = TM_OR)) {
+ /* XSI */
+ argc = test_eval(&te, TO_STNZE, lhs, NULL, true);
+ rv = test_eval(&te, TO_STNZE, *te.pos.wp++, NULL, true);
+ if (tm == TM_AND)
+ rv = argc && rv;
+ else
+ rv = argc || rv;
+ goto ptest_out;
+ }
/* back up to lhs */
te.pos.wp = swp;
if (ptest_isa(&te, TM_NOT)) {
/* = */
case TO_STEQL:
- if (te->flags & TEF_DBRACKET)
- return (gmatchx(opnd1, opnd2, false));
+ if (te->flags & TEF_DBRACKET) {
+ if ((i = gmatchx(opnd1, opnd2, false)))
+ record_match(opnd1);
+ return (i);
+ }
return (strcmp(opnd1, opnd2) == 0);
/* != */
case TO_STNEQ:
- if (te->flags & TEF_DBRACKET)
- return (!gmatchx(opnd1, opnd2, false));
+ if (te->flags & TEF_DBRACKET) {
+ if ((i = gmatchx(opnd1, opnd2, false)))
+ record_match(opnd1);
+ return (!i);
+ }
return (strcmp(opnd1, opnd2) != 0);
/* < */
/* unary expression */
opnd1 = (*te->getopnd)(te, op, do_eval);
if (!opnd1) {
- (*te->error)(te, -1, "missing argument");
+ (*te->error)(te, -1, Tno_args);
return (0);
}
te->flags |= TEF_ERROR;
if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
- bi_errorf("%s: %s", op, msg);
+ bi_errorf(Tf_sD_s, op, msg);
else
- bi_errorf("%s", msg);
+ bi_errorf(Tf_s, msg);
}
#ifndef MKSH_NO_LIMITS
found:
if (wp[builtin_opt.optind]) {
if (all || wp[builtin_opt.optind + 1]) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
bi_errorf(Tsynerr);
else if ((rv = rename(wp[0], wp[1])) != 0) {
rv = errno;
- bi_errorf("%s: %s", "failed", cstrerror(rv));
+ bi_errorf(Tf_sD_s, "failed", cstrerror(rv));
}
return (rv);
bi_errorf(Tsynerr);
else if ((buf = do_realpath(wp[0])) == NULL) {
rv = errno;
- bi_errorf("%s: %s", wp[0], cstrerror(rv));
+ bi_errorf(Tf_sD_s, wp[0], cstrerror(rv));
if ((unsigned int)rv > 255)
rv = 255;
} else {
- shprintf("%s\n", buf);
+ shprintf(Tf_sN, buf);
afree(buf, ATEMP);
rv = 0;
}
if (ksh_isdash(fn))
fd = STDIN_FILENO;
else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
- bi_errorf("%s: %s", fn, cstrerror(errno));
+ bi_errorf(Tf_sD_s, fn, cstrerror(errno));
rv = 1;
continue;
}
continue;
}
/* an error occured during reading */
- bi_errorf("%s: %s", fn, cstrerror(errno));
+ bi_errorf(Tf_sD_s, fn, cstrerror(errno));
rv = 1;
break;
} else if (n == 0)
rv = ksh_sigmask(SIGPIPE);
} else {
/* an error occured during writing */
- bi_errorf("%s: %s", "<stdout>",
+ bi_errorf(Tf_sD_s, "<stdout>",
cstrerror(errno));
rv = 1;
}
if (!wp[0] || wp[1])
bi_errorf(Tsynerr);
else if (parse_usec(wp[0], &tv))
- bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
+ bi_errorf(Tf_sD_s_qs, Tsynerr, cstrerror(errno), wp[0]);
else {
#ifndef MKSH_NOPROSPECTOFWORK
sigset_t omask, bmask;
*/
rv = 0;
else
- bi_errorf("%s: %s", Tselect, cstrerror(errno));
+ bi_errorf(Tf_sD_s, Tselect, cstrerror(errno));
#ifndef MKSH_NOPROSPECTOFWORK
/* this will re-schedule signal delivery */
sigprocmask(SIG_SETMASK, &omask, NULL);
c_suspend(const char **wp)
{
if (wp[1] != NULL) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
if (Flag(FLOGIN)) {
#include <sys/file.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.152 2016/01/14 23:18:08 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.158 2016/08/04 20:31:00 tg Exp $");
Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign;
static off_t histfsize;
#endif
-static const char Tnot_in_history[] = "not in history";
-#define Thistory (Tnot_in_history + 7)
-
-static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
-#define Tspdollaru (TFCEDIT_dollaru + 18)
-
/* HISTSIZE default: size of saved history, persistent or standard */
#ifdef MKSH_SMALL
#define MKSH_DEFHISTSIZE 255
else if (!last)
last = p;
else {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
break;
if (!first && (first = *wp))
wp++;
if (last || *wp) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
xp += rep_len;
}
if (!any_subst) {
- bi_errorf("bad substitution");
+ bi_errorf(Tbadsubst);
return (1);
}
len = strlen(s) + 1;
if (!last && (last = *wp))
wp++;
if (*wp) {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (1);
}
if (!first) {
for (hp = rflag ? hlast : hfirst;
hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
if (!nflag)
- shf_fprintf(shl_stdout, "%lu",
+ shf_fprintf(shl_stdout, Tf_lu,
(unsigned long)hist_source->line -
(unsigned long)(histptr - hp));
shf_putc('\t', shl_stdout);
*t++ = '\n';
s = t;
}
- shf_fprintf(shl_stdout, "%s\n", s);
+ shf_fprintf(shl_stdout, Tf_sN, s);
}
shf_flush(shl_stdout);
return (0);
tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
if (!(shf = tf->shf)) {
- bi_errorf("can't %s temporary file %s: %s",
- "create", tf->tffn, cstrerror(errno));
+ bi_errorf(Tf_temp, Tcreate, tf->tffn, cstrerror(errno));
return (1);
}
for (hp = rflag ? hlast : hfirst;
hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
- shf_fprintf(shf, "%s\n", *hp);
+ shf_fprintf(shf, Tf_sN, *hp);
if (shf_close(shf) == -1) {
- bi_errorf("can't %s temporary file %s: %s",
- "write", tf->tffn, cstrerror(errno));
+ bi_errorf(Tf_temp, Twrite, tf->tffn, cstrerror(errno));
return (1);
}
/* Ignore setstr errors here (arbitrary) */
setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
- /* XXX: source should not get trashed by this.. */
- {
- Source *sold = source;
- int ret;
-
- ret = command(editor ? editor : TFCEDIT_dollaru, 0);
- source = sold;
- if (ret)
- return (ret);
- }
+ if ((optc = command(editor ? editor : TFCEDIT_dollaru, 0)))
+ return (optc);
{
struct stat statb;
ssize_t n;
if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
- bi_errorf("can't %s temporary file %s: %s",
- "open", tf->tffn, cstrerror(errno));
+ bi_errorf(Tf_temp, Topen, tf->tffn, cstrerror(errno));
return (1);
}
if (stat(tf->tffn, &statb) < 0)
n = 128;
else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
- bi_errorf("%s %s too large: %lu", Thistory,
- "file", (unsigned long)statb.st_size);
+ bi_errorf(Tf_toolarge, Thistory,
+ Tfile, (unsigned long)statb.st_size);
goto errout;
} else
n = (size_t)statb.st_size + 1;
XcheckN(xs, xp, Xlength(xs, xp));
}
if (n < 0) {
- bi_errorf("can't %s temporary file %s: %s",
- "read", tf->tffn, cstrerror(shf_errno(shf)));
+ bi_errorf(Tf_temp, Tread, tf->tffn,
+ cstrerror(shf_errno(shf)));
errout:
shf_close(shf);
return (1);
hist_execute(char *cmd, Area *areap)
{
static int last_line = -1;
- Source *sold;
- int ret;
/* Back up over last histsave */
if (histptr >= history && last_line != hist_source->line) {
afree(cmd, areap);
cmd = *histptr;
/* pdksh says POSIX doesn’t say this is done, testsuite needs it */
- shellf("%s\n", cmd);
+ shellf(Tf_sN, cmd);
/*-
* Commands are executed here instead of pushing them onto the
* X=y fc -e - 42 2> /dev/null
* are to effect the repeated commands environment.
*/
- /* XXX: source should not get trashed by this.. */
- sold = source;
- ret = command(cmd, 0);
- source = sold;
- return (ret);
+ return (command(cmd, 0));
}
/*
if (approx)
hp = hist_get_oldest();
else {
- bi_errorf("%s: %s", str, Tnot_in_history);
+ bi_errorf(Tf_sD_s, str, Tnot_in_history);
hp = NULL;
}
} else if ((size_t)hp > (size_t)histptr) {
if (approx)
hp = hist_get_newest(allow_cur);
else {
- bi_errorf("%s: %s", str, Tnot_in_history);
+ bi_errorf(Tf_sD_s, str, Tnot_in_history);
hp = NULL;
}
} else if (!allow_cur && hp == histptr) {
- bi_errorf("%s: %s", str, "invalid range");
+ bi_errorf(Tf_sD_s, str, "invalid range");
hp = NULL;
}
} else {
/* the -1 is to avoid the current fc command */
if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
- bi_errorf("%s: %s", str, Tnot_in_history);
+ bi_errorf(Tf_sD_s, str, Tnot_in_history);
else
hp = &history[n];
}
strndupx(c, cmd, ccp - cmd, APERM);
if (svmode != HIST_APPEND) {
- if (ignoredups && !strcmp(c, *histptr)
+ if (ignoredups &&
+ histptr >= history &&
+ !strcmp(c, *histptr)
#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
&& !histsync()
#endif
goto retry;
}
if (hs != hist_init_retry)
- bi_errorf("can't %s %s: %s",
+ bi_errorf(Tf_cant,
"unlink HISTFILE", hname, cstrerror(errno));
histfsize = 0;
return;
{
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. */
+ /* populate sigtraps based on sys_signame and sys_siglist */
for (i = 1; i < ksh_NSIG; i++) {
sigtraps[i].signal = i;
#if HAVE_SYS_SIGNAME
cs = sys_signame[i];
#else
- const struct mksh_sigpair *pair = mksh_sigpairs;
+ 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);
+ sigtraps[i].name = null;
else {
char *s;
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(Tf_d, i);
#if HAVE_SYS_SIGLIST
sigtraps[i].mess = sys_siglist[i];
#elif HAVE_STRSIGNAL
#endif
if ((sigtraps[i].mess == NULL) ||
(sigtraps[i].mess[0] == '\0'))
- sigtraps[i].mess = shf_smprintf("%s %d",
+ sigtraps[i].mess = shf_smprintf(Tf_sd,
"Signal", i);
}
sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT;
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.117 2016/01/14 23:18:09 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.121 2016/07/25 00:04:44 tg Exp $");
#if HAVE_KILLPG
#define mksh_killpg killpg
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 */
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, Tf_ssfaileds,
+ Tj_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, Tf_ssfaileds,
+ Tj_suspend, "setpgid", cstrerror(errno));
}
}
}
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, Tf_ssfaileds,
+ Tj_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, Tf_ssfaileds,
+ Tj_suspend, "tcsetpgrp", cstrerror(errno));
ttypgrp_ok = false;
}
}
pid_t ttypgrp;
if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
- warningf(false, "%s: %s %s: %s",
- "j_init", "tcgetpgrp", "failed",
+ warningf(false, Tf_ssfaileds,
+ "j_init", "tcgetpgrp",
cstrerror(errno));
ttypgrp_ok = false;
break;
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, Tf_ssfaileds,
+ "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, Tf_ssfaileds,
+ "j_init", "tcsetpgrp",
cstrerror(errno));
ttypgrp_ok = false;
} else
}
#ifndef MKSH_DISABLE_TTY_WARNING
if (use_tty && !ttypgrp_ok)
- warningf(false, "%s: %s", "warning",
+ warningf(false, Tf_sD_s, "warning",
"won't have full job control");
#endif
} else {
errno = 0;
/* this is gonna annoy users; complain to your distro, people! */
if (nice(ness) == -1 && (eno = errno) != 0)
- warningf(false, "%s: %s", "bgnice", cstrerror(eno));
+ warningf(false, Tf_sD_s, "bgnice", cstrerror(eno));
#else
(void)nice(ness);
#endif
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)
#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);
if (Flag(FTALKING)) {
shf_fprintf(shl_out, "[%d]", j->job);
for (p = j->proc_list; p; p = p->next)
- shf_fprintf(shl_out, " %d",
+ shf_fprintf(shl_out, Tf__d,
(int)p->pid);
shf_putchar('\n', shl_out);
shf_flush(shl_out);
j = last_job;
if (!j || !(j->flags & JF_STARTED)) {
if (!j)
- warningf(true, "%s: %s", "waitlast", "no last job");
+ warningf(true, Tf_sD_s, "waitlast", "no last job");
else
- internal_warningf("%s: %s", "waitlast", "not started");
+ internal_warningf(Tf_sD_s, "waitlast", Tnot_started);
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
if (ecode != JL_NOSUCH)
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (-1);
}
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (1);
}
if (j->pgrp == 0) {
/* started when !Flag(FMONITOR) */
if (kill_job(j, sig) < 0) {
- bi_errorf("%s: %s", cp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errno));
rv = 1;
}
} else {
mksh_killpg(j->pgrp, SIGCONT);
#endif
if (mksh_killpg(j->pgrp, sig) < 0) {
- bi_errorf("%s: %s", cp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, cp, cstrerror(errno));
rv = 1;
}
}
if ((j = j_lookup(cp, &ecode)) == NULL) {
sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (1);
}
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(Tf_ldfailed,
+ "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);
}
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, Tf_ldfailed,
"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(Tf_s_sD_s, "can't continue job",
cp, cstrerror(eno));
return (1);
}
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+ bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
return (1);
}
} else
for (j = job_list; j; j = tmp) {
tmp = j->next;
if (j->flags & JF_REMOVE)
- remove_job(j, "jobs");
+ remove_job(j, Tjobs);
}
#ifndef MKSH_NOPROSPECTOFWORK
sigprocmask(SIG_SETMASK, &omask, NULL);
if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
remove_job(async_job, "async");
if (!(j->flags & JF_STARTED)) {
- internal_warningf("%s: %s", "j_async", "job not started");
+ internal_warningf(Tf_sD_s, "j_async", Tjob_not_started);
return;
}
async_job = j;
(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, Tf_ldfailed,
"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);
/* 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;
}
* group leader (ie, !FMONITOR). We arbitrarily return
* last pid (which is what $! returns).
*/
- shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
+ shf_fprintf(shf, Tf_dN, (int)(j->pgrp ? j->pgrp :
(j->last_proc ? j->last_proc->pid : 0)));
return;
}
while (p && p->state == state && p->status == status) {
if (how == JP_LONG)
shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
- (int)p->pid, " ", p->command,
+ (int)p->pid, T1space, p->command,
p->next ? "|" : null);
else if (how == JP_MEDIUM)
- shf_fprintf(shf, " %s%s", p->command,
+ shf_fprintf(shf, Tf__ss, p->command,
p->next ? "|" : null);
p = p->next;
}
curr = *prev;
}
if (curr != j) {
- internal_warningf("remove_job: job %s (%s)", "not found", where);
+ internal_warningf("remove_job: job %s (%s)", Tnot_found, where);
return;
}
*prev = curr->next;
break;
case 1:
#ifndef MKSH_DISABLE_TTY_WARNING
- warningf(false, "%s: %s %s: %s",
- "No controlling tty", "open", "/dev/tty",
- cstrerror(errno));
+ warningf(false, Tf_sD_s_sD_s,
+ "No controlling tty", Topen, T_devtty, cstrerror(errno));
#endif
break;
case 2:
#ifndef MKSH_DISABLE_TTY_WARNING
- warningf(false, "%s: %s", "can't find tty fd", cstrerror(errno));
+ warningf(false, Tf_sD_s_s, Tcant_find, Ttty_fd,
+ cstrerror(errno));
#endif
break;
case 3:
- warningf(false, "%s: %s %s: %s", "j_ttyinit",
- "dup of tty fd", "failed", cstrerror(errno));
+ warningf(false, Tf_ssfaileds, "j_ttyinit",
+ Ttty_fd_dupof, cstrerror(errno));
break;
case 4:
- warningf(false, "%s: %s: %s", "j_ttyinit",
+ warningf(false, Tf_sD_sD_s, "j_ttyinit",
"can't set close-on-exec flag", cstrerror(errno));
break;
}
/*-
- * Copyright (c) 2009, 2010, 2011, 2013, 2014
+ * Copyright (c) 2009, 2010, 2011, 2013, 2014, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
*/
#include "sh.h"
+#ifdef MKSH_ALLOC_CATCH_UNDERRUNS
+#include <err.h>
+#endif
-__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.23 2015/11/29 17:05:01 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)
#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;
#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) {
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 */
void
afreeall(Area *ap)
{
- ALLOC_ITEM *lp;
+ struct lalloc_common *lp;
/* traverse group (linked list) */
while ((lp = ap->next) != NULL) {
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.218 2016/01/20 21:34:12 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.228 2016/08/01 21:38:03 tg Exp $");
/*
* states while lexing word
break;
}
} else if (c == '/') {
+ c2 = ADELIM;
+ parse_adelim_slash:
*wp++ = CHAR;
*wp++ = c;
if ((c = getsc()) == '/') {
- *wp++ = ADELIM;
+ *wp++ = c2;
*wp++ = c;
} else
ungetsc(c);
statep->ls_adelim.num = 1;
statep->nparen = 0;
break;
+ } else if (c == '@') {
+ c2 = getsc();
+ ungetsc(c2);
+ if (c2 == '/') {
+ c2 = CHAR;
+ goto parse_adelim_slash;
+ }
}
/*
* If this is a trim operation,
*wp++ = COMSUB;
/*
* We need to know whether we are within double
- * quotes, since most shells translate \" to "
- * within "…`…\"…`…". This is not done in POSIX
- * mode (§2.2.3 Double-Quotes: “The backquote
- * shall retain its special meaning introducing
- * the other form of command substitution (see
- * Command Substitution). 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.”; §2.6.3 Command
- * Substitution: “Within the backquoted style
- * of command substitution, <backslash> shall
- * retain its literal meaning, except when
- * followed by: '$', '`', or <backslash>. The
- * search for the matching backquote shall be
- * satisfied by the first unquoted non-escaped
- * backquote; during this search, if a
- * non-escaped backquote is encountered[…],
- * undefined results occur.”).
+ * 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;
-#ifdef austingroupbugs1015_is_still_not_resolved
- if (Flag(FPOSIX))
- break;
-#endif
s2 = statep;
base = state_info.base;
while (/* CONSTCOND */ 1) {
#ifndef MKSH_LEGACY_MODE
(c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
#endif
- c == '<' || c == '>')) {
+ c == '<' || c == '>') && ((c2 = Xlength(ws, wp)) == 0 ||
+ (c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) {
struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
- if (Xlength(ws, wp) == 0)
- iop->unit = c == '<' ? 0 : 1;
- else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) {
- if (dp[c2] != CHAR)
- goto no_iop;
- if (!ksh_isdigit(dp[c2 + 1]))
- goto no_iop;
- iop->unit = iop->unit * 10 + ksh_numdig(dp[c2 + 1]);
- if (iop->unit >= FDBASE)
- goto no_iop;
- }
+ iop->unit = c2 == 2 ? ksh_numdig(dp[1]) : c == '<' ? 0 : 1;
if (c == '&') {
if ((c2 = getsc()) != '>') {
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 <<EOF' can cause this */
+ yyerror(Tf_heredoc,
+ evalstr((*p)->delim, 0));
}
return (c);
}
while (c != '\n') {
if (!c)
/* oops, reached EOF */
- yyerror("%s '%s' unclosed\n", "here document", eof);
+ yyerror(Tf_heredoc, eof);
/* store character */
Xcheck(xs, xp);
Xput(xs, xp, c);
s->start = s->str = "\n";
s->type = SEOF;
} else {
- s->start = s->str = " ";
+ s->start = s->str = T1space;
s->type = SWORDS;
}
break;
ksh_tmout_state = TMOUT_READING;
alarm(ksh_tmout);
}
- if (interactive)
+ if (interactive) {
+ if (cur_prompt == PS1)
+ histsave(&s->line, NULL, HIST_FLUSH, true);
change_winsz();
+ }
#ifndef MKSH_NO_CMDLINE_EDITING
if (have_tty && (
#if !MKSH_S_NOVI
if (*ps1 != '!' || *++ps1 == '!')
shf_putchar(*ps1++, shf);
else
- shf_fprintf(shf, "%lu", s ?
+ shf_fprintf(shf, Tf_lu, s ?
(unsigned long)s->line + 1 : 0UL);
ps1 = shf_sclose(shf);
saved_lineno = current_lineno;
{
char c;
enum parse_state {
- PS_INITIAL, PS_SAW_HASH, PS_IDENT,
- PS_NUMBER, PS_VAR1
+ PS_INITIAL, PS_SAW_PERCENT, PS_SAW_HASH, PS_SAW_BANG,
+ PS_IDENT, PS_NUMBER, PS_VAR1
} state = PS_INITIAL;
while (/* CONSTCOND */ 1) {
c = getsc();
/* State machine to figure out where the variable part ends. */
switch (state) {
+ case PS_SAW_HASH:
+ if (ctype(c, C_VAR1)) {
+ char c2;
+
+ c2 = getsc();
+ ungetsc(c2);
+ if (c2 != /*{*/ '}') {
+ ungetsc(c);
+ goto out;
+ }
+ }
+ goto ps_common;
+ case PS_SAW_BANG:
+ switch (c) {
+ case '@':
+ case '#':
+ case '-':
+ case '?':
+ goto out;
+ }
+ goto ps_common;
case PS_INITIAL:
- if (c == '#' || c == '!' || c == '%') {
+ switch (c) {
+ case '%':
+ state = PS_SAW_PERCENT;
+ goto next;
+ case '#':
state = PS_SAW_HASH;
- break;
+ goto next;
+ case '!':
+ state = PS_SAW_BANG;
+ goto next;
}
/* FALLTHROUGH */
- case PS_SAW_HASH:
+ case PS_SAW_PERCENT:
+ ps_common:
if (ksh_isalphx(c))
state = PS_IDENT;
else if (ksh_isdigit(c))
state = PS_NUMBER;
- else if (c == '#') {
- if (state == PS_SAW_HASH) {
- char c2;
-
- c2 = getsc();
- ungetsc(c2);
- if (c2 != /*{*/ '}') {
- ungetsc(c);
- goto out;
- }
- }
- state = PS_VAR1;
- } else if (ctype(c, C_VAR1))
+ else if (ctype(c, C_VAR1))
state = PS_VAR1;
else
goto out;
}
goto out;
}
+ next:
break;
case PS_NUMBER:
if (!ksh_isdigit(c))
-.\" $MirOS: src/bin/mksh/lksh.1,v 1.16 2015/12/12 22:25:14 tg Exp $
+.\" $MirOS: src/bin/mksh/lksh.1,v 1.18 2016/08/10 18:20:05 tg Exp $
.\"-
-.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015
+.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016
.\" mirabilos <m@mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
.\" * ^ 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.
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: December 12 2015 $
+.Dd $Mdocdate: August 10 2016 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
The compiler is permitted to delete all data and crash the system
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
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
+ * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#include <locale.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.306 2015/10/09 21:36:57 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.317 2016/08/04 20:51:35 tg Exp $");
extern char **environ;
static const char *initcoms[] = {
Ttypeset, "-r", initvsn, NULL,
- Ttypeset, "-x", "HOME", "PATH", "SHELL", NULL,
+ Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias,
"integer=\\typeset -i",
/* this is what AT&T ksh seems to track, with the addition of emacs */
Talias, "-tU",
Tcat, "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
- "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
+ "make", "mv", "pr", "rm", "sed", Tsh, "vi", "who", NULL,
NULL
};
static const char *restr_com[] = {
- Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
+ Ttypeset, "-r", TPATH, "ENV", TSHELL, NULL
};
static bool initio_done;
} *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 */
/* 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);
}
static const char *empty_argv[] = {
- "mksh", NULL
+ Tmksh, NULL
};
static uint8_t
/* 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;
#endif
/*
* this is uniform across all OSes unless it
- * breaks somewhere; don't try to optimise,
+ * breaks somewhere hard; don't try to optimise,
* e.g. add stuff for Interix or remove /usr
* for HURD, because e.g. Debian GNU/HURD is
* "keeping a regular /usr"; this is supposed
* Set PATH to def_path (will set the path global variable).
* (import of environment below will probably change this setting).
*/
- vp = global("PATH");
+ vp = global(TPATH);
/* setstr can't fail here */
setstr(vp, def_path, KSH_RETURN_ERROR);
substitute(initsubs, 0);
/* Figure out the current working directory and set $PWD */
- vp = global("PWD");
+ vp = global(TPWD);
cp = str_val(vp);
/* Try to use existing $PWD if it is valid */
- set_current_wd((mksh_abspath(cp) && test_eval(NULL, TO_FILEQ, cp, ".",
- true)) ? cp : NULL);
+ set_current_wd((mksh_abspath(cp) && test_eval(NULL, TO_FILEQ, cp,
+ Tdot, true)) ? cp : NULL);
if (current_wd[0])
simplify_path(current_wd);
/* Only set pwd if we know where we are or if it had a bogus value */
} else if (Flag(FCOMMAND)) {
s = pushs(SSTRINGCMDLINE, ATEMP);
if (!(s->start = s->str = argv[argi++]))
- errorf("%s %s", "-c", "requires an argument");
+ errorf(Tf_optfoo, "", "", 'c', Treq_arg);
while (*s->str) {
if (*s->str != ' ' && ctype(*s->str, C_QUOTE))
break;
SHF_MAPHI | SHF_CLEXEC);
if (s->u.shf == NULL) {
shl_stdout_ok = false;
- warningf(true, "%s: %s", s->file, cstrerror(errno));
+ warningf(true, Tf_sD_s, s->file, cstrerror(errno));
/* mandated by SUSv4 */
exstat = 127;
unwind(LERROR);
unwind(i);
/* NOTREACHED */
default:
- internal_errorf("%s %d", "include", i);
+ internal_errorf("include %d", i);
/* NOTREACHED */
}
}
int
command(const char *comm, int line)
{
- Source *s;
+ Source *s, *sold = source;
+ int rv;
s = pushs(SSTRING, ATEMP);
s->start = s->str = comm;
s->line = line;
- return (shell(s, false));
+ rv = shell(s, false);
+ source = sold;
+ return (rv);
}
/*
default:
source = old_source;
quitenv(NULL);
- internal_errorf("%s %d", "shell", i);
+ internal_errorf("shell %d", i);
/* NOTREACHED */
}
while (/* CONSTCOND */ 1) {
/* FALLTHROUGH */
default:
quitenv(NULL);
- /*
- * quitenv() may have reclaimed the memory
- * used by source which will end badly when
- * we jump to a function that expects it to
- * be valid
- */
- source = NULL;
}
}
}
* 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;
/* 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 */
remove_temps(e->temps);
e->temps = NULL;
+
+ /*
+ * if the memory backing source is reclaimed, things
+ * will end up badly when a function expecting it to
+ * be valid is run; a NULL pointer is easily debugged
+ */
+ if (source && source->areap == &e->area)
+ source = NULL;
afreeall(&e->area);
}
static void
remove_temps(struct temp *tp)
{
- for (; tp != NULL; tp = tp->next)
+ while (tp) {
if (tp->pid == procpid)
unlink(tp->tffn);
+ tp = tp->next;
+ }
}
/*
goto got_fd;
}
#endif
- if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
+ if ((fd = open(T_devtty, O_RDWR, 0)) >= 0) {
do_close = true;
goto got_fd;
}
{
if (fmt) {
if (flags & VWARNINGF_INTERNAL)
- shf_fprintf(shl_out, "internal error: ");
+ shf_fprintf(shl_out, Tf_sD_, "internal error");
if (flags & VWARNINGF_ERRORPREFIX)
error_prefix(tobool(flags & VWARNINGF_FILELINE));
if ((flags & VWARNINGF_BUILTIN) &&
/* not set when main() calls parse_args() */
builtin_argv0 && builtin_argv0 != kshname)
- shf_fprintf(shl_out, "%s: ", builtin_argv0);
+ shf_fprintf(shl_out, Tf_sD_, builtin_argv0);
shf_vfprintf(shl_out, fmt, ap);
shf_putchar('\n', shl_out);
}
/* Avoid foo: foo[2]: ... */
if (!fileline || !source || !source->file ||
strcmp(source->file, kshname) != 0)
- shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
+ shf_fprintf(shl_out, Tf_sD_, kshname + (*kshname == '-'));
if (fileline && source && source->file != NULL) {
shf_fprintf(shl_out, "%s[%lu]: ", source->file,
(unsigned long)(source->errline ?
if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
errorf("cannot get home directory");
- lfp = shf_smprintf("%s/mksh-dbg.txt", lfp);
+ lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
}
if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
int
check_fd(const char *name, int mode, const char **emsgp)
{
- int fd = 0, fl;
+ int fd, fl;
- if (name[0] == 'p' && !name[1])
+ if (!name[0] || name[1])
+ goto illegal_fd_name;
+ if (name[0] == 'p')
return (coproc_getfd(mode, emsgp));
- 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 (!ksh_isdigit(name[0])) {
+ illegal_fd_name:
if (emsgp)
*emsgp = "illegal file descriptor name";
return (-1);
}
- if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
+
+ if ((fl = fcntl((fd = ksh_numdig(name[0])), F_GETFL, 0)) < 0) {
if (emsgp)
*emsgp = "bad file descriptor";
return (-1);
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015
+ * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#include <grp.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.240 2015/10/09 16:11:17 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.245 2016/08/01 18:42:42 tg Exp $");
#define KSH_CHVT_FLAG
#ifdef MKSH_SMALL
void
setctypes(const char *s, int t)
{
- unsigned int i;
-
if (t & C_IFS) {
- for (i = 0; i < UCHAR_MAX + 1; i++)
+ unsigned int i = 0;
+
+ while (++i <= UCHAR_MAX)
chtypes[i] &= ~C_IFS;
- /* include \0 in C_IFS */
+ /* include '\0' in C_IFS */
chtypes[0] |= C_IFS;
}
while (*s != 0)
*/
int
parse_args(const char **argv,
- /* OF_CMDLINE or OF_SET */
+ /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
int what,
bool *setargsp)
{
else if ((i != (size_t)-1) && (OFF(i) & what))
change_flag((enum sh_flag)i, what, set);
else {
- bi_errorf("%s: %s", go.optarg, "bad option");
+ bi_errorf(Tf_sD_s, go.optarg,
+ Tunknown_option);
return (-1);
}
break;
if (*array)
ccp = skip_varname(array, false);
if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
- bi_errorf("%s: %s", array, "is not an identifier");
+ bi_errorf(Tf_sD_s, array, Tnot_ident);
return (-1);
}
}
go->buf[0] = c;
go->optarg = go->buf;
} else {
- warningf(true, "%s%s-%c: %s",
+ warningf(true, Tf_optfoo,
(go->flags & GF_NONAME) ? "" : argv[0],
- (go->flags & GF_NONAME) ? "" : ": ", c,
- "unknown option");
+ (go->flags & GF_NONAME) ? "" : Tcolsp,
+ c, Tunknown_option);
if (go->flags & GF_ERROR)
bi_errorfz();
}
go->optarg = go->buf;
return (':');
}
- warningf(true, "%s%s-%c: %s",
+ warningf(true, Tf_optfoo,
(go->flags & GF_NONAME) ? "" : argv[0],
- (go->flags & GF_NONAME) ? "" : ": ", c,
- "requires an argument");
+ (go->flags & GF_NONAME) ? "" : Tcolsp,
+ c, Treq_arg);
if (go->flags & GF_ERROR)
bi_errorfz();
return ('?');
shf_puts(str, shf);
else
shf_fprintf(shf, "%*s%*s",
- max_col, str, nspace, null);
+ (int)max_col, str, (int)nspace, null);
}
shf_putchar('\n', shf);
}
/* upath is a relative pathname, prepend cwd */
if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
return (NULL);
- ipath = shf_smprintf("%s%s%s", tp, "/", upath);
+ ipath = shf_smprintf(Tf_sss, tp, "/", upath);
afree(tp, ATEMP);
}
* otherwise continue with currently resolved prefix
*/
/* append rest of current input path to link target */
- tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
+ tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
afree(ipath, ATEMP);
ip = ipath = tp;
if (!mksh_abspath(ldest)) {
wp += builtin_opt.optind;
if (Flag(FRESTRICTED)) {
- bi_errorf("restricted shell - can't cd");
+ bi_errorf(Tcant_cd);
return (2);
}
- pwd_s = global("PWD");
- oldpwd_s = global("OLDPWD");
+ pwd_s = global(TPWD);
+ oldpwd_s = global(TOLDPWD);
if (!wp[0]) {
/* No arguments - go home */
allocd = NULL;
dir = str_val(oldpwd_s);
if (dir == null) {
- bi_errorf("no OLDPWD");
+ bi_errorf(Tno_OLDPWD);
return (2);
}
printpath = true;
* we don't
*/
if ((cp = strstr(current_wd, wp[0])) == NULL) {
- bi_errorf("bad substitution");
+ bi_errorf(Tbadsubst);
return (2);
}
/*-
memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
printpath = true;
} else {
- bi_errorf("too many arguments");
+ bi_errorf(Ttoo_many_args);
return (2);
}
if (rv < 0) {
if (cdnode)
- bi_errorf("%s: %s", dir, "bad directory");
+ bi_errorf(Tf_sD_s, dir, "bad directory");
else
- bi_errorf("%s: %s", tryp, cstrerror(errno));
+ bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
afree(allocd, ATEMP);
Xfree(xs, xp);
return (2);
rv = 1;
}
if (printpath || cdnode)
- shprintf("%s\n", pwd);
+ shprintf(Tf_sN, pwd);
afree(allocd, ATEMP);
Xfree(xs, xp);
memmove(cp + 1, cp, /* /dev/tty */ 8);
dv = cp + 1;
if (stat(dv, &sb)) {
- errorf("%s: %s: %s", "chvt",
+ errorf(Tf_sD_sD_s, "chvt",
"can't find tty", go->optarg);
}
}
}
if (!(sb.st_mode & S_IFCHR))
- errorf("%s: %s: %s", "chvt", "not a char device", dv);
+ errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
#ifndef MKSH_DISABLE_REVOKE_WARNING
#if HAVE_REVOKE
if (revoke(dv))
#endif
- warningf(false, "%s: %s %s", "chvt",
+ warningf(false, Tf_sD_s_s, "chvt",
"new shell is potentially insecure, can't revoke",
dv);
#endif
if ((fd = binopen2(dv, O_RDWR)) < 0) {
sleep(1);
if ((fd = binopen2(dv, O_RDWR)) < 0) {
- errorf("%s: %s %s", "chvt", "can't open", dv);
+ errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
}
}
if (go->optarg[0] != '!') {
switch (fork()) {
case -1:
- errorf("%s: %s %s", "chvt", "fork", "failed");
+ errorf(Tf_sD_s_s, "chvt", "fork", "failed");
case 0:
break;
default:
}
}
if (setsid() == -1)
- errorf("%s: %s %s", "chvt", "setsid", "failed");
+ errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
if (go->optarg[0] != '-') {
if (ioctl(fd, TIOCSCTTY, NULL) == -1)
- errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed");
+ errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
if (tcflush(fd, TCIOFLUSH))
- errorf("%s: %s %s", "chvt", "TCIOFLUSH", "failed");
+ errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
}
ksh_dup2(fd, 0, false);
ksh_dup2(fd, 1, false);
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.388 2016/01/20 22:04:54 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.413 2016/08/10 18:20:05 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,
.\" * ^ 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.
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: January 20 2016 $
+.Dd $Mdocdate: August 10 2016 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
.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
.Pq Sq \&"
quotes all characters, except
.Ql $ ,
-.Ql \`
-and
.Ql \e ,
-up to the next unquoted double quote.
+and
+.Ql \` ,
+up to the next unescaped double quote.
.Ql $
and
.Ql \`
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.
+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
-.Ql \e ,
+.Ql \&" ,
.Ql $ ,
-.Ql \` ,
+.Ql \e ,
or
-.Ql \&" ,
-it is replaced by the second character; if it is followed by a newline, both
-the
+.Ql \` ,
+only the
+.Ql \e
+is removed, i.e. the combination is replaced by the second character;
+if it is followed by a newline, both the
.Ql \e
and the newline are stripped; otherwise, both the
.Ql \e
followed by any of
.Ql $ ,
.Ql \` ,
-.Ql \&"
-.Pq currently, and violating Tn POSIX ,
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
.Ic nameref
command (which is an alias for
.Ic typeset Fl n ) .
+.Ar name
+cannot be one of most special parameters (see below).
.Pp
.It Pf ${! Ns Ar name Ns \&[*]}
.It Pf ${! Ns Ar name Ns \&[@]}
.Pp
.Sm off
.It Xo
+.Pf ${ Ar name
+.Pf @/ Ar pattern / Ar string No }
+.Xc
+.Sm on
+The same as
+.Sm off
+.Xo
+.Pf ${ Ar name
+.Pf // Ar pattern / Ar string No } ,
+.Xc
+.Sm on
+except that both
+.Ar pattern
+and
+.Ar string
+are expanded anew for each iteration.
+.Pp
+.Sm off
+.It Xo
.Pf ${ Ar name : Ns Ar pos
.Pf : Ns Ar len Ns }
.Xc
If this parameter is found to be set after any profile files are executed, the
expanded value is used as a shell startup file.
It typically contains function and alias definitions.
-.It Ev ERRNO
-Integer value of the shell's
-.Va errno
-variable.
-It indicates the reason the last system call failed.
-Not yet implemented.
+.It Ev EPOCHREALTIME
+Time since the epoch, as returned by
+.Xr gettimeofday 2 ,
+formatted as decimal
+.Va tv_sec
+followed by a dot
+.Pq Sq \&.
+and
+.Va tv_usec
+padded to exactly six decimal digits.
.It Ev EXECSHELL
If set, this parameter is assumed to contain the shell that is to be used to
execute commands that
The real group id of the shell.
.It Ev KSHUID
The real user id of the shell.
+.It Ev KSH_MATCH
+The last matched string.
+In a future version, this will be an indexed array,
+with indexes 1 and up capturing matching groups.
+Set by string comparisons (== and !=) in double-bracket test
+expressions when a match is found (when != returns false), by
+.Ic case
+when a match is encountered, and by the substitution operations
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf # Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf ## Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf % Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf %% Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf / Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf /# Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf /% Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf // Ar pat / Ar rpl No } ,
+.Sm on
+.Xc
+and
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf @/ Ar pat / Ar rpl No } .
+.Sm on
+.Xc
+See the end of the Emacs editing mode documentation for an example.
.It Ev KSH_VERSION
The name and version of the shell (read-only).
See also the version commands in
Always set, defaults to 24.
See
.Ev COLUMNS .
-.It Ev EPOCHREALTIME
-Time since the epoch, as returned by
-.Xr gettimeofday 2 ,
-formatted as decimal
-.Va tv_sec
-followed by a dot
-.Pq Sq .\&
-and
-.Va tv_usec
-padded to exactly six decimal digits.
.It Ev OLDPWD
The previous working directory.
Unset if
.Ql + ,
or
.Ql \- ,
-the value of the
+the simplified value of the
.Ev HOME ,
.Ev PWD ,
or
Standard input is duplicated from file descriptor
.Ar fd .
.Ar fd
-can be a number, indicating the number of an existing file descriptor;
+can be a single digit, indicating the number of an existing file descriptor;
the letter
.Ql p ,
indicating the file descriptor associated with the output of the current
co-process; or the character
.Ql \- ,
indicating standard input is to be closed.
-Note that
-.Ar fd
-is limited to a single digit in most shell implementations.
.It \*(Gt& Ns Ar fd
Same as
.Ic \*(Lt& ,
.Nm bash
extension supported by
.Nm
-which also supports the preceding explicit fd number, for example,
+which also supports the preceding explicit fd digit, for example,
.Ic 3&\*(Gt Ns Ar file
is the same as
.Ic 3\*(Gt Ns Ar file 2\*(Gt&3
In any of the above redirections, the file descriptor that is redirected
(i.e. standard input or standard output)
can be explicitly given by preceding the
-redirection with a number (portably, only a single digit).
+redirection with a single digit.
Parameter, command, and arithmetic
substitutions, tilde substitutions, and (if the shell is interactive)
file name generation are all performed on the
Binary operators:
.Bd -literal -offset indent
,
-= += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+= += \-= *= /= %= \*(Lt\*(Lt= \*(Gt\*(Gt= \*(ha\*(Lt= \*(ha\*(Gt= &= \*(ha= \*(Ba=
\*(Ba\*(Ba
&&
\*(Ba
&
== !=
\*(Lt \*(Lt= \*(Gt \*(Gt=
-\*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt \*(Lt\*(Lt \*(Gt\*(Gt
+\*(Lt\*(Lt \*(Gt\*(Gt \*(ha\*(Lt \*(ha\*(Gt
+ \-
* / %
.Ed
.Ar base Ns # Ns Ar number ,
where
.Ar base
-is a decimal integer specifying the base, and
+is a decimal integer specifying the base (up to 36), and
.Ar number
is a number in the specified base.
Additionally, base-16 integers may be specified by prefixing them with
.Pq case-insensitive
in all forms of arithmetic expressions, except as numeric arguments to the
.Ic test
-built-in command.
+built-in utility.
Prefixing numbers with a sole digit zero
.Pq Sq 0
-does not cause interpretation as octal, as that's unsafe to do.
+does not cause interpretation as octal (except in POSIX mode,
+as required by the standard), as that's unsafe to do.
.Pp
As a special
.Nm mksh
.It =
Assignment; the variable on the left is set to the value on the right.
.It Xo
-.No += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt=
-.No \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+.No += \-= *= /= %= \*(Lt\*(Lt= \*(Gt\*(Gt=
+.No \*(ha\*(Lt= \*(ha\*(Gt= &= \*(ha= \*(Ba=
.Xc
Assignment operators.
.Sm off
Less than or equal, greater than, greater than or equal.
See
.Ic \*(Lt .
-.It \*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt
-Rotate left (right); the result is similar to shift (see
-.Ic \*(Lt\*(Lt )
+.It \*(Lt\*(Lt \*(Gt\*(Gt
+Shift left (right); the result is the left argument with its bits
+arithmetically (signed operation) or logically (unsigned expression)
+shifted left (right) by the amount given in the right argument.
+.It \*(ha\*(Lt \*(ha\*(Gt
+Rotate left (right); the result is similar to shift,
except that the bits shifted out at one end are shifted in
at the other end, instead of zero or sign bits.
-.It \*(Lt\*(Lt \*(Gt\*(Gt
-Shift left (right); the result is the left argument with its bits shifted left
-(right) by the amount given in the right argument.
.It + \- * /
Addition, subtraction, multiplication, and division.
.It %
.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.
.Pp
.It Xo
.Ic print
-.Oo Fl nprsu Ns Oo Ar n Oc \*(Ba
+.Oo Fl Anprsu Ns Oo Ar n Oc \*(Ba
.Fl R Op Fl en Oc
.Op Ar argument ...
.Xc
option prints to the co-process (see
.Sx Co-processes
above).
+The
+.Fl A
+option prints the character corresponding to each
+.Ar argument Ns 's value .
.Pp
The
.Fl R
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
.Ic realpath
returns 0 if the pathname either exists or can be created immediately,
i.e. all but the last component exist and are directories.
+For calls from the shell, if any options are given, an external
+.Xr realpath 1
+utility is preferred over the builtin.
.Pp
.It Xo
.Ic rename
to
.Ar to .
Both must be complete pathnames and on the same device.
-This builtin is intended for emergency situations where
-.Pa /bin/mv
-becomes unusable, and directly calls
+An external utility is preferred over this builtin,
+which is intended for emergency situations
+.Pq where Pa /bin/mv No becomes unusable
+and directly calls
.Xr rename 2 .
.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
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
.Ic until ,
.Ic while ,
or
-.Ic !\&
+.Ic \&!
statements.
For
.Ic &&
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.).
.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
builtin does not interpret backslashes and only supports the exact option
.Dq Fl n .
.It
-\&... (list is incomplete and may change for R53)
+\&... (list is incomplete and may change for R54)
.El
.Ss SH mode
Compatibility mode; intended for use with legacy scripts that
builtin does not interpret backslashes and only supports the exact option
.Dq Fl n .
.It
-\&... (list is incomplete and may change for R53)
+The substitution operations
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf # Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf ## Ar pat No } ,
+.Sm on
+.Xc
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf % Ar pat No } ,
+.Sm on
+.Xc
+and
+.Sm off
+.Xo
+.Pf ${ Ar x
+.Pf %% Ar pat No }
+.Sm on
+.Xc
+wrongly do not require a parenthesis to be escaped and do not parse extglobs.
+.It
+\&... (list is incomplete and may change for R54)
.El
.Ss Interactive input line editing
The shell supports three modes of reading command lines from a
command.
Furthermore, many editing commands are useful only on terminals with
a visible cursor.
-The default bindings were chosen to resemble corresponding
-Emacs key bindings.
The user's
.Xr tty 4
characters (e.g.\&
.Dv ERASE )
are bound to
-reasonable substitutes and override the default bindings.
+reasonable substitutes and override the default bindings;
+their customary values are shown in parentheses below.
+The default bindings were chosen to resemble corresponding
+Emacs key bindings:
.Bl -tag -width Ds
-.It abort: \*(haC, \*(haG
+.It Xo abort:
+.No INTR Pq \*(haC ,
+.No \*(haG
+.Xc
Abort the current command, empty the line buffer and
set the exit state to interrupted.
.It auto\-insert: Op Ar n
Note that \*(haI is usually generated by the TAB (tabulator) key.
.It Xo delete\-char\-backward:
.Op Ar n
-.No ERASE , \*(ha? , \*(haH
+.No ERASE Pq \*(haH ,
+.No \*(ha? , \*(haH
.Xc
Deletes
.Ar n
characters after the cursor.
.It Xo delete\-word\-backward:
.Op Ar n
-.No WERASE , \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
+.No Pfx1+ERASE Pq \*(ha[\*(haH ,
+.No WERASE Pq \*(haW ,
+.No \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
.Xc
Deletes
.Ar n
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
-normal terminal input canonicalization.
+normal terminal input canonicalisation.
.It Xo eot\-or\-delete:
.Op Ar n
-.No \*(haD
+.No EOF Pq \*(haD
.Xc
-Acts as
-.Ic eot
-if alone on a line; otherwise acts as
+If alone on a line, same as
+.Ic eot ,
+otherwise,
.Ic delete\-char\-forward .
.It error: (not bound)
Error (ring the bell).
+.It evaluate\-region: \*(ha[\*(haE
+Evaluates the text between the mark and the cursor position
+.Pq the entire line if no mark is set
+as function substitution (if it cannot be parsed, the editing state is
+unchanged and the bell is rung to signal an error); $? is updated accordingly.
.It exchange\-point\-and\-mark: \*(haX\*(haX
Places the cursor where the mark is and sets the mark to where the cursor was.
.It expand\-file: \*(ha[*
.Xc
Goes to history number
.Ar n .
-.It kill\-line: KILL
+.It Xo kill\-line:
+.No KILL Pq \*(haU
+.Xc
Deletes the entire input line.
+If Ctrl-U should only delete the line up to the cursor, use:
+.Pp
+.D1 $ bind \-m \*(haU='\*(ha[0\*(haK'
.It kill\-region: \*(haW
Deletes the input between the cursor and the mark.
.It Xo kill\-to\-eol:
.Ic search\-history
or
.Ic search\-history\-up .
-.It no\-op: QUIT
+.It Xo no\-op:
+.No QUIT Pq \*(ha\e
+.Xc
This does nothing.
.It prefix\-1: \*(ha[
Introduces a 2-character command sequence.
.It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
-Introduces a 2-character command sequence.
+Introduces a multi-character command sequence.
.It Xo prev\-hist\-word:
.Op Ar n
.No \*(ha[. , \*(ha[_
.Ic yank ,
replaces the inserted text string with the next previously killed text string.
.El
+.Pp
+The tab completion escapes characters the same way as the following code:
+.Bd -literal
+print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
+.Ed
.Ss Vi editing mode
.Em Note:
The vi command-line editing mode is orphaned, yet still functional.
for the in-memory portion of the history is slow, should use
.Xr memmove 3 .
.Pp
-Handling of backslash plus double-quote inside the (deprecated)
-.Pf \` Ns Ar command Ns \`
-form of command substitution when the substitution itself is
-also inside double quotes currently deliberately violates
-.Tn POSIX
-even in
-.Fl o Ic posix
-mode until Austin group bug 1015 has been resolved either way,
-as the current wording of the standard prohibits the current
-and historic practice of several shells which several scripts
-(admittedly wrongly) depend on.
-.Pp
This document attempts to describe
-.Nm mksh\ R52b
+.Nm mksh\ R53
and up,
.\" with vendor patches from insert-your-name-here,
compiled without any options impacting functionality, such as
#endif
#ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.757 2016/01/20 21:34:13 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.786 2016/08/12 16:48:05 tg Exp $");
#endif
-#define MKSH_VERSION "R52 2016/01/20"
+#define MKSH_VERSION "R53 2016/08/12"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
#define mkssert(e) do { } while (/* CONSTCOND */ 0)
#endif
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 521)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 530)
#error Must run Build.sh to compile this.
extern void thiswillneverbedefinedIhope(void);
int
#ifndef MKSH_NO_CMDLINE_EDITING
#define MKSH_NO_CMDLINE_EDITING /* defined */
#endif
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS /* defined */
-#endif
#undef MKSH_S_NOVI
#define MKSH_S_NOVI 1
#endif
#ifdef MKSH_SMALL
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS /* defined */
-#endif
#ifndef MKSH_NOPWNAM
#define MKSH_NOPWNAM /* defined */
#endif
#define MKSH_UNEMPLOYED 1
#endif
-/* these shall be smaller than 100 */
-#ifdef MKSH_CONSERVATIVE_FDS
#define NUFILE 32 /* Number of user-accessible files */
#define FDBASE 10 /* First file usable by Shell */
-#else
-#define NUFILE 56 /* Number of user-accessible files */
-#define FDBASE 24 /* First file usable by Shell */
-#endif
/*
* simple grouping allocator
/* 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 */
/* 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"
+
+/* string pooling: do we rely on the compiler? */
+#ifndef HAVE_STRING_POOLING
+/* no, we use our own, saves quite some space */
+#elif HAVE_STRING_POOLING == 2
+/* “on demand” */
+#ifdef __GNUC__
+/* only for GCC 4 or later, older ones can get by without */
+#if __GNUC__ < 4
+#undef HAVE_STRING_POOLING
+#endif
#else
-EXTERN const char Tsynerr[] E_INIT("syntax error");
+/* not GCC, default to on */
#endif
-EXTERN const char Tselect[] E_INIT("select");
-EXTERN const char T_typeset[] E_INIT("=typeset");
-#define Ttypeset (T_typeset + 1) /* "typeset" */
+#elif HAVE_STRING_POOLING == 0
+/* default to on, unless explicitly set to 0 */
+#undef HAVE_STRING_POOLING
+#endif
+
+#ifndef HAVE_STRING_POOLING /* helpers for pooled strings */
+EXTERN const char T4spaces[] E_INIT(" ");
+#define T1space (T4spaces + 3)
+EXTERN const char Tcolsp[] E_INIT(": ");
+EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
+#define TC_IFSWS (TC_LEX1 + 7)
+EXTERN const char TFCEDIT_dollaru[] E_INIT("${FCEDIT:-/bin/ed} $_");
+#define Tspdollaru (TFCEDIT_dollaru + 18)
+EXTERN const char Tsgdot[] E_INIT("*=.");
+EXTERN const char Taugo[] E_INIT("augo");
+EXTERN const char Tbracket[] E_INIT("[");
+#define Tdot (Tsgdot + 2)
EXTERN const char Talias[] E_INIT("alias");
-EXTERN const char Tunalias[] E_INIT("unalias");
-EXTERN const char Tcat[] E_INIT("cat");
+EXTERN const char Tbadsubst[] E_INIT("bad substitution");
+EXTERN const char Tbg[] E_INIT("bg");
+EXTERN const char Tbad_bsize[] E_INIT("bad shf/buf/bsize");
+#define Tbsize (Tbad_bsize + 12)
+EXTERN const char Tbad_sig_ss[] E_INIT("%s: bad signal '%s'");
+#define Tbad_sig_s (Tbad_sig_ss + 4)
+EXTERN const char Tgbuiltin[] E_INIT("=builtin");
+#define Tbuiltin (Tgbuiltin + 1)
+EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
+EXTERN const char Tcant_cd[] E_INIT("restricted shell - can't cd");
+EXTERN const char Tcant_find[] E_INIT("can't find");
+EXTERN const char Tcant_open[] E_INIT("can't open");
+#define Tbytes (Toomem + 24)
+EXTERN const char Tbcat[] E_INIT("!cat");
+#define Tcat (Tbcat + 1)
+#define Tcd (Tcant_cd + 25)
+EXTERN const char Tcommand[] E_INIT("command");
+EXTERN const char Tcreate[] E_INIT("create");
+EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected");
+EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL");
+EXTERN const char Tsgexport[] E_INIT("*=export");
+#define Texport (Tsgexport + 2)
#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 Tsgexport[] E_INIT("*=export");
-#define Texport (Tsgexport + 2) /* "export" */
-EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
-#define Treadonly (Tsgreadonly + 2) /* "readonly" */
-EXTERN const char Tgbuiltin[] E_INIT("=builtin");
-#define Tbuiltin (Tgbuiltin + 1) /* "builtin" */
+EXTERN const char Tfalse[] E_INIT("false");
+EXTERN const char Tfg[] E_INIT("fg");
+EXTERN const char Tfg_badsubst[] E_INIT("fileglob: bad substitution");
+EXTERN const char Tfile[] E_INIT("file");
+EXTERN const char Tfile_fd[] E_INIT("function definition file");
+EXTERN const char TFPATH[] E_INIT("FPATH");
EXTERN const char T_function[] E_INIT(" function");
-#define Tfunction (T_function + 1) /* "function" */
+#define Tfunction (T_function + 1)
EXTERN const char T_funny_command[] E_INIT("funny $() command");
-#define Tcommand (T_funny_command + 10) /* "command" */
-EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
-#define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */
+EXTERN const char Tgetopts[] E_INIT("getopts");
+EXTERN const char Thistory[] E_INIT("history");
+EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
+EXTERN const char Tjobs[] E_INIT("jobs");
+EXTERN const char Tjob_not_started[] E_INIT("job not started");
+EXTERN const char Tmksh[] E_INIT("mksh");
+EXTERN const char Tname[] E_INIT("name");
+EXTERN const char Tno_args[] E_INIT("missing argument");
+EXTERN const char Tno_OLDPWD[] E_INIT("no OLDPWD");
+EXTERN const char Tnot_ident[] E_INIT("is not an identifier");
+EXTERN const char Tnot_in_history[] E_INIT("not in history");
+EXTERN const char Tnot_found_s[] E_INIT("%s not found");
+#define Tnot_found (Tnot_found_s + 3)
+#define Tnot_started (Tjob_not_started + 4)
+#define TOLDPWD (Tno_OLDPWD + 3)
+#define Topen (Tcant_open + 6)
+#define TPATH (TFPATH + 1)
+EXTERN const char Tpv[] E_INIT("pv");
+EXTERN const char TpVv[] E_INIT("Vpv");
+#define TPWD (Tno_OLDPWD + 6)
+EXTERN const char Tread[] E_INIT("read");
+EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
+#define Treadonly (Tsgreadonly + 2)
+EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection");
+#define Tredirection (Tredirection_dup + 19)
+EXTERN const char Treal_sp1[] E_INIT("real ");
+EXTERN const char Treal_sp2[] E_INIT(" real ");
+EXTERN const char Treq_arg[] E_INIT("requires an argument");
+EXTERN const char Tselect[] E_INIT("select");
+EXTERN const char Tsgset[] E_INIT("*=set");
+#define Tset (Tsgset + 2)
+#define Tsh (Tmksh + 2)
+#define TSHELL (TEXECSHELL + 4)
+EXTERN const char Tshf_read[] E_INIT("shf_read");
+EXTERN const char Tshf_write[] E_INIT("shf_write");
+EXTERN const char Tj_suspend[] E_INIT("j_suspend");
+#define Tsuspend (Tj_suspend + 2)
+EXTERN const char Tsynerr[] E_INIT("syntax error");
+EXTERN const char Ttime[] E_INIT("time");
+EXTERN const char Ttoo_many_args[] E_INIT("too many arguments");
+EXTERN const char Ttrue[] E_INIT("true");
+EXTERN const char Ttty_fd_dupof[] E_INIT("dup of tty fd");
+#define Ttty_fd (Ttty_fd_dupof + 7)
+EXTERN const char Tgtypeset[] E_INIT("=typeset");
+#define Ttypeset (Tgtypeset + 1)
+#define Tugo (Taugo + 1)
+EXTERN const char Tunalias[] E_INIT("unalias");
+#define Tunexpected (TELIF_unexpected + 6)
+EXTERN const char Tunknown_option[] E_INIT("unknown option");
+EXTERN const char Tuser_sp1[] E_INIT("user ");
+EXTERN const char Tuser_sp2[] E_INIT(" user ");
+#define Twrite (Tshf_write + 4)
+EXTERN const char Tf__S[] E_INIT(" %S");
+EXTERN const char Tf__d[] E_INIT(" %d");
+EXTERN const char Tf__ss[] E_INIT(" %s%s");
+EXTERN const char Tf__sN[] E_INIT(" %s\n");
+EXTERN const char Tf_sSs[] E_INIT("%s/%s");
+EXTERN const char Tf_T[] E_INIT("%T");
+EXTERN const char Tf_dN[] E_INIT("%d\n");
+EXTERN const char Tf_s_[] E_INIT("%s ");
+EXTERN const char Tf_s_T[] E_INIT("%s %T");
+EXTERN const char Tf_s_s_sN[] E_INIT("%s %s %s\n");
+EXTERN const char Tf_s_s[] E_INIT("%s %s");
+EXTERN const char Tf_s_sD_s[] E_INIT("%s %s: %s");
+EXTERN const char Tf_optfoo[] E_INIT("%s%s-%c: %s");
+EXTERN const char Tf_sD_[] E_INIT("%s: ");
+EXTERN const char Tf_szs[] E_INIT("%s: %zd %s");
+EXTERN const char Tf_parm[] E_INIT("%s: parameter not set");
+EXTERN const char Tf_coproc[] E_INIT("-p: %s");
+EXTERN const char Tf_cant[] E_INIT("can't %s %s: %s");
+EXTERN const char Tf_heredoc[] E_INIT("here document '%s' unclosed\n");
+#if HAVE_MKNOD
+EXTERN const char Tf_nonnum[] E_INIT("non-numeric %s %s '%s'");
+#endif
+EXTERN const char Tf_S_[] E_INIT("%S ");
+#define Tf_S (Tf__S + 1)
+EXTERN const char Tf_lu[] E_INIT("%lu");
+EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu");
+EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s");
+#define Tf_ss (Tf__ss + 1)
+EXTERN const char Tf_sss[] E_INIT("%s%s%s");
+EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s");
+EXTERN const char Tf_toomany[] E_INIT("too many %ss\n");
+EXTERN const char Tf_sd[] E_INIT("%s %d");
+#define Tf_s (Tf__ss + 3)
+EXTERN const char Tft_end[] E_INIT("%;");
+EXTERN const char Tft_R[] E_INIT("%R");
+#define Tf_d (Tf__d + 1)
+EXTERN const char Tf_sD_s_qs[] E_INIT("%s: %s '%s'");
+EXTERN const char Tf_ro[] E_INIT("read-only: %s");
+EXTERN const char Tf_flags[] E_INIT("%s: flags 0x%X");
+EXTERN const char Tf_temp[] E_INIT("can't %s temporary file %s: %s");
+EXTERN const char Tf_ssfaileds[] E_INIT("%s: %s failed: %s");
+EXTERN const char Tf_sD_sD_s[] E_INIT("%s: %s: %s");
+EXTERN const char Tf__c_[] E_INIT("-%c ");
+EXTERN const char Tf_sD_s_s[] E_INIT("%s: %s %s");
+#define Tf_sN (Tf__sN + 1)
+#define Tf_sD_s (Tf_s_sD_s + 3)
+EXTERN const char T_devtty[] E_INIT("/dev/tty");
+#else /* helpers for string pooling */
+#define T4spaces " "
+#define T1space " "
+#define Tcolsp ": "
+#define TC_LEX1 "|&;<>() \t\n"
+#define TC_IFSWS " \t\n"
+#define TFCEDIT_dollaru "${FCEDIT:-/bin/ed} $_"
+#define Tspdollaru " $_"
+#define Tsgdot "*=."
+#define Taugo "augo"
+#define Tbracket "["
+#define Tdot "."
+#define Talias "alias"
+#define Tbadsubst "bad substitution"
+#define Tbg "bg"
+#define Tbad_bsize "bad shf/buf/bsize"
+#define Tbsize "bsize"
+#define Tbad_sig_ss "%s: bad signal '%s'"
+#define Tbad_sig_s "bad signal '%s'"
+#define Tgbuiltin "=builtin"
+#define Tbuiltin "builtin"
+#define Toomem "can't allocate %zu data bytes"
+#define Tcant_cd "restricted shell - can't cd"
+#define Tcant_find "can't find"
+#define Tcant_open "can't open"
+#define Tbytes "bytes"
+#define Tbcat "!cat"
+#define Tcat "cat"
+#define Tcd "cd"
+#define Tcommand "command"
+#define Tcreate "create"
+#define TELIF_unexpected "TELIF unexpected"
+#define TEXECSHELL "EXECSHELL"
+#define Tsgexport "*=export"
+#define Texport "export"
+#ifdef __OS2__
+#define Textproc "extproc"
+#endif
+#define Tfalse "false"
+#define Tfg "fg"
+#define Tfg_badsubst "fileglob: bad substitution"
+#define Tfile "file"
+#define Tfile_fd "function definition file"
+#define TFPATH "FPATH"
+#define T_function " function"
+#define Tfunction "function"
+#define T_funny_command "funny $() command"
+#define Tgetopts "getopts"
+#define Thistory "history"
+#define Tintovfl "integer overflow %zu %c %zu prevented"
+#define Tjobs "jobs"
+#define Tjob_not_started "job not started"
+#define Tmksh "mksh"
+#define Tname "name"
+#define Tno_args "missing argument"
+#define Tno_OLDPWD "no OLDPWD"
+#define Tnot_ident "is not an identifier"
+#define Tnot_in_history "not in history"
+#define Tnot_found_s "%s not found"
+#define Tnot_found "not found"
+#define Tnot_started "not started"
+#define TOLDPWD "OLDPWD"
+#define Topen "open"
+#define TPATH "PATH"
+#define Tpv "pv"
+#define TpVv "Vpv"
+#define TPWD "PWD"
+#define Tread "read"
+#define Tsgreadonly "*=readonly"
+#define Treadonly "readonly"
+#define Tredirection_dup "can't finish (dup) redirection"
+#define Tredirection "redirection"
+#define Treal_sp1 "real "
+#define Treal_sp2 " real "
+#define Treq_arg "requires an argument"
+#define Tselect "select"
+#define Tsgset "*=set"
+#define Tset "set"
+#define Tsh "sh"
+#define TSHELL "SHELL"
+#define Tshf_read "shf_read"
+#define Tshf_write "shf_write"
+#define Tj_suspend "j_suspend"
+#define Tsuspend "suspend"
+#define Tsynerr "syntax error"
+#define Ttime "time"
+#define Ttoo_many_args "too many arguments"
+#define Ttrue "true"
+#define Ttty_fd_dupof "dup of tty fd"
+#define Ttty_fd "tty fd"
+#define Tgtypeset "=typeset"
+#define Ttypeset "typeset"
+#define Tugo "ugo"
+#define Tunalias "unalias"
+#define Tunexpected "unexpected"
+#define Tunknown_option "unknown option"
+#define Tuser_sp1 "user "
+#define Tuser_sp2 " user "
+#define Twrite "write"
+#define Tf__S " %S"
+#define Tf__d " %d"
+#define Tf__ss " %s%s"
+#define Tf__sN " %s\n"
+#define Tf_sSs "%s/%s"
+#define Tf_T "%T"
+#define Tf_dN "%d\n"
+#define Tf_s_ "%s "
+#define Tf_s_T "%s %T"
+#define Tf_s_s_sN "%s %s %s\n"
+#define Tf_s_s "%s %s"
+#define Tf_s_sD_s "%s %s: %s"
+#define Tf_optfoo "%s%s-%c: %s"
+#define Tf_sD_ "%s: "
+#define Tf_szs "%s: %zd %s"
+#define Tf_parm "%s: parameter not set"
+#define Tf_coproc "-p: %s"
+#define Tf_cant "can't %s %s: %s"
+#define Tf_heredoc "here document '%s' unclosed\n"
+#if HAVE_MKNOD
+#define Tf_nonnum "non-numeric %s %s '%s'"
+#endif
+#define Tf_S_ "%S "
+#define Tf_S "%S"
+#define Tf_lu "%lu"
+#define Tf_toolarge "%s %s too large: %lu"
+#define Tf_ldfailed "%s %s(%d, %ld) failed: %s"
+#define Tf_ss "%s%s"
+#define Tf_sss "%s%s%s"
+#define Tf_sD_s_sD_s "%s: %s %s: %s"
+#define Tf_toomany "too many %ss\n"
+#define Tf_sd "%s %d"
+#define Tf_s "%s"
+#define Tft_end "%;"
+#define Tft_R "%R"
+#define Tf_d "%d"
+#define Tf_sD_s_qs "%s: %s '%s'"
+#define Tf_ro "read-only: %s"
+#define Tf_flags "%s: flags 0x%X"
+#define Tf_temp "can't %s temporary file %s: %s"
+#define Tf_ssfaileds "%s: %s failed: %s"
+#define Tf_sD_sD_s "%s: %s: %s"
+#define Tf__c_ "-%c "
+#define Tf_sD_s_s "%s: %s %s"
+#define Tf_sN "%s\n"
+#define Tf_sD_s "%s: %s"
+#define T_devtty "/dev/tty"
+#endif /* end of string pooling */
typedef uint8_t Temp_type;
/* expanded heredoc */
EXTERN char *current_wd;
/* input line size */
-#define LINE (4096 - ALLOC_SIZE)
-/*
- * Minimum required space to work with on a line - if the prompt leaves
- * less space than this on a line, the prompt is truncated.
- */
-#define MIN_EDIT_SPACE 7
-/*
- * Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
- */
-#define MIN_COLS (2 + MIN_EDIT_SPACE + 3)
-#define MIN_LINS 3
-EXTERN mksh_ari_t x_cols E_INIT(80); /* tty columns */
-EXTERN mksh_ari_t x_lins E_INIT(24); /* tty lines */
+#ifdef MKSH_SMALL
+#define LINE (4096 - ALLOC_OVERHEAD)
+#else
+#define LINE (16384 - ALLOC_OVERHEAD)
+#endif
+/* columns and lines of the tty */
+EXTERN mksh_ari_t x_cols E_INIT(80);
+EXTERN mksh_ari_t x_lins E_INIT(24);
/* Determine the location of the system (common) profile */
(shf)->rnleft--, *(shf)->rp++ : \
shf_getchar(shf))
#define shf_putc_i(c, shf) ((shf)->wnleft == 0 ? \
- shf_putchar((c), (shf)) : \
+ shf_putchar((uint8_t)(c), (shf)) : \
((shf)->wnleft--, *(shf)->wp++ = (c)))
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
#define shf_error(shf) ((shf)->flags & SHF_ERROR)
char name[4];
};
-EXTERN struct tbl vtemp;
+EXTERN struct tbl *vtemp;
/* set by global() and local() */
EXTERN bool last_lookup_was_array;
#define FDELETE BIT(10) /* function deleted while it was executing */
#define FKSH BIT(11) /* function defined with function x (vs x()) */
#define SPEC_BI BIT(12) /* a POSIX special builtin */
+#define LOWER_BI BIT(13) /* (with LOW_BI) override even w/o flags */
+#define LOW_BI BIT(14) /* external utility overrides built-in one */
+
/*
* Attributes that can be set by the user (used to decide if an unset
* param should be repoted by set/typeset). Does not include ARRAY or
#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 */
#define X_EXTRA 20 /* this many extra bytes in X string */
typedef struct XString {
- char *end, *beg; /* end, begin of string */
- size_t len; /* length */
- Area *areap; /* area to allocate/free from */
+ /* begin of string */
+ char *beg;
+ /* length of allocated area, minus safety margin */
+ size_t len;
+ /* end of string */
+ char *end;
+ /* memory area used */
+ Area *areap;
} XString;
typedef char *XStringP;
typedef struct source Source;
struct source {
- const char *str; /* input pointer */
- const char *start; /* start of current buffer */
+ /* input buffer */
+ XString xs;
+ /* memory area, also checked in reclaim() */
+ Area *areap;
+ /* stacked source */
+ Source *next;
+ /* input pointer */
+ const char *str;
+ /* start of current buffer */
+ const char *start;
+ /* input file name */
+ const char *file;
+ /* extra data */
union {
- const char **strv; /* string [] */
- struct shf *shf; /* shell file */
- struct tbl *tblp; /* alias (SF_HASALIAS) */
- char *freeme; /* also for SREREAD */
+ /* string[] */
+ const char **strv;
+ /* shell file */
+ struct shf *shf;
+ /* alias (SF_HASALIAS) */
+ struct tbl *tblp;
+ /* (also for SREREAD) */
+ char *freeme;
} u;
- const char *file; /* input file name */
- int type; /* input type */
- int line; /* line number */
- int errline; /* line the error occurred on (0 if not set) */
- int flags; /* SF_* */
- Area *areap;
- Source *next; /* stacked source */
- XString xs; /* input buffer */
- char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and
- * alias (SALIAS) */
+ /* flags */
+ int flags;
+ /* input type */
+ int type;
+ /* line number */
+ int line;
+ /* line the error occurred on (0 if not set) */
+ int errline;
+ /* buffer for ungetsc() (SREREAD) and alias (SALIAS) */
+ char ugbuf[2];
};
/* Source.type values */
int x_read(char *);
#endif
void x_mkraw(int, mksh_ttyst *, bool);
+void x_initterm(const char *);
/* eval.c */
char *substitute(const char *, int);
char **eval(const char **, int);
const char *search_path(const char *, const char *, int, int *);
void pr_menu(const char * const *);
void pr_list(char * const *);
+int herein(struct ioword *, char **);
/* expr.c */
int evaluate(const char *, mksh_ari_t *, int, bool);
int v_evaluate(struct tbl *, const char *, volatile int, bool);
size_t utf_wctomb(char *, unsigned int);
int utf_widthadj(const char *, const char **);
size_t utf_mbswidth(const char *) MKSH_A_PURE;
-const char *utf_skipcols(const char *, int) MKSH_A_PURE;
+const char *utf_skipcols(const char *, int, int *);
size_t utf_ptradj(const char *) MKSH_A_PURE;
#ifdef MIRBSD_BOOTFLOPPY
#define utf_wcwidth(i) wcwidth((wchar_t)(i))
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 **);
ssize_t shf_vfprintf(struct shf *, const char *, va_list)
MKSH_A_FORMAT(__printf__, 2, 0);
/* syn.c */
-int assign_command(const char *, bool);
+int assign_command(const char *, bool) MKSH_A_PURE;
void initkeywords(void);
struct op *compile(Source *, bool);
bool parse_usec(const char *, struct timeval *);
mksh_ari_t rndget(void);
void rndset(unsigned long);
void rndpush(const void *);
+void record_match(const char *);
enum Test_op {
/* non-operator */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- * 2012, 2013, 2015
+ * 2012, 2013, 2015, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.69 2015/12/31 20:38:59 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.76 2016/07/25 00:04:47 tg Exp $");
/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
}
if (!(sflags & (SHF_RD | SHF_WR)))
- internal_errorf("%s: %s", where, "missing read/write");
+ internal_errorf(Tf_sD_s, where, "missing read/write");
}
/* Set up the shf structure for a file descriptor. Doesn't fail. */
shf_open_hlp(fd, &sflags, "shf_reopen");
if (!shf || !shf->buf || shf->bsize < bsize)
- internal_errorf("%s: %s", "shf_reopen", "bad shf/buf/bsize");
+ internal_errorf(Tf_sD_s, "shf_reopen", Tbad_bsize);
/* assumes shf->buf and shf->bsize already set up */
shf->fd = fd;
{
/* 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(Tf_flags, "shf_sopen",
+ (unsigned int)sflags);
if (!shf) {
shf = alloc(sizeof(struct shf), ATEMP);
return ((shf->flags & SHF_WR) ? -1 : 0);
if (shf->fd < 0)
- internal_errorf("%s: %s", "shf_flush", "no fd");
+ internal_errorf(Tf_sD_s, "shf_flush", "no fd");
if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
int ret = 0;
if (!(shf->flags & SHF_STRING) && shf->fd < 0)
- internal_errorf("%s: %s", "shf_emptybuf", "no fd");
+ internal_errorf(Tf_sD_s, "shf_emptybuf", "no fd");
if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
return (0);
if (shf->fd < 0)
- internal_errorf("%s: %s", "shf_fillbuf", "no fd");
+ internal_errorf(Tf_sD_s, "shf_fillbuf", "no fd");
if (shf->flags & (SHF_EOF | SHF_ERROR)) {
if (shf->flags & SHF_ERROR)
ssize_t ncopy, orig_bsize = bsize;
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_read", shf->flags);
+ internal_errorf(Tf_flags, Tshf_read,
+ (unsigned int)shf->flags);
if (bsize <= 0)
- internal_errorf("%s: %s %zd", "shf_write", "bsize", bsize);
+ internal_errorf(Tf_szs, Tshf_read, bsize, Tbsize);
while (bsize > 0) {
if (shf->rnleft == 0 &&
char *orig_buf = buf;
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_getse", shf->flags);
+ internal_errorf(Tf_flags, "shf_getse",
+ (unsigned int)shf->flags);
if (bsize <= 0)
return (NULL);
shf_getchar(struct shf *shf)
{
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags);
+ internal_errorf(Tf_flags, "shf_getchar",
+ (unsigned int)shf->flags);
if (shf->rnleft == 0 && (shf_fillbuf(shf) == -1 || shf->rnleft == 0))
return (-1);
shf_ungetc(int c, struct shf *shf)
{
if (!(shf->flags & SHF_RD))
- internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags);
+ internal_errorf(Tf_flags, "shf_ungetc",
+ (unsigned int)shf->flags);
if ((shf->flags & SHF_ERROR) || c == -1 ||
(shf->rp == shf->buf && shf->rnleft))
shf_putchar(int c, struct shf *shf)
{
if (!(shf->flags & SHF_WR))
- internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags);
+ internal_errorf(Tf_flags, "shf_putchar",
+ (unsigned int)shf->flags);
if (c == -1)
return (-1);
ssize_t n;
if (shf->fd < 0)
- internal_errorf("%s: %s", "shf_putchar", "no fd");
+ internal_errorf(Tf_sD_s, "shf_putchar", "no fd");
if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
return (-1);
ssize_t n, ncopy, orig_nbytes = nbytes;
if (!(shf->flags & SHF_WR))
- internal_errorf("%s: flags 0x%X", "shf_write", shf->flags);
+ internal_errorf(Tf_flags, Tshf_write,
+ (unsigned int)shf->flags);
if (nbytes < 0)
- internal_errorf("%s: %s %zd", "shf_write", "nbytes", nbytes);
+ internal_errorf(Tf_szs, Tshf_write, nbytes, Tbytes);
/* Don't buffer if buffer is empty and we're writting a large amount. */
if ((ncopy = shf->wnleft) &&
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];
+ char numbuf[(8 * sizeof(long) + 2) / 3 + 1 + /* NUL */ 1];
/* this stuff for dealing with the buffer */
ssize_t nwritten = 0;
*/
flags = 0;
field = precision = 0;
- for ( ; (c = *fmt++) ; ) {
+ while ((c = *fmt++)) {
switch (c) {
case '#':
flags |= FL_HASH;
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':
bool overflowed = false;
tmp = ksh_numdig(c);
- while (c = *fmt++, ksh_isdigit(c)) {
+ while (c = *fmt++, ksh_isdigit(c))
if (notok2mul(2147483647, tmp, 10))
overflowed = true;
- tmp = tmp * 10 + ksh_numdig(c);
- }
+ 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;
integral:
flags |= FL_NUMBER;
cp = numbuf + sizeof(numbuf);
+ *--cp = '\0';
switch (c) {
case 'd':
*--cp = (flags & FL_UPPER) ? 'X' : 'x';
*--cp = '0';
}
+ }
}
- }
- len = numbuf + sizeof(numbuf) - (s = cp);
+ len = numbuf + sizeof(numbuf) - 1 - (s = cp);
if (flags & FL_DOT) {
if (precision > len) {
field = precision;
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 == '-' ||
shf_putc(*s, shf);
s++;
nwritten++;
- if (--precision > 0 &&
+ if (--precision &&
ksh_eq(*s, 'X', 'x')) {
shf_putc(*s, shf);
s++;
c = '0';
} else
c = flags & FL_ZERO ? '0' : ' ';
- if (field < 0) {
- nwritten += -field;
- while (field < 0) {
- shf_putc(c, shf);
- ++field;
- }
- }
+ nwritten += field;
+ while (field--)
+ shf_putc(c, shf);
+ field = 0;
} else
c = ' ';
} else
field = 0;
- if (precision > 0) {
- const char *q;
+ nwritten += precision;
+ precision = utf_skipcols(s, precision, &tmp) - s;
+ while (precision--)
+ shf_putc(*s++, shf);
- nwritten += precision;
- q = utf_skipcols(s, precision);
- do {
- 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);
+++ /dev/null
- { "ABRT", 6 },
- { "FPE", 8 },
- { "ILL", 4 },
- { "INT", 2 },
- { "SEGV", 11 },
- { "TERM", 15 },
- { "ALRM", 14 },
- { "BUS", 7 },
- { "CHLD", 17 },
- { "CONT", 18 },
- { "HUP", 1 },
- { "KILL", 9 },
- { "PIPE", 13 },
- { "QUIT", 3 },
- { "STOP", 19 },
- { "TSTP", 20 },
- { "TTIN", 21 },
- { "TTOU", 22 },
- { "USR1", 10 },
- { "USR2", 12 },
- { "POLL", 29 },
- { "PROF", 27 },
- { "SYS", 31 },
- { "TRAP", 5 },
- { "URG", 23 },
- { "VTALRM", 26 },
- { "XCPU", 24 },
- { "XFSZ", 25 },
- { "WINCH", 28 },
- { "PWR", 30 },
- { "STKFLT", 16 },
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.109 2016/01/19 23:12:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.114 2016/08/04 20:32:14 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
};
struct yyrecursive_state {
+ struct ioword *old_heres[HERES];
struct yyrecursive_state *next;
struct ioword **old_herep;
int old_symbol;
iop->ioflag |= IOEVAL;
}
if (herep > &heres[HERES - 1])
- yyerror("too many %ss\n", "<<");
+ yyerror(Tf_toomany, "<<");
*herep++ = iop;
} else
iop->ioname = yylval.cp;
char *cp;
nextiop = alloc(sizeof(*iop), 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++ = digits_lc[iop->unit / 10];
- }
-#endif
*cp++ = CHAR;
*cp++ = digits_lc[iop->unit % 10];
*cp = EOS;
case REDIR:
while ((iop = synio(cf)) != NULL) {
if (iopn >= NUFILE)
- yyerror("too many %ss\n",
- "redirection");
+ yyerror(Tf_toomany,
+ Tredirection);
iops[iopn++] = iop;
}
break;
t = newtp((c == FOR) ? TFOR : TSELECT);
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();
while ((iop = synio(syniocf)) != NULL) {
if (iopn >= NUFILE)
- yyerror("too many %ss\n", "redirection");
+ yyerror(Tf_toomany, Tredirection);
iops[iopn++] = iop;
}
*/
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(),
{ "done", DONE, true },
{ "in", IN, true },
{ Tfunction, FUNCTION, true },
- { "time", TIME, true },
+ { Ttime, TIME, true },
{ "{", '{', true },
{ Tcbrace, '}', true },
{ "!", BANG, true },
int c;
if (!what)
- what = "unexpected";
+ what = Tunexpected;
REJECT;
c = token(0);
Again:
goto Again;
}
/* don't quote the EOF */
- yyerror("%s: %s %s\n", Tsynerr, "unexpected", "EOF");
+ yyerror("%s: unexpected EOF\n", Tsynerr);
/* NOTREACHED */
case LWORD:
- s = snptreef(NULL, 32, "%S", yylval.cp);
+ s = snptreef(NULL, 32, Tf_S, yylval.cp);
break;
case REDIR:
- s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
+ s = snptreef(redir, sizeof(redir), Tft_R, yylval.iop);
break;
default:
static int
inalias(struct source *s)
{
- for (; s && s->type == SALIAS; s = s->next)
+ while (s && s->type == SALIAS) {
if (!(s->flags & SF_ALIASEND))
return (1);
+ s = s->next;
+ }
return (0);
}
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;
yyrecursive_pop(false);
/* t->left because nested(TPAREN, ...) hides our goodies there */
- cp = snptreef(NULL, 0, "%T", t->left);
+ cp = snptreef(NULL, 0, Tf_T, t->left);
tfree(t, ATEMP);
return (cp);
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;
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.80 2016/01/14 22:30:43 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.86 2016/07/25 00:04:48 tg Exp $");
#define INDENT 8
struct ioword **ioact;
struct op *t1;
int i;
+ const char *ccp;
Chain:
if (t == NULL)
switch (t->type) {
case TCOM:
prevent_semicolon = false;
- /*
- * special-case 'var=<<EOF' (rough; see
- * exec.c:execute() for full code)
- */
+ /* special-case 'var=<<EOF' (cf. exec.c:execute) */
if (
- /* we have zero arguments, i.e. no programme to run */
+ /* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
- (t->ioact[0]->ioflag & IOTYPE) == IOHERE) {
- fptreef(shf, indent, "%S", t->vars[0]);
+ (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=") */
+ ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
+ /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
+ ccp[3] == '=' && ccp[4] == EOS))) {
+ fptreef(shf, indent, Tf_S, t->vars[0]);
break;
}
if (t->vars) {
w = (const char **)t->vars;
while (*w)
- fptreef(shf, indent, "%S ", *w++);
+ fptreef(shf, indent, Tf_S_, *w++);
} else
shf_puts("#no-vars# ", shf);
if (t->args) {
w = t->args;
while (*w)
- fptreef(shf, indent, "%S ", *w++);
+ fptreef(shf, indent, Tf_S_, *w++);
} else
shf_puts("#no-args# ", shf);
break;
w = t->args;
shf_puts("[[", shf);
while (*w)
- fptreef(shf, indent, " %S", *w++);
+ fptreef(shf, indent, Tf__S, *w++);
shf_puts(" ]] ", shf);
break;
case TSELECT:
shf_puts("in ", shf);
w = (const char **)t->vars;
while (*w)
- fptreef(shf, indent, "%S ", *w++);
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tf_S_, *w++);
+ fptreef(shf, indent, Tft_end);
}
fptreef(shf, indent + INDENT, "do%N%T", t->left);
fptreef(shf, indent, "%;done ");
}
fptreef(shf, indent, "%Nesac ");
break;
-#ifdef DEBUG
case TELIF:
- internal_errorf("TELIF in tree.c:ptree() unexpected");
+ internal_errorf(TELIF_unexpected);
/* FALLTHROUGH */
-#endif
case TIF:
i = 2;
t1 = t;
do {
t1 = t1->right;
i = 0;
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
process_TIF:
/* 5 == strlen("elif ") */
fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
t1 = t1->right;
if (t1->left != NULL) {
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
fptreef(shf, indent + INDENT, "%s%N%T",
"then", t1->left);
}
} while (t1->right && t1->right->type == TELIF);
if (t1->right != NULL) {
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
fptreef(shf, indent + INDENT, "%s%N%T",
"else", t1->right);
}
case TWHILE:
case TUNTIL:
/* 6 == strlen("while "/"until ") */
- fptreef(shf, indent + 6, "%s %T",
+ fptreef(shf, indent + 6, Tf_s_T,
(t->type == TWHILE) ? "while" : "until",
t->left);
- fptreef(shf, indent, "%;");
+ fptreef(shf, indent, Tft_end);
fptreef(shf, indent + INDENT, "do%N%T", t->right);
fptreef(shf, indent, "%;done ");
break;
fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
break;
case TTIME:
- fptreef(shf, indent, "%s %T", "time", t->left);
+ fptreef(shf, indent, Tf_s_T, Ttime, t->left);
break;
default:
shf_puts("<botch>", shf);
iop->heredoc) {
shf_putc('\n', shf);
shf_puts(iop->heredoc, shf);
- fptreef(shf, indent, "%s",
+ fptreef(shf, indent, Tf_s,
evalstr(iop->delim, 0));
need_nl = true;
}
(type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
iop->unit + 1;
if (iop->unit != expected)
- shf_fprintf(shf, "%d", (int)iop->unit);
+ shf_fprintf(shf, Tf_d, (int)iop->unit);
switch (type) {
case IOREAD:
++wp;
goto wdvarput_csubst;
}
+ /* FALLTHROUGH */
case CHAR:
c = *wp++;
shf_putc(c, shf);
break;
- case QCHAR: {
- bool doq;
-
+ case QCHAR:
c = *wp++;
- doq = (c == '"' || c == '`' || c == '$' || c == '\\');
- if (opmode & WDS_TPUTS) {
- if (quotelevel == 0)
- doq = true;
- } else {
- doq = false;
- }
- if (doq)
- shf_putc('\\', shf);
+ if (opmode & WDS_TPUTS)
+ switch (c) {
+ case '\n':
+ if (quotelevel == 0) {
+ c = '\'';
+ shf_putc(c, shf);
+ shf_putc('\n', shf);
+ }
+ break;
+ default:
+ if (quotelevel == 0)
+ /* FALLTHROUGH */
+ case '"':
+ case '`':
+ case '$':
+ case '\\':
+ shf_putc('\\', shf);
+ break;
+ }
shf_putc(c, shf);
break;
- }
case COMSUB:
shf_puts("$(", shf);
cs = ")";
break;
case 'd':
/* signed decimal */
- shf_fprintf(shf, "%d", va_arg(va, int));
+ shf_fprintf(shf, Tf_d, va_arg(va, int));
break;
case 'u':
/* unsigned decimal */
break;
default:
internal_warningf(
- "wdscan: unknown char 0x%x (carrying on)",
- wp[-1]);
+ "wdscan: unknown char 0x%X (carrying on)",
+ (unsigned char)wp[-1]);
}
}
size_t n;
buf = alloc(sz + 16, ATEMP);
- snptreef(buf, sz + 16, "%T", t);
+ snptreef(buf, sz + 16, Tf_T, t);
cp = buf;
vist_loop:
if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
return (--wp);
case ADELIM:
if (*wp == /*{*/'}') {
- shf_puts("]ADELIM(})", shf);
+ shf_puts(/*{*/ "]ADELIM(})", shf);
return (wp + 1);
}
shf_puts("ADELIM=", shf);
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
goto dumpleftandout;
OPEN(TFUNCT)
shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
- t->u.ksh_func ? "yes" : "no");
+ t->u.ksh_func ? Ttrue : Tfalse);
goto dumpleftandout;
OPEN(TTIME)
goto dumpleftandout;
break;
OPEN(TEOF)
dumpunexpected:
- shf_puts("unexpected", shf);
+ shf_puts(Tunexpected, shf);
break;
OPEN(TELIF)
goto dumpunexpected;
#include <sys/sysctl.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.197 2016/01/14 22:49:33 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.207 2016/08/01 21:38:07 tg Exp $");
/*-
* Variables
struct tbl *tp;
ktinit(APERM, &specials,
- /* currently 14 specials: 75% of 32 = 2^5 */
+ /* currently 15 specials: 75% of 32 = 2^5 */
5);
while (i < V_MAX - 1) {
tp = ktenter(&specials, initvar_names[i],
char *cp;
/* gotcha! */
- cp = shf_smprintf("%s%s", str_val(vp), p);
+ cp = shf_smprintf(Tf_ss, str_val(vp), p);
afree(ap, ATEMP);
n = ap = cp;
goto redo_from_ref;
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(vn, &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, Tf_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;
goto out;
}
+ vp->name[0] = c;
+ vp->name[1] = '\0';
vp->flag |= RDONLY;
if (vn[1] != '\0')
goto out;
vn = array_index_calc(n, &array, &val);
h = hash(vn);
if (!ksh_isalphx(*vn)) {
- vp = &vtemp;
+ vp = vtemp;
vp->flag = DEFINED|RDONLY;
vp->type = 0;
vp->areap = ATEMP;
error_ok &= ~0x4;
if ((vq->flag & RDONLY) && !no_ro_check) {
- warningf(true, "read-only: %s", vq->name);
+ warningf(true, Tf_ro, vq->name);
if (!error_ok)
errorfxz(2);
return (0);
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;
p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
if (vp->flag & (RJUST|LJUST)) {
- int slen = olen, i = 0;
+ int slen = olen;
if (vp->flag & RJUST) {
- const char *qq = s;
+ const char *qq;
int n = 0;
- while (i < slen)
- i += utf_widthadj(qq, &qq);
+ qq = utf_skipcols(s, slen, &slen);
+
/* strip trailing spaces (AT&T uses qq[-1] == ' ') */
while (qq > s && ksh_isspace(qq[-1])) {
--qq;
}
if (*val == '[') {
if (new_refflag != SRF_NOP)
- errorf("%s: %s", var,
+ errorf(Tf_sD_s, var,
"reference variable can't be an array");
len = array_ref_len(val);
if (len == 0)
/* 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 '?':
goto nameref_rhs_checked;
}
nameref_empty:
- errorf("%s: %s", var, "empty nameref target");
+ errorf(Tf_sD_s, var, "empty nameref target");
}
len = (*ccp == '[') ? array_ref_len(ccp) : 0;
if (ccp[len]) {
* junk after it" and "invalid array"; in the
* latter case, len is also 0 and points to '['
*/
- errorf("%s: %s", qval,
+ errorf(Tf_sD_s, qval,
"nameref target not a valid parameter name");
}
nameref_rhs_checked:
/* prevent nameref loops */
while (qval) {
if (!strcmp(qval, tvar))
- errorf("%s: %s", qval,
+ errorf(Tf_sD_s, qval,
"expression recurses on parameter");
varsearch(e->loc, &vp, qval, hash(qval));
qval = NULL;
}
/* prevent typeset from creating a local PATH/ENV/SHELL */
- if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
- strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
- errorf("%s: %s", tvar, "restricted");
+ if (Flag(FRESTRICTED) && (strcmp(tvar, TPATH) == 0 ||
+ strcmp(tvar, "ENV") == 0 || strcmp(tvar, TSHELL) == 0))
+ errorf(Tf_sD_s, tvar, "restricted");
innermost_refflag = new_refflag;
vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
if ((vpbase->flag & RDONLY) &&
(val || clr || (set & ~EXPORT)))
/* XXX check calls - is error here ok by POSIX? */
- errorfx(2, "read-only: %s", tvar);
+ errorfx(2, Tf_ro, tvar);
afree(tvar, ATEMP);
/* most calls are with set/clr == 0 */
char *tval;
if (vappend) {
- tval = shf_smprintf("%s%s", str_val(vp), val);
+ tval = shf_smprintf(Tf_ss, str_val(vp), val);
val = tval;
} else
tval = NULL;
static time_t seconds; /* time SECONDS last set */
static mksh_uari_t user_lineno; /* what user set $LINENO to */
+/* minimum values from the OS we consider sane, lowered for R53 */
+#define MIN_COLS 4
+#define MIN_LINS 2
+
static void
getspec(struct tbl *vp)
{
/* clear tracked aliases */
flushcom(true);
return;
+#ifndef MKSH_NO_CMDLINE_EDITING
+ case V_TERM:
+ x_initterm(str_val(vp));
+ return;
+#endif
case V_TMPDIR:
afree(tmpdir, APERM);
tmpdir = NULL;
if (getint(vp, &num, false) == -1) {
s = str_val(vp);
if (st != V_RANDOM)
- errorf("%s: %s: %s", vp->name, "bad number", s);
+ errorf(Tf_sD_sD_s, vp->name, "bad number", s);
num.u = hash(s);
}
vp->flag |= SPECIAL;
/* clear tracked aliases */
flushcom(true);
break;
+#ifndef MKSH_NO_CMDLINE_EDITING
+ case V_TERM:
+ x_initterm(null);
+ return;
+#endif
case V_TMPDIR:
/* should not become unspecial */
if (tmpdir) {
/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
if ((vp->flag&RDONLY))
- errorfx(2, "read-only: %s", ccp);
+ errorfx(2, Tf_ro, ccp);
/* This code is quite non-optimal */
if (reset) {
/* trash existing values and attributes */
BAFHUpdateOctet_reg(h, 0);
qh_state = h;
}
+
+/* record last glob match */
+void
+record_match(const char *istr)
+{
+ struct tbl *vp;
+
+ vp = local("KSH_MATCH", false);
+ unset(vp, 1);
+ vp->flag = DEFINED | RDONLY;
+ setstr(vp, istr, 0x4);
+}
/*-
- * Copyright (c) 2009, 2011, 2012
+ * Copyright (c) 2009, 2011, 2012, 2016
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
*/
#if defined(VARSPEC_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.7 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.9 2016/07/25 21:02:13 tg Exp $");
#define FN(name) /* nothing */
#elif defined(VARSPEC_ENUMS)
#define FN(name) V_##name,
FN(PATH)
FN(RANDOM)
FN(SECONDS)
+#ifndef MKSH_NO_CMDLINE_EDITING
+FN(TERM)
+#endif
FN(TMOUT)
FN(TMPDIR)