Merge "Upgrade to mksh R55."
diff --git a/Android.mk b/Android.mk
index 427aea9..5e7c633 100644
--- a/Android.mk
+++ b/Android.mk
@@ -83,6 +83,6 @@
-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=541
+ -DHAVE_PERSISTENT_HISTORY=0 -DMKSH_BUILD_R=551
include $(BUILD_EXECUTABLE)
diff --git a/src/Build.sh b/src/Build.sh
old mode 100755
new mode 100644
index d0ff130..ca88a06
--- a/src/Build.sh
+++ b/src/Build.sh
@@ -1,8 +1,8 @@
#!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.707 2016/11/11 23:31:29 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.716 2017/04/12 18:33:22 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015, 2016
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@@ -495,6 +495,7 @@
last=
tfn=
legacy=0
+textmode=0
for i
do
@@ -551,6 +552,12 @@
:-r)
r=1
;;
+ :-T)
+ textmode=1
+ ;;
+ :+T)
+ textmode=0
+ ;;
:-t)
last=t
;;
@@ -586,16 +593,21 @@
rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \
*.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out
-SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
+SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
if test $legacy = 0; then
- SRCS="$SRCS edit.c"
check_categories="$check_categories shell:legacy-no int:32"
else
check_categories="$check_categories shell:legacy-yes"
add_cppflags -DMKSH_LEGACY_MODE
- HAVE_PERSISTENT_HISTORY=0
+fi
+
+if test $textmode = 0; then
+ check_categories="$check_categories shell:textmode-no shell:binmode-yes"
+else
+ check_categories="$check_categories shell:textmode-yes shell:binmode-no"
+ add_cppflags -DMKSH_WITH_TEXTMODE
fi
if test x"$srcdir" = x"."; then
@@ -766,7 +778,6 @@
add_cppflags -DMKSH__NO_SETEUGID
oswarn=' and will currently not work'
add_cppflags -DMKSH_UNEMPLOYED
- add_cppflags -DMKSH_NOPROSPECTOFWORK
# these taken from Harvey-OS github and need re-checking
add_cppflags -D_setjmp=setjmp -D_longjmp=longjmp
: "${HAVE_CAN_NO_EH_FRAME=0}"
@@ -849,16 +860,39 @@
: "${HAVE_SETLOCALE_CTYPE=0}"
;;
OS/2)
+ add_cppflags -DMKSH_ASSUME_UTF8=0; HAVE_ISSET_MKSH_ASSUME_UTF8=1
HAVE_TERMIOS_H=0
HAVE_MKNOD=0 # setmode() incompatible
- oswarn="; it is currently being ported, get it from"
- oswarn="$oswarn${nl}https://github.com/komh/mksh-os2 in the meanwhile"
+ oswarn="; it is being ported"
check_categories="$check_categories nosymlink"
: "${CC=gcc}"
: "${SIZE=: size}"
+ SRCS="$SRCS os2.c"
add_cppflags -DMKSH_UNEMPLOYED
add_cppflags -DMKSH_NOPROSPECTOFWORK
add_cppflags -DMKSH_NO_LIMITS
+ add_cppflags -DMKSH_DOSPATH
+ if test $textmode = 0; then
+ x='dis'
+ y='standard OS/2 tools'
+ else
+ x='en'
+ y='standard Unix mksh and other tools'
+ fi
+ echo >&2 "
+OS/2 Note: mksh can be built with or without 'textmode'.
+Without 'textmode' it will behave like a standard Unix utility,
+compatible to mksh on all other platforms, using only ASCII LF
+(0x0A) as line ending character. This is supported by the mksh
+upstream developer.
+With 'textmode', mksh will be modified to behave more like other
+OS/2 utilities, supporting ASCII CR+LF (0x0D 0x0A) as line ending
+at the cost of deviation from standard mksh. This is supported by
+the mksh-os2 porter.
+
+] You are currently compiling with textmode ${x}abled, introducing
+] incompatibilities with $y.
+"
;;
OSF1)
HAVE_SIG_T=0 # incompatible
@@ -2152,68 +2186,6 @@
ac_testdone
ac_cppflags
-save_CFLAGS=$CFLAGS
-ac_testn compile_time_asserts_$$ '' 'whether compile-time assertions pass' <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- #ifndef CHAR_BIT
- #define CHAR_BIT 8 /* defuse this test on really legacy systems */
- #endif
- struct ctasserts {
- #define cta(name, assertion) char name[(assertion) ? 1 : -1]
-/* this one should be defined by the standard */
-cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
- (sizeof(unsigned char) == 1));
-cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) &&
- ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF));
-/* the next assertion is probably not really needed */
-cta(short_is_2_char, sizeof(short) == 2);
-cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short));
-/* the next assertion is probably not really needed */
-cta(int_is_4_char, sizeof(int) == 4);
-cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int));
-
-cta(long_ge_int, sizeof(long) >= sizeof(int));
-cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long));
-
-#ifndef MKSH_LEGACY_MODE
-/* the next assertion is probably not really needed */
-cta(ari_is_4_char, sizeof(mksh_ari_t) == 4);
-/* but this is */
-cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1));
-/* the next assertion is probably not really needed */
-cta(uari_is_4_char, sizeof(mksh_uari_t) == 4);
-/* but the next three are; we REQUIRE unsigned integer wraparound */
-cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1));
-cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3));
-cta(uari_wrap_32_bit,
- (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) >
- (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4));
-#define NUM 22
-#else
-#define NUM 16
-#endif
-/* these are always required */
-cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0);
-cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0);
-/* we require these to have the precisely same size and assume 2s complement */
-cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t));
-
-cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t));
-cta(sizet_voidptr_same_size, sizeof(size_t) == sizeof(void *));
-cta(sizet_funcptr_same_size, sizeof(size_t) == sizeof(void (*)(void)));
-/* our formatting routines assume this */
-cta(ptr_fits_in_long, sizeof(size_t) <= sizeof(long));
-cta(ari_fits_in_long, sizeof(mksh_ari_t) <= sizeof(long));
-/* for struct alignment people */
- char padding[64 - NUM];
- };
-char ctasserts_dblcheck[sizeof(struct ctasserts) == 64 ? 1 : -1];
- int main(void) { return (sizeof(ctasserts_dblcheck) + isatty(0)); }
-EOF
-CFLAGS=$save_CFLAGS
-eval test 1 = \$HAVE_COMPILE_TIME_ASSERTS_$$ || exit 1
-
#
# extra checks for legacy mksh
#
@@ -2367,7 +2339,7 @@
addsrcs USE_PRINTF_BUILTIN printf.c
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
-add_cppflags -DMKSH_BUILD_R=541
+add_cppflags -DMKSH_BUILD_R=551
$e $bi$me: Finished configuration testing, now producing output.$ao
diff --git a/src/check.t b/src/check.t
index 1b03af6..93c614f 100644
--- a/src/check.t
+++ b/src/check.t
Binary files differ
diff --git a/src/dot.mkshrc b/src/dot.mkshrc
index 13a1f54..af55d7d 100644
--- a/src/dot.mkshrc
+++ b/src/dot.mkshrc
@@ -1,8 +1,8 @@
# $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.108 2016/07/26 22:03:41 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.114 2017/03/19 22:31:26 tg Exp $
#-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-# 2011, 2012, 2013, 2014, 2015, 2016
+# 2011, 2012, 2013, 2014, 2015, 2016, 2017
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@@ -22,65 +22,100 @@
#-
# ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
-# catch non-mksh (including lksh) trying to run this file
+# catch non-mksh, non-lksh, trying to run this file
case ${KSH_VERSION:-} in
-*MIRBSD\ KSH*) ;;
-*) return 0 ;;
+*LEGACY\ KSH*|*MIRBSD\ KSH*) ;;
+*) \return 0 ;;
esac
-PS1='#'; (( USER_ID )) && PS1='$'; \: "${TERM:=vt100}${HOSTNAME:=$(\ulimit -c \
- 0; hostname 2>/dev/null)}${EDITOR:=/bin/ed}${USER:=$(\ulimit -c 0; id -un \
- 2>/dev/null || \echo \?)}${MKSH:=$(\builtin whence -p mksh)}"
-HOSTNAME=${HOSTNAME%%*([ ]).*}; HOSTNAME=${HOSTNAME##*([ ])}
-[[ $HOSTNAME = ?(ip6-)localhost?(6) ]] && HOSTNAME=
-\: "${HOSTNAME:=nil}${MKSH:=/bin/mksh}"; \export EDITOR HOSTNAME MKSH TERM USER
-PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
- \typeset e=$?
+# give MidnightBSD's laffer1 a bit of csh feeling
+function setenv {
+ if (( $# )); then
+ \\builtin eval '\\builtin export "$1"="${2:-}"'
+ else
+ \\builtin typeset -x
+ fi
+}
+
+# pager (not control character safe)
+smores() (
+ \\builtin set +m
+ \\builtin cat "$@" |&
+ \\builtin trap "rv=\$?; \\\\builtin kill $! >/dev/null 2>&1; \\\\builtin exit \$rv" EXIT
+ while IFS= \\builtin read -pr line; do
+ llen=${%line}
+ (( llen == -1 )) && llen=${#line}
+ (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
+ if (( (curlin += llen) >= LINES )); then
+ \\builtin print -nr -- $'\e[7m--more--\e[0m'
+ \\builtin read -u1 || \\builtin exit $?
+ [[ $REPLY = [Qq]* ]] && \\builtin exit 0
+ curlin=$llen
+ fi
+ \\builtin print -r -- "$line"
+ done
+)
+
+\\builtin alias ls=ls l='ls -F' la='l -a' ll='l -l' lo='l -alo'
+\: "${HOSTNAME:=$(\\builtin ulimit -c 0; \\builtin print -r -- $(hostname \
+ 2>/dev/null))}${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit \
+ -c 0; id -un 2>/dev/null)}${USER:=?}"
+[[ $HOSTNAME = ?(?(ip6-)localhost?(6)) ]] && HOSTNAME=nil; \\builtin unalias ls
+\\builtin export EDITOR HOSTNAME TERM USER
+
+# minimal support for lksh users
+if [[ $KSH_VERSION = *LEGACY\ KSH* ]]; then
+ PS1='$USER@${HOSTNAME%%.*}:$PWD>'
+ \\builtin return 0
+fi
+
+# mksh-specific from here
+\: "${MKSH:=$(\\builtin whence -p mksh)}${MKSH:=/bin/mksh}"
+\\builtin export MKSH
+
+PS4='[$EPOCHREALTIME] '; PS1='#'; (( USER_ID )) && PS1='$'; PS1=$'\001\r''${|
+ \\builtin typeset e=$?
(( e )) && REPLY+="$e|"
REPLY+=${USER}@${HOSTNAME%%.*}:
- \typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
- d=${d%/}; \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
+ \\builtin typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
+ d=${d%/}; \\builtin typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
REPLY+=$p$d
- \return $e
+ \\builtin return $e
} '"$PS1 "
-\alias ls=ls
-\unalias ls
-\alias l='ls -F'
-\alias la='l -a'
-\alias ll='l -l'
-\alias lo='l -alo'
-\alias doch='sudo mksh -c "$(\builtin fc -ln -1)"'
-\command -v rot13 >/dev/null || \alias rot13='tr \
+\\builtin alias doch='sudo mksh -c "$(\\builtin fc -ln -1)"'
+\\builtin command -v rot13 >/dev/null || \\builtin alias rot13='tr \
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
-if \command -v hd >/dev/null; then \:; elif \command -v hexdump >/dev/null; then
+if \\builtin command -v hd >/dev/null; then
+ \:
+elif \\builtin command -v hexdump >/dev/null; then
function hd {
hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \
-e '" |" "%_p"' -e '"|\n"' "$@"
}
else
function hd {
- \typeset -Uui16 -Z11 pos=0
- \typeset -Uui16 -Z5 hv=2147483647
- \typeset dasc line i
- \set +U
+ \\builtin typeset -Uui16 -Z11 pos=0
+ \\builtin typeset -Uui16 -Z5 hv=2147483647
+ \\builtin typeset dasc line i
+ \\builtin set +U
- \cat "$@" | if \read -arN -1 line; then
- \typeset -i1 'line[*]'
+ \\builtin cat "$@" | if \\builtin read -arN -1 line; then
+ \\builtin typeset -i1 'line[*]'
i=0
while (( i < ${#line[*]} )); do
hv=${line[i++]}
if (( (pos & 15) == 0 )); then
(( pos )) && \
- \builtin print -r -- "$dasc|"
- \builtin print -n "${pos#16#} "
+ \\builtin print -r -- "$dasc|"
+ \\builtin print -nr "${pos#16#} "
dasc=' |'
fi
- \builtin print -n "${hv#16#} "
+ \\builtin print -nr "${hv#16#} "
#XXX EBCDIC, but we need [[:print:]] to fix this
if (( (hv < 32) || (hv > 126) )); then
dasc+=.
@@ -88,69 +123,69 @@
dasc+=${line[i-1]#1#}
fi
(( (pos++ & 15) == 7 )) && \
- \builtin print -n -- '- '
+ \\builtin print -nr -- '- '
done
while (( pos & 15 )); do
- \builtin print -n ' '
+ \\builtin print -nr ' '
(( (pos++ & 15) == 7 )) && \
- \builtin print -n -- '- '
+ \\builtin print -nr -- '- '
done
- (( hv == 2147483647 )) || \builtin print -r -- "$dasc|"
+ (( hv == 2147483647 )) || \\builtin print -r -- "$dasc|"
fi
}
fi
# Berkeley C shell compatible dirs, popd, and pushd functions
# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
-DIRSTACKBASE=$(\builtin realpath ~/. 2>/dev/null || \
- \builtin print -nr -- "${HOME:-/}")
-set -A DIRSTACK
+DIRSTACKBASE=$(\\builtin realpath ~/. 2>/dev/null || \
+ \\builtin print -nr -- "${HOME:-/}")
+\\builtin set -A DIRSTACK
function chpwd {
- DIRSTACK[0]=$(\builtin realpath . 2>/dev/null || \
- \builtin print -r -- "$PWD")
+ DIRSTACK[0]=$(\\builtin realpath . 2>/dev/null || \
+ \\builtin print -nr -- "$PWD")
[[ $DIRSTACKBASE = ?(*/) ]] || \
DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/\~}
\:
}
\chpwd .
cd() {
- \builtin cd "$@" || \return $?
+ \\builtin cd "$@" || \\builtin return $?
\chpwd "$@"
}
function cd_csh {
- \typeset d t=${1/#\~/$DIRSTACKBASE}
+ \\builtin typeset d t=${1/#\~/$DIRSTACKBASE}
- if ! d=$(\builtin cd "$t" 2>&1); then
- \builtin print -u2 "${1}: ${d##*cd: $t: }."
- \return 1
+ if ! d=$(\\builtin cd "$t" 2>&1); then
+ \\builtin print -ru2 "${1}: ${d##*cd: $t: }."
+ \\builtin return 1
fi
\cd "$t"
}
function dirs {
- \typeset d dwidth
- \typeset -i fl=0 fv=0 fn=0 cpos=0
+ \\builtin typeset d dwidth
+ \\builtin typeset -i fl=0 fv=0 fn=0 cpos=0
- while \getopts ":lvn" d; do
+ while \\builtin getopts ":lvn" d; do
case $d {
(l) fl=1 ;;
(v) fv=1 ;;
(n) fn=1 ;;
- (*) \builtin print -u2 'Usage: dirs [-lvn].'
- \return 1 ;;
+ (*) \\builtin print -ru2 'Usage: dirs [-lvn].'
+ \\builtin return 1 ;;
}
done
- \shift $((OPTIND - 1))
+ \\builtin shift $((OPTIND - 1))
if (( $# > 0 )); then
- \builtin print -u2 'Usage: dirs [-lvn].'
- \return 1
+ \\builtin print -ru2 'Usage: dirs [-lvn].'
+ \\builtin return 1
fi
if (( fv )); then
fv=0
while (( fv < ${#DIRSTACK[*]} )); do
d=${DIRSTACK[fv]}
(( fl )) && d=${d/#\~/$DIRSTACKBASE}
- \builtin print -r -- "$fv $d"
- \builtin let fv++
+ \\builtin print -r -- "$fv $d"
+ (( ++fv ))
done
else
fv=0
@@ -160,136 +195,117 @@
(( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
if (( fn && (cpos += dwidth + 1) >= 79 && \
dwidth < 80 )); then
- \builtin print
+ \\builtin print
(( cpos = dwidth + 1 ))
fi
- \builtin print -nr -- "$d "
- \builtin let fv++
+ \\builtin print -nr -- "$d "
+ (( ++fv ))
done
- \builtin print
+ \\builtin print
fi
- \return 0
+ \\builtin return 0
}
function popd {
- \typeset d fa
- \typeset -i n=1
+ \\builtin typeset d fa
+ \\builtin typeset -i n=1
- while \getopts ":0123456789lvn" d; do
+ while \\builtin getopts ":0123456789lvn" d; do
case $d {
(l|v|n) fa+=" -$d" ;;
(+*) n=2
- \break ;;
- (*) \builtin print -u2 'Usage: popd [-lvn] [+<n>].'
- \return 1 ;;
+ \\builtin break ;;
+ (*) \\builtin print -ru2 'Usage: popd [-lvn] [+<n>].'
+ \\builtin return 1 ;;
}
done
- \shift $((OPTIND - n))
+ \\builtin shift $((OPTIND - n))
n=0
if (( $# > 1 )); then
- \builtin print -u2 popd: Too many arguments.
- \return 1
+ \\builtin print -ru2 popd: Too many arguments.
+ \\builtin return 1
elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
- \builtin print -u2 popd: Directory stack not that deep.
- \return 1
+ \\builtin print -ru2 popd: Directory stack not that deep.
+ \\builtin return 1
fi
elif [[ -n $1 ]]; then
- \builtin print -u2 popd: Bad directory.
- \return 1
+ \\builtin print -ru2 popd: Bad directory.
+ \\builtin return 1
fi
if (( ${#DIRSTACK[*]} < 2 )); then
- \builtin print -u2 popd: Directory stack empty.
- \return 1
+ \\builtin print -ru2 popd: Directory stack empty.
+ \\builtin return 1
fi
- \unset DIRSTACK[n]
- \set -A DIRSTACK -- "${DIRSTACK[@]}"
- \cd_csh "${DIRSTACK[0]}" || \return 1
+ \\builtin unset DIRSTACK[n]
+ \\builtin set -A DIRSTACK -- "${DIRSTACK[@]}"
+ \cd_csh "${DIRSTACK[0]}" || \\builtin return 1
\dirs $fa
}
function pushd {
- \typeset d fa
- \typeset -i n=1
+ \\builtin typeset d fa
+ \\builtin typeset -i n=1
- while \getopts ":0123456789lvn" d; do
+ while \\builtin getopts ":0123456789lvn" d; do
case $d {
(l|v|n) fa+=" -$d" ;;
(+*) n=2
- \break ;;
- (*) \builtin print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
- \return 1 ;;
+ \\builtin break ;;
+ (*) \\builtin print -ru2 'Usage: pushd [-lvn] [<dir>|+<n>].'
+ \\builtin return 1 ;;
}
done
- \shift $((OPTIND - n))
+ \\builtin shift $((OPTIND - n))
if (( $# == 0 )); then
if (( ${#DIRSTACK[*]} < 2 )); then
- \builtin print -u2 pushd: No other directory.
- \return 1
+ \\builtin print -ru2 pushd: No other directory.
+ \\builtin return 1
fi
d=${DIRSTACK[1]}
DIRSTACK[1]=${DIRSTACK[0]}
- \cd_csh "$d" || \return 1
+ \cd_csh "$d" || \\builtin return 1
elif (( $# > 1 )); then
- \builtin print -u2 pushd: Too many arguments.
- \return 1
+ \\builtin print -ru2 pushd: Too many arguments.
+ \\builtin return 1
elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
- \builtin print -u2 pushd: Directory stack not that deep.
- \return 1
+ \\builtin print -ru2 pushd: Directory stack not that deep.
+ \\builtin return 1
fi
while (( n-- )); do
d=${DIRSTACK[0]}
- \unset DIRSTACK[0]
- \set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
+ \\builtin unset DIRSTACK[0]
+ \\builtin set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
done
- \cd_csh "${DIRSTACK[0]}" || \return 1
+ \cd_csh "${DIRSTACK[0]}" || \\builtin return 1
else
- \set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
- \cd_csh "$1" || \return 1
+ \\builtin set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
+ \cd_csh "$1" || \\builtin return 1
fi
\dirs $fa
}
-# pager (not control character safe)
-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 {
- \set +U
- \typeset c s="$*" t
- [[ -n $s ]] || { s=$(\cat; \builtin print x); s=${s%x}; }
- \typeset -i i=0 j=0 n=${#s} p=0 v x
- \typeset -i16 o
+ \\builtin set +U
+ \\builtin typeset c s="$*" t
+ [[ -n $s ]] || { s=$(\\builtin cat; \\builtin print x); s=${s%x}; }
+ \\builtin typeset -i i=0 j=0 n=${#s} p=0 v x
+ \\builtin typeset -i16 o
while (( i < n )); do
c=${s:(i++):1}
case $c {
- (=) \break ;;
+ (=) \\builtin break ;;
([A-Z]) (( v = 1#$c - 65 )) ;;
([a-z]) (( v = 1#$c - 71 )) ;;
([0-9]) (( v = 1#$c + 4 )) ;;
(+) v=62 ;;
(/) v=63 ;;
- (*) \continue ;;
+ (*) \\builtin continue ;;
}
(( x = (x << 6) | v ))
case $((p++)) {
- (0) \continue ;;
+ (0) \\builtin continue ;;
(1) (( o = (x >> 4) & 255 )) ;;
(2) (( o = (x >> 2) & 255 )) ;;
(3) (( o = x & 255 ))
@@ -297,63 +313,60 @@
;;
}
t+=\\x${o#16#}
- (( ++j & 4095 )) && \continue
- \builtin print -n $t
+ (( ++j & 4095 )) && \\builtin continue
+ \\builtin print -n $t
t=
done
- \builtin print -n $t
+ \\builtin print -n $t
}
-
-\set -A Lb64encode_tbl -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
- a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
function Lb64encode {
- \set +U
- \typeset c s t
+ \\builtin set +U
+ \\builtin typeset c s t table
+ \\builtin set -A table -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
+ a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
if (( $# )); then
- \read -raN-1 s <<<"$*"
- \unset s[${#s[*]}-1]
+ \\builtin read -raN-1 s <<<"$*"
+ \\builtin unset s[${#s[*]}-1]
else
- \read -raN-1 s
+ \\builtin read -raN-1 s
fi
- \typeset -i i=0 n=${#s[*]} j v
+ \\builtin typeset -i i=0 n=${#s[*]} v
while (( i < n )); do
(( v = s[i++] << 16 ))
- (( j = i < n ? s[i++] : 0 ))
- (( v |= j << 8 ))
- (( j = i < n ? s[i++] : 0 ))
- (( v |= j ))
- t+=${Lb64encode_tbl[v >> 18]}${Lb64encode_tbl[v >> 12 & 63]}
- c=${Lb64encode_tbl[v >> 6 & 63]}
+ (( v |= s[i++] << 8 ))
+ (( v |= s[i++] ))
+ t+=${table[v >> 18]}${table[v >> 12 & 63]}
+ c=${table[v >> 6 & 63]}
if (( i <= n )); then
- t+=$c${Lb64encode_tbl[v & 63]}
+ t+=$c${table[v & 63]}
elif (( i == n + 1 )); then
t+=$c=
else
t+===
fi
if (( ${#t} == 76 || i >= n )); then
- \builtin print $t
+ \\builtin print -r $t
t=
fi
done
}
# Better Avalanche for the Jenkins Hash
-\typeset -Z11 -Uui16 Lbafh_v
+\\builtin typeset -Z11 -Uui16 Lbafh_v
function Lbafh_init {
Lbafh_v=0
}
function Lbafh_add {
- \set +U
- \typeset s
+ \\builtin set +U
+ \\builtin typeset s
if (( $# )); then
- \read -raN-1 s <<<"$*"
- \unset s[${#s[*]}-1]
+ \\builtin read -raN-1 s <<<"$*"
+ \\builtin unset s[${#s[*]}-1]
else
- \read -raN-1 s
+ \\builtin read -raN-1 s
fi
- \typeset -i i=0 n=${#s[*]}
+ \\builtin typeset -i i=0 n=${#s[*]}
while (( i < n )); do
((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 ))
@@ -361,7 +374,7 @@
done
}
function Lbafh_finish {
- \typeset -Ui t
+ \\builtin typeset -Ui t
((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
((Lbafh_v << 1) & 0xFEFEFEFE) ))
@@ -373,42 +386,33 @@
# strip comments (and leading/trailing whitespace if IFS is set) from
# any file(s) given as argument, or stdin if none, and spew to stdout
function Lstripcom {
- \set -o noglob
- \cat "$@" | while \read _line; do
+ \\builtin set -o noglob
+ \\builtin cat "$@" | while \\builtin read _line; do
_line=${_line%%#*}
- [[ -n $_line ]] && \builtin print -r -- $_line
+ [[ -n $_line ]] && \\builtin print -r -- $_line
done
}
-# give MidnightBSD's laffer1 a bit of csh feeling
-function setenv {
- if (( $# )); then
- \eval '\export "$1"="${2:-}"'
- else
- \typeset -x
- fi
-}
-
# toggle built-in aliases and utilities, and aliases and functions from mkshrc
function enable {
- \typeset doprnt=0 mode=1 x y z rv=0
- \typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all
- \set -A b_alias
- \set -A i_alias
- \set -A i_func
+ \\builtin typeset doprnt=0 mode=1 x y z rv=0
+ \\builtin typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all
+ \\builtin set -A b_alias
+ \\builtin set -A i_alias
+ \\builtin set -A i_func
# accumulate mksh built-in aliases, in ASCIIbetical order
- i_alias[nalias]=autoload; b_alias[nalias++]='\typeset -fu'
- i_alias[nalias]=functions; b_alias[nalias++]='\typeset -f'
- i_alias[nalias]=hash; b_alias[nalias++]='\builtin alias -t'
- i_alias[nalias]=history; b_alias[nalias++]='\builtin fc -l'
- i_alias[nalias]=integer; b_alias[nalias++]='\typeset -i'
- i_alias[nalias]=local; b_alias[nalias++]='\typeset'
- i_alias[nalias]=login; b_alias[nalias++]='\exec login'
- i_alias[nalias]=nameref; b_alias[nalias++]='\typeset -n'
+ i_alias[nalias]=autoload; b_alias[nalias++]='\\builtin typeset -fu'
+ i_alias[nalias]=functions; b_alias[nalias++]='\\builtin typeset -f'
+ i_alias[nalias]=hash; b_alias[nalias++]='\\builtin alias -t'
+ i_alias[nalias]=history; b_alias[nalias++]='\\builtin fc -l'
+ i_alias[nalias]=integer; b_alias[nalias++]='\\builtin typeset -i'
+ i_alias[nalias]=local; b_alias[nalias++]='\\builtin typeset'
+ i_alias[nalias]=login; b_alias[nalias++]='\\builtin exec login'
+ i_alias[nalias]=nameref; b_alias[nalias++]='\\builtin typeset -n'
i_alias[nalias]=nohup; b_alias[nalias++]='nohup '
- i_alias[nalias]=r; b_alias[nalias++]='\builtin fc -e -'
- i_alias[nalias]=type; b_alias[nalias++]='\builtin whence -v'
+ i_alias[nalias]=r; b_alias[nalias++]='\\builtin fc -e -'
+ i_alias[nalias]=type; b_alias[nalias++]='\\builtin whence -v'
# accumulate mksh built-in utilities, in definition order, even ifndef
i_func[nfunc++]=.
@@ -416,6 +420,7 @@
i_func[nfunc++]='['
i_func[nfunc++]=alias
i_func[nfunc++]=break
+ # \\builtin cannot, by design, be overridden
i_func[nfunc++]=builtin
i_func[nfunc++]=cat
i_func[nfunc++]=cd
@@ -434,7 +439,6 @@
i_func[nfunc++]=jobs
i_func[nfunc++]=kill
i_func[nfunc++]=let
- i_func[nfunc++]='let]'
i_func[nfunc++]=print
i_func[nfunc++]=pwd
i_func[nfunc++]=read
@@ -471,11 +475,13 @@
i_alias[nalias]=la; b_alias[nalias++]='l -a'
i_alias[nalias]=ll; b_alias[nalias++]='l -l'
i_alias[nalias]=lo; b_alias[nalias++]='l -alo'
- i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\builtin fc -ln -1)"'
+ i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\\builtin fc -ln -1)"'
i_alias[nalias]=rot13; b_alias[nalias++]='tr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
- i_alias[nalias]=cls; b_alias[nalias++]='\builtin print -n \\ec'
+ i_alias[nalias]=cls; b_alias[nalias++]='\\builtin print -n \\ec'
# accumulate functions from dot.mkshrc, in definition order
+ i_func[nfunc++]=setenv
+ i_func[nfunc++]=smores
i_func[nfunc++]=hd
i_func[nfunc++]=chpwd
i_func[nfunc++]=cd
@@ -483,21 +489,19 @@
i_func[nfunc++]=dirs
i_func[nfunc++]=popd
i_func[nfunc++]=pushd
- i_func[nfunc++]=smores
i_func[nfunc++]=Lb64decode
i_func[nfunc++]=Lb64encode
i_func[nfunc++]=Lbafh_init
i_func[nfunc++]=Lbafh_add
i_func[nfunc++]=Lbafh_finish
i_func[nfunc++]=Lstripcom
- i_func[nfunc++]=setenv
i_func[nfunc++]=enable
# collect all identifiers, sorted ASCIIbetically
- \set -sA i_all -- "${i_alias[@]}" "${i_func[@]}"
+ \\builtin set -sA i_all -- "${i_alias[@]}" "${i_func[@]}"
# handle options, we don't do dynamic loading
- while \getopts "adf:nps" x; do
+ while \\builtin getopts "adf:nps" x; do
case $x {
(a)
mode=-1
@@ -506,8 +510,8 @@
# deliberately causing an error, like bash-static
;|
(f)
- \builtin print -u2 enable: dynamic loading not available
- \return 2
+ \\builtin print -ru2 enable: dynamic loading not available
+ \\builtin return 2
;;
(n)
mode=0
@@ -516,88 +520,89 @@
doprnt=1
;;
(s)
- \set -sA i_all -- . : break continue eval exec exit \
- export readonly return set shift times trap unset
+ \\builtin set -sA i_all -- . : break continue eval \
+ exec exit export readonly return set shift times \
+ trap unset
;;
(*)
- \builtin print -u2 enable: usage: \
+ \\builtin print -ru2 enable: usage: \
"enable [-adnps] [-f filename] [name ...]"
return 2
;;
}
done
- \shift $((OPTIND - 1))
+ \\builtin shift $((OPTIND - 1))
# display builtins enabled/disabled/all/special?
if (( doprnt || ($# == 0) )); then
for x in "${i_all[@]}"; do
- y=$(\alias "$x") || y=
- [[ $y = "$x='\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)'" ]]; z=$?
+ y=$(\\builtin alias "$x") || y=
+ [[ $y = "$x='\\\\builtin whence -p $x >/dev/null || (\\\\builtin print -r mksh: $x: not found; \\\\builtin exit 127) && \$(\\\\builtin whence -p $x)'" ]]; z=$?
case $mode:$z {
(-1:0|0:0)
- \print -r -- "enable -n $x"
+ \\builtin print -r -- "enable -n $x"
;;
(-1:1|1:1)
- \print -r -- "enable $x"
+ \\builtin print -r -- "enable $x"
;;
}
done
- \return 0
+ \\builtin return 0
fi
for x in "$@"; do
z=0
for y in "${i_alias[@]}" "${i_func[@]}"; do
- [[ $x = "$y" ]] || \continue
+ [[ $x = "$y" ]] || \\builtin continue
z=1
- \break
+ \\builtin break
done
if (( !z )); then
- \builtin print -ru2 enable: "$x": not a shell builtin
+ \\builtin print -ru2 enable: "$x": not a shell builtin
rv=1
- \continue
+ \\builtin continue
fi
if (( !mode )); then
# disable this
- \alias "$x=\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)"
+ \\builtin alias "$x=\\\\builtin whence -p $x >/dev/null || (\\\\builtin print -r mksh: $x: not found; \\\\builtin exit 127) && \$(\\\\builtin whence -p $x)"
else
# find out if this is an alias or not, first
z=0
y=-1
while (( ++y < nalias )); do
- [[ $x = "${i_alias[y]}" ]] || \continue
+ [[ $x = "${i_alias[y]}" ]] || \\builtin continue
z=1
- \break
+ \\builtin break
done
if (( z )); then
# re-enable the original alias body
- \alias "$x=${b_alias[y]}"
+ \\builtin alias "$x=${b_alias[y]}"
else
# re-enable the original utility/function
- \unalias "$x"
+ \\builtin unalias "$x"
fi
fi
done
- \return $rv
+ \\builtin return $rv
}
\: place customisations below this line
for p in ~/.etc/bin ~/bin; do
- [[ -d $p/. ]] || \continue
- #XXX OS/2
- [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
+ [[ -d $p/. ]] || \\builtin continue
+ [[ $PATHSEP$PATH$PATHSEP = *"$PATHSEP$p$PATHSEP"* ]] || \
+ PATH=$p$PATHSEP$PATH
done
-\export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
-\alias cls='\builtin print -n \\ec'
+\\builtin export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
+\\builtin alias cls='\\builtin print -n \\ec'
-#\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
+#\\builtin unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
# LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
#p=en_GB.UTF-8
-#\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
-#\set -U
+#\\builtin export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
+#\\builtin set -U
-\unset p
+\\builtin unset p
\: place customisations above this line
diff --git a/src/edit.c b/src/edit.c
index 4ed5ca3..58eaf7f 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -5,7 +5,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +28,7 @@
#ifndef MKSH_NO_CMDLINE_EDITING
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.312 2016/11/11 23:48:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.321 2017/04/12 16:46:20 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
@@ -145,6 +145,9 @@
static int
x_getc(void)
{
+#ifdef __OS2__
+ return (_read_kbd(0, 1, 0));
+#else
char c;
ssize_t n;
@@ -166,6 +169,7 @@
x_mode(true);
}
return ((n == 1) ? (int)(unsigned char)c : -1);
+#endif
}
static void
@@ -339,7 +343,7 @@
* and if so, discern "~foo/bar" and "~/baz" from "~blah";
* if we have a directory part (the former), try to expand
*/
- if (*s == '~' && (cp = mksh_sdirsep(s)) != NULL) {
+ if (*s == '~' && (cp = /* not sdirsep */ strchr(s, '/')) != NULL) {
/* ok, so split into "~foo"/"bar" or "~"/"baz" */
*cp++ = 0;
/* try to expand the tilde */
@@ -658,7 +662,7 @@
}
}
- if (*toglob == '~' && !mksh_vdirsep(toglob)) {
+ if (*toglob == '~' && /* not vdirsep */ !vstrchr(toglob, '/')) {
/* neither for '~foo' (but '~foo/bar') */
*flagsp |= XCF_IS_NOSPACE;
goto dont_add_glob;
@@ -949,6 +953,7 @@
static char **x_histp; /* history position */
static int x_nextcmd; /* for newline-and-next */
static char **x_histncp; /* saved x_histp for " */
+static char **x_histmcp; /* saved x_histp for " */
static char *xmp; /* mark pointer */
static unsigned char x_last_command;
static unsigned char (*x_tab)[X_TABSZ]; /* key definition */
@@ -1163,6 +1168,7 @@
x_modified(void)
{
if (!modified) {
+ x_histmcp = x_histp;
x_histp = histptr + 1;
modified = 1;
}
@@ -1239,7 +1245,7 @@
xlp_valid = true;
xmp = NULL;
x_curprefix = 0;
- x_histp = histptr + 1;
+ x_histmcp = x_histp = histptr + 1;
x_last_command = XFUNC_error;
x_init_prompt(true);
@@ -1780,12 +1786,11 @@
static int
x_end_of_text(int c MKSH_A_UNUSED)
{
- unsigned char tmp;
- char *cp = (void *)&tmp;
+ unsigned char tmp[1], *cp = tmp;
- tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
+ *tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
(unsigned char)CTRL('D');
- x_zotc3(&cp);
+ x_zotc3((char **)&cp);
x_putc('\r');
x_putc('\n');
x_flush();
@@ -1862,9 +1867,11 @@
static int
x_nl_next_com(int c MKSH_A_UNUSED)
{
- if (!x_histncp || (x_histp != x_histncp && x_histp != histptr + 1))
+ if (!modified)
+ x_histmcp = x_histp;
+ if (!x_histncp || (x_histmcp != x_histncp && x_histmcp != histptr + 1))
/* fresh start of ^O */
- x_histncp = x_histp;
+ x_histncp = x_histmcp;
x_nextcmd = source->line - (histptr - x_histncp) + 1;
return (x_newline('\n'));
}
@@ -1922,8 +1929,10 @@
offset = -1;
break;
}
- if (p > pat)
- *--p = '\0';
+ if (p > pat) {
+ p = x_bs0(p - 1, pat);
+ *p = '\0';
+ }
if (p == pat)
offset = -1;
else
@@ -2933,8 +2942,12 @@
char *cp2;
width = utf_widthadj(*cp, (const char **)&cp2);
- while (*cp < cp2)
- x_putcf(*(*cp)++);
+ if (cp2 == *cp + 1) {
+ (*cp)++;
+ shf_puts("\xEF\xBF\xBD", shl_out);
+ } else
+ while (*cp < cp2)
+ x_putcf(*(*cp)++);
} else {
(*cp)++;
x_putc(c);
@@ -3162,6 +3175,8 @@
x_ins(cp);
*rcp = ch;
}
+ if (!modified)
+ x_histmcp = x_histp;
modified = m + 1;
return (KSTD);
}
@@ -3466,7 +3481,7 @@
static struct edstate ebuf;
static struct edstate undobuf;
-static struct edstate *es; /* current editor state */
+static struct edstate *vs; /* current Vi editing mode state */
static struct edstate *undo;
static char *ibuf; /* input buffer */
@@ -3530,7 +3545,7 @@
undobuf.linelen = ebuf.linelen = 0;
undobuf.cursor = ebuf.cursor = 0;
undobuf.winleft = ebuf.winleft = 0;
- es = &ebuf;
+ vs = &ebuf;
undo = &undobuf;
x_init_prompt(true);
@@ -3581,7 +3596,7 @@
unwind(LSHELL);
} else if (isched(c, edchars.eof) &&
state != VVERSION) {
- if (es->linelen == 0) {
+ if (vs->linelen == 0) {
x_vi_zotc(c);
c = -1;
break;
@@ -3598,15 +3613,15 @@
x_putc('\n');
x_flush();
- if (c == -1 || (ssize_t)LINE <= es->linelen)
+ if (c == -1 || (ssize_t)LINE <= vs->linelen)
return (-1);
- if (es->cbuf != buf)
- memcpy(buf, es->cbuf, es->linelen);
+ if (vs->cbuf != buf)
+ memcpy(buf, vs->cbuf, vs->linelen);
- buf[es->linelen++] = '\n';
+ buf[vs->linelen++] = '\n';
- return (es->linelen);
+ return (vs->linelen);
}
static int
@@ -3643,7 +3658,7 @@
break;
case 0:
if (state == VLIT) {
- es->cursor--;
+ vs->cursor--;
refresh(0);
} else
refresh(insert != 0);
@@ -3665,8 +3680,8 @@
state = nextstate(ch);
if (state == VSEARCH) {
save_cbuf();
- es->cursor = 0;
- es->linelen = 0;
+ vs->cursor = 0;
+ vs->linelen = 0;
if (putbuf(ch == '/' ? "/" : "?", 1,
false) != 0)
return (-1);
@@ -3674,8 +3689,8 @@
}
if (state == VVERSION) {
save_cbuf();
- es->cursor = 0;
- es->linelen = 0;
+ vs->cursor = 0;
+ vs->linelen = 0;
putbuf(KSH_VERSION,
strlen(KSH_VERSION), false);
refresh(0);
@@ -3686,10 +3701,10 @@
case VLIT:
if (is_bad(ch)) {
- del_range(es->cursor, es->cursor + 1);
+ del_range(vs->cursor, vs->cursor + 1);
vi_error();
} else
- es->cbuf[es->cursor++] = ch;
+ vs->cbuf[vs->cursor++] = ch;
refresh(1);
state = VNORMAL;
break;
@@ -3772,8 +3787,8 @@
} else if (isched(ch, edchars.erase) || ch == CTRL('h')) {
if (srchlen != 0) {
srchlen--;
- es->linelen -= char_len(locpat[srchlen]);
- es->cursor = es->linelen;
+ vs->linelen -= char_len(locpat[srchlen]);
+ vs->cursor = vs->linelen;
refresh(0);
return (0);
}
@@ -3782,8 +3797,8 @@
refresh(0);
} else if (isched(ch, edchars.kill)) {
srchlen = 0;
- es->linelen = 1;
- es->cursor = 1;
+ vs->linelen = 1;
+ vs->cursor = 1;
refresh(0);
return (0);
} else if (isched(ch, edchars.werase)) {
@@ -3793,16 +3808,16 @@
new_es.cursor = srchlen;
new_es.cbuf = locpat;
- save_es = es;
- es = &new_es;
+ save_es = vs;
+ vs = &new_es;
n = backword(1);
- es = save_es;
+ vs = save_es;
i = (unsigned)srchlen;
while (--i >= n)
- es->linelen -= char_len(locpat[i]);
+ vs->linelen -= char_len(locpat[i]);
srchlen = (int)n;
- es->cursor = es->linelen;
+ vs->cursor = vs->linelen;
refresh(0);
return (0);
} else {
@@ -3811,17 +3826,17 @@
else {
locpat[srchlen++] = ch;
if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
- if ((size_t)es->linelen + 2 >
- (size_t)es->cbufsize)
+ if ((size_t)vs->linelen + 2 >
+ (size_t)vs->cbufsize)
vi_error();
- es->cbuf[es->linelen++] = '^';
- es->cbuf[es->linelen++] = UNCTRL(ch);
+ vs->cbuf[vs->linelen++] = '^';
+ vs->cbuf[vs->linelen++] = UNCTRL(ch);
} else {
- if (es->linelen >= es->cbufsize)
+ if (vs->linelen >= vs->cbufsize)
vi_error();
- es->cbuf[es->linelen++] = ch;
+ vs->cbuf[vs->linelen++] = ch;
}
- es->cursor = es->linelen;
+ vs->cursor = vs->linelen;
refresh(0);
}
return (0);
@@ -3834,18 +3849,18 @@
switch (ch) {
case 'A':
/* the cursor may not be at the BOL */
- if (!es->cursor)
+ if (!vs->cursor)
break;
/* nor further in the line than we can search for */
- if ((size_t)es->cursor >= sizeof(srchpat) - 1)
- es->cursor = sizeof(srchpat) - 2;
+ if ((size_t)vs->cursor >= sizeof(srchpat) - 1)
+ vs->cursor = sizeof(srchpat) - 2;
/* anchor the search pattern */
srchpat[0] = '^';
/* take the current line up to the cursor */
- memmove(srchpat + 1, es->cbuf, es->cursor);
- srchpat[es->cursor + 1] = '\0';
+ memmove(srchpat + 1, vs->cbuf, vs->cursor);
+ srchpat[vs->cursor + 1] = '\0';
/* set a magic flag */
- argc1 = 2 + (int)es->cursor;
+ argc1 = 2 + (int)vs->cursor;
/* and emulate a backwards history search */
lastsearch = '/';
*curcmd = 'n';
@@ -3942,52 +3957,52 @@
if (isched(ch, edchars.erase) || ch == CTRL('h')) {
if (insert == REPLACE) {
- if (es->cursor == undo->cursor) {
+ if (vs->cursor == undo->cursor) {
vi_error();
return (0);
}
if (inslen > 0)
inslen--;
- es->cursor--;
- if (es->cursor >= undo->linelen)
- es->linelen--;
+ vs->cursor--;
+ if (vs->cursor >= undo->linelen)
+ vs->linelen--;
else
- es->cbuf[es->cursor] = undo->cbuf[es->cursor];
+ vs->cbuf[vs->cursor] = undo->cbuf[vs->cursor];
} else {
- if (es->cursor == 0)
+ if (vs->cursor == 0)
return (0);
if (inslen > 0)
inslen--;
- es->cursor--;
- es->linelen--;
- memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1],
- es->linelen - es->cursor + 1);
+ vs->cursor--;
+ vs->linelen--;
+ memmove(&vs->cbuf[vs->cursor], &vs->cbuf[vs->cursor + 1],
+ vs->linelen - vs->cursor + 1);
}
expanded = NONE;
return (0);
}
if (isched(ch, edchars.kill)) {
- if (es->cursor != 0) {
+ if (vs->cursor != 0) {
inslen = 0;
- memmove(es->cbuf, &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen -= es->cursor;
- es->cursor = 0;
+ memmove(vs->cbuf, &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen -= vs->cursor;
+ vs->cursor = 0;
}
expanded = NONE;
return (0);
}
if (isched(ch, edchars.werase)) {
- if (es->cursor != 0) {
+ if (vs->cursor != 0) {
tcursor = backword(1);
- memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen -= es->cursor - tcursor;
- if (inslen < es->cursor - tcursor)
+ memmove(&vs->cbuf[tcursor], &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen -= vs->cursor - tcursor;
+ if (inslen < vs->cursor - tcursor)
inslen = 0;
else
- inslen -= es->cursor - tcursor;
- es->cursor = tcursor;
+ inslen -= vs->cursor - tcursor;
+ vs->cursor = tcursor;
}
expanded = NONE;
return (0);
@@ -4034,7 +4049,7 @@
break;
case CTRL('e'):
- print_expansions(es, 0);
+ print_expansions(vs, 0);
break;
case CTRL('i'):
@@ -4046,17 +4061,17 @@
/* end nonstandard vi commands } */
default:
- if (es->linelen >= es->cbufsize - 1)
+ if (vs->linelen >= vs->cbufsize - 1)
return (-1);
ibuf[inslen++] = ch;
if (insert == INSERT) {
- memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen++;
+ memmove(&vs->cbuf[vs->cursor + 1], &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen++;
}
- es->cbuf[es->cursor++] = ch;
- if (insert == REPLACE && es->cursor > es->linelen)
- es->linelen++;
+ vs->cbuf[vs->cursor++] = ch;
+ if (insert == REPLACE && vs->cursor > vs->linelen)
+ vs->linelen++;
expanded = NONE;
}
return (0);
@@ -4075,18 +4090,18 @@
if (is_move(*cmd)) {
if ((cur = domove(argcnt, cmd, 0)) >= 0) {
- if (cur == es->linelen && cur != 0)
+ if (cur == vs->linelen && cur != 0)
cur--;
- es->cursor = cur;
+ vs->cursor = cur;
} else
return (-1);
} else {
/* Don't save state in middle of macro.. */
if (is_undoable(*cmd) && !macro.p) {
- undo->winleft = es->winleft;
- memmove(undo->cbuf, es->cbuf, es->linelen);
- undo->linelen = es->linelen;
- undo->cursor = es->cursor;
+ undo->winleft = vs->winleft;
+ memmove(undo->cbuf, vs->cbuf, vs->linelen);
+ undo->linelen = vs->linelen;
+ undo->cursor = vs->cursor;
lastac = argcnt;
memmove(lastcmd, cmd, MAXVICMD);
}
@@ -4141,8 +4156,8 @@
case 'a':
modified = 1;
hnum = hlast;
- if (es->linelen != 0)
- es->cursor++;
+ if (vs->linelen != 0)
+ vs->cursor++;
insert = INSERT;
break;
@@ -4150,13 +4165,13 @@
modified = 1;
hnum = hlast;
del_range(0, 0);
- es->cursor = es->linelen;
+ vs->cursor = vs->linelen;
insert = INSERT;
break;
case 'S':
- es->cursor = domove(1, "^", 1);
- del_range(es->cursor, es->linelen);
+ vs->cursor = domove(1, "^", 1);
+ del_range(vs->cursor, vs->linelen);
modified = 1;
hnum = hlast;
insert = INSERT;
@@ -4172,7 +4187,7 @@
case 'y':
if (*cmd == cmd[1]) {
c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
- c2 = es->linelen;
+ c2 = vs->linelen;
} else if (!is_move(cmd[1]))
return (-1);
else {
@@ -4180,18 +4195,18 @@
return (-1);
if (*cmd == 'c' &&
(cmd[1] == 'w' || cmd[1] == 'W') &&
- !ksh_isspace(es->cbuf[es->cursor])) {
+ !ksh_isspace(vs->cbuf[vs->cursor])) {
do {
--ncursor;
- } while (ksh_isspace(es->cbuf[ncursor]));
+ } while (ksh_isspace(vs->cbuf[ncursor]));
ncursor++;
}
- if (ncursor > es->cursor) {
- c1 = es->cursor;
+ if (ncursor > vs->cursor) {
+ c1 = vs->cursor;
c2 = ncursor;
} else {
c1 = ncursor;
- c2 = es->cursor;
+ c2 = vs->cursor;
if (cmd[1] == '%')
c2++;
}
@@ -4200,7 +4215,7 @@
yank_range(c1, c2);
if (*cmd != 'y') {
del_range(c1, c2);
- es->cursor = c1;
+ vs->cursor = c1;
}
if (*cmd == 'c') {
modified = 1;
@@ -4212,13 +4227,13 @@
case 'p':
modified = 1;
hnum = hlast;
- if (es->linelen != 0)
- es->cursor++;
+ if (vs->linelen != 0)
+ vs->cursor++;
while (putbuf(ybuf, yanklen, false) == 0 &&
--argcnt > 0)
;
- if (es->cursor != 0)
- es->cursor--;
+ if (vs->cursor != 0)
+ vs->cursor--;
if (argcnt != 0)
return (-1);
break;
@@ -4230,8 +4245,8 @@
while (putbuf(ybuf, yanklen, false) == 0 &&
--argcnt > 0)
any = 1;
- if (any && es->cursor != 0)
- es->cursor--;
+ if (any && vs->cursor != 0)
+ vs->cursor--;
if (argcnt != 0)
return (-1);
break;
@@ -4239,15 +4254,15 @@
case 'C':
modified = 1;
hnum = hlast;
- del_range(es->cursor, es->linelen);
+ del_range(vs->cursor, vs->linelen);
insert = INSERT;
break;
case 'D':
- yank_range(es->cursor, es->linelen);
- del_range(es->cursor, es->linelen);
- if (es->cursor != 0)
- es->cursor--;
+ yank_range(vs->cursor, vs->linelen);
+ del_range(vs->cursor, vs->linelen);
+ if (vs->cursor != 0)
+ vs->cursor--;
break;
case 'g':
@@ -4276,7 +4291,7 @@
case 'I':
modified = 1;
hnum = hlast;
- es->cursor = domove(1, "^", 1);
+ vs->cursor = domove(1, "^", 1);
insert = INSERT;
break;
@@ -4303,7 +4318,7 @@
break;
case 'r':
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
modified = 1;
hnum = hlast;
@@ -4312,11 +4327,11 @@
else {
int n;
- if (es->cursor + argcnt > es->linelen)
+ if (vs->cursor + argcnt > vs->linelen)
return (-1);
for (n = 0; n < argcnt; ++n)
- es->cbuf[es->cursor + n] = cmd[1];
- es->cursor += n - 1;
+ vs->cbuf[vs->cursor + n] = cmd[1];
+ vs->cursor += n - 1;
}
break;
@@ -4327,66 +4342,66 @@
break;
case 's':
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
modified = 1;
hnum = hlast;
- if (es->cursor + argcnt > es->linelen)
- argcnt = es->linelen - es->cursor;
- del_range(es->cursor, es->cursor + argcnt);
+ if (vs->cursor + argcnt > vs->linelen)
+ argcnt = vs->linelen - vs->cursor;
+ del_range(vs->cursor, vs->cursor + argcnt);
insert = INSERT;
break;
case 'v':
if (!argcnt) {
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
if (modified) {
- es->cbuf[es->linelen] = '\0';
- histsave(&source->line, es->cbuf,
+ vs->cbuf[vs->linelen] = '\0';
+ histsave(&source->line, vs->cbuf,
HIST_STORE, true);
} else
argcnt = source->line + 1 -
(hlast - hnum);
}
if (argcnt)
- shf_snprintf(es->cbuf, es->cbufsize, Tf_sd,
+ shf_snprintf(vs->cbuf, vs->cbufsize, Tf_sd,
"fc -e ${VISUAL:-${EDITOR:-vi}} --",
argcnt);
else
- strlcpy(es->cbuf,
+ strlcpy(vs->cbuf,
"fc -e ${VISUAL:-${EDITOR:-vi}} --",
- es->cbufsize);
- es->linelen = strlen(es->cbuf);
+ vs->cbufsize);
+ vs->linelen = strlen(vs->cbuf);
return (2);
case 'x':
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
modified = 1;
hnum = hlast;
- if (es->cursor + argcnt > es->linelen)
- argcnt = es->linelen - es->cursor;
- yank_range(es->cursor, es->cursor + argcnt);
- del_range(es->cursor, es->cursor + argcnt);
+ if (vs->cursor + argcnt > vs->linelen)
+ argcnt = vs->linelen - vs->cursor;
+ yank_range(vs->cursor, vs->cursor + argcnt);
+ del_range(vs->cursor, vs->cursor + argcnt);
break;
case 'X':
- if (es->cursor > 0) {
+ if (vs->cursor > 0) {
modified = 1;
hnum = hlast;
- if (es->cursor < argcnt)
- argcnt = es->cursor;
- yank_range(es->cursor - argcnt, es->cursor);
- del_range(es->cursor - argcnt, es->cursor);
- es->cursor -= argcnt;
+ if (vs->cursor < argcnt)
+ argcnt = vs->cursor;
+ yank_range(vs->cursor - argcnt, vs->cursor);
+ del_range(vs->cursor - argcnt, vs->cursor);
+ vs->cursor -= argcnt;
} else
return (-1);
break;
case 'u':
- t = es;
- es = undo;
+ t = vs;
+ vs = undo;
undo = t;
break;
@@ -4434,7 +4449,7 @@
}
if (argcnt >= 2) {
/* flag from cursor-up command */
- es->cursor = argcnt - 2;
+ vs->cursor = argcnt - 2;
return (0);
}
break;
@@ -4475,16 +4490,16 @@
}
modified = 1;
hnum = hlast;
- if (es->cursor != es->linelen)
- es->cursor++;
+ if (vs->cursor != vs->linelen)
+ vs->cursor++;
while (*p && !issp(*p)) {
argcnt++;
p++;
}
if (putbuf(T1space, 1, false) != 0 ||
putbuf(sp, argcnt, false) != 0) {
- if (es->cursor != 0)
- es->cursor--;
+ if (vs->cursor != 0)
+ vs->cursor--;
return (-1);
}
insert = INSERT;
@@ -4496,10 +4511,10 @@
char *p;
int i;
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
for (i = 0; i < argcnt; i++) {
- p = &es->cbuf[es->cursor];
+ p = &vs->cbuf[vs->cursor];
if (ksh_islower(*p)) {
modified = 1;
hnum = hlast;
@@ -4509,18 +4524,18 @@
hnum = hlast;
*p = ksh_tolower(*p);
}
- if (es->cursor < es->linelen - 1)
- es->cursor++;
+ if (vs->cursor < vs->linelen - 1)
+ vs->cursor++;
}
break;
}
case '#':
{
- int ret = x_do_comment(es->cbuf, es->cbufsize,
- &es->linelen);
+ int ret = x_do_comment(vs->cbuf, vs->cbufsize,
+ &vs->linelen);
if (ret >= 0)
- es->cursor = 0;
+ vs->cursor = 0;
return (ret);
}
@@ -4528,7 +4543,7 @@
case '=':
/* Nonstandard vi/ksh */
case CTRL('e'):
- print_expansions(es, 1);
+ print_expansions(vs, 1);
break;
@@ -4564,13 +4579,13 @@
case '[':
case 'O':
state = VPREFIX2;
- if (es->linelen != 0)
- es->cursor++;
+ if (vs->linelen != 0)
+ vs->cursor++;
insert = INSERT;
return (0);
}
- if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
- es->cursor--;
+ if (insert == 0 && vs->cursor != 0 && vs->cursor >= vs->linelen)
+ vs->cursor--;
}
return (0);
}
@@ -4583,30 +4598,30 @@
switch (*cmd) {
case 'b':
- if (!sub && es->cursor == 0)
+ if (!sub && vs->cursor == 0)
return (-1);
ncursor = backword(argcnt);
break;
case 'B':
- if (!sub && es->cursor == 0)
+ if (!sub && vs->cursor == 0)
return (-1);
ncursor = Backword(argcnt);
break;
case 'e':
- if (!sub && es->cursor + 1 >= es->linelen)
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = endword(argcnt);
- if (sub && ncursor < es->linelen)
+ if (sub && ncursor < vs->linelen)
ncursor++;
break;
case 'E':
- if (!sub && es->cursor + 1 >= es->linelen)
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = Endword(argcnt);
- if (sub && ncursor < es->linelen)
+ if (sub && ncursor < vs->linelen)
ncursor++;
break;
@@ -4634,32 +4649,32 @@
case 'h':
case CTRL('h'):
- if (!sub && es->cursor == 0)
+ if (!sub && vs->cursor == 0)
return (-1);
- ncursor = es->cursor - argcnt;
+ ncursor = vs->cursor - argcnt;
if (ncursor < 0)
ncursor = 0;
break;
case ' ':
case 'l':
- if (!sub && es->cursor + 1 >= es->linelen)
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
- if (es->linelen != 0) {
- ncursor = es->cursor + argcnt;
- if (ncursor > es->linelen)
- ncursor = es->linelen;
+ if (vs->linelen != 0) {
+ ncursor = vs->cursor + argcnt;
+ if (ncursor > vs->linelen)
+ ncursor = vs->linelen;
}
break;
case 'w':
- if (!sub && es->cursor + 1 >= es->linelen)
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = forwword(argcnt);
break;
case 'W':
- if (!sub && es->cursor + 1 >= es->linelen)
+ if (!sub && vs->cursor + 1 >= vs->linelen)
return (-1);
ncursor = Forwword(argcnt);
break;
@@ -4670,43 +4685,43 @@
case '^':
ncursor = 0;
- while (ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
+ while (ncursor < vs->linelen - 1 &&
+ ksh_isspace(vs->cbuf[ncursor]))
ncursor++;
break;
case '|':
ncursor = argcnt;
- if (ncursor > es->linelen)
- ncursor = es->linelen;
+ if (ncursor > vs->linelen)
+ ncursor = vs->linelen;
if (ncursor)
ncursor--;
break;
case '$':
- if (es->linelen != 0)
- ncursor = es->linelen;
+ if (vs->linelen != 0)
+ ncursor = vs->linelen;
else
ncursor = 0;
break;
case '%':
- ncursor = es->cursor;
- while (ncursor < es->linelen &&
- (i = bracktype(es->cbuf[ncursor])) == 0)
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen &&
+ (i = bracktype(vs->cbuf[ncursor])) == 0)
ncursor++;
- if (ncursor == es->linelen)
+ if (ncursor == vs->linelen)
return (-1);
bcount = 1;
do {
if (i > 0) {
- if (++ncursor >= es->linelen)
+ if (++ncursor >= vs->linelen)
return (-1);
} else {
if (--ncursor < 0)
return (-1);
}
- t = bracktype(es->cbuf[ncursor]);
+ t = bracktype(vs->cbuf[ncursor]);
if (t == i)
bcount++;
else if (t == -i)
@@ -4728,8 +4743,8 @@
while (count-- > 0)
if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0)
return (-1);
- if (es->cursor > 0)
- es->cursor--;
+ if (vs->cursor > 0)
+ vs->cursor--;
insert = 0;
return (0);
}
@@ -4739,7 +4754,7 @@
{
yanklen = b - a;
if (yanklen != 0)
- memmove(ybuf, &es->cbuf[a], yanklen);
+ memmove(ybuf, &vs->cbuf[a], yanklen);
}
static int
@@ -4777,17 +4792,17 @@
static void
save_cbuf(void)
{
- memmove(holdbufp, es->cbuf, es->linelen);
- holdlen = es->linelen;
+ memmove(holdbufp, vs->cbuf, vs->linelen);
+ holdlen = vs->linelen;
holdbufp[holdlen] = '\0';
}
static void
restore_cbuf(void)
{
- es->cursor = 0;
- es->linelen = holdlen;
- memmove(es->cbuf, holdbufp, holdlen);
+ vs->cursor = 0;
+ vs->linelen = holdlen;
+ memmove(vs->cbuf, holdbufp, holdlen);
}
/* return a new edstate */
@@ -4838,28 +4853,28 @@
if (len == 0)
return (0);
if (repl) {
- if (es->cursor + len >= es->cbufsize)
+ if (vs->cursor + len >= vs->cbufsize)
return (-1);
- if (es->cursor + len > es->linelen)
- es->linelen = es->cursor + len;
+ if (vs->cursor + len > vs->linelen)
+ vs->linelen = vs->cursor + len;
} else {
- if (es->linelen + len >= es->cbufsize)
+ if (vs->linelen + len >= vs->cbufsize)
return (-1);
- memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen += len;
+ memmove(&vs->cbuf[vs->cursor + len], &vs->cbuf[vs->cursor],
+ vs->linelen - vs->cursor);
+ vs->linelen += len;
}
- memmove(&es->cbuf[es->cursor], buf, len);
- es->cursor += len;
+ memmove(&vs->cbuf[vs->cursor], buf, len);
+ vs->cursor += len;
return (0);
}
static void
del_range(int a, int b)
{
- if (es->linelen != b)
- memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
- es->linelen -= b - a;
+ if (vs->linelen != b)
+ memmove(&vs->cbuf[a], &vs->cbuf[b], vs->linelen - b);
+ vs->linelen -= b - a;
}
static int
@@ -4867,19 +4882,19 @@
{
int ncursor;
- if (es->linelen == 0)
+ if (vs->linelen == 0)
return (-1);
- ncursor = es->cursor;
+ ncursor = vs->cursor;
while (cnt--) {
do {
if (forw) {
- if (++ncursor == es->linelen)
+ if (++ncursor == vs->linelen)
return (-1);
} else {
if (--ncursor < 0)
return (-1);
}
- } while (es->cbuf[ncursor] != ch);
+ } while (vs->cbuf[ncursor] != ch);
}
if (!incl) {
if (forw)
@@ -4895,19 +4910,19 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (ncursor < es->linelen &&
- ksh_isalnux(es->cbuf[ncursor]))
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen && argcnt--) {
+ if (ksh_isalnux(vs->cbuf[ncursor]))
+ while (ncursor < vs->linelen &&
+ ksh_isalnux(vs->cbuf[ncursor]))
ncursor++;
- else if (!ksh_isspace(es->cbuf[ncursor]))
- while (ncursor < es->linelen &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
+ else if (!ksh_isspace(vs->cbuf[ncursor]))
+ while (ncursor < vs->linelen &&
+ !ksh_isalnux(vs->cbuf[ncursor]) &&
+ !ksh_isspace(vs->cbuf[ncursor]))
ncursor++;
- while (ncursor < es->linelen &&
- ksh_isspace(es->cbuf[ncursor]))
+ while (ncursor < vs->linelen &&
+ ksh_isspace(vs->cbuf[ncursor]))
ncursor++;
}
return (ncursor);
@@ -4918,19 +4933,19 @@
{
int ncursor;
- ncursor = es->cursor;
+ ncursor = vs->cursor;
while (ncursor > 0 && argcnt--) {
- while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor]))
+ while (--ncursor > 0 && ksh_isspace(vs->cbuf[ncursor]))
;
if (ncursor > 0) {
- if (ksh_isalnux(es->cbuf[ncursor]))
+ if (ksh_isalnux(vs->cbuf[ncursor]))
while (--ncursor >= 0 &&
- ksh_isalnux(es->cbuf[ncursor]))
+ ksh_isalnux(vs->cbuf[ncursor]))
;
else
while (--ncursor >= 0 &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
+ !ksh_isalnux(vs->cbuf[ncursor]) &&
+ !ksh_isspace(vs->cbuf[ncursor]))
;
ncursor++;
}
@@ -4943,20 +4958,20 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- while (++ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen && argcnt--) {
+ while (++ncursor < vs->linelen - 1 &&
+ ksh_isspace(vs->cbuf[ncursor]))
;
- if (ncursor < es->linelen - 1) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (++ncursor < es->linelen &&
- ksh_isalnux(es->cbuf[ncursor]))
+ if (ncursor < vs->linelen - 1) {
+ if (ksh_isalnux(vs->cbuf[ncursor]))
+ while (++ncursor < vs->linelen &&
+ ksh_isalnux(vs->cbuf[ncursor]))
;
else
- while (++ncursor < es->linelen &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
+ while (++ncursor < vs->linelen &&
+ !ksh_isalnux(vs->cbuf[ncursor]) &&
+ !ksh_isspace(vs->cbuf[ncursor]))
;
ncursor--;
}
@@ -4969,13 +4984,13 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- while (ncursor < es->linelen &&
- !ksh_isspace(es->cbuf[ncursor]))
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen && argcnt--) {
+ while (ncursor < vs->linelen &&
+ !ksh_isspace(vs->cbuf[ncursor]))
ncursor++;
- while (ncursor < es->linelen &&
- ksh_isspace(es->cbuf[ncursor]))
+ while (ncursor < vs->linelen &&
+ ksh_isspace(vs->cbuf[ncursor]))
ncursor++;
}
return (ncursor);
@@ -4986,11 +5001,11 @@
{
int ncursor;
- ncursor = es->cursor;
+ ncursor = vs->cursor;
while (ncursor > 0 && argcnt--) {
- while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor]))
+ while (--ncursor >= 0 && ksh_isspace(vs->cbuf[ncursor]))
;
- while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor]))
+ while (ncursor >= 0 && !ksh_isspace(vs->cbuf[ncursor]))
ncursor--;
ncursor++;
}
@@ -5002,14 +5017,14 @@
{
int ncursor;
- ncursor = es->cursor;
- while (ncursor < es->linelen - 1 && argcnt--) {
- while (++ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
+ ncursor = vs->cursor;
+ while (ncursor < vs->linelen - 1 && argcnt--) {
+ while (++ncursor < vs->linelen - 1 &&
+ ksh_isspace(vs->cbuf[ncursor]))
;
- if (ncursor < es->linelen - 1) {
- while (++ncursor < es->linelen &&
- !ksh_isspace(es->cbuf[ncursor]))
+ if (ncursor < vs->linelen - 1) {
+ while (++ncursor < vs->linelen &&
+ !ksh_isspace(vs->cbuf[ncursor]))
;
ncursor--;
}
@@ -5036,10 +5051,10 @@
}
if (save)
save_cbuf();
- if ((es->linelen = strlen(hptr)) >= es->cbufsize)
- es->linelen = es->cbufsize - 1;
- memmove(es->cbuf, hptr, es->linelen);
- es->cursor = 0;
+ if ((vs->linelen = strlen(hptr)) >= vs->cbufsize)
+ vs->linelen = vs->cbufsize - 1;
+ memmove(vs->cbuf, hptr, vs->linelen);
+ vs->cursor = 0;
ohnum = n;
return (0);
}
@@ -5070,10 +5085,10 @@
save_cbuf();
histnum(hist);
hptr = *histpos();
- if ((es->linelen = strlen(hptr)) >= es->cbufsize)
- es->linelen = es->cbufsize - 1;
- memmove(es->cbuf, hptr, es->linelen);
- es->cursor = 0;
+ if ((vs->linelen = strlen(hptr)) >= vs->cbufsize)
+ vs->linelen = vs->cbufsize - 1;
+ memmove(vs->cbuf, hptr, vs->linelen);
+ vs->cursor = 0;
return (hist);
}
@@ -5108,12 +5123,12 @@
{
int cur, col;
- if (es->cursor < es->winleft)
+ if (vs->cursor < vs->winleft)
return (1);
col = 0;
- cur = es->winleft;
- while (cur < es->cursor)
- col = newcol((unsigned char)es->cbuf[cur++], col);
+ cur = vs->winleft;
+ while (cur < vs->cursor)
+ col = newcol((unsigned char)vs->cbuf[cur++], col);
if (col >= winwidth)
return (1);
return (0);
@@ -5128,19 +5143,19 @@
holdcur1 = holdcur2 = tcur = 0;
holdcol1 = holdcol2 = tcol = 0;
- while (tcur < es->cursor) {
+ while (tcur < vs->cursor) {
if (tcol - holdcol2 > winwidth / 2) {
holdcur1 = holdcur2;
holdcol1 = holdcol2;
holdcur2 = tcur;
holdcol2 = tcol;
}
- tcol = newcol((unsigned char)es->cbuf[tcur++], tcol);
+ tcol = newcol((unsigned char)vs->cbuf[tcur++], tcol);
}
while (tcol - holdcol1 > winwidth / 2)
- holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++],
+ holdcol1 = newcol((unsigned char)vs->cbuf[holdcur1++],
holdcol1);
- es->winleft = holdcur1;
+ vs->winleft = holdcur1;
}
static int
@@ -5161,13 +5176,13 @@
int moreright;
col = 0;
- cur = es->winleft;
+ cur = vs->winleft;
moreright = 0;
twb1 = wb1;
- while (col < winwidth && cur < es->linelen) {
- if (cur == es->cursor && leftside)
+ while (col < winwidth && cur < vs->linelen) {
+ if (cur == vs->cursor && leftside)
ncol = col + pwidth;
- if ((ch = es->cbuf[cur]) == '\t')
+ if ((ch = vs->cbuf[cur]) == '\t')
do {
*twb1++ = ' ';
} while (++col < winwidth && (col & 7) != 0);
@@ -5183,11 +5198,11 @@
col++;
}
}
- if (cur == es->cursor && !leftside)
+ if (cur == vs->cursor && !leftside)
ncol = col + pwidth - 1;
cur++;
}
- if (cur == es->cursor)
+ if (cur == vs->cursor)
ncol = col + pwidth;
if (col < winwidth) {
while (col < winwidth) {
@@ -5213,13 +5228,13 @@
twb2++;
col++;
}
- if (es->winleft > 0 && moreright)
+ if (vs->winleft > 0 && moreright)
/*
* POSIX says to use * for this but that is a globbing
* character and may confuse people; + is more innocuous
*/
mc = '+';
- else if (es->winleft > 0)
+ else if (vs->winleft > 0)
mc = '<';
else if (moreright)
mc = '>';
@@ -5267,7 +5282,7 @@
/* Undo previous expansion */
if (cmd == 0 && expanded == EXPAND && buf) {
- restore_edstate(es, buf);
+ restore_edstate(vs, buf);
buf = 0;
expanded = NONE;
return (0);
@@ -5278,17 +5293,17 @@
}
i = XCF_COMMAND_FILE | XCF_FULLPATH;
- nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor,
+ nwords = x_cf_glob(&i, vs->cbuf, vs->linelen, vs->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
return (-1);
}
- buf = save_edstate(es);
+ buf = save_edstate(vs);
expanded = EXPAND;
del_range(start, end);
- es->cursor = start;
+ vs->cursor = start;
i = 0;
while (i < nwords) {
if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
@@ -5302,7 +5317,7 @@
}
i = buf->cursor - end;
if (rval == 0 && i > 0)
- es->cursor += i;
+ vs->cursor += i;
modified = 1;
hnum = hlast;
insert = INSERT;
@@ -5328,7 +5343,7 @@
return (0);
}
if (cmd == 0 && expanded == PRINT && buf) {
- restore_edstate(es, buf);
+ restore_edstate(vs, buf);
buf = 0;
expanded = NONE;
return (0);
@@ -5345,7 +5360,7 @@
flags = XCF_COMMAND_FILE;
if (count)
flags |= XCF_FULLPATH;
- nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor,
+ nwords = x_cf_glob(&flags, vs->cbuf, vs->linelen, vs->cursor,
&start, &end, &words);
if (nwords == 0) {
vi_error();
@@ -5390,9 +5405,9 @@
is_unique = nwords == 1;
}
- buf = save_edstate(es);
+ buf = save_edstate(vs);
del_range(start, end);
- es->cursor = start;
+ vs->cursor = start;
/*
* escape all shell-sensitive characters and put the result into
@@ -5544,12 +5559,13 @@
if (!kshsetjmp(e->jbuf)) {
char *wds = alloc(len + 3, ATEMP);
- wds[0] = FUNSUB;
+ wds[0] = FUNASUB;
memcpy(wds + 1, cmd, len);
wds[len + 1] = '\0';
wds[len + 2] = EOS;
cp = evalstr(wds, DOSCALAR);
+ afree(wds, ATEMP);
strdupx(cp, cp, AEDIT);
} else
cp = NULL;
diff --git a/src/eval.c b/src/eval.c
index a9980c7..23894d6 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.194 2016/11/11 23:31:34 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.201 2017/04/06 01:59:54 tg Exp $");
/*
* string expansion
@@ -301,25 +301,36 @@
word = IFS_WORD;
quote = st->quotew;
continue;
+ case COMASUB:
case COMSUB:
+ case FUNASUB:
case FUNSUB:
case VALSUB:
tilde_ok = 0;
if (f & DONTRUNCOMMAND) {
word = IFS_WORD;
*dp++ = '$';
- *dp++ = c == COMSUB ? '(' : '{';
- if (c != COMSUB)
- *dp++ = c == FUNSUB ? ' ' : '|';
+ switch (c) {
+ case COMASUB:
+ case COMSUB:
+ *dp++ = '(';
+ c = ')';
+ break;
+ case FUNASUB:
+ case FUNSUB:
+ case VALSUB:
+ *dp++ = '{';
+ *dp++ = c == VALSUB ? '|' : ' ';
+ c = '}';
+ break;
+ }
while (*sp != '\0') {
Xcheck(ds, dp);
*dp++ = *sp++;
}
- if (c != COMSUB) {
+ if (c == '}')
*dp++ = ';';
- *dp++ = '}';
- } else
- *dp++ = ')';
+ *dp++ = c;
} else {
type = comsub(&x, sp, c);
if (type != XBASE && (f & DOBLANK))
@@ -625,13 +636,12 @@
break;
case '=':
/*
- * Enabling tilde expansion
- * after :s here is
- * non-standard ksh, but is
- * consistent with rules for
- * other assignments. Not
- * sure what POSIX thinks of
- * this.
+ * Tilde expansion for string
+ * variables in POSIX mode is
+ * governed by Austinbug 351.
+ * In non-POSIX mode historic
+ * ksh behaviour (enable it!)
+ * us followed.
* Not doing tilde expansion
* for integer variables is a
* non-POSIX thing - makes
@@ -640,7 +650,7 @@
*/
if (!(x.var->flag & INTEGER))
f |= DOASNTILDE | DOTILDE;
- f |= DOTEMP;
+ f |= DOTEMP | DOSCALAR;
/*
* These will be done after the
* value has been assigned.
@@ -880,10 +890,30 @@
c = '\n';
--newlines;
} else {
- while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
+ while ((c = shf_getc(x.u.shf)) == 0 ||
+#ifdef MKSH_WITH_TEXTMODE
+ c == '\r' ||
+#endif
+ c == '\n') {
+#ifdef MKSH_WITH_TEXTMODE
+ if (c == '\r') {
+ c = shf_getc(x.u.shf);
+ switch (c) {
+ case '\n':
+ break;
+ default:
+ shf_ungetc(c, x.u.shf);
+ /* FALLTHROUGH */
+ case -1:
+ c = '\r';
+ break;
+ }
+ }
+#endif
if (c == '\n')
/* save newlines */
newlines++;
+ }
if (newlines && c != -1) {
shf_ungetc(c, x.u.shf);
c = '\n';
@@ -1197,7 +1227,7 @@
} else if (ctype(c, C_SUBOP1)) {
slen += 2;
stype |= c;
- } else if (ctype(c, C_SUBOP2)) {
+ } else if (ksh_issubop2(c)) {
/* Note: ksh88 allows :%, :%%, etc */
slen += 2;
stype = c;
@@ -1207,12 +1237,16 @@
}
} else if (c == '@') {
/* @x where x is command char */
- slen += 2;
- stype |= 0x100;
- if (word[slen] == CHAR) {
- stype |= word[slen + 1];
- slen += 2;
+ switch (c = word[slen + 2] == CHAR ? word[slen + 3] : 0) {
+ case '#':
+ case '/':
+ case 'Q':
+ break;
+ default:
+ return (-1);
}
+ stype |= 0x100 | c;
+ slen += 4;
} else if (stype)
/* : is not ok */
return (-1);
@@ -1301,7 +1335,7 @@
c = stype & 0x7F;
/* test the compiler's code generator */
- if (((stype < 0x100) && (ctype(c, C_SUBOP2) ||
+ if (((stype < 0x100) && (ksh_issubop2(c) ||
(((stype & 0x80) ? *xp->str == '\0' : xp->str == null) &&
(state != XARG || (ifs0 || xp->split ?
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
@@ -1311,7 +1345,7 @@
/* expand word instead of variable value */
state = XBASE;
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
- (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
+ (ksh_issubop2(c) || (state != XBASE && c != '+')))
errorf(Tf_parm, sp);
*stypep = stype;
*slenp = slen;
@@ -1322,17 +1356,28 @@
* Run the command in $(...) and read its output.
*/
static int
-comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
+comsub(Expand *xp, const char *cp, int fn)
{
Source *s, *sold;
struct op *t;
struct shf *shf;
+ bool doalias = false;
uint8_t old_utfmode = UTFMODE;
+ switch (fn) {
+ case COMASUB:
+ fn = COMSUB;
+ if (0)
+ /* FALLTHROUGH */
+ case FUNASUB:
+ fn = FUNSUB;
+ doalias = true;
+ }
+
s = pushs(SSTRING, ATEMP);
s->start = s->str = cp;
sold = source;
- t = compile(s, true);
+ t = compile(s, true, doalias);
afree(s, ATEMP);
source = sold;
@@ -1713,7 +1758,7 @@
Xinit(ts, tp, 16, ATEMP);
/* : only for DOASNTILDE form */
- while (p[0] == CHAR && !mksh_cdirsep(p[1]) &&
+ while (p[0] == CHAR && /* not cdirsep */ p[1] != '/' &&
(!isassign || p[1] != ':')) {
Xcheck(ts, tp);
*tp++ = p[1];
diff --git a/src/exec.c b/src/exec.c
index 882b628..6307bce 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.186 2016/11/11 23:31:34 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.196 2017/04/12 16:46:21 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@@ -376,9 +376,8 @@
if (t->right == NULL)
/* should be error */
break;
- rv = execute(t->left, XERROK, NULL) == 0 ?
- execute(t->right->left, flags & XERROK, xerrok) :
- execute(t->right->right, flags & XERROK, xerrok);
+ rv = execute(execute(t->left, XERROK, NULL) == 0 ?
+ t->right->left : t->right->right, flags & XERROK, xerrok);
break;
case TCASE:
@@ -806,7 +805,7 @@
/* NOTREACHED */
default:
quitenv(NULL);
- internal_errorf(Tf_sd, "CFUNC", i);
+ internal_errorf(Tunexpected_type, Tunwind, Tfunction, i);
}
break;
}
@@ -889,6 +888,9 @@
unsigned short m;
ssize_t n;
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ setmode(fd, O_TEXT);
+#endif
/* read first couple of octets from file */
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
@@ -944,6 +946,17 @@
if (*cp)
*tp->args-- = (char *)cp;
}
+#ifdef __OS2__
+ /*
+ * Search shell/interpreter name without directory in PATH
+ * if specified path does not exist
+ */
+ if (mksh_vdirsep(sh) && !search_path(sh, path, X_OK, NULL)) {
+ cp = search_path(_getname(sh), path, X_OK, NULL);
+ if (cp)
+ sh = cp;
+ }
+#endif
goto nomagic;
noshebang:
m = buf[0] << 8 | buf[1];
@@ -964,6 +977,19 @@
buf[4] == 'Z') || (m == /* 7zip */ 0x377A) ||
(m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D))
errorf("%s: not executable: magic %04X", tp->str, m);
+#ifdef __OS2__
+ cp = _getext(tp->str);
+ if (cp && (!stricmp(cp, ".cmd") || !stricmp(cp, ".bat"))) {
+ /* execute .cmd and .bat with OS2_SHELL, usually CMD.EXE */
+ sh = str_val(global("OS2_SHELL"));
+ *tp->args-- = "/c";
+ /* convert slahes to backslashes */
+ for (cp = tp->str; *cp; cp++) {
+ if (*cp == '/')
+ *cp = '\\';
+ }
+ }
+#endif
nomagic:
;
}
@@ -978,13 +1004,17 @@
errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno));
}
+/* actual 'builtin' built-in utility call is handled in comexec() */
int
-shcomexec(const char **wp)
+c_builtin(const char **wp)
{
- struct tbl *tp;
+ return (call_builtin(get_builtin(*wp), wp, Tbuiltin, false));
+}
- tp = ktsearch(&builtins, *wp, hash(*wp));
- return (call_builtin(tp, wp, "shcomexec", false));
+struct tbl *
+get_builtin(const char *s)
+{
+ return (s && *s ? ktsearch(&builtins, s, hash(s)) : NULL);
}
/*
@@ -1090,6 +1120,14 @@
/* external utility overrides built-in utility, with flags */
flag |= LOW_BI;
break;
+ case '-':
+ /* is declaration utility if argv[1] is one (POSIX: command) */
+ flag |= DECL_FWDR;
+ break;
+ case '^':
+ /* is declaration utility (POSIX: export, readonly) */
+ flag |= DECL_UTIL;
+ break;
default:
goto flags_seen;
}
@@ -1123,7 +1161,11 @@
char *fpath;
union mksh_cchack npath;
- if (mksh_vdirsep(name)) {
+ if (mksh_vdirsep(name)
+#ifdef MKSH_DOSPATH
+ && (strcmp(name, T_builtin) != 0)
+#endif
+ ) {
insert = 0;
/* prevent FPATH search below */
flags &= ~FC_FUNC;
@@ -1242,7 +1284,7 @@
}
#ifdef __OS2__
/* treat all files as executable on OS/2 */
- sb.st_mode &= S_IXUSR | S_IXGRP | S_IXOTH;
+ sb.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
#endif
if (mode == X_OK && (!S_ISREG(sb.st_mode) ||
!(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
@@ -1251,6 +1293,13 @@
return (0);
}
+#ifdef __OS2__
+/* check if path is something we want to find, adding executable extensions */
+#define search_access(fn, mode) access_ex((search_access), (fn), (mode))
+#else
+#define search_access(fn, mode) (search_access)((fn), (mode))
+#endif
+
/*
* search for command with PATH
*/
@@ -1272,7 +1321,11 @@
search_path_ok:
if (errnop)
*errnop = 0;
+#ifndef __OS2__
return (name);
+#else
+ return (real_exec_name(name));
+#endif
}
goto search_path_err;
}
@@ -1289,6 +1342,10 @@
XcheckN(xs, xp, p - sp);
memcpy(xp, sp, p - sp);
xp += p - sp;
+#ifdef __OS2__
+ if (xp > Xstring(xs, xp) && mksh_cdirsep(xp[-1]))
+ xp--;
+#endif
*xp++ = '/';
}
sp = p;
@@ -1319,9 +1376,7 @@
if (!tp)
internal_errorf(Tf_sD_s, where, wp[0]);
builtin_argv0 = wp[0];
- builtin_spec = tobool(!resetspec &&
- /*XXX odd use of KEEPASN */
- ((tp->flag & SPEC_BI) || (Flag(FPOSIX) && (tp->flag & KEEPASN))));
+ builtin_spec = tobool(!resetspec && (tp->flag & SPEC_BI));
shf_reopen(1, SHF_WR, shl_stdout);
shl_stdout_ok = true;
ksh_getopt_reset(&builtin_opt, GF_ERROR);
@@ -1450,8 +1505,11 @@
/* herein() may already have printed message */
if (u == -1) {
u = errno;
- warningf(true, Tf_cant,
+ warningf(true, Tf_cant_ss_s,
+#if 0
+ /* can't happen */
iotype == IODUP ? "dup" :
+#endif
(iotype == IOREAD || iotype == IOHERE) ?
Topen : Tcreate, cp, cstrerror(u));
}
diff --git a/src/expr.c b/src/expr.c
index f259b0c..124dc17 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2016
+ * 2011, 2012, 2013, 2014, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.90 2016/11/07 16:58:48 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.93 2017/04/02 16:47:41 tg Exp $");
#define EXPRTOK_DEFNS
#include "exprtok.h"
@@ -203,7 +203,7 @@
case ET_BADLIT:
warningf(true, Tf_sD_s_qs, es->expression,
- "bad number", str);
+ Tbadnum, str);
break;
case ET_RECURSIVE:
@@ -572,8 +572,9 @@
if (c == '\0')
es->tok = END;
else if (ksh_isalphx(c)) {
- for (; ksh_isalnux(c); c = *cp)
- cp++;
+ do {
+ c = *++cp;
+ } while (ksh_isalnux(c));
if (c == '[') {
size_t len;
@@ -856,6 +857,9 @@
int
ksh_access(const char *fn, int mode)
{
+#ifdef __OS2__
+ return (access_ex(access, fn, mode));
+#else
int rv;
struct stat sb;
@@ -865,6 +869,7 @@
rv = -1;
return (rv);
+#endif
}
#ifndef MIRBSD_BOOTFLOPPY
diff --git a/src/funcs.c b/src/funcs.c
index b76fbfc..930462d 100644
--- a/src/funcs.c
+++ b/src/funcs.c
@@ -5,7 +5,7 @@
/*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- * 2010, 2011, 2012, 2013, 2014, 2015, 2016
+ * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -38,7 +38,7 @@
#endif
#endif
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.319 2016/11/11 23:48:29 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.340 2017/04/12 17:46:29 tg Exp $");
#if HAVE_KILLPG
/*
@@ -73,7 +73,7 @@
int rv;
if (!(rv = getn(as, ai)))
- bi_errorf(Tf_sD_s, as, "bad number");
+ bi_errorf(Tf_sD_s, Tbadnum, as);
return (rv);
}
@@ -92,6 +92,7 @@
/*
* A leading = means assignments before command are kept.
* A leading * means a POSIX special builtin.
+ * A leading ^ means declaration utility, - forwarder.
*/
const struct builtin mkshbuiltins[] = {
{Tsgdot, c_dot},
@@ -99,33 +100,34 @@
{Tbracket, c_test},
/* no =: AT&T manual wrong */
{Talias, c_alias},
- {"*=break", c_brkcont},
- {Tgbuiltin, c_builtin},
+ {Tsgbreak, c_brkcont},
+ {T__builtin, c_builtin},
+ {Tbuiltin, c_builtin},
#if !defined(__ANDROID__)
{Tbcat, c_cat},
#endif
{Tcd, c_cd},
/* dash compatibility hack */
{"chdir", c_cd},
- {Tcommand, c_command},
- {"*=continue", c_brkcont},
+ {T_command, c_command},
+ {Tsgcontinue, c_brkcont},
{"echo", c_print},
{"*=eval", c_eval},
{"*=exec", c_exec},
{"*=exit", c_exitreturn},
- {Tsgexport, c_typeset},
+ {Tdsgexport, c_typeset},
{Tfalse, c_false},
{"fc", c_fc},
{Tgetopts, c_getopts},
- {"=global", c_typeset},
+ /* deprecated, replaced by typeset -g */
+ {"^=global", c_typeset},
{Tjobs, c_jobs},
{"kill", c_kill},
{"let", c_let},
- {"let]", c_let},
{"print", c_print},
{"pwd", c_pwd},
{Tread, c_read},
- {Tsgreadonly, c_typeset},
+ {Tdsgreadonly, c_typeset},
#if !defined(__ANDROID__)
{"!realpath", c_realpath},
#endif
@@ -133,7 +135,7 @@
{"*=return", c_exitreturn},
{Tsgset, c_set},
{"*=shift", c_shift},
- {"=source", c_dot},
+ {Tgsource, c_dot},
#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
{Tsuspend, c_suspend},
#endif
@@ -141,12 +143,12 @@
{"*=times", c_times},
{"*=trap", c_trap},
{Ttrue, c_true},
- {Tgtypeset, c_typeset},
+ {Tdgtypeset, c_typeset},
{"ulimit", c_ulimit},
{"umask", c_umask},
{Tunalias, c_unalias},
{"*=unset", c_unset},
- {"=wait", c_wait},
+ {"wait", c_wait},
{"whence", c_whence},
#ifndef MKSH_UNEMPLOYED
{Tbg, c_fgbg},
@@ -193,8 +195,8 @@
{"-f", TO_FILREG },
{"-G", TO_FILGID },
{"-g", TO_FILSETG },
- {"-h", TO_FILSYM },
{"-H", TO_FILCDF },
+ {"-h", TO_FILSYM },
{"-k", TO_FILSTCK },
{"-L", TO_FILSYM },
{"-n", TO_STNZE },
@@ -202,10 +204,11 @@
{"-o", TO_OPTION },
{"-p", TO_FILFIFO },
{"-r", TO_FILRD },
- {"-s", TO_FILGZ },
{"-S", TO_FILSOCK },
+ {"-s", TO_FILGZ },
{"-t", TO_FILTT },
{"-u", TO_FILSETU },
+ {"-v", TO_ISSET },
{"-w", TO_FILWR },
{"-x", TO_FILEX },
{"-z", TO_STZER },
@@ -313,8 +316,6 @@
bool hist;
/* print words as wide characters? */
bool chars;
- /* print a "--" argument? */
- bool pminusminus;
/* writing to a coprocess (SIGPIPE blocked)? */
bool coproc;
bool copipe;
@@ -325,47 +326,39 @@
po.ws = ' ';
po.ls = '\n';
po.nl = true;
- po.exp = true;
if (wp[0][0] == 'e') {
/* "echo" builtin */
- ++wp;
-#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
- if (Flag(FSH)) {
- /*
- * MidnightBSD /bin/sh needs a BSD echo, that is,
- * one that supports -e but does not enable it by
- * default
- */
- po.exp = false;
- }
-#endif
if (Flag(FPOSIX) ||
#ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
Flag(FSH) ||
#endif
Flag(FAS_BUILTIN)) {
- /* Debian Policy 10.4 compliant "echo" builtin */
+ /* BSD "echo" cmd, Debian Policy 10.4 compliant */
+ ++wp;
+ bsd_echo:
if (*wp && !strcmp(*wp, "-n")) {
- /* recognise "-n" only as the first arg */
po.nl = false;
++wp;
}
- /* print everything as-is */
po.exp = false;
} else {
- bool new_exp = po.exp, new_nl = po.nl;
+ bool new_exp, new_nl = true;
- /**
- * a compromise between sysV and BSD echo commands:
- * escape sequences are enabled by default, and -n,
- * -e and -E are recognised if they appear in argu-
- * ments with no illegal options (ie, echo -nq will
- * print -nq).
- * Different from sysV echo since options are reco-
- * gnised, different from BSD echo since escape se-
- * quences are enabled by default.
+ /*-
+ * compromise between various historic echos: only
+ * recognise -Een if they appear in arguments with
+ * no illegal options; e.g. echo -nq outputs '-nq'
*/
+#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
+ /* MidnightBSD /bin/sh needs -e supported but off */
+ if (Flag(FSH))
+ new_exp = false;
+ else
+#endif
+ /* otherwise compromise on -e enabled by default */
+ new_exp = true;
+ goto print_tradparse_beg;
print_tradparse_arg:
if ((s = *wp) && *s++ == '-' && *s) {
@@ -381,6 +374,7 @@
new_nl = false;
goto print_tradparse_ch;
case '\0':
+ print_tradparse_beg:
po.exp = new_exp;
po.nl = new_nl;
++wp;
@@ -390,10 +384,10 @@
}
} else {
/* "print" builtin */
- const char *opts = "AclNnpRrsu,";
+ const char *opts = "AcelNnpRrsu,";
const char *emsg;
- po.pminusminus = false;
+ po.exp = true;
while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
switch (c) {
@@ -423,11 +417,9 @@
}
break;
case 'R':
- /* fake BSD echo command */
- po.pminusminus = true;
- po.exp = false;
- opts = "en";
- break;
+ /* fake BSD echo but don't reset other flags */
+ wp += builtin_opt.optind;
+ goto bsd_echo;
case 'r':
po.exp = false;
break;
@@ -451,8 +443,7 @@
if (wp[builtin_opt.optind] &&
ksh_isdash(wp[builtin_opt.optind]))
builtin_opt.optind++;
- } else if (po.pminusminus)
- builtin_opt.optind--;
+ }
wp += builtin_opt.optind;
}
@@ -747,7 +738,7 @@
break;
#ifndef MKSH_SMALL
default:
- bi_errorf("%s is of unknown type %d", id, tp->type);
+ bi_errorf(Tunexpected_type, id, Tcommand, tp->type);
return (1);
#endif
}
@@ -757,380 +748,15 @@
return (rv);
}
-/* 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,
- bool);
-int
-c_typeset(const char **wp)
+bool
+valid_alias_name(const char *cp)
{
- struct tbl *vp, **p;
- uint32_t fset = 0, fclr = 0, flag;
- int thing = 0, field = 0, base = 0, i;
- struct block *l;
- const char *opts;
- const char *fieldstr = NULL, *basestr = NULL;
- bool localv = false, func = false, pflag = false, istset = true;
- enum namerefflag new_refflag = SRF_NOP;
-
- switch (**wp) {
-
- /* export */
- case 'e':
- fset |= EXPORT;
- istset = false;
- break;
-
- /* readonly */
- case 'r':
- fset |= RDONLY;
- istset = false;
- break;
-
- /* set */
- case 's':
- /* called with 'typeset -' */
- break;
-
- /* typeset */
- case 't':
- localv = true;
- break;
- }
-
- /* see comment below regarding possible opions */
- opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
-
- builtin_opt.flags |= GF_PLUSOPT;
- /*
- * AT&T ksh seems to have 0-9 as options which are multiplied
- * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
- * sets right justify in a field of 12). This allows options
- * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
- * does not allow the number to be specified as a separate argument
- * Here, the number must follow the RLZi option, but is optional
- * (see the # kludge in ksh_getopt()).
- */
- while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
- flag = 0;
- switch (i) {
- case 'L':
- flag = LJUST;
- fieldstr = builtin_opt.optarg;
- break;
- case 'R':
- flag = RJUST;
- fieldstr = builtin_opt.optarg;
- break;
- case 'U':
- /*
- * AT&T ksh uses u, but this conflicts with
- * upper/lower case. If this option is changed,
- * need to change the -U below as well
- */
- flag = INT_U;
- break;
- case 'Z':
- flag = ZEROFIL;
- fieldstr = builtin_opt.optarg;
- break;
- case 'a':
- /*
- * this is supposed to set (-a) or unset (+a) the
- * indexed array attribute; it does nothing on an
- * existing regular string or indexed array though
- */
- break;
- case 'f':
- func = true;
- break;
- case 'i':
- flag = INTEGER;
- basestr = builtin_opt.optarg;
- break;
- case 'l':
- flag = LCASEV;
- break;
- case 'n':
- new_refflag = (builtin_opt.info & GI_PLUS) ?
- SRF_DISABLE : SRF_ENABLE;
- break;
- /* export, readonly: POSIX -p flag */
- case 'p':
- /* typeset: show values as well */
- pflag = true;
- if (istset)
- continue;
- break;
- case 'r':
- flag = RDONLY;
- break;
- case 't':
- flag = TRACE;
- break;
- case 'u':
- /* upper case / autoload */
- flag = UCASEV_AL;
- break;
- case 'x':
- flag = EXPORT;
- break;
- case '?':
- return (1);
- }
- if (builtin_opt.info & GI_PLUS) {
- fclr |= flag;
- fset &= ~flag;
- thing = '+';
- } else {
- fset |= flag;
- fclr &= ~flag;
- thing = '-';
- }
- }
-
- if (fieldstr && !bi_getn(fieldstr, &field))
- return (1);
- if (basestr) {
- if (!getn(basestr, &base)) {
- bi_errorf(Tf_sD_s, "bad integer base", basestr);
- return (1);
- }
- if (base < 1 || base > 36)
- base = 10;
- }
-
- if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
- (wp[builtin_opt.optind][0] == '-' ||
- wp[builtin_opt.optind][0] == '+') &&
- wp[builtin_opt.optind][1] == '\0') {
- thing = wp[builtin_opt.optind][0];
- builtin_opt.optind++;
- }
-
- if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
- new_refflag != SRF_NOP)) {
- bi_errorf("only -t, -u and -x options may be used with -f");
- return (1);
- }
- if (wp[builtin_opt.optind]) {
- /*
- * Take care of exclusions.
- * At this point, flags in fset are cleared in fclr and vice
- * versa. This property should be preserved.
- */
- if (fset & LCASEV)
- /* LCASEV has priority over UCASEV_AL */
- fset &= ~UCASEV_AL;
- if (fset & LJUST)
- /* LJUST has priority over RJUST */
- fset &= ~RJUST;
- if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
- /* -Z implies -ZR */
- fset |= RJUST;
- fclr &= ~RJUST;
- }
- /*
- * Setting these attributes clears the others, unless they
- * are also set in this command
- */
- if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
- INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
- fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
- LCASEV | INTEGER | INT_U | INT_L);
- }
- if (new_refflag != SRF_NOP) {
- fclr &= ~(ARRAY | ASSOC);
- fset &= ~(ARRAY | ASSOC);
- fclr |= EXPORT;
- fset |= ASSOC;
- if (new_refflag == SRF_DISABLE)
- fclr |= ASSOC;
- }
-
- /* set variables and attributes */
- if (wp[builtin_opt.optind] &&
- /* not "typeset -p varname" */
- !(!func && pflag && !(fset | fclr))) {
- int rv = 0;
- struct tbl *f;
-
- if (localv && !func)
- fset |= LOCAL;
- for (i = builtin_opt.optind; wp[i]; i++) {
- if (func) {
- f = findfunc(wp[i], hash(wp[i]),
- tobool(fset & UCASEV_AL));
- if (!f) {
- /* AT&T ksh does ++rv: bogus */
- rv = 1;
- continue;
- }
- if (fset | fclr) {
- f->flag |= fset;
- f->flag &= ~fclr;
- } else {
- fpFUNCTf(shl_stdout, 0,
- tobool(f->flag & FKSH),
- wp[i], f->val.t);
- shf_putc('\n', shl_stdout);
- }
- } else if (!typeset(wp[i], fset, fclr, field, base)) {
- bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
- return (1);
- }
- }
- return (rv);
- }
-
- /* list variables and attributes */
-
- /* no difference at this point.. */
- flag = fset | fclr;
- if (func) {
- for (l = e->loc; l; l = l->next) {
- for (p = ktsort(&l->funs); (vp = *p++); ) {
- if (flag && (vp->flag & flag) == 0)
- continue;
- if (thing == '-')
- fpFUNCTf(shl_stdout, 0,
- tobool(vp->flag & FKSH),
- vp->name, vp->val.t);
- else
- shf_puts(vp->name, shl_stdout);
- shf_putc('\n', shl_stdout);
- }
- }
- } else if (wp[builtin_opt.optind]) {
- for (i = builtin_opt.optind; wp[i]; i++) {
- varsearch(e->loc, &vp, wp[i], hash(wp[i]));
- c_typeset_vardump(vp, flag, thing, pflag, istset);
- }
- } else
- c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
- return (0);
-}
-
-static void
-c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
- bool pflag, bool istset)
-{
- struct tbl **blockvars, *vp;
-
- if (l->next)
- c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
- blockvars = ktsort(&l->vars);
- while ((vp = *blockvars++))
- c_typeset_vardump(vp, flag, thing, pflag, istset);
- /*XXX doesn’t this leak? */
-}
-
-static void
-c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
- bool istset)
-{
- struct tbl *tvp;
- int any_set = 0;
- char *s;
-
- if (!vp)
- return;
-
- /*
- * See if the parameter is set (for arrays, if any
- * element is set).
- */
- for (tvp = vp; tvp; tvp = tvp->u.array)
- if (tvp->flag & ISSET) {
- any_set = 1;
- break;
- }
-
- /*
- * Check attributes - note that all array elements
- * have (should have?) the same attributes, so checking
- * the first is sufficient.
- *
- * Report an unset param only if the user has
- * explicitly given it some attribute (like export);
- * otherwise, after "echo $FOO", we would report FOO...
- */
- if (!any_set && !(vp->flag & USERATTRIB))
- return;
- if (flag && (vp->flag & flag) == 0)
- return;
- if (!(vp->flag & ARRAY))
- /* optimise later conditionals */
- any_set = 0;
- do {
- /*
- * Ignore array elements that aren't set unless there
- * are no set elements, in which case the first is
- * reported on
- */
- if (any_set && !(vp->flag & ISSET))
- continue;
- /* no arguments */
- if (!thing && !flag) {
- if (any_set == 1) {
- shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
- any_set = 2;
- }
- /*
- * AT&T ksh prints things like export, integer,
- * leftadj, zerofill, etc., but POSIX says must
- * be suitable for re-entry...
- */
- shprintf(Tf_s_s, Ttypeset, "");
- if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
- shprintf(Tf__c_, 'n');
- if ((vp->flag & INTEGER))
- shprintf(Tf__c_, 'i');
- if ((vp->flag & EXPORT))
- shprintf(Tf__c_, 'x');
- if ((vp->flag & RDONLY))
- shprintf(Tf__c_, 'r');
- if ((vp->flag & TRACE))
- 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(Tf__c_, 'Z');
- if ((vp->flag & LCASEV))
- shprintf(Tf__c_, 'l');
- if ((vp->flag & UCASEV_AL))
- shprintf(Tf__c_, 'u');
- if ((vp->flag & INT_U))
- shprintf(Tf__c_, 'U');
- } else if (pflag) {
- shprintf(Tf_s_s, istset ? Ttypeset :
- (flag & EXPORT) ? Texport : Treadonly, "");
- }
- if (any_set)
- shprintf("%s[%lu]", vp->name, arrayindex(vp));
+ while (*cp)
+ if (!ksh_isalias(*cp))
+ return (false);
else
- shf_puts(vp->name, shl_stdout);
- if ((!thing && !flag && pflag) ||
- (thing == '-' && (vp->flag & ISSET))) {
- s = str_val(vp);
- shf_putc('=', shl_stdout);
- /* AT&T ksh can't have justified integers... */
- if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
- shf_puts(s, shl_stdout);
- else
- print_value_quoted(shl_stdout, s);
- }
- shf_putc('\n', shl_stdout);
-
- /*
- * Only report first 'element' of an array with
- * no set elements.
- */
- if (!any_set)
- return;
- } while ((vp = vp->u.array));
+ ++cp;
+ return (true);
}
int
@@ -1231,6 +857,11 @@
strndupx(xalias, alias, val++ - alias, ATEMP);
alias = xalias;
}
+ if (!valid_alias_name(alias) || *alias == '-') {
+ bi_errorf(Tinvname, alias, Talias);
+ afree(xalias, ATEMP);
+ return (1);
+ }
h = hash(alias);
if (val == NULL && !tflag && !xflag) {
ap = ktsearch(t, alias, h);
@@ -1639,7 +1270,7 @@
if (user_opt.optarg == NULL)
unset(voptarg, 1);
else
- /* This can't fail (have cleared readonly/integer) */
+ /* this can't fail (haing cleared readonly/integer) */
setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
rv = 0;
@@ -1737,7 +1368,7 @@
/* nothing to do */
return (0);
} else if (n < 0) {
- bi_errorf(Tf_sD_s, arg, "bad number");
+ bi_errorf(Tf_sD_s, Tbadnum, arg);
return (1);
}
if (l->argc < n) {
@@ -1798,7 +1429,7 @@
++cp;
}
if (*cp) {
- bi_errorf("bad number");
+ bi_errorf(Tbadnum);
return (1);
}
} else {
@@ -1982,6 +1613,10 @@
#else
#define c_read_opts "Aad:N:n:prsu,"
#endif
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ int saved_mode;
+ int saved_errno;
+#endif
while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
switch (c) {
@@ -2110,7 +1745,15 @@
}
#endif
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ saved_mode = setmode(fd, O_TEXT);
+#endif
if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) {
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ saved_errno = errno;
+ setmode(fd, saved_mode);
+ errno = saved_errno;
+#endif
if (errno == EINTR) {
/* check whether the signal would normally kill */
if (!fatal_trap_check()) {
@@ -2125,6 +1768,9 @@
rv = 2;
goto c_read_out;
}
+#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE)
+ setmode(fd, saved_mode);
+#endif
switch (readmode) {
case READALL:
@@ -2386,6 +2032,7 @@
return (1);
s = pushs(SWORDS, ATEMP);
s->u.strv = wp + builtin_opt.optind;
+ s->line = current_lineno;
/*-
* The following code handles the case where the command is
@@ -2423,7 +2070,7 @@
savef = Flag(FERREXIT);
Flag(FERREXIT) |= 0x80;
- rv = shell(s, false);
+ rv = shell(s, 2);
Flag(FERREXIT) = savef;
source = saves;
afree(s, ATEMP);
@@ -2559,7 +2206,7 @@
* scripts, but don't generate an error (ie, keep going).
*/
if ((unsigned int)n == quit) {
- warningf(true, "%s: can't %s", wp[0], wp[0]);
+ warningf(true, Tf_cant_s, wp[0], wp[0]);
return (0);
}
/*
@@ -2827,7 +2474,6 @@
for (i = 0; i < NUFILE; i++) {
if (e->savefd[i] > 0)
close(e->savefd[i]);
-#ifndef MKSH_LEGACY_MODE
/*
* keep all file descriptors > 2 private for ksh,
* but not for POSIX or legacy/kludge sh
@@ -2835,14 +2481,13 @@
if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
e->savefd[i])
fcntl(i, F_SETFD, FD_CLOEXEC);
-#endif
}
e->savefd = NULL;
}
return (0);
}
-#if HAVE_MKNOD
+#if HAVE_MKNOD && !defined(__OS2__)
int
c_mknod(const char **wp)
{
@@ -2939,14 +2584,13 @@
| "(" oexpr ")"
;
- unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
- "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
- "-L"|"-h"|"-S"|"-H";
+ unary-operator ::= "-a"|"-b"|"-c"|"-d"|"-e"|"-f"|"-G"|"-g"|"-H"|"-h"|
+ "-k"|"-L"|"-n"|"-O"|"-o"|"-p"|"-r"|"-S"|"-s"|"-t"|
+ "-u"|"-v"|"-w"|"-x"|"-z";
- binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
- "-nt"|"-ot"|"-ef"|
- "<"|">" # rules used for [[ ... ]] expressions
- ;
+ binary-operator ::= "="|"=="|"!="|"<"|">"|"-eq"|"-ne"|"-gt"|"-ge"|
+ "-lt"|"-le"|"-ef"|"-nt"|"-ot";
+
operand ::= <anything>
*/
@@ -3099,6 +2743,14 @@
return (TO_NONOP);
}
+#ifdef __OS2__
+#define test_access(name, mode) access_ex(access, (name), (mode))
+#define test_stat(name, buffer) stat_ex((name), (buffer))
+#else
+#define test_access(name, mode) access((name), (mode))
+#define test_stat(name, buffer) stat((name), (buffer))
+#endif
+
int
test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
bool do_eval)
@@ -3107,6 +2759,7 @@
size_t k;
struct stat b1, b2;
mksh_ari_t v1, v2;
+ struct tbl *vp;
if (!do_eval)
return (0);
@@ -3153,6 +2806,10 @@
case TO_STZER:
return (*opnd1 == '\0');
+ /* -v */
+ case TO_ISSET:
+ return ((vp = isglobal(opnd1, false)) && (vp->flag & ISSET));
+
/* -o */
case TO_OPTION:
if ((i = *opnd1) == '!' || i == '?')
@@ -3164,12 +2821,12 @@
/* -r */
case TO_FILRD:
/* LINTED use of access */
- return (access(opnd1, R_OK) == 0);
+ return (test_access(opnd1, R_OK) == 0);
/* -w */
case TO_FILWR:
/* LINTED use of access */
- return (access(opnd1, W_OK) == 0);
+ return (test_access(opnd1, W_OK) == 0);
/* -x */
case TO_FILEX:
@@ -3179,11 +2836,11 @@
case TO_FILAXST:
/* -e */
case TO_FILEXST:
- return (stat(opnd1, &b1) == 0);
+ return (test_stat(opnd1, &b1) == 0);
- /* -r */
+ /* -f */
case TO_FILREG:
- return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
+ return (test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
/* -d */
case TO_FILID:
@@ -3282,7 +2939,7 @@
* Binary Operators
*/
- /* = */
+ /* =, == */
case TO_STEQL:
if (te->flags & TEF_DBRACKET) {
if ((i = gmatchx(opnd1, opnd2, false)))
@@ -3668,7 +3325,7 @@
if (!all)
print_ulimit(rlimits[i], how);
else for (i = 0; i < NELEM(rlimits); ++i) {
- shprintf("%-20s ", rlimits[i]->name);
+ shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name);
print_ulimit(rlimits[i], how);
}
return (0);
diff --git a/src/histrap.c b/src/histrap.c
index 56723ff..26dd521 100644
--- a/src/histrap.c
+++ b/src/histrap.c
@@ -27,7 +27,7 @@
#include <sys/file.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.159 2016/11/11 18:44:32 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.160 2017/04/08 01:07:16 tg Exp $");
Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign;
@@ -829,7 +829,7 @@
goto retry;
}
if (hs != hist_init_retry)
- bi_errorf(Tf_cant,
+ bi_errorf(Tf_cant_ss_s,
"unlink HISTFILE", hname, cstrerror(errno));
histfsize = 0;
return;
@@ -1033,8 +1033,8 @@
if (!strcmp(sigtraps[i].name, "EXIT") ||
!strcmp(sigtraps[i].name, "ERR")) {
#ifndef MKSH_SMALL
- internal_warningf("ignoring invalid signal name %s",
- sigtraps[i].name);
+ internal_warningf(Tinvname, sigtraps[i].name,
+ "signal");
#endif
sigtraps[i].name = null;
}
diff --git a/src/lex.c b/src/lex.c
index 741bb93..78c2ee7 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.228 2016/08/01 21:38:03 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.234 2017/04/06 01:59:55 tg Exp $");
/*
* states while lexing word
@@ -489,7 +489,7 @@
* If this is a trim operation,
* treat (,|,) specially in STBRACE.
*/
- if (ctype(c, C_SUBOP2)) {
+ if (ksh_issubop2(c)) {
ungetsc(c);
if (Flag(FSH))
PUSH_STATE(STBRACEBOURNE);
@@ -532,7 +532,7 @@
case '`':
subst_gravis:
PUSH_STATE(SBQUOTE);
- *wp++ = COMSUB;
+ *wp++ = COMASUB;
/*
* We need to know whether we are within double
* quotes in order to translate \" to " within
@@ -885,7 +885,7 @@
Xcheck(ws, wp);
if (statep != &states[1])
/* XXX figure out what is missing */
- yyerror("no closing quote\n");
+ yyerror("no closing quote");
/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
if (state == SHEREDELIM)
@@ -893,9 +893,7 @@
dp = Xstring(ws, wp);
if (state == SBASE && (
-#ifndef MKSH_LEGACY_MODE
(c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
-#endif
c == '<' || c == '>') && ((c2 = Xlength(ws, wp)) == 0 ||
(c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) {
struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
@@ -1016,15 +1014,12 @@
while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
*dp++ = *sp++;
if (c != EOS)
- /* word is not unquoted */
+ /* word is not unquoted, or space ran out */
dp = ident;
/* make sure the ident array stays NUL padded */
memset(dp, 0, (ident + IDENT) - dp + 1);
- if (!(cf & (KEYWORD | ALIAS)))
- return (LWORD);
-
- if (*ident != '\0') {
+ if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) {
struct tbl *p;
uint32_t h = hash(ident);
@@ -1068,6 +1063,7 @@
s->start = s->str = p->val.s;
s->u.tblp = p;
s->flags |= SF_HASALIAS;
+ s->line = source->line;
s->next = source;
if (source->type == SEOF) {
/* prevent infinite recursion at EOS */
@@ -1079,9 +1075,12 @@
goto Again;
}
}
- } else if (cf & ALIAS) {
+ } else if (*ident == '\0') {
/* retain typeset et al. even when quoted */
- if (assign_command((dp = wdstrip(yylval.cp, 0)), true))
+ struct tbl *tt = get_builtin((dp = wdstrip(yylval.cp, 0)));
+ uint32_t flag = tt ? tt->flag : 0;
+
+ if (flag & (DECL_UTIL | DECL_FWDR))
strlcpy(ident, dp, sizeof(ident));
afree(dp, ATEMP);
}
@@ -1204,6 +1203,7 @@
error_prefix(true);
va_start(va, fmt);
shf_vfprintf(shl_out, fmt, va);
+ shf_putc('\n', shl_out);
va_end(va);
errorfz();
}
@@ -1625,7 +1625,7 @@
char *tmp, *p;
if (!arraysub(&tmp))
- yyerror("missing ]\n");
+ yyerror("missing ]");
*wp++ = c;
for (p = tmp; *p; ) {
Xcheck(*wsp, wp);
diff --git a/src/lksh.1 b/src/lksh.1
index a9f8be7..c3a82cb 100644
--- a/src/lksh.1
+++ b/src/lksh.1
@@ -1,6 +1,6 @@
-.\" $MirOS: src/bin/mksh/lksh.1,v 1.20 2016/11/11 23:31:35 tg Exp $
+.\" $MirOS: src/bin/mksh/lksh.1,v 1.23 2017/04/02 13:38:02 tg Exp $
.\"-
-.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016
+.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017
.\" mirabilos <m@mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
@@ -74,7 +74,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: November 11 2016 $
+.Dd $Mdocdate: April 2 2017 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
@@ -173,37 +173,34 @@
refer to its manual page for details on the scripting language.
It is recommended to port scripts to
.Nm mksh
-instead of relying on legacy or idiotic POSIX-mandated behaviour,
+instead of relying on legacy or objectionable POSIX-mandated behaviour,
since the MirBSD Korn Shell scripting language is much more consistent.
.Pp
+Do not use
+.Nm
+as an interactive or login shell; use
+.Nm mksh
+instead.
+.Pp
Note that it's strongly recommended to invoke
.Nm
-with at least the
+with
.Fl o Ic posix
-option, if not both that
-.Em and Fl o Ic sh ,
to fully enjoy better compatibility to the
.Tn POSIX
standard (which is probably why you use
.Nm
over
.Nm mksh
-in the first place) or legacy scripts, respectively.
+in the first place);
+.Fl o Ic sh
+(possibly additionally to the above) may be needed for some legacy scripts.
.Sh LEGACY MODE
.Nm
currently has the following differences from
.Nm mksh :
.Bl -bullet
.It
-.\"XXX TODO: remove (some systems may wish to have lksh as ksh)
-There is no explicit support for interactive use,
-nor any command line editing or history code.
-Hence,
-.Nm
-is not suitable as a user's login shell, either; use
-.Nm mksh
-instead.
-.It
The
.Ev KSH_VERSION
string identifies
@@ -241,25 +238,11 @@
The compiler is permitted to delete all data and crash the system
if Undefined Behaviour occurs (see above for an example).
.It
-.\"XXX TODO: move this to FPOSIX
The rotation arithmetic operators are not available.
.It
The shift arithmetic operators take all bits of the second operand into
account; if they exceed permitted precision, the result is unspecified.
.It
-.\"XXX TODO: move this to FPOSIX
-The
-.Tn GNU
-.Nm bash
-extension &\*(Gt to redirect stdout and stderr in one go is not parsed.
-.It
-.\"XXX TODO: drop along with allowing interactivity
-The
-.Nm mksh
-command line option
-.Fl T
-is not available.
-.It
Unless
.Ic set -o posix
is active,
@@ -275,19 +258,6 @@
.Xr getopt 1
command.
.It
-.\"XXX TODO: move to FPOSIX/FSH
-Unlike
-.At
-.Nm ksh ,
-.Nm mksh
-in
-.Fl o Ic posix
-or
-.Fl o Ic sh
-mode and
-.Nm lksh
-do not keep file descriptors \*(Gt 2 private from sub-processes.
-.It
Functions defined with the
.Ic function
reserved word share the shell options
@@ -297,16 +267,10 @@
.Sh SEE ALSO
.Xr mksh 1
.Pp
-.Pa https://www.mirbsd.org/mksh.htm
+.Pa http://www.mirbsd.org/mksh.htm
.Pp
-.Pa https://www.mirbsd.org/ksh\-chan.htm
+.Pa http://www.mirbsd.org/ksh\-chan.htm
.Sh CAVEATS
-The distinction between the shell variants
-.Pq Nm lksh / Nm mksh
-and shell flags
-.Pq Fl o Ic posix / Ic sh
-will be reworked for an upcoming release.
-.Pp
To use
.Nm
as
@@ -315,16 +279,22 @@
.Ic set -o posix
by default if called as
.Nm sh
+.Pq adding Dv \-DMKSH_BINSHPOSIX to Dv CPPFLAGS
is highly recommended for better standards compliance.
+.Pp
For better compatibility with legacy scripts, such as many
.Tn Debian
maintainer scripts, Upstart and SYSV init scripts, and other
-unfixed scripts, using the compile-time options for enabling
+unfixed scripts, also adding the
+.Dv \-DMKSH_BINSHREDUCED
+compile-time option to enable
.Em both
.Ic set -o posix -o sh
when the shell is run as
-.Nm sh
-is recommended.
+.Nm sh ,
+as well as integrating the optional disrecommended
+.Xr printf 1
+builtin, might be necessary.
.Pp
.Nm
tries to make a cross between a legacy bourne/posix compatibl-ish
@@ -332,14 +302,6 @@
.Dq legacy
is not exactly specified.
.Pp
-The
-.Ic set
-built-in command does not currently have all options one would expect
-from a full-blown
-.Nm mksh
-or
-.Nm pdksh .
-.Pp
Talk to the
.Mx
development team using the mailing list at
diff --git a/src/main.c b/src/main.c
index ebbadd9..a556d5d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,7 +5,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -34,7 +34,7 @@
#include <locale.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.322 2016/11/11 23:48:30 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.332 2017/04/12 16:01:45 tg Exp $");
extern char **environ;
@@ -56,8 +56,6 @@
static void x_sigwinch(int);
#endif
-static const char initifs[] = "IFS= \t\n";
-
static const char initsubs[] =
"${PS2=> }"
"${PS3=#? }"
@@ -71,18 +69,18 @@
Ttypeset, "-x", "HOME", TPATH, TSHELL, NULL,
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias,
- "integer=\\typeset -i",
- "local=\\typeset",
+ "integer=\\\\builtin typeset -i",
+ "local=\\\\builtin typeset",
/* not "alias -t --": hash -r needs to work */
- "hash=\\builtin alias -t",
- "type=\\builtin whence -v",
- "autoload=\\typeset -fu",
- "functions=\\typeset -f",
- "history=\\builtin fc -l",
- "nameref=\\typeset -n",
+ "hash=\\\\builtin alias -t",
+ "type=\\\\builtin whence -v",
+ "autoload=\\\\builtin typeset -fu",
+ "functions=\\\\builtin typeset -f",
+ "history=\\\\builtin fc -l",
+ "nameref=\\\\builtin typeset -n",
"nohup=nohup ",
- "r=\\builtin fc -e -",
- "login=\\exec login",
+ "r=\\\\builtin fc -e -",
+ "login=\\\\builtin exec login",
NULL,
/* this is what AT&T ksh seems to track, with the addition of emacs */
Talias, "-tU",
@@ -101,6 +99,51 @@
static struct env env;
struct env *e = &env;
+/* compile-time assertions */
+#define cta(name, expr) struct cta_ ## name { char t[(expr) ? 1 : -1]; }
+
+/* this one should be defined by the standard */
+cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
+ (sizeof(unsigned char) == 1));
+cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) &&
+ ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF));
+/* the next assertion is probably not really needed */
+cta(short_is_2_char, sizeof(short) == 2);
+cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short));
+/* the next assertion is probably not really needed */
+cta(int_is_4_char, sizeof(int) == 4);
+cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int));
+
+cta(long_ge_int, sizeof(long) >= sizeof(int));
+cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long));
+
+#ifndef MKSH_LEGACY_MODE
+/* the next assertion is probably not really needed */
+cta(ari_is_4_char, sizeof(mksh_ari_t) == 4);
+/* but this is */
+cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1));
+/* the next assertion is probably not really needed */
+cta(uari_is_4_char, sizeof(mksh_uari_t) == 4);
+/* but the next three are; we REQUIRE unsigned integer wraparound */
+cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1));
+cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3));
+cta(uari_wrap_32_bit,
+ (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) >
+ (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4));
+#endif
+/* these are always required */
+cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0);
+cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0);
+/* we require these to have the precisely same size and assume 2s complement */
+cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t));
+
+cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t));
+cta(sizet_voidptr_same_size, sizeof(size_t) == sizeof(void *));
+cta(sizet_funcptr_same_size, sizeof(size_t) == sizeof(void (*)(void)));
+/* our formatting routines assume this */
+cta(ptr_fits_in_long, sizeof(size_t) <= sizeof(long));
+cta(ari_fits_in_long, sizeof(mksh_ari_t) <= sizeof(long));
+
static mksh_uari_t
rndsetup(void)
{
@@ -197,6 +240,8 @@
for (i = 0; i < 3; ++i)
if (!isatty(i))
setmode(i, O_BINARY);
+
+ os2_init(&argc, &argv);
#endif
/* do things like getpgrp() et al. */
@@ -363,9 +408,10 @@
}
/* for security */
- typeset(initifs, 0, 0, 0, 0);
+ typeset("IFS= \t\n", 0, 0, 0, 0);
/* assign default shell variable values */
+ typeset("PATHSEP=" MKSH_PATHSEPS, 0, 0, 0, 0);
substitute(initsubs, 0);
/* Figure out the current working directory and set $PWD */
@@ -382,7 +428,7 @@
setstr(vp, current_wd, KSH_RETURN_ERROR);
for (wp = initcoms; *wp != NULL; wp++) {
- shcomexec(wp);
+ c_builtin(wp);
while (*wp != NULL)
wp++;
}
@@ -468,7 +514,9 @@
* to search for it. This changes the behaviour of a
* simple "mksh foo", but can't be helped.
*/
- s->file = search_path(argv[argi++], path, X_OK, NULL);
+ s->file = argv[argi++];
+ if (search_access(s->file, X_OK) != 0)
+ s->file = search_path(s->file, path, X_OK, NULL);
if (!s->file || !*s->file)
s->file = argv[argi - 1];
#else
@@ -627,7 +675,7 @@
}
if (restricted_shell) {
- shcomexec(restr_com);
+ c_builtin(restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 1;
}
@@ -657,9 +705,9 @@
if ((rv = main_init(argc, argv, &s, &l)) == 0) {
if (Flag(FAS_BUILTIN)) {
- rv = shcomexec(l->argv);
+ rv = c_builtin(l->argv);
} else {
- shell(s, true);
+ shell(s, 0);
/* NOTREACHED */
}
}
@@ -712,7 +760,7 @@
unwind(i);
/* NOTREACHED */
default:
- internal_errorf("include %d", i);
+ internal_errorf(Tunexpected_type, Tunwind, Tsource, i);
/* NOTREACHED */
}
}
@@ -723,7 +771,7 @@
s = pushs(SFILE, ATEMP);
s->u.shf = shf;
strdupx(s->file, name, ATEMP);
- i = shell(s, false);
+ i = shell(s, 1);
quitenv(s->u.shf);
if (old_argv) {
e->loc->argv = old_argv;
@@ -743,7 +791,7 @@
s = pushs(SSTRING, ATEMP);
s->start = s->str = comm;
s->line = line;
- rv = shell(s, false);
+ rv = shell(s, 1);
source = sold;
return (rv);
}
@@ -752,22 +800,33 @@
* run the commands from the input source, returning status.
*/
int
-shell(Source * volatile s, volatile bool toplevel)
+shell(Source * volatile s, volatile int level)
{
struct op *t;
volatile bool wastty = tobool(s->flags & SF_TTY);
volatile uint8_t attempts = 13;
- volatile bool interactive = Flag(FTALKING) && toplevel;
+ volatile bool interactive = (level == 0) && Flag(FTALKING);
volatile bool sfirst = true;
Source *volatile old_source = source;
int i;
- newenv(E_PARSE);
+ newenv(level == 2 ? E_EVAL : E_PARSE);
if (interactive)
really_exit = false;
switch ((i = kshsetjmp(e->jbuf))) {
case 0:
break;
+ case LBREAK:
+ case LCONTIN:
+ if (level != 2) {
+ source = old_source;
+ quitenv(NULL);
+ internal_errorf(Tf_cant_s, Tshell,
+ i == LBREAK ? Tbreak : Tcontinue);
+ /* NOTREACHED */
+ }
+ /* assert: interactive == false */
+ /* FALLTHROUGH */
case LINTR:
/* we get here if SIGINT not caught or ignored */
case LERROR:
@@ -807,7 +866,7 @@
default:
source = old_source;
quitenv(NULL);
- internal_errorf("shell %d", i);
+ internal_errorf(Tunexpected_type, Tunwind, Tshell, i);
/* NOTREACHED */
}
while (/* CONSTCOND */ 1) {
@@ -824,7 +883,7 @@
j_notify();
set_prompt(PS1, s);
}
- t = compile(s, sfirst);
+ t = compile(s, sfirst, true);
if (interactive)
histsave(&s->line, NULL, HIST_FLUSH, true);
sfirst = false;
@@ -845,7 +904,7 @@
* immediately after the last command
* executed.
*/
- if (toplevel)
+ if (level == 0)
unwind(LEXIT);
break;
}
@@ -908,6 +967,7 @@
case E_INCL:
case E_LOOP:
case E_ERRH:
+ case E_EVAL:
kshlongjmp(e->jbuf, i);
/* NOTREACHED */
case E_NONE:
@@ -1271,12 +1331,10 @@
VWARNINGF_BUILTIN, fmt, va);
va_end(va);
- /*
- * POSIX special builtins and ksh special builtins cause
- * non-interactive shells to exit. XXX may not want LERROR here
- */
+ /* POSIX special builtins cause non-interactive shells to exit */
if (builtin_spec) {
builtin_argv0 = NULL;
+ /* may not want to use LERROR here */
unwind(LERROR);
}
}
@@ -1379,19 +1437,19 @@
#ifdef DF
if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
if ((lfp = getenv("HOME")) == NULL || !mksh_abspath(lfp))
- errorf("cannot get home directory");
+ errorf("can't get home directory");
lfp = shf_smprintf(Tf_sSs, lfp, "mksh-dbg.txt");
}
if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
- errorf("cannot open debug output file %s", lfp);
+ errorf("can't open debug output file %s", lfp);
if (shl_dbg_fd < FDBASE) {
int nfd;
nfd = fcntl(shl_dbg_fd, F_DUPFD, FDBASE);
close(shl_dbg_fd);
if ((shl_dbg_fd = nfd) == -1)
- errorf("cannot dup debug output file");
+ errorf("can't dup debug output file");
}
fcntl(shl_dbg_fd, F_SETFD, FD_CLOEXEC);
shf_fdopen(shl_dbg_fd, SHF_WR, shl_dbg);
@@ -1407,7 +1465,7 @@
int rv;
if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF))
- errorf("too many files open in shell");
+ errorf(Ttoo_many_files);
#ifdef __ultrix
/*XXX imake style */
@@ -1431,7 +1489,7 @@
(errno == EBADF || errno == EPERM))
return (-1);
if (nfd < 0 || nfd > SHRT_MAX)
- errorf("too many files open in shell");
+ errorf(Ttoo_many_files);
fcntl(nfd, F_SETFD, FD_CLOEXEC);
return ((short)nfd);
}
@@ -1464,6 +1522,10 @@
pv[1] = savefd(lpv[1]);
if (pv[1] != lpv[1])
close(lpv[1]);
+#ifdef __OS2__
+ setmode(pv[0], O_BINARY);
+ setmode(pv[1], O_BINARY);
+#endif
}
void
diff --git a/src/misc.c b/src/misc.c
index 647d1d1..6957c22 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -3,7 +3,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -30,7 +30,7 @@
#include <grp.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.249 2016/11/11 23:31:35 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.255 2017/04/12 16:46:22 tg Exp $");
#define KSH_CHVT_FLAG
#ifdef MKSH_SMALL
@@ -40,10 +40,6 @@
#define KSH_CHVT_CODE
#define KSH_CHVT_FLAG
#endif
-#ifdef MKSH_LEGACY_MODE
-#undef KSH_CHVT_CODE
-#undef KSH_CHVT_FLAG
-#endif
/* type bits for unsigned char */
unsigned char chtypes[UCHAR_MAX + 1];
@@ -93,11 +89,10 @@
void
initctypes(void)
{
- setctypes(letters_uc, C_ALPHA);
- setctypes(letters_lc, C_ALPHA);
- chtypes['_'] |= C_ALPHA;
+ setctypes(letters_uc, C_ALPHX);
+ setctypes(letters_lc, C_ALPHX);
+ chtypes['_'] |= C_ALPHX;
setctypes("0123456789", C_DIGIT);
- /* \0 added automatically */
setctypes(TC_LEX1, C_LEX1);
setctypes("*@#!$-?", C_VAR1);
setctypes(TC_IFSWS, C_IFSWS);
@@ -506,7 +501,7 @@
if (arrayset) {
const char *ccp = NULL;
- if (*array)
+ if (array && *array)
ccp = skip_varname(array, false);
if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
bi_errorf(Tf_sD_s, array, Tnot_ident);
@@ -1541,12 +1536,11 @@
/* symlink target is an absolute path */
xp = Xstring(xs, xp);
beginning_of_a_pathname:
- /* assert: (ip == ipath)[0] == '/' */
+ /* assert: mksh_cdirsep((ip == ipath)[0]) */
/* assert: xp == xs.beg => start of path */
/* exactly two leading slashes? (SUSv4 3.266) */
- /* @komh do NOT use mksh_cdirsep() here */
- if (ip[1] == '/' && ip[2] != '/') {
+ if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
/* keep them, e.g. for UNC pathnames */
Xput(xs, xp, '/');
}
@@ -1711,15 +1705,13 @@
case 0:
return;
case '/':
- /* exactly two leading slashes? (SUSv4 3.266) */
- /* @komh no mksh_cdirsep() here! */
- if (p[1] == '/' && p[2] != '/')
- /* keep them, e.g. for UNC pathnames */
- ++p;
-#ifdef __OS2__
- /* FALLTHROUGH */
+#ifdef MKSH_DOSPATH
case '\\':
#endif
+ /* exactly two leading slashes? (SUSv4 3.266) */
+ if (p[1] == p[0] && !mksh_cdirsep(p[2]))
+ /* keep them, e.g. for UNC pathnames */
+ ++p;
needslash = true;
break;
default:
@@ -2157,7 +2149,7 @@
int
unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
{
- int wc, i, c, fc;
+ int wc, i, c, fc, n;
fc = (*fg)();
switch (fc) {
@@ -2245,7 +2237,8 @@
* four (U: eight) digits; convert to Unicode
*/
wc = 0;
- while (i--) {
+ n = 0;
+ while (n < i || i == -1) {
wc <<= 4;
if ((c = (*fg)()) >= ord('0') && c <= ord('9'))
wc += ksh_numdig(c);
@@ -2258,7 +2251,10 @@
(*fp)(c);
break;
}
+ ++n;
}
+ if (!n)
+ goto unknown_escape;
if ((cstyle && wc > 0xFF) || fc != 'x')
/* Unicode marker */
wc += 0x100;
diff --git a/src/mksh.1 b/src/mksh.1
index 0de2b8f..6a2609a 100644
--- a/src/mksh.1
+++ b/src/mksh.1
@@ -1,8 +1,8 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.420 2016/11/11 23:31:36 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.442 2017/04/12 18:30:58 tg Exp $
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016
+.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
.\" mirabilos <m@mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
@@ -76,7 +76,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
-.Dd $Mdocdate: November 11 2016 $
+.Dd $Mdocdate: April 12 2017 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
@@ -186,23 +186,8 @@
into account all information is first and foremost presented with
.Nm
in mind and should be taken as such.
-.Ss I'm an Android user, so what's mksh?
-.Nm mksh
-is a
-.Ux
-shell / command interpreter, similar to
-.Nm COMMAND.COM
-or
-.Nm CMD.EXE ,
-which has been included with
-.Tn Android Open Source Project
-for a while now.
-Basically, it's a program that runs in a terminal (console window),
-takes user input and runs commands or scripts, which it can also
-be asked to do by other programs, even in the background.
-Any privilege pop-ups you might be encountering are thus not
-.Nm mksh
-issues but questions by some other program utilising it.
+.Ss I use Android, OS/2, etc. so what...?
+Please see the FAQ at the end of this document.
.Ss Invocation
Most builtins can be called directly, for example if a link points from its
name to the shell; not all make sense, have been tested or work at all though.
@@ -1130,17 +1115,17 @@
.Pp
The following command aliases are defined automatically by the shell:
.Bd -literal -offset indent
-autoload=\*(aq\etypeset \-fu\*(aq
-functions=\*(aq\etypeset \-f\*(aq
-hash=\*(aq\ebuiltin alias \-t\*(aq
-history=\*(aq\ebuiltin fc \-l\*(aq
-integer=\*(aq\etypeset \-i\*(aq
-local=\*(aq\etypeset\*(aq
-login=\*(aq\eexec login\*(aq
-nameref=\*(aq\etypeset \-n\*(aq
+autoload=\*(aq\e\ebuiltin typeset \-fu\*(aq
+functions=\*(aq\e\ebuiltin typeset \-f\*(aq
+hash=\*(aq\e\ebuiltin alias \-t\*(aq
+history=\*(aq\e\ebuiltin fc \-l\*(aq
+integer=\*(aq\e\ebuiltin typeset \-i\*(aq
+local=\*(aq\e\ebuiltin typeset\*(aq
+login=\*(aq\e\ebuiltin exec login\*(aq
+nameref=\*(aq\e\ebuiltin typeset \-n\*(aq
nohup=\*(aqnohup \*(aq
-r=\*(aq\ebuiltin fc \-e \-\*(aq
-type=\*(aq\ebuiltin whence \-v\*(aq
+r=\*(aq\e\ebuiltin fc \-e \-\*(aq
+type=\*(aq\e\ebuiltin whence \-v\*(aq
.Ed
.Pp
Tracked aliases allow the shell to remember where it found a particular
@@ -2035,9 +2020,11 @@
.Dq Li \&.
command (see below).
An empty string resulting from a leading or trailing
-colon, or two adjacent colons, is treated as a
+(semi)colon, or two adjacent ones, is treated as a
.Dq Li \&.
(the current directory).
+.It Ev PATHSEP
+A colon (semicolon on OS/2), for the user's convenience.
.It Ev PGRP
The process ID of the shell's process group leader.
.It Ev PIPESTATUS
@@ -2127,7 +2114,7 @@
.Nm
now also supports the following form:
.Bd -literal -offset indent
-PS1=$'\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt '
+PS1=$\*(aq\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt \*(aq
.Ed
.It Ev PS2
Secondary prompt string, by default
@@ -2185,9 +2172,18 @@
The effective user id of the shell.
.El
.Ss Tilde expansion
-Tilde expansion which is done in parallel with parameter substitution, is done
-on words starting with an unquoted
+Tilde expansion, which is done in parallel with parameter substitution,
+is applied to words starting with an unquoted
.Ql \*(TI .
+In parameter assignments (such as those preceding a simple-command or those
+occurring in the arguments of a declaration utility), tilde expansion is done
+after any assignment (i.e. after the equals sign) or after an unquoted colon
+.Pq Ql \&: ;
+login names are also delimited by colons.
+The Korn shell, except in POSIX mode, always expands tildes after unquoted
+equals signs, not just in assignment context (see below), and enables tab
+completion for tildes after all unquoted colons during command line editing.
+.Pp
The characters following the tilde, up to the first
.Ql / ,
if any, are assumed to be a login name.
@@ -2208,21 +2204,6 @@
if any quoting or parameter substitution occurs in the login name, no
substitution is performed.
.Pp
-In parameter assignments
-(such as those preceding a simple-command or those occurring
-in the arguments of
-.Ic alias ,
-.Ic export ,
-.Ic global ,
-.Ic readonly
-and
-.Ic typeset ) ,
-tilde expansion is done after any assignment
-(i.e. after the equals sign)
-or after an unquoted colon
-.Pq Ql \&: ;
-login names are also delimited by colons.
-.Pp
The home directory of previously expanded login names are cached and re-used.
The
.Ic alias Fl d
@@ -2586,7 +2567,7 @@
pipelines are created and in the order they are given, so the following
will print an error with a line number prepended to it:
.Pp
-.D1 $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t
+.Dl $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t
.Pp
File descriptors created by I/O redirections are private to the shell.
.Ss Arithmetic expressions
@@ -2946,6 +2927,12 @@
A function can be made to finish immediately using the
.Ic return
command; this may also be used to explicitly specify the exit status.
+Note that when called in a subshell,
+.Ic return
+will only exit that subshell and will not cause the original shell to exit
+a running function (see the
+.Ic while Ns Li \&... Ns Ic read
+loop FAQ below).
.Pp
Functions defined with the
.Ic function
@@ -3022,18 +3009,18 @@
.Nm
commands keeping assignments:
.Pp
-.Ic builtin , global , source , typeset ,
-.Ic wait
+.Ic global , source , typeset
.Pp
Builtins that are not special:
.Pp
.Ic [ , alias , bg , bind ,
-.Ic cat , cd , command , echo ,
-.Ic false , fc , fg , getopts ,
-.Ic jobs , kill , let , print ,
-.Ic pwd , read , realpath , rename ,
-.Ic sleep , suspend , test , true ,
-.Ic ulimit , umask , unalias , whence
+.Ic builtin , cat , cd , command ,
+.Ic echo , false , fc , fg ,
+.Ic getopts , jobs , kill , let ,
+.Ic print , pwd , read , realpath ,
+.Ic rename , sleep , suspend , test ,
+.Ic true , ulimit , umask , unalias ,
+.Ic wait , whence
.Pp
Once the type of command has been determined, any command-line parameter
assignments are performed and exported for the duration of the command.
@@ -3082,6 +3069,8 @@
Any name with a value defines an alias (see
.Sx Aliases
above).
+.Li \&[A\-Za\-z0\-9_!%,@\-]
+are valid in names except they may not begin with a hyphen-minus.
.Pp
When listing aliases, one of two formats is used.
Normally, aliases are listed as
@@ -3217,6 +3206,18 @@
.Ar command .
.Pp
.It Xo
+.Ic \ebuiltin
+.Ar command Op Ar arg ...
+.Xc
+Same as
+.Ic builtin .
+Additionally acts as declaration utility forwarder, i.e. this is a
+declaration utility (see
+.Sx Tilde expansion )
+.No iff Ar command
+is a declaration utility.
+.Pp
+.It Xo
.Ic cat
.Op Fl u
.Op Ar
@@ -3346,6 +3347,7 @@
and secondly, special built-in commands lose their specialness
(i.e. redirection and utility errors do not cause the shell to
exit, and command assignments are not permanent).
+The declaration utility property is not reset.
.Pp
If the
.Fl p
@@ -3421,8 +3423,10 @@
.Ic posix
or
.Ic sh
-option is set or this is a direct builtin call, only the first argument
-is treated as an option, and only if it is exactly
+option is set or this is a direct builtin call or
+.Ic print
+.Fl R ,
+only the first argument is treated as an option, and only if it is exactly
.Dq Li \-n .
Backslash interpretation is disabled.
.Pp
@@ -3463,7 +3467,7 @@
it does pass these file descriptors on.
.Pp
.It Ic exit Op Ar status
-The shell exits with the specified exit status.
+The shell or subshell exits with the specified exit status.
If
.Ar status
is not specified, the exit status is the current value of the
@@ -3478,6 +3482,7 @@
Sets the export attribute of the named parameters.
Exported parameters are passed in the environment to executed commands.
If values are specified, the named parameters are also assigned.
+This is a declaration utility.
.Pp
If no parameters are specified, all parameters with the export attribute
set are printed one per line; either their names, or, if a
@@ -3632,9 +3637,22 @@
.Ev OPTIND
may lead to unexpected results.
.Pp
-.It global Ar ...
+.It Xo
+.Ic global
+.Op Ic +\-aglpnrtUux
+.Oo Fl L Ns Op Ar n
+.No \*(Ba Fl R Ns Op Ar n
+.No \*(Ba Fl Z Ns Op Ar n Oc
+.Op Fl i Ns Op Ar n
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
See
-.Ic typeset .
+.Ic typeset Fl g .
+.No Deprecated , Em will
+be removed from a future version of
+.Nm .
.Pp
.It Xo
.Ic hash
@@ -3712,12 +3730,8 @@
the parsing or evaluation of an expression, the exit status is greater than 1.
Since expressions may need to be quoted,
.No \&(( Ar expr No ))
-is syntactic sugar for
-.No "{ let '" Ns Ar expr Ns "'; }" .
-.Pp
-.It Ic let]
-Internally used alias for
-.Ic let .
+is syntactic sugar for:
+.Dl "{ \e\ebuiltin let \*(aq" Ns Ar expr Ns "\*(aq; }"
.Pp
.It Xo
.Ic mknod
@@ -3757,13 +3771,13 @@
.Pp
.It Xo
.Ic print
-.Oo Fl AclNnprsu Ns Oo Ar n Oc \*(Ba
-.Fl R Op Fl en Oc
+.Oo Fl AcelNnprsu Ns Oo Ar n Oc \*(Ba
+.Fl R Op Fl n Oc
.Op Ar argument ...
.Xc
Print the specified argument(s) on the standard output,
separated by spaces, terminated with a newline.
-The C escapes mentioned in
+The escapes mentioned in
.Sx Backslash expansion
above, as well as
.Dq Li \ec ,
@@ -3789,6 +3803,9 @@
built-in utility and the
.Ic select
statement do.
+.It Fl e
+Restore backslash expansion after a previous
+.Fl r .
.It Fl l
Change the output word separator to newline.
.It Fl N
@@ -3803,7 +3820,7 @@
Inhibit backslash expansion.
.It Fl s
Print to the history file instead of standard output.
-.It Fl u Op Ar n
+.It Fl u Ns Op Ar n
Print to the file descriptor
.Ar n Pq defaults to 1 if omitted
instead of standard output.
@@ -3811,31 +3828,13 @@
.Pp
The
.Fl R
-option is used to emulate, to some degree, the
+option mostly emulates the
.Bx
.Xr echo 1
-command which does not process
-.Ql \e
-sequences unless the
-.Fl e
-option is given.
-As above, the
-.Fl n
-option suppresses the trailing newline.
-.Pp
-.It Ic printf Ar format Op Ar arguments ...
-Formatted output.
-Approximately the same as the
-.Xr printf 1 ,
-utility, except it uses the same
-.Sx Backslash expansion
-and I/O code and does not handle floating point as the rest of
-.Nm mksh .
-An external utility is preferred over the builtin.
-This is not normally part of
-.Nm mksh ;
-however, distributors may have added this as builtin as a speed hack.
-Do not use in new code.
+command which does not expand backslashes and interprets
+its first argument as option only if it is exactly
+.Dq Li \-n
+.Pq to suppress the trailing newline .
.Pp
.It Ic pwd Op Fl LP
Print the present working directory.
@@ -3970,40 +3969,6 @@
.Ic read
exits with a non-zero status.
.Pp
-Another handy set of tricks:
-If
-.Ic read
-is run in a loop such as
-.Ic while read foo; do ...; done
-then leading whitespace will be removed (IFS) and backslashes processed.
-You might want to use
-.Ic while IFS= read \-r foo; do ...; done
-for pristine I/O.
-Similarly, when using the
-.Fl a
-option, use of the
-.Fl r
-option might be prudent; the same applies for:
-.Bd -literal -offset indent
-find . \-type f \-print0 \*(Ba& \e
- while IFS= read \-d \*(aq\*(aq \-pr filename; do
- print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
-done
-.Ed
-.Pp
-The inner loop will be executed in a subshell and variable changes
-cannot be propagated if executed in a pipeline:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba while read foo; do ...; done
-.Ed
-.Pp
-Use co-processes instead:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba&
-while read \-p foo; do ...; done
-exec 3\*(Gt&p; exec 3\*(Gt&\-
-.Ed
-.Pp
.It Xo
.Ic readonly
.Op Fl p
@@ -4012,6 +3977,7 @@
.Ar ... Oc
.Xc
Sets the read-only attribute of the named parameters.
+This is a declaration utility.
If values are given,
parameters are set to them before setting the attribute.
Once a parameter is
@@ -4266,7 +4232,6 @@
.It Fl o Ic braceexpand
Enable brace expansion (a.k.a. alternation).
This is enabled by default.
-If disabled, tilde expansion after an equals sign is disabled as a side effect.
.It Fl o Ic emacs
Enable BRL emacs-like command-line editing (interactive shells only); see
.Sx Emacs editing mode .
@@ -4507,34 +4472,6 @@
.It Fl O Ar file
.Ar file Ns 's
owner is the shell's effective user ID.
-.It Fl o Ar option
-Shell
-.Ar option
-is set (see the
-.Ic set
-command above for a list of options).
-As a non-standard extension, if the option starts with a
-.Ql \&! ,
-the test is negated; the test always fails if
-.Ar option
-doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option
-.Ar foo
-exists).
-The same can be achieved with [ \-o ?foo ] like in
-.At
-.Nm ksh93 .
-.Ar option
-can also be the short flag led by either
-.Ql \-
-or
-.Ql +
-.Pq no logical negation ,
-for example
-.Dq Li \-x
-or
-.Dq Li +x
-instead of
-.Dq Li xtrace .
.It Fl p Ar file
.Ar file
is a named pipe
@@ -4596,6 +4533,38 @@
.It Fl z Ar string
.Ar string
is empty.
+.It Fl v Ar name
+The shell parameter
+.Ar name
+is set.
+.It Fl o Ar option
+Shell
+.Ar option
+is set (see the
+.Ic set
+command above for a list of options).
+As a non-standard extension, if the option starts with a
+.Ql \&! ,
+the test is negated; the test always fails if
+.Ar option
+doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option
+.Ar foo
+exists).
+The same can be achieved with [ \-o ?foo ] like in
+.At
+.Nm ksh93 .
+.Ar option
+can also be the short flag led by either
+.Ql \-
+or
+.Ql +
+.Pq no logical negation ,
+for example
+.Dq Li \-x
+or
+.Dq Li +x
+instead of
+.Dq Li xtrace .
.It Ar string No = Ar string
Strings are equal.
.It Ar string No == Ar string
@@ -4800,28 +4769,23 @@
A command that exits with a zero value.
.Pp
.It Xo
-.Ic global
-.Oo Op Ic +\-alpnrtUux
-.Op Fl L Ns Op Ar n
-.Op Fl R Ns Op Ar n
-.Op Fl Z Ns Op Ar n
+.Ic typeset
+.Op Ic +\-aglpnrtUux
+.Oo Fl L Ns Op Ar n
+.No \*(Ba Fl R Ns Op Ar n
+.No \*(Ba Fl Z Ns Op Ar n Oc
.Op Fl i Ns Op Ar n
-.No \*(Ba Fl f Op Fl tux Oc
.Oo Ar name
.Op Ns = Ns Ar value
.Ar ... Oc
.Xc
.It Xo
.Ic typeset
-.Oo Op Ic +\-alpnrtUux
-.Op Fl LRZ Ns Op Ar n
-.Op Fl i Ns Op Ar n
-.No \*(Ba Fl f Op Fl tux Oc
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
+.Fl f Op Fl tux
+.Op Ar name ...
.Xc
Display or set parameter attributes.
+This is a declaration utility.
With no
.Ar name
arguments, parameter attributes are displayed; if no options are used, the
@@ -4837,27 +4801,16 @@
If
.Ar name
arguments are given, the attributes of the named parameters are set
-.Pq Ic \-
+.Pq Ic \&\-
or cleared
-.Pq Ic + .
+.Pq Ic \&+ ;
+inside a function, this will cause the parameters to be created
+(with no value) in the local scope (but see
+.Fl g ) .
Values for parameters may optionally be specified.
For
.Ar name Ns \&[*] ,
-the change affects the entire array, and no value may be specified.
-.Pp
-If
-.Ic typeset
-is used inside a function, any parameters specified are localised.
-This is not done by the otherwise identical
-.Ic global .
-.Em Note :
-This means that
-.Nm No 's Ic global
-command is
-.Em not
-equivalent to other programming languages' as it does not allow a
-function called from another function to access a parameter at truly
-global scope, but only prevents putting an accessed one into local scope.
+the change affects all elements of the array, and no value may be specified.
.Pp
When
.Fl f
@@ -4877,6 +4830,9 @@
.It Fl f
Function mode.
Display or set functions and their attributes, instead of parameters.
+.It Fl g
+Do not cause named parameters to be created in
+the local scope when called inside a function.
.It Fl i Ns Op Ar n
Integer attribute.
.Ar n
@@ -5028,7 +4984,7 @@
Also note that the types of limits available are system
dependent \*(en some systems have only the
.Fl f
-limit.
+limit, or not even that, or can set only the soft limits
.Bl -tag -width 5n
.It Fl a
Display all limits; unless
@@ -5395,11 +5351,11 @@
or opinionated differences can be disabled by using this mode; these are:
.Bl -bullet
.It
-The GNU
+The incompatible GNU
.Nm bash
I/O redirection
.Ic &\*(Gt Ns Ar file
-is no longer supported.
+is not supported.
.It
File descriptors created by I/O redirections are inherited by
child processes.
@@ -5409,20 +5365,34 @@
The
.Nm echo
builtin does not interpret backslashes and only supports the exact option
-.Dq Li \-n .
+.Fl n .
.It
-\&... (list is incomplete and may change for R54)
+Alias expansion with a trailing space only reruns on command words.
+.It
+Tilde expansion follows POSIX instead of Korn shell rules.
+.It
+The exit status of
+.Ic fg
+is always 0.
+.It
+.Ic kill
+.Fl l
+only lists signal names, all in one line.
+.It
+.Ic getopts
+does not accept options with a leading
+.Ql + .
.El
.Ss SH mode
Compatibility mode; intended for use with legacy scripts that
cannot easily be fixed; the changes are as follows:
.Bl -bullet
.It
-The GNU
+The incompatible GNU
.Nm bash
I/O redirection
.Ic &\*(Gt Ns Ar file
-is no longer supported.
+is not supported.
.It
File descriptors created by I/O redirections are inherited by
child processes.
@@ -5430,7 +5400,9 @@
The
.Nm echo
builtin does not interpret backslashes and only supports the exact option
-.Dq Li \-n .
+.Fl n ,
+unless built with
+.Ev \-DMKSH_MIDNIGHTBSD01ASH_COMPAT .
.It
The substitution operations
.Sm off
@@ -5460,7 +5432,16 @@
.Xc
wrongly do not require a parenthesis to be escaped and do not parse extglobs.
.It
-\&... (list is incomplete and may change for R54)
+The getopt construct from
+.Xr lksh 1
+passes through the errorlevel.
+.It
+.Nm sh
+.Fl c
+eats a leading
+.Fl \-
+if built with
+.Ev \-DMKSH_MIDNIGHTBSD01ASH_COMPAT .
.El
.Ss Interactive input line editing
The shell supports three modes of reading command lines from a
@@ -5744,9 +5725,6 @@
.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:
@@ -6514,7 +6492,7 @@
.Xr utf\-8 7 ,
.Xr mknod 8
.Pp
-.Pa https://www.mirbsd.org/ksh\-chan.htm
+.Pa http://www.mirbsd.org/ksh\-chan.htm
.Rs
.%A Morris Bolsky
.%B "The KornShell Command and Programming Language"
@@ -6608,9 +6586,17 @@
contributors including our users, to improve the shell is appreciated.
See the documentation, web site and CVS for details.
.Pp
+.Nm mksh\-os2
+is developed by
+.An KO Myung-Hun Aq Mt komh@chollian.net .
+.Pp
+.Nm mksh\-w32
+is developed by
+.An Michael Langguth Aq Mt lan@scalaris.com .
+.Pp
The BSD daemon is Copyright \(co Marshall Kirk McKusick.
The complete legalese is at:
-.Pa https://www.mirbsd.org/TaC\-mksh.txt
+.Pa http://www.mirbsd.org/TaC\-mksh.txt
.\"
.\" This boils down to: feel free to use mksh.ico as application icon
.\" or shortcut for mksh or mksh/Win32 or OS/2; distro patches are ok
@@ -6625,24 +6611,6 @@
.\" to protect it or lose it, which McKusick almost did.
.\"
.Sh CAVEATS
-.Nm
-has a different scope model from
-.At
-.Nm ksh ,
-which leads to subtle differences in semantics for identical builtins.
-This can cause issues with a
-.Ic nameref
-to suddenly point to a local variable by accident; fixing this is hard.
-.Pp
-The parts of a pipeline, like below, are executed in subshells.
-Thus, variable assignments inside them are not visible in the
-surrounding execution environment.
-Use co-processes instead.
-.Bd -literal -offset indent
-foo \*(Ba bar \*(Ba read baz # will not change $baz
-foo \*(Ba bar \*(Ba& read \-p baz # will, however, do so
-.Ed
-.Pp
.Nm mksh
provides a consistent 32-bit integer arithmetic implementation, both
signed and unsigned, with sign of the result of a remainder operation
@@ -6687,6 +6655,8 @@
esac
.Ed
In near future, (Unicode) locale tracking will be implemented though.
+.Pp
+See also the FAQ below.
.Sh BUGS
Suspending (using \*(haZ) pipelines like the one below will only suspend
the currently running part of the pipeline; in this example,
@@ -6710,7 +6680,7 @@
.Xr memmove 3 .
.Pp
This document attempts to describe
-.Nm mksh\ R54
+.Nm mksh\ R55
and up,
.\" with vendor patches from insert-your-name-here,
compiled without any options impacting functionality, such as
@@ -6727,9 +6697,8 @@
Please report bugs in
.Nm
to the
-.Mx
-mailing list at
.Aq Mt miros\-mksh@mirbsd.org
+mailing list
or in the
.Li \&#\&!/bin/mksh
.Pq or Li \&#ksh
@@ -6738,3 +6707,177 @@
.Pq Port 6697 SSL, 6667 unencrypted ,
or at:
.Pa https://launchpad.net/mksh
+.Sh FREQUENTLY ASKED QUESTIONS
+This FAQ attempts to document some of the questions users of
+.Nm
+or readers of this manual page may encounter.
+.Ss I'm an Android user, so what's mksh?
+.Nm mksh
+is a
+.Ux
+shell / command interpreter, similar to
+.Nm COMMAND.COM
+or
+.Nm CMD.EXE ,
+which has been included with
+.Tn Android Open Source Project
+for a while now.
+Basically, it's a program that runs in a terminal (console window),
+takes user input and runs commands or scripts, which it can also
+be asked to do by other programs, even in the background.
+Any privilege pop-ups you might be encountering are thus not
+.Nm mksh
+issues but questions by some other program utilising it.
+.Ss "I'm an OS/2 user, what do I need to know?"
+Unlike the native command prompt, the current working directory is,
+for security reasons common on Unix systems which the shell is designed for,
+not in the search path at all; if you really need this, run the command
+.Li PATH=.$PATHSEP$PATH
+or add that to a suitable initialisation file.
+.Pp
+There are two different newline modes for mksh-os2: standard (Unix) mode,
+in which only LF (0A hex) is supported as line separator, and "textmode",
+which also accepts ASCII newlines (CR+LF), like most other tools on OS/2,
+but creating an incompatibility with standard
+.Nm .
+If you compiled mksh from source, you will get the standard Unix mode unless
+.Fl T
+is added during compilation; you will most likely have gotten this shell
+through komh's port on Hobbes, or from his OS/2 Factory on eComStation
+Korea, which uses "textmode", though.
+Most OS/2 users will want to use "textmode" unless they need absolute
+compatibility with Unix
+.Nm .
+.Ss "How do I start mksh on a specific terminal?"
+Normally:
+.Dl mksh \-T/dev/tty2
+.Pp
+However, if you want for it to return (e.g. for an embedded
+system rescue shell), use this on your real console device instead:
+.Dl mksh \-T!/dev/ttyACM0
+.Pp
+.Nm
+can also daemonise (send to the background):
+.Dl mksh \-T\- \-c \*(aqexec cdio lock\*(aq
+.Ss "POSIX says..."
+Run the shell in POSIX mode (and possibly
+.Nm lksh
+instead of
+.Nm mksh ) :
+.Dl set \-o posix
+.Ss "My prompt from <some other shell> does not work!"
+Contact us on the mailing list or on IRC, we'll convert it for you.
+.Ss "Something is going wrong with my while...read loop"
+Most likely, you've encountered the problem in which the shell runs
+all parts of a pipeline as subshell.
+The inner loop will be executed in a subshell and variable changes
+cannot be propagated if run in a pipeline:
+.Bd -literal -offset indent
+bar \*(Ba baz \*(Ba while read foo; do ...; done
+.Ed
+.Pp
+Note that
+.Ic exit
+in the inner loop will only exit the subshell and not the original shell.
+Likewise, if the code is inside a function,
+.Ic return
+in the inner loop will only exit the subshell and won't terminate the function.
+.Pp
+Use co-processes instead:
+.Bd -literal -offset indent
+bar \*(Ba baz \*(Ba&
+while read \-p foo; do ...; done
+exec 3\*(Gt&p; exec 3\*(Gt&\-
+.Ed
+.Pp
+If
+.Ic read
+is run in a loop such as
+.Ic while read foo; do ...; done
+then leading whitespace will be removed (IFS) and backslashes processed.
+You might want to use
+.Ic while IFS= read \-r foo; do ...; done
+for pristine I/O.
+Similarly, when using the
+.Fl a
+option, use of the
+.Fl r
+option might be prudent
+.Pq Dq Li read \-raN\-1 arr \*(Ltfile ;
+the same applies for NUL-terminated lines:
+.Bd -literal -offset indent
+find . \-type f \-print0 \*(Ba& \e
+ while IFS= read \-d \*(aq\*(aq \-pr filename; do
+ print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
+done
+.Ed
+.Pp
+.Ss "What differences in function-local scopes are there?"
+.Nm
+has a different scope model from
+.At
+.Nm ksh ,
+which leads to subtle differences in semantics for identical builtins.
+This can cause issues with a
+.Ic nameref
+to suddenly point to a local variable by accident.
+.Pp
+.Tn GNU
+.Nm bash
+allows unsetting local variables; in
+.Nm ,
+doing so in a function allows back access to the global variable
+(actually the one in the next scope up) with the same name.
+The following code, when run before the function definitions, changes
+the behaviour of
+.Ic unset
+to behave like other shells (the alias can be removed after the definitions):
+.Bd -literal -offset indent
+case ${KSH_VERSION:\-} in
+*MIRBSD\ KSH*\*(Ba*LEGACY\ KSH*)
+ function unset_compat {
+ \e\ebuiltin typeset unset_compat_x
+
+ for unset_compat_x in "$@"; do
+ eval "\e\e\e\ebuiltin unset $unset_compat_x[*]"
+ done
+ }
+ \e\ebuiltin alias unset=unset_compat
+ ;;
+esac
+.Ed
+.Pp
+When a local variable is created (e.g. using
+.Ic local ,
+.Ic typeset ,
+.Ic integer ,
+.Ic \e\ebuiltin typeset )
+it does not, like in other shells, inherit the value from the global
+(next scope up) variable with the same name; it is rather created
+without any value (unset but defined).
+.Ss "I get an error in this regex comparison"
+Use extglobs instead of regexes:
+.Dl "[[ foo =~ (foo\*(Babar).*baz ]] # becomes"
+.Dl "[[ foo = *@(foo\*(Babar)*baz* ]] # instead"
+.Ss "Are there any extensions to avoid?"
+.Tn GNU
+.Nm bash
+supports
+.Dq Li &\*(Gt
+.Pq and Dq Li \*(Ba&
+to redirect both stdout and stderr in one go, but this breaks POSIX
+and Korn Shell syntax; use POSIX redirections instead:
+.Dl "foo \*(Ba& bar \*(Ba& baz &\*(Gtlog # GNU bash"
+.Dl "foo 2\*(Gt&1 \*(Ba bar 2\*(Gt&1 \*(Ba baz \*(Gtlog 2\*(Gt&1 # POSIX"
+.Ss "\*(haL (Ctrl-L) does not clear the screen"
+Use \*(ha[\*(haL (Escape+Ctrl-L) or rebind it:
+.Dl bind \*(aq\*(haL=clear-screen\*(aq
+.Ss "\*(haU (Ctrl-U) clears the entire line"
+If it should only delete the line up to the cursor, use:
+.Dl bind \-m \*(haU=\*(aq\*(ha[0\*(haK\*(aq
+.Ss "Cursor Up behaves differently from zsh"
+Some shells make Cursor Up search in the history only for
+commands starting with what was already entered.
+.Nm
+separates the shortcuts: Cursor Up goes up one command
+and PgUp searches the history as described above.
diff --git a/src/os2.c b/src/os2.c
new file mode 100644
index 0000000..5d39630
--- /dev/null
+++ b/src/os2.c
@@ -0,0 +1,557 @@
+/*-
+ * Copyright (c) 2015
+ * KO Myung-Hun <komh@chollian.net>
+ *
+ * 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.
+ */
+
+#define INCL_DOS
+#include <os2.h>
+
+#include "sh.h"
+
+#include <klibc/startup.h>
+#include <io.h>
+#include <unistd.h>
+#include <process.h>
+
+__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.1 2017/04/02 15:00:44 tg Exp $");
+
+static char *remove_trailing_dots(char *);
+static int access_stat_ex(int (*)(), const char *, void *);
+static int test_exec_exist(const char *, char *);
+static void response(int *, const char ***);
+static char *make_response_file(char * const *);
+static void env_slashify(void);
+static void add_temp(const char *);
+static void cleanup_temps(void);
+static void cleanup(void);
+
+#define RPUT(x) do { \
+ if (new_argc >= new_alloc) { \
+ new_alloc += 20; \
+ if (!(new_argv = realloc(new_argv, \
+ new_alloc * sizeof(char *)))) \
+ goto exit_out_of_memory; \
+ } \
+ new_argv[new_argc++] = (x); \
+} while (/* CONSTCOND */ 0)
+
+#define KLIBC_ARG_RESPONSE_EXCLUDE \
+ (__KLIBC_ARG_DQUOTE | __KLIBC_ARG_WILDCARD | __KLIBC_ARG_SHELL)
+
+static void
+response(int *argcp, const char ***argvp)
+{
+ int i, old_argc, new_argc, new_alloc = 0;
+ const char **old_argv, **new_argv;
+ char *line, *l, *p;
+ FILE *f;
+
+ old_argc = *argcp;
+ old_argv = *argvp;
+ for (i = 1; i < old_argc; ++i)
+ if (old_argv[i] &&
+ !(old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) &&
+ old_argv[i][0] == '@')
+ break;
+
+ if (i >= old_argc)
+ /* do nothing */
+ return;
+
+ new_argv = NULL;
+ new_argc = 0;
+ for (i = 0; i < old_argc; ++i) {
+ if (i == 0 || !old_argv[i] ||
+ (old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) ||
+ old_argv[i][0] != '@' ||
+ !(f = fopen(old_argv[i] + 1, "rt")))
+ RPUT(old_argv[i]);
+ else {
+ long filesize;
+
+ fseek(f, 0, SEEK_END);
+ filesize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ line = malloc(filesize + /* type */ 1 + /* NUL */ 1);
+ if (!line) {
+ exit_out_of_memory:
+ fputs("Out of memory while reading response file\n", stderr);
+ exit(255);
+ }
+
+ line[0] = __KLIBC_ARG_NONZERO | __KLIBC_ARG_RESPONSE;
+ l = line + 1;
+ while (fgets(l, (filesize + 1) - (l - (line + 1)), f)) {
+ p = strchr(l, '\n');
+ if (p) {
+ /*
+ * if a line ends with a backslash,
+ * concatenate with the next line
+ */
+ if (p > l && p[-1] == '\\') {
+ char *p1;
+ int count = 0;
+
+ for (p1 = p - 1; p1 >= l &&
+ *p1 == '\\'; p1--)
+ count++;
+
+ if (count & 1) {
+ l = p + 1;
+
+ continue;
+ }
+ }
+
+ *p = 0;
+ }
+ p = strdup(line);
+ if (!p)
+ goto exit_out_of_memory;
+
+ RPUT(p + 1);
+
+ l = line + 1;
+ }
+
+ free(line);
+
+ if (ferror(f)) {
+ fputs("Cannot read response file\n", stderr);
+ exit(255);
+ }
+
+ fclose(f);
+ }
+ }
+
+ RPUT(NULL);
+ --new_argc;
+
+ *argcp = new_argc;
+ *argvp = new_argv;
+}
+
+static void
+init_extlibpath(void)
+{
+ const char *vars[] = {
+ "BEGINLIBPATH",
+ "ENDLIBPATH",
+ "LIBPATHSTRICT",
+ NULL
+ };
+ char val[512];
+ int flag;
+
+ for (flag = 0; vars[flag]; flag++) {
+ DosQueryExtLIBPATH(val, flag + 1);
+ if (val[0])
+ setenv(vars[flag], val, 1);
+ }
+}
+
+/*
+ * Convert backslashes of environmental variables to forward slahes.
+ * A backslash may be used as an escaped character when doing 'echo'.
+ * This leads to an unexpected behavior.
+ */
+static void
+env_slashify(void)
+{
+ /*
+ * PATH and TMPDIR are used by OS/2 as well. That is, they may
+ * have backslashes as a directory separator.
+ * BEGINLIBPATH and ENDLIBPATH are special variables on OS/2.
+ */
+ const char *var_list[] = {
+ "PATH",
+ "TMPDIR",
+ "BEGINLIBPATH",
+ "ENDLIBPATH",
+ NULL
+ };
+ const char **var;
+ char *value;
+
+ for (var = var_list; *var; var++) {
+ value = getenv(*var);
+
+ if (value)
+ _fnslashify(value);
+ }
+}
+
+void
+os2_init(int *argcp, const char ***argvp)
+{
+ response(argcp, argvp);
+
+ init_extlibpath();
+ env_slashify();
+
+ if (!isatty(STDIN_FILENO))
+ setmode(STDIN_FILENO, O_BINARY);
+ if (!isatty(STDOUT_FILENO))
+ setmode(STDOUT_FILENO, O_BINARY);
+ if (!isatty(STDERR_FILENO))
+ setmode(STDERR_FILENO, O_BINARY);
+
+ atexit(cleanup);
+}
+
+void
+setextlibpath(const char *name, const char *val)
+{
+ int flag;
+ char *p, *cp;
+
+ if (!strcmp(name, "BEGINLIBPATH"))
+ flag = BEGIN_LIBPATH;
+ else if (!strcmp(name, "ENDLIBPATH"))
+ flag = END_LIBPATH;
+ else if (!strcmp(name, "LIBPATHSTRICT"))
+ flag = LIBPATHSTRICT;
+ else
+ return;
+
+ /* convert slashes to backslashes */
+ strdupx(cp, val, ATEMP);
+ for (p = cp; *p; p++) {
+ if (*p == '/')
+ *p = '\\';
+ }
+
+ DosSetExtLIBPATH(cp, flag);
+
+ afree(cp, ATEMP);
+}
+
+/* remove trailing dots */
+static char *
+remove_trailing_dots(char *name)
+{
+ char *p;
+
+ for (p = name + strlen(name); --p > name && *p == '.'; )
+ /* nothing */;
+
+ if (*p != '.' && *p != '/' && *p != '\\' && *p != ':')
+ p[1] = '\0';
+
+ return (name);
+}
+
+#define REMOVE_TRAILING_DOTS(name) \
+ remove_trailing_dots(memcpy(alloca(strlen(name) + 1), name, strlen(name) + 1))
+
+/* alias of stat() */
+extern int _std_stat(const char *, struct stat *);
+
+/* replacement for stat() of kLIBC which fails if there are trailing dots */
+int
+stat(const char *name, struct stat *buffer)
+{
+ return (_std_stat(REMOVE_TRAILING_DOTS(name), buffer));
+}
+
+/* alias of access() */
+extern int _std_access(const char *, int);
+
+/* replacement for access() of kLIBC which fails if there are trailing dots */
+int
+access(const char *name, int mode)
+{
+ /*
+ * On OS/2 kLIBC, X_OK is set only for executable files.
+ * This prevents scripts from being executed.
+ */
+ if (mode & X_OK)
+ mode = (mode & ~X_OK) | R_OK;
+
+ return (_std_access(REMOVE_TRAILING_DOTS(name), mode));
+}
+
+#define MAX_X_SUFFIX_LEN 4
+
+static const char *x_suffix_list[] =
+ { "", ".ksh", ".exe", ".sh", ".cmd", ".com", ".bat", NULL };
+
+/* call fn() by appending executable extensions */
+static int
+access_stat_ex(int (*fn)(), const char *name, void *arg)
+{
+ char *x_name;
+ const char **x_suffix;
+ int rc = -1;
+ size_t x_namelen = strlen(name) + MAX_X_SUFFIX_LEN + 1;
+
+ /* otherwise, try to append executable suffixes */
+ x_name = alloc(x_namelen, ATEMP);
+
+ for (x_suffix = x_suffix_list; rc && *x_suffix; x_suffix++) {
+ strlcpy(x_name, name, x_namelen);
+ strlcat(x_name, *x_suffix, x_namelen);
+
+ rc = fn(x_name, arg);
+ }
+
+ afree(x_name, ATEMP);
+
+ return (rc);
+}
+
+/* access()/search_access() version */
+int
+access_ex(int (*fn)(const char *, int), const char *name, int mode)
+{
+ /*XXX this smells fishy --mirabilos */
+ return (access_stat_ex(fn, name, (void *)mode));
+}
+
+/* stat() version */
+int
+stat_ex(const char *name, struct stat *buffer)
+{
+ return (access_stat_ex(stat, name, buffer));
+}
+
+static int
+test_exec_exist(const char *name, char *real_name)
+{
+ struct stat sb;
+
+ if (stat(name, &sb) < 0 || !S_ISREG(sb.st_mode))
+ return (-1);
+
+ /* safe due to calculations in real_exec_name() */
+ memcpy(real_name, name, strlen(name) + 1);
+
+ return (0);
+}
+
+const char *
+real_exec_name(const char *name)
+{
+ char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1];
+ const char *real_name = name;
+
+ if (access_stat_ex(test_exec_exist, real_name, x_name) != -1)
+ /*XXX memory leak */
+ strdupx(real_name, x_name, ATEMP);
+
+ return (real_name);
+}
+
+/* OS/2 can process a command line up to 32 KiB */
+#define MAX_CMD_LINE_LEN 32768
+
+/* make a response file to pass a very long command line */
+static char *
+make_response_file(char * const *argv)
+{
+ char rsp_name_arg[] = "@mksh-rsp-XXXXXX";
+ char *rsp_name = &rsp_name_arg[1];
+ int arg_len = 0;
+ int i;
+
+ for (i = 0; argv[i]; i++)
+ arg_len += strlen(argv[i]) + 1;
+
+ /*
+ * If a length of command line is longer than MAX_CMD_LINE_LEN, then
+ * use a response file. OS/2 cannot process a command line longer
+ * than 32K. Of course, a response file cannot be recognised by a
+ * normal OS/2 program, that is, neither non-EMX or non-kLIBC. But
+ * it cannot accept a command line longer than 32K in itself. So
+ * using a response file in this case, is an acceptable solution.
+ */
+ if (arg_len > MAX_CMD_LINE_LEN) {
+ int fd;
+ char *result;
+
+ if ((fd = mkstemp(rsp_name)) == -1)
+ return (NULL);
+
+ /* write all the arguments except a 0th program name */
+ for (i = 1; argv[i]; i++) {
+ write(fd, argv[i], strlen(argv[i]));
+ write(fd, "\n", 1);
+ }
+
+ close(fd);
+ add_temp(rsp_name);
+ strdupx(result, rsp_name_arg, ATEMP);
+ return (result);
+ }
+
+ return (NULL);
+}
+
+/* alias of execve() */
+extern int _std_execve(const char *, char * const *, char * const *);
+
+/* replacement for execve() of kLIBC */
+int
+execve(const char *name, char * const *argv, char * const *envp)
+{
+ const char *exec_name;
+ FILE *fp;
+ char sign[2];
+ char *rsp_argv[3];
+ char *rsp_name_arg;
+ int pid;
+ int status;
+ int fd;
+ int rc;
+
+ /*
+ * #! /bin/sh : append .exe
+ * extproc sh : search sh.exe in PATH
+ */
+ exec_name = search_path(name, path, X_OK, NULL);
+ if (!exec_name) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ /*-
+ * kLIBC execve() has problems when executing scripts.
+ * 1. it fails to execute a script if a directory whose name
+ * is same as an interpreter exists in a current directory.
+ * 2. it fails to execute a script not starting with sharpbang.
+ * 3. it fails to execute a batch file if COMSPEC is set to a shell
+ * incompatible with cmd.exe, such as /bin/sh.
+ * And ksh process scripts more well, so let ksh process scripts.
+ */
+ errno = 0;
+ if (!(fp = fopen(exec_name, "rb")))
+ errno = ENOEXEC;
+
+ if (!errno && fread(sign, 1, sizeof(sign), fp) != sizeof(sign))
+ errno = ENOEXEC;
+
+ if (fp && fclose(fp))
+ errno = ENOEXEC;
+
+ if (!errno &&
+ !((sign[0] == 'M' && sign[1] == 'Z') ||
+ (sign[0] == 'N' && sign[1] == 'E') ||
+ (sign[0] == 'L' && sign[1] == 'X')))
+ errno = ENOEXEC;
+
+ if (errno == ENOEXEC)
+ return (-1);
+
+ rsp_name_arg = make_response_file(argv);
+
+ if (rsp_name_arg) {
+ rsp_argv[0] = argv[0];
+ rsp_argv[1] = rsp_name_arg;
+ rsp_argv[2] = NULL;
+
+ argv = rsp_argv;
+ }
+
+ pid = spawnve(P_NOWAIT, exec_name, argv, envp);
+
+ afree(rsp_name_arg, ATEMP);
+
+ if (pid == -1) {
+ cleanup_temps();
+
+ return (-1);
+ }
+
+ /* close all opened handles */
+ for (fd = 0; fd < NUFILE; fd++) {
+ if (fcntl(fd, F_GETFD) == -1)
+ continue;
+
+ close(fd);
+ }
+
+ while ((rc = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+ /* nothing */;
+
+ cleanup_temps();
+
+ /* Is this possible? And is this right? */
+ if (rc == -1)
+ return (-1);
+
+ if (WIFSIGNALED(status))
+ _exit(ksh_sigmask(WTERMSIG(status)));
+
+ _exit(WEXITSTATUS(status));
+}
+
+static struct temp *templist = NULL;
+
+static void
+add_temp(const char *name)
+{
+ struct temp *tp;
+
+ tp = alloc(offsetof(struct temp, tffn[0]) + strlen(name) + 1, APERM);
+ memcpy(tp->tffn, name, strlen(name) + 1);
+ tp->next = templist;
+ templist = tp;
+}
+
+/* alias of unlink() */
+extern int _std_unlink(const char *);
+
+/*
+ * Replacement for unlink() of kLIBC not supporting to remove files used by
+ * another processes.
+ */
+int
+unlink(const char *name)
+{
+ int rc;
+
+ rc = _std_unlink(name);
+ if (rc == -1 && errno != ENOENT)
+ add_temp(name);
+
+ return (rc);
+}
+
+static void
+cleanup_temps(void)
+{
+ struct temp *tp;
+ struct temp **tpnext;
+
+ for (tpnext = &templist, tp = templist; tp; tp = *tpnext) {
+ if (_std_unlink(tp->tffn) == 0 || errno == ENOENT) {
+ *tpnext = tp->next;
+ afree(tp, APERM);
+ } else {
+ tpnext = &tp->next;
+ }
+ }
+}
+
+static void
+cleanup(void)
+{
+ cleanup_temps();
+}
diff --git a/src/sh.h b/src/sh.h
index c9b9078..5b36378 100644
--- a/src/sh.h
+++ b/src/sh.h
@@ -10,7 +10,7 @@
/*-
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -175,9 +175,9 @@
#endif
#ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.791 2016/11/11 23:31:38 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.808 2017/04/12 17:38:46 tg Exp $");
#endif
-#define MKSH_VERSION "R54 2016/11/11"
+#define MKSH_VERSION "R55 2017/04/12"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@@ -392,13 +392,20 @@
#endif
#ifdef __OS2__
+#define MKSH_UNIXROOT "/@unixroot"
+#else
+#define MKSH_UNIXROOT ""
+#endif
+
+#ifdef MKSH_DOSPATH
+#ifndef __GNUC__
+# error GCC extensions needed later on
+#endif
#define MKSH_PATHSEPS ";"
#define MKSH_PATHSEPC ';'
-#define MKSH_UNIXROOT "/@unixroot"
#else
#define MKSH_PATHSEPS ":"
#define MKSH_PATHSEPC ':'
-#define MKSH_UNIXROOT ""
#endif
#if !HAVE_FLOCK_DECL
@@ -505,12 +512,20 @@
EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
#ifdef MKSH_LEGACY_MODE
-#define KSH_VERSIONNAME "LEGACY"
+#define KSH_VERSIONNAME_ISLEGACY "LEGACY"
#else
-#define KSH_VERSIONNAME "MIRBSD"
+#define KSH_VERSIONNAME_ISLEGACY "MIRBSD"
#endif
-EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME \
- " KSH " MKSH_VERSION);
+#ifdef MKSH_WITH_TEXTMODE
+#define KSH_VERSIONNAME_TEXTMODE " +TEXTMODE"
+#else
+#define KSH_VERSIONNAME_TEXTMODE ""
+#endif
+#ifndef KSH_VERSIONNAME_VENDOR_EXT
+#define KSH_VERSIONNAME_VENDOR_EXT ""
+#endif
+EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME_ISLEGACY \
+ " KSH " MKSH_VERSION KSH_VERSIONNAME_TEXTMODE KSH_VERSIONNAME_VENDOR_EXT);
#define KSH_VERSION (initvsn + /* "KSH_VERSION=@(#)" */ 16)
EXTERN const char digits_uc[] E_INIT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
@@ -578,7 +593,7 @@
#define mkssert(e) do { } while (/* CONSTCOND */ 0)
#endif
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 541)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 551)
#error Must run Build.sh to compile this.
extern void thiswillneverbedefinedIhope(void);
int
@@ -630,14 +645,6 @@
} while (/* CONSTCOND */ 0)
#endif
-#ifdef MKSH_LEGACY_MODE
-#ifndef MKSH_NO_CMDLINE_EDITING
-#define MKSH_NO_CMDLINE_EDITING /* defined */
-#endif
-#undef MKSH_S_NOVI
-#define MKSH_S_NOVI 1
-#endif
-
#ifdef MKSH_SMALL
#ifndef MKSH_NOPWNAM
#define MKSH_NOPWNAM /* defined */
@@ -772,6 +779,7 @@
#define E_LOOP 5 /* executing for/while # */
#define E_ERRH 6 /* general error handler # */
#define E_GONE 7 /* hidden in child */
+#define E_EVAL 8 /* running eval # */
/* # indicates env has valid jbuf (see unwind()) */
/* struct env.flag values */
@@ -855,8 +863,8 @@
#ifndef HAVE_STRING_POOLING /* helpers for pooled strings */
EXTERN const char T4spaces[] E_INIT(" ");
-#define T1space (T4spaces + 3)
-EXTERN const char Tcolsp[] E_INIT(": ");
+#define T1space (Treal_sp2 + 5)
+#define Tcolsp (Tf_sD_ + 2)
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} $_");
@@ -865,15 +873,19 @@
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 Tbadsubst[] E_INIT("bad substitution");
+#define Talias (Tunalias + 2)
+EXTERN const char Tbadnum[] E_INIT("bad number");
+#define Tbadsubst (Tfg_badsubst + 10)
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 Tsgbreak[] E_INIT("*=break");
+#define Tbreak (Tsgbreak + 2)
+EXTERN const char T__builtin[] E_INIT("-\\builtin");
+#define T_builtin (T__builtin + 1)
+#define Tbuiltin (T__builtin + 2)
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");
@@ -882,31 +894,35 @@
EXTERN const char Tbcat[] E_INIT("!cat");
#define Tcat (Tbcat + 1)
#define Tcd (Tcant_cd + 25)
-EXTERN const char Tcommand[] E_INIT("command");
+#define T_command (T_funny_command + 9)
+#define Tcommand (T_funny_command + 10)
+EXTERN const char Tsgcontinue[] E_INIT("*=continue");
+#define Tcontinue (Tsgcontinue + 2)
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)
+EXTERN const char Tdsgexport[] E_INIT("^*=export");
+#define Texport (Tdsgexport + 3)
#ifdef __OS2__
EXTERN const char Textproc[] E_INIT("extproc");
#endif
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");
+#define Tfile (Tfile_fd + 20)
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)
-EXTERN const char T_funny_command[] E_INIT("funny $() command");
+EXTERN const char T_funny_command[] E_INIT("funny $()-command");
EXTERN const char Tgetopts[] E_INIT("getopts");
-EXTERN const char Thistory[] E_INIT("history");
+#define Thistory (Tnot_in_history + 7)
EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
+EXTERN const char Tinvname[] E_INIT("%s: invalid %s name");
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");
+#define Tname (Tinvname + 15)
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");
@@ -917,77 +933,84 @@
#define TOLDPWD (Tno_OLDPWD + 3)
#define Topen (Tcant_open + 6)
#define TPATH (TFPATH + 1)
-EXTERN const char Tpv[] E_INIT("pv");
+#define Tpv (TpVv + 1)
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)
+#define Tread (Tshf_read + 4)
+EXTERN const char Tdsgreadonly[] E_INIT("^*=readonly");
+#define Treadonly (Tdsgreadonly + 3)
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 ");
+#define Treal_sp1 (Treal_sp2 + 1)
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 Tset (Tf_parm + 18)
#define Tsh (Tmksh + 2)
#define TSHELL (TEXECSHELL + 4)
+#define Tshell (Ttoo_many_files + 23)
EXTERN const char Tshf_read[] E_INIT("shf_read");
EXTERN const char Tshf_write[] E_INIT("shf_write");
+EXTERN const char Tgsource[] E_INIT("=source");
+#define Tsource (Tgsource + 1)
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 Ttoo_many_files[] E_INIT("too many open files in shell");
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)
+EXTERN const char Tdgtypeset[] E_INIT("^=typeset");
+#define Ttypeset (Tdgtypeset + 2)
#define Tugo (Taugo + 1)
EXTERN const char Tunalias[] E_INIT("unalias");
#define Tunexpected (TELIF_unexpected + 6)
+EXTERN const char Tunexpected_type[] E_INIT("%s: unexpected %s type %d");
EXTERN const char Tunknown_option[] E_INIT("unknown option");
-EXTERN const char Tuser_sp1[] E_INIT("user ");
+EXTERN const char Tunwind[] E_INIT("unwind");
+#define Tuser_sp1 (Tuser_sp2 + 1)
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");
+#define Tf__d (Tunexpected_type + 22)
EXTERN const char Tf__ss[] E_INIT(" %s%s");
-EXTERN const char Tf__sN[] E_INIT(" %s\n");
+#define Tf__sN (Tf_s_s_sN + 5)
EXTERN const char Tf_sSs[] E_INIT("%s/%s");
-EXTERN const char Tf_T[] E_INIT("%T");
+#define Tf_T (Tf_s_T + 3)
EXTERN const char Tf_dN[] E_INIT("%d\n");
EXTERN const char Tf_s_[] E_INIT("%s ");
EXTERN const char Tf_s_T[] E_INIT("%s %T");
EXTERN const char Tf_s_s_sN[] E_INIT("%s %s %s\n");
-EXTERN const char Tf_s_s[] E_INIT("%s %s");
-EXTERN const char Tf_s_sD_s[] E_INIT("%s %s: %s");
+#define Tf_s_s (Tf_sD_s_s + 4)
+#define Tf_s_sD_s (Tf_cant_ss_s + 6)
EXTERN const char Tf_optfoo[] E_INIT("%s%s-%c: %s");
EXTERN const char Tf_sD_[] E_INIT("%s: ");
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");
+EXTERN const char Tf_cant_s[] E_INIT("%s: can't %s");
+EXTERN const char Tf_cant_ss_s[] E_INIT("can't %s %s: %s");
+EXTERN const char Tf_heredoc[] E_INIT("here document '%s' unclosed");
#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");
+#define Tf_lu (Tf_toolarge + 17)
EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu");
EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s");
-#define Tf_ss (Tf__ss + 1)
+#define Tf_ss (Tf_sss + 2)
EXTERN const char Tf_sss[] E_INIT("%s%s%s");
EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s");
-EXTERN const char Tf_toomany[] E_INIT("too many %ss\n");
+EXTERN const char Tf_toomany[] E_INIT("too many %ss");
EXTERN const char Tf_sd[] E_INIT("%s %d");
-#define Tf_s (Tf__ss + 3)
+#define Tf_s (Tf_temp + 28)
EXTERN const char Tft_end[] E_INIT("%;");
EXTERN const char Tft_R[] E_INIT("%R");
-#define Tf_d (Tf__d + 1)
+#define Tf_d (Tunexpected_type + 23)
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");
@@ -996,8 +1019,8 @@
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)
+#define Tf_sN (Tf_s_s_sN + 6)
+#define Tf_sD_s (Tf_temp + 24)
EXTERN const char T_devtty[] E_INIT("/dev/tty");
#else /* helpers for string pooling */
#define T4spaces " "
@@ -1012,13 +1035,17 @@
#define Tbracket "["
#define Tdot "."
#define Talias "alias"
+#define Tbadnum "bad number"
#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 Tsgbreak "*=break"
+#define Tbreak "break"
+#define T__builtin "-\\builtin"
+#define T_builtin "\\builtin"
#define Tbuiltin "builtin"
#define Toomem "can't allocate %zu data bytes"
#define Tcant_cd "restricted shell - can't cd"
@@ -1028,11 +1055,14 @@
#define Tbcat "!cat"
#define Tcat "cat"
#define Tcd "cd"
+#define T_command "-command"
#define Tcommand "command"
+#define Tsgcontinue "*=continue"
+#define Tcontinue "continue"
#define Tcreate "create"
#define TELIF_unexpected "TELIF unexpected"
#define TEXECSHELL "EXECSHELL"
-#define Tsgexport "*=export"
+#define Tdsgexport "^*=export"
#define Texport "export"
#ifdef __OS2__
#define Textproc "extproc"
@@ -1045,10 +1075,11 @@
#define TFPATH "FPATH"
#define T_function " function"
#define Tfunction "function"
-#define T_funny_command "funny $() command"
+#define T_funny_command "funny $()-command"
#define Tgetopts "getopts"
#define Thistory "history"
#define Tintovfl "integer overflow %zu %c %zu prevented"
+#define Tinvname "%s: invalid %s name"
#define Tjobs "jobs"
#define Tjob_not_started "job not started"
#define Tmksh "mksh"
@@ -1067,7 +1098,7 @@
#define TpVv "Vpv"
#define TPWD "PWD"
#define Tread "read"
-#define Tsgreadonly "*=readonly"
+#define Tdsgreadonly "^*=readonly"
#define Treadonly "readonly"
#define Tredirection_dup "can't finish (dup) redirection"
#define Tredirection "redirection"
@@ -1079,22 +1110,28 @@
#define Tset "set"
#define Tsh "sh"
#define TSHELL "SHELL"
+#define Tshell "shell"
#define Tshf_read "shf_read"
#define Tshf_write "shf_write"
+#define Tgsource "=source"
+#define Tsource "source"
#define Tj_suspend "j_suspend"
#define Tsuspend "suspend"
#define Tsynerr "syntax error"
#define Ttime "time"
#define Ttoo_many_args "too many arguments"
+#define Ttoo_many_files "too many open files in shell"
#define Ttrue "true"
#define Ttty_fd_dupof "dup of tty fd"
#define Ttty_fd "tty fd"
-#define Tgtypeset "=typeset"
+#define Tdgtypeset "^=typeset"
#define Ttypeset "typeset"
#define Tugo "ugo"
#define Tunalias "unalias"
#define Tunexpected "unexpected"
+#define Tunexpected_type "%s: unexpected %s type %d"
#define Tunknown_option "unknown option"
+#define Tunwind "unwind"
#define Tuser_sp1 "user "
#define Tuser_sp2 " user "
#define Twrite "write"
@@ -1115,8 +1152,9 @@
#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"
+#define Tf_cant_s "%s: can't %s"
+#define Tf_cant_ss_s "can't %s %s: %s"
+#define Tf_heredoc "here document '%s' unclosed"
#if HAVE_MKNOD
#define Tf_nonnum "non-numeric %s %s '%s'"
#endif
@@ -1128,7 +1166,7 @@
#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_toomany "too many %ss"
#define Tf_sd "%s %d"
#define Tf_s "%s"
#define Tft_end "%;"
@@ -1247,7 +1285,7 @@
/*
* fast character classes
*/
-#define C_ALPHA BIT(0) /* a-z_A-Z */
+#define C_ALPHX BIT(0) /* A-Za-z_ */
#define C_DIGIT BIT(1) /* 0-9 */
#define C_LEX1 BIT(2) /* \t \n\0|&;<>() */
#define C_VAR1 BIT(3) /* *@#!$-? */
@@ -1255,17 +1293,19 @@
#define C_SUBOP1 BIT(5) /* "=-+?" */
#define C_QUOTE BIT(6) /* \t\n "#$&'()*;<=>?[\]`| (needing quoting) */
#define C_IFS BIT(7) /* $IFS */
-#define C_SUBOP2 BIT(8) /* "#%" (magic, see below) */
extern unsigned char chtypes[];
-#define ctype(c, t) tobool( ((t) == C_SUBOP2) ? \
- (((c) == '#' || (c) == '%') ? 1 : 0) : \
- (chtypes[(unsigned char)(c)] & (t)) )
+#define ctype(c, t) tobool(chtypes[(unsigned char)(c)] & (t))
#define ord(c) ((int)(unsigned char)(c))
-#define ksh_isalphx(c) ctype((c), C_ALPHA)
-#define ksh_isalnux(c) ctype((c), C_ALPHA | C_DIGIT)
-#define ksh_isdigit(c) (((c) >= '0') && ((c) <= '9'))
+#define ksh_issubop2(c) tobool((c) == ord('#') || (c) == ord('%'))
+#define ksh_isalias(c) (ctype((c), C_ALPHX | C_DIGIT) || (c) == ord('!') || \
+ (c) == ord('%') || (c) == ord(',') || \
+ (c) == ord('@') || (c) == ord('-'))
+#define ksh_isalpha(c) (ctype((c), C_ALPHX) && (c) != ord('_'))
+#define ksh_isalphx(c) ctype((c), C_ALPHX)
+#define ksh_isalnux(c) ctype((c), C_ALPHX | C_DIGIT)
+#define ksh_isdigit(c) ctype((c), C_DIGIT)
#define ksh_islower(c) (((c) >= 'a') && ((c) <= 'z'))
#define ksh_isupper(c) (((c) >= 'A') && ((c) <= 'Z'))
#define ksh_tolower(c) (ksh_isupper(c) ? (c) - 'A' + 'a' : (c))
@@ -1330,7 +1370,7 @@
/* name of called builtin function (used by error functions) */
EXTERN const char *builtin_argv0;
-/* is called builtin SPEC_BI? (also KEEPASN, odd use though) */
+/* is called builtin a POSIX special builtin? (error functions only) */
EXTERN bool builtin_spec;
/* current working directory */
@@ -1463,7 +1503,7 @@
};
EXTERN struct tbl *vtemp;
-/* set by global() and local() */
+/* set by isglobal(), global() and local() */
EXTERN bool last_lookup_was_array;
/* common flag bits */
@@ -1500,6 +1540,8 @@
#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 */
+#define DECL_UTIL BIT(15) /* is declaration utility */
+#define DECL_FWDR BIT(16) /* is declaration utility forwarder */
/*
* Attributes that can be set by the user (used to decide if an unset
@@ -1672,6 +1714,8 @@
#define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */
#define FUNSUB 14 /* ${ foo;} substitution (NUL terminated) */
#define VALSUB 15 /* ${|foo;} substitution (NUL terminated) */
+#define COMASUB 16 /* `…` substitution (COMSUB but expand aliases) */
+#define FUNASUB 17 /* function substitution but expand aliases */
/*
* IO redirection
@@ -2019,7 +2063,8 @@
char *do_tilde(char *);
/* exec.c */
int execute(struct op * volatile, volatile int, volatile int * volatile);
-int shcomexec(const char **);
+int c_builtin(const char **);
+struct tbl *get_builtin(const char *);
struct tbl *findfunc(const char *, uint32_t, bool);
int define(const char *, struct op *);
const char *builtin(const char *, int (*)(const char **));
@@ -2057,6 +2102,7 @@
int c_whence(const char **);
int c_command(const char **);
int c_typeset(const char **);
+bool valid_alias_name(const char *);
int c_alias(const char **);
int c_unalias(const char **);
int c_let(const char **);
@@ -2086,8 +2132,6 @@
int timex(struct op *, int, volatile int *);
void timex_hook(struct op *, char ** volatile *);
int c_exec(const char **);
-/* dummy function (just need pointer value), special case in comexec() */
-#define c_builtin shcomexec
int c_test(const char **);
#if HAVE_MKNOD
int c_mknod(const char **);
@@ -2170,7 +2214,7 @@
/* main.c */
int include(const char *, int, const char **, bool);
int command(const char *, int);
-int shell(Source * volatile, volatile bool);
+int shell(Source * volatile, volatile int);
/* argument MUST NOT be 0 */
void unwind(int) MKSH_A_NORETURN;
void newenv(int);
@@ -2262,6 +2306,14 @@
char *strndup_i(const char *, size_t, Area *);
#endif
int unbksl(bool, int (*)(void), void (*)(int));
+#ifdef __OS2__
+/* os2.c */
+void os2_init(int *, const char ***);
+void setextlibpath(const char *, const char *);
+int access_ex(int (*)(const char *, int), const char *, int);
+int stat_ex(const char *, struct stat *);
+const char *real_exec_name(const char *);
+#endif
/* shf.c */
struct shf *shf_open(const char *, int, int, int);
struct shf *shf_fdopen(int, int, struct shf *);
@@ -2295,9 +2347,8 @@
ssize_t shf_vfprintf(struct shf *, const char *, va_list)
MKSH_A_FORMAT(__printf__, 2, 0);
/* syn.c */
-int assign_command(const char *, bool) MKSH_A_PURE;
void initkeywords(void);
-struct op *compile(Source *, bool);
+struct op *compile(Source *, bool, bool);
bool parse_usec(const char *, struct timeval *);
char *yyrecursive(int);
void yyrecursive_pop(bool);
@@ -2323,6 +2374,7 @@
void initvar(void);
struct block *varsearch(struct block *, struct tbl **, const char *, uint32_t);
struct tbl *global(const char *);
+struct tbl *isglobal(const char *, bool);
struct tbl *local(const char *, bool);
char *str_val(struct tbl *);
int setstr(struct tbl *, const char *, int);
@@ -2352,7 +2404,7 @@
/* non-operator */
TO_NONOP = 0,
/* unary operators */
- TO_STNZE, TO_STZER, TO_OPTION,
+ TO_STNZE, TO_STZER, TO_ISSET, TO_OPTION,
TO_FILAXST,
TO_FILEXST,
TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
@@ -2410,9 +2462,6 @@
extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */
#ifdef __OS2__
-#ifndef __GNUC__
-# error oops?
-#endif
#define binopen2(path,flags) __extension__({ \
int binopen2_fd = open((path), (flags) | O_BINARY); \
if (binopen2_fd >= 0) \
@@ -2425,24 +2474,31 @@
setmode(binopen3_fd, O_BINARY); \
(binopen3_fd); \
})
+#else
+#define binopen2(path,flags) open((path), (flags) | O_BINARY)
+#define binopen3(path,flags,mode) open((path), (flags) | O_BINARY, (mode))
+#endif
+
+#ifdef MKSH_DOSPATH
#define mksh_abspath(s) __extension__({ \
const char *mksh_abspath_s = (s); \
(mksh_cdirsep(mksh_abspath_s[0]) || \
- (ksh_isalphx(mksh_abspath_s[0]) && \
+ (ksh_isalpha(mksh_abspath_s[0]) && \
mksh_abspath_s[1] == ':')); \
})
#define mksh_cdirsep(c) __extension__({ \
char mksh_cdirsep_c = (c); \
(mksh_cdirsep_c == '/' || mksh_cdirsep_c == '\\'); \
})
-/*
- * I've seen mksh_sdirsep(s) and mksh_vdirsep(s) but need to think
- * more about the OS/2 port (and, possibly, toy with it) before I
- * can merge this upstream, but good job so far @komh, thanks!
- */
+#define mksh_sdirsep(s) __extension__({ \
+ const char *mksh_sdirsep_s = (s); \
+ ((char *)((ksh_isalphx(mksh_sdirsep_s[0]) && \
+ mksh_sdirsep_s[1] == ':' && \
+ !mksh_cdirsep(mksh_sdirsep_s[2])) ? \
+ (mksh_sdirsep_s + 1) : strpbrk(mksh_sdirsep_s, "/\\"))); \
+})
+#define mksh_vdirsep(s) (mksh_sdirsep((s)) != NULL)
#else
-#define binopen2(path,flags) open((path), (flags) | O_BINARY)
-#define binopen3(path,flags,mode) open((path), (flags) | O_BINARY, (mode))
#define mksh_abspath(s) ((s)[0] == '/')
#define mksh_cdirsep(c) ((c) == '/')
#define mksh_sdirsep(s) strchr((s), '/')
diff --git a/src/sh_flags.gen b/src/sh_flags.gen
index bcbc729..24b3359 100644
--- a/src/sh_flags.gen
+++ b/src/sh_flags.gen
@@ -1,6 +1,6 @@
/* +++ GENERATED FILE +++ DO NOT EDIT +++ */
/*-
- * Copyright (c) 2013, 2014, 2015
+ * Copyright (c) 2013, 2014, 2015, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -21,7 +21,7 @@
#ifndef SHFLAGS_OPTCS
#if defined(SHFLAGS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.4 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $");
#elif defined(SHFLAGS_ENUMS)
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
@@ -36,11 +36,11 @@
FN("bgnice", FBGNICE, OF_ANY, 0)
#endif
FN("braceexpand", FBRACEEXPAND, OF_ANY, 0)
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("emacs", FEMACS, OF_ANY, 0)
#endif
FN("errexit", FERREXIT, OF_ANY, 'e')
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("gmacs", FGMACS, OF_ANY, 0)
#endif
FN("ignoreeof", FIGNOREEOF, OF_ANY, 0)
@@ -79,16 +79,16 @@
FN("trackall", FTRACKALL, OF_ANY, 'h')
FN("utf8-mode", FUNICODE, OF_ANY, 'U')
FN("verbose", FVERBOSE, OF_ANY, 'v')
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("vi", FVI, OF_ANY, 0)
#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY, 0)
#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY, 0)
#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+#ifndef MKSH_NO_CMDLINE_EDITING
FN("viraw", FVIRAW, OF_ANY, 0)
#endif
FN("xtrace", FXTRACE, OF_ANY, 'x')
diff --git a/src/sh_flags.opt b/src/sh_flags.opt
index 9f364d5..795d198 100644
--- a/src/sh_flags.opt
+++ b/src/sh_flags.opt
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2013, 2014, 2015
+ * Copyright (c) 2013, 2014, 2015, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -19,7 +19,7 @@
*/
@SHFLAGS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.4 2015/12/12 21:08:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $");
@SHFLAGS_ENUMS
#define FN(sname,cname,flags,ochar) cname,
#define F0(sname,cname,flags,ochar) cname = 0,
@@ -52,7 +52,7 @@
FN("braceexpand", FBRACEEXPAND, OF_ANY
/* ./. Emacs command line editing mode */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("emacs", FEMACS, OF_ANY
/* -e quit on error */
@@ -60,7 +60,7 @@
FN("errexit", FERREXIT, OF_ANY
/* ./. Emacs command line editing mode, gmacs variant */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("gmacs", FGMACS, OF_ANY
/* ./. reading EOF does not exit */
@@ -160,19 +160,19 @@
FN("verbose", FVERBOSE, OF_ANY
/* ./. Vi command line editing mode */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("vi", FVI, OF_ANY
/* ./. enable ESC as file name completion character (non-standard) */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY
/* ./. enable Tab as file name completion character (non-standard) */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY
/* ./. always read in raw mode (no effect) */
->|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+>|!MKSH_NO_CMDLINE_EDITING
FN("viraw", FVIRAW, OF_ANY
/* -x execution trace (display commands as they are run) */
diff --git a/src/shf.c b/src/shf.c
index ace0ab6..09cc7c3 100644
--- a/src/shf.c
+++ b/src/shf.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- * 2012, 2013, 2015, 2016
+ * 2012, 2013, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -25,7 +25,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.76 2016/07/25 00:04:47 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.79 2017/04/12 17:08:49 tg Exp $");
/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
@@ -289,29 +289,31 @@
int
shf_flush(struct shf *shf)
{
+ int rv = 0;
+
if (shf->flags & SHF_STRING)
- return ((shf->flags & SHF_WR) ? -1 : 0);
-
- if (shf->fd < 0)
+ rv = (shf->flags & SHF_WR) ? -1 : 0;
+ else if (shf->fd < 0)
internal_errorf(Tf_sD_s, "shf_flush", "no fd");
-
- if (shf->flags & SHF_ERROR) {
+ else if (shf->flags & SHF_ERROR) {
errno = shf->errnosv;
- return (-1);
- }
-
- if (shf->flags & SHF_READING) {
+ rv = -1;
+ } else if (shf->flags & SHF_READING) {
shf->flags &= ~(SHF_EOF | SHF_READING);
if (shf->rnleft > 0) {
- lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR);
+ if (lseek(shf->fd, (off_t)-shf->rnleft,
+ SEEK_CUR) == -1) {
+ shf->flags |= SHF_ERROR;
+ shf->errnosv = errno;
+ rv = -1;
+ }
shf->rnleft = 0;
shf->rp = shf->buf;
}
- return (0);
} else if (shf->flags & SHF_WRITING)
- return (shf_emptybuf(shf, 0));
+ rv = shf_emptybuf(shf, 0);
- return (0);
+ return (rv);
}
/*
@@ -518,7 +520,23 @@
shf->rnleft -= ncopy;
buf += ncopy;
bsize -= ncopy;
+#ifdef MKSH_WITH_TEXTMODE
+ if (end && buf > orig_buf + 1 && buf[-2] == '\r') {
+ buf--;
+ bsize++;
+ buf[-1] = '\n';
+ }
+#endif
} while (!end && bsize);
+#ifdef MKSH_WITH_TEXTMODE
+ if (!bsize && buf[-1] == '\r') {
+ int c = shf_getc(shf);
+ if (c == '\n')
+ buf[-1] = '\n';
+ else if (c != -1)
+ shf_ungetc(c, shf);
+ }
+#endif
*buf = '\0';
return (buf);
}
diff --git a/src/syn.c b/src/syn.c
index 87b7c75..0454488 100644
--- a/src/syn.c
+++ b/src/syn.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.115 2016/09/01 12:59:12 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.120 2017/04/06 01:59:57 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@@ -35,25 +35,24 @@
struct yyrecursive_state *next;
struct ioword **old_herep;
int old_symbol;
- int old_salias;
int old_nesting_type;
bool old_reject;
};
-static void yyparse(void);
-static struct op *pipeline(int);
-static struct op *andor(void);
-static struct op *c_list(bool);
+static void yyparse(bool);
+static struct op *pipeline(int, int);
+static struct op *andor(int);
+static struct op *c_list(int, bool);
static struct ioword *synio(int);
-static struct op *nested(int, int, int);
-static struct op *get_command(int);
-static struct op *dogroup(void);
-static struct op *thenpart(void);
-static struct op *elsepart(void);
-static struct op *caselist(void);
-static struct op *casepart(int);
-static struct op *function_body(char *, bool);
-static char **wordlist(void);
+static struct op *nested(int, int, int, int);
+static struct op *get_command(int, int);
+static struct op *dogroup(int);
+static struct op *thenpart(int);
+static struct op *elsepart(int);
+static struct op *caselist(int);
+static struct op *casepart(int, int);
+static struct op *function_body(char *, int, bool);
+static char **wordlist(int);
static struct op *block(int, struct op *, struct op *);
static struct op *newtp(int);
static void syntaxerr(const char *) MKSH_A_NORETURN;
@@ -71,7 +70,6 @@
static bool reject; /* token(cf) gets symbol again */
static int symbol; /* yylex value */
-static int sALIAS = ALIAS; /* 0 in yyrecursive */
#define REJECT (reject = true)
#define ACCEPT (reject = false)
@@ -83,13 +81,13 @@
static const char Tesac[] = "esac";
static void
-yyparse(void)
+yyparse(bool doalias)
{
int c;
ACCEPT;
- outtree = c_list(source->type == SSTRING);
+ outtree = c_list(doalias ? ALIAS : 0, source->type == SSTRING);
c = tpeek(0);
if (c == 0 && !outtree)
outtree = newtp(TEOF);
@@ -98,14 +96,14 @@
}
static struct op *
-pipeline(int cf)
+pipeline(int cf, int sALIAS)
{
struct op *t, *p, *tl = NULL;
- t = get_command(cf);
+ t = get_command(cf, sALIAS);
if (t != NULL) {
while (token(0) == '|') {
- if ((p = get_command(CONTIN)) == NULL)
+ if ((p = get_command(CONTIN, sALIAS)) == NULL)
syntaxerr(NULL);
if (tl == NULL)
t = tl = block(TPIPE, t, p);
@@ -118,15 +116,15 @@
}
static struct op *
-andor(void)
+andor(int sALIAS)
{
struct op *t, *p;
int c;
- t = pipeline(0);
+ t = pipeline(0, sALIAS);
if (t != NULL) {
while ((c = token(0)) == LOGAND || c == LOGOR) {
- if ((p = pipeline(CONTIN)) == NULL)
+ if ((p = pipeline(CONTIN, sALIAS)) == NULL)
syntaxerr(NULL);
t = block(c == LOGAND? TAND: TOR, t, p);
}
@@ -136,14 +134,14 @@
}
static struct op *
-c_list(bool multi)
+c_list(int sALIAS, bool multi)
{
struct op *t = NULL, *p, *tl = NULL;
int c;
bool have_sep;
while (/* CONSTCOND */ 1) {
- p = andor();
+ p = andor(sALIAS);
/*
* Token has always been read/rejected at this point, so
* we don't worry about what flags to pass token()
@@ -232,23 +230,27 @@
}
static struct op *
-nested(int type, int smark, int emark)
+nested(int type, int smark, int emark, int sALIAS)
{
struct op *t;
struct nesting_state old_nesting;
nesting_push(&old_nesting, smark);
- t = c_list(true);
+ t = c_list(sALIAS, true);
musthave(emark, KEYWORD|sALIAS);
nesting_pop(&old_nesting);
return (block(type, t, NULL));
}
+static const char builtin_cmd[] = {
+ QCHAR, '\\', CHAR, 'b', CHAR, 'u', CHAR, 'i',
+ CHAR, 'l', CHAR, 't', CHAR, 'i', CHAR, 'n', EOS
+};
static const char let_cmd[] = {
- QCHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS
+ CHAR, 'l', CHAR, 'e', CHAR, 't', EOS
};
static const char setA_cmd0[] = {
- QCHAR, 's', CHAR, 'e', CHAR, 't', EOS
+ CHAR, 's', CHAR, 'e', CHAR, 't', EOS
};
static const char setA_cmd1[] = {
CHAR, '-', CHAR, 'A', EOS
@@ -258,7 +260,7 @@
};
static struct op *
-get_command(int cf)
+get_command(int cf, int sALIAS)
{
struct op *t;
int c, iopn = 0, syniocf, lno;
@@ -289,11 +291,11 @@
t->lineno = source->line;
goto get_command_start;
while (/* CONSTCOND */ 1) {
- bool check_assign_cmd;
+ bool check_decl_utility;
if (XPsize(args) == 0) {
get_command_start:
- check_assign_cmd = true;
+ check_decl_utility = true;
cf = sALIAS | CMDASN;
} else if (t->u.evalflags)
cf = CMDWORD | CMDASN;
@@ -311,16 +313,15 @@
case LWORD:
ACCEPT;
- /*
- * the iopn == 0 and XPsize(vars) == 0 are
- * dubious but AT&T ksh acts this way
- */
- if (iopn == 0 && XPsize(vars) == 0 &&
- check_assign_cmd) {
- if (assign_command(ident, false))
+ if (check_decl_utility) {
+ struct tbl *tt = get_builtin(ident);
+ uint32_t flag;
+
+ flag = tt ? tt->flag : 0;
+ if (flag & DECL_UTIL)
t->u.evalflags = DOVACHECK;
- else if (strcmp(ident, Tcommand) != 0)
- check_assign_cmd = false;
+ if (!(flag & DECL_FWDR))
+ check_decl_utility = false;
}
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
is_wdvarassign(yylval.cp))
@@ -343,6 +344,7 @@
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
/* construct new args strings */
+ XPput(args, wdcopy(builtin_cmd, ATEMP));
XPput(args, wdcopy(setA_cmd0, ATEMP));
XPput(args, wdcopy(setA_cmd1, ATEMP));
XPput(args, tcp);
@@ -372,7 +374,8 @@
syntaxerr(NULL);
ACCEPT;
musthave(/*(*/')', 0);
- t = function_body(XPptrv(args)[0], false);
+ t = function_body(XPptrv(args)[0],
+ sALIAS, false);
}
goto Leave;
@@ -388,13 +391,13 @@
Subshell:
subshell_nesting_type_saved = subshell_nesting_type;
subshell_nesting_type = ')';
- t = nested(TPAREN, '(', ')');
+ t = nested(TPAREN, '(', ')', sALIAS);
subshell_nesting_type = subshell_nesting_type_saved;
break;
}
case '{': /*}*/
- t = nested(TBRACE, '{', '}');
+ t = nested(TBRACE, '{', '}', sALIAS);
break;
case MDPAREN:
@@ -412,6 +415,7 @@
}
t = newtp(TCOM);
t->lineno = lno;
+ XPput(args, wdcopy(builtin_cmd, ATEMP));
XPput(args, wdcopy(let_cmd, ATEMP));
XPput(args, yylval.cp);
break;
@@ -439,12 +443,12 @@
t = newtp((c == FOR) ? TFOR : TSELECT);
musthave(LWORD, CMDASN);
if (!is_wdvarname(yylval.cp, true))
- yyerror("%s: bad identifier\n",
+ yyerror("%s: bad identifier",
c == FOR ? "for" : Tselect);
strdupx(t->str, ident, ATEMP);
nesting_push(&old_nesting, c);
- t->vars = wordlist();
- t->left = dogroup();
+ t->vars = wordlist(sALIAS);
+ t->left = dogroup(sALIAS);
nesting_pop(&old_nesting);
break;
@@ -452,8 +456,8 @@
case UNTIL:
nesting_push(&old_nesting, c);
t = newtp((c == WHILE) ? TWHILE : TUNTIL);
- t->left = c_list(true);
- t->right = dogroup();
+ t->left = c_list(sALIAS, true);
+ t->right = dogroup(sALIAS);
nesting_pop(&old_nesting);
break;
@@ -462,22 +466,22 @@
musthave(LWORD, 0);
t->str = yylval.cp;
nesting_push(&old_nesting, c);
- t->left = caselist();
+ t->left = caselist(sALIAS);
nesting_pop(&old_nesting);
break;
case IF:
nesting_push(&old_nesting, c);
t = newtp(TIF);
- t->left = c_list(true);
- t->right = thenpart();
+ t->left = c_list(sALIAS, true);
+ t->right = thenpart(sALIAS);
musthave(FI, KEYWORD|sALIAS);
nesting_pop(&old_nesting);
break;
case BANG:
syniocf &= ~(KEYWORD|sALIAS);
- t = pipeline(0);
+ t = pipeline(0, sALIAS);
if (t == NULL)
syntaxerr(NULL);
t = block(TBANG, NULL, t);
@@ -485,7 +489,7 @@
case TIME:
syniocf &= ~(KEYWORD|sALIAS);
- t = pipeline(0);
+ t = pipeline(0, sALIAS);
if (t && t->type == TCOM) {
t->str = alloc(2, ATEMP);
/* TF_* flags */
@@ -497,7 +501,7 @@
case FUNCTION:
musthave(LWORD, 0);
- t = function_body(yylval.cp, true);
+ t = function_body(yylval.cp, sALIAS, true);
break;
}
@@ -536,7 +540,7 @@
}
static struct op *
-dogroup(void)
+dogroup(int sALIAS)
{
int c;
struct op *list;
@@ -554,40 +558,40 @@
c = '}';
else
syntaxerr(NULL);
- list = c_list(true);
+ list = c_list(sALIAS, true);
musthave(c, KEYWORD|sALIAS);
return (list);
}
static struct op *
-thenpart(void)
+thenpart(int sALIAS)
{
struct op *t;
musthave(THEN, KEYWORD|sALIAS);
t = newtp(0);
- t->left = c_list(true);
+ t->left = c_list(sALIAS, true);
if (t->left == NULL)
syntaxerr(NULL);
- t->right = elsepart();
+ t->right = elsepart(sALIAS);
return (t);
}
static struct op *
-elsepart(void)
+elsepart(int sALIAS)
{
struct op *t;
switch (token(KEYWORD|sALIAS|CMDASN)) {
case ELSE:
- if ((t = c_list(true)) == NULL)
+ if ((t = c_list(sALIAS, true)) == NULL)
syntaxerr(NULL);
return (t);
case ELIF:
t = newtp(TELIF);
- t->left = c_list(true);
- t->right = thenpart();
+ t->left = c_list(sALIAS, true);
+ t->right = thenpart(sALIAS);
return (t);
default:
@@ -597,7 +601,7 @@
}
static struct op *
-caselist(void)
+caselist(int sALIAS)
{
struct op *t, *tl;
int c;
@@ -613,7 +617,7 @@
t = tl = NULL;
/* no ALIAS here */
while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) {
- struct op *tc = casepart(c);
+ struct op *tc = casepart(c, sALIAS);
if (tl == NULL)
t = tl = tc, tl->right = NULL;
else
@@ -624,7 +628,7 @@
}
static struct op *
-casepart(int endtok)
+casepart(int endtok, int sALIAS)
{
struct op *t;
XPtrV ptns;
@@ -656,7 +660,7 @@
t->vars = (char **)XPclose(ptns);
musthave(')', 0);
- t->left = c_list(true);
+ t->left = c_list(sALIAS, true);
/* initialise to default for ;; or omitted */
t->u.charflag = ';';
@@ -680,7 +684,7 @@
}
static struct op *
-function_body(char *name,
+function_body(char *name, int sALIAS,
/* function foo { ... } vs foo() { .. } */
bool ksh_func)
{
@@ -697,7 +701,7 @@
*/
for (p = sname; *p; p++)
if (ctype(*p, C_QUOTE))
- yyerror("%s: invalid function name\n", sname);
+ yyerror(Tinvname, sname, Tfunction);
/*
* Note that POSIX allows only compound statements after foo(),
@@ -722,7 +726,7 @@
t->u.ksh_func = tobool(ksh_func);
t->lineno = source->line;
- if ((t->left = get_command(CONTIN)) == NULL) {
+ if ((t->left = get_command(CONTIN, sALIAS)) == NULL) {
char *tv;
/*
* Probably something like foo() followed by EOF or ';'.
@@ -747,7 +751,7 @@
}
static char **
-wordlist(void)
+wordlist(int sALIAS)
{
int c;
XPtrV args;
@@ -864,7 +868,7 @@
goto Again;
}
/* don't quote the EOF */
- yyerror("%s: unexpected EOF\n", Tsynerr);
+ yyerror("%s: unexpected EOF", Tsynerr);
/* NOTREACHED */
case LWORD:
@@ -891,7 +895,7 @@
s = redir;
}
}
- yyerror("%s: '%s' %s\n", Tsynerr, s, what);
+ yyerror(Tf_sD_s_qs, Tsynerr, what, s);
}
static void
@@ -925,7 +929,7 @@
}
struct op *
-compile(Source *s, bool skiputf8bom)
+compile(Source *s, bool skiputf8bom, bool doalias)
{
nesting.start_token = 0;
nesting.start_line = 0;
@@ -933,33 +937,10 @@
source = s;
if (skiputf8bom)
yyskiputf8bom();
- yyparse();
+ yyparse(doalias);
return (outtree);
}
-/*-
- * This kludge exists to take care of sh/AT&T ksh oddity in which
- * the arguments of alias/export/readonly/typeset have no field
- * splitting, file globbing, or (normal) tilde expansion done.
- * AT&T ksh seems to do something similar to this since
- * $ touch a=a; typeset a=[ab]; echo "$a"
- * a=[ab]
- * $ x=typeset; $x a=[ab]; echo "$a"
- * a=a
- * $
- */
-int
-assign_command(const char *s, bool docommand)
-{
- if (!*s)
- return (0);
- return ((strcmp(s, Talias) == 0) ||
- (strcmp(s, Texport) == 0) ||
- (strcmp(s, Treadonly) == 0) ||
- (docommand && (strcmp(s, Tcommand) == 0)) ||
- (strcmp(s, Ttypeset) == 0));
-}
-
/* Check if we are in the middle of reading an alias */
static int
inalias(struct source *s)
@@ -1144,7 +1125,7 @@
* a COMSUB recursively using the main shell parser and lexer
*/
char *
-yyrecursive(int subtype MKSH_A_UNUSED)
+yyrecursive(int subtype)
{
struct op *t;
char *cp;
@@ -1172,12 +1153,10 @@
memcpy(ys->old_heres, heres, sizeof(heres));
ys->old_herep = herep;
herep = heres;
- ys->old_salias = sALIAS;
- sALIAS = 0;
ys->next = e->yyrecursive_statep;
e->yyrecursive_statep = ys;
/* we use TPAREN as a helper container here */
- t = nested(TPAREN, stok, etok);
+ t = nested(TPAREN, stok, etok, ALIAS);
yyrecursive_pop(false);
/* t->left because nested(TPAREN, ...) hides our goodies there */
@@ -1197,7 +1176,6 @@
return;
e->yyrecursive_statep = ys->next;
- sALIAS = ys->old_salias;
memcpy(heres, ys->old_heres, sizeof(heres));
herep = ys->old_herep;
reject = ys->old_reject;
diff --git a/src/tree.c b/src/tree.c
index ff15077..1fd8f2a 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2015, 2016
+ * 2011, 2012, 2013, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.86 2016/07/25 00:04:48 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.89 2017/04/12 16:46:23 tg Exp $");
#define INDENT 8
@@ -58,7 +58,7 @@
case TCOM:
prevent_semicolon = false;
/* special-case 'var=<<EOF' (cf. exec.c:execute) */
- if (
+ if (t->args &&
/* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
@@ -86,6 +86,15 @@
shf_puts("#no-vars# ", shf);
if (t->args) {
w = t->args;
+ if (*w && **w == CHAR) {
+ char *cp = wdstrip(*w++, WDS_TPUTS);
+
+ if (valid_alias_name(cp))
+ shf_putc('\\', shf);
+ shf_puts(cp, shf);
+ shf_putc(' ', shf);
+ afree(cp, ATEMP);
+ }
while (*w)
fptreef(shf, indent, Tf_S_, *w++);
} else
@@ -352,14 +361,18 @@
}
shf_putc(c, shf);
break;
+ case COMASUB:
case COMSUB:
shf_puts("$(", shf);
cs = ")";
+ if (*wp == '(' /*)*/)
+ shf_putc(' ', shf);
pSUB:
while ((c = *wp++) != 0)
shf_putc(c, shf);
shf_puts(cs, shf);
break;
+ case FUNASUB:
case FUNSUB:
c = ' ';
if (0)
@@ -409,8 +422,9 @@
case SPAT:
c = '|';
if (0)
+ /* FALLTHROUGH */
case CPAT:
- c = /*(*/ ')';
+ c = /*(*/ ')';
shf_putc(c, shf);
break;
}
@@ -606,7 +620,9 @@
case QCHAR:
wp++;
break;
+ case COMASUB:
case COMSUB:
+ case FUNASUB:
case FUNSUB:
case VALSUB:
case EXPRSUB:
@@ -832,8 +848,9 @@
}
shf_puts("ADELIM=", shf);
if (0)
+ /* FALLTHROUGH */
case CHAR:
- shf_puts("CHAR=", shf);
+ shf_puts("CHAR=", shf);
dumpchar(shf, *wp++);
break;
case QCHAR:
@@ -844,6 +861,9 @@
shf_putc('\\', shf);
dumpchar(shf, c);
goto closeandout;
+ case COMASUB:
+ shf_puts("COMASUB<", shf);
+ goto dumpsub;
case COMSUB:
shf_puts("COMSUB<", shf);
dumpsub:
@@ -852,6 +872,9 @@
closeandout:
shf_putc('>', shf);
break;
+ case FUNASUB:
+ shf_puts("FUNASUB<", shf);
+ goto dumpsub;
case FUNSUB:
shf_puts("FUNSUB<", shf);
goto dumpsub;
diff --git a/src/var.c b/src/var.c
index 64de0f9..b83977f 100644
--- a/src/var.c
+++ b/src/var.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- * 2011, 2012, 2013, 2014, 2015, 2016
+ * 2011, 2012, 2013, 2014, 2015, 2016, 2017
* mirabilos <m@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +28,7 @@
#include <sys/sysctl.h>
#endif
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.209 2016/11/11 23:31:39 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.214 2017/04/02 16:47:43 tg Exp $");
/*-
* Variables
@@ -45,6 +45,9 @@
/* may only be set by typeset() just before call to array_index_calc() */
static enum namerefflag innermost_refflag = SRF_NOP;
+static void c_typeset_vardump(struct tbl *, uint32_t, int, int, bool, bool);
+static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
+ bool);
static char *formatstr(struct tbl *, const char *);
static void exportprep(struct tbl *, const char *);
static int special(const char *);
@@ -225,6 +228,13 @@
struct tbl *
global(const char *n)
{
+ return (isglobal(n, true));
+}
+
+/* search for variable; if not found, return NULL or create globally */
+struct tbl *
+isglobal(const char *n, bool docreate)
+{
struct tbl *vp;
union mksh_cchack vname;
struct block *l = e->loc;
@@ -291,17 +301,19 @@
goto out;
}
l = varsearch(e->loc, &vp, vn, h);
+ if (vp == NULL && docreate)
+ vp = ktenter(&l->vars, vn, h);
+ else
+ docreate = false;
if (vp != NULL) {
if (array)
vp = arraysearch(vp, val);
- goto out;
+ if (docreate) {
+ vp->flag |= DEFINED;
+ if (special(vn))
+ vp->flag |= SPECIAL;
+ }
}
- vp = ktenter(&l->vars, vn, h);
- if (array)
- vp = arraysearch(vp, val);
- vp->flag |= DEFINED;
- if (special(vn))
- vp->flag |= SPECIAL;
out:
last_lookup_was_array = array;
if (vn != n)
@@ -1053,8 +1065,9 @@
size_t alen;
if (s && ksh_isalphx(*s)) {
- while (*++s && ksh_isalnux(*s))
- ;
+ do {
+ ++s;
+ } while (ksh_isalnux(*s));
if (aok && *s == '[' && (alen = array_ref_len(s)))
s += alen;
}
@@ -1346,7 +1359,7 @@
if (getint(vp, &num, false) == -1) {
s = str_val(vp);
if (st != V_RANDOM)
- errorf(Tf_sD_sD_s, vp->name, "bad number", s);
+ errorf(Tf_sD_sD_s, vp->name, Tbadnum, s);
num.u = hash(s);
}
vp->flag |= SPECIAL;
@@ -1770,3 +1783,381 @@
vp->flag = DEFINED | RDONLY;
setstr(vp, istr, 0x4);
}
+
+/* typeset, global(deprecated), export, and readonly */
+int
+c_typeset(const char **wp)
+{
+ struct tbl *vp, **p;
+ uint32_t fset = 0, fclr = 0, flag;
+ int thing = 0, field = 0, base = 0, i;
+ struct block *l;
+ const char *opts;
+ const char *fieldstr = NULL, *basestr = NULL;
+ bool localv = false, func = false, pflag = false, istset = true;
+ enum namerefflag new_refflag = SRF_NOP;
+
+ switch (**wp) {
+
+ /* export */
+ case 'e':
+ fset |= EXPORT;
+ istset = false;
+ break;
+
+ /* readonly */
+ case 'r':
+ fset |= RDONLY;
+ istset = false;
+ break;
+
+ /* set */
+ case 's':
+ /* called with 'typeset -' */
+ break;
+
+ /* typeset */
+ case 't':
+ localv = true;
+ break;
+ }
+
+ /* see comment below regarding possible opions */
+ opts = istset ? "L#R#UZ#afgi#lnprtux" : "p";
+
+ builtin_opt.flags |= GF_PLUSOPT;
+ /*
+ * AT&T ksh seems to have 0-9 as options which are multiplied
+ * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
+ * sets right justify in a field of 12). This allows options
+ * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
+ * does not allow the number to be specified as a separate argument
+ * Here, the number must follow the RLZi option, but is optional
+ * (see the # kludge in ksh_getopt()).
+ */
+ while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
+ flag = 0;
+ switch (i) {
+ case 'L':
+ flag = LJUST;
+ fieldstr = builtin_opt.optarg;
+ break;
+ case 'R':
+ flag = RJUST;
+ fieldstr = builtin_opt.optarg;
+ break;
+ case 'U':
+ /*
+ * AT&T ksh uses u, but this conflicts with
+ * upper/lower case. If this option is changed,
+ * need to change the -U below as well
+ */
+ flag = INT_U;
+ break;
+ case 'Z':
+ flag = ZEROFIL;
+ fieldstr = builtin_opt.optarg;
+ break;
+ case 'a':
+ /*
+ * this is supposed to set (-a) or unset (+a) the
+ * indexed array attribute; it does nothing on an
+ * existing regular string or indexed array though
+ */
+ break;
+ case 'f':
+ func = true;
+ break;
+ case 'g':
+ localv = (builtin_opt.info & GI_PLUS) ? true : false;
+ break;
+ case 'i':
+ flag = INTEGER;
+ basestr = builtin_opt.optarg;
+ break;
+ case 'l':
+ flag = LCASEV;
+ break;
+ case 'n':
+ new_refflag = (builtin_opt.info & GI_PLUS) ?
+ SRF_DISABLE : SRF_ENABLE;
+ break;
+ /* export, readonly: POSIX -p flag */
+ case 'p':
+ /* typeset: show values as well */
+ pflag = true;
+ if (istset)
+ continue;
+ break;
+ case 'r':
+ flag = RDONLY;
+ break;
+ case 't':
+ flag = TRACE;
+ break;
+ case 'u':
+ /* upper case / autoload */
+ flag = UCASEV_AL;
+ break;
+ case 'x':
+ flag = EXPORT;
+ break;
+ case '?':
+ return (1);
+ }
+ if (builtin_opt.info & GI_PLUS) {
+ fclr |= flag;
+ fset &= ~flag;
+ thing = '+';
+ } else {
+ fset |= flag;
+ fclr &= ~flag;
+ thing = '-';
+ }
+ }
+
+ if (fieldstr && !getn(fieldstr, &field)) {
+ bi_errorf(Tf_sD_s, Tbadnum, fieldstr);
+ return (1);
+ }
+ if (basestr) {
+ if (!getn(basestr, &base)) {
+ bi_errorf(Tf_sD_s, "bad integer base", basestr);
+ return (1);
+ }
+ if (base < 1 || base > 36)
+ base = 10;
+ }
+
+ if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
+ (wp[builtin_opt.optind][0] == '-' ||
+ wp[builtin_opt.optind][0] == '+') &&
+ wp[builtin_opt.optind][1] == '\0') {
+ thing = wp[builtin_opt.optind][0];
+ builtin_opt.optind++;
+ }
+
+ if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
+ new_refflag != SRF_NOP)) {
+ bi_errorf("only -t, -u and -x options may be used with -f");
+ return (1);
+ }
+ if (wp[builtin_opt.optind]) {
+ /*
+ * Take care of exclusions.
+ * At this point, flags in fset are cleared in fclr and vice
+ * versa. This property should be preserved.
+ */
+ if (fset & LCASEV)
+ /* LCASEV has priority over UCASEV_AL */
+ fset &= ~UCASEV_AL;
+ if (fset & LJUST)
+ /* LJUST has priority over RJUST */
+ fset &= ~RJUST;
+ if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
+ /* -Z implies -ZR */
+ fset |= RJUST;
+ fclr &= ~RJUST;
+ }
+ /*
+ * Setting these attributes clears the others, unless they
+ * are also set in this command
+ */
+ if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
+ INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
+ fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
+ LCASEV | INTEGER | INT_U | INT_L);
+ }
+ if (new_refflag != SRF_NOP) {
+ fclr &= ~(ARRAY | ASSOC);
+ fset &= ~(ARRAY | ASSOC);
+ fclr |= EXPORT;
+ fset |= ASSOC;
+ if (new_refflag == SRF_DISABLE)
+ fclr |= ASSOC;
+ }
+
+ /* set variables and attributes */
+ if (wp[builtin_opt.optind] &&
+ /* not "typeset -p varname" */
+ !(!func && pflag && !(fset | fclr))) {
+ int rv = 0;
+ struct tbl *f;
+
+ if (localv && !func)
+ fset |= LOCAL;
+ for (i = builtin_opt.optind; wp[i]; i++) {
+ if (func) {
+ f = findfunc(wp[i], hash(wp[i]),
+ tobool(fset & UCASEV_AL));
+ if (!f) {
+ /* AT&T ksh does ++rv: bogus */
+ rv = 1;
+ continue;
+ }
+ if (fset | fclr) {
+ f->flag |= fset;
+ f->flag &= ~fclr;
+ } else {
+ fpFUNCTf(shl_stdout, 0,
+ tobool(f->flag & FKSH),
+ wp[i], f->val.t);
+ shf_putc('\n', shl_stdout);
+ }
+ } else if (!typeset(wp[i], fset, fclr, field, base)) {
+ bi_errorf(Tf_sD_s, wp[i], Tnot_ident);
+ return (1);
+ }
+ }
+ return (rv);
+ }
+
+ /* list variables and attributes */
+
+ /* no difference at this point.. */
+ flag = fset | fclr;
+ if (func) {
+ for (l = e->loc; l; l = l->next) {
+ for (p = ktsort(&l->funs); (vp = *p++); ) {
+ if (flag && (vp->flag & flag) == 0)
+ continue;
+ if (thing == '-')
+ fpFUNCTf(shl_stdout, 0,
+ tobool(vp->flag & FKSH),
+ vp->name, vp->val.t);
+ else
+ shf_puts(vp->name, shl_stdout);
+ shf_putc('\n', shl_stdout);
+ }
+ }
+ } else if (wp[builtin_opt.optind]) {
+ for (i = builtin_opt.optind; wp[i]; i++) {
+ vp = isglobal(wp[i], false);
+ c_typeset_vardump(vp, flag, thing,
+ last_lookup_was_array ? 4 : 0, pflag, istset);
+ }
+ } else
+ c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
+ return (0);
+}
+
+static void
+c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
+ bool pflag, bool istset)
+{
+ struct tbl **blockvars, *vp;
+
+ if (l->next)
+ c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
+ blockvars = ktsort(&l->vars);
+ while ((vp = *blockvars++))
+ c_typeset_vardump(vp, flag, thing, 0, pflag, istset);
+ /*XXX doesn’t this leak? */
+}
+
+static void
+c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set,
+ bool pflag, bool istset)
+{
+ struct tbl *tvp;
+ char *s;
+
+ if (!vp)
+ return;
+
+ /*
+ * See if the parameter is set (for arrays, if any
+ * element is set).
+ */
+ for (tvp = vp; tvp; tvp = tvp->u.array)
+ if (tvp->flag & ISSET) {
+ any_set |= 1;
+ break;
+ }
+
+ /*
+ * Check attributes - note that all array elements
+ * have (should have?) the same attributes, so checking
+ * the first is sufficient.
+ *
+ * Report an unset param only if the user has
+ * explicitly given it some attribute (like export);
+ * otherwise, after "echo $FOO", we would report FOO...
+ */
+ if (!any_set && !(vp->flag & USERATTRIB))
+ return;
+ if (flag && (vp->flag & flag) == 0)
+ return;
+ if (!(vp->flag & ARRAY))
+ /* optimise later conditionals */
+ any_set = 0;
+ do {
+ /*
+ * Ignore array elements that aren't set unless there
+ * are no set elements, in which case the first is
+ * reported on
+ */
+ if (any_set && !(vp->flag & ISSET))
+ continue;
+ /* no arguments */
+ if (!thing && !flag) {
+ if (any_set == 1) {
+ shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
+ any_set = 2;
+ }
+ /*
+ * AT&T ksh prints things like export, integer,
+ * leftadj, zerofill, etc., but POSIX says must
+ * be suitable for re-entry...
+ */
+ shprintf(Tf_s_s, Ttypeset, "");
+ if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
+ shprintf(Tf__c_, 'n');
+ if ((vp->flag & INTEGER))
+ shprintf(Tf__c_, 'i');
+ if ((vp->flag & EXPORT))
+ shprintf(Tf__c_, 'x');
+ if ((vp->flag & RDONLY))
+ shprintf(Tf__c_, 'r');
+ if ((vp->flag & TRACE))
+ 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(Tf__c_, 'Z');
+ if ((vp->flag & LCASEV))
+ shprintf(Tf__c_, 'l');
+ if ((vp->flag & UCASEV_AL))
+ shprintf(Tf__c_, 'u');
+ if ((vp->flag & INT_U))
+ shprintf(Tf__c_, 'U');
+ } else if (pflag) {
+ shprintf(Tf_s_s, istset ? Ttypeset :
+ (flag & EXPORT) ? Texport : Treadonly, "");
+ }
+ if (any_set)
+ shprintf("%s[%lu]", vp->name, arrayindex(vp));
+ else
+ shf_puts(vp->name, shl_stdout);
+ if ((!thing && !flag && pflag) ||
+ (thing == '-' && (vp->flag & ISSET))) {
+ s = str_val(vp);
+ shf_putc('=', shl_stdout);
+ /* AT&T ksh can't have justified integers... */
+ if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
+ shf_puts(s, shl_stdout);
+ else
+ print_value_quoted(shl_stdout, s);
+ }
+ shf_putc('\n', shl_stdout);
+
+ /*
+ * Only report first 'element' of an array with
+ * no set elements.
+ */
+ if (!any_set)
+ return;
+ } while (!(any_set & 4) && (vp = vp->u.array));
+}