Update mksh to R43 (formal release, from tarball)
The files in src/ are just the unmodified mksh release .tgz and is Not a Contribution to Android, it’s the upstream code project. ChangeLog: https://www.mirbsd.org/mksh.htm#clog Changes, other than upstream mksh code: • update Copyright years • sync with moving to a different AOSP project and other AOSP changes • mkmf.sh: add test compilation mode • be conservative in filedescriptor usage • don’t compile the $MirOS$ RCS IDs into the binary to save space • enable UTF-8 mode, to match reality • help memory leak debugging by adding -DDEBUG_LEAKS • update rest of mkmf.sh to work with updated AOSP and mksh • use ro.product.device property as default hostname in mkshrc (originally submitted by John Michelau <john.michelau@motorola.com>) • do not export PS1 in mkshrc (cross-shell developers agreed on this) • add “more” function to mkshrc to get a simplistic pager • add “hd” and “setenv” functions to mkshrc for better UX • mention Launchpad as upstream bugtracker as comment in mkshrc • change TMPDIR to /data/local/tmp for now, as /sqlite_stmt_journals is gone (although this is still no full replacement, it’s better than before) • address the segfaults seen by DONG-DONG YANG Change-Id: I2d4d175bc5163b3d6f5098024f98f316fe812e55
This commit is contained in:
parent
93bd42c789
commit
c2dc5def5e
28 changed files with 6701 additions and 3246 deletions
60
Android.mk
60
Android.mk
|
@ -1,4 +1,4 @@
|
|||
# Copyright © 2010
|
||||
# Copyright © 2010, 2013
|
||||
# Thorsten Glaser <t.glaser@tarent.de>
|
||||
# This file is provided under the same terms as mksh.
|
||||
|
||||
|
@ -34,31 +34,37 @@ LOCAL_SYSTEM_SHARED_LIBRARIES:= libc
|
|||
|
||||
LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src
|
||||
# additional flags first, then from Makefrag.inc: CFLAGS, CPPFLAGS
|
||||
LOCAL_CFLAGS:= -DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\" \
|
||||
-DMKSH_DEFAULT_TMPDIR=\"/sqlite_stmt_journals\" \
|
||||
-DMKSHRC_PATH=\"/system/etc/mkshrc\" \
|
||||
-fwrapv \
|
||||
-DMKSH_ASSUME_UTF8=0 -DMKSH_NOPWNAM \
|
||||
-D_GNU_SOURCE \
|
||||
-DHAVE_ATTRIBUTE_BOUNDED=0 -DHAVE_ATTRIBUTE_FORMAT=1 \
|
||||
-DHAVE_ATTRIBUTE_NONNULL=1 -DHAVE_ATTRIBUTE_NORETURN=1 \
|
||||
-DHAVE_ATTRIBUTE_UNUSED=1 -DHAVE_ATTRIBUTE_USED=1 \
|
||||
-DHAVE_SYS_PARAM_H=1 -DHAVE_SYS_MKDEV_H=0 \
|
||||
-DHAVE_SYS_MMAN_H=1 -DHAVE_SYS_SYSMACROS_H=1 \
|
||||
-DHAVE_GRP_H=1 -DHAVE_LIBGEN_H=1 -DHAVE_LIBUTIL_H=0 \
|
||||
-DHAVE_PATHS_H=1 -DHAVE_STDBOOL_H=1 -DHAVE_STDINT_H=1 \
|
||||
-DHAVE_STRINGS_H=1 -DHAVE_ULIMIT_H=0 -DHAVE_VALUES_H=0 \
|
||||
-DHAVE_CAN_INTTYPES=1 -DHAVE_CAN_UCBINTS=1 \
|
||||
-DHAVE_CAN_INT8TYPE=1 -DHAVE_CAN_UCBINT8=1 \
|
||||
-DHAVE_RLIM_T=1 -DHAVE_SIG_T=1 -DHAVE_SYS_SIGNAME=1 \
|
||||
-DHAVE_SYS_SIGLIST=1 -DHAVE_STRSIGNAL=0 \
|
||||
-DHAVE_GETRUSAGE=1 -DHAVE_KILLPG=1 -DHAVE_MKNOD=0 \
|
||||
-DHAVE_MKSTEMP=1 -DHAVE_NICE=1 -DHAVE_REVOKE=0 \
|
||||
-DHAVE_SETLOCALE_CTYPE=0 -DHAVE_LANGINFO_CODESET=0 \
|
||||
-DHAVE_SETMODE=1 -DHAVE_SETRESUGID=1 \
|
||||
-DHAVE_SETGROUPS=1 -DHAVE_STRCASESTR=1 \
|
||||
-DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 \
|
||||
-DHAVE_REVOKE_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \
|
||||
-DHAVE_PERSISTENT_HISTORY=0
|
||||
LOCAL_CFLAGS:= -DMKSHRC_PATH=\"/system/etc/mkshrc\" \
|
||||
-DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\" \
|
||||
-DMKSH_DEFAULT_TMPDIR=\"/data/local/tmp\" \
|
||||
-fno-asynchronous-unwind-tables -fwrapv \
|
||||
-DDEBUG_LEAKS -DMKSH_ASSUME_UTF8 -DMKSH_CONSERVATIVE_FDS \
|
||||
-DMKSH_DONT_EMIT_IDSTRING -DMKSH_NOPWNAM -DMKSH_BUILDSH \
|
||||
-D_GNU_SOURCE -DSETUID_CAN_FAIL_WITH_EAGAIN \
|
||||
-DHAVE_ATTRIBUTE_BOUNDED=0 -DHAVE_ATTRIBUTE_FORMAT=1 \
|
||||
-DHAVE_ATTRIBUTE_NORETURN=1 -DHAVE_ATTRIBUTE_UNUSED=1 \
|
||||
-DHAVE_ATTRIBUTE_USED=1 -DHAVE_SYS_TIME_H=1 -DHAVE_TIME_H=1 \
|
||||
-DHAVE_BOTH_TIME_H=1 -DHAVE_SYS_BSDTYPES_H=0 \
|
||||
-DHAVE_SYS_FILE_H=1 -DHAVE_SYS_MKDEV_H=0 -DHAVE_SYS_MMAN_H=1 \
|
||||
-DHAVE_SYS_PARAM_H=1 -DHAVE_SYS_RESOURCE_H=1 \
|
||||
-DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SYSMACROS_H=1 \
|
||||
-DHAVE_BSTRING_H=0 -DHAVE_GRP_H=1 -DHAVE_LIBGEN_H=1 \
|
||||
-DHAVE_LIBUTIL_H=0 -DHAVE_PATHS_H=1 -DHAVE_STDINT_H=1 \
|
||||
-DHAVE_STRINGS_H=1 -DHAVE_TERMIOS_H=1 -DHAVE_ULIMIT_H=0 \
|
||||
-DHAVE_VALUES_H=0 -DHAVE_CAN_INTTYPES=1 -DHAVE_CAN_UCBINTS=1 \
|
||||
-DHAVE_CAN_INT8TYPE=1 -DHAVE_CAN_UCBINT8=1 -DHAVE_RLIM_T=1 \
|
||||
-DHAVE_SIG_T=1 -DHAVE_SYS_ERRLIST=0 -DHAVE_SYS_SIGNAME=1 \
|
||||
-DHAVE_SYS_SIGLIST=1 -DHAVE_FLOCK=1 -DHAVE_LOCK_FCNTL=1 \
|
||||
-DHAVE_GETRUSAGE=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_KILLPG=1 \
|
||||
-DHAVE_MEMMOVE=1 -DHAVE_MKNOD=0 -DHAVE_MMAP=1 -DHAVE_NICE=1 \
|
||||
-DHAVE_REVOKE=0 -DHAVE_SETLOCALE_CTYPE=0 \
|
||||
-DHAVE_LANGINFO_CODESET=0 -DHAVE_SELECT=1 -DHAVE_SETRESUGID=1 \
|
||||
-DHAVE_SETGROUPS=1 -DHAVE_STRERROR=1 -DHAVE_STRSIGNAL=0 \
|
||||
-DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 -DHAVE_REVOKE_DECL=1 \
|
||||
-DHAVE_SYS_ERRLIST_DECL=0 -DHAVE_SYS_SIGLIST_DECL=1 \
|
||||
-DHAVE_PERSISTENT_HISTORY=0 -DHAVE_SILENT_IDIVWRAPV=0 \
|
||||
-DMKSH_BUILD_R=431
|
||||
|
||||
# check categories: shell:legacy-no int:32 android convfds no-histfile
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
|
3
NOTICE
3
NOTICE
|
@ -1,7 +1,8 @@
|
|||
mksh is covered by The MirOS Licence:
|
||||
|
||||
/*-
|
||||
* Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
||||
* Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
|
179
mkmf.sh
179
mkmf.sh
|
@ -1,5 +1,5 @@
|
|||
# Copyright © 2010
|
||||
# Thorsten Glaser <t.glaser@tarent.de>
|
||||
# Copyright © 2010, 2012, 2013
|
||||
# Thorsten Glaser <tg@mirbsd.org>
|
||||
# This file is provided under the same terms as mksh.
|
||||
#-
|
||||
# Helper script to let src/Build.sh generate Makefrag.inc
|
||||
|
@ -8,11 +8,21 @@
|
|||
# This script is supposed to be run from/inside AOSP by the
|
||||
# porter of mksh to Android (and only manually).
|
||||
|
||||
if test x"$1" = x"-t"; then
|
||||
# test compilation
|
||||
args=-r
|
||||
mkmfmode=1
|
||||
else
|
||||
# prepare for AOSP
|
||||
args=-M
|
||||
mkmfmode=0
|
||||
fi
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
srcdir=$(pwd)
|
||||
rm -rf tmp
|
||||
mkdir tmp
|
||||
cd ../../..
|
||||
cd ../..
|
||||
aospdir=$(pwd)
|
||||
cd $srcdir/tmp
|
||||
|
||||
|
@ -42,64 +52,124 @@ LIBS=
|
|||
# Since we no longer use the NDK, the AOSP has to have been
|
||||
# built before using this script (targetting generic/emulator).
|
||||
|
||||
CC=$aospdir/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-gcc
|
||||
addvar CPPFLAGS -I$aospdir/system/core/include \
|
||||
-I$aospdir/hardware/libhardware/include \
|
||||
-I$aospdir/system/core/include \
|
||||
-I$aospdir/hardware/libhardware/include \
|
||||
-I$aospdir/hardware/libhardware_legacy/include \
|
||||
-I$aospdir/hardware/ril/include \
|
||||
-I$aospdir/libnativehelper/include \
|
||||
-I$aospdir/frameworks/base/include \
|
||||
-I$aospdir/frameworks/base/opengl/include \
|
||||
-I$aospdir/external/skia/include \
|
||||
-I$aospdir/out/target/product/generic/obj/include \
|
||||
-I$aospdir/bionic/libc/arch-arm/include \
|
||||
-I$aospdir/bionic/libc/include \
|
||||
-I$aospdir/bionic/libstdc++/include \
|
||||
-I$aospdir/bionic/libc/kernel/common \
|
||||
-I$aospdir/bionic/libc/kernel/arch-arm \
|
||||
-I$aospdir/bionic/libm/include \
|
||||
-I$aospdir/bionic/libm/include/arch/arm \
|
||||
-I$aospdir/bionic/libthread_db/include \
|
||||
-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ \
|
||||
-I$aospdir/system/core/include/arch/linux-arm/ \
|
||||
-include $aospdir/system/core/include/arch/linux-arm/AndroidConfig.h \
|
||||
CC=$aospdir/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-gcc
|
||||
addvar CPPFLAGS \
|
||||
-I$aospdir/libnativehelper/include/nativehelper \
|
||||
-isystem $aospdir/system/core/include \
|
||||
-isystem $aospdir/hardware/libhardware/include \
|
||||
-isystem $aospdir/hardware/libhardware_legacy/include \
|
||||
-isystem $aospdir/hardware/ril/include \
|
||||
-isystem $aospdir/libnativehelper/include \
|
||||
-isystem $aospdir/frameworks/native/include \
|
||||
-isystem $aospdir/frameworks/native/opengl/include \
|
||||
-isystem $aospdir/frameworks/av/include \
|
||||
-isystem $aospdir/frameworks/base/include \
|
||||
-isystem $aospdir/frameworks/base/opengl/include \
|
||||
-isystem $aospdir/external/skia/include \
|
||||
-isystem $aospdir/out/target/product/generic/obj/include \
|
||||
-isystem $aospdir/bionic/libc/arch-arm/include \
|
||||
-isystem $aospdir/bionic/libc/include \
|
||||
-isystem $aospdir/bionic/libstdc++/include \
|
||||
-isystem $aospdir/bionic/libc/kernel/common \
|
||||
-isystem $aospdir/bionic/libc/kernel/arch-arm \
|
||||
-isystem $aospdir/bionic/libm/include \
|
||||
-isystem $aospdir/bionic/libm/include/arm \
|
||||
-isystem $aospdir/bionic/libthread_db/include \
|
||||
-D_FORTIFY_SOURCE=1 \
|
||||
-include $aospdir/build/core/combo/include/arch/linux-arm/AndroidConfig.h \
|
||||
-I$aospdir/build/core/combo/include/arch/linux-arm/ \
|
||||
-DANDROID -DNDEBUG -UDEBUG
|
||||
addvar CFLAGS -fno-exceptions -Wno-multichar -msoft-float -fpic \
|
||||
-ffunction-sections -funwind-tables -fstack-protector -fno-short-enums \
|
||||
-march=armv5te -mtune=xscale -mthumb-interwork -fmessage-length=0 \
|
||||
-W -Wall -Wno-unused -Winit-self -Wpointer-arith -Werror=return-type \
|
||||
-Werror=non-virtual-dtor -Werror=address -Werror=sequence-point \
|
||||
-Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once \
|
||||
-fgcse-after-reload -frerun-cse-after-loop -frename-registers -mthumb \
|
||||
-Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64
|
||||
addvar LDFLAGS -nostdlib -Bdynamic -Wl,-T,$aospdir/build/core/armelf.x \
|
||||
-Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections \
|
||||
-Wl,-z,nocopyreloc -Wl,--no-undefined \
|
||||
# who would have thought the AOSP devs are funny? -fno-builtin-sin
|
||||
addvar CFLAGS \
|
||||
-fno-exceptions \
|
||||
-Wno-multichar \
|
||||
-msoft-float \
|
||||
-fpic \
|
||||
-fPIE \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-funwind-tables \
|
||||
-fstack-protector \
|
||||
-Wa,--noexecstack \
|
||||
-Werror=format-security \
|
||||
-fno-short-enums \
|
||||
-march=armv7-a \
|
||||
-mfloat-abi=softfp \
|
||||
-mfpu=vfpv3-d16 \
|
||||
-Wno-unused-but-set-variable \
|
||||
-fno-builtin-sin \
|
||||
-fno-strict-volatile-bitfields \
|
||||
-Wno-psabi \
|
||||
-mthumb-interwork \
|
||||
-fmessage-length=0 \
|
||||
-W \
|
||||
-Wall \
|
||||
-Wno-unused \
|
||||
-Winit-self \
|
||||
-Wpointer-arith \
|
||||
-Werror=return-type \
|
||||
-Werror=non-virtual-dtor \
|
||||
-Werror=address \
|
||||
-Werror=sequence-point \
|
||||
-g \
|
||||
-Wstrict-aliasing=2 \
|
||||
-fgcse-after-reload \
|
||||
-frerun-cse-after-loop \
|
||||
-frename-registers \
|
||||
-mthumb \
|
||||
-Os \
|
||||
-fomit-frame-pointer \
|
||||
-fno-strict-aliasing
|
||||
addvar LDFLAGS \
|
||||
-nostdlib \
|
||||
-Bdynamic \
|
||||
-pie \
|
||||
-Wl,-dynamic-linker,/system/bin/linker \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,-z,nocopyreloc \
|
||||
-Wl,-z,noexecstack \
|
||||
-Wl,-z,relro \
|
||||
-Wl,-z,now \
|
||||
-Wl,--warn-shared-textrel \
|
||||
-Wl,--icf=safe \
|
||||
-Wl,--fix-cortex-a8 \
|
||||
-Wl,--no-undefined \
|
||||
$aospdir/out/target/product/generic/obj/lib/crtbegin_dynamic.o
|
||||
addvar LIBS -L$aospdir/out/target/product/generic/obj/lib \
|
||||
-Wl,-rpath-link=$aospdir/out/target/product/generic/obj/lib -lc \
|
||||
$aospdir/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/interwork/libgcc.a \
|
||||
addvar LIBS \
|
||||
-L$aospdir/out/target/product/generic/obj/lib \
|
||||
-Wl,-rpath-link=$aospdir/out/target/product/generic/obj/lib \
|
||||
-lc \
|
||||
-Wl,--no-whole-archive \
|
||||
$aospdir/out/target/product/generic/obj/STATIC_LIBRARIES/libcompiler-rt-extras_intermediates/libcompiler-rt-extras.a \
|
||||
$aospdir/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7/bin/../lib/gcc/arm-linux-androideabi/4.7/armv7-a/libgcc.a \
|
||||
$aospdir/out/target/product/generic/obj/lib/crtend_android.o
|
||||
|
||||
|
||||
### Flags used by test builds
|
||||
if test $mkmfmode = 1; then
|
||||
addvar CPPFLAGS '-DMKSHRC_PATH=\"/system/etc/mkshrc\"'
|
||||
addvar CPPFLAGS '-DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\"'
|
||||
addvar CPPFLAGS '-DMKSH_DEFAULT_TMPDIR=\"/data/local/tmp\"'
|
||||
fi
|
||||
|
||||
### Override flags
|
||||
# We don’t even *support* UTF-8 by default ☹
|
||||
addvar CPPFLAGS -DMKSH_ASSUME_UTF8=0
|
||||
# Let the shell free all memory upon exiting
|
||||
addvar CPPFLAGS -DDEBUG_LEAKS
|
||||
# UTF-8 works nowadays
|
||||
addvar CPPFLAGS -DMKSH_ASSUME_UTF8
|
||||
# Reduce filedescriptor usage
|
||||
addvar CPPFLAGS -DMKSH_CONSERVATIVE_FDS
|
||||
# Leave out RCS ID strings from the binary
|
||||
addvar CPPFLAGS -DMKSH_DONT_EMIT_IDSTRING
|
||||
# No getpwnam() calls (affects "cd ~username/" only)
|
||||
addvar CPPFLAGS -DMKSH_NOPWNAM
|
||||
# Compile an extra small mksh (optional)
|
||||
#addvar CPPFLAGS -DMKSH_SMALL
|
||||
# Leave out the ulimit builtin
|
||||
#addvar CPPFLAGS -DMKSH_NO_LIMITS
|
||||
# Compile an extra small mksh (optional)
|
||||
#addvar CPPFLAGS -DMKSH_SMALL
|
||||
|
||||
# Set target platform
|
||||
TARGET_OS=Linux
|
||||
# Building with -std=c99 or -std=gnu99 clashes with Bionic headers
|
||||
HAVE_CAN_STDG99=0
|
||||
HAVE_CAN_STDC99=0
|
||||
export HAVE_CAN_STDG99 HAVE_CAN_STDC99
|
||||
TARGET_OS=Android
|
||||
|
||||
# Android-x86 does not have helper functions for ProPolice SSP
|
||||
# and AOSP adds the flags by itself (same for warning flags)
|
||||
|
@ -108,16 +178,19 @@ HAVE_CAN_FSTACKPROTECTORALL=0
|
|||
HAVE_CAN_WALL=0
|
||||
export HAVE_CAN_FNOSTRICTALIASING HAVE_CAN_FSTACKPROTECTORALL HAVE_CAN_WALL
|
||||
|
||||
# disable the mknod(8) built-in to get rid of needing setmode.c
|
||||
HAVE_MKNOD=0; export HAVE_MKNOD
|
||||
|
||||
# even the idea of persistent history on a phone is funny
|
||||
HAVE_PERSISTENT_HISTORY=0; export HAVE_PERSISTENT_HISTORY
|
||||
|
||||
# this is a run-time check and dependent on the target CPU
|
||||
# architecture (at _least_!) and cannot be auto-detected,
|
||||
# so always include the safety check even if unnecessary
|
||||
HAVE_SILENT_IDIVWRAPV=0; export HAVE_SILENT_IDIVWRAPV
|
||||
|
||||
# ... and run it!
|
||||
export CC CPPFLAGS CFLAGS LDFLAGS LIBS TARGET_OS
|
||||
sh ../src/Build.sh -M
|
||||
sh ../src/Build.sh $args
|
||||
rv=$?
|
||||
test x"$args" = x"-r" && exit $rv
|
||||
test x0 = x"$rv" && mv -f Makefrag.inc ../
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
|
|
37
mkshrc
37
mkshrc
|
@ -1,11 +1,13 @@
|
|||
# Copyright (c) 2010
|
||||
# Thorsten Glaser <t.glaser@tarent.de>
|
||||
# Copyright (c) 2010, 2012, 2013
|
||||
# Thorsten Glaser <tg@mirbsd.org>
|
||||
# This file is provided under the same terms as mksh.
|
||||
#-
|
||||
# Minimal /system/etc/mkshrc for Android
|
||||
#
|
||||
# Support: https://launchpad.net/mksh
|
||||
|
||||
: ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${HOSTNAME:=android}
|
||||
: ${SHELL:=$MKSH} ${USER:=$(typeset x=$(id); x=${x#*\(}; print -r -- ${x%%\)*})}
|
||||
: ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${HOSTNAME:=$(getprop ro.product.device)}
|
||||
: ${SHELL:=$MKSH} ${USER:=$(typeset x=$(id); x=${x#*\(}; print -r -- ${x%%\)*})} ${HOSTNAME:=android}
|
||||
if (( USER_ID )); then PS1='$'; else PS1='#'; fi
|
||||
function precmd {
|
||||
typeset e=$?
|
||||
|
@ -13,12 +15,37 @@ function precmd {
|
|||
(( e )) && print -n "$e|"
|
||||
}
|
||||
PS1='$(precmd)$USER@$HOSTNAME:${PWD:-?} '"$PS1 "
|
||||
export HOME HOSTNAME MKSH PS1 SHELL TERM USER
|
||||
export HOME HOSTNAME MKSH SHELL TERM USER
|
||||
alias l='ls'
|
||||
alias la='l -a'
|
||||
alias ll='l -l'
|
||||
alias lo='l -a -l'
|
||||
|
||||
function hd {
|
||||
cat "$@" | command hd /proc/self/fd/0
|
||||
}
|
||||
|
||||
function more {
|
||||
local dummy line llen curlin=0
|
||||
|
||||
cat "$@" | while IFS= read -r line; do
|
||||
llen=${%line}
|
||||
(( llen == -1 )) && llen=${#line}
|
||||
(( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
|
||||
if (( (curlin += llen) >= LINES )); then
|
||||
print -n -- '\033[7m--more--\033[0m'
|
||||
read -u1 dummy
|
||||
[[ $dummy = [Qq]* ]] && return 0
|
||||
curlin=$llen
|
||||
fi
|
||||
print -r -- "$line"
|
||||
done
|
||||
}
|
||||
|
||||
function setenv {
|
||||
eval export "\"$1\""'="$2"'
|
||||
}
|
||||
|
||||
for p in ~/.bin; do
|
||||
[[ -d $p/. ]] || continue
|
||||
[[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
This is mksh from AnonCVS on 2010-08-24 with the
|
||||
following files removed:
|
||||
• Makefile
|
||||
(not part of regular mksh releases anyway)
|
||||
• dot.mkshrc
|
||||
(not needed, we use our own for Android)
|
||||
• mksh.1
|
||||
(manpage; also available from the web)
|
||||
• setmode.c
|
||||
(not needed, we don’t use the mknod builtin)
|
||||
• strlcpy.c
|
||||
(not needed, bionic provides this)
|
||||
|
||||
The manual page can be downloaded as PDF (ISO A4 paper) from
|
||||
https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh.pdf or read
|
||||
online at https://www.mirbsd.org/man1/mksh (HTML).
|
||||
|
||||
The following changes are done to code in this subdirectory
|
||||
at the moment:
|
||||
• check.t main.sh: remove the 'stop' alias to 'kill -STOP'
|
||||
since Android has a built in stop command that the alias
|
||||
overrides, causing problems with testing tools
|
1189
src/Build.sh
1189
src/Build.sh
File diff suppressed because it is too large
Load diff
97
src/Makefile
97
src/Makefile
|
@ -1,97 +0,0 @@
|
|||
# $MirOS: src/bin/mksh/Makefile,v 1.88 2011/10/07 19:51:17 tg Exp $
|
||||
#-
|
||||
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
# Thorsten Glaser <tg@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
# are retained or reproduced in an accompanying document, permission
|
||||
# is granted to deal in this work without restriction, including un-
|
||||
# limited rights to use, publicly perform, distribute, sell, modify,
|
||||
# merge, give away, or sublicence.
|
||||
#
|
||||
# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
|
||||
# the utmost extent permitted by applicable law, neither express nor
|
||||
# implied; without malicious intent or gross negligence. In no event
|
||||
# may a licensor, author or contributor be held liable for indirect,
|
||||
# direct, other damage, loss, or other issues arising in any way out
|
||||
# of dealing in the work, even if advised of the possibility of such
|
||||
# damage or existence of a defect, except proven that it results out
|
||||
# of said person's immediate fault when using the work as intended.
|
||||
#-
|
||||
# use CPPFLAGS=-DDEBUG __CRAZY=Yes to check for certain more stuff
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
PROG= mksh
|
||||
SRCS= edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c \
|
||||
lalloc.c lex.c main.c misc.c shf.c syn.c tree.c var.c
|
||||
.if !make(test-build)
|
||||
CPPFLAGS+= -DMKSH_ASSUME_UTF8 \
|
||||
-DHAVE_ATTRIBUTE_BOUNDED=1 -DHAVE_ATTRIBUTE_FORMAT=1 \
|
||||
-DHAVE_ATTRIBUTE_NONNULL=1 -DHAVE_ATTRIBUTE_NORETURN=1 \
|
||||
-DHAVE_ATTRIBUTE_UNUSED=1 -DHAVE_ATTRIBUTE_USED=1 \
|
||||
-DHAVE_SYS_BSDTYPES_H=0 -DHAVE_SYS_FILE_H=1 \
|
||||
-DHAVE_SYS_MKDEV_H=0 -DHAVE_SYS_MMAN_H=1 -DHAVE_SYS_PARAM_H=1 \
|
||||
-DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SYSMACROS_H=0 \
|
||||
-DHAVE_BSTRING_H=0 -DHAVE_GRP_H=1 -DHAVE_LIBGEN_H=1 \
|
||||
-DHAVE_LIBUTIL_H=0 -DHAVE_PATHS_H=1 -DHAVE_STDINT_H=1 \
|
||||
-DHAVE_STRINGS_H=1 -DHAVE_ULIMIT_H=0 -DHAVE_VALUES_H=0 \
|
||||
-DHAVE_CAN_INTTYPES=1 -DHAVE_CAN_UCBINTS=1 \
|
||||
-DHAVE_CAN_INT8TYPE=1 -DHAVE_CAN_UCBINT8=1 -DHAVE_RLIM_T=1 \
|
||||
-DHAVE_SIG_T=1 -DHAVE_SYS_SIGNAME=1 -DHAVE_SYS_SIGLIST=1 \
|
||||
-DHAVE_STRSIGNAL=0 -DHAVE_GETRUSAGE=1 -DHAVE_KILLPG=1 \
|
||||
-DHAVE_MKNOD=0 -DHAVE_MKSTEMP=1 -DHAVE_NICE=1 -DHAVE_REVOKE=1 \
|
||||
-DHAVE_SETLOCALE_CTYPE=0 -DHAVE_LANGINFO_CODESET=0 \
|
||||
-DHAVE_SELECT=1 -DHAVE_SETRESUGID=1 -DHAVE_SETGROUPS=1 \
|
||||
-DHAVE_STRCASESTR=1 -DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 \
|
||||
-DHAVE_REVOKE_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \
|
||||
-DHAVE_PERSISTENT_HISTORY=1
|
||||
COPTS+= -std=gnu99 -Wall
|
||||
.endif
|
||||
|
||||
USE_PRINTF_BUILTIN?= 0
|
||||
.if ${USE_PRINTF_BUILTIN} == 1
|
||||
.PATH: ${BSDSRCDIR}/usr.bin/printf
|
||||
SRCS+= printf.c
|
||||
CPPFLAGS+= -DMKSH_PRINTF_BUILTIN
|
||||
.endif
|
||||
|
||||
MANLINKS= [ false pwd sh sleep test true
|
||||
BINLINKS= ${MANLINKS} echo domainname kill
|
||||
.for _i in ${BINLINKS}
|
||||
LINKS+= ${BINDIR}/${PROG} ${BINDIR}/${_i}
|
||||
.endfor
|
||||
.for _i in ${MANLINKS}
|
||||
MLINKS+= ${PROG}.1 ${_i}.1
|
||||
.endfor
|
||||
|
||||
regress: ${PROG} check.pl check.t
|
||||
-rm -rf regress-dir
|
||||
mkdir -p regress-dir
|
||||
echo export FNORD=666 >regress-dir/.mkshrc
|
||||
HOME=$$(realpath regress-dir) perl ${.CURDIR}/check.pl \
|
||||
-s ${.CURDIR}/check.t -v -p ./${PROG}
|
||||
|
||||
test-build: .PHONY
|
||||
-rm -rf build-dir
|
||||
mkdir -p build-dir
|
||||
.if ${USE_PRINTF_BUILTIN} == 1
|
||||
cp ${BSDSRCDIR}/usr.bin/printf/printf.c build-dir/
|
||||
.endif
|
||||
cd build-dir; env CC=${CC:Q} CFLAGS=${CFLAGS:M*:Q} \
|
||||
CPPFLAGS=${CPPFLAGS:M*:Q} LDFLAGS=${LDFLAGS:M*:Q} \
|
||||
LIBS= NOWARN=-Wno-error TARGET_OS= CPP= /bin/sh \
|
||||
${.CURDIR}/Build.sh -Q -r && ./test.sh -v
|
||||
|
||||
cleandir: clean-extra
|
||||
|
||||
clean-extra: .PHONY
|
||||
-rm -rf build-dir regress-dir printf.o printf.ln
|
||||
|
||||
distribution:
|
||||
sed 's!\$$I''d\([:$$]\)!$$M''irSecuCron\1!g' \
|
||||
${.CURDIR}/dot.mkshrc >${DESTDIR}/etc/skel/.mkshrc
|
||||
chown ${BINOWN}:${CONFGRP} ${DESTDIR}/etc/skel/.mkshrc
|
||||
chmod 0644 ${DESTDIR}/etc/skel/.mkshrc
|
||||
|
||||
.include <bsd.prog.mk>
|
29
src/check.pl
29
src/check.pl
|
@ -1,7 +1,7 @@
|
|||
# $MirOS: src/bin/mksh/check.pl,v 1.27 2011/05/29 02:18:47 tg Exp $
|
||||
# $MirOS: src/bin/mksh/check.pl,v 1.31 2012/04/06 12:22:14 tg Exp $
|
||||
# $OpenBSD: th,v 1.13 2006/05/18 21:27:23 miod Exp $
|
||||
#-
|
||||
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011
|
||||
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012
|
||||
# Thorsten Glaser <tg@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -75,6 +75,7 @@
|
|||
# USER
|
||||
# (values taken from the environment of
|
||||
# the test harness).
|
||||
# CYGWIN is set to nodosfilewarning.
|
||||
# ENV is set to /nonexistant.
|
||||
# __progname is set to the -p argument.
|
||||
# __perlname is set to $^X (perlexe).
|
||||
|
@ -150,7 +151,24 @@
|
|||
# p tag takes parameters (used with m).
|
||||
# s tag can be used several times.
|
||||
|
||||
use POSIX qw(EINTR);
|
||||
# pull EINTR from POSIX.pm or Errno.pm if they exist
|
||||
# otherwise just skip it
|
||||
BEGIN {
|
||||
$EINTR = 0;
|
||||
eval {
|
||||
require POSIX;
|
||||
$EINTR = POSIX::EINTR();
|
||||
};
|
||||
if ($@) {
|
||||
eval {
|
||||
require Errno;
|
||||
$EINTR = Errno::EINTR();
|
||||
} or do {
|
||||
$EINTR = 0;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
use Getopt::Std;
|
||||
use Config;
|
||||
|
||||
|
@ -262,6 +280,7 @@ foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
|
|||
'PATH', 'SHELL', 'UNIXMODE', 'USER')) {
|
||||
$new_env{$env} = $ENV{$env} if defined $ENV{$env};
|
||||
}
|
||||
$new_env{'CYGWIN'} = 'nodosfilewarning';
|
||||
$new_env{'ENV'} = '/nonexistant';
|
||||
if (($os eq 'VMS') || ($Config{perlpath} =~ m/$Config{_exe}$/i)) {
|
||||
$new_env{'__perlname'} = $Config{perlpath};
|
||||
|
@ -555,7 +574,9 @@ run_test
|
|||
$xpid = waitpid($pid, 0);
|
||||
$child_kill_ok = 0;
|
||||
if ($xpid < 0) {
|
||||
next if $! == EINTR;
|
||||
if ($EINTR) {
|
||||
next if $! == $EINTR;
|
||||
}
|
||||
print STDERR "$prog: error waiting for child - $!\n";
|
||||
return undef;
|
||||
}
|
||||
|
|
2115
src/check.t
2115
src/check.t
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,8 @@
|
|||
# $Id$
|
||||
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.65 2011/08/27 18:06:40 tg Exp $
|
||||
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.77 2013/02/17 15:58:26 tg Exp $
|
||||
#-
|
||||
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013
|
||||
# Thorsten Glaser <tg@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -21,19 +22,21 @@
|
|||
#-
|
||||
# ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
|
||||
|
||||
: ${EDITOR:=/bin/ed} ${TERM:=vt100} ${HOSTNAME:=$(ulimit -c 0;hostname -s 2>&-)}
|
||||
[[ $HOSTNAME = @(localhost|*([ ])) ]] && HOSTNAME=$(ulimit -c 0;hostname 2>&-)
|
||||
: ${HOSTNAME:=nil}; if (( USER_ID )); then PS1='$'; else PS1='#'; fi
|
||||
PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \
|
||||
2>/dev/null)} = *([ ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \
|
||||
2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100}
|
||||
function precmd {
|
||||
local e=$?
|
||||
|
||||
(( e )) && print -n "$e|"
|
||||
# precmd is required to retain the errorlevel when ${ …;} is used
|
||||
return $e
|
||||
}
|
||||
PS1=$'\001\r''$(precmd)${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?
|
||||
)}@${HOSTNAME%%.*}:$(local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || \
|
||||
PS1=$'\001\r''${ precmd;}${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?
|
||||
)}@${HOSTNAME%%.*}:${ local e=$? d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || \
|
||||
d=${d/#$p/~}; local m=${%d} n p=...; (( m > 0 )) || m=${#d}
|
||||
(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || \
|
||||
p=; print -nr -- "$p$d") '"$PS1 "
|
||||
p=; print -nr -- "$p$d"; return $e;} '"$PS1 "
|
||||
: ${MKSH:=$(whence -p mksh)}; export EDITOR HOSTNAME MKSH TERM USER
|
||||
alias ls=ls
|
||||
unalias ls
|
||||
|
@ -41,20 +44,20 @@ alias l='ls -F'
|
|||
alias la='l -a'
|
||||
alias ll='l -l'
|
||||
alias lo='l -alo'
|
||||
whence -p rot13 >&- || alias rot13='tr \
|
||||
whence -p rot13 >/dev/null || alias rot13='tr \
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
|
||||
nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
|
||||
whence -p hd >&- || function hd {
|
||||
whence -p hd >/dev/null || function hd {
|
||||
hexdump -e '"%08.8_ax " 8/1 "%02X " " - " 8/1 "%02X "' \
|
||||
-e '" |" "%_p"' -e '"|\n"' "$@"
|
||||
}
|
||||
|
||||
# Berkeley C shell compatible dirs, popd, and pushd functions
|
||||
# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
|
||||
DIRSTACKBASE=$(realpath ~/. 2>&- || print -nr -- "$HOME")
|
||||
DIRSTACKBASE=$(realpath ~/. 2>/dev/null || print -nr -- "${HOME:-/}")
|
||||
set -A DIRSTACK
|
||||
function chpwd {
|
||||
DIRSTACK[0]=$(realpath . 2>&- || print -r -- "$PWD")
|
||||
DIRSTACK[0]=$(realpath . 2>/dev/null || print -r -- "$PWD")
|
||||
[[ $DIRSTACKBASE = ?(*/) ]] || \
|
||||
DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/~}
|
||||
:
|
||||
|
@ -75,10 +78,8 @@ function cd_csh {
|
|||
}
|
||||
function dirs {
|
||||
local d dwidth
|
||||
local -i isnoglob=0 fl=0 fv=0 fn=0 cpos=0
|
||||
local -i fl=0 fv=0 fn=0 cpos=0
|
||||
|
||||
[[ $(set +o) == *@(-o noglob)@(| *) ]] && isnoglob=1
|
||||
set -o noglob
|
||||
while getopts ":lvn" d; do
|
||||
case $d {
|
||||
(l) fl=1 ;;
|
||||
|
@ -117,18 +118,15 @@ function dirs {
|
|||
done
|
||||
print
|
||||
fi
|
||||
(( isnoglob )) || set +o noglob
|
||||
return 0
|
||||
}
|
||||
function popd {
|
||||
local d fa
|
||||
local -i isnoglob=0 n=1
|
||||
local -i n=1
|
||||
|
||||
[[ $(set +o) == *@(-o noglob)@(| *) ]] && isnoglob=1
|
||||
set -o noglob
|
||||
while getopts ":0123456789lvn" d; do
|
||||
case $d {
|
||||
(l|v|n) fa="$fa -$d" ;;
|
||||
(l|v|n) fa+=" -$d" ;;
|
||||
(+*) n=2
|
||||
break ;;
|
||||
(*) print -u2 'Usage: popd [-lvn] [+<n>].'
|
||||
|
@ -156,18 +154,15 @@ function popd {
|
|||
unset DIRSTACK[n]
|
||||
set -A DIRSTACK -- "${DIRSTACK[@]}"
|
||||
cd_csh "${DIRSTACK[0]}" || return 1
|
||||
(( isnoglob )) || set +o noglob
|
||||
dirs $fa
|
||||
}
|
||||
function pushd {
|
||||
local d fa
|
||||
local -i isnoglob=0 n=1
|
||||
local -i n=1
|
||||
|
||||
[[ $(set +o) == *@(-o noglob)@(| *) ]] && isnoglob=1
|
||||
set -o noglob
|
||||
while getopts ":0123456789lvn" d; do
|
||||
case $d {
|
||||
(l|v|n) fa="$fa -$d" ;;
|
||||
(l|v|n) fa+=" -$d" ;;
|
||||
(+*) n=2
|
||||
break ;;
|
||||
(*) print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
|
||||
|
@ -201,7 +196,6 @@ function pushd {
|
|||
set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
|
||||
cd_csh "$1" || return 1
|
||||
fi
|
||||
(( isnoglob )) || set +o noglob
|
||||
dirs $fa
|
||||
}
|
||||
|
||||
|
@ -229,7 +223,7 @@ function Lb64decode {
|
|||
set +U
|
||||
local c s="$*" t=
|
||||
[[ -n $s ]] || { s=$(cat;print x); s=${s%x}; }
|
||||
local -i i=0 n=${#s} p=0 v x
|
||||
local -i i=0 j=0 n=${#s} p=0 v x
|
||||
local -i16 o
|
||||
|
||||
while (( i < n )); do
|
||||
|
@ -252,7 +246,10 @@ function Lb64decode {
|
|||
p=0
|
||||
;;
|
||||
}
|
||||
t=$t\\x${o#16#}
|
||||
t+=\\x${o#16#}
|
||||
(( ++j & 4095 )) && continue
|
||||
print -n $t
|
||||
t=
|
||||
done
|
||||
print -n $t
|
||||
(( u )) || set -U
|
||||
|
@ -278,14 +275,14 @@ function Lb64encode {
|
|||
(( v |= j << 8 ))
|
||||
(( j = i < n ? s[i++] : 0 ))
|
||||
(( v |= j ))
|
||||
t=$t${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]}
|
||||
t+=${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]}
|
||||
c=${Lb64encode_code[v >> 6 & 63]}
|
||||
if (( i <= n )); then
|
||||
t=$t$c${Lb64encode_code[v & 63]}
|
||||
t+=$c${Lb64encode_code[v & 63]}
|
||||
elif (( i == n + 1 )); then
|
||||
t=$t$c=
|
||||
t+=$c=
|
||||
else
|
||||
t=$t==
|
||||
t+===
|
||||
fi
|
||||
if (( ${#t} == 76 || i >= n )); then
|
||||
print $t
|
||||
|
@ -332,6 +329,9 @@ function Lnzaathash {
|
|||
function Lnzathash {
|
||||
Lnzathash_v=0
|
||||
Lnzathash_add "$@"
|
||||
Lnzathash_end
|
||||
}
|
||||
function Lnzathash_end {
|
||||
if (( Lnzathash_v )); then
|
||||
Lnzaathash_end
|
||||
else
|
||||
|
@ -351,7 +351,7 @@ function Lstripcom {
|
|||
|
||||
# give MidnightBSD's laffer1 a bit of csh feeling
|
||||
function setenv {
|
||||
eval export $1'="$2"'
|
||||
eval export "\"$1\""'="$2"'
|
||||
}
|
||||
|
||||
: place customisations below this line
|
||||
|
|
594
src/edit.c
594
src/edit.c
File diff suppressed because it is too large
Load diff
210
src/eval.c
210
src/eval.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: eval.c,v 1.37 2011/10/11 14:32:43 otto Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.109 2011/10/11 19:06:07 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.136 2013/02/10 23:43:59 tg Exp $");
|
||||
|
||||
/*
|
||||
* string expansion
|
||||
|
@ -40,7 +41,7 @@ typedef struct Expand {
|
|||
struct shf *shf; /* file */
|
||||
} u; /* source */
|
||||
struct tbl *var; /* variable in ${var..} */
|
||||
short split; /* split "$@" / call waitlast $() */
|
||||
bool split; /* split "$@" / call waitlast $() */
|
||||
} Expand;
|
||||
|
||||
#define XBASE 0 /* scanning original */
|
||||
|
@ -57,12 +58,12 @@ typedef struct Expand {
|
|||
#define IFS_NWS 2 /* have seen IFS non-white-space */
|
||||
|
||||
static int varsub(Expand *, const char *, const char *, int *, int *);
|
||||
static int comsub(Expand *, const char *);
|
||||
static int comsub(Expand *, const char *, int);
|
||||
static void funsub(struct op *);
|
||||
static char *trimsub(char *, char *, int);
|
||||
static void glob(char *, XPtrV *, int);
|
||||
static void glob(char *, XPtrV *, bool);
|
||||
static void globit(XString *, char **, char *, XPtrV *, int);
|
||||
static const char *maybe_expand_tilde(const char *, XString *, char **, int);
|
||||
static char *tilde(char *);
|
||||
#ifndef MKSH_NOPWNAM
|
||||
static char *homedir(char *);
|
||||
#endif
|
||||
|
@ -189,8 +190,8 @@ typedef struct SubType {
|
|||
struct tbl *var; /* variable for ${var..} */
|
||||
struct SubType *prev; /* old type */
|
||||
struct SubType *next; /* poped type (to avoid re-allocating) */
|
||||
size_t base; /* begin position of expanded word */
|
||||
short stype; /* [=+-?%#] action after expanded word */
|
||||
short base; /* begin position of expanded word */
|
||||
short f; /* saved value of f (DOPAT, etc) */
|
||||
uint8_t quotep; /* saved value of quote (for ${..[%#]..}) */
|
||||
uint8_t quotew; /* saved value of quote (for ${..[+-=]..}) */
|
||||
|
@ -216,8 +217,8 @@ expand(const char *cp, /* input word */
|
|||
SubType st_head, *st;
|
||||
/* For trailing newlines in COMSUB */
|
||||
int newlines = 0;
|
||||
int saw_eq, tilde_ok;
|
||||
int make_magic;
|
||||
bool saw_eq, make_magic;
|
||||
int tilde_ok;
|
||||
size_t len;
|
||||
|
||||
if (cp == NULL)
|
||||
|
@ -239,11 +240,11 @@ expand(const char *cp, /* input word */
|
|||
type = XBASE;
|
||||
sp = cp;
|
||||
fdo = 0;
|
||||
saw_eq = 0;
|
||||
saw_eq = false;
|
||||
/* must be 1/0 */
|
||||
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
|
||||
doblank = 0;
|
||||
make_magic = 0;
|
||||
make_magic = false;
|
||||
word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
|
||||
/* clang doesn't know OSUBST comes before CSUBST */
|
||||
memset(&st_head, 0, sizeof(st_head));
|
||||
|
@ -277,17 +278,27 @@ expand(const char *cp, /* input word */
|
|||
quote = st->quotew;
|
||||
continue;
|
||||
case COMSUB:
|
||||
case FUNSUB:
|
||||
tilde_ok = 0;
|
||||
if (f & DONTRUNCOMMAND) {
|
||||
word = IFS_WORD;
|
||||
*dp++ = '$'; *dp++ = '(';
|
||||
*dp++ = '$';
|
||||
if (c == FUNSUB) {
|
||||
*dp++ = '{';
|
||||
*dp++ = ' ';
|
||||
} else
|
||||
*dp++ = '(';
|
||||
while (*sp != '\0') {
|
||||
Xcheck(ds, dp);
|
||||
*dp++ = *sp++;
|
||||
}
|
||||
*dp++ = ')';
|
||||
if (c == FUNSUB) {
|
||||
*dp++ = ';';
|
||||
*dp++ = '}';
|
||||
} else
|
||||
*dp++ = ')';
|
||||
} else {
|
||||
type = comsub(&x, sp);
|
||||
type = comsub(&x, sp, c);
|
||||
if (type == XCOM && (f&DOBLANK))
|
||||
doblank++;
|
||||
sp = strnul(sp) + 1;
|
||||
|
@ -368,16 +379,52 @@ expand(const char *cp, /* input word */
|
|||
st->stype = stype;
|
||||
st->base = Xsavepos(ds, dp);
|
||||
st->f = f;
|
||||
st->var = x.var;
|
||||
if (x.var == &vtemp) {
|
||||
st->var = tempvar();
|
||||
st->var->flag &= ~INTEGER;
|
||||
/* can't fail here */
|
||||
setstr(st->var,
|
||||
str_val(x.var),
|
||||
KSH_RETURN_ERROR | 0x4);
|
||||
} else
|
||||
st->var = x.var;
|
||||
|
||||
st->quotew = st->quotep = quote;
|
||||
/* skip qualifier(s) */
|
||||
if (stype)
|
||||
sp += slen;
|
||||
switch (stype & 0x17F) {
|
||||
case 0x100 | '#':
|
||||
{
|
||||
char *beg, *end;
|
||||
mksh_ari_t seed;
|
||||
register uint32_t h;
|
||||
|
||||
beg = wdcopy(sp, ATEMP);
|
||||
end = beg + (wdscan(sp, CSUBST) - sp);
|
||||
end[-2] = EOS;
|
||||
end = wdstrip(beg, 0);
|
||||
afree(beg, ATEMP);
|
||||
evaluate(substitute(end, 0),
|
||||
&seed, KSH_UNWIND_ERROR, true);
|
||||
/* hash with seed, for now */
|
||||
h = seed;
|
||||
NZATUpdateString(h,
|
||||
str_val(st->var));
|
||||
NZAATFinish(h);
|
||||
x.str = shf_smprintf("%08X",
|
||||
(unsigned int)hash(str_val(st->var)));
|
||||
(unsigned int)h);
|
||||
break;
|
||||
}
|
||||
case 0x100 | 'Q':
|
||||
{
|
||||
struct shf shf;
|
||||
|
||||
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
|
||||
print_value_quoted(&shf, str_val(st->var));
|
||||
x.str = shf_sclose(&shf);
|
||||
break;
|
||||
}
|
||||
case '0': {
|
||||
char *beg, *mid, *end, *stg;
|
||||
mksh_ari_t from = 0, num = -1, flen, finc = 0;
|
||||
|
@ -470,6 +517,7 @@ expand(const char *cp, /* input word */
|
|||
|
||||
/* check for special cases */
|
||||
d = str_val(st->var);
|
||||
mkssert(d != NULL);
|
||||
switch (*pat) {
|
||||
case '#':
|
||||
/* anchor at begin */
|
||||
|
@ -574,8 +622,8 @@ expand(const char *cp, /* input word */
|
|||
case '#':
|
||||
case '%':
|
||||
/* ! DOBLANK,DOBRACE,DOTILDE */
|
||||
f = DOPAT | (f&DONTRUNCOMMAND) |
|
||||
DOTEMP;
|
||||
f = (f & DONTRUNCOMMAND) |
|
||||
DOPAT | DOTEMP;
|
||||
st->quotew = quote = 0;
|
||||
/*
|
||||
* Prepend open pattern (so |
|
||||
|
@ -706,6 +754,7 @@ expand(const char *cp, /* input word */
|
|||
case '0':
|
||||
case '/':
|
||||
case 0x100 | '#':
|
||||
case 0x100 | 'Q':
|
||||
dp = Xrestpos(ds, dp, st->base);
|
||||
type = XSUB;
|
||||
if (f&DOBLANK)
|
||||
|
@ -720,19 +769,19 @@ expand(const char *cp, /* input word */
|
|||
case OPAT:
|
||||
/* open pattern: *(foo|bar) */
|
||||
/* Next char is the type of pattern */
|
||||
make_magic = 1;
|
||||
make_magic = true;
|
||||
c = *sp++ | 0x80;
|
||||
break;
|
||||
|
||||
case SPAT:
|
||||
/* pattern separator (|) */
|
||||
make_magic = 1;
|
||||
make_magic = true;
|
||||
c = '|';
|
||||
break;
|
||||
|
||||
case CPAT:
|
||||
/* close pattern */
|
||||
make_magic = 1;
|
||||
make_magic = true;
|
||||
c = /*(*/ ')';
|
||||
break;
|
||||
}
|
||||
|
@ -770,6 +819,7 @@ expand(const char *cp, /* input word */
|
|||
case XARGSEP:
|
||||
type = XARG;
|
||||
quote = 1;
|
||||
/* FALLTHROUGH */
|
||||
case XARG:
|
||||
if ((c = *x.str++) == '\0') {
|
||||
/*
|
||||
|
@ -855,19 +905,27 @@ expand(const char *cp, /* input word */
|
|||
p + Xlength(ds, (dp - 1)),
|
||||
fdo | (f & DOMARKDIRS));
|
||||
else if (fdo & DOGLOB)
|
||||
glob(p, wp, f & DOMARKDIRS);
|
||||
glob(p, wp, tobool(f & DOMARKDIRS));
|
||||
else if ((f & DOPAT) || !(fdo & DOMAGIC))
|
||||
XPput(*wp, p);
|
||||
else
|
||||
XPput(*wp, debunk(p, p, strlen(p) + 1));
|
||||
fdo = 0;
|
||||
saw_eq = 0;
|
||||
saw_eq = false;
|
||||
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
|
||||
if (c != 0)
|
||||
Xinit(ds, dp, 128, ATEMP);
|
||||
}
|
||||
if (c == 0)
|
||||
if (c == 0)
|
||||
return;
|
||||
Xinit(ds, dp, 128, ATEMP);
|
||||
} else if (c == 0) {
|
||||
return;
|
||||
} else if (type == XSUB && ctype(c, C_IFS) &&
|
||||
!ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
|
||||
char *p;
|
||||
|
||||
*(p = alloc(1, ATEMP)) = '\0';
|
||||
XPput(*wp, p);
|
||||
type = XSUBMID;
|
||||
}
|
||||
if (word != IFS_NWS)
|
||||
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
|
||||
} else {
|
||||
|
@ -888,7 +946,7 @@ expand(const char *cp, /* input word */
|
|||
if (!quote)
|
||||
switch (c) {
|
||||
case '[':
|
||||
case NOT:
|
||||
case '!':
|
||||
case '-':
|
||||
case ']':
|
||||
/*
|
||||
|
@ -910,10 +968,10 @@ expand(const char *cp, /* input word */
|
|||
*dp++ = MAGIC;
|
||||
}
|
||||
break;
|
||||
case OBRACE:
|
||||
case '{':
|
||||
case '}':
|
||||
case ',':
|
||||
case CBRACE:
|
||||
if ((f & DOBRACE) && (c == OBRACE ||
|
||||
if ((f & DOBRACE) && (c == '{' /*}*/ ||
|
||||
(fdo & DOBRACE))) {
|
||||
fdo |= DOBRACE|DOMAGIC;
|
||||
*dp++ = MAGIC;
|
||||
|
@ -924,7 +982,7 @@ expand(const char *cp, /* input word */
|
|||
if (!(f & DOTEMP) && !saw_eq &&
|
||||
(Flag(FBRACEEXPAND) ||
|
||||
(f & DOASNTILDE))) {
|
||||
saw_eq = 1;
|
||||
saw_eq = true;
|
||||
tilde_ok = 1;
|
||||
}
|
||||
break;
|
||||
|
@ -966,7 +1024,7 @@ expand(const char *cp, /* input word */
|
|||
quote &= ~2;
|
||||
|
||||
if (make_magic) {
|
||||
make_magic = 0;
|
||||
make_magic = false;
|
||||
fdo |= DOMAGIC | (f & DOGLOB);
|
||||
*dp++ = MAGIC;
|
||||
} else if (ISMAGIC(c)) {
|
||||
|
@ -1124,6 +1182,7 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
case '0':
|
||||
case '/':
|
||||
case 0x100 | '#':
|
||||
case 0x100 | 'Q':
|
||||
return (-1);
|
||||
}
|
||||
if (e->loc->argc == 0) {
|
||||
|
@ -1133,7 +1192,8 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
} else {
|
||||
xp->u.strv = (const char **)e->loc->argv + 1;
|
||||
xp->str = *xp->u.strv++;
|
||||
xp->split = c == '@'; /* $@ */
|
||||
/* $@ */
|
||||
xp->split = tobool(c == '@');
|
||||
state = XARG;
|
||||
}
|
||||
/* POSIX 2009? */
|
||||
|
@ -1151,6 +1211,7 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
case '0':
|
||||
case '/':
|
||||
case 0x100 | '#':
|
||||
case 0x100 | 'Q':
|
||||
return (-1);
|
||||
}
|
||||
XPinit(wv, 32);
|
||||
|
@ -1172,7 +1233,8 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
XPput(wv, 0);
|
||||
xp->u.strv = (const char **)XPptrv(wv);
|
||||
xp->str = *xp->u.strv++;
|
||||
xp->split = p[1] == '@'; /* ${foo[@]} */
|
||||
/* ${foo[@]} */
|
||||
xp->split = tobool(p[1] == '@');
|
||||
state = XARG;
|
||||
}
|
||||
} else {
|
||||
|
@ -1207,7 +1269,8 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||
if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
|
||||
(((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
|
||||
c == '=' || c == '-' || c == '?' : c == '+'))) ||
|
||||
stype == (0x80 | '0') || stype == (0x100 | '#'))
|
||||
stype == (0x80 | '0') || stype == (0x100 | '#') ||
|
||||
stype == (0x100 | 'Q'))
|
||||
/* expand word instead of variable value */
|
||||
state = XBASE;
|
||||
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
|
||||
|
@ -1220,7 +1283,7 @@ 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)
|
||||
comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
|
||||
{
|
||||
Source *s, *sold;
|
||||
struct op *t;
|
||||
|
@ -1234,10 +1297,15 @@ comsub(Expand *xp, const char *cp)
|
|||
afree(s, ATEMP);
|
||||
source = sold;
|
||||
|
||||
UTFMODE = old_utfmode;
|
||||
|
||||
if (t == NULL)
|
||||
return (XBASE);
|
||||
|
||||
if (t != NULL && t->type == TCOM &&
|
||||
/* no waitlast() unless specifically enabled later */
|
||||
xp->split = false;
|
||||
|
||||
if (t->type == TCOM &&
|
||||
*t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
|
||||
/* $(<file) */
|
||||
struct ioword *io = *t->ioact;
|
||||
|
@ -1250,10 +1318,35 @@ comsub(Expand *xp, const char *cp)
|
|||
SHF_MAPHI|SHF_CLEXEC);
|
||||
if (shf == NULL)
|
||||
errorf("%s: %s %s", name, "can't open", "$() input");
|
||||
/* no waitlast() */
|
||||
xp->split = 0;
|
||||
} else if (fn == FUNSUB) {
|
||||
int ofd1;
|
||||
struct temp *tf = NULL;
|
||||
|
||||
/* create a temporary file, open for writing */
|
||||
maketemp(ATEMP, TT_FUNSUB, &tf);
|
||||
if (!tf->shf) {
|
||||
errorf("can't %s temporary file %s: %s",
|
||||
"create", tf->tffn, cstrerror(errno));
|
||||
}
|
||||
/* save stdout and make the temporary file it */
|
||||
ofd1 = savefd(1);
|
||||
ksh_dup2(shf_fileno(tf->shf), 1, false);
|
||||
/*
|
||||
* run tree, with output thrown into the tempfile,
|
||||
* in a new function block
|
||||
*/
|
||||
funsub(t);
|
||||
subst_exstat = exstat & 0xFF;
|
||||
/* close the tempfile and restore regular stdout */
|
||||
shf_close(tf->shf);
|
||||
restfd(1, ofd1);
|
||||
/* now open, unlink and free the tempfile for reading */
|
||||
shf = shf_open(tf->tffn, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
|
||||
unlink(tf->tffn);
|
||||
afree(tf, ATEMP);
|
||||
} else {
|
||||
int ofd1, pv[2];
|
||||
|
||||
openpipe(pv);
|
||||
shf = shf_fdopen(pv[0], SHF_RD, NULL);
|
||||
ofd1 = savefd(1);
|
||||
|
@ -1261,14 +1354,13 @@ comsub(Expand *xp, const char *cp)
|
|||
ksh_dup2(pv[1], 1, false);
|
||||
close(pv[1]);
|
||||
}
|
||||
execute(t, XFORK|XXCOM|XPIPEO, NULL);
|
||||
execute(t, XXCOM | XPIPEO | XFORK, NULL);
|
||||
restfd(1, ofd1);
|
||||
startlast();
|
||||
/* waitlast() */
|
||||
xp->split = 1;
|
||||
xp->split = true;
|
||||
}
|
||||
|
||||
UTFMODE = old_utfmode;
|
||||
xp->u.shf = shf;
|
||||
return (XCOM);
|
||||
}
|
||||
|
@ -1276,7 +1368,6 @@ comsub(Expand *xp, const char *cp)
|
|||
/*
|
||||
* perform #pattern and %pattern substitution in ${}
|
||||
*/
|
||||
|
||||
static char *
|
||||
trimsub(char *str, char *pat, int how)
|
||||
{
|
||||
|
@ -1344,7 +1435,7 @@ trimsub(char *str, char *pat, int how)
|
|||
|
||||
/* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
|
||||
static void
|
||||
glob(char *cp, XPtrV *wp, int markdirs)
|
||||
glob(char *cp, XPtrV *wp, bool markdirs)
|
||||
{
|
||||
int oldsize = XPsize(*wp);
|
||||
|
||||
|
@ -1365,7 +1456,7 @@ glob(char *cp, XPtrV *wp, int markdirs)
|
|||
* the number of matches found.
|
||||
*/
|
||||
int
|
||||
glob_str(char *cp, XPtrV *wp, int markdirs)
|
||||
glob_str(char *cp, XPtrV *wp, bool markdirs)
|
||||
{
|
||||
int oldsize = XPsize(*wp);
|
||||
XString xs;
|
||||
|
@ -1410,7 +1501,7 @@ globit(XString *xs, /* dest string */
|
|||
struct stat lstatb, statb;
|
||||
int stat_done = 0; /* -1: failed, 1 ok */
|
||||
|
||||
if (lstat(Xstring(*xs, xp), &lstatb) < 0)
|
||||
if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
|
||||
return;
|
||||
/*
|
||||
* special case for systems which strip trailing
|
||||
|
@ -1584,7 +1675,7 @@ maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign)
|
|||
* based on a version by Arnold Robbins
|
||||
*/
|
||||
|
||||
static char *
|
||||
char *
|
||||
tilde(char *cp)
|
||||
{
|
||||
char *dp = null;
|
||||
|
@ -1638,7 +1729,7 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
|
|||
char *p;
|
||||
|
||||
/* search for open brace */
|
||||
for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
|
||||
for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != '{' /*}*/; p += 2)
|
||||
;
|
||||
brace_start = p;
|
||||
|
||||
|
@ -1648,9 +1739,9 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
|
|||
count = 1;
|
||||
for (p += 2; *p && count; p++) {
|
||||
if (ISMAGIC(*p)) {
|
||||
if (*++p == OBRACE)
|
||||
if (*++p == '{' /*}*/)
|
||||
count++;
|
||||
else if (*p == CBRACE)
|
||||
else if (*p == /*{*/ '}')
|
||||
--count;
|
||||
else if (*p == ',' && count == 1)
|
||||
comma = p;
|
||||
|
@ -1665,7 +1756,7 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
|
|||
* expansion. }
|
||||
*/
|
||||
if (fdo & DOGLOB)
|
||||
glob(start, wp, fdo & DOMARKDIRS);
|
||||
glob(start, wp, tobool(fdo & DOMARKDIRS));
|
||||
else
|
||||
XPput(*wp, debunk(start, start, end - start));
|
||||
return;
|
||||
|
@ -1681,9 +1772,9 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
|
|||
count = 1;
|
||||
for (p = brace_start + 2; p != brace_end; p++) {
|
||||
if (ISMAGIC(*p)) {
|
||||
if (*++p == OBRACE)
|
||||
if (*++p == '{' /*}*/)
|
||||
count++;
|
||||
else if ((*p == CBRACE && --count == 0) ||
|
||||
else if ((*p == /*{*/ '}' && --count == 0) ||
|
||||
(*p == ',' && count == 1)) {
|
||||
char *news;
|
||||
int l1, l2, l3;
|
||||
|
@ -1708,3 +1799,14 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* helper function due to setjmp/longjmp woes */
|
||||
static void
|
||||
funsub(struct op *t)
|
||||
{
|
||||
newblock();
|
||||
e->type = E_FUNC;
|
||||
if (!kshsetjmp(e->jbuf))
|
||||
execute(t, XXCOM | XERROK, NULL);
|
||||
popblock();
|
||||
}
|
||||
|
|
178
src/exec.c
178
src/exec.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: exec.c,v 1.49 2009/01/29 23:27:26 jaredy Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.96 2011/09/07 15:24:14 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.116 2013/02/17 05:40:15 tg Exp $");
|
||||
|
||||
#ifndef MKSH_DEFAULT_EXECSHELL
|
||||
#define MKSH_DEFAULT_EXECSHELL "/bin/sh"
|
||||
|
@ -31,14 +32,17 @@ __RCSID("$MirOS: src/bin/mksh/exec.c,v 1.96 2011/09/07 15:24:14 tg Exp $");
|
|||
static int comexec(struct op *, struct tbl * volatile, const char **,
|
||||
int volatile, volatile int *);
|
||||
static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
|
||||
static int call_builtin(struct tbl *, const char **);
|
||||
static int call_builtin(struct tbl *, const char **, const char *);
|
||||
static int iosetup(struct ioword *, struct tbl *);
|
||||
static int herein(const char *, int, char **);
|
||||
static int herein(struct ioword *, char **);
|
||||
static const char *do_selectargs(const char **, bool);
|
||||
static Test_op dbteste_isa(Test_env *, Test_meta);
|
||||
static const char *dbteste_getopnd(Test_env *, Test_op, bool);
|
||||
static void dbteste_error(Test_env *, int, const char *);
|
||||
static int search_access(const char *, int);
|
||||
/* XXX: horrible kludge to fit within the framework */
|
||||
static char *plain_fmt_entry(char *, size_t, unsigned int, const void *);
|
||||
static char *select_fmt_entry(char *, size_t, unsigned int, const void *);
|
||||
|
||||
/*
|
||||
* execute command tree
|
||||
|
@ -92,8 +96,7 @@ execute(struct op * volatile t,
|
|||
/* and has no right-hand side (i.e. "varname=") */
|
||||
ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS &&
|
||||
/* plus we can have a here document content */
|
||||
herein(t->ioact[0]->heredoc, t->ioact[0]->flag & IOEVAL,
|
||||
&cp) == 0 && cp && *cp) {
|
||||
herein(t->ioact[0], &cp) == 0 && cp && *cp) {
|
||||
char *sp = cp, *dp;
|
||||
size_t n = ccp - t->vars[0] + 2, z;
|
||||
|
||||
|
@ -234,8 +237,7 @@ execute(struct op * volatile t,
|
|||
*/
|
||||
sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
|
||||
e->type = E_ERRH;
|
||||
i = sigsetjmp(e->jbuf, 0);
|
||||
if (i) {
|
||||
if ((i = kshsetjmp(e->jbuf))) {
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
quitenv(NULL);
|
||||
unwind(i);
|
||||
|
@ -337,10 +339,7 @@ execute(struct op * volatile t,
|
|||
(const char **)eval((const char **)t->vars,
|
||||
DOBLANK | DOGLOB | DOTILDE);
|
||||
e->type = E_LOOP;
|
||||
while (/* CONSTCOND */ 1) {
|
||||
i = sigsetjmp(e->jbuf, 0);
|
||||
if (!i)
|
||||
break;
|
||||
while ((i = kshsetjmp(e->jbuf))) {
|
||||
if ((e->flags&EF_BRKCONT_PASS) ||
|
||||
(i != LBREAK && i != LCONTIN)) {
|
||||
quitenv(NULL);
|
||||
|
@ -375,10 +374,7 @@ execute(struct op * volatile t,
|
|||
case TWHILE:
|
||||
case TUNTIL:
|
||||
e->type = E_LOOP;
|
||||
while (/* CONSTCOND */ 1) {
|
||||
i = sigsetjmp(e->jbuf, 0);
|
||||
if (!i)
|
||||
break;
|
||||
while ((i = kshsetjmp(e->jbuf))) {
|
||||
if ((e->flags&EF_BRKCONT_PASS) ||
|
||||
(i != LBREAK && i != LCONTIN)) {
|
||||
quitenv(NULL);
|
||||
|
@ -464,10 +460,10 @@ execute(struct op * volatile t,
|
|||
if (rv == ENOEXEC)
|
||||
scriptexec(t, (const char **)up);
|
||||
else
|
||||
errorf("%s: %s", s, strerror(rv));
|
||||
errorf("%s: %s", s, cstrerror(rv));
|
||||
}
|
||||
Break:
|
||||
exstat = rv;
|
||||
exstat = rv & 0xFF;
|
||||
if (vp_pipest->flag & INT_L) {
|
||||
unset(vp_pipest, 1);
|
||||
vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY |
|
||||
|
@ -482,9 +478,14 @@ execute(struct op * volatile t,
|
|||
unwind(LEXIT);
|
||||
if (rv != 0 && !(flags & XERROK) &&
|
||||
(xerrok == NULL || !*xerrok)) {
|
||||
trapsig(ksh_SIGERR);
|
||||
if (Flag(FERREXIT))
|
||||
unwind(LERROR);
|
||||
if (Flag(FERREXIT) & 0x80) {
|
||||
/* inside eval */
|
||||
Flag(FERREXIT) = 0;
|
||||
} else {
|
||||
trapsig(ksh_SIGERR);
|
||||
if (Flag(FERREXIT))
|
||||
unwind(LERROR);
|
||||
}
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
@ -504,7 +505,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
/* Must be static (XXX but why?) */
|
||||
static struct op texec;
|
||||
int type_flags;
|
||||
int keepasn_ok;
|
||||
bool keepasn_ok;
|
||||
int fcflags = FC_BI|FC_FUNC|FC_PATH;
|
||||
bool bourne_function_call = false;
|
||||
struct block *l_expand, *l_assign;
|
||||
|
@ -536,7 +537,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
* FOO=bar command FOO is neither kept nor exported
|
||||
* PATH=... foobar use new PATH in foobar search
|
||||
*/
|
||||
keepasn_ok = 1;
|
||||
keepasn_ok = true;
|
||||
while (tp && tp->type == CSHELL) {
|
||||
/* undo effects of command */
|
||||
fcflags = FC_BI|FC_FUNC|FC_PATH;
|
||||
|
@ -583,7 +584,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
* POSIX says special builtins lose their status
|
||||
* if accessed using command.
|
||||
*/
|
||||
keepasn_ok = 0;
|
||||
keepasn_ok = false;
|
||||
if (!ap[0]) {
|
||||
/* ensure command with no args exits with 0 */
|
||||
subst_exstat = 0;
|
||||
|
@ -607,11 +608,20 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
else
|
||||
/* go on, use the builtin */
|
||||
break;
|
||||
#endif
|
||||
#if !defined(MKSH_SMALL)
|
||||
} else if (tp->val.f == c_trap) {
|
||||
t->u.evalflags &= ~DOTCOMEXEC;
|
||||
break;
|
||||
#endif
|
||||
} else
|
||||
break;
|
||||
tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
|
||||
}
|
||||
#if !defined(MKSH_SMALL)
|
||||
if (t->u.evalflags & DOTCOMEXEC)
|
||||
flags |= XEXEC;
|
||||
#endif
|
||||
l_expand = e->loc;
|
||||
if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
|
||||
type_flags = 0;
|
||||
|
@ -666,7 +676,11 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
|
||||
/* shell built-in */
|
||||
case CSHELL:
|
||||
rv = call_builtin(tp, (const char **)ap);
|
||||
rv = call_builtin(tp, (const char **)ap, null);
|
||||
if (!keepasn_ok && tp->val.f == c_shift) {
|
||||
l_expand->argc = l_assign->argc;
|
||||
l_expand->argv = l_assign->argv;
|
||||
}
|
||||
break;
|
||||
|
||||
/* function call */
|
||||
|
@ -682,14 +696,14 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
|
||||
warningf(true, "%s: %s %s: %s", cp,
|
||||
"can't find", "function definition file",
|
||||
strerror(tp->u2.errnov));
|
||||
cstrerror(tp->u2.errnov));
|
||||
break;
|
||||
}
|
||||
if (include(tp->u.fpath, 0, NULL, 0) < 0) {
|
||||
if (include(tp->u.fpath, 0, NULL, false) < 0) {
|
||||
rv = errno;
|
||||
warningf(true, "%s: %s %s %s: %s", cp,
|
||||
"can't open", "function definition file",
|
||||
tp->u.fpath, strerror(rv));
|
||||
tp->u.fpath, cstrerror(rv));
|
||||
rv = 127;
|
||||
break;
|
||||
}
|
||||
|
@ -733,10 +747,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
tp->flag |= FINUSE;
|
||||
|
||||
e->type = E_FUNC;
|
||||
i = sigsetjmp(e->jbuf, 0);
|
||||
if (i == 0) {
|
||||
/* seems odd to pass XERROK here, but AT&T ksh does */
|
||||
exstat = execute(tp->val.t, flags & XERROK, xerrok);
|
||||
if (!(i = kshsetjmp(e->jbuf))) {
|
||||
execute(tp->val.t, flags & XERROK, NULL);
|
||||
i = LRETURN;
|
||||
}
|
||||
kshname = old_kshname;
|
||||
|
@ -757,7 +769,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
switch (i) {
|
||||
case LRETURN:
|
||||
case LERROR:
|
||||
rv = exstat;
|
||||
rv = exstat & 0xFF;
|
||||
break;
|
||||
case LINTR:
|
||||
case LEXIT:
|
||||
|
@ -784,7 +796,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
} else {
|
||||
rv = 126;
|
||||
warningf(true, "%s: %s: %s", cp, "can't execute",
|
||||
strerror(tp->u2.errnov));
|
||||
cstrerror(tp->u2.errnov));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -817,7 +829,7 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap,
|
|||
}
|
||||
Leave:
|
||||
if (flags & XEXEC) {
|
||||
exstat = rv;
|
||||
exstat = rv & 0xFF;
|
||||
unwind(LLEAVE);
|
||||
}
|
||||
return (rv);
|
||||
|
@ -919,7 +931,7 @@ scriptexec(struct op *tp, const char **ap)
|
|||
execve(args.rw[0], args.rw, cap.rw);
|
||||
|
||||
/* report both the programme that was run and the bogus interpreter */
|
||||
errorf("%s: %s: %s", tp->str, sh, strerror(errno));
|
||||
errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -928,9 +940,7 @@ shcomexec(const char **wp)
|
|||
struct tbl *tp;
|
||||
|
||||
tp = ktsearch(&builtins, *wp, hash(*wp));
|
||||
if (tp == NULL)
|
||||
internal_errorf("%s: %s", "shcomexec", *wp);
|
||||
return (call_builtin(tp, wp));
|
||||
return (call_builtin(tp, wp, "shcomexec"));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -979,6 +989,8 @@ define(const char *name, struct op *t)
|
|||
|
||||
while (/* CONSTCOND */ 1) {
|
||||
tp = findfunc(name, nhash, true);
|
||||
/* because findfunc:create=true */
|
||||
mkssert(tp != NULL);
|
||||
|
||||
if (tp->flag & ISSET)
|
||||
was_set = true;
|
||||
|
@ -1245,10 +1257,12 @@ search_path(const char *name, const char *lpath,
|
|||
}
|
||||
|
||||
static int
|
||||
call_builtin(struct tbl *tp, const char **wp)
|
||||
call_builtin(struct tbl *tp, const char **wp, const char *where)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (!tp)
|
||||
internal_errorf("%s: %s", where, wp[0]);
|
||||
builtin_argv0 = wp[0];
|
||||
builtin_flag = tp->flag;
|
||||
shf_reopen(1, SHF_WR, shl_stdout);
|
||||
|
@ -1271,7 +1285,8 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
|||
int u = -1;
|
||||
char *cp = iop->name;
|
||||
int iotype = iop->flag & IOTYPE;
|
||||
int do_open = 1, do_close = 0, flags = 0;
|
||||
bool do_open = true, do_close = false;
|
||||
int flags = 0;
|
||||
struct ioword iotmp;
|
||||
struct stat statb;
|
||||
|
||||
|
@ -1313,20 +1328,20 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
|||
break;
|
||||
|
||||
case IOHERE:
|
||||
do_open = 0;
|
||||
do_open = false;
|
||||
/* herein() returns -2 if error has been printed */
|
||||
u = herein(iop->heredoc, iop->flag & IOEVAL, NULL);
|
||||
u = herein(iop, NULL);
|
||||
/* cp may have wrong name */
|
||||
break;
|
||||
|
||||
case IODUP: {
|
||||
const char *emsg;
|
||||
|
||||
do_open = 0;
|
||||
do_open = false;
|
||||
if (*cp == '-' && !cp[1]) {
|
||||
/* prevent error return below */
|
||||
u = 1009;
|
||||
do_close = 1;
|
||||
do_close = true;
|
||||
} else if ((u = check_fd(cp,
|
||||
X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
|
||||
&emsg)) < 0) {
|
||||
|
@ -1338,7 +1353,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
|||
/* "dup from" == "dup to" */
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_open) {
|
||||
|
@ -1355,7 +1370,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
|||
warningf(true, "can't %s %s: %s",
|
||||
iotype == IODUP ? "dup" :
|
||||
(iotype == IOREAD || iotype == IOHERE) ?
|
||||
"open" : "create", cp, strerror(u));
|
||||
"open" : "create", cp, cstrerror(u));
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
@ -1379,13 +1394,13 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
|||
close(iop->unit);
|
||||
else if (u != iop->unit) {
|
||||
if (ksh_dup2(u, iop->unit, true) < 0) {
|
||||
int ev;
|
||||
int eno;
|
||||
|
||||
ev = errno;
|
||||
eno = errno;
|
||||
warningf(true, "%s %s %s",
|
||||
"can't finish (dup) redirection",
|
||||
snptreef(NULL, 32, "%R", &iotmp),
|
||||
strerror(ev));
|
||||
cstrerror(eno));
|
||||
if (iotype != IODUP)
|
||||
close(u);
|
||||
return (-1);
|
||||
|
@ -1419,12 +1434,12 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
|||
static int
|
||||
hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
|
||||
{
|
||||
const char *ccp;
|
||||
const char * volatile ccp = content;
|
||||
struct source *s, *osource;
|
||||
|
||||
osource = source;
|
||||
newenv(E_ERRH);
|
||||
if (sigsetjmp(e->jbuf, 0)) {
|
||||
if (kshsetjmp(e->jbuf)) {
|
||||
source = osource;
|
||||
quitenv(shf);
|
||||
/* special to iosetup(): don't print error */
|
||||
|
@ -1433,14 +1448,13 @@ hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
|
|||
if (sub) {
|
||||
/* do substitutions on the content of heredoc */
|
||||
s = pushs(SSTRING, ATEMP);
|
||||
s->start = s->str = content;
|
||||
s->start = s->str = ccp;
|
||||
source = s;
|
||||
if (yylex(ONEWORD|HEREDOC) != LWORD)
|
||||
if (yylex(sub) != LWORD)
|
||||
internal_errorf("%s: %s", "herein", "yylex");
|
||||
source = osource;
|
||||
ccp = evalstr(yylval.cp, 0);
|
||||
} else
|
||||
ccp = content;
|
||||
}
|
||||
|
||||
if (resbuf == NULL)
|
||||
shf_puts(ccp, shf);
|
||||
|
@ -1452,7 +1466,7 @@ hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
|
|||
}
|
||||
|
||||
static int
|
||||
herein(const char *content, int sub, char **resbuf)
|
||||
herein(struct ioword *iop, char **resbuf)
|
||||
{
|
||||
int fd = -1;
|
||||
struct shf *shf;
|
||||
|
@ -1460,32 +1474,35 @@ herein(const char *content, int sub, char **resbuf)
|
|||
int i;
|
||||
|
||||
/* ksh -c 'cat << EOF' can cause this... */
|
||||
if (content == NULL) {
|
||||
if (iop->heredoc == NULL) {
|
||||
warningf(true, "%s missing", "here document");
|
||||
/* special to iosetup(): don't print error */
|
||||
return (-2);
|
||||
}
|
||||
|
||||
/* lexer substitution flags */
|
||||
i = (iop->flag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
|
||||
|
||||
/* skip all the fd setup if we just want the value */
|
||||
if (resbuf != NULL)
|
||||
return (hereinval(content, sub, resbuf, NULL));
|
||||
return (hereinval(iop->heredoc, i, resbuf, NULL));
|
||||
|
||||
/*
|
||||
* Create temp file to hold content (done before newenv
|
||||
* so temp doesn't get removed too soon).
|
||||
*/
|
||||
h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
|
||||
if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) {
|
||||
if (!(shf = h->shf) || (fd = open(h->tffn, O_RDONLY, 0)) < 0) {
|
||||
i = errno;
|
||||
warningf(true, "can't %s temporary file %s: %s",
|
||||
!shf ? "create" : "open", h->name, strerror(i));
|
||||
!shf ? "create" : "open", h->tffn, cstrerror(i));
|
||||
if (shf)
|
||||
shf_close(shf);
|
||||
/* special to iosetup(): don't print error */
|
||||
return (-2);
|
||||
}
|
||||
|
||||
if (hereinval(content, sub, NULL, shf) == -2) {
|
||||
if (hereinval(iop->heredoc, i, NULL, shf) == -2) {
|
||||
close(fd);
|
||||
/* special to iosetup(): don't print error */
|
||||
return (-2);
|
||||
|
@ -1494,7 +1511,8 @@ herein(const char *content, int sub, char **resbuf)
|
|||
if (shf_close(shf) == EOF) {
|
||||
i = errno;
|
||||
close(fd);
|
||||
warningf(true, "%s: %s: %s", "write", h->name, strerror(i));
|
||||
warningf(true, "can't %s temporary file %s: %s",
|
||||
"write", h->tffn, cstrerror(i));
|
||||
/* special to iosetup(): don't print error */
|
||||
return (-2);
|
||||
}
|
||||
|
@ -1527,14 +1545,14 @@ do_selectargs(const char **ap, bool print_menu)
|
|||
if (print_menu || !*str_val(global("REPLY")))
|
||||
pr_menu(ap);
|
||||
shellf("%s", str_val(global("PS3")));
|
||||
if (call_builtin(findcom("read", FC_BI), read_args))
|
||||
if (call_builtin(findcom("read", FC_BI), read_args, Tselect))
|
||||
return (NULL);
|
||||
s = str_val(global("REPLY"));
|
||||
if (*s) {
|
||||
getn(s, &i);
|
||||
return ((i >= 1 && i <= argct) ? ap[i - 1] : null);
|
||||
}
|
||||
print_menu = 1;
|
||||
print_menu = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1543,16 +1561,14 @@ struct select_menu_info {
|
|||
int num_width;
|
||||
};
|
||||
|
||||
static char *select_fmt_entry(char *, size_t, int, const void *);
|
||||
|
||||
/* format a single select menu item */
|
||||
static char *
|
||||
select_fmt_entry(char *buf, size_t buflen, int i, const void *arg)
|
||||
select_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
|
||||
{
|
||||
const struct select_menu_info *smi =
|
||||
(const struct select_menu_info *)arg;
|
||||
|
||||
shf_snprintf(buf, buflen, "%*d) %s",
|
||||
shf_snprintf(buf, buflen, "%*u) %s",
|
||||
smi->num_width, i + 1, smi->args[i]);
|
||||
return (buf);
|
||||
}
|
||||
|
@ -1560,13 +1576,13 @@ select_fmt_entry(char *buf, size_t buflen, int i, const void *arg)
|
|||
/*
|
||||
* print a select style menu
|
||||
*/
|
||||
int
|
||||
void
|
||||
pr_menu(const char * const *ap)
|
||||
{
|
||||
struct select_menu_info smi;
|
||||
const char * const *pp;
|
||||
size_t acols = 0, aocts = 0, i;
|
||||
int n;
|
||||
unsigned int n;
|
||||
|
||||
/*
|
||||
* width/column calculations were done once and saved, but this
|
||||
|
@ -1598,25 +1614,20 @@ pr_menu(const char * const *ap)
|
|||
print_columns(shl_out, n, select_fmt_entry, (void *)&smi,
|
||||
smi.num_width + 2 + aocts, smi.num_width + 2 + acols,
|
||||
true);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
/* XXX: horrible kludge to fit within the framework */
|
||||
static char *plain_fmt_entry(char *, size_t, int, const void *);
|
||||
|
||||
static char *
|
||||
plain_fmt_entry(char *buf, size_t buflen, int i, const void *arg)
|
||||
plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
|
||||
{
|
||||
strlcpy(buf, ((const char * const *)arg)[i], buflen);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
pr_list(char * const *ap)
|
||||
{
|
||||
size_t acols = 0, aocts = 0, i;
|
||||
int n;
|
||||
unsigned int n;
|
||||
char * const *pp;
|
||||
|
||||
for (n = 0, pp = ap; *pp; n++, pp++) {
|
||||
|
@ -1630,8 +1641,6 @@ pr_list(char * const *ap)
|
|||
|
||||
print_columns(shl_out, n, plain_fmt_entry, (const void *)ap,
|
||||
aocts, acols, false);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1664,9 +1673,10 @@ dbteste_isa(Test_env *te, Test_meta meta)
|
|||
char buf[8];
|
||||
char *q = buf;
|
||||
|
||||
for (p = *te->pos.wp;
|
||||
*p == CHAR && q < &buf[sizeof(buf) - 1]; p += 2)
|
||||
*q++ = p[1];
|
||||
p = *te->pos.wp;
|
||||
while (*p++ == CHAR &&
|
||||
(size_t)(q - buf) < sizeof(buf) - 1)
|
||||
*q++ = *p++;
|
||||
*q = '\0';
|
||||
ret = test_isop(meta, buf);
|
||||
}
|
||||
|
|
234
src/expr.c
234
src/expr.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: expr.c,v 1.21 2009/06/01 19:00:57 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,7 +23,21 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.49 2011/09/07 15:24:14 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.61 2013/02/15 18:36:48 tg Exp $");
|
||||
|
||||
#if !HAVE_SILENT_IDIVWRAPV
|
||||
#if !defined(MKSH_LEGACY_MODE) || HAVE_LONG_32BIT
|
||||
#define IDIVWRAPV_VL (mksh_uari_t)0x80000000UL
|
||||
#define IDIVWRAPV_VR (mksh_uari_t)0xFFFFFFFFUL
|
||||
#elif HAVE_LONG_64BIT
|
||||
#define IDIVWRAPV_VL (mksh_uari_t)0x8000000000000000UL
|
||||
#define IDIVWRAPV_VR (mksh_uari_t)0xFFFFFFFFFFFFFFFFUL
|
||||
#else
|
||||
# warning "cannot guarantee integer division wraparound"
|
||||
#undef HAVE_SILENT_IDIVWRAPV
|
||||
#define HAVE_SILENT_IDIVWRAPV 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The order of these enums is constrained by the order of opinfo[] */
|
||||
enum token {
|
||||
|
@ -124,29 +139,29 @@ static const struct opinfo opinfo[] = {
|
|||
{ "", 0, P_PRIMARY }
|
||||
};
|
||||
|
||||
typedef struct expr_state Expr_state;
|
||||
struct expr_state {
|
||||
const char *expression; /* expression being evaluated */
|
||||
const char *tokp; /* lexical position */
|
||||
struct tbl *val; /* value from token() */
|
||||
struct tbl *evaling; /* variable that is being recursively
|
||||
* expanded (EXPRINEVAL flag set) */
|
||||
int noassign; /* don't do assigns (for ?:,&&,||) */
|
||||
enum token tok; /* token from token() */
|
||||
bool arith; /* evaluating an $(()) expression? */
|
||||
bool natural; /* unsigned arithmetic calculation */
|
||||
};
|
||||
typedef struct expr_state {
|
||||
/* expression being evaluated */
|
||||
const char *expression;
|
||||
/* lexical position */
|
||||
const char *tokp;
|
||||
/* value from token() */
|
||||
struct tbl *val;
|
||||
/* variable that is being recursively expanded (EXPRINEVAL flag set) */
|
||||
struct tbl *evaling;
|
||||
/* token from token() */
|
||||
enum token tok;
|
||||
/* don't do assignments (for ?:, &&, ||) */
|
||||
short noassign;
|
||||
/* evaluating an $(()) expression? */
|
||||
bool arith;
|
||||
/* unsigned arithmetic calculation */
|
||||
bool natural;
|
||||
} Expr_state;
|
||||
|
||||
#define bivui(x, op, y) (es->natural ? \
|
||||
(mksh_ari_t)((x)->val.u op (y)->val.u) : \
|
||||
(mksh_ari_t)((x)->val.i op (y)->val.i) \
|
||||
(mksh_uari_t)((x)->val.u op (y)->val.u) : \
|
||||
(mksh_uari_t)((x)->val.i op (y)->val.i) \
|
||||
)
|
||||
#define stvui(x, n) do { \
|
||||
if (es->natural) \
|
||||
(x)->val.u = (n); \
|
||||
else \
|
||||
(x)->val.i = (n); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
enum error_type {
|
||||
ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
|
||||
|
@ -159,7 +174,6 @@ static struct tbl *evalexpr(Expr_state *, int);
|
|||
static void exprtoken(Expr_state *);
|
||||
static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool);
|
||||
static void assign_check(Expr_state *, enum token, struct tbl *);
|
||||
static struct tbl *tempvar(void);
|
||||
static struct tbl *intvar(Expr_state *, struct tbl *);
|
||||
|
||||
/*
|
||||
|
@ -191,15 +205,13 @@ v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
|
|||
int i;
|
||||
|
||||
/* save state to allow recursive calls */
|
||||
memset(&curstate, 0, sizeof(curstate));
|
||||
curstate.expression = curstate.tokp = expr;
|
||||
curstate.noassign = 0;
|
||||
curstate.tok = BAD;
|
||||
curstate.arith = arith;
|
||||
curstate.evaling = NULL;
|
||||
curstate.natural = false;
|
||||
|
||||
newenv(E_ERRH);
|
||||
i = sigsetjmp(e->jbuf, 0);
|
||||
if (i) {
|
||||
if ((i = kshsetjmp(e->jbuf))) {
|
||||
/* Clear EXPRINEVAL in of any variables we were playing with */
|
||||
if (curstate.evaling)
|
||||
curstate.evaling->flag &= ~EXPRINEVAL;
|
||||
|
@ -284,7 +296,7 @@ evalerr(Expr_state *es, enum error_type type, const char *str)
|
|||
|
||||
case ET_RDONLY:
|
||||
warningf(true, "%s: %s %s",
|
||||
es->expression, str, "applied to read only variable");
|
||||
es->expression, str, "applied to read-only variable");
|
||||
break;
|
||||
|
||||
default: /* keep gcc happy */
|
||||
|
@ -300,7 +312,7 @@ evalexpr(Expr_state *es, int prec)
|
|||
{
|
||||
struct tbl *vl, *vr = NULL, *vasn;
|
||||
enum token op;
|
||||
mksh_ari_t res = 0;
|
||||
mksh_uari_t res = 0;
|
||||
|
||||
if (prec == P_PRIMARY) {
|
||||
op = es->tok;
|
||||
|
@ -343,10 +355,12 @@ evalexpr(Expr_state *es, int prec)
|
|||
op = es->tok) {
|
||||
exprtoken(es);
|
||||
vasn = vl;
|
||||
if (op != O_ASN) /* vl may not have a value yet */
|
||||
if (op != O_ASN)
|
||||
/* vl may not have a value yet */
|
||||
vl = intvar(es, vl);
|
||||
if (IS_ASSIGNOP(op)) {
|
||||
assign_check(es, op, vasn);
|
||||
if (!es->noassign)
|
||||
assign_check(es, op, vasn);
|
||||
vr = intvar(es, evalexpr(es, P_ASSIGN));
|
||||
} else if (op != O_TERN && op != O_LAND && op != O_LOR)
|
||||
vr = intvar(es, evalexpr(es, prec - 1));
|
||||
|
@ -364,11 +378,36 @@ evalexpr(Expr_state *es, int prec)
|
|||
break;
|
||||
case O_DIV:
|
||||
case O_DIVASN:
|
||||
res = bivui(vl, /, vr);
|
||||
#if !HAVE_SILENT_IDIVWRAPV
|
||||
/*
|
||||
* we are doing the comparisons here for the
|
||||
* signed arithmetics (!es->natural) case,
|
||||
* but the exact value checks and the bypass
|
||||
* case assignments are done unsignedly as
|
||||
* several compilers bitch around otherwise
|
||||
*/
|
||||
if (!es->natural &&
|
||||
vl->val.u == IDIVWRAPV_VL &&
|
||||
vr->val.u == IDIVWRAPV_VR) {
|
||||
/* -2147483648 / -1 = 2147483648 */
|
||||
/* this ^ is really (1 << 31) though */
|
||||
res = IDIVWRAPV_VL;
|
||||
} else
|
||||
#endif
|
||||
res = bivui(vl, /, vr);
|
||||
break;
|
||||
case O_MOD:
|
||||
case O_MODASN:
|
||||
res = bivui(vl, %, vr);
|
||||
#if !HAVE_SILENT_IDIVWRAPV
|
||||
/* see O_DIV / O_DIVASN for the reason behind this */
|
||||
if (!es->natural &&
|
||||
vl->val.u == IDIVWRAPV_VL &&
|
||||
vr->val.u == IDIVWRAPV_VR) {
|
||||
/* -2147483648 % -1 = 0 */
|
||||
res = 0;
|
||||
} else
|
||||
#endif
|
||||
res = bivui(vl, %, vr);
|
||||
break;
|
||||
case O_PLUS:
|
||||
case O_PLUSASN:
|
||||
|
@ -453,23 +492,23 @@ evalexpr(Expr_state *es, int prec)
|
|||
}
|
||||
break;
|
||||
case O_ASN:
|
||||
res = vr->val.i;
|
||||
res = vr->val.u;
|
||||
break;
|
||||
case O_COMMA:
|
||||
res = vr->val.i;
|
||||
res = vr->val.u;
|
||||
break;
|
||||
}
|
||||
if (IS_ASSIGNOP(op)) {
|
||||
stvui(vr, res);
|
||||
vr->val.u = res;
|
||||
if (!es->noassign) {
|
||||
if (vasn->flag & INTEGER)
|
||||
setint_v(vasn, vr, es->arith);
|
||||
else
|
||||
setint(vasn, res);
|
||||
setint(vasn, (mksh_ari_t)res);
|
||||
}
|
||||
vl = vr;
|
||||
} else if (op != O_TERN)
|
||||
stvui(vl, res);
|
||||
vl->val.u = res;
|
||||
}
|
||||
return (vl);
|
||||
}
|
||||
|
@ -597,7 +636,8 @@ do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
|
|||
setint_v(vasn, vl, es->arith);
|
||||
else
|
||||
setint(vasn, vl->val.i);
|
||||
if (!is_prefix) /* undo the inc/dec */
|
||||
if (!is_prefix)
|
||||
/* undo the increment/decrement */
|
||||
vl->val.i = oval;
|
||||
|
||||
return (vl);
|
||||
|
@ -606,14 +646,14 @@ do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
|
|||
static void
|
||||
assign_check(Expr_state *es, enum token op, struct tbl *vasn)
|
||||
{
|
||||
if (es->tok == END ||
|
||||
if (es->tok == END || !vasn ||
|
||||
(vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
|
||||
evalerr(es, ET_LVALUE, opinfo[(int)op].name);
|
||||
else if (vasn->flag & RDONLY)
|
||||
evalerr(es, ET_RDONLY, opinfo[(int)op].name);
|
||||
}
|
||||
|
||||
static struct tbl *
|
||||
struct tbl *
|
||||
tempvar(void)
|
||||
{
|
||||
struct tbl *vp;
|
||||
|
@ -804,7 +844,7 @@ utf_wctomb(char *dst, unsigned int wc)
|
|||
* disclaims all warranties with regard to this software.
|
||||
*/
|
||||
|
||||
__RCSID("$miros: src/lib/libc/i18n/wcwidth.c,v 1.10 2010/12/11 16:05:03 tg Exp $");
|
||||
__RCSID("$miros: src/lib/libc/i18n/wcwidth.c,v 1.11 2012/09/01 23:46:43 tg Exp $");
|
||||
|
||||
int
|
||||
utf_wcwidth(unsigned int c)
|
||||
|
@ -813,69 +853,71 @@ utf_wcwidth(unsigned int c)
|
|||
unsigned short first;
|
||||
unsigned short last;
|
||||
} comb[] = {
|
||||
/* Unicode 6.0.0 BMP */
|
||||
/* Unicode 6.1.0 BMP */
|
||||
{ 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD },
|
||||
{ 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 },
|
||||
{ 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, { 0x0610, 0x061A },
|
||||
{ 0x05C7, 0x05C7 }, { 0x0600, 0x0604 }, { 0x0610, 0x061A },
|
||||
{ 0x064B, 0x065F }, { 0x0670, 0x0670 }, { 0x06D6, 0x06DD },
|
||||
{ 0x06DF, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0816, 0x0819 },
|
||||
{ 0x081B, 0x0823 }, { 0x0825, 0x0827 }, { 0x0829, 0x082D },
|
||||
{ 0x0859, 0x085B }, { 0x0900, 0x0902 }, { 0x093A, 0x093A },
|
||||
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
|
||||
{ 0x0951, 0x0957 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
|
||||
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
|
||||
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
|
||||
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
|
||||
{ 0x0A51, 0x0A51 }, { 0x0A70, 0x0A71 }, { 0x0A75, 0x0A75 },
|
||||
{ 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 },
|
||||
{ 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 },
|
||||
{ 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F },
|
||||
{ 0x0B41, 0x0B44 }, { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 },
|
||||
{ 0x0B62, 0x0B63 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
|
||||
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
|
||||
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0C62, 0x0C63 },
|
||||
{ 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 },
|
||||
{ 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D44 },
|
||||
{ 0x0D4D, 0x0D4D }, { 0x0D62, 0x0D63 }, { 0x0DCA, 0x0DCA },
|
||||
{ 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 },
|
||||
{ 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 },
|
||||
{ 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD },
|
||||
{ 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 },
|
||||
{ 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 },
|
||||
{ 0x0F86, 0x0F87 }, { 0x0F8D, 0x0F97 }, { 0x0F99, 0x0FBC },
|
||||
{ 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1037 },
|
||||
{ 0x1039, 0x103A }, { 0x103D, 0x103E }, { 0x1058, 0x1059 },
|
||||
{ 0x105E, 0x1060 }, { 0x1071, 0x1074 }, { 0x1082, 0x1082 },
|
||||
{ 0x1085, 0x1086 }, { 0x108D, 0x108D }, { 0x109D, 0x109D },
|
||||
{ 0x1160, 0x11FF }, { 0x135D, 0x135F }, { 0x1712, 0x1714 },
|
||||
{ 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 },
|
||||
{ 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },
|
||||
{ 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D },
|
||||
{ 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 },
|
||||
{ 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 },
|
||||
{ 0x1A56, 0x1A56 }, { 0x1A58, 0x1A5E }, { 0x1A60, 0x1A60 },
|
||||
{ 0x1A62, 0x1A62 }, { 0x1A65, 0x1A6C }, { 0x1A73, 0x1A7C },
|
||||
{ 0x1A7F, 0x1A7F }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
|
||||
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
|
||||
{ 0x1B6B, 0x1B73 }, { 0x1B80, 0x1B81 }, { 0x1BA2, 0x1BA5 },
|
||||
{ 0x1BA8, 0x1BA9 }, { 0x1BE6, 0x1BE6 }, { 0x1BE8, 0x1BE9 },
|
||||
{ 0x1BED, 0x1BED }, { 0x1BEF, 0x1BF1 }, { 0x1C2C, 0x1C33 },
|
||||
{ 0x1C36, 0x1C37 }, { 0x1CD0, 0x1CD2 }, { 0x1CD4, 0x1CE0 },
|
||||
{ 0x1CE2, 0x1CE8 }, { 0x1CED, 0x1CED }, { 0x1DC0, 0x1DE6 },
|
||||
{ 0x0859, 0x085B }, { 0x08E4, 0x08FE }, { 0x0900, 0x0902 },
|
||||
{ 0x093A, 0x093A }, { 0x093C, 0x093C }, { 0x0941, 0x0948 },
|
||||
{ 0x094D, 0x094D }, { 0x0951, 0x0957 }, { 0x0962, 0x0963 },
|
||||
{ 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 },
|
||||
{ 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 },
|
||||
{ 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 },
|
||||
{ 0x0A4B, 0x0A4D }, { 0x0A51, 0x0A51 }, { 0x0A70, 0x0A71 },
|
||||
{ 0x0A75, 0x0A75 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
|
||||
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
|
||||
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
|
||||
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B44 }, { 0x0B4D, 0x0B4D },
|
||||
{ 0x0B56, 0x0B56 }, { 0x0B62, 0x0B63 }, { 0x0B82, 0x0B82 },
|
||||
{ 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },
|
||||
{ 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },
|
||||
{ 0x0C62, 0x0C63 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF },
|
||||
{ 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 },
|
||||
{ 0x0D41, 0x0D44 }, { 0x0D4D, 0x0D4D }, { 0x0D62, 0x0D63 },
|
||||
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
|
||||
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
|
||||
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
|
||||
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
|
||||
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
|
||||
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F8D, 0x0F97 },
|
||||
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
|
||||
{ 0x1032, 0x1037 }, { 0x1039, 0x103A }, { 0x103D, 0x103E },
|
||||
{ 0x1058, 0x1059 }, { 0x105E, 0x1060 }, { 0x1071, 0x1074 },
|
||||
{ 0x1082, 0x1082 }, { 0x1085, 0x1086 }, { 0x108D, 0x108D },
|
||||
{ 0x109D, 0x109D }, { 0x1160, 0x11FF }, { 0x135D, 0x135F },
|
||||
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
|
||||
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
|
||||
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
|
||||
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
|
||||
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
|
||||
{ 0x1A17, 0x1A18 }, { 0x1A56, 0x1A56 }, { 0x1A58, 0x1A5E },
|
||||
{ 0x1A60, 0x1A60 }, { 0x1A62, 0x1A62 }, { 0x1A65, 0x1A6C },
|
||||
{ 0x1A73, 0x1A7C }, { 0x1A7F, 0x1A7F }, { 0x1B00, 0x1B03 },
|
||||
{ 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C },
|
||||
{ 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1B80, 0x1B81 },
|
||||
{ 0x1BA2, 0x1BA5 }, { 0x1BA8, 0x1BA9 }, { 0x1BAB, 0x1BAB },
|
||||
{ 0x1BE6, 0x1BE6 }, { 0x1BE8, 0x1BE9 }, { 0x1BED, 0x1BED },
|
||||
{ 0x1BEF, 0x1BF1 }, { 0x1C2C, 0x1C33 }, { 0x1C36, 0x1C37 },
|
||||
{ 0x1CD0, 0x1CD2 }, { 0x1CD4, 0x1CE0 }, { 0x1CE2, 0x1CE8 },
|
||||
{ 0x1CED, 0x1CED }, { 0x1CF4, 0x1CF4 }, { 0x1DC0, 0x1DE6 },
|
||||
{ 0x1DFC, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E },
|
||||
{ 0x2060, 0x2064 }, { 0x206A, 0x206F }, { 0x20D0, 0x20F0 },
|
||||
{ 0x2CEF, 0x2CF1 }, { 0x2D7F, 0x2D7F }, { 0x2DE0, 0x2DFF },
|
||||
{ 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA66F, 0xA672 },
|
||||
{ 0xA67C, 0xA67D }, { 0xA6F0, 0xA6F1 }, { 0xA802, 0xA802 },
|
||||
{ 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 },
|
||||
{ 0xA8C4, 0xA8C4 }, { 0xA8E0, 0xA8F1 }, { 0xA926, 0xA92D },
|
||||
{ 0xA947, 0xA951 }, { 0xA980, 0xA982 }, { 0xA9B3, 0xA9B3 },
|
||||
{ 0xA9B6, 0xA9B9 }, { 0xA9BC, 0xA9BC }, { 0xAA29, 0xAA2E },
|
||||
{ 0xAA31, 0xAA32 }, { 0xAA35, 0xAA36 }, { 0xAA43, 0xAA43 },
|
||||
{ 0xAA4C, 0xAA4C }, { 0xAAB0, 0xAAB0 }, { 0xAAB2, 0xAAB4 },
|
||||
{ 0xAAB7, 0xAAB8 }, { 0xAABE, 0xAABF }, { 0xAAC1, 0xAAC1 },
|
||||
{ 0x302A, 0x302D }, { 0x3099, 0x309A }, { 0xA66F, 0xA672 },
|
||||
{ 0xA674, 0xA67D }, { 0xA69F, 0xA69F }, { 0xA6F0, 0xA6F1 },
|
||||
{ 0xA802, 0xA802 }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
|
||||
{ 0xA825, 0xA826 }, { 0xA8C4, 0xA8C4 }, { 0xA8E0, 0xA8F1 },
|
||||
{ 0xA926, 0xA92D }, { 0xA947, 0xA951 }, { 0xA980, 0xA982 },
|
||||
{ 0xA9B3, 0xA9B3 }, { 0xA9B6, 0xA9B9 }, { 0xA9BC, 0xA9BC },
|
||||
{ 0xAA29, 0xAA2E }, { 0xAA31, 0xAA32 }, { 0xAA35, 0xAA36 },
|
||||
{ 0xAA43, 0xAA43 }, { 0xAA4C, 0xAA4C }, { 0xAAB0, 0xAAB0 },
|
||||
{ 0xAAB2, 0xAAB4 }, { 0xAAB7, 0xAAB8 }, { 0xAABE, 0xAABF },
|
||||
{ 0xAAC1, 0xAAC1 }, { 0xAAEC, 0xAAED }, { 0xAAF6, 0xAAF6 },
|
||||
{ 0xABE5, 0xABE5 }, { 0xABE8, 0xABE8 }, { 0xABED, 0xABED },
|
||||
{ 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE26 },
|
||||
{ 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }
|
||||
|
|
802
src/funcs.c
802
src/funcs.c
File diff suppressed because it is too large
Load diff
894
src/histrap.c
894
src/histrap.c
File diff suppressed because it is too large
Load diff
162
src/jobs.c
162
src/jobs.c
|
@ -1,7 +1,7 @@
|
|||
/* $OpenBSD: jobs.c,v 1.38 2009/12/12 04:28:44 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.81 2011/08/27 18:06:46 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.94 2012/12/28 02:28:36 tg Exp $");
|
||||
|
||||
#if HAVE_KILLPG
|
||||
#define mksh_killpg killpg
|
||||
|
@ -37,13 +37,15 @@ __RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.81 2011/08/27 18:06:46 tg Exp $");
|
|||
#define PSIGNALLED 2
|
||||
#define PSTOPPED 3
|
||||
|
||||
typedef struct proc Proc;
|
||||
typedef struct proc Proc;
|
||||
struct proc {
|
||||
Proc *next; /* next process in pipeline (if any) */
|
||||
pid_t pid; /* process id */
|
||||
int state;
|
||||
int status; /* wait status */
|
||||
char command[48]; /* process command string */
|
||||
/* process command string from vistree */
|
||||
char command[64 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) +
|
||||
2 * sizeof(int))];
|
||||
};
|
||||
|
||||
/* Notify/print flag - j_print() argument */
|
||||
|
@ -87,7 +89,7 @@ struct job {
|
|||
int32_t age; /* number of jobs started */
|
||||
Coproc_id coproc_id; /* 0 or id of coprocess output pipe */
|
||||
#ifndef MKSH_UNEMPLOYED
|
||||
struct termios ttystat; /* saved tty state for stopped jobs */
|
||||
mksh_ttyst ttystat; /* saved tty state for stopped jobs */
|
||||
pid_t saved_ttypgrp; /* saved tty process group for stopped jobs */
|
||||
#endif
|
||||
};
|
||||
|
@ -105,7 +107,7 @@ struct job {
|
|||
#define JL_AMBIG 2 /* %foo or %?foo is ambiguous */
|
||||
#define JL_INVALID 3 /* non-pid, non-% job id */
|
||||
|
||||
static const char *const lookup_msgs[] = {
|
||||
static const char * const lookup_msgs[] = {
|
||||
null,
|
||||
"no such job",
|
||||
"ambiguous",
|
||||
|
@ -150,6 +152,9 @@ static void put_job(Job *, int);
|
|||
static void remove_job(Job *, const char *);
|
||||
static int kill_job(Job *, int);
|
||||
|
||||
static void tty_init_talking(void);
|
||||
static void tty_init_state(void);
|
||||
|
||||
/* initialise job control */
|
||||
void
|
||||
j_init(void)
|
||||
|
@ -199,13 +204,15 @@ j_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* j_change() calls tty_init() */
|
||||
/* j_change() calls tty_init_talking() and tty_init_state() */
|
||||
if (Flag(FMONITOR))
|
||||
j_change();
|
||||
else
|
||||
#endif
|
||||
if (Flag(FTALKING))
|
||||
tty_init(true, true);
|
||||
if (Flag(FTALKING)) {
|
||||
tty_init_talking();
|
||||
tty_init_state();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -284,12 +291,12 @@ j_change(void)
|
|||
if (Flag(FMONITOR)) {
|
||||
bool use_tty = Flag(FTALKING);
|
||||
|
||||
/* Don't call tcgetattr() 'til we own the tty process group */
|
||||
/* don't call mksh_tcget until we own the tty process group */
|
||||
if (use_tty)
|
||||
tty_init(false, true);
|
||||
tty_init_talking();
|
||||
|
||||
/* no controlling tty, no SIGT* */
|
||||
if ((ttypgrp_ok = use_tty && tty_fd >= 0 && tty_devtty)) {
|
||||
if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
|
||||
setsig(&sigtraps[SIGTTIN], SIG_DFL,
|
||||
SS_RESTORE_ORIG|SS_FORCE);
|
||||
/* wait to be given tty (POSIX.1, B.2, job control) */
|
||||
|
@ -299,7 +306,7 @@ j_change(void)
|
|||
if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
|
||||
warningf(false, "%s: %s %s: %s",
|
||||
"j_init", "tcgetpgrp", "failed",
|
||||
strerror(errno));
|
||||
cstrerror(errno));
|
||||
ttypgrp_ok = false;
|
||||
break;
|
||||
}
|
||||
|
@ -314,24 +321,24 @@ j_change(void)
|
|||
if (ttypgrp_ok && kshpgrp != kshpid) {
|
||||
if (setpgid(0, kshpid) < 0) {
|
||||
warningf(false, "%s: %s %s: %s", "j_init",
|
||||
"setpgid", "failed", strerror(errno));
|
||||
"setpgid", "failed", cstrerror(errno));
|
||||
ttypgrp_ok = false;
|
||||
} else {
|
||||
if (tcsetpgrp(tty_fd, kshpid) < 0) {
|
||||
warningf(false, "%s: %s %s: %s",
|
||||
"j_init", "tcsetpgrp", "failed",
|
||||
strerror(errno));
|
||||
cstrerror(errno));
|
||||
ttypgrp_ok = false;
|
||||
} else
|
||||
restore_ttypgrp = kshpgrp;
|
||||
kshpgrp = kshpid;
|
||||
}
|
||||
}
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
if (use_tty && !ttypgrp_ok)
|
||||
warningf(false, "%s: %s", "warning",
|
||||
"won't have full job control");
|
||||
if (tty_fd >= 0)
|
||||
tcgetattr(tty_fd, &tty_state);
|
||||
#endif
|
||||
} else {
|
||||
ttypgrp_ok = false;
|
||||
if (Flag(FTALKING))
|
||||
|
@ -347,9 +354,8 @@ j_change(void)
|
|||
SIG_IGN : SIG_DFL,
|
||||
SS_RESTORE_ORIG|SS_FORCE);
|
||||
}
|
||||
if (!Flag(FTALKING))
|
||||
tty_close();
|
||||
}
|
||||
tty_init_state();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -359,12 +365,12 @@ static void
|
|||
ksh_nice(int ness)
|
||||
{
|
||||
#if defined(__USE_FORTIFY_LEVEL) && (__USE_FORTIFY_LEVEL > 0)
|
||||
int e;
|
||||
int eno;
|
||||
|
||||
errno = 0;
|
||||
/* this is gonna annoy users; complain to your distro, people! */
|
||||
if (nice(ness) == -1 && (e = errno) != 0)
|
||||
warningf(false, "%s: %s", "bgnice", strerror(e));
|
||||
if (nice(ness) == -1 && (eno = errno) != 0)
|
||||
warningf(false, "%s: %s", "bgnice", cstrerror(eno));
|
||||
#else
|
||||
(void)nice(ness);
|
||||
#endif
|
||||
|
@ -473,6 +479,7 @@ exchild(struct op *t, int flags,
|
|||
/* job control set up */
|
||||
if (Flag(FMONITOR) && !(flags&XXCOM)) {
|
||||
bool dotty = false;
|
||||
|
||||
if (j->pgrp == 0) {
|
||||
/* First process */
|
||||
j->pgrp = p->pid;
|
||||
|
@ -542,7 +549,6 @@ exchild(struct op *t, int flags,
|
|||
Flag(FMONITOR) = 0;
|
||||
#endif
|
||||
Flag(FTALKING) = 0;
|
||||
tty_close();
|
||||
cleartraps();
|
||||
/* no return */
|
||||
execute(t, (flags & XERROK) | XEXEC, NULL);
|
||||
|
@ -729,7 +735,7 @@ j_kill(const char *cp, int sig)
|
|||
if (j->pgrp == 0) {
|
||||
/* started when !Flag(FMONITOR) */
|
||||
if (kill_job(j, sig) < 0) {
|
||||
bi_errorf("%s: %s", cp, strerror(errno));
|
||||
bi_errorf("%s: %s", cp, cstrerror(errno));
|
||||
rv = 1;
|
||||
}
|
||||
} else {
|
||||
|
@ -738,7 +744,7 @@ j_kill(const char *cp, int sig)
|
|||
mksh_killpg(j->pgrp, SIGCONT);
|
||||
#endif
|
||||
if (mksh_killpg(j->pgrp, sig) < 0) {
|
||||
bi_errorf("%s: %s", cp, strerror(errno));
|
||||
bi_errorf("%s: %s", cp, cstrerror(errno));
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
|
@ -801,20 +807,20 @@ j_resume(const char *cp, int bg)
|
|||
/* attach tty to job */
|
||||
if (j->state == PRUNNING) {
|
||||
if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
|
||||
tcsetattr(tty_fd, TCSADRAIN, &j->ttystat);
|
||||
mksh_tcset(tty_fd, &j->ttystat);
|
||||
/* See comment in j_waitj regarding saved_ttypgrp. */
|
||||
if (ttypgrp_ok &&
|
||||
tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ?
|
||||
j->saved_ttypgrp : j->pgrp) < 0) {
|
||||
rv = errno;
|
||||
if (j->flags & JF_SAVEDTTY)
|
||||
tcsetattr(tty_fd, TCSADRAIN, &tty_state);
|
||||
mksh_tcset(tty_fd, &tty_state);
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
bi_errorf("%s %s(%d, %ld) %s: %s",
|
||||
"1st", "tcsetpgrp", tty_fd,
|
||||
(long)((j->flags & JF_SAVEDTTYPGRP) ?
|
||||
j->saved_ttypgrp : j->pgrp), "failed",
|
||||
strerror(rv));
|
||||
cstrerror(rv));
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
@ -825,20 +831,20 @@ j_resume(const char *cp, int bg)
|
|||
}
|
||||
|
||||
if (j->state == PRUNNING && mksh_killpg(j->pgrp, SIGCONT) < 0) {
|
||||
int err = errno;
|
||||
int eno = errno;
|
||||
|
||||
if (!bg) {
|
||||
j->flags &= ~JF_FG;
|
||||
if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
|
||||
tcsetattr(tty_fd, TCSADRAIN, &tty_state);
|
||||
mksh_tcset(tty_fd, &tty_state);
|
||||
if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
|
||||
warningf(true, "%s %s(%d, %ld) %s: %s",
|
||||
"fg: 2nd", "tcsetpgrp", tty_fd,
|
||||
(long)kshpgrp, "failed", strerror(errno));
|
||||
(long)kshpgrp, "failed", cstrerror(errno));
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
bi_errorf("%s %s %s", "can't continue job",
|
||||
cp, strerror(err));
|
||||
cp, cstrerror(eno));
|
||||
return (1);
|
||||
}
|
||||
if (!bg) {
|
||||
|
@ -1060,6 +1066,9 @@ j_waitj(Job *j,
|
|||
const char *where)
|
||||
{
|
||||
int rv;
|
||||
#ifdef MKSH_NO_SIGSUSPEND
|
||||
sigset_t omask;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* No auto-notify on the job we are waiting on.
|
||||
|
@ -1076,7 +1085,14 @@ j_waitj(Job *j,
|
|||
while (j->state == PRUNNING ||
|
||||
((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) {
|
||||
#ifndef MKSH_NOPROSPECTOFWORK
|
||||
#ifdef MKSH_NO_SIGSUSPEND
|
||||
sigprocmask(SIG_SETMASK, &sm_default, &omask);
|
||||
pause();
|
||||
/* note that handlers may run here so they need to know */
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
#else
|
||||
sigsuspend(&sm_default);
|
||||
#endif
|
||||
#else
|
||||
j_sigchld(SIGCHLD);
|
||||
#endif
|
||||
|
@ -1115,14 +1131,14 @@ j_waitj(Job *j,
|
|||
if (tcsetpgrp(tty_fd, kshpgrp) < 0)
|
||||
warningf(true, "%s %s(%d, %ld) %s: %s",
|
||||
"j_waitj:", "tcsetpgrp", tty_fd,
|
||||
(long)kshpgrp, "failed", strerror(errno));
|
||||
(long)kshpgrp, "failed", cstrerror(errno));
|
||||
if (j->state == PSTOPPED) {
|
||||
j->flags |= JF_SAVEDTTY;
|
||||
tcgetattr(tty_fd, &j->ttystat);
|
||||
mksh_tcget(tty_fd, &j->ttystat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (tty_fd >= 0) {
|
||||
if (tty_hasstate) {
|
||||
/*
|
||||
* Only restore tty settings if job was originally
|
||||
* started in the foreground. Problems can be
|
||||
|
@ -1134,9 +1150,9 @@ j_waitj(Job *j,
|
|||
*/
|
||||
if (j->state == PEXITED && j->status == 0 &&
|
||||
(j->flags & JF_USETTYMODE)) {
|
||||
tcgetattr(tty_fd, &tty_state);
|
||||
mksh_tcget(tty_fd, &tty_state);
|
||||
} else {
|
||||
tcsetattr(tty_fd, TCSADRAIN, &tty_state);
|
||||
mksh_tcset(tty_fd, &tty_state);
|
||||
/*-
|
||||
* Don't use tty mode if job is stopped and
|
||||
* later restarted and exits. Consider
|
||||
|
@ -1242,6 +1258,12 @@ j_sigchld(int sig MKSH_A_UNUSED)
|
|||
pid_t pid;
|
||||
int status;
|
||||
struct rusage ru0, ru1;
|
||||
#ifdef MKSH_NO_SIGSUSPEND
|
||||
sigset_t omask;
|
||||
|
||||
/* this handler can run while SIGCHLD is not blocked, so block it now */
|
||||
sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
|
||||
#endif
|
||||
|
||||
#ifndef MKSH_NOPROSPECTOFWORK
|
||||
/*
|
||||
|
@ -1253,7 +1275,7 @@ j_sigchld(int sig MKSH_A_UNUSED)
|
|||
for (j = job_list; j; j = j->next)
|
||||
if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
|
||||
held_sigchld = 1;
|
||||
return;
|
||||
goto j_sigchld_out;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1270,7 +1292,7 @@ j_sigchld(int sig MKSH_A_UNUSED)
|
|||
* or interrupted (-1)
|
||||
*/
|
||||
if (pid <= 0)
|
||||
return;
|
||||
goto j_sigchld_out;
|
||||
|
||||
getrusage(RUSAGE_CHILDREN, &ru1);
|
||||
|
||||
|
@ -1313,6 +1335,12 @@ j_sigchld(int sig MKSH_A_UNUSED)
|
|||
#else
|
||||
while (/* CONSTCOND */ 0);
|
||||
#endif
|
||||
|
||||
j_sigchld_out:
|
||||
#ifdef MKSH_NO_SIGSUSPEND
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
#endif
|
||||
/* nothing */;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1491,6 +1519,8 @@ j_print(Job *j, int how, struct shf *shf)
|
|||
strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,
|
||||
sizeof(buf));
|
||||
break;
|
||||
default:
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
if (how != JP_SHORT) {
|
||||
|
@ -1692,10 +1722,13 @@ remove_job(Job *j, const char *where)
|
|||
Proc *p, *tmp;
|
||||
Job **prev, *curr;
|
||||
|
||||
mkssert(j != NULL);
|
||||
prev = &job_list;
|
||||
curr = *prev;
|
||||
for (; curr != NULL && curr != j; prev = &curr->next, curr = *prev)
|
||||
;
|
||||
curr = job_list;
|
||||
while (curr && curr != j) {
|
||||
prev = &curr->next;
|
||||
curr = *prev;
|
||||
}
|
||||
if (curr != j) {
|
||||
internal_warningf("remove_job: job %s (%s)", "not found", where);
|
||||
return;
|
||||
|
@ -1732,11 +1765,14 @@ put_job(Job *j, int where)
|
|||
{
|
||||
Job **prev, *curr;
|
||||
|
||||
mkssert(j != NULL);
|
||||
/* Remove job from list (if there) */
|
||||
prev = &job_list;
|
||||
curr = job_list;
|
||||
for (; curr && curr != j; prev = &curr->next, curr = *prev)
|
||||
;
|
||||
while (curr && curr != j) {
|
||||
prev = &curr->next;
|
||||
curr = *prev;
|
||||
}
|
||||
if (curr == j)
|
||||
*prev = curr->next;
|
||||
|
||||
|
@ -1775,3 +1811,41 @@ kill_job(Job *j, int sig)
|
|||
rval = -1;
|
||||
return (rval);
|
||||
}
|
||||
|
||||
static void
|
||||
tty_init_talking(void)
|
||||
{
|
||||
switch (tty_init_fd()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
warningf(false, "%s: %s %s: %s",
|
||||
"No controlling tty", "open", "/dev/tty",
|
||||
cstrerror(errno));
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
warningf(false, "%s: %s", "can't find tty fd", cstrerror(errno));
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
warningf(false, "%s: %s %s: %s", "j_ttyinit",
|
||||
"dup of tty fd", "failed", cstrerror(errno));
|
||||
break;
|
||||
case 4:
|
||||
warningf(false, "%s: %s: %s", "j_ttyinit",
|
||||
"can't set close-on-exec flag", cstrerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tty_init_state(void)
|
||||
{
|
||||
if (tty_fd >= 0) {
|
||||
mksh_tcget(tty_fd, &tty_state);
|
||||
tty_hasstate = true;
|
||||
}
|
||||
}
|
||||
|
|
269
src/lex.c
269
src/lex.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: lex.c,v 1.45 2011/03/09 09:30:39 okan Exp $ */
|
||||
/* $OpenBSD: lex.c,v 1.46 2013/01/20 14:47:46 stsp Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.156 2011/09/07 15:24:16 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.182 2013/02/19 18:45:20 tg Exp $");
|
||||
|
||||
/*
|
||||
* states while lexing word
|
||||
|
@ -37,13 +38,12 @@ __RCSID("$MirOS: src/bin/mksh/lex.c,v 1.156 2011/09/07 15:24:16 tg Exp $");
|
|||
#define SQBRACE 7 /* inside "${}" */
|
||||
#define SBQUOTE 8 /* inside `` */
|
||||
#define SASPAREN 9 /* inside $(( )) */
|
||||
#define SHEREDELIM 10 /* parsing <<,<<- delimiter */
|
||||
#define SHEREDQUOTE 11 /* parsing " in <<,<<- delimiter */
|
||||
#define SHEREDELIM 10 /* parsing <<,<<-,<<< delimiter */
|
||||
#define SHEREDQUOTE 11 /* parsing " in <<,<<-,<<< delimiter */
|
||||
#define SPATTERN 12 /* parsing *(...|...) pattern (*+?@!) */
|
||||
#define SADELIM 13 /* like SBASE, looking for delimiter */
|
||||
#define SHERESTRING 14 /* parsing <<< string */
|
||||
#define STBRACEKORN 15 /* parsing ${...[#%]...} !FSH */
|
||||
#define STBRACEBOURNE 16 /* parsing ${...[#%]...} FSH */
|
||||
#define STBRACEKORN 14 /* parsing ${...[#%]...} !FSH */
|
||||
#define STBRACEBOURNE 15 /* parsing ${...[#%]...} FSH */
|
||||
#define SINVALID 255 /* invalid state */
|
||||
|
||||
struct sretrace_info {
|
||||
|
@ -90,7 +90,7 @@ typedef struct {
|
|||
|
||||
static void readhere(struct ioword *);
|
||||
static void ungetsc(int);
|
||||
static void ungetsc_(int);
|
||||
static void ungetsc_i(int);
|
||||
static int getsc_uu(void);
|
||||
static void getsc_line(Source *);
|
||||
static int getsc_bn(void);
|
||||
|
@ -99,16 +99,13 @@ static void s_put(int);
|
|||
static char *get_brace_var(XString *, char *);
|
||||
static bool arraysub(char **);
|
||||
static void gethere(bool);
|
||||
static Lex_state *push_state_(State_info *, Lex_state *);
|
||||
static Lex_state *pop_state_(State_info *, Lex_state *);
|
||||
static Lex_state *push_state_i(State_info *, Lex_state *);
|
||||
static Lex_state *pop_state_i(State_info *, Lex_state *);
|
||||
|
||||
static int dopprompt(const char *, int, bool);
|
||||
void yyskiputf8bom(void);
|
||||
|
||||
static int backslash_skip;
|
||||
static int ignore_backslash_newline;
|
||||
static struct sretrace_info *retrace_info;
|
||||
short subshell_nesting_level = 0;
|
||||
|
||||
/* optimised getsc_bn() */
|
||||
#define o_getsc() (*source->str != '\0' && *source->str != '\\' && \
|
||||
|
@ -130,7 +127,7 @@ short subshell_nesting_level = 0;
|
|||
return (cev); \
|
||||
}
|
||||
|
||||
#ifdef MKSH_SMALL
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
static int getsc(void);
|
||||
|
||||
static int
|
||||
|
@ -154,13 +151,13 @@ getsc_r(int c)
|
|||
|
||||
#define PUSH_STATE(s) do { \
|
||||
if (++statep == state_info.end) \
|
||||
statep = push_state_(&state_info, statep); \
|
||||
statep = push_state_i(&state_info, statep); \
|
||||
state = statep->type = (s); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
#define POP_STATE() do { \
|
||||
if (--statep == state_info.base) \
|
||||
statep = pop_state_(&state_info, statep); \
|
||||
statep = pop_state_i(&state_info, statep); \
|
||||
state = statep->type; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
|
@ -247,7 +244,7 @@ yylex(int cf)
|
|||
if (state == SHEREDELIM) {
|
||||
c = getsc();
|
||||
if (c == '<') {
|
||||
state = SHERESTRING;
|
||||
state = SHEREDELIM;
|
||||
while ((c = getsc()) == ' ' || c == '\t')
|
||||
;
|
||||
ungetsc(c);
|
||||
|
@ -259,8 +256,12 @@ yylex(int cf)
|
|||
|
||||
/* collect non-special or quoted characters to form word */
|
||||
while (!((c = getsc()) == 0 ||
|
||||
((state == SBASE || state == SHEREDELIM || state == SHERESTRING) &&
|
||||
ctype(c, C_LEX1)))) {
|
||||
((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
|
||||
if (state == SBASE &&
|
||||
subshell_nesting_type == /*{*/ '}' &&
|
||||
c == /*{*/ '}')
|
||||
/* possibly end ${ :;} */
|
||||
break;
|
||||
accept_nonword:
|
||||
Xcheck(ws, wp);
|
||||
switch (state) {
|
||||
|
@ -269,8 +270,8 @@ yylex(int cf)
|
|||
statep->nparen++;
|
||||
else if (c == ')')
|
||||
statep->nparen--;
|
||||
else if (statep->nparen == 0 &&
|
||||
(c == /*{*/ '}' || c == statep->ls_adelim.delimiter)) {
|
||||
else if (statep->nparen == 0 && (c == /*{*/ '}' ||
|
||||
c == (int)statep->ls_adelim.delimiter)) {
|
||||
*wp++ = ADELIM;
|
||||
*wp++ = c;
|
||||
if (c == /*{*/ '}' || --statep->ls_adelim.num == 0)
|
||||
|
@ -347,6 +348,22 @@ yylex(int cf)
|
|||
*wp++ = OQUOTE;
|
||||
PUSH_STATE(SDQUOTE);
|
||||
break;
|
||||
case '$':
|
||||
/*
|
||||
* processing of dollar sign belongs into
|
||||
* Subst, except for those which can open
|
||||
* a string: $'…' and $"…"
|
||||
*/
|
||||
subst_dollar_ex:
|
||||
c = getsc();
|
||||
switch (c) {
|
||||
case '"':
|
||||
goto open_sdquote;
|
||||
case '\'':
|
||||
goto open_sequote;
|
||||
default:
|
||||
goto SubstS;
|
||||
}
|
||||
default:
|
||||
goto Subst;
|
||||
}
|
||||
|
@ -381,8 +398,8 @@ yylex(int cf)
|
|||
}
|
||||
break;
|
||||
case '$':
|
||||
subst_dollar:
|
||||
c = getsc();
|
||||
SubstS:
|
||||
if (c == '(') /*)*/ {
|
||||
c = getsc();
|
||||
if (c == '(') /*)*/ {
|
||||
|
@ -394,14 +411,26 @@ yylex(int cf)
|
|||
} else {
|
||||
ungetsc(c);
|
||||
subst_command:
|
||||
sp = yyrecursive();
|
||||
c = COMSUB;
|
||||
subst_command2:
|
||||
sp = yyrecursive(c);
|
||||
cz = strlen(sp) + 1;
|
||||
XcheckN(ws, wp, cz);
|
||||
*wp++ = COMSUB;
|
||||
*wp++ = c;
|
||||
memcpy(wp, sp, cz);
|
||||
wp += cz;
|
||||
}
|
||||
} else if (c == '{') /*}*/ {
|
||||
c = getsc();
|
||||
if (ctype(c, C_IFSWS)) {
|
||||
/*
|
||||
* non-subenvironment
|
||||
* "command" substitution
|
||||
*/
|
||||
c = FUNSUB;
|
||||
goto subst_command2;
|
||||
}
|
||||
ungetsc(c);
|
||||
*wp++ = OSUBST;
|
||||
*wp++ = '{'; /*}*/
|
||||
wp = get_brace_var(&ws, wp);
|
||||
|
@ -491,20 +520,9 @@ yylex(int cf)
|
|||
*wp++ = '\0';
|
||||
*wp++ = CSUBST;
|
||||
*wp++ = 'X';
|
||||
} else if (c == '\'' && (state == SBASE)) {
|
||||
/* XXX which other states are valid? */
|
||||
*wp++ = OQUOTE;
|
||||
ignore_backslash_newline++;
|
||||
PUSH_STATE(SEQUOTE);
|
||||
statep->ls_bool = false;
|
||||
break;
|
||||
} else if (c == '"' && (state == SBASE)) {
|
||||
/* XXX which other states are valid? */
|
||||
goto DEQUOTE;
|
||||
} else {
|
||||
*wp++ = CHAR;
|
||||
*wp++ = '$';
|
||||
DEQUOTE:
|
||||
ungetsc(c);
|
||||
}
|
||||
break;
|
||||
|
@ -682,7 +700,7 @@ yylex(int cf)
|
|||
if (c == '"')
|
||||
goto open_sdquote;
|
||||
else if (c == '$')
|
||||
goto subst_dollar;
|
||||
goto subst_dollar_ex;
|
||||
else if (c == '`')
|
||||
goto subst_gravis;
|
||||
else if (c != /*{*/ '}')
|
||||
|
@ -779,98 +797,60 @@ yylex(int cf)
|
|||
++statep->nparen;
|
||||
goto Sbase2;
|
||||
|
||||
/* <<< delimiter */
|
||||
case SHERESTRING:
|
||||
if (c == '\\') {
|
||||
c = getsc();
|
||||
if (c) {
|
||||
/* trailing \ is lost */
|
||||
*wp++ = QCHAR;
|
||||
*wp++ = c;
|
||||
}
|
||||
} else if (c == '$') {
|
||||
if ((c2 = getsc()) == '\'') {
|
||||
PUSH_STATE(SEQUOTE);
|
||||
statep->ls_bool = false;
|
||||
goto sherestring_quoted;
|
||||
} else if (c2 == '"')
|
||||
goto sherestring_dquoted;
|
||||
ungetsc(c2);
|
||||
goto sherestring_regular;
|
||||
} else if (c == '\'') {
|
||||
PUSH_STATE(SSQUOTE);
|
||||
sherestring_quoted:
|
||||
*wp++ = OQUOTE;
|
||||
ignore_backslash_newline++;
|
||||
} else if (c == '"') {
|
||||
sherestring_dquoted:
|
||||
state = statep->type = SHEREDQUOTE;
|
||||
*wp++ = OQUOTE;
|
||||
/* just don't IFS split; no quoting mode */
|
||||
} else {
|
||||
sherestring_regular:
|
||||
*wp++ = CHAR;
|
||||
*wp++ = c;
|
||||
}
|
||||
break;
|
||||
|
||||
/* <<,<<- delimiter */
|
||||
/* <<, <<-, <<< delimiter */
|
||||
case SHEREDELIM:
|
||||
/*
|
||||
* XXX chuck this state (and the next) - use
|
||||
* the existing states ($ and \`...` should be
|
||||
* stripped of their specialness after the
|
||||
* fact).
|
||||
*/
|
||||
/*
|
||||
* here delimiters need a special case since
|
||||
* $ and `...` are not to be treated specially
|
||||
*/
|
||||
if (c == '\\') {
|
||||
c = getsc();
|
||||
if (c) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
if ((c = getsc())) {
|
||||
/* trailing \ is lost */
|
||||
*wp++ = QCHAR;
|
||||
*wp++ = c;
|
||||
}
|
||||
} else if (c == '$') {
|
||||
break;
|
||||
case '\'':
|
||||
goto open_ssquote;
|
||||
case '$':
|
||||
if ((c2 = getsc()) == '\'') {
|
||||
open_sequote:
|
||||
*wp++ = OQUOTE;
|
||||
ignore_backslash_newline++;
|
||||
PUSH_STATE(SEQUOTE);
|
||||
statep->ls_bool = false;
|
||||
goto sheredelim_quoted;
|
||||
} else if (c2 == '"')
|
||||
goto sheredelim_dquoted;
|
||||
break;
|
||||
} else if (c2 == '"') {
|
||||
/* FALLTHROUGH */
|
||||
case '"':
|
||||
state = statep->type = SHEREDQUOTE;
|
||||
PUSH_SRETRACE();
|
||||
break;
|
||||
}
|
||||
ungetsc(c2);
|
||||
goto sheredelim_regular;
|
||||
} else if (c == '\'') {
|
||||
PUSH_STATE(SSQUOTE);
|
||||
sheredelim_quoted:
|
||||
*wp++ = OQUOTE;
|
||||
ignore_backslash_newline++;
|
||||
} else if (c == '"') {
|
||||
sheredelim_dquoted:
|
||||
state = statep->type = SHEREDQUOTE;
|
||||
*wp++ = OQUOTE;
|
||||
} else {
|
||||
sheredelim_regular:
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
*wp++ = CHAR;
|
||||
*wp++ = c;
|
||||
}
|
||||
break;
|
||||
|
||||
/* " in <<,<<- delimiter */
|
||||
/* " in <<, <<-, <<< delimiter */
|
||||
case SHEREDQUOTE:
|
||||
if (c == '"') {
|
||||
*wp++ = CQUOTE;
|
||||
state = statep->type =
|
||||
/* dp[1] == '<' means here string */
|
||||
Xstring(ws, wp)[1] == '<' ?
|
||||
SHERESTRING : SHEREDELIM;
|
||||
} else {
|
||||
if (c != '"')
|
||||
goto Subst;
|
||||
POP_SRETRACE();
|
||||
dp = strnul(sp) - 1;
|
||||
/* remove the trailing double quote */
|
||||
*dp = '\0';
|
||||
/* store the quoted string */
|
||||
*wp++ = OQUOTE;
|
||||
XcheckN(ws, wp, (dp - sp));
|
||||
dp = sp;
|
||||
while ((c = *dp++)) {
|
||||
if (c == '\\') {
|
||||
switch (c = getsc()) {
|
||||
case 0:
|
||||
/* trailing \ is lost */
|
||||
switch ((c = *dp++)) {
|
||||
case '\\':
|
||||
case '"':
|
||||
case '$':
|
||||
|
@ -885,6 +865,9 @@ yylex(int cf)
|
|||
*wp++ = CHAR;
|
||||
*wp++ = c;
|
||||
}
|
||||
afree(sp, ATEMP);
|
||||
*wp++ = CQUOTE;
|
||||
state = statep->type = SHEREDELIM;
|
||||
break;
|
||||
|
||||
/* in *(...|...) pattern (*+?@!) */
|
||||
|
@ -911,7 +894,7 @@ yylex(int cf)
|
|||
yyerror("no closing quote\n");
|
||||
|
||||
/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
|
||||
if (state == SHEREDELIM || state == SHERESTRING)
|
||||
if (state == SHEREDELIM)
|
||||
state = SBASE;
|
||||
|
||||
dp = Xstring(ws, wp);
|
||||
|
@ -1033,17 +1016,17 @@ yylex(int cf)
|
|||
sp = yylval.cp;
|
||||
dp = ident;
|
||||
if ((cf & HEREDELIM) && (sp[1] == '<'))
|
||||
while (dp < ident+IDENT) {
|
||||
while ((dp - ident) < IDENT) {
|
||||
if ((c = *sp++) == CHAR)
|
||||
*dp++ = *sp++;
|
||||
else if ((c != OQUOTE) && (c != CQUOTE))
|
||||
break;
|
||||
}
|
||||
else
|
||||
while (dp < ident+IDENT && (c = *sp++) == CHAR)
|
||||
while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
|
||||
*dp++ = *sp++;
|
||||
/* Make sure the ident array stays '\0' padded */
|
||||
memset(dp, 0, (ident+IDENT) - dp + 1);
|
||||
memset(dp, 0, (ident + IDENT) - dp + 1);
|
||||
if (c != EOS)
|
||||
/* word is not unquoted */
|
||||
*ident = '\0';
|
||||
|
@ -1170,7 +1153,7 @@ readhere(struct ioword *iop)
|
|||
/* end of here document marker, what to do? */
|
||||
switch (c) {
|
||||
case /*(*/ ')':
|
||||
if (!subshell_nesting_level)
|
||||
if (!subshell_nesting_type)
|
||||
/*-
|
||||
* not allowed outside $(...) or (...)
|
||||
* => mismatch
|
||||
|
@ -1282,6 +1265,7 @@ getsc_uu(void)
|
|||
break;
|
||||
|
||||
case SSTRING:
|
||||
case SSTRINGCMDLINE:
|
||||
break;
|
||||
|
||||
case SWORDS:
|
||||
|
@ -1369,7 +1353,7 @@ getsc_line(Source *s)
|
|||
{
|
||||
char *xp = Xstring(s->xs, xp), *cp;
|
||||
bool interactive = Flag(FTALKING) && s->type == SSTDIN;
|
||||
int have_tty = interactive && (s->flags & SF_TTY);
|
||||
bool have_tty = tobool(interactive && (s->flags & SF_TTY));
|
||||
|
||||
/* Done here to ensure nothing odd happens when a timeout occurs */
|
||||
XcheckN(s->xs, xp, LINE);
|
||||
|
@ -1382,6 +1366,7 @@ getsc_line(Source *s)
|
|||
}
|
||||
if (interactive)
|
||||
change_winsz();
|
||||
#ifndef MKSH_NO_CMDLINE_EDITING
|
||||
if (have_tty && (
|
||||
#if !MKSH_S_NOVI
|
||||
Flag(FVI) ||
|
||||
|
@ -1395,7 +1380,9 @@ getsc_line(Source *s)
|
|||
nread = 0;
|
||||
xp[nread] = '\0';
|
||||
xp += nread;
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (interactive)
|
||||
pprompt(prompt, 0);
|
||||
else
|
||||
|
@ -1440,25 +1427,6 @@ getsc_line(Source *s)
|
|||
alarm(0);
|
||||
}
|
||||
cp = Xstring(s->xs, xp);
|
||||
#ifndef MKSH_SMALL
|
||||
if (interactive && *cp == '!' && cur_prompt == PS1) {
|
||||
int linelen;
|
||||
|
||||
linelen = Xlength(s->xs, xp);
|
||||
XcheckN(s->xs, xp, Zfc_e_dash + /* NUL */ 1);
|
||||
/* reload after potential realloc */
|
||||
cp = Xstring(s->xs, xp);
|
||||
/* change initial '!' into space */
|
||||
*cp = ' ';
|
||||
/* NUL terminate the current string */
|
||||
*xp = '\0';
|
||||
/* move the actual string forward */
|
||||
memmove(cp + Zfc_e_dash, cp, linelen + /* NUL */ 1);
|
||||
xp += Zfc_e_dash;
|
||||
/* prepend it with "fc -e -" */
|
||||
memcpy(cp, Tfc_e_dash, Zfc_e_dash);
|
||||
}
|
||||
#endif
|
||||
s->start = s->str = cp;
|
||||
strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
|
||||
/* Note: if input is all nulls, this is not eof */
|
||||
|
@ -1514,7 +1482,7 @@ set_prompt(int to, Source *s)
|
|||
ps1 = shf_sclose(shf);
|
||||
saved_atemp = ATEMP;
|
||||
newenv(E_ERRH);
|
||||
if (sigsetjmp(e->jbuf, 0)) {
|
||||
if (kshsetjmp(e->jbuf)) {
|
||||
prompt = safe_prompt;
|
||||
/*
|
||||
* Don't print an error - assume it has already
|
||||
|
@ -1540,7 +1508,8 @@ set_prompt(int to, Source *s)
|
|||
static int
|
||||
dopprompt(const char *cp, int ntruncate, bool doprint)
|
||||
{
|
||||
int columns = 0, lines = 0, indelimit = 0;
|
||||
int columns = 0, lines = 0;
|
||||
bool indelimit = false;
|
||||
char delimiter = 0;
|
||||
|
||||
/*
|
||||
|
@ -1633,7 +1602,7 @@ get_brace_var(XString *wsp, char *wp)
|
|||
|
||||
c2 = getsc();
|
||||
ungetsc(c2);
|
||||
if (c2 != '}') {
|
||||
if (c2 != /*{*/ '}') {
|
||||
ungetsc(c);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1691,7 +1660,7 @@ arraysub(char **strp)
|
|||
XString ws;
|
||||
char *wp, c;
|
||||
/* we are just past the initial [ */
|
||||
int depth = 1;
|
||||
unsigned int depth = 1;
|
||||
|
||||
Xinit(ws, wp, 32, ATEMP);
|
||||
|
||||
|
@ -1727,10 +1696,10 @@ ungetsc(int c)
|
|||
rp->xp--;
|
||||
rp = rp->next;
|
||||
}
|
||||
ungetsc_(c);
|
||||
ungetsc_i(c);
|
||||
}
|
||||
static void
|
||||
ungetsc_(int c)
|
||||
ungetsc_i(int c)
|
||||
{
|
||||
if (source->str > source->start)
|
||||
source->str--;
|
||||
|
@ -1768,7 +1737,7 @@ getsc_bn(void)
|
|||
if ((c2 = o_getsc_u()) == '\n')
|
||||
/* ignore the \newline; get the next char... */
|
||||
continue;
|
||||
ungetsc_(c2);
|
||||
ungetsc_i(c2);
|
||||
backslash_skip = 1;
|
||||
}
|
||||
return (c);
|
||||
|
@ -1781,25 +1750,25 @@ yyskiputf8bom(void)
|
|||
int c;
|
||||
|
||||
if ((unsigned char)(c = o_getsc_u()) != 0xEF) {
|
||||
ungetsc_(c);
|
||||
ungetsc_i(c);
|
||||
return;
|
||||
}
|
||||
if ((unsigned char)(c = o_getsc_u()) != 0xBB) {
|
||||
ungetsc_(c);
|
||||
ungetsc_(0xEF);
|
||||
ungetsc_i(c);
|
||||
ungetsc_i(0xEF);
|
||||
return;
|
||||
}
|
||||
if ((unsigned char)(c = o_getsc_u()) != 0xBF) {
|
||||
ungetsc_(c);
|
||||
ungetsc_(0xBB);
|
||||
ungetsc_(0xEF);
|
||||
ungetsc_i(c);
|
||||
ungetsc_i(0xBB);
|
||||
ungetsc_i(0xEF);
|
||||
return;
|
||||
}
|
||||
UTFMODE |= 8;
|
||||
}
|
||||
|
||||
static Lex_state *
|
||||
push_state_(State_info *si, Lex_state *old_end)
|
||||
push_state_i(State_info *si, Lex_state *old_end)
|
||||
{
|
||||
Lex_state *news = alloc2(STATE_BSIZE, sizeof(Lex_state), ATEMP);
|
||||
|
||||
|
@ -1810,7 +1779,7 @@ push_state_(State_info *si, Lex_state *old_end)
|
|||
}
|
||||
|
||||
static Lex_state *
|
||||
pop_state_(State_info *si, Lex_state *old_end)
|
||||
pop_state_i(State_info *si, Lex_state *old_end)
|
||||
{
|
||||
Lex_state *old_base = si->base;
|
||||
|
||||
|
|
649
src/main.c
649
src/main.c
File diff suppressed because it is too large
Load diff
366
src/misc.c
366
src/misc.c
|
@ -2,7 +2,8 @@
|
|||
/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -29,7 +30,20 @@
|
|||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.172 2011/09/07 15:24:18 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.205 2012/12/17 23:18:08 tg Exp $");
|
||||
|
||||
#define KSH_CHVT_FLAG
|
||||
#ifdef MKSH_SMALL
|
||||
#undef KSH_CHVT_FLAG
|
||||
#endif
|
||||
#ifdef TIOCSCTTY
|
||||
#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];
|
||||
|
@ -38,8 +52,8 @@ static const unsigned char *pat_scan(const unsigned char *,
|
|||
const unsigned char *, bool);
|
||||
static int do_gmatch(const unsigned char *, const unsigned char *,
|
||||
const unsigned char *, const unsigned char *);
|
||||
static const unsigned char *cclass(const unsigned char *, int);
|
||||
#ifdef TIOCSCTTY
|
||||
static const unsigned char *cclass(const unsigned char *, unsigned char);
|
||||
#ifdef KSH_CHVT_CODE
|
||||
static void chvt(const char *);
|
||||
#endif
|
||||
|
||||
|
@ -87,16 +101,16 @@ initctypes(void)
|
|||
chtypes['_'] |= C_ALPHA;
|
||||
setctypes("0123456789", C_DIGIT);
|
||||
/* \0 added automatically */
|
||||
setctypes(" \t\n|&;<>()", C_LEX1);
|
||||
setctypes(TC_LEX1, C_LEX1);
|
||||
setctypes("*@#!$-?", C_VAR1);
|
||||
setctypes(" \t\n", C_IFSWS);
|
||||
setctypes(TC_IFSWS, C_IFSWS);
|
||||
setctypes("=-+?", C_SUBOP1);
|
||||
setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE);
|
||||
}
|
||||
|
||||
/* called from XcheckN() to grow buffer */
|
||||
char *
|
||||
Xcheck_grow_(XString *xsp, const char *xp, size_t more)
|
||||
Xcheck_grow(XString *xsp, const char *xp, size_t more)
|
||||
{
|
||||
const char *old_beg = xsp->beg;
|
||||
|
||||
|
@ -141,12 +155,12 @@ struct options_info {
|
|||
int opts[NELEM(options)];
|
||||
};
|
||||
|
||||
static char *options_fmt_entry(char *, size_t, int, const void *);
|
||||
static char *options_fmt_entry(char *, size_t, unsigned int, const void *);
|
||||
static void printoptions(bool);
|
||||
|
||||
/* format a single select menu item */
|
||||
static char *
|
||||
options_fmt_entry(char *buf, size_t buflen, int i, const void *arg)
|
||||
options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
|
||||
{
|
||||
const struct options_info *oi = (const struct options_info *)arg;
|
||||
|
||||
|
@ -162,7 +176,7 @@ printoptions(bool verbose)
|
|||
size_t i = 0;
|
||||
|
||||
if (verbose) {
|
||||
ssize_t n = 0, len, octs = 0;
|
||||
size_t n = 0, len, octs = 0;
|
||||
struct options_info oi;
|
||||
|
||||
/* verbose version */
|
||||
|
@ -176,8 +190,8 @@ printoptions(bool verbose)
|
|||
if (len > octs)
|
||||
octs = len;
|
||||
len = utf_mbswidth(options[i].name);
|
||||
if (len > oi.opt_width)
|
||||
oi.opt_width = len;
|
||||
if ((int)len > oi.opt_width)
|
||||
oi.opt_width = (int)len;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
@ -212,19 +226,20 @@ getoptions(void)
|
|||
|
||||
/* change a Flag(*) value; takes care of special actions */
|
||||
void
|
||||
change_flag(enum sh_flag f, int what, unsigned int newval)
|
||||
change_flag(enum sh_flag f, int what, bool newset)
|
||||
{
|
||||
unsigned char oldval;
|
||||
unsigned char newval;
|
||||
|
||||
oldval = Flag(f);
|
||||
/* needed for tristates */
|
||||
Flag(f) = newval ? 1 : 0;
|
||||
Flag(f) = newval = (newset ? 1 : 0);
|
||||
#ifndef MKSH_UNEMPLOYED
|
||||
if (f == FMONITOR) {
|
||||
if (what != OF_CMDLINE && newval != oldval)
|
||||
j_change();
|
||||
} else
|
||||
#endif
|
||||
#ifndef MKSH_NO_CMDLINE_EDITING
|
||||
if ((
|
||||
#if !MKSH_S_NOVI
|
||||
f == FVI ||
|
||||
|
@ -234,8 +249,10 @@ change_flag(enum sh_flag f, int what, unsigned int newval)
|
|||
Flag(FVI) =
|
||||
#endif
|
||||
Flag(FEMACS) = Flag(FGMACS) = 0;
|
||||
Flag(f) = (unsigned char)newval;
|
||||
} else if (f == FPRIVILEGED && oldval && !newval) {
|
||||
Flag(f) = newval;
|
||||
} else
|
||||
#endif
|
||||
if (f == FPRIVILEGED && oldval && !newval) {
|
||||
/* Turning off -p? */
|
||||
|
||||
/*XXX this can probably be optimised */
|
||||
|
@ -249,19 +266,24 @@ change_flag(enum sh_flag f, int what, unsigned int newval)
|
|||
DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
|
||||
#else
|
||||
/* seteuid, setegid, setgid don't EAGAIN on Linux */
|
||||
seteuid(ksheuid = kshuid = getuid());
|
||||
ksheuid = kshuid = getuid();
|
||||
#ifndef MKSH__NO_SETEUGID
|
||||
seteuid(ksheuid);
|
||||
#endif
|
||||
DO_SETUID(setuid, (ksheuid));
|
||||
#ifndef MKSH__NO_SETEUGID
|
||||
setegid(kshegid);
|
||||
#endif
|
||||
setgid(kshegid);
|
||||
#endif
|
||||
} else if ((f == FPOSIX || f == FSH) && newval) {
|
||||
Flag(FPOSIX) = Flag(FSH) = Flag(FBRACEEXPAND) = 0;
|
||||
Flag(f) = (unsigned char)newval;
|
||||
Flag(f) = newval;
|
||||
}
|
||||
/* Changing interactive flag? */
|
||||
if (f == FTALKING) {
|
||||
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
|
||||
Flag(FTALKING_I) = (unsigned char)newval;
|
||||
Flag(FTALKING_I) = newval;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +299,8 @@ parse_args(const char **argv,
|
|||
{
|
||||
static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */
|
||||
static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */
|
||||
char set, *opts;
|
||||
bool set;
|
||||
char *opts;
|
||||
const char *array = NULL;
|
||||
Getopt go;
|
||||
size_t i;
|
||||
|
@ -291,7 +314,7 @@ parse_args(const char **argv,
|
|||
/* see cmd_opts[] declaration */
|
||||
*p++ = 'o';
|
||||
*p++ = ':';
|
||||
#if !defined(MKSH_SMALL) || defined(TIOCSCTTY)
|
||||
#ifdef KSH_CHVT_FLAG
|
||||
*p++ = 'T';
|
||||
*p++ = ':';
|
||||
#endif
|
||||
|
@ -332,7 +355,7 @@ parse_args(const char **argv,
|
|||
opts = set_opts;
|
||||
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
|
||||
while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
|
||||
set = (go.info & GI_PLUS) ? 0 : 1;
|
||||
set = tobool(!(go.info & GI_PLUS));
|
||||
switch (optc) {
|
||||
case 'A':
|
||||
if (what == OF_FIRSTTIME)
|
||||
|
@ -356,7 +379,7 @@ parse_args(const char **argv,
|
|||
break;
|
||||
}
|
||||
i = option(go.optarg);
|
||||
if ((i != (size_t)-1) && set == Flag(i))
|
||||
if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
|
||||
/*
|
||||
* Don't check the context if the flag
|
||||
* isn't changing - makes "set -o interactive"
|
||||
|
@ -372,14 +395,14 @@ parse_args(const char **argv,
|
|||
}
|
||||
break;
|
||||
|
||||
#if !defined(MKSH_SMALL) || defined(TIOCSCTTY)
|
||||
#ifdef KSH_CHVT_FLAG
|
||||
case 'T':
|
||||
if (what != OF_FIRSTTIME)
|
||||
break;
|
||||
#ifndef TIOCSCTTY
|
||||
#ifndef KSH_CHVT_CODE
|
||||
errorf("no TIOCSCTTY ioctl");
|
||||
#else
|
||||
change_flag(FTALKING, OF_CMDLINE, 1);
|
||||
change_flag(FTALKING, OF_CMDLINE, true);
|
||||
chvt(go.optarg);
|
||||
break;
|
||||
#endif
|
||||
|
@ -423,6 +446,7 @@ parse_args(const char **argv,
|
|||
if (arrayset) {
|
||||
const char *ccp = NULL;
|
||||
|
||||
mkssert(array != NULL);
|
||||
if (*array)
|
||||
ccp = skip_varname(array, false);
|
||||
if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
|
||||
|
@ -447,45 +471,40 @@ parse_args(const char **argv,
|
|||
int
|
||||
getn(const char *s, int *ai)
|
||||
{
|
||||
int i, c, rv = 0;
|
||||
char c;
|
||||
unsigned int i = 0;
|
||||
bool neg = false;
|
||||
|
||||
do {
|
||||
c = *s++;
|
||||
} while (ksh_isspace(c));
|
||||
if (c == '-') {
|
||||
|
||||
switch (c) {
|
||||
case '-':
|
||||
neg = true;
|
||||
/* FALLTHROUGH */
|
||||
case '+':
|
||||
c = *s++;
|
||||
} else if (c == '+')
|
||||
c = *s++;
|
||||
*ai = i = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!ksh_isdigit(c))
|
||||
goto getn_out;
|
||||
i *= 10;
|
||||
if (i < *ai)
|
||||
/* overflow */
|
||||
goto getn_out;
|
||||
i += c - '0';
|
||||
*ai = i;
|
||||
/* not numeric */
|
||||
return (0);
|
||||
if (i > 214748364U)
|
||||
/* overflow on multiplication */
|
||||
return (0);
|
||||
i = i * 10U + (unsigned int)(c - '0');
|
||||
/* now: i <= 2147483649U */
|
||||
} while ((c = *s++));
|
||||
rv = 1;
|
||||
|
||||
getn_out:
|
||||
if (neg)
|
||||
*ai = -*ai;
|
||||
return (rv);
|
||||
}
|
||||
if (i > (neg ? 2147483648U : 2147483647U))
|
||||
/* overflow for signed 32-bit int */
|
||||
return (0);
|
||||
|
||||
/* getn() that prints error */
|
||||
int
|
||||
bi_getn(const char *as, int *ai)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (!(rv = getn(as, ai)))
|
||||
bi_errorf("%s: %s", as, "bad number");
|
||||
return (rv);
|
||||
*ai = neg ? -(int)i : (int)i;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -644,7 +663,7 @@ has_globbing(const char *xp, const char *xpe)
|
|||
if (!in_bracket) {
|
||||
saw_glob = true;
|
||||
in_bracket = true;
|
||||
if (ISMAGIC(p[1]) && p[2] == NOT)
|
||||
if (ISMAGIC(p[1]) && p[2] == '!')
|
||||
p += 2;
|
||||
if (ISMAGIC(p[1]) && p[2] == ']')
|
||||
p += 2;
|
||||
|
@ -688,7 +707,7 @@ static int
|
|||
do_gmatch(const unsigned char *s, const unsigned char *se,
|
||||
const unsigned char *p, const unsigned char *pe)
|
||||
{
|
||||
int sc, pc;
|
||||
unsigned char sc, pc;
|
||||
const unsigned char *prest, *psub, *pnext;
|
||||
const unsigned char *srest;
|
||||
|
||||
|
@ -818,12 +837,13 @@ do_gmatch(const unsigned char *s, const unsigned char *se,
|
|||
}
|
||||
|
||||
static const unsigned char *
|
||||
cclass(const unsigned char *p, int sub)
|
||||
cclass(const unsigned char *p, unsigned char sub)
|
||||
{
|
||||
int c, d, notp, found = 0;
|
||||
unsigned char c, d;
|
||||
bool notp, found = false;
|
||||
const unsigned char *orig_p = p;
|
||||
|
||||
if ((notp = (ISMAGIC(*p) && *++p == NOT)))
|
||||
if ((notp = tobool(ISMAGIC(*p) && *++p == '!')))
|
||||
p++;
|
||||
do {
|
||||
c = *p++;
|
||||
|
@ -857,7 +877,7 @@ cclass(const unsigned char *p, int sub)
|
|||
} else
|
||||
d = c;
|
||||
if (c == sub || (c <= sub && sub <= d))
|
||||
found = 1;
|
||||
found = true;
|
||||
} while (!(ISMAGIC(p[0]) && p[1] == ']'));
|
||||
|
||||
return ((found != notp) ? p+2 : NULL);
|
||||
|
@ -1030,41 +1050,116 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
|
|||
* No trailing newline is printed.
|
||||
*/
|
||||
void
|
||||
print_value_quoted(const char *s)
|
||||
print_value_quoted(struct shf *shf, const char *s)
|
||||
{
|
||||
const char *p;
|
||||
bool inquote = false;
|
||||
unsigned char c;
|
||||
const unsigned char *p = (const unsigned char *)s;
|
||||
bool inquote = true;
|
||||
|
||||
/* first, check whether any quotes are needed */
|
||||
for (p = s; *p; p++)
|
||||
if (ctype(*p, C_QUOTE))
|
||||
break;
|
||||
if (!*p) {
|
||||
/* nope, use the shortcut */
|
||||
shf_puts(s, shl_stdout);
|
||||
return;
|
||||
}
|
||||
while ((c = *p++) >= 32)
|
||||
if (ctype(c, C_QUOTE))
|
||||
inquote = false;
|
||||
|
||||
/* quote via state machine */
|
||||
for (p = s; *p; p++) {
|
||||
if (*p == '\'') {
|
||||
/*
|
||||
* multiple '''s or any ' at beginning of string
|
||||
* look nicer this way than when simply substituting
|
||||
*/
|
||||
if (inquote) {
|
||||
shf_putc('\'', shl_stdout);
|
||||
inquote = false;
|
||||
}
|
||||
shf_putc('\\', shl_stdout);
|
||||
} else if (!inquote) {
|
||||
shf_putc('\'', shl_stdout);
|
||||
inquote = true;
|
||||
p = (const unsigned char *)s;
|
||||
if (c == 0) {
|
||||
if (inquote) {
|
||||
/* nope, use the shortcut */
|
||||
shf_puts(s, shf);
|
||||
return;
|
||||
}
|
||||
shf_putc(*p, shl_stdout);
|
||||
|
||||
/* otherwise, quote nicely via state machine */
|
||||
while ((c = *p++) != 0) {
|
||||
if (c == '\'') {
|
||||
/*
|
||||
* multiple single quotes or any of them
|
||||
* at the beginning of a string look nicer
|
||||
* this way than when simply substituting
|
||||
*/
|
||||
if (inquote) {
|
||||
shf_putc('\'', shf);
|
||||
inquote = false;
|
||||
}
|
||||
shf_putc('\\', shf);
|
||||
} else if (!inquote) {
|
||||
shf_putc('\'', shf);
|
||||
inquote = true;
|
||||
}
|
||||
shf_putc(c, shf);
|
||||
}
|
||||
} else {
|
||||
unsigned int wc;
|
||||
size_t n;
|
||||
|
||||
/* use $'...' quote format */
|
||||
shf_putc('$', shf);
|
||||
shf_putc('\'', shf);
|
||||
while ((c = *p) != 0) {
|
||||
if (c >= 0xC2) {
|
||||
n = utf_mbtowc(&wc, (const char *)p);
|
||||
if (n != (size_t)-1) {
|
||||
p += n;
|
||||
shf_fprintf(shf, "\\u%04X", wc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++p;
|
||||
switch (c) {
|
||||
/* see unbksl() in this file for comments */
|
||||
case 7:
|
||||
c = 'a';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case '\b':
|
||||
c = 'b';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case '\f':
|
||||
c = 'f';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case '\n':
|
||||
c = 'n';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case '\r':
|
||||
c = 'r';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case '\t':
|
||||
c = 't';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case 11:
|
||||
c = 'v';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case '\033':
|
||||
/* take E not e because \e is \ in *roff */
|
||||
c = 'E';
|
||||
/* FALLTHROUGH */
|
||||
case '\\':
|
||||
shf_putc('\\', shf);
|
||||
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
if (c < 32 || c > 0x7E) {
|
||||
/* FALLTHROUGH */
|
||||
case '\'':
|
||||
shf_fprintf(shf, "\\%03o", c);
|
||||
break;
|
||||
}
|
||||
|
||||
shf_putc(c, shf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inquote = true;
|
||||
}
|
||||
if (inquote)
|
||||
shf_putc('\'', shl_stdout);
|
||||
shf_putc('\'', shf);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1072,36 +1167,35 @@ print_value_quoted(const char *s)
|
|||
* the i-th element
|
||||
*/
|
||||
void
|
||||
print_columns(struct shf *shf, int n,
|
||||
char *(*func)(char *, size_t, int, const void *),
|
||||
print_columns(struct shf *shf, unsigned int n,
|
||||
char *(*func)(char *, size_t, unsigned int, const void *),
|
||||
const void *arg, size_t max_oct, size_t max_colz, bool prefcol)
|
||||
{
|
||||
int i, r, c, rows, cols, nspace, max_col;
|
||||
unsigned int i, r, c, rows, cols, nspace, max_col;
|
||||
char *str;
|
||||
|
||||
if (n <= 0) {
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
if (max_colz > 2147483646) {
|
||||
#ifndef MKSH_SMALL
|
||||
internal_warningf("print_columns called with n=%d <= 0", n);
|
||||
internal_warningf("print_columns called with %s=%zu >= INT_MAX",
|
||||
"max_col", max_colz);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
max_col = (unsigned int)max_colz;
|
||||
|
||||
if (max_colz > 2147483647) {
|
||||
if (max_oct > 2147483646) {
|
||||
#ifndef MKSH_SMALL
|
||||
internal_warningf("print_columns called with max_col=%zu > INT_MAX",
|
||||
max_colz);
|
||||
internal_warningf("print_columns called with %s=%zu >= INT_MAX",
|
||||
"max_oct", max_oct);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
max_col = (int)max_colz;
|
||||
|
||||
++max_oct;
|
||||
str = alloc(max_oct, ATEMP);
|
||||
|
||||
/* ensure x_cols is valid first */
|
||||
if (x_cols < MIN_COLS)
|
||||
change_winsz();
|
||||
|
||||
/*
|
||||
* We use (max_col + 1) to consider the space separator.
|
||||
* Note that no space is printed after the last column
|
||||
|
@ -1219,7 +1313,7 @@ reset_nonblock(int fd)
|
|||
char *
|
||||
ksh_get_wd(void)
|
||||
{
|
||||
#ifdef NO_PATH_MAX
|
||||
#ifdef MKSH__NO_PATH_MAX
|
||||
char *rv, *cp;
|
||||
|
||||
if ((cp = get_current_dir_name())) {
|
||||
|
@ -1239,6 +1333,10 @@ ksh_get_wd(void)
|
|||
return (rv);
|
||||
}
|
||||
|
||||
#ifndef ELOOP
|
||||
#define ELOOP E2BIG
|
||||
#endif
|
||||
|
||||
char *
|
||||
do_realpath(const char *upath)
|
||||
{
|
||||
|
@ -1248,7 +1346,7 @@ do_realpath(const char *upath)
|
|||
size_t len;
|
||||
int llen;
|
||||
struct stat sb;
|
||||
#ifdef NO_PATH_MAX
|
||||
#ifdef MKSH__NO_PATH_MAX
|
||||
size_t ldestlen = 0;
|
||||
#define pathlen sb.st_size
|
||||
#define pathcnd (ldestlen < (pathlen + 1))
|
||||
|
@ -1316,7 +1414,7 @@ do_realpath(const char *upath)
|
|||
*xp = '\0';
|
||||
|
||||
/* lstat the current output, see if it's a symlink */
|
||||
if (lstat(Xstring(xs, xp), &sb)) {
|
||||
if (mksh_lstat(Xstring(xs, xp), &sb)) {
|
||||
/* lstat failed */
|
||||
if (errno == ENOENT) {
|
||||
/* because the pathname does not exist */
|
||||
|
@ -1335,6 +1433,7 @@ do_realpath(const char *upath)
|
|||
|
||||
/* check if we encountered a symlink? */
|
||||
if (S_ISLNK(sb.st_mode)) {
|
||||
#ifndef MKSH__NO_SYMLINK
|
||||
/* reached maximum recursion depth? */
|
||||
if (!symlinks--) {
|
||||
/* yep, prevent infinite loops */
|
||||
|
@ -1344,7 +1443,7 @@ do_realpath(const char *upath)
|
|||
|
||||
/* get symlink(7) target */
|
||||
if (pathcnd) {
|
||||
#ifdef NO_PATH_MAX
|
||||
#ifdef MKSH__NO_PATH_MAX
|
||||
if (notoktoadd(pathlen, 1)) {
|
||||
errno = ENAMETOOLONG;
|
||||
goto notfound;
|
||||
|
@ -1369,7 +1468,9 @@ do_realpath(const char *upath)
|
|||
if (ldest[0] != '/') {
|
||||
/* symlink target is a relative path */
|
||||
xp = Xrestpos(xs, xp, pos);
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* symlink target is an absolute path */
|
||||
xp = Xstring(xs, xp);
|
||||
beginning_of_a_pathname:
|
||||
|
@ -1730,7 +1831,7 @@ c_cd(const char **wp)
|
|||
return (2);
|
||||
}
|
||||
|
||||
#ifdef NO_PATH_MAX
|
||||
#ifdef MKSH__NO_PATH_MAX
|
||||
/* only a first guess; make_path will enlarge xs if necessary */
|
||||
XinitN(xs, 1024, ATEMP);
|
||||
#else
|
||||
|
@ -1752,7 +1853,7 @@ c_cd(const char **wp)
|
|||
if (cdnode)
|
||||
bi_errorf("%s: %s", dir, "bad directory");
|
||||
else
|
||||
bi_errorf("%s: %s", tryp, strerror(errno));
|
||||
bi_errorf("%s: %s", tryp, cstrerror(errno));
|
||||
afree(allocd, ATEMP);
|
||||
Xfree(xs, xp);
|
||||
return (2);
|
||||
|
@ -1809,7 +1910,7 @@ c_cd(const char **wp)
|
|||
}
|
||||
|
||||
|
||||
#ifdef TIOCSCTTY
|
||||
#ifdef KSH_CHVT_CODE
|
||||
extern void chvt_reinit(void);
|
||||
|
||||
static void
|
||||
|
@ -1847,9 +1948,9 @@ chvt(const char *fn)
|
|||
"new shell is potentially insecure, can't revoke",
|
||||
fn);
|
||||
}
|
||||
if ((fd = open(fn, O_RDWR)) == -1) {
|
||||
if ((fd = open(fn, O_RDWR)) < 0) {
|
||||
sleep(1);
|
||||
if ((fd = open(fn, O_RDWR)) == -1)
|
||||
if ((fd = open(fn, O_RDWR)) < 0)
|
||||
errorf("%s: %s %s", "chvt", "can't open", fn);
|
||||
}
|
||||
switch (fork()) {
|
||||
|
@ -1886,19 +1987,6 @@ chvt(const char *fn)
|
|||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define assert_eq(name, a, b) char name[a == b ? 1 : -1]
|
||||
#define assert_ge(name, a, b) char name[a >= b ? 1 : -1]
|
||||
assert_ge(intsize_is_okay, sizeof(int), 4);
|
||||
assert_eq(intsizes_are_okay, sizeof(int), sizeof(unsigned int));
|
||||
assert_ge(longsize_is_okay, sizeof(long), sizeof(int));
|
||||
assert_eq(arisize_is_okay, sizeof(mksh_ari_t), 4);
|
||||
assert_eq(uarisize_is_okay, sizeof(mksh_uari_t), 4);
|
||||
assert_eq(sizesizes_are_okay, sizeof(size_t), sizeof(ssize_t));
|
||||
assert_eq(ptrsizes_are_okay, sizeof(ptrdiff_t), sizeof(void *));
|
||||
assert_eq(ptrsize_is_sizet, sizeof(ptrdiff_t), sizeof(size_t));
|
||||
/* formatting routines assume this */
|
||||
assert_ge(ptr_fits_in_long, sizeof(long), sizeof(size_t));
|
||||
|
||||
char *
|
||||
strchr(char *p, int ch)
|
||||
{
|
||||
|
@ -1930,29 +2018,9 @@ strstr(char *b, const char *l)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !HAVE_STRCASESTR
|
||||
const char *
|
||||
stristr(const char *b, const char *l)
|
||||
{
|
||||
char first, c;
|
||||
size_t n;
|
||||
|
||||
if ((first = *l++), ((first = ksh_tolower(first)) == '\0'))
|
||||
return (b);
|
||||
n = strlen(l);
|
||||
stristr_look:
|
||||
while ((c = *b++), ((c = ksh_tolower(c)) != first))
|
||||
if (c == '\0')
|
||||
return (NULL);
|
||||
if (strncasecmp(b, l, n))
|
||||
goto stristr_look;
|
||||
return (b - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MKSH_SMALL
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
char *
|
||||
strndup_(const char *src, size_t len, Area *ap)
|
||||
strndup_i(const char *src, size_t len, Area *ap)
|
||||
{
|
||||
char *dst = NULL;
|
||||
|
||||
|
@ -1965,9 +2033,9 @@ strndup_(const char *src, size_t len, Area *ap)
|
|||
}
|
||||
|
||||
char *
|
||||
strdup_(const char *src, Area *ap)
|
||||
strdup_i(const char *src, Area *ap)
|
||||
{
|
||||
return (src == NULL ? NULL : strndup_(src, strlen(src), ap));
|
||||
return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
237
src/mksh.1
237
src/mksh.1
|
@ -1,8 +1,8 @@
|
|||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.275 2011/10/07 19:51:29 tg Exp $
|
||||
.\" $OpenBSD: ksh.1,v 1.141 2011/09/03 22:59:08 jmc Exp $
|
||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.305 2013/02/19 18:45:20 tg Exp $
|
||||
.\" $OpenBSD: ksh.1,v 1.145 2013/01/17 21:20:25 jmc Exp $
|
||||
.\"-
|
||||
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
.\" 2010, 2011
|
||||
.\" 2010, 2011, 2012, 2013
|
||||
.\" Thorsten Glaser <tg@mirbsd.org>
|
||||
.\"
|
||||
.\" Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -28,6 +28,8 @@
|
|||
.\" * ~ is size-reduced and placed atop in groff, so use \*(TI
|
||||
.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
|
||||
.\" * \(en does not work in nroff, so use \*(en
|
||||
.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
|
||||
.\" Also make sure to use \& especially with two-letter words.
|
||||
.\" The section after the "doc" macropackage has been loaded contains
|
||||
.\" additional code to convene between the UCB mdoc macropackage (and
|
||||
.\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
|
||||
|
@ -72,11 +74,13 @@
|
|||
.\" with -mandoc, it might implement .Mx itself, but we want to
|
||||
.\" use our own definition. And .Dd must come *first*, always.
|
||||
.\"
|
||||
.Dd $Mdocdate: October 7 2011 $
|
||||
.Dd $Mdocdate: February 19 2013 $
|
||||
.\"
|
||||
.\" Check which macro package we use
|
||||
.\" Check which macro package we use, and do other -mdoc setup.
|
||||
.\"
|
||||
.ie \n(.g \{\
|
||||
. if \*[.T]utf8 .tr \[la]\*(Lt
|
||||
. if \*[.T]utf8 .tr \[ra]\*(Gt
|
||||
. ie d volume-ds-1 .ds tT gnu
|
||||
. el .ds tT bsd
|
||||
.\}
|
||||
|
@ -153,7 +157,7 @@
|
|||
.Nm
|
||||
.Bk -words
|
||||
.Op Fl +abCefhiklmnprUuvXx
|
||||
.Op Fl T Ar /dev/ttyCn | \-
|
||||
.Op Fl T Ar /dev/ttyCn \*(Ba \-
|
||||
.Op Fl +o Ar option
|
||||
.Oo
|
||||
.Fl c Ar string \*(Ba
|
||||
|
@ -171,7 +175,24 @@ script use.
|
|||
Its command language is a superset of the
|
||||
.Xr sh C
|
||||
shell language and largely compatible to the original Korn shell.
|
||||
.Pp
|
||||
.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 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.
|
||||
.Pp
|
||||
|
@ -923,7 +944,7 @@ This even works indirectly:
|
|||
.Bd -literal -offset indent
|
||||
$ bar=foobar; baz=\*(aqf*r\*(aq
|
||||
$ [[ $bar = $baz ]]; echo $?
|
||||
$ [[ $bar = "$baz" ]]; echo $?
|
||||
$ [[ $bar = \&"$baz" ]]; echo $?
|
||||
.Ed
|
||||
.Pp
|
||||
Perhaps surprisingly, the first comparison succeeds,
|
||||
|
@ -1064,6 +1085,12 @@ space or tab, the following word is also checked for alias expansion.
|
|||
The alias expansion process stops when a word that is not an alias is found,
|
||||
when a quoted word is found, or when an alias word that is currently being
|
||||
expanded is found.
|
||||
Aliases are specifically an interactive feature: while they do happen
|
||||
to work in scripts and on the command line in some cases, aliases are
|
||||
expanded during lexing, so their use must be in a separate command tree
|
||||
from their definition; otherwise, the alias will not be found.
|
||||
Noticeably, command lists (separated by semicolon, in command substitutions
|
||||
also by newline) may be one same parse tree.
|
||||
.Pp
|
||||
The following command aliases are defined automatically by the shell:
|
||||
.Bd -literal -offset indent
|
||||
|
@ -1135,9 +1162,23 @@ or
|
|||
command substitutions take the form
|
||||
.Pf $( Ns Ar command Ns \&)
|
||||
or (deprecated)
|
||||
.Pf \` Ns Ar command Ns \` ;
|
||||
.Pf \` Ns Ar command Ns \`
|
||||
or (executed in the current environment)
|
||||
.Pf ${\ \& Ar command Ns \&;}
|
||||
and strip trailing newlines;
|
||||
and arithmetic substitutions take the form
|
||||
.Pf $(( Ns Ar expression Ns )) .
|
||||
Parsing the current-environment command substitution requires a space,
|
||||
tab or newline after the opening brace and that the closing brace be
|
||||
recognised as a keyword (i.e. is preceded by a newline or semicolon).
|
||||
They are also called funsubs (function substitutions) and behave like
|
||||
functions in that
|
||||
.Ic local
|
||||
and
|
||||
.Ic return
|
||||
work, and in that
|
||||
.Ic exit
|
||||
terminates the parent shell.
|
||||
.Pp
|
||||
If a substitution appears outside of double quotes, the results of the
|
||||
substitution are generally subject to word or field splitting according to
|
||||
|
@ -1215,6 +1256,8 @@ A command substitution is replaced by the output generated by the specified
|
|||
command which is run in a subshell.
|
||||
For
|
||||
.Pf $( Ns Ar command Ns \&)
|
||||
and
|
||||
.Pf ${\ \& Ar command Ns \&;}
|
||||
substitutions, normal quoting rules are used when
|
||||
.Ar command
|
||||
is parsed; however, for the deprecated
|
||||
|
@ -1565,6 +1608,7 @@ begins with
|
|||
it is anchored at the beginning of the value; if it begins with
|
||||
.Ql % ,
|
||||
it is anchored at the end.
|
||||
Patterns that are empty or consist only of wildcards are invalid.
|
||||
A single
|
||||
.Ql /
|
||||
replaces the first occurence of the search
|
||||
|
@ -1576,7 +1620,7 @@ is omitted, the
|
|||
.Ar pattern
|
||||
is replaced by the empty string, i.e. deleted.
|
||||
Cannot be applied to a vector.
|
||||
Inefficiently implemented.
|
||||
Inefficiently implemented, may be slow.
|
||||
.Pp
|
||||
.Sm off
|
||||
.It Xo
|
||||
|
@ -1614,12 +1658,22 @@ Currently,
|
|||
must start with a space, opening parenthesis or digit to be recognised.
|
||||
Cannot be applied to a vector.
|
||||
.Pp
|
||||
.It Pf ${ Ns Ar name Ns @#}
|
||||
.It Xo
|
||||
.Pf ${ Ar name
|
||||
.Pf @# Ns Oo Ar seed Oc Ns }
|
||||
.Xc
|
||||
The internal hash of the expansion of
|
||||
.Ar name .
|
||||
At the moment, this is NZAT (a never-zero 32-bit hash based on
|
||||
.Ar name ,
|
||||
with an optional (defaulting to zero)
|
||||
.Op Ar seed .
|
||||
At the moment, this is NZAAT (a 32-bit hash based on
|
||||
Bob Jenkins' one-at-a-time hash), but this is not set.
|
||||
This is the hash the shell uses internally for its associative arrays.
|
||||
.Pp
|
||||
.It Pf ${ Ns Ar name Ns @Q}
|
||||
A quoted expression safe for re-entry, whose value is the value of the
|
||||
.Ar name
|
||||
parameter, is substituted.
|
||||
.El
|
||||
.Pp
|
||||
Note that
|
||||
|
@ -1713,6 +1767,8 @@ When an external command is executed by the shell, this parameter is set in the
|
|||
environment of the new process to the path of the executed command.
|
||||
In interactive use, this parameter is also set in the parent shell to the last
|
||||
word of the previous command.
|
||||
.It Ev BASHPID
|
||||
The PID of the shell or subshell.
|
||||
.It Ev CDPATH
|
||||
Search path for the
|
||||
.Ic cd
|
||||
|
@ -1738,7 +1794,7 @@ Set to the number of columns on the terminal or window.
|
|||
Always set, defaults to 80, unless the
|
||||
value as reported by
|
||||
.Xr stty 1
|
||||
is non-zero and sane enough; similar for
|
||||
is non-zero and sane enough (minimum is 12x3); similar for
|
||||
.Ev LINES .
|
||||
This parameter is used by the interactive line editing modes, and by the
|
||||
.Ic select ,
|
||||
|
@ -1746,6 +1802,8 @@ This parameter is used by the interactive line editing modes, and by the
|
|||
and
|
||||
.Ic kill \-l
|
||||
commands to format information columns.
|
||||
Importing from the environment or unsetting this parameter removes the
|
||||
binding to the actual terminal size in favour of the provided value.
|
||||
.It Ev ENV
|
||||
If this parameter is found to be set after any profile files are executed, the
|
||||
expanded value is used as a shell startup file.
|
||||
|
@ -1793,7 +1851,7 @@ This is different from
|
|||
.Nm ksh .
|
||||
.It Ev HISTSIZE
|
||||
The number of commands normally stored for history.
|
||||
The default is 500.
|
||||
The default is 2047.
|
||||
.It Ev HOME
|
||||
The default directory for the
|
||||
.Ic cd
|
||||
|
@ -1833,6 +1891,18 @@ executed.
|
|||
.It Ev LINES
|
||||
Set to the number of lines on the terminal or window.
|
||||
Always set, defaults to 24.
|
||||
See
|
||||
.Ev COLUMNS .
|
||||
.It Ev EPOCHREALTIME
|
||||
Time since the epoch, as returned by
|
||||
.Xr gettimeofday 2 ,
|
||||
formatted as decimal
|
||||
.Va tv_sec
|
||||
followed by a dot
|
||||
.Pq Sq .\&
|
||||
and
|
||||
.Va tv_usec
|
||||
padded to exactly six decimal digits.
|
||||
.It Ev OLDPWD
|
||||
The previous working directory.
|
||||
Unset if
|
||||
|
@ -1943,7 +2013,7 @@ x=$(print \e\e001)
|
|||
PS1="$x$(print \e\er)$x$(tput smso)$x\e$PWD$x$(tput rmso)$x\*(Gt "
|
||||
.Ed
|
||||
.Pp
|
||||
Due to pressure from David G. Korn,
|
||||
Due to a strong suggestion from David G. Korn,
|
||||
.Nm
|
||||
now also supports the following form:
|
||||
.Bd -literal -offset indent
|
||||
|
@ -2191,6 +2261,9 @@ matches no strings; the pattern
|
|||
matches all strings (think about it).
|
||||
.El
|
||||
.Pp
|
||||
Note that complicated globbing, especially with alternatives,
|
||||
is slow; using separate comparisons may (or may not) be faster.
|
||||
.Pp
|
||||
Note that
|
||||
.Nm mksh
|
||||
.Po and Nm pdksh Pc
|
||||
|
@ -2465,14 +2538,8 @@ Grouping operators:
|
|||
( )
|
||||
.Ed
|
||||
.Pp
|
||||
Integer constants and expressions are calculated using the
|
||||
.Vt mksh_ari_t
|
||||
.Po if signed Pc
|
||||
or
|
||||
.Vt mksh_uari_t
|
||||
.Po if unsigned Pc
|
||||
type, and are limited to 32 bits.
|
||||
Overflows wrap silently.
|
||||
Integer constants and expressions are calculated using an exactly 32-bit
|
||||
wide, signed or unsigned, type with silent wraparound on integer overflow.
|
||||
Integer constants may be specified with arbitrary bases using the notation
|
||||
.Ar base Ns # Ns Ar number ,
|
||||
where
|
||||
|
@ -2480,22 +2547,15 @@ where
|
|||
is a decimal integer specifying the base, and
|
||||
.Ar number
|
||||
is a number in the specified base.
|
||||
Additionally,
|
||||
integers may be prefixed with
|
||||
.Sq 0X
|
||||
or
|
||||
Additionally, base-16 integers may be specified by prefixing them with
|
||||
.Sq 0x
|
||||
(specifying base 16), similar to
|
||||
.At
|
||||
.Nm ksh ,
|
||||
or
|
||||
.Sq 0
|
||||
(base 8), as an
|
||||
.Nm
|
||||
extension, in all forms of arithmetic expressions,
|
||||
except as numeric arguments to the
|
||||
.Pq case-insensitive
|
||||
in all forms of arithmetic expressions, except as numeric arguments to the
|
||||
.Ic test
|
||||
command.
|
||||
built-in command.
|
||||
It is discouraged to prefix numbers with a sole zero
|
||||
.Pq Sq 0 ,
|
||||
because some shells may interpret them as base-8 integers.
|
||||
As a special
|
||||
.Nm mksh
|
||||
extension, numbers to the base of one are treated as either (8-bit
|
||||
|
@ -2511,6 +2571,7 @@ instead of
|
|||
.Dq 1#x
|
||||
is also supported.
|
||||
Note that NUL bytes (integral value of zero) cannot be used.
|
||||
An unset or empty parameter evaluates to 0 in integer context.
|
||||
In Unicode mode, raw octets are mapped into the range EF80..EFFF as in
|
||||
OPTU-8, which is in the PUA and has been assigned by CSUR for this use.
|
||||
If more than one octet in ASCII mode, or a sequence of more than one
|
||||
|
@ -2748,7 +2809,8 @@ command can be used inside a function to create a local parameter.
|
|||
Note that
|
||||
.At
|
||||
.Nm ksh93
|
||||
uses static scoping (one global scope, one local scope per function), whereas
|
||||
uses static scoping (one global scope, one local scope per function)
|
||||
and allows local variables only on Korn style functions, whereas
|
||||
.Nm mksh
|
||||
uses dynamic scoping (nested scopes of varying locality).
|
||||
Note that special parameters (e.g.\&
|
||||
|
@ -2792,7 +2854,7 @@ and remove alias definitions upon encounter, while aliases take precedence
|
|||
over Korn-style functions.
|
||||
.El
|
||||
.Pp
|
||||
In the future, the following differences will also be added:
|
||||
In the future, the following differences may also be added:
|
||||
.Bl -bullet
|
||||
.It
|
||||
A separate trap/signal environment will be used during the execution of
|
||||
|
@ -2856,8 +2918,8 @@ regular commands
|
|||
.Pp
|
||||
.Ic \&[ , chdir , bind , cat ,
|
||||
.Ic echo , let , mknod , print ,
|
||||
.Ic printf , pwd , realpath , rename ,
|
||||
.Ic sleep , test , ulimit , whence
|
||||
.Ic pwd , realpath , rename , sleep ,
|
||||
.Ic test , ulimit , whence
|
||||
.Pp
|
||||
In the future, the additional
|
||||
.Nm
|
||||
|
@ -3224,8 +3286,6 @@ defaults to 1.
|
|||
.Em Warning:
|
||||
this utility is not portable; use the Korn shell builtin
|
||||
.Ic print
|
||||
or the much slower POSIX utility
|
||||
.Ic printf
|
||||
instead.
|
||||
.Pp
|
||||
Prints its arguments (separated by spaces) followed by a newline, to the
|
||||
|
@ -3294,12 +3354,13 @@ 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.
|
||||
.Pp
|
||||
If no parameters are specified, the names of all parameters with the export
|
||||
attribute are printed one per line, unless the
|
||||
.Fl p
|
||||
option is used, in which case
|
||||
If no parameters are specified, all parameters with the export attribute
|
||||
set are printed one per line; either their names, or, if a
|
||||
.Ql \-
|
||||
with no option letter is specified, name=value pairs, or, with
|
||||
.Fl p ,
|
||||
.Ic export
|
||||
commands defining all exported parameters, including their values, are printed.
|
||||
commands suitable for re-entry.
|
||||
.Pp
|
||||
.It Ic false
|
||||
A command that exits with a non-zero status.
|
||||
|
@ -3360,11 +3421,8 @@ The meaning of
|
|||
and
|
||||
.Fl s
|
||||
is identical: re-execute the selected command without invoking an editor.
|
||||
This command is usually accessed with the predefined
|
||||
This command is usually accessed with the predefined:
|
||||
.Ic alias r=\*(aqfc \-e \-\*(aq
|
||||
or by prefixing an interactive mode input line with
|
||||
.Sq \&!
|
||||
.Pq wbx extension .
|
||||
.Pp
|
||||
.It Ic fg Op Ar job ...
|
||||
Resume the specified job(s) in the foreground.
|
||||
|
@ -3548,7 +3606,7 @@ The file type may be
|
|||
(character type device),
|
||||
or
|
||||
.Cm p
|
||||
(named pipe).
|
||||
.Pq named pipe , Tn FIFO .
|
||||
The file created may be modified according to its
|
||||
.Ar mode
|
||||
(via the
|
||||
|
@ -3619,15 +3677,16 @@ option suppresses the trailing newline.
|
|||
.Pp
|
||||
.It Ic printf Ar format Op Ar arguments ...
|
||||
Formatted output.
|
||||
Approximately the same as the utility
|
||||
.Ic printf ,
|
||||
except that it uses the same
|
||||
Approximately the same as the
|
||||
.Xr printf 1 ,
|
||||
utility, except it uses the same
|
||||
.Sx Backslash expansion
|
||||
and I/O code as the rest of
|
||||
and I/O code and does hot handle floating point as the rest of
|
||||
.Nm mksh .
|
||||
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.
|
||||
.Pp
|
||||
.It Ic pwd Op Fl LP
|
||||
Print the present working directory.
|
||||
|
@ -3650,7 +3709,7 @@ directories to the root directory) is printed.
|
|||
.Pp
|
||||
.It Xo
|
||||
.Ic read
|
||||
.Op Fl A | Fl a
|
||||
.Op Fl A \*(Ba Fl a
|
||||
.Op Fl d Ar x
|
||||
.Oo Fl N Ar z \*(Ba
|
||||
.Fl n Ar z Oc
|
||||
|
@ -3769,7 +3828,7 @@ option might be prudent; the same applies for:
|
|||
.Bd -literal -offset indent
|
||||
find . \-type f \-print0 \*(Ba \e
|
||||
while IFS= read \-d \*(aq\*(aq \-r filename; do
|
||||
print \-r \-\- "found <${filename#./}>"
|
||||
print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
|
||||
done
|
||||
.Ed
|
||||
.Pp
|
||||
|
@ -4295,7 +4354,8 @@ instead of
|
|||
.Ql xtrace .
|
||||
.It Fl p Ar file
|
||||
.Ar file
|
||||
is a named pipe.
|
||||
is a named pipe
|
||||
.Pq Tn FIFO .
|
||||
.It Fl r Ar file
|
||||
.Ar file
|
||||
exists and is readable.
|
||||
|
@ -4393,22 +4453,22 @@ a mathematical term or the name of an integer variable:
|
|||
x=1; [ "x" \-eq 1 ] evaluates to true
|
||||
.Ed
|
||||
.Pp
|
||||
Note that some special rules are applied (courtesy of POSIX)
|
||||
if the number of
|
||||
arguments to
|
||||
Note that some special rules are applied (courtesy of
|
||||
.Px
|
||||
) if the number of arguments to
|
||||
.Ic test
|
||||
or
|
||||
or inside the brackets
|
||||
.Ic \&[ ... \&]
|
||||
is less than five: if leading
|
||||
.Ql \&!
|
||||
arguments can be stripped such that only one argument remains then a string
|
||||
length test is performed (again, even if the argument is a unary operator); if
|
||||
leading
|
||||
.Ql \&!
|
||||
arguments can be stripped such that three arguments remain and the second
|
||||
argument is a binary operator, then the binary operation is performed (even
|
||||
if the first argument is a unary operator, including an unstripped
|
||||
.Ql \&! ) .
|
||||
arguments can be stripped such that only one to three arguments remain,
|
||||
then the lowered comparison is executed; (thanks to XSI) parentheses
|
||||
.Ic \e( ... \e)
|
||||
lower four- and three-argument forms to two- and one-argument forms,
|
||||
respectively; three-argument forms ultimately prefer binary operations,
|
||||
followed by negation and parenthesis lowering; two- and four-argument forms
|
||||
prefer negation followed by parenthesis; the one-argument form always implies
|
||||
.Fl n .
|
||||
.Pp
|
||||
.Sy Note :
|
||||
A common mistake is to use
|
||||
|
@ -4430,7 +4490,11 @@ instead, or the double-bracket operator
|
|||
or, to avoid pattern matching (see
|
||||
.Ic \&[[
|
||||
above):
|
||||
.Dq if \&[[ $foo = "$bar" \&]]
|
||||
.Dq if \&[[ $foo = \&"$bar" \&]]
|
||||
.Pp
|
||||
The
|
||||
.Ic \&[[ ... ]]
|
||||
construct is not only more secure to use but also often faster.
|
||||
.Pp
|
||||
.It Xo
|
||||
.Ic time
|
||||
|
@ -4600,6 +4664,14 @@ If
|
|||
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.
|
||||
.Pp
|
||||
When
|
||||
.Fl f
|
||||
|
@ -6128,7 +6200,6 @@ contains the system and suid profile.
|
|||
.Xr cat 1 ,
|
||||
.Xr ed 1 ,
|
||||
.Xr getopt 1 ,
|
||||
.Xr printf 1 ,
|
||||
.Xr sed 1 ,
|
||||
.Xr sh 1 ,
|
||||
.Xr stty 1 ,
|
||||
|
@ -6155,6 +6226,8 @@ contains the system and suid profile.
|
|||
.Xr mknod 8
|
||||
.Pp
|
||||
.Pa http://docsrv.sco.com:507/en/man/html.C/sh.C.html
|
||||
.Pp
|
||||
.Pa https://www.mirbsd.org/ksh\-chan.htm
|
||||
.Rs
|
||||
.%A Morris Bolsky
|
||||
.%B "The KornShell Command and Programming Language"
|
||||
|
@ -6262,19 +6335,27 @@ $ /bin/sleep 666 && echo fubar
|
|||
.Ed
|
||||
.Pp
|
||||
This document attempts to describe
|
||||
.Nm mksh\ R40+CVS
|
||||
.Nm mksh\ R43
|
||||
and up,
|
||||
compiled without any options impacting functionality, such as
|
||||
.Dv MKSH_SMALL ,
|
||||
when not called as
|
||||
.Pa /bin/sh
|
||||
which, on some systems only, enables
|
||||
.Ic set \-o sh
|
||||
automatically (whose behaviour differs across targets),
|
||||
for an operating environment supporting all of its advanced needs.
|
||||
Please report bugs in
|
||||
.Nm
|
||||
to the
|
||||
.Mx
|
||||
mailing list at
|
||||
.Aq miros\-discuss@mirbsd.org
|
||||
.Aq miros\-mksh@mirbsd.org
|
||||
or in the
|
||||
.Li \&#\&!/bin/mksh
|
||||
.Pq or Li \&#ksh
|
||||
IRC channel at
|
||||
.Pa irc.freenode.net:6667 .
|
||||
.Pa irc.freenode.net
|
||||
.Pq Port 6697 SSL, 6667 unencrypted ,
|
||||
or at:
|
||||
.Pa https://launchpad.net/mksh
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#if defined(SHFLAGS_DEFNS)
|
||||
__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.9 2011/06/12 15:37:10 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.12 2012/06/28 20:14:17 tg Exp $");
|
||||
#define FN(sname,cname,ochar,flags) /* nothing */
|
||||
#elif defined(SHFLAGS_ENUMS)
|
||||
#define FN(sname,cname,ochar,flags) cname,
|
||||
|
@ -29,14 +29,18 @@ FN("bgnice", FBGNICE, 0, OF_ANY)
|
|||
/* ./. enable {} globbing (non-standard) */
|
||||
FN("braceexpand", FBRACEEXPAND, 0, OF_ANY)
|
||||
|
||||
#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
|
||||
/* ./. Emacs command line editing mode */
|
||||
FN("emacs", FEMACS, 0, OF_ANY)
|
||||
#endif
|
||||
|
||||
/* -e quit on error */
|
||||
FN("errexit", FERREXIT, 'e', OF_ANY)
|
||||
|
||||
#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
|
||||
/* ./. Emacs command line editing mode, gmacs variant */
|
||||
FN("gmacs", FGMACS, 0, OF_ANY)
|
||||
#endif
|
||||
|
||||
/* ./. reading EOF does not exit */
|
||||
FN("ignoreeof", FIGNOREEOF, 0, OF_ANY)
|
||||
|
@ -108,7 +112,7 @@ FN("utf8-mode", FUNICODE, 'U', OF_ANY)
|
|||
/* -v echo input */
|
||||
FN("verbose", FVERBOSE, 'v', OF_ANY)
|
||||
|
||||
#if !MKSH_S_NOVI
|
||||
#if !defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
|
||||
/* ./. Vi command line editing mode */
|
||||
FN("vi", FVI, 0, OF_ANY)
|
||||
|
||||
|
|
133
src/shf.c
133
src/shf.c
|
@ -1,7 +1,7 @@
|
|||
/* $OpenBSD: shf.c,v 1.15 2006/04/02 00:48:33 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.44 2011/09/07 15:24:20 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.56 2013/01/01 03:32:44 tg Exp $");
|
||||
|
||||
/* flags to shf_emptybuf() */
|
||||
#define EB_READSW 0x01 /* about to switch to reading */
|
||||
|
@ -490,7 +490,7 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf)
|
|||
return (NULL);
|
||||
|
||||
/* save room for NUL */
|
||||
--bsize;
|
||||
--bsize;
|
||||
do {
|
||||
if (shf->rnleft == 0) {
|
||||
if (shf_fillbuf(shf) == EOF)
|
||||
|
@ -552,7 +552,7 @@ shf_ungetc(int c, struct shf *shf)
|
|||
* Can unget what was read, but not something different;
|
||||
* we don't want to modify a string.
|
||||
*/
|
||||
if (shf->rp[-1] != c)
|
||||
if ((int)(shf->rp[-1]) != c)
|
||||
return (EOF);
|
||||
shf->flags &= ~SHF_EOF;
|
||||
shf->rp--;
|
||||
|
@ -722,7 +722,7 @@ shf_snprintf(char *buf, ssize_t bsize, const char *fmt, ...)
|
|||
n = shf_vfprintf(&shf, fmt, args);
|
||||
va_end(args);
|
||||
/* NUL terminates */
|
||||
shf_sclose(&shf);
|
||||
shf_sclose(&shf);
|
||||
return (n);
|
||||
}
|
||||
|
||||
|
@ -764,7 +764,12 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
ssize_t field, precision, len;
|
||||
unsigned long lnum;
|
||||
/* %#o produces the longest output */
|
||||
char numbuf[(8 * sizeof(long) + 2) / 3 + 1];
|
||||
char numbuf[(8 * sizeof(long) + 2) / 3 + 1
|
||||
#ifdef DEBUG
|
||||
/* a NUL for LLVM/Clang scan-build */
|
||||
+ 1
|
||||
#endif
|
||||
];
|
||||
/* this stuff for dealing with the buffer */
|
||||
ssize_t nwritten = 0;
|
||||
|
||||
|
@ -842,12 +847,16 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
continue;
|
||||
}
|
||||
if (ksh_isdigit(c)) {
|
||||
bool overflowed = false;
|
||||
|
||||
tmp = c - '0';
|
||||
while (c = *fmt++, ksh_isdigit(c))
|
||||
while (c = *fmt++, ksh_isdigit(c)) {
|
||||
if (notok2mul(2147483647, tmp, 10))
|
||||
overflowed = true;
|
||||
tmp = tmp * 10 + c - '0';
|
||||
}
|
||||
--fmt;
|
||||
if (tmp < 0)
|
||||
/* overflow? */
|
||||
if (overflowed)
|
||||
tmp = 0;
|
||||
if (flags & FL_DOT)
|
||||
precision = tmp;
|
||||
|
@ -898,6 +907,16 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
integral:
|
||||
flags |= FL_NUMBER;
|
||||
cp = numbuf + sizeof(numbuf);
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* this is necessary so Clang 3.2 realises
|
||||
* utf_skipcols/shf_putc in the output loop
|
||||
* terminate; these values are always ASCII
|
||||
* so an out-of-bounds access cannot happen
|
||||
* but Clang doesn't know that
|
||||
*/
|
||||
*--cp = '\0';
|
||||
#endif
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
|
@ -949,6 +968,10 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
}
|
||||
}
|
||||
len = numbuf + sizeof(numbuf) - (s = cp);
|
||||
#ifdef DEBUG
|
||||
/* see above comment for Clang 3.2 */
|
||||
--len;
|
||||
#endif
|
||||
if (flags & FL_DOT) {
|
||||
if (precision > len) {
|
||||
field = precision;
|
||||
|
@ -967,14 +990,13 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
|
||||
case 'c':
|
||||
flags &= ~FL_DOT;
|
||||
numbuf[0] = (char)(VA(int));
|
||||
s = numbuf;
|
||||
len = 1;
|
||||
break;
|
||||
c = (char)(VA(int));
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case '%':
|
||||
default:
|
||||
numbuf[0] = c;
|
||||
numbuf[1] = 0;
|
||||
s = numbuf;
|
||||
len = 1;
|
||||
break;
|
||||
|
@ -1042,16 +1064,95 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
|
|||
return (shf_error(shf) ? EOF : nwritten);
|
||||
}
|
||||
|
||||
#ifdef MKSH_SMALL
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
int
|
||||
shf_getc(struct shf *shf)
|
||||
{
|
||||
return (shf_getc_(shf));
|
||||
return (shf_getc_i(shf));
|
||||
}
|
||||
|
||||
int
|
||||
shf_putc(int c, struct shf *shf)
|
||||
{
|
||||
return (shf_putc_(c, shf));
|
||||
return (shf_putc_i(c, shf));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
const char *
|
||||
cstrerror(int errnum)
|
||||
{
|
||||
#undef strerror
|
||||
return (strerror(errnum));
|
||||
#define strerror dontuse_strerror /* poisoned */
|
||||
}
|
||||
#elif !HAVE_STRERROR
|
||||
|
||||
#if HAVE_SYS_ERRLIST
|
||||
#if !HAVE_SYS_ERRLIST_DECL
|
||||
extern const int sys_nerr;
|
||||
extern const char * const sys_errlist[];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const char *
|
||||
cstrerror(int errnum)
|
||||
{
|
||||
/* "Unknown error: " + sign + rough estimate + NUL */
|
||||
static char errbuf[15 + 1 + (8 * sizeof(int) + 2) / 3 + 1];
|
||||
|
||||
#if HAVE_SYS_ERRLIST
|
||||
if (errnum > 0 && errnum < sys_nerr && sys_errlist[errnum])
|
||||
return (sys_errlist[errnum]);
|
||||
#endif
|
||||
|
||||
switch (errnum) {
|
||||
case 0:
|
||||
return ("Undefined error: 0");
|
||||
#ifdef EPERM
|
||||
case EPERM:
|
||||
return ("Operation not permitted");
|
||||
#endif
|
||||
#ifdef ENOENT
|
||||
case ENOENT:
|
||||
return ("No such file or directory");
|
||||
#endif
|
||||
#ifdef ESRCH
|
||||
case ESRCH:
|
||||
return ("No such process");
|
||||
#endif
|
||||
#ifdef E2BIG
|
||||
case E2BIG:
|
||||
return ("Argument list too long");
|
||||
#endif
|
||||
#ifdef ENOEXEC
|
||||
case ENOEXEC:
|
||||
return ("Exec format error");
|
||||
#endif
|
||||
#ifdef ENOMEM
|
||||
case ENOMEM:
|
||||
return ("Cannot allocate memory");
|
||||
#endif
|
||||
#ifdef EACCES
|
||||
case EACCES:
|
||||
return ("Permission denied");
|
||||
#endif
|
||||
#ifdef ENOTDIR
|
||||
case ENOTDIR:
|
||||
return ("Not a directory");
|
||||
#endif
|
||||
#ifdef EINVAL
|
||||
case EINVAL:
|
||||
return ("Invalid argument");
|
||||
#endif
|
||||
#ifdef ELOOP
|
||||
case ELOOP:
|
||||
return ("Too many levels of symbolic links");
|
||||
#endif
|
||||
default:
|
||||
shf_snprintf(errbuf, sizeof(errbuf),
|
||||
"Unknown error: %d", errnum);
|
||||
return (errbuf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
315
src/syn.c
315
src/syn.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: syn.c,v 1.28 2008/07/23 16:34:38 jaredy Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2011, 2012
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,16 +23,22 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.69 2011/09/07 15:24:21 tg Exp $");
|
||||
|
||||
extern short subshell_nesting_level;
|
||||
extern void yyskiputf8bom(void);
|
||||
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.88 2012/12/28 02:28:39 tg Exp $");
|
||||
|
||||
struct nesting_state {
|
||||
int start_token; /* token than began nesting (eg, FOR) */
|
||||
int start_line; /* line nesting began on */
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -51,7 +58,7 @@ static struct op *newtp(int);
|
|||
static void syntaxerr(const char *) MKSH_A_NORETURN;
|
||||
static void nesting_push(struct nesting_state *, int);
|
||||
static void nesting_pop(struct nesting_state *);
|
||||
static int assign_command(char *);
|
||||
static int assign_command(const char *);
|
||||
static int inalias(struct source *);
|
||||
static Test_op dbtestp_isa(Test_env *, Test_meta);
|
||||
static const char *dbtestp_getopnd(Test_env *, Test_op, bool);
|
||||
|
@ -64,6 +71,7 @@ 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)
|
||||
|
@ -71,6 +79,9 @@ static int symbol; /* yylex value */
|
|||
#define tpeek(cf) ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
|
||||
#define musthave(c,cf) do { if (token(cf) != (c)) syntaxerr(NULL); } while (/* CONSTCOND */ 0)
|
||||
|
||||
static const char Tcbrace[] = "}";
|
||||
static const char Tesac[] = "esac";
|
||||
|
||||
static void
|
||||
yyparse(void)
|
||||
{
|
||||
|
@ -227,18 +238,32 @@ nested(int type, int smark, int emark)
|
|||
|
||||
nesting_push(&old_nesting, smark);
|
||||
t = c_list(true);
|
||||
musthave(emark, KEYWORD|ALIAS);
|
||||
musthave(emark, KEYWORD|sALIAS);
|
||||
nesting_pop(&old_nesting);
|
||||
return (block(type, t, NOBLOCK, NOWORDS));
|
||||
}
|
||||
|
||||
static const char let_cmd[] = {
|
||||
CHAR, 'l', CHAR, 'e', CHAR, 't', EOS
|
||||
};
|
||||
static const char setA_cmd0[] = {
|
||||
CHAR, 's', CHAR, 'e', CHAR, 't', EOS
|
||||
};
|
||||
static const char setA_cmd1[] = {
|
||||
CHAR, '-', CHAR, 'A', EOS
|
||||
};
|
||||
static const char setA_cmd2[] = {
|
||||
CHAR, '-', CHAR, '-', EOS
|
||||
};
|
||||
|
||||
static struct op *
|
||||
get_command(int cf)
|
||||
{
|
||||
struct op *t;
|
||||
int c, iopn = 0, syniocf;
|
||||
int c, iopn = 0, syniocf, lno;
|
||||
struct ioword *iop, **iops;
|
||||
XPtrV args, vars;
|
||||
char *tcp;
|
||||
struct nesting_state old_nesting;
|
||||
|
||||
/* NUFILE is small enough to leave this addition unchecked */
|
||||
|
@ -246,8 +271,8 @@ get_command(int cf)
|
|||
XPinit(args, 16);
|
||||
XPinit(vars, 16);
|
||||
|
||||
syniocf = KEYWORD|ALIAS;
|
||||
switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
|
||||
syniocf = KEYWORD|sALIAS;
|
||||
switch (c = token(cf|KEYWORD|sALIAS|VARASN)) {
|
||||
default:
|
||||
REJECT;
|
||||
afree(iops, ATEMP);
|
||||
|
@ -259,12 +284,12 @@ get_command(int cf)
|
|||
case LWORD:
|
||||
case REDIR:
|
||||
REJECT;
|
||||
syniocf &= ~(KEYWORD|ALIAS);
|
||||
syniocf &= ~(KEYWORD|sALIAS);
|
||||
t = newtp(TCOM);
|
||||
t->lineno = source->line;
|
||||
while (/* CONSTCOND */ 1) {
|
||||
cf = (t->u.evalflags ? ARRAYVAR : 0) |
|
||||
(XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
|
||||
(XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD);
|
||||
switch (tpeek(cf)) {
|
||||
case REDIR:
|
||||
while ((iop = synio(cf)) != NULL) {
|
||||
|
@ -292,67 +317,50 @@ get_command(int cf)
|
|||
XPput(args, yylval.cp);
|
||||
break;
|
||||
|
||||
case '(':
|
||||
#ifndef MKSH_SMALL
|
||||
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
|
||||
XPsize(vars) == 1 && is_wdvarassign(yylval.cp))
|
||||
goto is_wdarrassign;
|
||||
#endif
|
||||
/*
|
||||
* Check for "> foo (echo hi)" which AT&T ksh
|
||||
* allows (not POSIX, but not disallowed)
|
||||
*/
|
||||
afree(t, ATEMP);
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 0) {
|
||||
case '(' /*)*/:
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
|
||||
is_wdvarassign(yylval.cp)) {
|
||||
/* wdarrassign: foo=(bar) */
|
||||
ACCEPT;
|
||||
goto Subshell;
|
||||
|
||||
/* manipulate the vars string */
|
||||
tcp = XPptrv(vars)[(vars.len = 0)];
|
||||
/* 'varname=' -> 'varname' */
|
||||
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
|
||||
|
||||
/* construct new args strings */
|
||||
XPput(args, wdcopy(setA_cmd0, ATEMP));
|
||||
XPput(args, wdcopy(setA_cmd1, ATEMP));
|
||||
XPput(args, tcp);
|
||||
XPput(args, wdcopy(setA_cmd2, ATEMP));
|
||||
|
||||
/* slurp in words till closing paren */
|
||||
while (token(CONTIN) == LWORD)
|
||||
XPput(args, yylval.cp);
|
||||
if (symbol != /*(*/ ')')
|
||||
syntaxerr(NULL);
|
||||
} else {
|
||||
/*
|
||||
* Check for "> foo (echo hi)"
|
||||
* which AT&T ksh allows (not
|
||||
* POSIX, but not disallowed)
|
||||
*/
|
||||
afree(t, ATEMP);
|
||||
if (XPsize(args) == 0 &&
|
||||
XPsize(vars) == 0) {
|
||||
ACCEPT;
|
||||
goto Subshell;
|
||||
}
|
||||
|
||||
/* must be a function */
|
||||
if (iopn != 0 || XPsize(args) != 1 ||
|
||||
XPsize(vars) != 0)
|
||||
syntaxerr(NULL);
|
||||
ACCEPT;
|
||||
musthave(/*(*/')', 0);
|
||||
t = function_body(XPptrv(args)[0], false);
|
||||
}
|
||||
|
||||
/* must be a function */
|
||||
if (iopn != 0 || XPsize(args) != 1 ||
|
||||
XPsize(vars) != 0)
|
||||
syntaxerr(NULL);
|
||||
ACCEPT;
|
||||
musthave(/*(*/')', 0);
|
||||
t = function_body(XPptrv(args)[0], false);
|
||||
goto Leave;
|
||||
#ifndef MKSH_SMALL
|
||||
is_wdarrassign:
|
||||
{
|
||||
static const char set_cmd0[] = {
|
||||
CHAR, 's', CHAR, 'e',
|
||||
CHAR, 't', EOS
|
||||
};
|
||||
static const char set_cmd1[] = {
|
||||
CHAR, '-', CHAR, 'A', EOS
|
||||
};
|
||||
static const char set_cmd2[] = {
|
||||
CHAR, '-', CHAR, '-', EOS
|
||||
};
|
||||
char *tcp;
|
||||
|
||||
ACCEPT;
|
||||
|
||||
/* manipulate the vars string */
|
||||
tcp = *(--vars.cur);
|
||||
/* 'varname=' -> 'varname' */
|
||||
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
|
||||
|
||||
/* construct new args strings */
|
||||
XPput(args, wdcopy(set_cmd0, ATEMP));
|
||||
XPput(args, wdcopy(set_cmd1, ATEMP));
|
||||
XPput(args, tcp);
|
||||
XPput(args, wdcopy(set_cmd2, ATEMP));
|
||||
|
||||
/* slurp in words till closing paren */
|
||||
while (token(CONTIN) == LWORD)
|
||||
XPput(args, yylval.cp);
|
||||
if (symbol != /*(*/ ')')
|
||||
syntaxerr(NULL);
|
||||
|
||||
goto Leave;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto Leave;
|
||||
|
@ -361,24 +369,21 @@ get_command(int cf)
|
|||
Leave:
|
||||
break;
|
||||
|
||||
case '(':
|
||||
case '(': /*)*/ {
|
||||
int subshell_nesting_type_saved;
|
||||
Subshell:
|
||||
++subshell_nesting_level;
|
||||
subshell_nesting_type_saved = subshell_nesting_type;
|
||||
subshell_nesting_type = ')';
|
||||
t = nested(TPAREN, '(', ')');
|
||||
--subshell_nesting_level;
|
||||
subshell_nesting_type = subshell_nesting_type_saved;
|
||||
break;
|
||||
}
|
||||
|
||||
case '{': /*}*/
|
||||
t = nested(TBRACE, '{', '}');
|
||||
break;
|
||||
|
||||
case MDPAREN: {
|
||||
int lno;
|
||||
static const char let_cmd[] = {
|
||||
CHAR, 'l', CHAR, 'e',
|
||||
CHAR, 't', EOS
|
||||
};
|
||||
|
||||
case MDPAREN:
|
||||
/* leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
|
||||
lno = source->line;
|
||||
ACCEPT;
|
||||
|
@ -395,7 +400,6 @@ get_command(int cf)
|
|||
XPput(args, wdcopy(let_cmd, ATEMP));
|
||||
XPput(args, yylval.cp);
|
||||
break;
|
||||
}
|
||||
|
||||
case DBRACKET: /* [[ .. ]] */
|
||||
/* leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
|
||||
|
@ -452,12 +456,12 @@ get_command(int cf)
|
|||
t = newtp(TIF);
|
||||
t->left = c_list(true);
|
||||
t->right = thenpart();
|
||||
musthave(FI, KEYWORD|ALIAS);
|
||||
musthave(FI, KEYWORD|sALIAS);
|
||||
nesting_pop(&old_nesting);
|
||||
break;
|
||||
|
||||
case BANG:
|
||||
syniocf &= ~(KEYWORD|ALIAS);
|
||||
syniocf &= ~(KEYWORD|sALIAS);
|
||||
t = pipeline(0);
|
||||
if (t == NULL)
|
||||
syntaxerr(NULL);
|
||||
|
@ -465,9 +469,9 @@ get_command(int cf)
|
|||
break;
|
||||
|
||||
case TIME:
|
||||
syniocf &= ~(KEYWORD|ALIAS);
|
||||
syniocf &= ~(KEYWORD|sALIAS);
|
||||
t = pipeline(0);
|
||||
if (t) {
|
||||
if (t && t->type == TCOM) {
|
||||
t->str = alloc(2, ATEMP);
|
||||
/* TF_* flags */
|
||||
t->str[0] = '\0';
|
||||
|
@ -516,7 +520,7 @@ dogroup(void)
|
|||
int c;
|
||||
struct op *list;
|
||||
|
||||
c = token(CONTIN|KEYWORD|ALIAS);
|
||||
c = token(CONTIN|KEYWORD|sALIAS);
|
||||
/*
|
||||
* A {...} can be used instead of do...done for for/select loops
|
||||
* but not for while/until loops - we don't need to check if it
|
||||
|
@ -530,7 +534,7 @@ dogroup(void)
|
|||
else
|
||||
syntaxerr(NULL);
|
||||
list = c_list(true);
|
||||
musthave(c, KEYWORD|ALIAS);
|
||||
musthave(c, KEYWORD|sALIAS);
|
||||
return (list);
|
||||
}
|
||||
|
||||
|
@ -539,7 +543,7 @@ thenpart(void)
|
|||
{
|
||||
struct op *t;
|
||||
|
||||
musthave(THEN, KEYWORD|ALIAS);
|
||||
musthave(THEN, KEYWORD|sALIAS);
|
||||
t = newtp(0);
|
||||
t->left = c_list(true);
|
||||
if (t->left == NULL)
|
||||
|
@ -553,7 +557,7 @@ elsepart(void)
|
|||
{
|
||||
struct op *t;
|
||||
|
||||
switch (token(KEYWORD|ALIAS|VARASN)) {
|
||||
switch (token(KEYWORD|sALIAS|VARASN)) {
|
||||
case ELSE:
|
||||
if ((t = c_list(true)) == NULL)
|
||||
syntaxerr(NULL);
|
||||
|
@ -577,7 +581,7 @@ caselist(void)
|
|||
struct op *t, *tl;
|
||||
int c;
|
||||
|
||||
c = token(CONTIN|KEYWORD|ALIAS);
|
||||
c = token(CONTIN|KEYWORD|sALIAS);
|
||||
/* A {...} can be used instead of in...esac for case statements */
|
||||
if (c == IN)
|
||||
c = ESAC;
|
||||
|
@ -594,7 +598,7 @@ caselist(void)
|
|||
else
|
||||
tl->right = tc, tl = tc;
|
||||
}
|
||||
musthave(c, KEYWORD|ALIAS);
|
||||
musthave(c, KEYWORD|sALIAS);
|
||||
return (t);
|
||||
}
|
||||
|
||||
|
@ -610,7 +614,20 @@ casepart(int endtok)
|
|||
if (token(CONTIN | KEYWORD) != '(')
|
||||
REJECT;
|
||||
do {
|
||||
musthave(LWORD, 0);
|
||||
switch (token(0)) {
|
||||
case LWORD:
|
||||
break;
|
||||
case '}':
|
||||
case ESAC:
|
||||
if (symbol != endtok) {
|
||||
strdupx(yylval.cp,
|
||||
symbol == '}' ? Tcbrace : Tesac, ATEMP);
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
syntaxerr(NULL);
|
||||
}
|
||||
XPput(ptns, yylval.cp);
|
||||
} while (token(0) == '|');
|
||||
REJECT;
|
||||
|
@ -619,17 +636,23 @@ casepart(int endtok)
|
|||
musthave(')', 0);
|
||||
|
||||
t->left = c_list(true);
|
||||
/* Note: POSIX requires the ;; */
|
||||
if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
|
||||
|
||||
/* initialise to default for ;; or omitted */
|
||||
t->u.charflag = ';';
|
||||
/* SUSv4 requires the ;; except in the last casepart */
|
||||
if ((tpeek(CONTIN|KEYWORD|sALIAS)) != endtok)
|
||||
switch (symbol) {
|
||||
default:
|
||||
syntaxerr(NULL);
|
||||
case BREAK:
|
||||
case BRKEV:
|
||||
t->u.charflag = '|';
|
||||
if (0)
|
||||
/* FALLTHROUGH */
|
||||
case BRKFT:
|
||||
t->u.charflag =
|
||||
(symbol == BRKEV) ? '|' :
|
||||
(symbol == BRKFT) ? '&' : ';';
|
||||
t->u.charflag = '&';
|
||||
/* FALLTHROUGH */
|
||||
case BREAK:
|
||||
/* initialised above, but we need to eat the token */
|
||||
ACCEPT;
|
||||
}
|
||||
return (t);
|
||||
|
@ -642,7 +665,6 @@ function_body(char *name,
|
|||
{
|
||||
char *sname, *p;
|
||||
struct op *t;
|
||||
bool old_func_parse;
|
||||
|
||||
sname = wdstrip(name, 0);
|
||||
/*-
|
||||
|
@ -663,14 +685,14 @@ function_body(char *name,
|
|||
* only accepts an open-brace.
|
||||
*/
|
||||
if (ksh_func) {
|
||||
if (tpeek(CONTIN|KEYWORD|ALIAS) == '(' /*)*/) {
|
||||
/* function foo () { */
|
||||
if (tpeek(CONTIN|KEYWORD|sALIAS) == '(' /*)*/) {
|
||||
/* function foo () { //}*/
|
||||
ACCEPT;
|
||||
musthave(')', 0);
|
||||
/* degrade to POSIX function */
|
||||
ksh_func = false;
|
||||
}
|
||||
musthave('{' /*}*/, CONTIN|KEYWORD|ALIAS);
|
||||
musthave('{' /*}*/, CONTIN|KEYWORD|sALIAS);
|
||||
REJECT;
|
||||
}
|
||||
|
||||
|
@ -679,8 +701,6 @@ function_body(char *name,
|
|||
t->u.ksh_func = tobool(ksh_func);
|
||||
t->lineno = source->line;
|
||||
|
||||
old_func_parse = e->flags & EF_FUNC_PARSE;
|
||||
e->flags |= EF_FUNC_PARSE;
|
||||
if ((t->left = get_command(CONTIN)) == NULL) {
|
||||
char *tv;
|
||||
/*
|
||||
|
@ -701,8 +721,6 @@ function_body(char *name,
|
|||
t->left->vars[0] = NULL;
|
||||
t->left->lineno = 1;
|
||||
}
|
||||
if (!old_func_parse)
|
||||
e->flags &= ~EF_FUNC_PARSE;
|
||||
|
||||
return (t);
|
||||
}
|
||||
|
@ -715,7 +733,7 @@ wordlist(void)
|
|||
|
||||
XPinit(args, 16);
|
||||
/* POSIX does not do alias expansion here... */
|
||||
if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
|
||||
if ((c = token(CONTIN|KEYWORD|sALIAS)) != IN) {
|
||||
if (c != ';')
|
||||
/* non-POSIX, but AT&T ksh accepts a ; here */
|
||||
REJECT;
|
||||
|
@ -750,7 +768,7 @@ block(int type, struct op *t1, struct op *t2, char **wp)
|
|||
return (t);
|
||||
}
|
||||
|
||||
const struct tokeninfo {
|
||||
static const struct tokeninfo {
|
||||
const char *name;
|
||||
short val;
|
||||
short reserved;
|
||||
|
@ -762,7 +780,7 @@ const struct tokeninfo {
|
|||
{ "elif", ELIF, true },
|
||||
{ "fi", FI, true },
|
||||
{ "case", CASE, true },
|
||||
{ "esac", ESAC, true },
|
||||
{ Tesac, ESAC, true },
|
||||
{ "for", FOR, true },
|
||||
{ Tselect, SELECT, true },
|
||||
{ "while", WHILE, true },
|
||||
|
@ -773,7 +791,7 @@ const struct tokeninfo {
|
|||
{ Tfunction, FUNCTION, true },
|
||||
{ "time", TIME, true },
|
||||
{ "{", '{', true },
|
||||
{ "}", '}', true },
|
||||
{ Tcbrace, '}', true },
|
||||
{ "!", BANG, true },
|
||||
{ "[[", DBRACKET, true },
|
||||
/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
|
||||
|
@ -796,7 +814,7 @@ initkeywords(void)
|
|||
struct tbl *p;
|
||||
|
||||
ktinit(APERM, &keywords,
|
||||
/* currently 28 keywords -> 80% of 64 (2^6) */
|
||||
/* currently 28 keywords: 75% of 64 = 2^6 */
|
||||
6);
|
||||
for (tt = tokentab; tt->name; tt++) {
|
||||
if (tt->reserved) {
|
||||
|
@ -916,13 +934,13 @@ compile(Source *s, bool skiputf8bom)
|
|||
* $
|
||||
*/
|
||||
static int
|
||||
assign_command(char *s)
|
||||
assign_command(const char *s)
|
||||
{
|
||||
if (!*s)
|
||||
return (0);
|
||||
return ((strcmp(s, Talias) == 0) ||
|
||||
(strcmp(s, "export") == 0) ||
|
||||
(strcmp(s, "readonly") == 0) ||
|
||||
(strcmp(s, Texport) == 0) ||
|
||||
(strcmp(s, Treadonly) == 0) ||
|
||||
(strcmp(s, Ttypeset) == 0));
|
||||
}
|
||||
|
||||
|
@ -948,13 +966,13 @@ static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
|
|||
static const char dbtest_not[] = { CHAR, '!', EOS };
|
||||
static const char dbtest_oparen[] = { CHAR, '(', EOS };
|
||||
static const char dbtest_cparen[] = { CHAR, ')', EOS };
|
||||
const char *const dbtest_tokens[] = {
|
||||
const char * const dbtest_tokens[] = {
|
||||
dbtest_or, dbtest_and, dbtest_not,
|
||||
dbtest_oparen, dbtest_cparen
|
||||
};
|
||||
const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
|
||||
const char db_lthan[] = { CHAR, '<', EOS };
|
||||
const char db_gthan[] = { CHAR, '>', EOS };
|
||||
static const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
|
||||
static const char db_lthan[] = { CHAR, '<', EOS };
|
||||
static const char db_gthan[] = { CHAR, '>', EOS };
|
||||
|
||||
/*
|
||||
* Test if the current token is a whatever. Accepts the current token if
|
||||
|
@ -997,7 +1015,7 @@ dbtestp_isa(Test_env *te, Test_meta meta)
|
|||
db_close)) ? TO_NONNULL : TO_NONOP;
|
||||
if (ret != TO_NONOP) {
|
||||
ACCEPT;
|
||||
if (meta < NELEM(dbtest_tokens))
|
||||
if ((unsigned int)meta < NELEM(dbtest_tokens))
|
||||
save = wdcopy(dbtest_tokens[(int)meta], ATEMP);
|
||||
if (save)
|
||||
XPput(*te->pos.av, save);
|
||||
|
@ -1106,32 +1124,65 @@ parse_usec(const char *s, struct timeval *tv)
|
|||
* a COMSUB recursively using the main shell parser and lexer
|
||||
*/
|
||||
char *
|
||||
yyrecursive(void)
|
||||
yyrecursive(int subtype MKSH_A_UNUSED)
|
||||
{
|
||||
struct op *t;
|
||||
char *cp;
|
||||
bool old_reject;
|
||||
int old_symbol;
|
||||
struct ioword **old_herep;
|
||||
struct yyrecursive_state *ys;
|
||||
int stok, etok;
|
||||
|
||||
if (subtype == FUNSUB) {
|
||||
stok = '{';
|
||||
etok = '}';
|
||||
} else {
|
||||
stok = '(';
|
||||
etok = ')';
|
||||
}
|
||||
|
||||
ys = alloc(sizeof(struct yyrecursive_state), ATEMP);
|
||||
|
||||
/* tell the lexer to accept a closing parenthesis as EOD */
|
||||
++subshell_nesting_level;
|
||||
ys->old_nesting_type = subshell_nesting_type;
|
||||
subshell_nesting_type = etok;
|
||||
|
||||
/* push reject state, parse recursively, pop reject state */
|
||||
old_reject = reject;
|
||||
old_symbol = symbol;
|
||||
ys->old_reject = reject;
|
||||
ys->old_symbol = symbol;
|
||||
ACCEPT;
|
||||
old_herep = herep;
|
||||
ys->old_herep = herep;
|
||||
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, '(', ')');
|
||||
herep = old_herep;
|
||||
reject = old_reject;
|
||||
symbol = old_symbol;
|
||||
t = nested(TPAREN, stok, etok);
|
||||
yyrecursive_pop(false);
|
||||
|
||||
/* t->left because nested(TPAREN, ...) hides our goodies there */
|
||||
cp = snptreef(NULL, 0, "%T", t->left);
|
||||
tfree(t, ATEMP);
|
||||
|
||||
--subshell_nesting_level;
|
||||
return (cp);
|
||||
}
|
||||
|
||||
void
|
||||
yyrecursive_pop(bool popall)
|
||||
{
|
||||
struct yyrecursive_state *ys;
|
||||
|
||||
popnext:
|
||||
if (!(ys = e->yyrecursive_statep))
|
||||
return;
|
||||
e->yyrecursive_statep = ys->next;
|
||||
|
||||
sALIAS = ys->old_salias;
|
||||
herep = ys->old_herep;
|
||||
reject = ys->old_reject;
|
||||
symbol = ys->old_symbol;
|
||||
|
||||
subshell_nesting_type = ys->old_nesting_type;
|
||||
|
||||
afree(ys, ATEMP);
|
||||
if (popall)
|
||||
goto popnext;
|
||||
}
|
||||
|
|
208
src/tree.c
208
src/tree.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: tree.c,v 1.19 2008/08/11 21:50:35 jaredy Exp $ */
|
||||
/* $OpenBSD: tree.c,v 1.20 2012/06/27 07:17:19 otto Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -22,7 +23,7 @@
|
|||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.51 2011/09/07 15:24:21 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.67 2012/12/04 01:10:35 tg Exp $");
|
||||
|
||||
#define INDENT 8
|
||||
|
||||
|
@ -36,6 +37,8 @@ static void iofree(struct ioword **, Area *);
|
|||
/* "foo& ; bar" and "foo |& ; bar" are invalid */
|
||||
static bool prevent_semicolon;
|
||||
|
||||
static const char Telif_pT[] = "elif %T";
|
||||
|
||||
/*
|
||||
* print a command tree
|
||||
*/
|
||||
|
@ -52,6 +55,25 @@ ptree(struct op *t, int indent, struct shf *shf)
|
|||
return;
|
||||
switch (t->type) {
|
||||
case TCOM:
|
||||
prevent_semicolon = false;
|
||||
/*
|
||||
* special-case 'var=<<EOF' (rough; see
|
||||
* exec.c:execute() for full code)
|
||||
*/
|
||||
if (
|
||||
/* we have zero arguments, i.e. no programme to run */
|
||||
t->args[0] == NULL &&
|
||||
/* we have exactly one variable assignment */
|
||||
t->vars[0] != NULL && t->vars[1] == NULL &&
|
||||
/* we have exactly one I/O redirection */
|
||||
t->ioact != NULL && t->ioact[0] != NULL &&
|
||||
t->ioact[1] == NULL &&
|
||||
/* of type "here document" (or "here string") */
|
||||
(t->ioact[0]->flag & IOTYPE) == IOHERE) {
|
||||
fptreef(shf, indent, "%S", t->vars[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->vars) {
|
||||
w = (const char **)t->vars;
|
||||
while (*w)
|
||||
|
@ -64,7 +86,6 @@ ptree(struct op *t, int indent, struct shf *shf)
|
|||
fptreef(shf, indent, "%S ", *w++);
|
||||
} else
|
||||
shf_puts("#no-args# ", shf);
|
||||
prevent_semicolon = false;
|
||||
break;
|
||||
case TEXEC:
|
||||
t = t->left;
|
||||
|
@ -126,38 +147,39 @@ ptree(struct op *t, int indent, struct shf *shf)
|
|||
}
|
||||
fptreef(shf, indent, "%Nesac ");
|
||||
break;
|
||||
#ifndef MKSH_NO_DEPRECATED_WARNING
|
||||
#ifdef DEBUG
|
||||
case TELIF:
|
||||
internal_errorf("TELIF in tree.c:ptree() unexpected");
|
||||
/* FALLTHROUGH */
|
||||
#endif
|
||||
case TIF:
|
||||
i = 2;
|
||||
t1 = t;
|
||||
goto process_TIF;
|
||||
do {
|
||||
t = t->right;
|
||||
t1 = t1->right;
|
||||
i = 0;
|
||||
fptreef(shf, indent, "%;");
|
||||
process_TIF:
|
||||
/* 5 == strlen("elif ") */
|
||||
fptreef(shf, indent + 5 - i, "elif %T" + i, t->left);
|
||||
t = t->right;
|
||||
if (t->left != NULL) {
|
||||
fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
|
||||
t1 = t1->right;
|
||||
if (t1->left != NULL) {
|
||||
fptreef(shf, indent, "%;");
|
||||
fptreef(shf, indent + INDENT, "%s%N%T",
|
||||
"then", t->left);
|
||||
"then", t1->left);
|
||||
}
|
||||
} while (t->right && t->right->type == TELIF);
|
||||
if (t->right != NULL) {
|
||||
} while (t1->right && t1->right->type == TELIF);
|
||||
if (t1->right != NULL) {
|
||||
fptreef(shf, indent, "%;");
|
||||
fptreef(shf, indent + INDENT, "%s%N%T",
|
||||
"else", t->right);
|
||||
"else", t1->right);
|
||||
}
|
||||
fptreef(shf, indent, "%;fi ");
|
||||
break;
|
||||
case TWHILE:
|
||||
case TUNTIL:
|
||||
/* 6 == strlen("while"/"until") */
|
||||
/* 6 == strlen("while "/"until ") */
|
||||
fptreef(shf, indent + 6, "%s %T",
|
||||
(t->type == TWHILE) ? "while" : "until",
|
||||
t->left);
|
||||
|
@ -214,8 +236,10 @@ ptree(struct op *t, int indent, struct shf *shf)
|
|||
* often leads to an extra blank line, but it's not
|
||||
* worth worrying about)
|
||||
*/
|
||||
if (need_nl)
|
||||
if (need_nl) {
|
||||
shf_putc('\n', shf);
|
||||
prevent_semicolon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,8 +280,8 @@ pioact(struct shf *shf, int indent, struct ioword *iop)
|
|||
/* name/delim are NULL when printing syntax errors */
|
||||
if (type == IOHERE) {
|
||||
if (iop->delim)
|
||||
fptreef(shf, indent, "%S ", iop->delim);
|
||||
else
|
||||
wdvarput(shf, iop->delim, 0, WDS_TPUTS);
|
||||
if (iop->flag & IOHERESTR)
|
||||
shf_putc(' ', shf);
|
||||
} else if (iop->name)
|
||||
fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
|
||||
|
@ -270,6 +294,7 @@ static const char *
|
|||
wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
|
||||
{
|
||||
int c;
|
||||
const char *cs;
|
||||
|
||||
/*-
|
||||
* problems:
|
||||
|
@ -289,7 +314,7 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
|
|||
case CHAR:
|
||||
c = *wp++;
|
||||
if ((opmode & WDS_MAGIC) &&
|
||||
(ISMAGIC(c) || c == '[' || c == NOT ||
|
||||
(ISMAGIC(c) || c == '[' || c == '!' ||
|
||||
c == '-' || c == ']' || c == '*' || c == '?'))
|
||||
shf_putc(MAGIC, shf);
|
||||
shf_putc(c, shf);
|
||||
|
@ -313,16 +338,20 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
|
|||
}
|
||||
case COMSUB:
|
||||
shf_puts("$(", shf);
|
||||
cs = ")";
|
||||
pSUB:
|
||||
while ((c = *wp++) != 0)
|
||||
shf_putc(c, shf);
|
||||
shf_putc(')', shf);
|
||||
shf_puts(cs, shf);
|
||||
break;
|
||||
case FUNSUB:
|
||||
shf_puts("${ ", shf);
|
||||
cs = ";}";
|
||||
goto pSUB;
|
||||
case EXPRSUB:
|
||||
shf_puts("$((", shf);
|
||||
while ((c = *wp++) != 0)
|
||||
shf_putc(c, shf);
|
||||
shf_puts("))", shf);
|
||||
break;
|
||||
cs = "))";
|
||||
goto pSUB;
|
||||
case OQUOTE:
|
||||
if (opmode & WDS_TPUTS) {
|
||||
quotelevel++;
|
||||
|
@ -558,6 +587,7 @@ wdscan(const char *wp, int c)
|
|||
wp++;
|
||||
break;
|
||||
case COMSUB:
|
||||
case FUNSUB:
|
||||
case EXPRSUB:
|
||||
while (*wp++ != 0)
|
||||
;
|
||||
|
@ -711,24 +741,42 @@ fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
|
|||
void
|
||||
vistree(char *dst, size_t sz, struct op *t)
|
||||
{
|
||||
int c;
|
||||
unsigned int c;
|
||||
char *cp, *buf;
|
||||
size_t n;
|
||||
|
||||
buf = alloc(sz, ATEMP);
|
||||
snptreef(buf, sz, "%T", t);
|
||||
buf = alloc(sz + 8, ATEMP);
|
||||
snptreef(buf, sz + 8, "%T", t);
|
||||
cp = buf;
|
||||
while ((c = *cp++)) {
|
||||
if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
|
||||
/* C0 or C1 control character or DEL */
|
||||
if (!--sz)
|
||||
break;
|
||||
*dst++ = (c & 0x80) ? '$' : '^';
|
||||
c = (c & 0x7F) ^ 0x40;
|
||||
}
|
||||
if (!--sz)
|
||||
break;
|
||||
*dst++ = c;
|
||||
vist_loop:
|
||||
if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
|
||||
if (c == 0 || n >= sz)
|
||||
/* NUL or not enough free space */
|
||||
goto vist_out;
|
||||
/* copy multibyte char */
|
||||
sz -= n;
|
||||
while (n--)
|
||||
*dst++ = *cp++;
|
||||
goto vist_loop;
|
||||
}
|
||||
if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0)
|
||||
/* NUL or not enough free space */
|
||||
goto vist_out;
|
||||
if ((c & 0x60) == 0 || (c & 0x7F) == 0x7F) {
|
||||
/* C0 or C1 control character or DEL */
|
||||
if (--sz == 0)
|
||||
/* not enough free space for two chars */
|
||||
goto vist_out;
|
||||
*dst++ = (c & 0x80) ? '$' : '^';
|
||||
c = (c & 0x7F) ^ 0x40;
|
||||
} else if (UTFMODE && c > 0x7F) {
|
||||
/* better not try to display broken multibyte chars */
|
||||
c = '?';
|
||||
}
|
||||
*dst++ = c;
|
||||
goto vist_loop;
|
||||
|
||||
vist_out:
|
||||
*dst = '\0';
|
||||
afree(buf, ATEMP);
|
||||
}
|
||||
|
@ -747,7 +795,7 @@ dumpchar(struct shf *shf, int c)
|
|||
|
||||
/* see: wdvarput */
|
||||
static const char *
|
||||
dumpwdvar_(struct shf *shf, const char *wp, int quotelevel)
|
||||
dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
|
||||
{
|
||||
int c;
|
||||
|
||||
|
@ -779,6 +827,9 @@ dumpwdvar_(struct shf *shf, const char *wp, int quotelevel)
|
|||
closeandout:
|
||||
shf_putc('>', shf);
|
||||
break;
|
||||
case FUNSUB:
|
||||
shf_puts("FUNSUB<", shf);
|
||||
goto dumpsub;
|
||||
case EXPRSUB:
|
||||
shf_puts("EXPRSUB<", shf);
|
||||
goto dumpsub;
|
||||
|
@ -799,7 +850,7 @@ dumpwdvar_(struct shf *shf, const char *wp, int quotelevel)
|
|||
while ((c = *wp++) != 0)
|
||||
dumpchar(shf, c);
|
||||
shf_putc('|', shf);
|
||||
wp = dumpwdvar_(shf, wp, 0);
|
||||
wp = dumpwdvar_i(shf, wp, 0);
|
||||
break;
|
||||
case CSUBST:
|
||||
shf_puts("]CSUBST(", shf);
|
||||
|
@ -826,16 +877,75 @@ dumpwdvar_(struct shf *shf, const char *wp, int quotelevel)
|
|||
void
|
||||
dumpwdvar(struct shf *shf, const char *wp)
|
||||
{
|
||||
dumpwdvar_(shf, wp, 0);
|
||||
dumpwdvar_i(shf, wp, 0);
|
||||
}
|
||||
|
||||
void
|
||||
dumpioact(struct shf *shf, struct op *t)
|
||||
{
|
||||
struct ioword **ioact, *iop;
|
||||
|
||||
if ((ioact = t->ioact) == NULL)
|
||||
return;
|
||||
|
||||
shf_puts("{IOACT", shf);
|
||||
while ((iop = *ioact++) != NULL) {
|
||||
int type = iop->flag & IOTYPE;
|
||||
#define DT(x) case x: shf_puts(#x, shf); break;
|
||||
#define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
|
||||
|
||||
shf_putc(';', shf);
|
||||
switch (type) {
|
||||
DT(IOREAD)
|
||||
DT(IOWRITE)
|
||||
DT(IORDWR)
|
||||
DT(IOHERE)
|
||||
DT(IOCAT)
|
||||
DT(IODUP)
|
||||
default:
|
||||
shf_fprintf(shf, "unk%d", type);
|
||||
}
|
||||
DB(IOEVAL)
|
||||
DB(IOSKIP)
|
||||
DB(IOCLOB)
|
||||
DB(IORDUP)
|
||||
DB(IONAMEXP)
|
||||
DB(IOBASH)
|
||||
DB(IOHERESTR)
|
||||
DB(IONDELIM)
|
||||
shf_fprintf(shf, ",unit=%d", iop->unit);
|
||||
if (iop->delim) {
|
||||
shf_puts(",delim<", shf);
|
||||
dumpwdvar(shf, iop->delim);
|
||||
shf_putc('>', shf);
|
||||
}
|
||||
if (iop->name) {
|
||||
if (iop->flag & IONAMEXP) {
|
||||
shf_puts(",name=", shf);
|
||||
print_value_quoted(shf, iop->name);
|
||||
} else {
|
||||
shf_puts(",name<", shf);
|
||||
dumpwdvar(shf, iop->name);
|
||||
shf_putc('>', shf);
|
||||
}
|
||||
}
|
||||
if (iop->heredoc) {
|
||||
shf_puts(",heredoc=", shf);
|
||||
print_value_quoted(shf, iop->heredoc);
|
||||
}
|
||||
#undef DT
|
||||
#undef DB
|
||||
}
|
||||
shf_putc('}', shf);
|
||||
}
|
||||
|
||||
void
|
||||
dumptree(struct shf *shf, struct op *t)
|
||||
{
|
||||
int i;
|
||||
int i, j;
|
||||
const char **w, *name;
|
||||
struct op *t1;
|
||||
static int nesting = 0;
|
||||
static int nesting;
|
||||
|
||||
for (i = 0; i < nesting; ++i)
|
||||
shf_putc('\t', shf);
|
||||
|
@ -845,6 +955,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
name = "(null)";
|
||||
goto out;
|
||||
}
|
||||
dumpioact(shf, t);
|
||||
switch (t->type) {
|
||||
#define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
|
||||
|
||||
|
@ -854,7 +965,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
w = (const char **)t->vars;
|
||||
while (*w) {
|
||||
shf_putc('\n', shf);
|
||||
for (int j = 0; j < nesting; ++j)
|
||||
for (j = 0; j < nesting; ++j)
|
||||
shf_putc('\t', shf);
|
||||
shf_fprintf(shf, " var%d<", i++);
|
||||
dumpwdvar(shf, *w++);
|
||||
|
@ -867,7 +978,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
w = t->args;
|
||||
while (*w) {
|
||||
shf_putc('\n', shf);
|
||||
for (int j = 0; j < nesting; ++j)
|
||||
for (j = 0; j < nesting; ++j)
|
||||
shf_putc('\t', shf);
|
||||
shf_fprintf(shf, " arg%d<", i++);
|
||||
dumpwdvar(shf, *w++);
|
||||
|
@ -907,7 +1018,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
w = t->args;
|
||||
while (*w) {
|
||||
shf_putc('\n', shf);
|
||||
for (int j = 0; j < nesting; ++j)
|
||||
for (j = 0; j < nesting; ++j)
|
||||
shf_putc('\t', shf);
|
||||
shf_fprintf(shf, " arg%d<", i++);
|
||||
dumpwdvar(shf, *w++);
|
||||
|
@ -922,7 +1033,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
w = (const char **)t->vars;
|
||||
while (*w) {
|
||||
shf_putc('\n', shf);
|
||||
for (int j = 0; j < nesting; ++j)
|
||||
for (j = 0; j < nesting; ++j)
|
||||
shf_putc('\t', shf);
|
||||
shf_fprintf(shf, " var%d<", i++);
|
||||
dumpwdvar(shf, *w++);
|
||||
|
@ -937,7 +1048,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
i = 0;
|
||||
for (t1 = t->left; t1 != NULL; t1 = t1->right) {
|
||||
shf_putc('\n', shf);
|
||||
for (int j = 0; j < nesting; ++j)
|
||||
for (j = 0; j < nesting; ++j)
|
||||
shf_putc('\t', shf);
|
||||
shf_fprintf(shf, " sub%d[(", i);
|
||||
w = (const char **)t1->vars;
|
||||
|
@ -948,6 +1059,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
++w;
|
||||
}
|
||||
shf_putc(')', shf);
|
||||
dumpioact(shf, t);
|
||||
shf_putc('\n', shf);
|
||||
dumptree(shf, t1->left);
|
||||
shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
|
||||
|
@ -974,6 +1086,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
shf_putc('\n', shf);
|
||||
dumptree(shf, t->left);
|
||||
t = t->right;
|
||||
dumpioact(shf, t);
|
||||
if (t->left != NULL) {
|
||||
shf_puts(" /TTHEN:\n", shf);
|
||||
dumptree(shf, t->left);
|
||||
|
@ -981,6 +1094,7 @@ dumptree(struct shf *shf, struct op *t)
|
|||
if (t->right && t->right->type == TELIF) {
|
||||
shf_puts(" /TELIF:", shf);
|
||||
t = t->right;
|
||||
dumpioact(shf, t);
|
||||
goto dumpif;
|
||||
}
|
||||
if (t->right != NULL) {
|
||||
|
|
307
src/var.c
307
src/var.c
|
@ -1,7 +1,8 @@
|
|||
/* $OpenBSD: var.c,v 1.34 2007/10/15 02:16:35 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
|
@ -26,7 +27,7 @@
|
|||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.132 2011/09/07 15:24:22 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.166 2013/02/18 22:24:52 tg Exp $");
|
||||
|
||||
/*-
|
||||
* Variables
|
||||
|
@ -38,7 +39,6 @@ __RCSID("$MirOS: src/bin/mksh/var.c,v 1.132 2011/09/07 15:24:22 tg Exp $");
|
|||
* if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
|
||||
*/
|
||||
|
||||
static struct tbl vtemp;
|
||||
static struct table specials;
|
||||
static uint32_t lcg_state = 5381;
|
||||
|
||||
|
@ -130,8 +130,8 @@ initvar(void)
|
|||
struct tbl *tp;
|
||||
|
||||
ktinit(APERM, &specials,
|
||||
/* currently 12 specials -> 80% of 16 (2^4) */
|
||||
4);
|
||||
/* currently 14 specials: 75% of 32 = 2^5 */
|
||||
5);
|
||||
while (i < V_MAX - 1) {
|
||||
tp = ktenter(&specials, initvar_names[i],
|
||||
hash(initvar_names[i]));
|
||||
|
@ -140,8 +140,8 @@ initvar(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* common code for several functions below */
|
||||
static struct block *
|
||||
/* common code for several functions below and c_typeset() */
|
||||
struct block *
|
||||
varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
|
||||
{
|
||||
register struct tbl *vp;
|
||||
|
@ -260,7 +260,7 @@ global(const char *n)
|
|||
vp->flag &= ~(ISSET|INTEGER);
|
||||
break;
|
||||
case '?':
|
||||
vp->val.i = exstat;
|
||||
vp->val.i = exstat & 0xFF;
|
||||
break;
|
||||
case '#':
|
||||
vp->val.i = l->argc;
|
||||
|
@ -299,6 +299,7 @@ local(const char *n, bool copy)
|
|||
|
||||
/* check to see if this is an array */
|
||||
n = array_index_calc(n, &array, &val);
|
||||
mkssert(n != NULL);
|
||||
h = hash(n);
|
||||
if (!ksh_isalphx(*n)) {
|
||||
vp = &vtemp;
|
||||
|
@ -405,11 +406,11 @@ int
|
|||
setstr(struct tbl *vq, const char *s, int error_ok)
|
||||
{
|
||||
char *salloc = NULL;
|
||||
int no_ro_check = error_ok & 0x4;
|
||||
bool no_ro_check = tobool(error_ok & 0x4);
|
||||
|
||||
error_ok &= ~0x4;
|
||||
if ((vq->flag & RDONLY) && !no_ro_check) {
|
||||
warningf(true, "%s: %s", vq->name, "is read only");
|
||||
warningf(true, "read-only: %s", vq->name);
|
||||
if (!error_ok)
|
||||
errorfxz(2);
|
||||
return (0);
|
||||
|
@ -417,12 +418,15 @@ setstr(struct tbl *vq, const char *s, int error_ok)
|
|||
if (!(vq->flag&INTEGER)) {
|
||||
/* string dest */
|
||||
if ((vq->flag&ALLOC)) {
|
||||
#ifndef MKSH_SMALL
|
||||
/* debugging */
|
||||
if (s >= vq->val.s &&
|
||||
s <= vq->val.s + strlen(vq->val.s))
|
||||
s <= vq->val.s + strlen(vq->val.s)) {
|
||||
internal_errorf(
|
||||
"setstr: %s=%s: assigning to self",
|
||||
vq->name, s);
|
||||
}
|
||||
#endif
|
||||
afree(vq->val.s, vq->areap);
|
||||
}
|
||||
vq->flag &= ~(ISSET|ALLOC);
|
||||
|
@ -469,10 +473,10 @@ setint(struct tbl *vq, mksh_ari_t n)
|
|||
static int
|
||||
getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
|
||||
{
|
||||
char *s;
|
||||
int c, base, neg;
|
||||
mksh_uari_t num;
|
||||
const char *s;
|
||||
bool have_base = false;
|
||||
mksh_ari_t num;
|
||||
|
||||
if (vp->flag&SPECIAL)
|
||||
getspec(vp);
|
||||
|
@ -487,31 +491,32 @@ getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
|
|||
base = 10;
|
||||
num = 0;
|
||||
neg = 0;
|
||||
if (arith && *s == '0' && *(s+1)) {
|
||||
s++;
|
||||
if (*s == 'x' || *s == 'X') {
|
||||
s++;
|
||||
base = 16;
|
||||
} else if (vp->flag & ZEROFIL) {
|
||||
while (*s == '0')
|
||||
s++;
|
||||
} else
|
||||
base = 8;
|
||||
if (arith && s[0] == '0' && (s[1] | 0x20) == 'x') {
|
||||
s += 2;
|
||||
base = 16;
|
||||
have_base = true;
|
||||
}
|
||||
for (c = *s++; c ; c = *s++) {
|
||||
#ifdef MKSH_LEGACY_MODE
|
||||
if (arith && s[0] == '0' && ksh_isdigit(s[1]) &&
|
||||
!(vp->flag & ZEROFIL)) {
|
||||
/* interpret as octal (deprecated) */
|
||||
base = 8;
|
||||
have_base = true;
|
||||
}
|
||||
#endif
|
||||
while ((c = *s++)) {
|
||||
if (c == '-') {
|
||||
neg++;
|
||||
continue;
|
||||
} else if (c == '#') {
|
||||
base = (int)num;
|
||||
if (have_base || base < 1 || base > 36)
|
||||
if (have_base || num < 1 || num > 36)
|
||||
return (-1);
|
||||
base = (int)num;
|
||||
if (base == 1) {
|
||||
unsigned int wc;
|
||||
|
||||
if (!UTFMODE)
|
||||
wc = *(unsigned char *)s;
|
||||
wc = *(const unsigned char *)s;
|
||||
else if (utf_mbtowc(&wc, s) == (size_t)-1)
|
||||
/* OPTU-8 -> OPTU-16 */
|
||||
/*
|
||||
|
@ -519,7 +524,7 @@ getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
|
|||
* the same as 1#\x80 does, thus is
|
||||
* not round-tripping correctly XXX)
|
||||
*/
|
||||
wc = 0xEF00 + *(unsigned char *)s;
|
||||
wc = 0xEF00 + *(const unsigned char *)s;
|
||||
*nump = (mksh_ari_t)wc;
|
||||
return (1);
|
||||
}
|
||||
|
@ -538,9 +543,7 @@ getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
|
|||
return (-1);
|
||||
num = num * base + c;
|
||||
}
|
||||
if (neg)
|
||||
num = -num;
|
||||
*nump = num;
|
||||
*nump = neg ? -((mksh_ari_t)num) : (mksh_ari_t)num;
|
||||
return (base);
|
||||
}
|
||||
|
||||
|
@ -556,7 +559,7 @@ setint_v(struct tbl *vq, struct tbl *vp, bool arith)
|
|||
|
||||
if ((base = getint(vp, &num, arith)) == -1)
|
||||
return (NULL);
|
||||
setint_n(vq, num);
|
||||
setint_n(vq, num, 0);
|
||||
if (vq->type == 0)
|
||||
/* default base */
|
||||
vq->type = base;
|
||||
|
@ -565,7 +568,7 @@ setint_v(struct tbl *vq, struct tbl *vp, bool arith)
|
|||
|
||||
/* convert variable vq to integer variable, setting its value to num */
|
||||
void
|
||||
setint_n(struct tbl *vq, mksh_ari_t num)
|
||||
setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
|
||||
{
|
||||
if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
|
||||
vq->flag &= ~ALLOC;
|
||||
|
@ -573,6 +576,8 @@ setint_n(struct tbl *vq, mksh_ari_t num)
|
|||
afree(vq->val.s, vq->areap);
|
||||
}
|
||||
vq->val.i = num;
|
||||
if (newbase != 0)
|
||||
vq->type = newbase;
|
||||
vq->flag |= ISSET|INTEGER;
|
||||
if (vq->flag&SPECIAL)
|
||||
setspec(vq);
|
||||
|
@ -611,10 +616,13 @@ formatstr(struct tbl *vp, const char *s)
|
|||
--slen;
|
||||
}
|
||||
if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
|
||||
if (!s[0] || !s[1])
|
||||
goto uhm_no;
|
||||
if (s[1] == '#')
|
||||
n = 2;
|
||||
else if (s[2] == '#')
|
||||
n = 3;
|
||||
uhm_no:
|
||||
if (vp->u2.field <= n)
|
||||
n = 0;
|
||||
}
|
||||
|
@ -665,6 +673,8 @@ exportprep(struct tbl *vp, const char *val)
|
|||
char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
|
||||
size_t namelen, vallen;
|
||||
|
||||
mkssert(val != NULL);
|
||||
|
||||
namelen = strlen(vp->name);
|
||||
vallen = strlen(val) + 1;
|
||||
|
||||
|
@ -698,10 +708,6 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
|
||||
/* check for valid variable name, search for value */
|
||||
val = skip_varname(var, false);
|
||||
if (val == var)
|
||||
return (NULL);
|
||||
mkssert(var != NULL);
|
||||
mkssert(*var != 0);
|
||||
if (*val == '[') {
|
||||
if (set_refflag != SRF_NOP)
|
||||
errorf("%s: %s", var,
|
||||
|
@ -713,7 +719,7 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
* IMPORT is only used when the shell starts up and is
|
||||
* setting up its environment. Allow only simple array
|
||||
* references at this time since parameter/command
|
||||
* substitution is preformed on the [expression] which
|
||||
* substitution is performed on the [expression] which
|
||||
* would be a major security hole.
|
||||
*/
|
||||
if (set & IMPORT) {
|
||||
|
@ -731,10 +737,16 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
++val;
|
||||
vappend = true;
|
||||
}
|
||||
} else if ((val[0] != '\0') || (set & IMPORT)) {
|
||||
/*
|
||||
* must have a = when setting a variable by importing
|
||||
* the original environment, otherwise be empty; we
|
||||
* also end up here when a variable name was invalid
|
||||
* or none given
|
||||
*/
|
||||
return (NULL);
|
||||
} else {
|
||||
/* importing from original environment: must have an = */
|
||||
if (set & IMPORT)
|
||||
return (NULL);
|
||||
/* just varname with no value part nor equals sign */
|
||||
strdupx(tvar, var, ATEMP);
|
||||
val = NULL;
|
||||
/* handle foo[*] => foo (whole array) mapping for R39b */
|
||||
|
@ -802,7 +814,7 @@ typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
|
|||
if ((vpbase->flag&RDONLY) &&
|
||||
(val || clr || (set & ~EXPORT)))
|
||||
/* XXX check calls - is error here ok by POSIX? */
|
||||
errorfx(2, "%s: %s", tvar, "is read only");
|
||||
errorfx(2, "read-only: %s", tvar);
|
||||
afree(tvar, ATEMP);
|
||||
|
||||
/* most calls are with set/clr == 0 */
|
||||
|
@ -1084,38 +1096,9 @@ getspec(struct tbl *vp)
|
|||
{
|
||||
register mksh_ari_t i;
|
||||
int st;
|
||||
struct timeval tv;
|
||||
|
||||
switch ((st = special(vp->name))) {
|
||||
case V_SECONDS:
|
||||
/*
|
||||
* On start up the value of SECONDS is used before
|
||||
* it has been set - don't do anything in this case
|
||||
* (see initcoms[] in main.c).
|
||||
*/
|
||||
if (vp->flag & ISSET) {
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
i = tv.tv_sec - seconds;
|
||||
} else
|
||||
return;
|
||||
break;
|
||||
case V_RANDOM:
|
||||
/*
|
||||
* this is the same Linear Congruential PRNG as Borland
|
||||
* C/C++ allegedly uses in its built-in rand() function
|
||||
*/
|
||||
i = ((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF;
|
||||
break;
|
||||
case V_HISTSIZE:
|
||||
i = histsize;
|
||||
break;
|
||||
case V_OPTIND:
|
||||
i = user_opt.uoptind;
|
||||
break;
|
||||
case V_LINENO:
|
||||
i = current_lineno + user_lineno;
|
||||
break;
|
||||
case V_COLUMNS:
|
||||
case V_LINES:
|
||||
/*
|
||||
|
@ -1125,15 +1108,62 @@ getspec(struct tbl *vp)
|
|||
* and the window is then resized, the app won't
|
||||
* see the change cause the environ doesn't change.
|
||||
*/
|
||||
change_winsz();
|
||||
i = st == V_COLUMNS ? x_cols : x_lins;
|
||||
if (got_winch)
|
||||
change_winsz();
|
||||
break;
|
||||
}
|
||||
switch (st) {
|
||||
case V_BASHPID:
|
||||
i = (mksh_ari_t)procpid;
|
||||
break;
|
||||
case V_COLUMNS:
|
||||
i = x_cols;
|
||||
break;
|
||||
case V_HISTSIZE:
|
||||
i = histsize;
|
||||
break;
|
||||
case V_LINENO:
|
||||
i = current_lineno + user_lineno;
|
||||
break;
|
||||
case V_LINES:
|
||||
i = x_lins;
|
||||
break;
|
||||
case V_EPOCHREALTIME: {
|
||||
/* 10(%u) + 1(.) + 6 + NUL */
|
||||
char buf[18];
|
||||
|
||||
vp->flag &= ~SPECIAL;
|
||||
mksh_TIME(tv);
|
||||
shf_snprintf(buf, sizeof(buf), "%u.%06u",
|
||||
(unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
|
||||
setstr(vp, buf, KSH_RETURN_ERROR | 0x4);
|
||||
vp->flag |= SPECIAL;
|
||||
return;
|
||||
}
|
||||
case V_OPTIND:
|
||||
i = user_opt.uoptind;
|
||||
break;
|
||||
case V_RANDOM:
|
||||
i = rndget();
|
||||
break;
|
||||
case V_SECONDS:
|
||||
/*
|
||||
* On start up the value of SECONDS is used before
|
||||
* it has been set - don't do anything in this case
|
||||
* (see initcoms[] in main.c).
|
||||
*/
|
||||
if (vp->flag & ISSET) {
|
||||
mksh_TIME(tv);
|
||||
i = tv.tv_sec - seconds;
|
||||
} else
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
/* do nothing, do not touch vp at all */
|
||||
return;
|
||||
}
|
||||
vp->flag &= ~SPECIAL;
|
||||
setint_n(vp, i);
|
||||
setint_n(vp, i, 0);
|
||||
vp->flag |= SPECIAL;
|
||||
}
|
||||
|
||||
|
@ -1145,6 +1175,15 @@ setspec(struct tbl *vp)
|
|||
int st;
|
||||
|
||||
switch ((st = special(vp->name))) {
|
||||
#if HAVE_PERSISTENT_HISTORY
|
||||
case V_HISTFILE:
|
||||
sethistfile(str_val(vp));
|
||||
return;
|
||||
#endif
|
||||
case V_IFS:
|
||||
setctypes(s = str_val(vp), C_IFS);
|
||||
ifs0 = *s;
|
||||
return;
|
||||
case V_PATH:
|
||||
if (path)
|
||||
afree(path, APERM);
|
||||
|
@ -1153,10 +1192,6 @@ setspec(struct tbl *vp)
|
|||
/* clear tracked aliases */
|
||||
flushcom(true);
|
||||
return;
|
||||
case V_IFS:
|
||||
setctypes(s = str_val(vp), C_IFS);
|
||||
ifs0 = *s;
|
||||
return;
|
||||
case V_TMPDIR:
|
||||
if (tmpdir) {
|
||||
afree(tmpdir, APERM);
|
||||
|
@ -1176,20 +1211,21 @@ setspec(struct tbl *vp)
|
|||
strdupx(tmpdir, s, APERM);
|
||||
}
|
||||
return;
|
||||
#if HAVE_PERSISTENT_HISTORY
|
||||
case V_HISTFILE:
|
||||
sethistfile(str_val(vp));
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* common sub-cases */
|
||||
case V_OPTIND:
|
||||
case V_HISTSIZE:
|
||||
case V_COLUMNS:
|
||||
case V_LINES:
|
||||
if (vp->flag & IMPORT) {
|
||||
/* do not touch */
|
||||
unspecial(vp->name);
|
||||
vp->flag &= ~SPECIAL;
|
||||
return;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case V_HISTSIZE:
|
||||
case V_LINENO:
|
||||
case V_OPTIND:
|
||||
case V_RANDOM:
|
||||
case V_SECONDS:
|
||||
case V_LINENO:
|
||||
case V_TMOUT:
|
||||
vp->flag &= ~SPECIAL;
|
||||
if (getint(vp, &i, false) == -1) {
|
||||
|
@ -1208,20 +1244,24 @@ setspec(struct tbl *vp)
|
|||
/* process the singular parts of the common cases */
|
||||
|
||||
switch (st) {
|
||||
case V_OPTIND:
|
||||
getopts_reset((int)i);
|
||||
break;
|
||||
case V_HISTSIZE:
|
||||
sethistsize((int)i);
|
||||
break;
|
||||
case V_COLUMNS:
|
||||
if (i >= MIN_COLS)
|
||||
x_cols = i;
|
||||
break;
|
||||
case V_HISTSIZE:
|
||||
sethistsize(i);
|
||||
break;
|
||||
case V_LINENO:
|
||||
/* The -1 is because line numbering starts at 1. */
|
||||
user_lineno = (unsigned int)i - current_lineno - 1;
|
||||
break;
|
||||
case V_LINES:
|
||||
if (i >= MIN_LINS)
|
||||
x_lins = i;
|
||||
break;
|
||||
case V_OPTIND:
|
||||
getopts_reset((int)i);
|
||||
break;
|
||||
case V_RANDOM:
|
||||
/*
|
||||
* mksh R39d+ no longer has the traditional repeatability
|
||||
|
@ -1233,14 +1273,10 @@ setspec(struct tbl *vp)
|
|||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
mksh_TIME(tv);
|
||||
seconds = tv.tv_sec - i;
|
||||
}
|
||||
break;
|
||||
case V_LINENO:
|
||||
/* The -1 is because line numbering starts at 1. */
|
||||
user_lineno = (unsigned int)i - current_lineno - 1;
|
||||
break;
|
||||
case V_TMOUT:
|
||||
ksh_tmout = i >= 0 ? i : 0;
|
||||
break;
|
||||
|
@ -1250,7 +1286,19 @@ setspec(struct tbl *vp)
|
|||
static void
|
||||
unsetspec(struct tbl *vp)
|
||||
{
|
||||
/*
|
||||
* AT&T ksh man page says OPTIND, OPTARG and _ lose special
|
||||
* meaning, but OPTARG does not (still set by getopts) and _ is
|
||||
* also still set in various places. Don't know what AT&T does
|
||||
* for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
|
||||
* loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
|
||||
*/
|
||||
|
||||
switch (special(vp->name)) {
|
||||
case V_IFS:
|
||||
setctypes(TC_IFSWS, C_IFS);
|
||||
ifs0 = ' ';
|
||||
break;
|
||||
case V_PATH:
|
||||
if (path)
|
||||
afree(path, APERM);
|
||||
|
@ -1258,10 +1306,6 @@ unsetspec(struct tbl *vp)
|
|||
/* clear tracked aliases */
|
||||
flushcom(true);
|
||||
break;
|
||||
case V_IFS:
|
||||
setctypes(" \t\n", C_IFS);
|
||||
ifs0 = ' ';
|
||||
break;
|
||||
case V_TMPDIR:
|
||||
/* should not become unspecial */
|
||||
if (tmpdir) {
|
||||
|
@ -1276,14 +1320,6 @@ unsetspec(struct tbl *vp)
|
|||
/* AT&T ksh leaves previous value in place */
|
||||
unspecial(vp->name);
|
||||
break;
|
||||
|
||||
/*
|
||||
* AT&T ksh man page says OPTIND, OPTARG and _ lose special
|
||||
* meaning, but OPTARG does not (still set by getopts) and _ is
|
||||
* also still set in various places. Don't know what AT&T does
|
||||
* for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
|
||||
* loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1377,39 +1413,35 @@ set_array(const char *var, bool reset, const char **vals)
|
|||
{
|
||||
struct tbl *vp, *vq;
|
||||
mksh_uari_t i = 0, j = 0;
|
||||
const char *ccp;
|
||||
#ifndef MKSH_SMALL
|
||||
const char *ccp = var;
|
||||
char *cp = NULL;
|
||||
size_t n;
|
||||
#endif
|
||||
|
||||
/* to get local array, use "typeset foo; set -A foo" */
|
||||
#ifndef MKSH_SMALL
|
||||
/* to get local array, use "local foo; set -A foo" */
|
||||
n = strlen(var);
|
||||
if (n > 0 && var[n - 1] == '+') {
|
||||
/* append mode */
|
||||
reset = false;
|
||||
strndupx(cp, var, n - 1, ATEMP);
|
||||
ccp = cp;
|
||||
}
|
||||
#define CPORVAR (cp ? cp : var)
|
||||
#else
|
||||
#define CPORVAR var
|
||||
#endif
|
||||
vp = global(CPORVAR);
|
||||
vp = global(ccp);
|
||||
|
||||
/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
|
||||
if ((vp->flag&RDONLY))
|
||||
errorfx(2, "%s: %s", CPORVAR, "is read only");
|
||||
errorfx(2, "read-only: %s", ccp);
|
||||
/* This code is quite non-optimal */
|
||||
if (reset)
|
||||
if (reset) {
|
||||
/* trash existing values and attributes */
|
||||
unset(vp, 1);
|
||||
/* allocate-by-access the [0] element to keep in scope */
|
||||
arraysearch(vp, 0);
|
||||
}
|
||||
/*
|
||||
* TODO: would be nice for assignment to completely succeed or
|
||||
* completely fail. Only really effects integer arrays:
|
||||
* evaluation of some of vals[] may fail...
|
||||
*/
|
||||
#ifndef MKSH_SMALL
|
||||
if (cp != NULL) {
|
||||
/* find out where to set when appending */
|
||||
for (vq = vp; vq; vq = vq->u.array) {
|
||||
|
@ -1420,9 +1452,7 @@ set_array(const char *var, bool reset, const char **vals)
|
|||
}
|
||||
afree(cp, ATEMP);
|
||||
}
|
||||
#endif
|
||||
while ((ccp = vals[i])) {
|
||||
#ifndef MKSH_SMALL
|
||||
if (*ccp == '[') {
|
||||
int level = 0;
|
||||
|
||||
|
@ -1443,7 +1473,6 @@ set_array(const char *var, bool reset, const char **vals)
|
|||
} else
|
||||
ccp = vals[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
vq = arraysearch(vp, j);
|
||||
/* would be nice to deal with errors here... (see above) */
|
||||
|
@ -1458,19 +1487,9 @@ set_array(const char *var, bool reset, const char **vals)
|
|||
void
|
||||
change_winsz(void)
|
||||
{
|
||||
if (x_lins < 0) {
|
||||
/* first time initialisation */
|
||||
#ifdef TIOCGWINSZ
|
||||
if (tty_fd < 0)
|
||||
/* non-FTALKING, try to get an fd anyway */
|
||||
tty_init(true, false);
|
||||
#endif
|
||||
x_cols = -1;
|
||||
}
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
/* check if window size has changed */
|
||||
if (tty_fd >= 0) {
|
||||
if (tty_init_fd() < 2) {
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
|
||||
|
@ -1500,10 +1519,20 @@ hash(const void *s)
|
|||
|
||||
NZATInit(h);
|
||||
NZATUpdateString(h, s);
|
||||
NZATFinish(h);
|
||||
NZAATFinish(h);
|
||||
return (h);
|
||||
}
|
||||
|
||||
mksh_ari_t
|
||||
rndget(void)
|
||||
{
|
||||
/*
|
||||
* this is the same Linear Congruential PRNG as Borland
|
||||
* C/C++ allegedly uses in its built-in rand() function
|
||||
*/
|
||||
return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
|
||||
}
|
||||
|
||||
void
|
||||
rndset(long v)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#if defined(VARSPEC_DEFNS)
|
||||
__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.2 2011/06/05 19:58:21 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.6 2012/11/30 16:45:25 tg Exp $");
|
||||
#define FN(name) /* nothing */
|
||||
#elif defined(VARSPEC_ENUMS)
|
||||
#define FN(name) V_##name,
|
||||
|
@ -19,7 +19,9 @@ __RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.2 2011/06/05 19:58:21 tg Exp $");
|
|||
F0(NONE)
|
||||
|
||||
/* 1 and up are special variables */
|
||||
FN(BASHPID)
|
||||
FN(COLUMNS)
|
||||
FN(EPOCHREALTIME)
|
||||
#if HAVE_PERSISTENT_HISTORY
|
||||
FN(HISTFILE)
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue