Merge "Upgrade to mksh R55." am: 693c2ea1e4

am: e0d2df7f07

Change-Id: I2e1ef7d4911a211b3dc58e3cab8e6c6e0ad2778a
This commit is contained in:
Elliott Hughes 2017-04-14 06:00:07 +00:00 committed by android-build-merger
commit 0d2db5fba1
23 changed files with 3184 additions and 1964 deletions

View file

@ -83,6 +83,6 @@ LOCAL_CFLAGS += \
-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)

114
src/Build.sh Executable file → Normal file
View file

@ -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 @@ check_categories=
last=
tfn=
legacy=0
textmode=0
for i
do
@ -551,6 +552,12 @@ do
:-r)
r=1
;;
:-T)
textmode=1
;;
:+T)
textmode=0
;;
:-t)
last=t
;;
@ -586,16 +593,21 @@ fi
rmf a.exe* a.out* conftest.c conftest.exe* *core core.* ${tfn}* *.bc *.dbg \
*.ll *.o *.gen *.cat1 Rebuild.sh lft no signames.inc test.sh x vv.out
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 @@ Harvey)
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 @@ OpenBSD)
: "${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 @@ test 1 = $fv || check_categories="$check_categories no-histfile"
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 '!' HAVE_STRLCPY strlcpy.c
addsrcs USE_PRINTF_BUILTIN printf.c
test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
add_cppflags -DMKSH_BUILD_R=541
add_cppflags -DMKSH_BUILD_R=551
$e $bi$me: Finished configuration testing, now producing output.$ao

File diff suppressed because it is too large Load diff

View file

@ -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 @@ else
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 @@ function dirs {
(( 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 @@ function Lb64decode {
;;
}
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 @@ function Lbafh_add {
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 @@ function Lbafh_finish {
# 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 @@ function enable {
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 @@ function enable {
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 @@ function enable {
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 @@ function enable {
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 @@ function enable {
# 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 @@ function enable {
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

File diff suppressed because it is too large Load diff

View file

@ -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 @@ expand(
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 @@ expand(
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 @@ expand(
*/
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 @@ expand(
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 @@ varsub(Expand *xp, const char *sp, const char *word,
} 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 @@ varsub(Expand *xp, const char *sp, const char *word,
}
} 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 @@ varsub(Expand *xp, const char *sp, const char *word,
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 @@ varsub(Expand *xp, const char *sp, const char *word,
/* 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 @@ varsub(Expand *xp, const char *sp, const char *word,
* 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 @@ maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign)
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];

View file

@ -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 @@ execute(struct op * volatile t,
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 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
/* NOTREACHED */
default:
quitenv(NULL);
internal_errorf(Tf_sd, "CFUNC", i);
internal_errorf(Tunexpected_type, Tunwind, Tfunction, i);
}
break;
}
@ -889,6 +888,9 @@ scriptexec(struct op *tp, const char **ap)
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 @@ scriptexec(struct op *tp, const char **ap)
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 @@ scriptexec(struct op *tp, const char **ap)
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 @@ scriptexec(struct op *tp, const char **ap)
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 @@ builtin(const char *name, int (*func) (const char **))
/* 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 @@ findcom(const char *name, int flags)
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 @@ search_access(const char *fn, int mode)
}
#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 @@ search_access(const char *fn, int mode)
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(const char *name, const char *lpath,
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 @@ search_path(const char *name, const char *lpath,
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 @@ call_builtin(struct tbl *tp, const char **wp, const char *where, bool resetspec)
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 @@ iosetup(struct ioword *iop, struct tbl *tp)
/* 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));
}

View file

@ -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 @@ evalerr(Expr_state *es, enum error_type type, const char *str)
case ET_BADLIT:
warningf(true, Tf_sD_s_qs, es->expression,
"bad number", str);
Tbadnum, str);
break;
case ET_RECURSIVE:
@ -572,8 +572,9 @@ exprtoken(Expr_state *es)
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 @@ utf_wctomb(char *dst, unsigned int wc)
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 @@ ksh_access(const char *fn, int mode)
rv = -1;
return (rv);
#endif
}
#ifndef MIRBSD_BOOTFLOPPY

View file

@ -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 @@ bi_getn(const char *as, int *ai)
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 @@ c_false(const char **wp MKSH_A_UNUSED)
/*
* A leading = means assignments before command are kept.
* A leading * means a POSIX special builtin.
* A leading ^ means declaration utility, - forwarder.
*/
const struct builtin mkshbuiltins[] = {
{Tsgdot, c_dot},
@ -99,33 +100,34 @@ const struct builtin mkshbuiltins[] = {
{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 @@ const struct builtin mkshbuiltins[] = {
{"*=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 @@ const struct builtin mkshbuiltins[] = {
{"*=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 @@ static const struct t_op {
{"-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 @@ static const struct t_op {
{"-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 @@ c_print(const char **wp)
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 @@ c_print(const char **wp)
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 @@ c_print(const char **wp)
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 @@ c_print(const char **wp)
}
} 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 @@ c_print(const char **wp)
}
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 @@ c_print(const char **wp)
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 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
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 @@ do_whence(const char **wp, int fcflags, bool vflag, bool iscommand)
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 doesnt 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 @@ c_alias(const char **wp)
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 @@ c_getopts(const char **wp)
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 @@ c_shift(const char **wp)
/* 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 @@ c_umask(const char **wp)
++cp;
}
if (*cp) {
bi_errorf("bad number");
bi_errorf(Tbadnum);
return (1);
}
} else {
@ -1981,6 +1612,10 @@ c_read(const char **wp)
#define c_read_opts "Aad:N:n:prst:u,"
#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)
@ -2110,7 +1745,15 @@ c_read(const char **wp)
}
#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 @@ c_read(const char **wp)
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 @@ c_eval(const char **wp)
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 @@ c_eval(const char **wp)
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 @@ c_brkcont(const char **wp)
* 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 @@ c_exec(const char **wp MKSH_A_UNUSED)
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 @@ c_exec(const char **wp MKSH_A_UNUSED)
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 @@ c_mknod(const char **wp)
| "(" 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"|"-gt"|"-ge"|
"-lt"|"-le"|"-ef"|"-nt"|"-ot";
binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
"-nt"|"-ot"|"-ef"|
"<"|">" # rules used for [[ ... ]] expressions
;
operand ::= <anything>
*/
@ -3099,6 +2743,14 @@ test_isop(Test_meta meta, const char *s)
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 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
size_t k;
struct stat b1, b2;
mksh_ari_t v1, v2;
struct tbl *vp;
if (!do_eval)
return (0);
@ -3153,6 +2806,10 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
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 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* -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 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
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 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
* Binary Operators
*/
/* = */
/* =, == */
case TO_STEQL:
if (te->flags & TEF_DBRACKET) {
if ((i = gmatchx(opnd1, opnd2, false)))
@ -3668,7 +3325,7 @@ c_ulimit(const char **wp)
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);

View file

@ -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 @@ hist_init(Source *s)
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 @@ inittraps(void)
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;
}

View file

@ -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 @@ yylex(int cf)
* 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 @@ yylex(int cf)
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 @@ yylex(int cf)
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 @@ yylex(int cf)
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 @@ yylex(int cf)
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 @@ yylex(int cf)
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 @@ yylex(int cf)
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 @@ yyerror(const char *fmt, ...)
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 @@ get_brace_var(XString *wsp, char *wp)
char *tmp, *p;
if (!arraysub(&tmp))
yyerror("missing ]\n");
yyerror("missing ]");
*wp++ = c;
for (p = tmp; *p; ) {
Xcheck(*wsp, wp);

View file

@ -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 @@ It is built on
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 @@ Division of the largest negative number by \-1 is Undefined Behaviour.
The compiler is permitted to delete all data and crash the system
if Undefined Behaviour occurs (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 @@ passes through the errorlevel from the
.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 @@ instead of locally scoping them.
.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 @@ compilation to enable
.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 @@ shell and a legacy pdksh-alike but
.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

View file

@ -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 mksh_uari_t rndsetup(void);
static void x_sigwinch(int);
#endif
static const char initifs[] = "IFS= \t\n";
static const char initsubs[] =
"${PS2=> }"
"${PS3=#? }"
@ -71,18 +69,18 @@ static const char *initcoms[] = {
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 bool initio_done;
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 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
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 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
}
/* 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 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
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 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
* 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 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
}
if (restricted_shell) {
shcomexec(restr_com);
c_builtin(restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 1;
}
@ -657,9 +705,9 @@ main(int argc, const char *argv[])
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 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
unwind(i);
/* NOTREACHED */
default:
internal_errorf("include %d", i);
internal_errorf(Tunexpected_type, Tunwind, Tsource, i);
/* NOTREACHED */
}
}
@ -723,7 +771,7 @@ include(const char *name, int argc, const char **argv, bool intr_ok)
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 @@ command(const char *comm, int line)
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 @@ command(const char *comm, int line)
* 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 @@ shell(Source * volatile s, volatile bool toplevel)
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 @@ shell(Source * volatile s, volatile bool toplevel)
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 @@ shell(Source * volatile s, volatile bool toplevel)
* immediately after the last command
* executed.
*/
if (toplevel)
if (level == 0)
unwind(LEXIT);
break;
}
@ -908,6 +967,7 @@ unwind(int i)
case E_INCL:
case E_LOOP:
case E_ERRH:
case E_EVAL:
kshlongjmp(e->jbuf, i);
/* NOTREACHED */
case E_NONE:
@ -1271,12 +1331,10 @@ bi_errorf(const char *fmt, ...)
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 @@ initio(void)
#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 @@ ksh_dup2(int ofd, int nfd, bool errok)
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 @@ savefd(int fd)
(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 @@ openpipe(int *pv)
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

View file

@ -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 @@ __RCSID("$MirOS: src/bin/mksh/misc.c,v 1.249 2016/11/11 23:31:35 tg Exp $");
#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 @@ setctypes(const char *s, int t)
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 @@ parse_args(const char **argv,
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 @@ do_realpath(const char *upath)
/* 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 @@ simplify_path(char *p)
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 @@ getrusage(int what, struct rusage *ru)
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 @@ unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
* 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 @@ unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
(*fp)(c);
break;
}
++n;
}
if (!n)
goto unknown_escape;
if ((cstyle && wc > 0xFF) || fc != 'x')
/* Unicode marker */
wc += 0x100;

View file

@ -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 @@ sometimes does take portable shell scripting or various standards
into account all information is first and foremost presented with
.Nm
in mind and should be taken as such.
.Ss I'm an Android user, so what's mksh?
.Nm mksh
is a
.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 @@ also by newline) may be one same parse tree.
.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 @@ searched when looking for commands and files sourced using the
.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 @@ Due to a strong suggestion from David G. Korn,
.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 @@ files are created in
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 the login name is not found in the password file or
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 @@ Redirections are processed after
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 @@ function.
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 @@ Additional
.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 @@ For any name without a value, the existing alias is listed.
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 @@ Execute the built-in command
.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 @@ cannot be a shell function;
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 @@ If the
.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 @@ Note that the Bourne shell differs here;
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 @@ parameter.
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 @@ resetting
.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 @@ If an error occurs during
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 @@ however, distributors may have added this as builtin as a speed hack.
.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 @@ utility, tab completion, the
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 @@ above).
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 @@ instead of standard output.
.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 @@ If no input is read or a timeout occurred,
.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 @@ exec 3\*(Gt&p; exec 3\*(Gt&\-
.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 @@ Background jobs are run with lower priority.
.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 @@ is a symbolic link.
.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 @@ is not empty.
.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 @@ traps in functions are not yet implemented.
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 @@ parameter values are not printed.
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 @@ Indexed array attribute.
.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 @@ once they are set.
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 @@ Most other historic,
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 @@ Numbers with a leading digit zero are interpreted as octal.
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 @@ child processes.
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 @@ and
.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 @@ Goes to history number
.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 @@ contains the system and suid profile.
.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 @@ The effort of several projects, such as Debian and OpenBSD, and other
contributors including our users, to improve the shell is appreciated.
See the documentation, 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 @@ The complete legalese is at:
.\" 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 @@ case ${KSH_VERSION:\-} in
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 @@ for the in-memory portion of the history is slow, should use
.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 @@ for an operating environment supporting all of its advanced needs.
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 @@ IRC channel at
.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.

557
src/os2.c Normal file
View file

@ -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();
}

236
src/sh.h
View file

@ -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 @@ struct rusage {
#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 int __cdecl setegid(gid_t);
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 @@ char *ucstrstr(char *, const char *);
#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 @@ im_sorry_dave(void)
} 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 @@ extern struct env {
#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 @@ EXTERN char null[] E_INIT("");
#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 Tsgdot[] E_INIT("*=.");
EXTERN const char Taugo[] E_INIT("augo");
EXTERN const char Tbracket[] E_INIT("[");
#define Tdot (Tsgdot + 2)
EXTERN const char Talias[] E_INIT("alias");
EXTERN const char 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 Tcant_open[] E_INIT("can't open");
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 @@ EXTERN const char Tnot_found_s[] E_INIT("%s not found");
#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_ssfaileds[] E_INIT("%s: %s failed: %s");
EXTERN const char Tf_sD_sD_s[] E_INIT("%s: %s: %s");
EXTERN const char Tf__c_[] E_INIT("-%c ");
EXTERN const char Tf_sD_s_s[] E_INIT("%s: %s %s");
#define Tf_sN (Tf__sN + 1)
#define Tf_sD_s (Tf_s_sD_s + 3)
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#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 @@ EXTERN bool really_exit;
/*
* 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 @@ EXTERN bool really_exit;
#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 @@ EXTERN sigset_t sm_default, sm_sigchld;
/* 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 @@ struct tbl {
};
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 @@ EXTERN bool last_lookup_was_array;
#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 @@ struct op {
#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 @@ int glob_str(char *, XPtrV *, bool);
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_printf(const char **);
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 c_times(const char **);
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 @@ int pprompt(const char *, int);
/* 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 *strdup_i(const char *, Area *);
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 @@ char *shf_smprintf(const char *, ...)
ssize_t shf_vfprintf(struct shf *, const char *, va_list)
MKSH_A_FORMAT(__printf__, 2, 0);
/* syn.c */
int assign_command(const char *, bool) 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 popblock(void);
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 @@ enum Test_op {
/* 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 bool tty_hasstate; /* true if tty_state is valid */
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 @@ extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */
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), '/')

View file

@ -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 @@ F0("allexport", FEXPORT, OF_ANY, 'a')
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("stdin", FSTDIN, OF_CMDLINE, 's')
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')

View file

@ -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("bgnice", FBGNICE, OF_ANY
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("emacs", FEMACS, OF_ANY
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("utf8-mode", FUNICODE, OF_ANY
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) */

View file

@ -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 @@ shf_sclose(struct shf *shf)
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_getse(char *buf, ssize_t bsize, struct shf *shf)
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);
}

190
src/syn.c
View file

@ -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 {
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 struct nesting_state nesting; /* \n changed to ; */
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 Tcbrace[] = "}";
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 @@ yyparse(void)
}
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 @@ pipeline(int cf)
}
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 @@ andor(void)
}
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 @@ synio(int cf)
}
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 const char setA_cmd2[] = {
};
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
}
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
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 @@ get_command(int cf)
case FUNCTION:
musthave(LWORD, 0);
t = function_body(yylval.cp, true);
t = function_body(yylval.cp, sALIAS, true);
break;
}
@ -536,7 +540,7 @@ get_command(int cf)
}
static struct op *
dogroup(void)
dogroup(int sALIAS)
{
int c;
struct op *list;
@ -554,40 +558,40 @@ dogroup(void)
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 @@ elsepart(void)
}
static struct op *
caselist(void)
caselist(int sALIAS)
{
struct op *t, *tl;
int c;
@ -613,7 +617,7 @@ caselist(void)
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 @@ caselist(void)
}
static struct op *
casepart(int endtok)
casepart(int endtok, int sALIAS)
{
struct op *t;
XPtrV ptns;
@ -656,7 +660,7 @@ casepart(int endtok)
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 @@ casepart(int endtok)
}
static struct op *
function_body(char *name,
function_body(char *name, int sALIAS,
/* function foo { ... } vs foo() { .. } */
bool ksh_func)
{
@ -697,7 +701,7 @@ function_body(char *name,
*/
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 @@ function_body(char *name,
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 @@ function_body(char *name,
}
static char **
wordlist(void)
wordlist(int sALIAS)
{
int c;
XPtrV args;
@ -864,7 +868,7 @@ syntaxerr(const char *what)
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 @@ syntaxerr(const char *what)
s = redir;
}
}
yyerror("%s: '%s' %s\n", Tsynerr, s, what);
yyerror(Tf_sD_s_qs, Tsynerr, what, s);
}
static void
@ -925,7 +929,7 @@ newtp(int type)
}
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 @@ compile(Source *s, bool skiputf8bom)
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 @@ parse_usec(const char *s, struct timeval *tv)
* 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 @@ yyrecursive(int subtype MKSH_A_UNUSED)
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 @@ yyrecursive_pop(bool popall)
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;

View file

@ -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 @@ ptree(struct op *t, int indent, struct shf *shf)
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 @@ ptree(struct op *t, int indent, struct shf *shf)
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 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
}
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 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
case SPAT:
c = '|';
if (0)
/* FALLTHROUGH */
case CPAT:
c = /*(*/ ')';
c = /*(*/ ')';
shf_putc(c, shf);
break;
}
@ -606,7 +620,9 @@ wdscan(const char *wp, int c)
case QCHAR:
wp++;
break;
case COMASUB:
case COMSUB:
case FUNASUB:
case FUNSUB:
case VALSUB:
case EXPRSUB:
@ -832,8 +848,9 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
}
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 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
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 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
closeandout:
shf_putc('>', shf);
break;
case FUNASUB:
shf_puts("FUNASUB<", shf);
goto dumpsub;
case FUNSUB:
shf_puts("FUNSUB<", shf);
goto dumpsub;

415
src/var.c
View file

@ -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 @@ static uint32_t lcg_state = 5381, qh_state = 4711;
/* 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 *);
@ -224,6 +227,13 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
*/
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;
@ -291,17 +301,19 @@ global(const char *n)
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 @@ skip_varname(const char *s, bool aok)
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 @@ setspec(struct tbl *vp)
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 @@ record_match(const char *istr)
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 doesnt 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));
}