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:
Thorsten Glaser 2013-02-18 23:02:51 +00:00
parent 93bd42c789
commit c2dc5def5e
28 changed files with 6701 additions and 3246 deletions

View file

@ -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
View file

@ -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
View file

@ -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 dont 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
View file

@ -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

View file

@ -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 dont 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

File diff suppressed because it is too large Load diff

View file

@ -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>

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -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 }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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
View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

580
src/sh.h

File diff suppressed because it is too large Load diff

View file

@ -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
View file

@ -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
View file

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

View file

@ -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
View file

@ -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)
{

View file

@ -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