Merge "Upgrade to mksh R55." am: 693c2ea1e4
am: e0d2df7f07
Change-Id: I2e1ef7d4911a211b3dc58e3cab8e6c6e0ad2778a
This commit is contained in:
commit
0d2db5fba1
23 changed files with 3184 additions and 1964 deletions
|
@ -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
114
src/Build.sh
Executable file → Normal 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
|
||||
|
||||
|
|
834
src/check.t
834
src/check.t
File diff suppressed because it is too large
Load diff
443
src/dot.mkshrc
443
src/dot.mkshrc
|
@ -1,8 +1,8 @@
|
|||
# $Id$
|
||||
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.108 2016/07/26 22:03:41 tg Exp $
|
||||
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.114 2017/03/19 22:31:26 tg Exp $
|
||||
#-
|
||||
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,65 +22,100 @@
|
|||
#-
|
||||
# ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
|
||||
|
||||
# catch non-mksh (including lksh) trying to run this file
|
||||
# catch non-mksh, non-lksh, trying to run this file
|
||||
case ${KSH_VERSION:-} in
|
||||
*MIRBSD\ KSH*) ;;
|
||||
*) return 0 ;;
|
||||
*LEGACY\ KSH*|*MIRBSD\ KSH*) ;;
|
||||
*) \return 0 ;;
|
||||
esac
|
||||
|
||||
PS1='#'; (( USER_ID )) && PS1='$'; \: "${TERM:=vt100}${HOSTNAME:=$(\ulimit -c \
|
||||
0; hostname 2>/dev/null)}${EDITOR:=/bin/ed}${USER:=$(\ulimit -c 0; id -un \
|
||||
2>/dev/null || \echo \?)}${MKSH:=$(\builtin whence -p mksh)}"
|
||||
HOSTNAME=${HOSTNAME%%*([ ]).*}; HOSTNAME=${HOSTNAME##*([ ])}
|
||||
[[ $HOSTNAME = ?(ip6-)localhost?(6) ]] && HOSTNAME=
|
||||
\: "${HOSTNAME:=nil}${MKSH:=/bin/mksh}"; \export EDITOR HOSTNAME MKSH TERM USER
|
||||
PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
|
||||
\typeset e=$?
|
||||
# give MidnightBSD's laffer1 a bit of csh feeling
|
||||
function setenv {
|
||||
if (( $# )); then
|
||||
\\builtin eval '\\builtin export "$1"="${2:-}"'
|
||||
else
|
||||
\\builtin typeset -x
|
||||
fi
|
||||
}
|
||||
|
||||
# pager (not control character safe)
|
||||
smores() (
|
||||
\\builtin set +m
|
||||
\\builtin cat "$@" |&
|
||||
\\builtin trap "rv=\$?; \\\\builtin kill $! >/dev/null 2>&1; \\\\builtin exit \$rv" EXIT
|
||||
while IFS= \\builtin read -pr line; do
|
||||
llen=${%line}
|
||||
(( llen == -1 )) && llen=${#line}
|
||||
(( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
|
||||
if (( (curlin += llen) >= LINES )); then
|
||||
\\builtin print -nr -- $'\e[7m--more--\e[0m'
|
||||
\\builtin read -u1 || \\builtin exit $?
|
||||
[[ $REPLY = [Qq]* ]] && \\builtin exit 0
|
||||
curlin=$llen
|
||||
fi
|
||||
\\builtin print -r -- "$line"
|
||||
done
|
||||
)
|
||||
|
||||
\\builtin alias ls=ls l='ls -F' la='l -a' ll='l -l' lo='l -alo'
|
||||
\: "${HOSTNAME:=$(\\builtin ulimit -c 0; \\builtin print -r -- $(hostname \
|
||||
2>/dev/null))}${EDITOR:=/bin/ed}${TERM:=vt100}${USER:=$(\\builtin ulimit \
|
||||
-c 0; id -un 2>/dev/null)}${USER:=?}"
|
||||
[[ $HOSTNAME = ?(?(ip6-)localhost?(6)) ]] && HOSTNAME=nil; \\builtin unalias ls
|
||||
\\builtin export EDITOR HOSTNAME TERM USER
|
||||
|
||||
# minimal support for lksh users
|
||||
if [[ $KSH_VERSION = *LEGACY\ KSH* ]]; then
|
||||
PS1='$USER@${HOSTNAME%%.*}:$PWD>'
|
||||
\\builtin return 0
|
||||
fi
|
||||
|
||||
# mksh-specific from here
|
||||
\: "${MKSH:=$(\\builtin whence -p mksh)}${MKSH:=/bin/mksh}"
|
||||
\\builtin export MKSH
|
||||
|
||||
PS4='[$EPOCHREALTIME] '; PS1='#'; (( USER_ID )) && PS1='$'; PS1=$'\001\r''${|
|
||||
\\builtin typeset e=$?
|
||||
|
||||
(( e )) && REPLY+="$e|"
|
||||
REPLY+=${USER}@${HOSTNAME%%.*}:
|
||||
|
||||
\typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
|
||||
d=${d%/}; \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
|
||||
\\builtin typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
|
||||
d=${d%/}; \\builtin typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
|
||||
(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
|
||||
REPLY+=$p$d
|
||||
|
||||
\return $e
|
||||
\\builtin return $e
|
||||
} '"$PS1 "
|
||||
\alias ls=ls
|
||||
\unalias ls
|
||||
\alias l='ls -F'
|
||||
\alias la='l -a'
|
||||
\alias ll='l -l'
|
||||
\alias lo='l -alo'
|
||||
\alias doch='sudo mksh -c "$(\builtin fc -ln -1)"'
|
||||
\command -v rot13 >/dev/null || \alias rot13='tr \
|
||||
\\builtin alias doch='sudo mksh -c "$(\\builtin fc -ln -1)"'
|
||||
\\builtin command -v rot13 >/dev/null || \\builtin alias rot13='tr \
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
|
||||
nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
|
||||
if \command -v hd >/dev/null; then \:; elif \command -v hexdump >/dev/null; then
|
||||
if \\builtin command -v hd >/dev/null; then
|
||||
\:
|
||||
elif \\builtin command -v hexdump >/dev/null; then
|
||||
function hd {
|
||||
hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \
|
||||
-e '" |" "%_p"' -e '"|\n"' "$@"
|
||||
}
|
||||
else
|
||||
function hd {
|
||||
\typeset -Uui16 -Z11 pos=0
|
||||
\typeset -Uui16 -Z5 hv=2147483647
|
||||
\typeset dasc line i
|
||||
\set +U
|
||||
\\builtin typeset -Uui16 -Z11 pos=0
|
||||
\\builtin typeset -Uui16 -Z5 hv=2147483647
|
||||
\\builtin typeset dasc line i
|
||||
\\builtin set +U
|
||||
|
||||
\cat "$@" | if \read -arN -1 line; then
|
||||
\typeset -i1 'line[*]'
|
||||
\\builtin cat "$@" | if \\builtin read -arN -1 line; then
|
||||
\\builtin typeset -i1 'line[*]'
|
||||
i=0
|
||||
while (( i < ${#line[*]} )); do
|
||||
hv=${line[i++]}
|
||||
if (( (pos & 15) == 0 )); then
|
||||
(( pos )) && \
|
||||
\builtin print -r -- "$dasc|"
|
||||
\builtin print -n "${pos#16#} "
|
||||
\\builtin print -r -- "$dasc|"
|
||||
\\builtin print -nr "${pos#16#} "
|
||||
dasc=' |'
|
||||
fi
|
||||
\builtin print -n "${hv#16#} "
|
||||
\\builtin print -nr "${hv#16#} "
|
||||
#XXX EBCDIC, but we need [[:print:]] to fix this
|
||||
if (( (hv < 32) || (hv > 126) )); then
|
||||
dasc+=.
|
||||
|
@ -88,69 +123,69 @@ 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
|
||||
|
|
588
src/edit.c
588
src/edit.c
File diff suppressed because it is too large
Load diff
103
src/eval.c
103
src/eval.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.194 2016/11/11 23:31:34 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.201 2017/04/06 01:59:54 tg Exp $");
|
||||
|
||||
/*
|
||||
* string expansion
|
||||
|
@ -301,25 +301,36 @@ 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];
|
||||
|
|
90
src/exec.c
90
src/exec.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.186 2016/11/11 23:31:34 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.196 2017/04/12 16:46:21 tg Exp $");
|
||||
|
||||
#ifndef MKSH_DEFAULT_EXECSHELL
|
||||
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
|
||||
|
@ -376,9 +376,8 @@ 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));
|
||||
}
|
||||
|
|
15
src/expr.c
15
src/expr.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2016
|
||||
* 2011, 2012, 2013, 2014, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.90 2016/11/07 16:58:48 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.93 2017/04/02 16:47:41 tg Exp $");
|
||||
|
||||
#define EXPRTOK_DEFNS
|
||||
#include "exprtok.h"
|
||||
|
@ -203,7 +203,7 @@ 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
|
||||
|
|
551
src/funcs.c
551
src/funcs.c
|
@ -5,7 +5,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2010, 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -38,7 +38,7 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.319 2016/11/11 23:48:29 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.340 2017/04/12 17:46:29 tg Exp $");
|
||||
|
||||
#if HAVE_KILLPG
|
||||
/*
|
||||
|
@ -73,7 +73,7 @@ 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 doesn’t this leak? */
|
||||
}
|
||||
|
||||
static void
|
||||
c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
|
||||
bool istset)
|
||||
{
|
||||
struct tbl *tvp;
|
||||
int any_set = 0;
|
||||
char *s;
|
||||
|
||||
if (!vp)
|
||||
return;
|
||||
|
||||
/*
|
||||
* See if the parameter is set (for arrays, if any
|
||||
* element is set).
|
||||
*/
|
||||
for (tvp = vp; tvp; tvp = tvp->u.array)
|
||||
if (tvp->flag & ISSET) {
|
||||
any_set = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check attributes - note that all array elements
|
||||
* have (should have?) the same attributes, so checking
|
||||
* the first is sufficient.
|
||||
*
|
||||
* Report an unset param only if the user has
|
||||
* explicitly given it some attribute (like export);
|
||||
* otherwise, after "echo $FOO", we would report FOO...
|
||||
*/
|
||||
if (!any_set && !(vp->flag & USERATTRIB))
|
||||
return;
|
||||
if (flag && (vp->flag & flag) == 0)
|
||||
return;
|
||||
if (!(vp->flag & ARRAY))
|
||||
/* optimise later conditionals */
|
||||
any_set = 0;
|
||||
do {
|
||||
/*
|
||||
* Ignore array elements that aren't set unless there
|
||||
* are no set elements, in which case the first is
|
||||
* reported on
|
||||
*/
|
||||
if (any_set && !(vp->flag & ISSET))
|
||||
continue;
|
||||
/* no arguments */
|
||||
if (!thing && !flag) {
|
||||
if (any_set == 1) {
|
||||
shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
|
||||
any_set = 2;
|
||||
}
|
||||
/*
|
||||
* AT&T ksh prints things like export, integer,
|
||||
* leftadj, zerofill, etc., but POSIX says must
|
||||
* be suitable for re-entry...
|
||||
*/
|
||||
shprintf(Tf_s_s, Ttypeset, "");
|
||||
if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
|
||||
shprintf(Tf__c_, 'n');
|
||||
if ((vp->flag & INTEGER))
|
||||
shprintf(Tf__c_, 'i');
|
||||
if ((vp->flag & EXPORT))
|
||||
shprintf(Tf__c_, 'x');
|
||||
if ((vp->flag & RDONLY))
|
||||
shprintf(Tf__c_, 'r');
|
||||
if ((vp->flag & TRACE))
|
||||
shprintf(Tf__c_, 't');
|
||||
if ((vp->flag & LJUST))
|
||||
shprintf("-L%d ", vp->u2.field);
|
||||
if ((vp->flag & RJUST))
|
||||
shprintf("-R%d ", vp->u2.field);
|
||||
if ((vp->flag & ZEROFIL))
|
||||
shprintf(Tf__c_, 'Z');
|
||||
if ((vp->flag & LCASEV))
|
||||
shprintf(Tf__c_, 'l');
|
||||
if ((vp->flag & UCASEV_AL))
|
||||
shprintf(Tf__c_, 'u');
|
||||
if ((vp->flag & INT_U))
|
||||
shprintf(Tf__c_, 'U');
|
||||
} else if (pflag) {
|
||||
shprintf(Tf_s_s, istset ? Ttypeset :
|
||||
(flag & EXPORT) ? Texport : Treadonly, "");
|
||||
}
|
||||
if (any_set)
|
||||
shprintf("%s[%lu]", vp->name, arrayindex(vp));
|
||||
while (*cp)
|
||||
if (!ksh_isalias(*cp))
|
||||
return (false);
|
||||
else
|
||||
shf_puts(vp->name, shl_stdout);
|
||||
if ((!thing && !flag && pflag) ||
|
||||
(thing == '-' && (vp->flag & ISSET))) {
|
||||
s = str_val(vp);
|
||||
shf_putc('=', shl_stdout);
|
||||
/* AT&T ksh can't have justified integers... */
|
||||
if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
|
||||
shf_puts(s, shl_stdout);
|
||||
else
|
||||
print_value_quoted(shl_stdout, s);
|
||||
}
|
||||
shf_putc('\n', shl_stdout);
|
||||
|
||||
/*
|
||||
* Only report first 'element' of an array with
|
||||
* no set elements.
|
||||
*/
|
||||
if (!any_set)
|
||||
return;
|
||||
} while ((vp = vp->u.array));
|
||||
++cp;
|
||||
return (true);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1231,6 +857,11 @@ 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
30
src/lex.c
30
src/lex.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.228 2016/08/01 21:38:03 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.234 2017/04/06 01:59:55 tg Exp $");
|
||||
|
||||
/*
|
||||
* states while lexing word
|
||||
|
@ -489,7 +489,7 @@ 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);
|
||||
|
|
88
src/lksh.1
88
src/lksh.1
|
@ -1,6 +1,6 @@
|
|||
.\" $MirOS: src/bin/mksh/lksh.1,v 1.20 2016/11/11 23:31:35 tg Exp $
|
||||
.\" $MirOS: src/bin/mksh/lksh.1,v 1.23 2017/04/02 13:38:02 tg Exp $
|
||||
.\"-
|
||||
.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016
|
||||
.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017
|
||||
.\" mirabilos <m@mirbsd.org>
|
||||
.\"
|
||||
.\" Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -74,7 +74,7 @@
|
|||
.\" with -mandoc, it might implement .Mx itself, but we want to
|
||||
.\" use our own definition. And .Dd must come *first*, always.
|
||||
.\"
|
||||
.Dd $Mdocdate: November 11 2016 $
|
||||
.Dd $Mdocdate: April 2 2017 $
|
||||
.\"
|
||||
.\" Check which macro package we use, and do other -mdoc setup.
|
||||
.\"
|
||||
|
@ -173,37 +173,34 @@ 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
|
||||
|
|
138
src/main.c
138
src/main.c
|
@ -5,7 +5,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -34,7 +34,7 @@
|
|||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.322 2016/11/11 23:48:30 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.332 2017/04/12 16:01:45 tg Exp $");
|
||||
|
||||
extern char **environ;
|
||||
|
||||
|
@ -56,8 +56,6 @@ static 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
|
||||
|
|
42
src/misc.c
42
src/misc.c
|
@ -3,7 +3,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -30,7 +30,7 @@
|
|||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.249 2016/11/11 23:31:35 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.255 2017/04/12 16:46:22 tg Exp $");
|
||||
|
||||
#define KSH_CHVT_FLAG
|
||||
#ifdef MKSH_SMALL
|
||||
|
@ -40,10 +40,6 @@ __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;
|
||||
|
|
591
src/mksh.1
591
src/mksh.1
|
@ -1,8 +1,8 @@
|
|||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.420 2016/11/11 23:31:36 tg Exp $
|
||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.442 2017/04/12 18:30:58 tg Exp $
|
||||
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
|
||||
.\"-
|
||||
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016
|
||||
.\" 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
.\" mirabilos <m@mirbsd.org>
|
||||
.\"
|
||||
.\" Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -76,7 +76,7 @@
|
|||
.\" with -mandoc, it might implement .Mx itself, but we want to
|
||||
.\" use our own definition. And .Dd must come *first*, always.
|
||||
.\"
|
||||
.Dd $Mdocdate: November 11 2016 $
|
||||
.Dd $Mdocdate: April 12 2017 $
|
||||
.\"
|
||||
.\" Check which macro package we use, and do other -mdoc setup.
|
||||
.\"
|
||||
|
@ -186,23 +186,8 @@ 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
557
src/os2.c
Normal 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
236
src/sh.h
|
@ -10,7 +10,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -175,9 +175,9 @@
|
|||
#endif
|
||||
|
||||
#ifdef EXTERN
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.791 2016/11/11 23:31:38 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.808 2017/04/12 17:38:46 tg Exp $");
|
||||
#endif
|
||||
#define MKSH_VERSION "R54 2016/11/11"
|
||||
#define MKSH_VERSION "R55 2017/04/12"
|
||||
|
||||
/* arithmetic types: C implementation */
|
||||
#if !HAVE_CAN_INTTYPES
|
||||
|
@ -392,13 +392,20 @@ 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), '/')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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) */
|
||||
|
|
48
src/shf.c
48
src/shf.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
|
||||
* 2012, 2013, 2015, 2016
|
||||
* 2012, 2013, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.76 2016/07/25 00:04:47 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.79 2017/04/12 17:08:49 tg Exp $");
|
||||
|
||||
/* flags to shf_emptybuf() */
|
||||
#define EB_READSW 0x01 /* about to switch to reading */
|
||||
|
@ -289,29 +289,31 @@ 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
190
src/syn.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.115 2016/09/01 12:59:12 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.120 2017/04/06 01:59:57 tg Exp $");
|
||||
|
||||
struct nesting_state {
|
||||
int start_token; /* token than began nesting (eg, FOR) */
|
||||
|
@ -35,25 +35,24 @@ struct yyrecursive_state {
|
|||
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;
|
||||
|
|
33
src/tree.c
33
src/tree.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2015, 2016
|
||||
* 2011, 2012, 2013, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.86 2016/07/25 00:04:48 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.89 2017/04/12 16:46:23 tg Exp $");
|
||||
|
||||
#define INDENT 8
|
||||
|
||||
|
@ -58,7 +58,7 @@ 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
415
src/var.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016, 2017
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -28,7 +28,7 @@
|
|||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.209 2016/11/11 23:31:39 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.214 2017/04/02 16:47:43 tg Exp $");
|
||||
|
||||
/*-
|
||||
* Variables
|
||||
|
@ -45,6 +45,9 @@ 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 doesn’t this leak? */
|
||||
}
|
||||
|
||||
static void
|
||||
c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, int any_set,
|
||||
bool pflag, bool istset)
|
||||
{
|
||||
struct tbl *tvp;
|
||||
char *s;
|
||||
|
||||
if (!vp)
|
||||
return;
|
||||
|
||||
/*
|
||||
* See if the parameter is set (for arrays, if any
|
||||
* element is set).
|
||||
*/
|
||||
for (tvp = vp; tvp; tvp = tvp->u.array)
|
||||
if (tvp->flag & ISSET) {
|
||||
any_set |= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check attributes - note that all array elements
|
||||
* have (should have?) the same attributes, so checking
|
||||
* the first is sufficient.
|
||||
*
|
||||
* Report an unset param only if the user has
|
||||
* explicitly given it some attribute (like export);
|
||||
* otherwise, after "echo $FOO", we would report FOO...
|
||||
*/
|
||||
if (!any_set && !(vp->flag & USERATTRIB))
|
||||
return;
|
||||
if (flag && (vp->flag & flag) == 0)
|
||||
return;
|
||||
if (!(vp->flag & ARRAY))
|
||||
/* optimise later conditionals */
|
||||
any_set = 0;
|
||||
do {
|
||||
/*
|
||||
* Ignore array elements that aren't set unless there
|
||||
* are no set elements, in which case the first is
|
||||
* reported on
|
||||
*/
|
||||
if (any_set && !(vp->flag & ISSET))
|
||||
continue;
|
||||
/* no arguments */
|
||||
if (!thing && !flag) {
|
||||
if (any_set == 1) {
|
||||
shprintf(Tf_s_s_sN, Tset, "-A", vp->name);
|
||||
any_set = 2;
|
||||
}
|
||||
/*
|
||||
* AT&T ksh prints things like export, integer,
|
||||
* leftadj, zerofill, etc., but POSIX says must
|
||||
* be suitable for re-entry...
|
||||
*/
|
||||
shprintf(Tf_s_s, Ttypeset, "");
|
||||
if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
|
||||
shprintf(Tf__c_, 'n');
|
||||
if ((vp->flag & INTEGER))
|
||||
shprintf(Tf__c_, 'i');
|
||||
if ((vp->flag & EXPORT))
|
||||
shprintf(Tf__c_, 'x');
|
||||
if ((vp->flag & RDONLY))
|
||||
shprintf(Tf__c_, 'r');
|
||||
if ((vp->flag & TRACE))
|
||||
shprintf(Tf__c_, 't');
|
||||
if ((vp->flag & LJUST))
|
||||
shprintf("-L%d ", vp->u2.field);
|
||||
if ((vp->flag & RJUST))
|
||||
shprintf("-R%d ", vp->u2.field);
|
||||
if ((vp->flag & ZEROFIL))
|
||||
shprintf(Tf__c_, 'Z');
|
||||
if ((vp->flag & LCASEV))
|
||||
shprintf(Tf__c_, 'l');
|
||||
if ((vp->flag & UCASEV_AL))
|
||||
shprintf(Tf__c_, 'u');
|
||||
if ((vp->flag & INT_U))
|
||||
shprintf(Tf__c_, 'U');
|
||||
} else if (pflag) {
|
||||
shprintf(Tf_s_s, istset ? Ttypeset :
|
||||
(flag & EXPORT) ? Texport : Treadonly, "");
|
||||
}
|
||||
if (any_set)
|
||||
shprintf("%s[%lu]", vp->name, arrayindex(vp));
|
||||
else
|
||||
shf_puts(vp->name, shl_stdout);
|
||||
if ((!thing && !flag && pflag) ||
|
||||
(thing == '-' && (vp->flag & ISSET))) {
|
||||
s = str_val(vp);
|
||||
shf_putc('=', shl_stdout);
|
||||
/* AT&T ksh can't have justified integers... */
|
||||
if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
|
||||
shf_puts(s, shl_stdout);
|
||||
else
|
||||
print_value_quoted(shl_stdout, s);
|
||||
}
|
||||
shf_putc('\n', shl_stdout);
|
||||
|
||||
/*
|
||||
* Only report first 'element' of an array with
|
||||
* no set elements.
|
||||
*/
|
||||
if (!any_set)
|
||||
return;
|
||||
} while (!(any_set & 4) && (vp = vp->u.array));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue