am 008cbcd0: Merge "Add -F for classify to ls"
* commit '008cbcd082f21683b3b53d1ca84725b72b2f90b6':
Add -F for classify to ls
diff --git a/mksh/Android.mk b/mksh/Android.mk
deleted file mode 100644
index e53b863..0000000
--- a/mksh/Android.mk
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright © 2010
-# Thorsten Glaser <t.glaser@tarent.de>
-# This file is provided under the same terms as mksh.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-# /system/etc/mkshrc
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= mkshrc
-LOCAL_MODULE_TAGS:= shell_mksh
-LOCAL_MODULE_CLASS:= ETC
-LOCAL_MODULE_PATH:= $(TARGET_OUT)/etc
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-
-# /system/bin/mksh
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= mksh
-LOCAL_MODULE_TAGS:= shell_mksh
-
-# mksh source files
-LOCAL_SRC_FILES:= src/lalloc.c src/edit.c src/eval.c src/exec.c \
- src/expr.c src/funcs.c src/histrap.c src/jobs.c \
- src/lex.c src/main.c src/misc.c src/shf.c \
- src/syn.c src/tree.c src/var.c
-
-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
-
-include $(BUILD_EXECUTABLE)
diff --git a/mksh/MODULE_LICENSE_BSD_LIKE b/mksh/MODULE_LICENSE_BSD_LIKE
deleted file mode 100644
index e69de29..0000000
--- a/mksh/MODULE_LICENSE_BSD_LIKE
+++ /dev/null
diff --git a/mksh/NOTICE b/mksh/NOTICE
deleted file mode 100644
index 350061f..0000000
--- a/mksh/NOTICE
+++ /dev/null
@@ -1,21 +0,0 @@
-mksh is covered by The MirOS Licence:
-
-/*-
- * Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
diff --git a/mksh/mkmf.sh b/mksh/mkmf.sh
deleted file mode 100644
index 15d0432..0000000
--- a/mksh/mkmf.sh
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright © 2010
-# Thorsten Glaser <t.glaser@tarent.de>
-# This file is provided under the same terms as mksh.
-#-
-# Helper script to let src/Build.sh generate Makefrag.inc
-# which we in turn use in the manual creation of Android.mk
-#
-# This script is supposed to be run from/inside AOSP by the
-# porter of mksh to Android (and only manually).
-
-cd "$(dirname "$0")"
-srcdir=$(pwd)
-rm -rf tmp
-mkdir tmp
-cd ../../..
-aospdir=$(pwd)
-cd $srcdir/tmp
-
-addvar() {
- _vn=$1; shift
-
- eval $_vn=\"\$$_vn '$*"'
-}
-
-CFLAGS=
-CPPFLAGS=
-LDFLAGS=
-LIBS=
-
-# The definitions below were generated by examining the
-# output of the following command:
-# make showcommands out/target/product/generic/system/bin/mksh 2>&1 | tee log
-#
-# They are only used to let Build.sh find the compiler, header
-# files, linker and libraries to generate Makefrag.inc (similar
-# to what GNU autotools’ configure scripts do), and never used
-# during the real build process. We need this to port mksh to
-# the Android platform and it is crucial these are as close as
-# possible to the values used later. (You also must example the
-# results gathered from Makefrag.inc to see they are the same
-# across all Android platforms, or add appropriate ifdefs.)
-# 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/dalvik/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 \
- -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 \
- $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 \
- $aospdir/out/target/product/generic/obj/lib/crtend_android.o
-
-
-### Override flags
-# We don’t even *support* UTF-8 by default ☹
-addvar CPPFLAGS -DMKSH_ASSUME_UTF8=0
-# 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
-
-# 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
-
-# Android-x86 does not have helper functions for ProPolice SSP
-# and AOSP adds the flags by itself (same for warning flags)
-HAVE_CAN_FNOSTRICTALIASING=0
-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
-
-# ... and run it!
-export CC CPPFLAGS CFLAGS LDFLAGS LIBS TARGET_OS
-sh ../src/Build.sh -M
-rv=$?
-test x0 = x"$rv" && mv -f Makefrag.inc ../
-cd ..
-rm -rf tmp
-exit $rv
diff --git a/mksh/mkshrc b/mksh/mkshrc
deleted file mode 100644
index 0da5ea6..0000000
--- a/mksh/mkshrc
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) 2010
-# Thorsten Glaser <t.glaser@tarent.de>
-# This file is provided under the same terms as mksh.
-#-
-# Minimal /system/etc/mkshrc for Android
-
-: ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${HOSTNAME:=android}
-: ${SHELL:=$MKSH} ${USER:=$(typeset x=$(id); x=${x#*\(}; print -r -- ${x%%\)*})}
-if (( USER_ID )); then PS1='$'; else PS1='#'; fi
-function precmd {
- typeset e=$?
-
- (( e )) && print -n "$e|"
-}
-PS1='$(precmd)$USER@$HOSTNAME:${PWD:-?} '"$PS1 "
-export HOME HOSTNAME MKSH PS1 SHELL TERM USER
-alias l='ls'
-alias la='l -a'
-alias ll='l -l'
-alias lo='l -a -l'
-
-for p in ~/.bin; do
- [[ -d $p/. ]] || continue
- [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
-done
-
-unset p
-
-: place customisations above this line
diff --git a/mksh/src/00-NOTE.txt b/mksh/src/00-NOTE.txt
deleted file mode 100644
index b51d541..0000000
--- a/mksh/src/00-NOTE.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This is mksh from AnonCVS on 2010-08-24 with the
-following files removed:
-• Makefile
- (not part of regular mksh releases anyway)
-• dot.mkshrc
- (not needed, we use our own for Android)
-• mksh.1
- (manpage; also available from the web)
-• setmode.c
- (not needed, we don’t use the mknod builtin)
-• strlcpy.c
- (not needed, bionic provides this)
-
-The manual page can be downloaded as PDF (ISO A4 paper) from
-https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh.pdf or read
-online at https://www.mirbsd.org/man1/mksh (HTML).
-
-The following changes are done to code in this subdirectory
-at the moment:
-• check.t main.sh: remove the 'stop' alias to 'kill -STOP'
- since Android has a built in stop command that the alias
- overrides, causing problems with testing tools
\ No newline at end of file
diff --git a/mksh/src/Build.sh b/mksh/src/Build.sh
deleted file mode 100644
index c98b1ca..0000000
--- a/mksh/src/Build.sh
+++ /dev/null
@@ -1,1600 +0,0 @@
-#!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.459 2010/08/24 15:46:06 tg Exp $'
-#-
-# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-# 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.
-#-
-# People analysing the output must whitelist conftest.c for any kind
-# of compiler warning checks (mirtoconf is by design not quiet).
-#
-# Environment used: CC CFLAGS CPPFLAGS LDFLAGS LIBS NOWARN NROFF
-# TARGET_OS TARGET_OSREV
-# Feature selectors: USE_PRINTF_BUILTIN
-# CPPFLAGS recognised: MKSH_ASSUME_UTF8 MKSH_BINSHREDUCED MKSH_CLS_STRING
-# MKSH_CONSERVATIVE_FDS MKSH_MIDNIGHTBSD01ASH_COMPAT
-# MKSH_NOPWNAM MKSH_NO_LIMITS MKSH_SMALL MKSH_S_NOVI
-# MKSH_UNEMPLOYED MKSH_DEFAULT_EXECSHELL MKSHRC_PATH
-# MKSH_DEFAULT_TMPDIR MKSH_CLRTOEOL_STRING MKSH_A4PB
-
-LC_ALL=C
-export LC_ALL
-
-v() {
- $e "$*"
- eval "$@"
-}
-
-vv() {
- _c=$1
- shift
- $e "\$ $*" 2>&1
- eval "$@" >vv.out 2>&1
- sed "s^${_c} " <vv.out
-}
-
-vq() {
- eval "$@"
-}
-
-rmf() {
- for _f in "$@"; do
- case ${_f} in
- mksh.1) ;;
- *) rm -f "${_f}" ;;
- esac
- done
-}
-
-if test -d /usr/xpg4/bin/. >/dev/null 2>&1; then
- # Solaris: some of the tools have weird behaviour, use portable ones
- PATH=/usr/xpg4/bin:$PATH
- export PATH
-fi
-
-if test -n "${ZSH_VERSION+x}" && (emulate sh) >/dev/null 2>&1; then
- emulate sh
- NULLCMD=:
-fi
-
-allu=QWERTYUIOPASDFGHJKLZXCVBNM
-alll=qwertyuiopasdfghjklzxcvbnm
-alln=0123456789
-alls=______________________________________________________________
-nl='
-'
-tcfn=no
-bi=
-ui=
-ao=
-fx=
-me=`basename "$0"`
-orig_CFLAGS=$CFLAGS
-phase=x
-oldish_ed=stdout-ed,no-stderr-ed
-
-if test -t 1; then
- bi='[1m'
- ui='[4m'
- ao='[0m'
-fi
-
-upper() {
- echo :"$@" | sed 's/^://' | tr $alll $allu
-}
-
-# clean up after ac_testrun()
-ac_testdone() {
- eval HAVE_$fu=$fv
- fr=no
- test 0 = $fv || fr=yes
- $e "$bi==> $fd...$ao $ui$fr$ao$fx"
- fx=
-}
-
-# ac_cache label: sets f, fu, fv?=0
-ac_cache() {
- f=$1
- fu=`upper $f`
- eval fv=\$HAVE_$fu
- case $fv in
- 0|1)
- fx=' (cached)'
- return 0
- ;;
- esac
- fv=0
- return 1
-}
-
-# ac_testinit label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
-# returns 1 if value was cached/implied, 0 otherwise: call ac_testdone
-ac_testinit() {
- if ac_cache $1; then
- test x"$2" = x"!" && shift
- test x"$2" = x"" || shift
- fd=${3-$f}
- ac_testdone
- return 1
- fi
- fc=0
- if test x"$2" = x""; then
- ft=1
- else
- if test x"$2" = x"!"; then
- fc=1
- shift
- fi
- eval ft=\$HAVE_`upper $2`
- shift
- fi
- fd=${3-$f}
- if test $fc = "$ft"; then
- fv=$2
- fx=' (implied)'
- ac_testdone
- return 1
- fi
- $e ... $fd
- return 0
-}
-
-# pipe .c | ac_test[n] [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
-ac_testn() {
- if test x"$1" = x"!"; then
- fr=1
- shift
- else
- fr=0
- fi
- ac_testinit "$@" || return
- cat >conftest.c
- vv ']' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN conftest.c $LIBS $ccpr"
- test $tcfn = no && test -f a.out && tcfn=a.out
- test $tcfn = no && test -f a.exe && tcfn=a.exe
- test $tcfn = no && test -f conftest && tcfn=conftest
- if test -f $tcfn; then
- test 1 = $fr || fv=1
- else
- test 0 = $fr || fv=1
- fi
- vscan=
- if test $phase = u; then
- test $ct = gcc && vscan='unrecogni[sz]ed'
- test $ct = hpcc && vscan='unsupported'
- test $ct = pcc && vscan='unsupported'
- test $ct = sunpro && vscan='-e ignored -e turned.off'
- fi
- test -n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
- rmf conftest.c conftest.o ${tcfn}* vv.out
- ac_testdone
-}
-
-# ac_ifcpp cppexpr [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
-ac_ifcpp() {
- expr=$1; shift
- ac_testn "$@" <<-EOF
- int main(void) { return (
- #$expr
- 0
- #else
- /* force a failure: expr is false */
- thiswillneverbedefinedIhope()
- #endif
- ); }
-EOF
- test x"$1" = x"!" && shift
- f=$1
- fu=`upper $f`
- eval fv=\$HAVE_$fu
- test x"$fv" = x"1"
-}
-
-ac_cppflags() {
- test x"$1" = x"" || fu=$1
- fv=$2
- test x"$2" = x"" && eval fv=\$HAVE_$fu
- CPPFLAGS="$CPPFLAGS -DHAVE_$fu=$fv"
-}
-
-ac_test() {
- ac_testn "$@"
- ac_cppflags
-}
-
-# ac_flags [-] add varname flags [text]
-ac_flags() {
- if test x"$1" = x"-"; then
- shift
- hf=1
- else
- hf=0
- fi
- fa=$1
- vn=$2
- f=$3
- ft=$4
- test x"$ft" = x"" && ft="if $f can be used"
- save_CFLAGS=$CFLAGS
- CFLAGS="$CFLAGS $f"
- if test 1 = $hf; then
- ac_testn can_$vn '' "$ft"
- else
- ac_testn can_$vn '' "$ft" <<-'EOF'
- /* evil apo'stroph in comment test */
- int main(void) { return (0); }
- EOF
- fi
- eval fv=\$HAVE_CAN_`upper $vn`
- test 11 = $fa$fv || CFLAGS=$save_CFLAGS
-}
-
-# ac_header [!] header [prereq ...]
-ac_header() {
- if test x"$1" = x"!"; then
- na=1
- shift
- else
- na=0
- fi
- hf=$1; shift
- hv=`echo "$hf" | tr -d '\012\015' | tr -c $alll$allu$alln $alls`
- for i
- do
- echo "#include <$i>" >>x
- done
- echo "#include <$hf>" >>x
- echo 'int main(void) { return (0); }' >>x
- ac_testn "$hv" "" "<$hf>" <x
- rmf x
- test 1 = $na || ac_cppflags
-}
-
-addsrcs() {
- if test x"$1" = x"!"; then
- fr=0
- shift
- else
- fr=1
- fi
- eval i=\$$1
- test $fr = "$i" && case " $SRCS " in
- *\ $2\ *) ;;
- *) SRCS="$SRCS $2" ;;
- esac
-}
-
-
-if test -d mksh || test -d mksh.exe; then
- echo "$me: Error: ./mksh is a directory!" >&2
- exit 1
-fi
-rmf a.exe* a.out* conftest.c *core lft mksh* no *.bc *.ll *.o \
- Rebuild.sh signames.inc test.sh x vv.out
-
-curdir=`pwd` srcdir=`dirname "$0"` check_categories=
-test -n "$dirname" || dirname=.
-dstversion=`sed -n '/define MKSH_VERSION/s/^.*"\(.*\)".*$/\1/p' $srcdir/sh.h`
-
-e=echo
-r=0
-eq=0
-pm=0
-cm=normal
-optflags=-std-compile-opts
-last=
-
-for i
-do
- case $last:$i in
- c:combine|c:dragonegg|c:llvm)
- cm=$i
- last=
- ;;
- c:*)
- echo "$me: Unknown option -c '$i'!" >&2
- exit 1
- ;;
- o:*)
- optflags=$i
- last=
- ;;
- :-c)
- last=c
- ;;
- :-combine)
- cm=combine
- echo "$me: Warning: '$i' is deprecated, use '-c combine' instead!" >&2
- ;;
- :-g)
- # checker, debug, valgrind build
- CPPFLAGS="$CPPFLAGS -DDEBUG"
- CFLAGS="$CFLAGS -g3 -fno-builtin"
- ;;
- :-j)
- pm=1
- ;;
- :-llvm)
- cm=llvm
- optflags=-std-compile-opts
- echo "$me: Warning: '$i' is deprecated, use '-c llvm -O' instead!" >&2
- ;;
- :-llvm=*)
- cm=llvm
- optflags=`echo "x$i" | sed 's/^x-llvm=//'`
- echo "$me: Warning: '$i' is deprecated, use '-c llvm -o $llvm' instead!" >&2
- ;;
- :-M)
- cm=makefile
- ;;
- :-O)
- optflags=-std-compile-opts
- ;;
- :-o)
- last=o
- ;;
- :-Q)
- eq=1
- ;;
- :-r)
- r=1
- ;;
- :-v)
- echo "Build.sh $srcversion"
- echo "for mksh $dstversion"
- exit 0
- ;;
- :*)
- echo "$me: Unknown option '$i'!" >&2
- exit 1
- ;;
- *)
- echo "$me: Unknown option -'$last' '$i'!" >&2
- exit 1
- ;;
- esac
-done
-if test -n "$last"; then
- echo "$me: Option -'$last' not followed by argument!" >&2
- exit 1
-fi
-
-SRCS="lalloc.c edit.c eval.c exec.c expr.c funcs.c histrap.c"
-SRCS="$SRCS jobs.c lex.c main.c misc.c shf.c syn.c tree.c var.c"
-
-if test x"$srcdir" = x"."; then
- CPPFLAGS="-I. $CPPFLAGS"
-else
- CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS"
-fi
-
-test x"$TARGET_OS" = x"" && TARGET_OS=`uname -s 2>/dev/null || uname`
-oswarn=
-ccpc=-Wc,
-ccpl=-Wl,
-tsts=
-ccpr='|| for _f in ${tcfn}*; do test x"${_f}" = x"mksh.1" || rm -f "${_f}"; done'
-
-# Configuration depending on OS revision, on OSes that need them
-case $TARGET_OS in
-QNX)
- test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
- ;;
-esac
-
-# Configuration depending on OS name
-case $TARGET_OS in
-AIX)
- CPPFLAGS="$CPPFLAGS -D_ALL_SOURCE"
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-BeOS)
- oswarn=' and will currently not work'
- ;;
-BSD/OS)
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-CYGWIN*)
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-Darwin)
- ;;
-DragonFly)
- ;;
-FreeBSD)
- ;;
-GNU)
- # define NO_PATH_MAX to use Hurd-only functions
- CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE -DNO_PATH_MAX"
- ;;
-GNU/kFreeBSD)
- CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
- ;;
-Haiku)
- CPPFLAGS="$CPPFLAGS -DMKSH_ASSUME_UTF8"
- ;;
-HP-UX)
- ;;
-Interix)
- ccpc='-X '
- ccpl='-Y '
- CPPFLAGS="$CPPFLAGS -D_ALL_SOURCE"
- : ${LIBS='-lcrypt'}
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-IRIX*)
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-Linux)
- CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
- : ${HAVE_REVOKE=0}
- ;;
-MidnightBSD)
- ;;
-Minix)
- CPPFLAGS="$CPPFLAGS -DMKSH_UNEMPLOYED -DMKSH_CONSERVATIVE_FDS"
- CPPFLAGS="$CPPFLAGS -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX"
- oldish_ed=no-stderr-ed # /usr/bin/ed(!) is broken
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-MirBSD)
- ;;
-NetBSD)
- ;;
-OpenBSD)
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-OSF1)
- HAVE_SIG_T=0 # incompatible
- CPPFLAGS="$CPPFLAGS -D_OSF_SOURCE -D_POSIX_C_SOURCE=200112L"
- CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED"
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-Plan9)
- CPPFLAGS="$CPPFLAGS -D_POSIX_SOURCE -D_LIMITS_EXTENSION"
- CPPFLAGS="$CPPFLAGS -D_BSD_EXTENSION -D_SUSV2_SOURCE"
- oswarn=' and will currently not work'
- CPPFLAGS="$CPPFLAGS -DMKSH_ASSUME_UTF8 -DMKSH_UNEMPLOYED"
- ;;
-PW32*)
- HAVE_SIG_T=0 # incompatible
- oswarn=' and will currently not work'
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-QNX)
- CPPFLAGS="$CPPFLAGS -D__NO_EXT_QNX"
- case $TARGET_OSREV in
- [012345].*|6.[0123].*|6.4.[01])
- oldish_ed=no-stderr-ed # oldish /bin/ed is broken
- ;;
- esac
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-SunOS)
- CPPFLAGS="$CPPFLAGS -D_BSD_SOURCE -D__EXTENSIONS__"
- ;;
-syllable)
- CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
- oswarn=' and will currently not work'
- ;;
-ULTRIX)
- : ${CC=cc -YPOSIX}
- CPPFLAGS="$CPPFLAGS -Dssize_t=int"
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-UWIN*)
- ccpc='-Yc,'
- ccpl='-Yl,'
- tsts=" 3<>/dev/tty"
- oswarn="; it will compile, but the target"
- oswarn="$oswarn${nl}platform itself is very flakey/unreliable"
- : ${HAVE_SETLOCALE_CTYPE=0}
- ;;
-*)
- oswarn='; it may or may not work'
- ;;
-esac
-
-: ${CC=cc} ${NROFF=nroff}
-test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \
- NROFF="$NROFF -c"
-
-# this aids me in tracing FTBFSen without access to the buildd
-$e "Hi from$ao $bi$srcversion$ao on:"
-case $TARGET_OS in
-Darwin)
- vv '|' "hwprefs machine_type os_type os_class >&2"
- vv '|' "uname -a >&2"
- ;;
-IRIX*)
- vv '|' "uname -a >&2"
- vv '|' "hinv -v >&2"
- ;;
-OSF1)
- vv '|' "uname -a >&2"
- vv '|' "/usr/sbin/sizer -v >&2"
- ;;
-*)
- vv '|' "uname -a >&2"
- ;;
-esac
-test -z "$oswarn" || echo >&2 "
-Warning: mksh has not yet been ported to or tested on your
-operating system '$TARGET_OS'$oswarn. If you can provide
-a shell account to the developer, this may improve; please
-drop us a success or failure notice or even send in diffs.
-"
-$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao"
-
-#
-# Begin of mirtoconf checks
-#
-$e $bi$me: Scanning for functions... please ignore any errors.$ao
-
-#
-# Compiler: which one?
-#
-# notes:
-# - ICC defines __GNUC__ too
-# - GCC defines __hpux too
-# - LLVM+clang defines __GNUC__ too
-# - nwcc defines __GNUC__ too
-CPP="$CC -E"
-$e ... which compiler seems to be used
-cat >conftest.c <<'EOF'
-#if defined(__ICC) || defined(__INTEL_COMPILER)
-ct=icc
-#elif defined(__xlC__) || defined(__IBMC__)
-ct=xlc
-#elif defined(__SUNPRO_C)
-ct=sunpro
-#elif defined(__ACK__)
-ct=ack
-#elif defined(__BORLANDC__)
-ct=bcc
-#elif defined(__WATCOMC__)
-ct=watcom
-#elif defined(__MWERKS__)
-ct=metrowerks
-#elif defined(__HP_cc)
-ct=hpcc
-#elif defined(__DECC) || (defined(__osf__) && !defined(__GNUC__))
-ct=dec
-#elif defined(__PGI)
-ct=pgi
-#elif defined(__DMC__)
-ct=dmc
-#elif defined(_MSC_VER)
-ct=msc
-#elif defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__)
-ct=adsp
-#elif defined(__IAR_SYSTEMS_ICC__)
-ct=iar
-#elif defined(SDCC)
-ct=sdcc
-#elif defined(__PCC__)
-ct=pcc
-#elif defined(__TenDRA__)
-ct=tendra
-#elif defined(__TINYC__)
-ct=tcc
-#elif defined(__llvm__) && defined(__clang__)
-ct=clang
-#elif defined(__NWCC__)
-ct=nwcc
-#elif defined(__GNUC__)
-ct=gcc
-#elif defined(_COMPILER_VERSION)
-ct=mipspro
-#elif defined(__sgi)
-ct=mipspro
-#elif defined(__hpux) || defined(__hpua)
-ct=hpcc
-#elif defined(__ultrix)
-ct=ucode
-#else
-ct=unknown
-#endif
-EOF
-ct=unknown
-vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c | grep ct= | tr -d \\\\015 >x"
-sed 's/^/[ /' x
-eval `cat x`
-rmf x vv.out
-echo 'int main(void) { return (0); }' >conftest.c
-case $ct in
-ack)
- # work around "the famous ACK const bug"
- CPPFLAGS="-Dconst= $CPPFLAGS"
- ;;
-adsp)
- echo >&2 'Warning: Analog Devices C++ compiler for Blackfin, TigerSHARC
- and SHARC (21000) DSPs detected. This compiler has not yet
- been tested for compatibility with mksh. Continue at your
- own risk, please report success/failure to the developers.'
- ;;
-bcc)
- echo >&2 "Warning: Borland C++ Builder detected. This compiler might
- produce broken executables. Continue at your own risk,
- please report success/failure to the developers."
- ;;
-clang)
- # does not work with current "ccc" compiler driver
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
- # this works, for now
- vv '|' "${CLANG-clang} -version"
- # ensure compiler and linker are in sync unless overridden
- case $CCC_CC:$CCC_LD in
- :*) ;;
- *:) CCC_LD=$CCC_CC; export CCC_LD ;;
- esac
- ;;
-dec)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS"
- ;;
-dmc)
- echo >&2 "Warning: Digital Mars Compiler detected. When running under"
- echo >&2 " UWIN, mksh tends to be unstable due to the limitations"
- echo >&2 " of this platform. Continue at your own risk,"
- echo >&2 " please report success/failure to the developers."
- ;;
-gcc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
- vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
- -dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
- $LIBS -dumpversion`'
- ;;
-hpcc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
- ;;
-iar)
- echo >&2 'Warning: IAR Systems (http://www.iar.com) compiler for embedded
- systems detected. This unsupported compiler has not yet
- been tested for compatibility with mksh. Continue at your
- own risk, please report success/failure to the developers.'
- ;;
-icc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
- ;;
-metrowerks)
- echo >&2 'Warning: Metrowerks C compiler detected. This has not yet
- been tested for compatibility with mksh. Continue at your
- own risk, please report success/failure to the developers.'
- ;;
-mipspro)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
- ;;
-msc)
- ccpr= # errorlevels are not reliable
- case $TARGET_OS in
- Interix)
- if [[ -n $C89_COMPILER ]]; then
- C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
- else
- C89_COMPILER=CL.EXE
- fi
- if [[ -n $C89_LINKER ]]; then
- C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
- else
- C89_LINKER=LINK.EXE
- fi
- vv '|' "$C89_COMPILER /HELP >&2"
- vv '|' "$C89_LINKER /LINK >&2"
- ;;
- esac
- ;;
-nwcc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
- ;;
-pcc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v"
- ;;
-pgi)
- echo >&2 'Warning: PGI detected. This unknown compiler has not yet
- been tested for compatibility with mksh. Continue at your
- own risk, please report success/failure to the developers.'
- ;;
-sdcc)
- echo >&2 'Warning: sdcc (http://sdcc.sourceforge.net), the small devices
- C compiler for embedded systems detected. This has not yet
- been tested for compatibility with mksh. Continue at your
- own risk, please report success/failure to the developers.'
- ;;
-sunpro)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
- ;;
-tcc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v"
- ;;
-tendra)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V 2>&1 | \
- fgrep -i -e version -e release"
- ;;
-ucode)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS"
- ;;
-watcom)
- echo >&2 'Warning: Watcom C Compiler detected. This compiler has not yet
- been tested for compatibility with mksh. Continue at your
- own risk, please report success/failure to the developers.'
- ;;
-xlc)
- vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion=verbose"
- vv '|' "ld -V"
- ;;
-*)
- ct=unknown
- ;;
-esac
-case $cm in
-dragonegg|llvm)
- vv '|' "llc -version"
- ;;
-esac
-$e "$bi==> which compiler seems to be used...$ao $ui$ct$ao"
-rmf conftest.c conftest.o conftest a.out* a.exe* vv.out
-
-#
-# Compiler: works as-is, with -Wno-error and -Werror
-#
-save_NOWARN=$NOWARN
-NOWARN=
-DOWARN=
-ac_flags 0 compiler_works '' 'if the compiler works'
-test 1 = $HAVE_CAN_COMPILER_WORKS || exit 1
-HAVE_COMPILER_KNOWN=0
-test $ct = unknown || HAVE_COMPILER_KNOWN=1
-if ac_ifcpp 'if 0' compiler_fails '' \
- 'if the compiler does not fail correctly'; then
- save_CFLAGS=$CFLAGS
- : ${HAVE_CAN_DELEXE=x}
- if test $ct = dmc; then
- CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE"
- ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF
- int main(void) { return (0); }
- EOF
- elif test $ct = dec; then
- CFLAGS="$CFLAGS ${ccpl}-non_shared"
- ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF
- int main(void) { return (0); }
- EOF
- else
- exit 1
- fi
- test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS
- ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF
- EOF
- test 1 = $HAVE_COMPILER_STILL_FAILS && exit 1
-fi
-if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \
- 'if this could be tcc'; then
- ct=tcc
- CPP='cpp -D__TINYC__'
-fi
-
-if test $ct = sunpro; then
- test x"$save_NOWARN" = x"" && save_NOWARN='-errwarn=%none'
- ac_flags 0 errwarnnone "$save_NOWARN"
- test 1 = $HAVE_CAN_ERRWARNNONE || save_NOWARN=
- ac_flags 0 errwarnall "-errwarn=%all"
- test 1 = $HAVE_CAN_ERRWARNALL && DOWARN="-errwarn=%all"
-elif test $ct = hpcc; then
- save_NOWARN=
- DOWARN=+We
-elif test $ct = mipspro; then
- save_NOWARN=
- DOWARN="-diag_error 1-10000"
-elif test $ct = msc; then
- save_NOWARN="${ccpc}/w"
- DOWARN="${ccpc}/WX"
-elif test $ct = dmc; then
- save_NOWARN="${ccpc}-w"
- DOWARN="${ccpc}-wx"
-elif test $ct = bcc; then
- save_NOWARN="${ccpc}-w"
- DOWARN="${ccpc}-w!"
-elif test $ct = dec; then
- : -msg_* flags not used yet, or is -w2 correct?
-elif test $ct = xlc; then
- save_NOWARN=-qflag=i:e
- DOWARN=-qflag=i:i
-elif test $ct = tendra; then
- save_NOWARN=-w
-elif test $ct = ucode; then
- save_NOWARN=
- DOWARN=-w2
-else
- test x"$save_NOWARN" = x"" && save_NOWARN=-Wno-error
- ac_flags 0 wnoerror "$save_NOWARN"
- test 1 = $HAVE_CAN_WNOERROR || save_NOWARN=
- ac_flags 0 werror -Werror
- test 1 = $HAVE_CAN_WERROR && DOWARN=-Werror
-fi
-
-test $ct = icc && DOWARN="$DOWARN -wd1419"
-NOWARN=$save_NOWARN
-
-#
-# Compiler: extra flags (-O2 -f* -W* etc.)
-#
-i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln`
-# optimisation: only if orig_CFLAGS is empty
-test x"$i" = x"" && if test $ct = sunpro; then
- cat >x <<-'EOF'
- int main(void) { return (0); }
- #define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p
- #define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p)
- #define pad void __IDSTRING_EXPAND(__LINE__,x)(void) { }
- EOF
- yes pad | head -n 256 >>x
- ac_flags - 1 otwo -xO2 <x
- rmf x
-elif test $ct = hpcc; then
- phase=u
- ac_flags 1 otwo +O2
- phase=x
-elif test $ct = xlc; then
- ac_flags 1 othree "-O3 -qstrict"
- test 1 = $HAVE_CAN_OTHREE || ac_flags 1 otwo -O2
-elif test $ct = tcc || test $ct = tendra; then
- : no special optimisation
-else
- ac_flags 1 otwo -O2
- test 1 = $HAVE_CAN_OTWO || ac_flags 1 optimise -O
-fi
-# other flags: just add them if they are supported
-i=0
-if test $ct = gcc; then
- # The following tests run with -Werror (gcc only) if possible
- NOWARN=$DOWARN; phase=u
- ac_flags 1 fnostrictaliasing -fno-strict-aliasing
- ac_flags 1 fstackprotectorall -fstack-protector-all
- ac_flags 1 fwrapv -fwrapv
- test $cm = combine && ac_flags 0 combine \
- '-fwhole-program --combine' \
- 'if gcc supports -fwhole-program --combine'
- i=1
-elif test $ct = icc; then
- ac_flags 1 fnobuiltinsetmode -fno-builtin-setmode
- ac_flags 1 fnostrictaliasing -fno-strict-aliasing
- ac_flags 1 fstacksecuritycheck -fstack-security-check
- i=1
-elif test $ct = sunpro; then
- phase=u
- ac_flags 1 v -v
- ac_flags 1 xc99 -xc99 'for support of ISO C99'
- ac_flags 1 ipo -xipo 'for cross-module optimisation'
- phase=x
-elif test $ct = hpcc; then
- phase=u
- ac_flags 1 agcc -Agcc 'for support of GCC extensions'
- ac_flags 1 ac99 -AC99 'for support of ISO C99'
- phase=x
-elif test $ct = dec; then
- ac_flags 0 verb -verbose
- ac_flags 1 rodata -readonly_strings
-elif test $ct = dmc; then
- ac_flags 1 decl "${ccpc}-r" 'for strict prototype checks'
- ac_flags 1 schk "${ccpc}-s" 'for stack overflow checking'
-elif test $ct = bcc; then
- ac_flags 1 strpool "${ccpc}-d" 'if string pooling can be enabled'
-elif test $ct = mipspro; then
- ac_flags 1 xc99 -c99 'for support of ISO C99'
- ac_flags 1 fullwarn -fullwarn 'for remark output support'
-elif test $ct = msc; then
- ac_flags 1 strpool "${ccpc}/GF" 'if string pooling can be enabled'
- echo 'int main(void) { char test[64] = ""; return (*test); }' >x
- ac_flags - 1 stackon "${ccpc}/GZ" 'if stack checks can be enabled' <x
- ac_flags - 1 stckall "${ccpc}/Ge" 'stack checks for all functions' <x
- ac_flags - 1 secuchk "${ccpc}/GS" 'for compiler security checks' <x
- rmf x
- ac_flags 1 wall "${ccpc}/Wall" 'to enable all warnings'
- ac_flags 1 wp64 "${ccpc}/Wp64" 'to enable 64-bit warnings'
-elif test $ct = xlc; then
- ac_flags 1 x99 -qlanglvl=extc99
- test 1 = $HAVE_CAN_X99 || ac_flags 1 c99 -qlanglvl=stdc99
- ac_flags 1 rodata "-qro -qroconst -qroptr"
- ac_flags 1 rtcheck -qcheck=all
- ac_flags 1 rtchkc -qextchk
- ac_flags 1 wformat "-qformat=all -qformat=nozln"
- #ac_flags 1 wp64 -qwarn64 # too verbose for now
-elif test $ct = tendra; then
- ac_flags 0 ysystem -Ysystem
- test 1 = $HAVE_CAN_YSYSTEM && CPPFLAGS="-Ysystem $CPPFLAGS"
- ac_flags 1 extansi -Xa
-elif test $ct = tcc; then
- ac_flags 1 boundschk -b
-elif test $ct = clang; then
- i=1
-elif test $ct = nwcc; then
- i=1
- #broken# ac_flags 1 ssp -stackprotect
-fi
-# flags common to a subset of compilers (run with -Werror on gcc)
-if test 1 = $i; then
- ac_flags 1 stdg99 -std=gnu99 'for support of ISO C99 + GCC extensions'
- test 1 = $HAVE_CAN_STDG99 || \
- ac_flags 1 stdc99 -std=c99 'for support of ISO C99'
- ac_flags 1 wall -Wall
-fi
-phase=x
-
-# The following tests run with -Werror or similar (all compilers) if possible
-NOWARN=$DOWARN
-test $ct = pcc && phase=u
-
-#
-# Compiler: check for stuff that only generates warnings
-#
-ac_test attribute_bounded '' 'for __attribute__((bounded))' <<-'EOF'
- #if defined(__GNUC__) && (__GNUC__ < 2)
- /* force a failure: gcc 1.42 has a false positive here */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- #include <string.h>
- #undef __attribute__
- int xcopy(const void *, void *, size_t)
- __attribute__((bounded (buffer, 1, 3)))
- __attribute__((bounded (buffer, 2, 3)));
- int main(int ac, char *av[]) { return (xcopy(av[0], av[--ac], 1)); }
- int xcopy(const void *s, void *d, size_t n) {
- memmove(d, s, n); return ((int)n);
- }
- #endif
-EOF
-ac_test attribute_format '' 'for __attribute__((format))' <<-'EOF'
- #if defined(__GNUC__) && (__GNUC__ < 2)
- /* force a failure: gcc 1.42 has a false positive here */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- #include <stdio.h>
- #undef __attribute__
- #undef printf
- extern int printf(const char *format, ...)
- __attribute__((format (printf, 1, 2)));
- int main(int ac, char **av) { return (printf("%s%d", *av, ac)); }
- #endif
-EOF
-ac_test attribute_nonnull '' 'for __attribute__((nonnull))' <<-'EOF'
- #if defined(__GNUC__) && (__GNUC__ < 2)
- /* force a failure: gcc 1.42 has a false positive here */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- int foo(char *s1, char *s2) __attribute__((nonnull));
- int bar(char *s1, char *s2) __attribute__((nonnull (1, 2)));
- int baz(char *s) __attribute__((nonnull (1)));
- int foo(char *s1, char *s2) { return (bar(s2, s1)); }
- int bar(char *s1, char *s2) { return (baz(s1) - baz(s2)); }
- int baz(char *s) { return (*s); }
- int main(int ac, char **av) { return (ac == foo(av[0], av[ac-1])); }
- #endif
-EOF
-ac_test attribute_noreturn '' 'for __attribute__((noreturn))' <<-'EOF'
- #if defined(__GNUC__) && (__GNUC__ < 2)
- /* force a failure: gcc 1.42 has a false positive here */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- #include <stdlib.h>
- #undef __attribute__
- void fnord(void) __attribute__((noreturn));
- int main(void) { fnord(); }
- void fnord(void) { exit(0); }
- #endif
-EOF
-ac_test attribute_unused '' 'for __attribute__((unused))' <<-'EOF'
- #if defined(__GNUC__) && (__GNUC__ < 2)
- /* force a failure: gcc 1.42 has a false positive here */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- int main(int ac __attribute__((unused)), char **av
- __attribute__((unused))) { return (0); }
- #endif
-EOF
-ac_test attribute_used '' 'for __attribute__((used))' <<-'EOF'
- #if defined(__GNUC__) && (__GNUC__ < 2)
- /* force a failure: gcc 1.42 has a false positive here */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- static const char fnord[] __attribute__((used)) = "42";
- int main(void) { return (0); }
- #endif
-EOF
-
-# End of tests run with -Werror
-NOWARN=$save_NOWARN
-phase=x
-
-#
-# mksh: flavours (full/small mksh, omit certain stuff)
-#
-if ac_ifcpp 'ifdef MKSH_SMALL' isset_MKSH_SMALL '' \
- "if a reduced-feature mksh is requested"; then
- #XXX this sucks; fix it for *all* compilers
- case $ct in
- clang|icc|nwcc)
- ac_flags 1 fnoinline -fno-inline
- ;;
- gcc)
- NOWARN=$DOWARN; phase=u
- ac_flags 1 fnoinline -fno-inline
- NOWARN=$save_NOWARN; phase=x
- ;;
- sunpro)
- ac_flags 1 fnoinline -xinline=
- ;;
- xlc)
- ac_flags 1 fnoinline -qnoinline
- ;;
- esac
-
- : ${HAVE_MKNOD=0}
- : ${HAVE_NICE=0}
- : ${HAVE_REVOKE=0}
- : ${HAVE_PERSISTENT_HISTORY=0}
- check_categories=$check_categories,smksh
- HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1 # from sh.h
-fi
-ac_ifcpp 'ifdef MKSH_BINSHREDUCED' isset_MKSH_BINSHREDUCED '' \
- "if a reduced-feature sh is requested" && \
- check_categories=$check_categories,binsh
-ac_ifcpp 'ifdef MKSH_UNEMPLOYED' isset_MKSH_UNEMPLOYED '' \
- "if mksh will be built without job control" && \
- check_categories=$check_categories,arge
-ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \
- 'if the default UTF-8 mode is specified' && : ${HAVE_SETLOCALE_CTYPE=0}
-ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
- 'if traditional/conservative fd use is requested' && \
- check_categories=$check_categories,convfds
-
-#
-# Environment: headers
-#
-ac_header sys/param.h
-ac_header sys/mkdev.h sys/types.h
-ac_header sys/mman.h sys/types.h
-ac_header sys/sysmacros.h
-ac_header grp.h sys/types.h
-ac_header libgen.h
-ac_header libutil.h sys/types.h
-ac_header paths.h
-ac_header stdbool.h
-ac_header stdint.h stdarg.h
-ac_header strings.h sys/types.h
-ac_header ulimit.h sys/types.h
-ac_header values.h
-
-#
-# Environment: definitions
-#
-echo '#include <sys/types.h>
-/* check that off_t can represent 2^63-1 correctly, thx FSF */
-#define LARGE_OFF_T (((off_t)1 << 62) - 1 + ((off_t)1 << 62))
-int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 &&
- LARGE_OFF_T % 2147483647 == 1) ? 1 : -1];
-int main(void) { return (0); }' >lft.c
-ac_testn can_lfs '' "for large file support" <lft.c
-save_CPPFLAGS=$CPPFLAGS
-CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64"
-ac_testn can_lfs_sus '!' can_lfs 0 "... with -D_FILE_OFFSET_BITS=64" <lft.c
-if test 0 = $HAVE_CAN_LFS_SUS; then
- CPPFLAGS="$save_CPPFLAGS -D_LARGE_FILES=1"
- ac_testn can_lfs_aix '!' can_lfs 0 "... with -D_LARGE_FILES=1" <lft.c
- test 1 = $HAVE_CAN_LFS_AIX || CPPFLAGS=$save_CPPFLAGS
-fi
-rmf lft* # end of large file support test
-
-#
-# Environment: types
-#
-ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF'
- #include <sys/types.h>
- #include <stddef.h>
- int main(int ac, char **av) { return ((uint32_t)(ptrdiff_t)*av + (int32_t)ac); }
-EOF
-ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF'
- #include <sys/types.h>
- #include <stddef.h>
- int main(int ac, char **av) { return ((u_int32_t)(ptrdiff_t)*av + (int32_t)ac); }
-EOF
-ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF'
- #include <sys/types.h>
- #include <stddef.h>
- int main(int ac, char **av) { return ((uint8_t)(ptrdiff_t)av[ac]); }
-EOF
-ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF'
- #include <sys/types.h>
- #include <stddef.h>
- int main(int ac, char **av) { return ((u_int8_t)(ptrdiff_t)av[ac]); }
-EOF
-
-ac_test rlim_t <<-'EOF'
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/resource.h>
- #include <unistd.h>
- int main(void) { return ((int)(rlim_t)0); }
-EOF
-
-# only testn: added later below
-ac_testn sig_t <<-'EOF'
- #include <sys/types.h>
- #include <signal.h>
- #include <stddef.h>
- int main(void) { return ((int)(ptrdiff_t)(sig_t)kill(0,0)); }
-EOF
-
-ac_testn sighandler_t '!' sig_t 0 <<-'EOF'
- #include <sys/types.h>
- #include <signal.h>
- #include <stddef.h>
- int main(void) { return ((int)(ptrdiff_t)(sighandler_t)kill(0,0)); }
-EOF
-if test 1 = $HAVE_SIGHANDLER_T; then
- CPPFLAGS="$CPPFLAGS -Dsig_t=sighandler_t"
- HAVE_SIG_T=1
-fi
-
-ac_testn __sighandler_t '!' sig_t 0 <<-'EOF'
- #include <sys/types.h>
- #include <signal.h>
- #include <stddef.h>
- int main(void) { return ((int)(ptrdiff_t)(__sighandler_t)kill(0,0)); }
-EOF
-if test 1 = $HAVE___SIGHANDLER_T; then
- CPPFLAGS="$CPPFLAGS -Dsig_t=__sighandler_t"
- HAVE_SIG_T=1
-fi
-
-test 1 = $HAVE_SIG_T || CPPFLAGS="$CPPFLAGS -Dsig_t=nosig_t"
-ac_cppflags SIG_T
-
-#
-# Environment: signals
-#
-test x"NetBSD" = x"$TARGET_OS" && $e Ignore the compatibility warning.
-
-for what in name list; do
- uwhat=`upper $what`
- ac_testn sys_sig$what '' "the sys_sig${what}[] array" <<-EOF
- extern const char *const sys_sig${what}[];
- int main(void) { return (sys_sig${what}[0][0]); }
- EOF
- ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig${what}[] array" <<-EOF
- extern const char *const _sys_sig${what}[];
- int main(void) { return (_sys_sig${what}[0][0]); }
- EOF
- if eval "test 1 = \$HAVE__SYS_SIG$uwhat"; then
- CPPFLAGS="$CPPFLAGS -Dsys_sig$what=_sys_sig$what"
- eval "HAVE_SYS_SIG$uwhat=1"
- fi
- ac_cppflags SYS_SIG$uwhat
-done
-
-ac_test strsignal '!' sys_siglist 0 <<-'EOF'
- #include <string.h>
- #include <signal.h>
- int main(void) { return (strsignal(1)[0]); }
-EOF
-
-#
-# Environment: library functions
-#
-ac_testn flock_ex '' 'flock and mmap' <<-'EOF'
- #include <sys/types.h>
- #include <sys/file.h>
- #include <sys/mman.h>
- #include <fcntl.h>
- #include <stdlib.h>
- int main(void) { return ((void *)mmap(NULL, (size_t)flock(0, LOCK_EX),
- PROT_READ, MAP_PRIVATE, 0, (off_t)0) == (void *)NULL ? 1 :
- munmap(NULL, 0)); }
-EOF
-
-ac_test getrusage <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- int main(void) {
- struct rusage ru;
- return (getrusage(RUSAGE_SELF, &ru) +
- getrusage(RUSAGE_CHILDREN, &ru));
- }
-EOF
-
-ac_test killpg <<-'EOF'
- #include <signal.h>
- int main(int ac, char *av[]) { return (av[0][killpg(123, ac)]); }
-EOF
-
-ac_test mknod '' 'if to use mknod(), makedev() and friends' <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- int main(int ac, char *av[]) {
- dev_t dv;
- dv = makedev((unsigned int)ac, (unsigned int)av[0][0]);
- return (mknod(av[0], (mode_t)0, dv) ? (int)major(dv) :
- (int)minor(dv));
- }
-EOF
-
-ac_test mkstemp <<-'EOF'
- #include <stdlib.h>
- #include <unistd.h>
- int main(void) { char tmpl[] = "X"; return (mkstemp(tmpl)); }
-EOF
-
-ac_test nice <<-'EOF'
- #include <unistd.h>
- int main(void) { return (nice(4)); }
-EOF
-
-ac_test revoke <<-'EOF'
- #include <sys/types.h>
- #if HAVE_LIBUTIL_H
- #include <libutil.h>
- #endif
- #include <unistd.h>
- int main(int ac, char *av[]) { return (ac + revoke(av[0])); }
-EOF
-
-ac_test setlocale_ctype '' 'setlocale(LC_CTYPE, "")' <<-'EOF'
- #include <locale.h>
- #include <stddef.h>
- int main(void) { return ((int)(ptrdiff_t)(void *)setlocale(LC_CTYPE, "")); }
-EOF
-
-ac_test langinfo_codeset setlocale_ctype 0 'nl_langinfo(CODESET)' <<-'EOF'
- #include <langinfo.h>
- #include <stddef.h>
- int main(void) { return ((int)(ptrdiff_t)(void *)nl_langinfo(CODESET)); }
-EOF
-
-ac_test setmode mknod 1 <<-'EOF'
- /* XXX imake style */
- /* XXX conditions correct? */
- #if defined(__MSVCRT__) || defined(__CYGWIN__)
- /* force a failure: Win32 setmode() is not what we want... */
- int main(void) { return (thiswillneverbedefinedIhope()); }
- #else
- #include <sys/types.h>
- #include <unistd.h>
- int main(int ac, char *av[]) { return (getmode(setmode(av[0]),
- (mode_t)ac)); }
- #endif
-EOF
-
-ac_test setresugid <<-'EOF'
- #include <sys/types.h>
- #include <unistd.h>
- int main(void) { setresuid(0,0,0); return (setresgid(0,0,0)); }
-EOF
-
-ac_test setgroups setresugid 0 <<-'EOF'
- #include <sys/types.h>
- #if HAVE_GRP_H
- #include <grp.h>
- #endif
- #include <unistd.h>
- int main(void) { gid_t gid = 0; return (setgroups(0, &gid)); }
-EOF
-
-ac_test strcasestr <<-'EOF'
- #include <sys/types.h>
- #include <stddef.h>
- #include <string.h>
- #if HAVE_STRINGS_H
- #include <strings.h>
- #endif
- int main(int ac, char *av[]) {
- return ((int)(ptrdiff_t)(void *)strcasestr(*av, av[ac]));
- }
-EOF
-
-ac_test strlcpy <<-'EOF'
- #include <string.h>
- int main(int ac, char *av[]) { return (strlcpy(*av, av[1],
- (size_t)ac)); }
-EOF
-
-#
-# check headers for declarations
-#
-save_CC=$CC; save_LDFLAGS=$LDFLAGS; save_LIBS=$LIBS
-CC="$CC -c -o $tcfn"; LDFLAGS=; LIBS=
-ac_test '!' flock_decl flock_ex 1 'if flock() does not need to be declared' <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- long flock(void); /* this clashes if defined before */
- int main(void) { return ((int)flock()); }
-EOF
-ac_test '!' revoke_decl revoke 1 'if revoke() does not need to be declared' <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- long revoke(void); /* this clashes if defined before */
- int main(void) { return ((int)revoke()); }
-EOF
-ac_test sys_siglist_decl sys_siglist 1 'if sys_siglist[] does not need to be declared' <<-'EOF'
- #define MKSH_INCLUDES_ONLY
- #include "sh.h"
- int main(void) { return (sys_siglist[0][0]); }
-EOF
-CC=$save_CC; LDFLAGS=$save_LDFLAGS; LIBS=$save_LIBS
-
-#
-# other checks
-#
-fd='if to use persistent history'
-ac_cache PERSISTENT_HISTORY || test 0 = $HAVE_FLOCK_EX || fv=1
-test 1 = $fv || check_categories=$check_categories,no-histfile
-ac_testdone
-ac_cppflags
-
-#
-# Compiler: Praeprocessor (only if needed)
-#
-test 0 = $HAVE_SYS_SIGNAME && if ac_testinit cpp_dd '' \
- 'checking if the C Preprocessor supports -dD'; then
- echo '#define foo bar' >conftest.c
- vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c >x"
- grep '#define foo bar' x >/dev/null 2>&1 && fv=1
- rmf conftest.c x vv.out
- ac_testdone
-fi
-
-#
-# End of mirtoconf checks
-#
-$e ... done.
-
-# Some operating systems have ancient versions of ed(1) writing
-# the character count to standard output; cope for that
-echo wq >x
-ed x <x 2>/dev/null | grep 3 >/dev/null 2>&1 && \
- check_categories=$check_categories,$oldish_ed
-rmf x vv.out
-
-if test 0 = $HAVE_SYS_SIGNAME; then
- if test 1 = $HAVE_CPP_DD; then
- $e Generating list of signal names...
- else
- $e No list of signal names available via cpp. Falling back...
- fi
- sigseen=:
- echo '#include <signal.h>
-#ifndef NSIG
-#if defined(_NSIG)
-#define NSIG _NSIG
-#elif defined(SIGMAX)
-#define NSIG (SIGMAX+1)
-#endif
-#endif
-mksh_cfg: NSIG' >conftest.c
- NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
- grep mksh_cfg: | sed 's/^mksh_cfg:[ ]*\([0-9x ()+-]*\).*$/\1/'`
- case $NSIG in
- *[\ \(\)+-]*) NSIG=`awk "BEGIN { print $NSIG }"` ;;
- esac
- printf=printf
- (printf hallo) >/dev/null 2>&1 || printf=echo
- test $printf = echo || NSIG=`printf %d "$NSIG" 2>/dev/null`
- $printf "NSIG=$NSIG ... "
- sigs="ABRT ALRM BUS CHLD CLD CONT DIL EMT FPE HUP ILL INFO INT IO IOT"
- sigs="$sigs KILL LOST PIPE PROF PWR QUIT RESV SAK SEGV STOP SYS TERM"
- sigs="$sigs TRAP TSTP TTIN TTOU URG USR1 USR2 VTALRM WINCH XCPU XFSZ"
- test 1 = $HAVE_CPP_DD && test $NSIG -gt 1 && sigs="$sigs "`vq \
- "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c" | \
- grep '[ ]SIG[A-Z0-9]*[ ]' | \
- sed 's/^\(.*[ ]SIG\)\([A-Z0-9]*\)\([ ].*\)$/\2/' | sort`
- test $NSIG -gt 1 || sigs=
- for name in $sigs; do
- echo '#include <signal.h>' >conftest.c
- echo mksh_cfg: SIG$name >>conftest.c
- vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
- grep mksh_cfg: | \
- sed 's/^mksh_cfg:[ ]*\([0-9x]*\).*$/\1:'$name/
- done | grep -v '^:' | while IFS=: read nr name; do
- test $printf = echo || nr=`printf %d "$nr" 2>/dev/null`
- test $nr -gt 0 && test $nr -le $NSIG || continue
- case $sigseen in
- *:$nr:*) ;;
- *) echo " { \"$name\", $nr },"
- sigseen=$sigseen$nr:
- $printf "$name=$nr " >&2
- ;;
- esac
- done 2>&1 >signames.inc
- rmf conftest.c
- $e done.
-fi
-
-addsrcs '!' HAVE_SETMODE setmode.c
-addsrcs '!' HAVE_STRLCPY strlcpy.c
-addsrcs USE_PRINTF_BUILTIN printf.c
-test 1 = "$USE_PRINTF_BUILTIN" && CPPFLAGS="$CPPFLAGS -DMKSH_PRINTF_BUILTIN"
-test 0 = "$HAVE_SETMODE" && CPPFLAGS="$CPPFLAGS -DHAVE_CONFIG_H -DCONFIG_H_FILENAME=\\\"sh.h\\\""
-test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
-
-$e $bi$me: Finished configuration testing, now producing output.$ao
-
-files=
-objs=
-sp=
-case $curdir in
-*\ *) echo "#!./mksh" >test.sh ;;
-*) echo "#!$curdir/mksh" >test.sh ;;
-esac
-cat >>test.sh <<-EOF
- LC_ALL=C PATH='$PATH'; export LC_ALL PATH
- test -n "\$KSH_VERSION" || exit 1
- check_categories=$check_categories
- print Testing mksh for conformance:
- fgrep MirOS: '$srcdir/check.t'
- fgrep MIRBSD '$srcdir/check.t'
- print "This shell is actually:\\n\\t\$KSH_VERSION"
- print 'test.sh built for mksh $dstversion'
- cstr='\$os = defined \$^O ? \$^O : "unknown";'
- cstr="\$cstr"'print \$os . ", Perl version " . \$];'
- for perli in \$PERL perl5 perl no; do
- [[ \$perli = no ]] && exit 1
- perlos=\$(\$perli -e "\$cstr") 2>&- || continue
- print "Perl interpreter '\$perli' running on '\$perlos'"
- [[ -n \$perlos ]] && break
- done
- exec \$perli '$srcdir/check.pl' -s '$srcdir/check.t' -p '$curdir/mksh' \${check_categories:+-C} \${check_categories#,} \$*$tsts
-EOF
-chmod 755 test.sh
-test $HAVE_CAN_COMBINE$cm = 0combine && cm=normal
-if test $cm = llvm; then
- emitbc="-emit-llvm -c"
-elif test $cm = dragonegg; then
- emitbc="-S -flto"
-else
- emitbc=-c
-fi
-echo set -x >Rebuild.sh
-for file in $SRCS; do
- op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
- test -f $file || file=$srcdir/$file
- files="$files$sp$file"
- sp=' '
- echo "$CC $CFLAGS $CPPFLAGS $emitbc $file || exit 1" >>Rebuild.sh
- if test $cm = dragonegg; then
- echo "mv ${op}s ${op}ll" >>Rebuild.sh
- echo "llvm-as ${op}ll || exit 1" >>Rebuild.sh
- objs="$objs$sp${op}bc"
- else
- objs="$objs$sp${op}o"
- fi
-done
-case $cm in
-dragonegg|llvm)
- echo "rm -f mksh.s" >>Rebuild.sh
- echo "llvm-link -o - $objs | opt $optflags | llc -o mksh.s" >>Rebuild.sh
- lobjs=mksh.s
- ;;
-*)
- lobjs=$objs
- ;;
-esac
-case $tcfn in
-a.exe) mkshexe=mksh.exe ;;
-*) mkshexe=mksh ;;
-esac
-echo tcfn=$mkshexe >>Rebuild.sh
-echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
-echo 'test -f $tcfn || exit 1; size $tcfn' >>Rebuild.sh
-if test $cm = makefile; then
- extras='emacsfn.h sh.h sh_flags.h var_spec.h'
- test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
- cat >Makefrag.inc <<EOF
-# Makefile fragment for building mksh $dstversion
-
-PROG= $mkshexe
-MAN= mksh.1
-SRCS= $SRCS
-SRCS_FP= $files
-OBJS_BP= $objs
-INDSRCS= $extras
-NONSRCS_INST= dot.mkshrc \$(MAN)
-NONSRCS_NOINST= Build.sh Makefile Rebuild.sh check.pl check.t test.sh
-CC= $CC
-CFLAGS= $CFLAGS
-CPPFLAGS= $CPPFLAGS
-LDFLAGS= $LDFLAGS
-LIBS= $LIBS
-
-# not BSD make only:
-#VPATH= $srcdir
-#all: \$(PROG)
-#\$(PROG): \$(OBJS_BP)
-# \$(CC) \$(CFLAGS) \$(LDFLAGS) -o \$@ \$(OBJS_BP) \$(LIBS)
-#\$(OBJS_BP): \$(SRCS_FP) \$(NONSRCS)
-#.c.o:
-# \$(CC) \$(CFLAGS) \$(CPPFLAGS) -c \$<
-
-# for all make variants:
-#REGRESS_FLAGS= -v
-#regress:
-# ./test.sh \$(REGRESS_FLAGS)
-
-# for BSD make only:
-#.PATH: $srcdir
-#.include <bsd.prog.mk>
-EOF
- $e
- $e Generated Makefrag.inc successfully.
- exit 0
-fi
-if test $cm = combine; then
- objs="-o $mkshexe"
- for file in $SRCS; do
- test -f $file || file=$srcdir/$file
- objs="$objs $file"
- done
- emitbc="-fwhole-program --combine"
- v "$CC $CFLAGS $CPPFLAGS $LDFLAGS $emitbc $objs $LIBS $ccpr"
-elif test 1 = $pm; then
- for file in $SRCS; do
- test -f $file || file=$srcdir/$file
- v "$CC $CFLAGS $CPPFLAGS $emitbc $file" &
- done
- wait
-else
- for file in $SRCS; do
- test $cm = dragonegg && \
- op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
- test -f $file || file=$srcdir/$file
- v "$CC $CFLAGS $CPPFLAGS $emitbc $file" || exit 1
- if test $cm = dragonegg; then
- v "mv ${op}s ${op}ll"
- v "llvm-as ${op}ll" || exit 1
- fi
- done
-fi
-case $cm in
-dragonegg|llvm)
- rmf mksh.s
- v "llvm-link -o - $objs | opt $optflags | llc -o mksh.s"
- ;;
-esac
-tcfn=$mkshexe
-test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
-test -f $tcfn || exit 1
-test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >mksh.cat1" || \
- rmf mksh.cat1
-test 0 = $eq && v size $tcfn
-i=install
-test -f /usr/ucb/$i && i=/usr/ucb/$i
-test 1 = $eq && e=:
-$e
-$e Installing the shell:
-$e "# $i -c -s -o root -g bin -m 555 mksh /bin/mksh"
-$e "# grep -x /bin/mksh /etc/shells >/dev/null || echo /bin/mksh >>/etc/shells"
-$e "# $i -c -o root -g bin -m 444 dot.mkshrc /usr/share/doc/mksh/examples/"
-$e
-$e Installing the manual:
-if test -f mksh.cat1; then
- $e "# $i -c -o root -g bin -m 444 mksh.cat1" \
- "/usr/share/man/cat1/mksh.0"
- $e or
-fi
-$e "# $i -c -o root -g bin -m 444 mksh.1 /usr/share/man/man1/mksh.1"
-$e
-$e Run the regression test suite: ./test.sh
-$e Please also read the sample file dot.mkshrc and the fine manual.
-exit 0
diff --git a/mksh/src/check.pl b/mksh/src/check.pl
deleted file mode 100644
index e793e95..0000000
--- a/mksh/src/check.pl
+++ /dev/null
@@ -1,1241 +0,0 @@
-# $MirOS: src/bin/mksh/check.pl,v 1.23 2009/06/10 18:12:43 tg Rel $
-# $OpenBSD: th,v 1.13 2006/05/18 21:27:23 miod Exp $
-#-
-# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
-# 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.
-#-
-# Example test:
-# name: a-test
-# description:
-# a test to show how tests are done
-# arguments: !-x!-f!
-# stdin:
-# echo -n *
-# false
-# expected-stdout: !
-# *
-# expected-stderr:
-# + echo -n *
-# + false
-# expected-exit: 1
-# ---
-# This runs the test-program (eg, mksh) with the arguments -x and -f,
-# standard input is a file containing "echo hi*\nfalse\n". The program
-# is expected to produce "hi*" (no trailing newline) on standard output,
-# "+ echo hi*\n+false\n" on standard error, and an exit code of 1.
-#
-#
-# Format of test files:
-# - blank lines and lines starting with # are ignored
-# - a test file contains a series of tests
-# - a test is a series of tag:value pairs ended with a "---" line
-# (leading/trailing spaces are stripped from the first line of value)
-# - test tags are:
-# Tag Flag Description
-# ----- ---- -----------
-# name r The name of the test; should be unique
-# description m What test does
-# arguments M Arguments to pass to the program;
-# default is no arguments.
-# script m Value is written to a file which
-# is passed as an argument to the program
-# (after the arguments arguments)
-# stdin m Value is written to a file which is
-# used as standard-input for the program;
-# default is to use /dev/null.
-# perl-setup m Value is a perl script which is executed
-# just before the test is run. Try to
-# avoid using this...
-# perl-cleanup m Value is a perl script which is executed
-# just after the test is run. Try to
-# avoid using this...
-# env-setup M Value is a list of NAME=VALUE elements
-# which are put in the environment before
-# the test is run. If the =VALUE is
-# missing, NAME is removed from the
-# environment. Programs are run with
-# the following minimal environment:
-# HOME, LD_LIBRARY_PATH, LOCPATH,
-# LOGNAME, PATH, SHELL, USER
-# (values taken from the environment of
-# the test harness).
-# ENV is set to /nonexistant.
-# __progname is set to the -p argument.
-# __perlname is set to $^X (perlexe).
-# file-setup mps Used to create files, directories
-# and symlinks. First word is either
-# file, dir or symlink; second word is
-# permissions; this is followed by a
-# quoted word that is the name of the
-# file; the end-quote should be followed
-# by a newline, then the file data
-# (if any). The first word may be
-# preceded by a ! to strip the trailing
-# newline in a symlink.
-# file-result mps Used to verify a file, symlink or
-# directory is created correctly.
-# The first word is either
-# file, dir or symlink; second word is
-# expected permissions; third word
-# is user-id; fourth is group-id;
-# fifth is "exact" or "pattern"
-# indicating whether the file contents
-# which follow is to be matched exactly
-# or if it is a regular expression.
-# The fifth argument is the quoted name
-# of the file that should be created.
-# The end-quote should be followed
-# by a newline, then the file data
-# (if any). The first word may be
-# preceded by a ! to strip the trailing
-# newline in the file contents.
-# The permissions, user and group fields
-# may be * meaning accept any value.
-# time-limit Time limit - the program is sent a
-# SIGKILL N seconds. Default is no
-# limit.
-# expected-fail 'yes' if the test is expected to fail.
-# expected-exit expected exit code. Can be a number,
-# or a C expression using the variables
-# e, s and w (exit code, termination
-# signal, and status code).
-# expected-stdout m What the test should generate on stdout;
-# default is to expect no output.
-# expected-stdout-pattern m A perl pattern which matches the
-# expected output.
-# expected-stderr m What the test should generate on stderr;
-# default is to expect no output.
-# expected-stderr-pattern m A perl pattern which matches the
-# expected standard error.
-# category m Specify a comma separated list of
-# 'categories' of program that the test
-# is to be run for. A category can be
-# negated by prefixing the name with a !.
-# The idea is that some tests in a
-# test suite may apply to a particular
-# program version and shouldn't be run
-# on other versions. The category(s) of
-# the program being tested can be
-# specified on the command line.
-# One category os:XXX is predefined
-# (XXX is the operating system name,
-# eg, linux, dec_osf).
-# Flag meanings:
-# r tag is required (eg, a test must have a name tag).
-# m value can be multiple lines. Lines must be prefixed with
-# a tab. If the value part of the initial tag:value line is
-# - empty: the initial blank line is stripped.
-# - a lone !: the last newline in the value is stripped;
-# M value can be multiple lines (prefixed by a tab) and consists
-# of multiple fields, delimited by a field separator character.
-# The value must start and end with the f-s-c.
-# p tag takes parameters (used with m).
-# s tag can be used several times.
-
-use POSIX qw(EINTR);
-use Getopt::Std;
-use Config;
-
-$os = defined $^O ? $^O : 'unknown';
-
-($prog = $0) =~ s#.*/##;
-
-$Usage = <<EOF ;
-Usage: $prog [-s test-set] [-C category] [-p prog] [-v] [-e e=v] name ...
- -p p Use p as the program to test
- -C c Specify the comma separated list of categories the program
- belongs to (see category field).
- -s s Read tests from file s; if s is a directory, it is recursively
- scaned for test files (which end in .t).
- -t t Use t as default time limit for tests (default is unlimited)
- -P program (-p) string has multiple words, and the program is in
- the path (kludge option)
- -v Verbose mode: print reason test failed.
- -e e=v Set the environment variable e to v for all tests
- (if no =v is given, the current value is used)
- Only one -e option can be given at the moment, sadly.
- name specifies the name of the test(s) to run; if none are
- specified, all tests are run.
-EOF
-
-# See comment above for flag meanings
-%test_fields = (
- 'name', 'r',
- 'description', 'm',
- 'arguments', 'M',
- 'script', 'm',
- 'stdin', 'm',
- 'perl-setup', 'm',
- 'perl-cleanup', 'm',
- 'env-setup', 'M',
- 'file-setup', 'mps',
- 'file-result', 'mps',
- 'time-limit', '',
- 'expected-fail', '',
- 'expected-exit', '',
- 'expected-stdout', 'm',
- 'expected-stdout-pattern', 'm',
- 'expected-stderr', 'm',
- 'expected-stderr-pattern', 'm',
- 'category', 'm',
- );
-# Filled in by read_test()
-%internal_test_fields = (
- ':full-name', 1, # file:name
- ':long-name', 1, # dir/file:lineno:name
- );
-
-# Categories of the program under test. Provide the current
-# os by default.
-%categories = (
- "os:$os", '1'
- );
-
-$temps = "/tmp/rts$$";
-$tempi = "/tmp/rti$$";
-$tempo = "/tmp/rto$$";
-$tempe = "/tmp/rte$$";
-$tempdir = "/tmp/rtd$$";
-
-$nfailed = 0;
-$nxfailed = 0;
-$npassed = 0;
-$nxpassed = 0;
-
-%known_tests = ();
-
-if (!getopts('C:p:Ps:t:ve:')) {
- print STDERR $Usage;
- exit 1;
-}
-
-die "$prog: no program specified (use -p)\n" if !defined $opt_p;
-die "$prog: no test set specified (use -s)\n" if !defined $opt_s;
-$test_prog = $opt_p;
-$verbose = defined $opt_v && $opt_v;
-$test_set = $opt_s;
-if (defined $opt_t) {
- die "$prog: bad -t argument (should be number > 0): $opt_t\n"
- if $opt_t !~ /^\d+$/ || $opt_t <= 0;
- $default_time_limit = $opt_t;
-}
-$program_kludge = defined $opt_P ? $opt_P : 0;
-
-if (defined $opt_C) {
- foreach $c (split(',', $opt_C)) {
- $c =~ s/\s+//;
- die "$prog: categories can't be negated on the command line\n"
- if ($c =~ /^!/);
- $categories{$c} = 1;
- }
-}
-
-# Note which tests are to be run.
-%do_test = ();
-grep($do_test{$_} = 1, @ARGV);
-$all_tests = @ARGV == 0;
-
-# Set up a very minimal environment
-%new_env = ();
-foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
- 'PATH', 'SHELL', 'USER')) {
- $new_env{$env} = $ENV{$env} if defined $ENV{$env};
-}
-$new_env{'ENV'} = '/nonexistant';
-if (($os eq 'VMS') || ($Config{perlpath} =~ m/$Config{_exe}$/i)) {
- $new_env{'__perlname'} = $Config{perlpath};
-} else {
- $new_env{'__perlname'} = $Config{perlpath} . $Config{_exe};
-}
-if (defined $opt_e) {
- # XXX need a way to allow many -e arguments...
- if ($opt_e =~ /^([a-zA-Z_]\w*)(|=(.*))$/) {
- $new_env{$1} = $2 eq '' ? $ENV{$1} : $3;
- } else {
- die "$0: bad -e argument: $opt_e\n";
- }
-}
-%old_env = %ENV;
-
-die "$prog: couldn't make directory $tempdir - $!\n" if !mkdir($tempdir, 0777);
-
-chop($pwd = `pwd 2>/dev/null`);
-die "$prog: couldn't get current working directory\n" if $pwd eq '';
-die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd);
-
-if (!$program_kludge) {
- $test_prog = "$pwd/$test_prog" if substr($test_prog, 0, 1) ne '/';
- die "$prog: $test_prog is not executable - bye\n"
- if (! -x $test_prog && $os ne 'os2');
-}
-
-@trap_sigs = ('TERM', 'QUIT', 'INT', 'PIPE', 'HUP');
-@SIG{@trap_sigs} = ('cleanup_exit') x @trap_sigs;
-$child_kill_ok = 0;
-$SIG{'ALRM'} = 'catch_sigalrm';
-
-$| = 1;
-
-if (-d $test_set) {
- $file_prefix_skip = length($test_set) + 1;
- $ret = &process_test_dir($test_set);
-} else {
- $file_prefix_skip = 0;
- $ret = &process_test_file($test_set);
-}
-&cleanup_exit() if !defined $ret;
-
-$tot_failed = $nfailed + $nxfailed;
-$tot_passed = $npassed + $nxpassed;
-if ($tot_failed || $tot_passed) {
- print "Total failed: $tot_failed";
- print " ($nxfailed unexpected)" if $nxfailed;
- print " (as expected)" if $nfailed && !$nxfailed;
- print "\nTotal passed: $tot_passed";
- print " ($nxpassed unexpected)" if $nxpassed;
- print "\n";
-}
-
-&cleanup_exit('ok');
-
-sub
-cleanup_exit
-{
- local($sig, $exitcode) = ('', 1);
-
- if ($_[0] eq 'ok') {
- $exitcode = 0;
- } elsif ($_[0] ne '') {
- $sig = $_[0];
- }
-
- unlink($tempi, $tempo, $tempe, $temps);
- &scrub_dir($tempdir) if defined $tempdir;
- rmdir($tempdir) if defined $tempdir;
-
- if ($sig) {
- $SIG{$sig} = 'DEFAULT';
- kill $sig, $$;
- return;
- }
- exit $exitcode;
-}
-
-sub
-catch_sigalrm
-{
- $SIG{'ALRM'} = 'catch_sigalrm';
- kill(9, $child_pid) if $child_kill_ok;
- $child_killed = 1;
-}
-
-sub
-process_test_dir
-{
- local($dir) = @_;
- local($ret, $file);
- local(@todo) = ();
-
- if (!opendir(DIR, $dir)) {
- print STDERR "$prog: can't open directory $dir - $!\n";
- return undef;
- }
- while (defined ($file = readdir(DIR))) {
- push(@todo, $file) if $file =~ /^[^.].*\.t$/;
- }
- closedir(DIR);
-
- foreach $file (@todo) {
- $file = "$dir/$file";
- if (-d $file) {
- $ret = &process_test_dir($file);
- } elsif (-f _) {
- $ret = &process_test_file($file);
- }
- last if !defined $ret;
- }
-
- return $ret;
-}
-
-sub
-process_test_file
-{
- local($file) = @_;
- local($ret);
-
- if (!open(IN, $file)) {
- print STDERR "$prog: can't open $file - $!\n";
- return undef;
- }
- binmode(IN);
- while (1) {
- $ret = &read_test($file, IN, *test);
- last if !defined $ret || !$ret;
- next if !$all_tests && !$do_test{$test{'name'}};
- next if !&category_check(*test);
- $ret = &run_test(*test);
- last if !defined $ret;
- }
- close(IN);
-
- return $ret;
-}
-
-sub
-run_test
-{
- local(*test) = @_;
- local($name) = $test{':full-name'};
-
- if (defined $test{'stdin'}) {
- return undef if !&write_file($tempi, $test{'stdin'});
- $ifile = $tempi;
- } else {
- $ifile = '/dev/null';
- }
-
- if (defined $test{'script'}) {
- return undef if !&write_file($temps, $test{'script'});
- }
-
- return undef if !&scrub_dir($tempdir);
-
- if (!chdir($tempdir)) {
- print STDERR "$prog: couldn't cd to $tempdir - $!\n";
- return undef;
- }
-
- if (defined $test{'file-setup'}) {
- local($i);
- local($type, $perm, $rest, $c, $len, $name);
-
- for ($i = 0; $i < $test{'file-setup'}; $i++) {
- $val = $test{"file-setup:$i"};
-
- # format is: type perm "name"
- ($type, $perm, $rest) =
- split(' ', $val, 3);
- $c = substr($rest, 0, 1);
- $len = index($rest, $c, 1) - 1;
- $name = substr($rest, 1, $len);
- $rest = substr($rest, 2 + $len);
- $perm = oct($perm) if $perm =~ /^\d+$/;
- if ($type eq 'file') {
- return undef if !&write_file($name, $rest);
- if (!chmod($perm, $name)) {
- print STDERR
- "$prog:$test{':long-name'}: can't chmod $perm $name - $!\n";
- return undef;
- }
- } elsif ($type eq 'dir') {
- if (!mkdir($name, $perm)) {
- print STDERR
- "$prog:$test{':long-name'}: can't mkdir $perm $name - $!\n";
- return undef;
- }
- } elsif ($type eq 'symlink') {
- local($oumask) = umask($perm);
- local($ret) = symlink($rest, $name);
- umask($oumask);
- if (!$ret) {
- print STDERR
- "$prog:$test{':long-name'}: couldn't create symlink $name - $!\n";
- return undef;
- }
- }
- }
- }
-
- if (defined $test{'perl-setup'}) {
- eval $test{'perl-setup'};
- if ($@ ne '') {
- print STDERR "$prog:$test{':long-name'}: error running perl-setup - $@\n";
- return undef;
- }
- }
-
- $pid = fork;
- if (!defined $pid) {
- print STDERR "$prog: can't fork - $!\n";
- return undef;
- }
- if (!$pid) {
- @SIG{@trap_sigs} = ('DEFAULT') x @trap_sigs;
- $SIG{'ALRM'} = 'DEFAULT';
- if (defined $test{'env-setup'}) {
- local($var, $val, $i);
-
- foreach $var (split(substr($test{'env-setup'}, 0, 1),
- $test{'env-setup'}))
- {
- $i = index($var, '=');
- next if $i == 0 || $var eq '';
- if ($i < 0) {
- delete $new_env{$var};
- } else {
- $new_env{substr($var, 0, $i)} = substr($var, $i + 1);
- }
- }
- }
- if (!open(STDIN, "< $ifile")) {
- print STDERR "$prog: couldn't open $ifile in child - $!\n";
- kill('TERM', $$);
- }
- binmode(STDIN);
- if (!open(STDOUT, "> $tempo")) {
- print STDERR "$prog: couldn't open $tempo in child - $!\n";
- kill('TERM', $$);
- }
- binmode(STDOUT);
- if (!open(STDERR, "> $tempe")) {
- print STDOUT "$prog: couldn't open $tempe in child - $!\n";
- kill('TERM', $$);
- }
- binmode(STDERR);
- if ($program_kludge) {
- @argv = split(' ', $test_prog);
- } else {
- @argv = ($test_prog);
- }
- if (defined $test{'arguments'}) {
- push(@argv,
- split(substr($test{'arguments'}, 0, 1),
- substr($test{'arguments'}, 1)));
- }
- push(@argv, $temps) if defined $test{'script'};
-
- #XXX realpathise, use which/whence -p, or sth. like that
- #XXX if !$program_kludge, we get by with not doing it for now tho
- $new_env{'__progname'} = $argv[0];
-
- # The following doesn't work with perl5... Need to do it explicitly - yuck.
- #%ENV = %new_env;
- foreach $k (keys(%ENV)) {
- delete $ENV{$k};
- }
- $ENV{$k} = $v while ($k,$v) = each %new_env;
-
- exec { $argv[0] } @argv;
- print STDERR "$prog: couldn't execute $test_prog - $!\n";
- kill('TERM', $$);
- exit(95);
- }
- $child_pid = $pid;
- $child_killed = 0;
- $child_kill_ok = 1;
- alarm($test{'time-limit'}) if defined $test{'time-limit'};
- while (1) {
- $xpid = waitpid($pid, 0);
- $child_kill_ok = 0;
- if ($xpid < 0) {
- next if $! == EINTR;
- print STDERR "$prog: error waiting for child - $!\n";
- return undef;
- }
- last;
- }
- $status = $?;
- alarm(0) if defined $test{'time-limit'};
-
- $failed = 0;
- $why = '';
-
- if ($child_killed) {
- $failed = 1;
- $why .= "\ttest timed out (limit of $test{'time-limit'} seconds)\n";
- }
-
- $ret = &eval_exit($test{'long-name'}, $status, $test{'expected-exit'});
- return undef if !defined $ret;
- if (!$ret) {
- local($expl);
-
- $failed = 1;
- if (($status & 0xff) == 0x7f) {
- $expl = "stopped";
- } elsif (($status & 0xff)) {
- $expl = "signal " . ($status & 0x7f);
- } else {
- $expl = "exit-code " . (($status >> 8) & 0xff);
- }
- $why .=
- "\tunexpected exit status $status ($expl), expected $test{'expected-exit'}\n";
- }
-
- $tmp = &check_output($test{'long-name'}, $tempo, 'stdout',
- $test{'expected-stdout'}, $test{'expected-stdout-pattern'});
- return undef if !defined $tmp;
- if ($tmp ne '') {
- $failed = 1;
- $why .= $tmp;
- }
-
- $tmp = &check_output($test{'long-name'}, $tempe, 'stderr',
- $test{'expected-stderr'}, $test{'expected-stderr-pattern'});
- return undef if !defined $tmp;
- if ($tmp ne '') {
- $failed = 1;
- $why .= $tmp;
- }
-
- $tmp = &check_file_result(*test);
- return undef if !defined $tmp;
- if ($tmp ne '') {
- $failed = 1;
- $why .= $tmp;
- }
-
- if (defined $test{'perl-cleanup'}) {
- eval $test{'perl-cleanup'};
- if ($@ ne '') {
- print STDERR "$prog:$test{':long-name'}: error running perl-cleanup - $@\n";
- return undef;
- }
- }
-
- if (!chdir($pwd)) {
- print STDERR "$prog: couldn't cd to $pwd - $!\n";
- return undef;
- }
-
- if ($failed) {
- if (!$test{'expected-fail'}) {
- print "FAIL $name\n";
- $nxfailed++;
- } else {
- print "fail $name (as expected)\n";
- $nfailed++;
- }
- $why = "\tDescription"
- . &wrap_lines($test{'description'}, " (missing)\n")
- . $why;
- } elsif ($test{'expected-fail'}) {
- print "PASS $name (unexpectedly)\n";
- $nxpassed++;
- } else {
- print "pass $name\n";
- $npassed++;
- }
- print $why if $verbose;
- return 0;
-}
-
-sub
-category_check
-{
- local(*test) = @_;
- local($c);
-
- return 1 if (!defined $test{'category'});
- local($ok) = 0;
- foreach $c (split(',', $test{'category'})) {
- $c =~ s/\s+//;
- if ($c =~ /^!/) {
- $c = $';
- return 0 if (defined $categories{$c});
- $ok = 1;
- } else {
- $ok = 1 if (defined $categories{$c});
- }
- }
- return $ok;
-}
-
-sub
-scrub_dir
-{
- local($dir) = @_;
- local(@todo) = ();
- local($file);
-
- if (!opendir(DIR, $dir)) {
- print STDERR "$prog: couldn't open directory $dir - $!\n";
- return undef;
- }
- while (defined ($file = readdir(DIR))) {
- push(@todo, $file) if $file ne '.' && $file ne '..';
- }
- closedir(DIR);
- foreach $file (@todo) {
- $file = "$dir/$file";
- if (-d $file) {
- return undef if !&scrub_dir($file);
- if (!rmdir($file)) {
- print STDERR "$prog: couldn't rmdir $file - $!\n";
- return undef;
- }
- } else {
- if (!unlink($file)) {
- print STDERR "$prog: couldn't unlink $file - $!\n";
- return undef;
- }
- }
- }
- return 1;
-}
-
-sub
-write_file
-{
- local($file, $str) = @_;
-
- if (!open(TEMP, "> $file")) {
- print STDERR "$prog: can't open $file - $!\n";
- return undef;
- }
- binmode(TEMP);
- print TEMP $str;
- if (!close(TEMP)) {
- print STDERR "$prog: error writing $file - $!\n";
- return undef;
- }
- return 1;
-}
-
-sub
-check_output
-{
- local($name, $file, $what, $expect, $expect_pat) = @_;
- local($got) = '';
- local($why) = '';
- local($ret);
-
- if (!open(TEMP, "< $file")) {
- print STDERR "$prog:$name($what): couldn't open $file after running program - $!\n";
- return undef;
- }
- binmode(TEMP);
- while (<TEMP>) {
- $got .= $_;
- }
- close(TEMP);
- return compare_output($name, $what, $expect, $expect_pat, $got);
-}
-
-sub
-compare_output
-{
- local($name, $what, $expect, $expect_pat, $got) = @_;
- local($why) = '';
-
- if (defined $expect_pat) {
- $_ = $got;
- $ret = eval "$expect_pat";
- if ($@ ne '') {
- print STDERR "$prog:$name($what): error evaluating $what pattern: $expect_pat - $@\n";
- return undef;
- }
- if (!$ret) {
- $why = "\tunexpected $what - wanted pattern";
- $why .= &wrap_lines($expect_pat);
- $why .= "\tgot";
- $why .= &wrap_lines($got);
- }
- } else {
- $expect = '' if !defined $expect;
- if ($got ne $expect) {
- $why .= "\tunexpected $what - " . &first_diff($expect, $got) . "\n";
- $why .= "\twanted";
- $why .= &wrap_lines($expect);
- $why .= "\tgot";
- $why .= &wrap_lines($got);
- }
- }
- return $why;
-}
-
-sub
-wrap_lines
-{
- local($str, $empty) = @_;
- local($nonl) = substr($str, -1, 1) ne "\n";
-
- return (defined $empty ? $empty : " nothing\n") if $str eq '';
- substr($str, 0, 0) = ":\n";
- $str =~ s/\n/\n\t\t/g;
- if ($nonl) {
- $str .= "\n\t[incomplete last line]\n";
- } else {
- chop($str);
- chop($str);
- }
- return $str;
-}
-
-sub
-first_diff
-{
- local($exp, $got) = @_;
- local($lineno, $char) = (1, 1);
- local($i, $exp_len, $got_len);
- local($ce, $cg);
-
- $exp_len = length($exp);
- $got_len = length($got);
- if ($exp_len != $got_len) {
- if ($exp_len < $got_len) {
- if (substr($got, 0, $exp_len) eq $exp) {
- return "got too much output";
- }
- } elsif (substr($exp, 0, $got_len) eq $got) {
- return "got too little output";
- }
- }
- for ($i = 0; $i < $exp_len; $i++) {
- $ce = substr($exp, $i, 1);
- $cg = substr($got, $i, 1);
- last if $ce ne $cg;
- $char++;
- if ($ce eq "\n") {
- $lineno++;
- $char = 1;
- }
- }
- return "first difference: line $lineno, char $char (wanted '"
- . &format_char($ce) . "', got '"
- . &format_char($cg) . "'";
-}
-
-sub
-format_char
-{
- local($ch, $s);
-
- $ch = ord($_[0]);
- if ($ch == 10) {
- return '\n';
- } elsif ($ch == 13) {
- return '\r';
- } elsif ($ch == 8) {
- return '\b';
- } elsif ($ch == 9) {
- return '\t';
- } elsif ($ch > 127) {
- $ch -= 127;
- $s = "M-";
- } else {
- $s = '';
- }
- if ($ch < 32) {
- $s .= '^';
- $ch += ord('@');
- } elsif ($ch == 127) {
- return $s . "^?";
- }
- return $s . sprintf("%c", $ch);
-}
-
-sub
-eval_exit
-{
- local($name, $status, $expect) = @_;
- local($expr);
- local($w, $e, $s) = ($status, ($status >> 8) & 0xff, $status & 0x7f);
-
- $e = -1000 if $status & 0xff;
- $s = -1000 if $s == 0x7f;
- if (!defined $expect) {
- $expr = '$w == 0';
- } elsif ($expect =~ /^(|-)\d+$/) {
- $expr = "\$e == $expect";
- } else {
- $expr = $expect;
- $expr =~ s/\b([wse])\b/\$$1/g;
- $expr =~ s/\b(SIG[A-Z0-9]+)\b/&$1/g;
- }
- $w = eval $expr;
- if ($@ ne '') {
- print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $expect ($@)\n";
- return undef;
- }
- return $w;
-}
-
-sub
-read_test
-{
- local($file, $in, *test) = @_;
- local($field, $val, $flags, $do_chop, $need_redo, $start_lineno);
- local(%cnt, $sfield);
-
- %test = ();
- %cnt = ();
- while (<$in>) {
- next if /^\s*$/;
- next if /^ *#/;
- last if /^\s*---\s*$/;
- $start_lineno = $. if !defined $start_lineno;
- if (!/^([-\w]+):\s*(|\S|\S.*\S)\s*$/) {
- print STDERR "$prog:$file:$.: unrecognised line\n";
- return undef;
- }
- ($field, $val) = ($1, $2);
- $sfield = $field;
- $flags = $test_fields{$field};
- if (!defined $flags) {
- print STDERR "$prog:$file:$.: unrecognised field \"$field\"\n";
- return undef;
- }
- if ($flags =~ /s/) {
- local($cnt) = $cnt{$field}++;
- $test{$field} = $cnt{$field};
- $cnt = 0 if $cnt eq '';
- $sfield .= ":$cnt";
- } elsif (defined $test{$field}) {
- print STDERR "$prog:$file:$.: multiple \"$field\" fields\n";
- return undef;
- }
- $do_chop = $flags !~ /m/;
- $need_redo = 0;
- if ($val eq '' || $val eq '!' || $flags =~ /p/) {
- if ($flags =~ /[Mm]/) {
- if ($flags =~ /p/) {
- if ($val =~ /^!/) {
- $do_chop = 1;
- $val = $';
- } else {
- $do_chop = 0;
- }
- if ($val eq '') {
- print STDERR
- "$prog:$file:$.: no parameters given for field \"$field\"\n";
- return undef;
- }
- } else {
- if ($val eq '!') {
- $do_chop = 1;
- }
- $val = '';
- }
- while (<$in>) {
- last if !/^\t/;
- $val .= $';
- }
- chop $val if $do_chop;
- $do_chop = 1;
- $need_redo = 1;
-
- # Syntax check on fields that can several instances
- # (can give useful line numbers this way)
-
- if ($field eq 'file-setup') {
- local($type, $perm, $rest, $c, $len, $name);
-
- # format is: type perm "name"
- if ($val !~ /^[ \t]*(\S+)[ \t]+(\S+)[ \t]+([^ \t].*)/) {
- print STDERR
- "$prog:$file:$.: bad parameter line for file-setup field\n";
- return undef;
- }
- ($type, $perm, $rest) = ($1, $2, $3);
- if ($type !~ /^(file|dir|symlink)$/) {
- print STDERR
- "$prog:$file:$.: bad file type for file-setup: $type\n";
- return undef;
- }
- if ($perm !~ /^\d+$/) {
- print STDERR
- "$prog:$file:$.: bad permissions for file-setup: $type\n";
- return undef;
- }
- $c = substr($rest, 0, 1);
- if (($len = index($rest, $c, 1) - 1) <= 0) {
- print STDERR
- "$prog:$file:$.: missing end quote for file name in file-setup: $rest\n";
- return undef;
- }
- $name = substr($rest, 1, $len);
- if ($name =~ /^\// || $name =~ /(^|\/)\.\.(\/|$)/) {
- # Note: this is not a security thing - just a sanity
- # check - a test can still use symlinks to get at files
- # outside the test directory.
- print STDERR
-"$prog:$file:$.: file name in file-setup is absolute or contains ..: $name\n";
- return undef;
- }
- }
- if ($field eq 'file-result') {
- local($type, $perm, $uid, $gid, $matchType,
- $rest, $c, $len, $name);
-
- # format is: type perm uid gid matchType "name"
- if ($val !~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S.*)/) {
- print STDERR
- "$prog:$file:$.: bad parameter line for file-result field\n";
- return undef;
- }
- ($type, $perm, $uid, $gid, $matchType, $rest)
- = ($1, $2, $3, $4, $5, $6);
- if ($type !~ /^(file|dir|symlink)$/) {
- print STDERR
- "$prog:$file:$.: bad file type for file-result: $type\n";
- return undef;
- }
- if ($perm !~ /^\d+$/ && $perm ne '*') {
- print STDERR
- "$prog:$file:$.: bad permissions for file-result: $perm\n";
- return undef;
- }
- if ($uid !~ /^\d+$/ && $uid ne '*') {
- print STDERR
- "$prog:$file:$.: bad user-id for file-result: $uid\n";
- return undef;
- }
- if ($gid !~ /^\d+$/ && $gid ne '*') {
- print STDERR
- "$prog:$file:$.: bad group-id for file-result: $gid\n";
- return undef;
- }
- if ($matchType !~ /^(exact|pattern)$/) {
- print STDERR
- "$prog:$file:$.: bad match type for file-result: $matchType\n";
- return undef;
- }
- $c = substr($rest, 0, 1);
- if (($len = index($rest, $c, 1) - 1) <= 0) {
- print STDERR
- "$prog:$file:$.: missing end quote for file name in file-result: $rest\n";
- return undef;
- }
- $name = substr($rest, 1, $len);
- if ($name =~ /^\// || $name =~ /(^|\/)\.\.(\/|$)/) {
- # Note: this is not a security thing - just a sanity
- # check - a test can still use symlinks to get at files
- # outside the test directory.
- print STDERR
-"$prog:$file:$.: file name in file-result is absolute or contains ..: $name\n";
- return undef;
- }
- }
- } elsif ($val eq '') {
- print STDERR
- "$prog:$file:$.: no value given for field \"$field\"\n";
- return undef;
- }
- }
- $val .= "\n" if !$do_chop;
- $test{$sfield} = $val;
- redo if $need_redo;
- }
- if ($_ eq '') {
- if (%test) {
- print STDERR
- "$prog:$file:$start_lineno: end-of-file while reading test\n";
- return undef;
- }
- return 0;
- }
-
- while (($field, $val) = each %test_fields) {
- if ($val =~ /r/ && !defined $test{$field}) {
- print STDERR
- "$prog:$file:$start_lineno: required field \"$field\" missing\n";
- return undef;
- }
- }
-
- $test{':full-name'} = substr($file, $file_prefix_skip) . ":$test{'name'}";
- $test{':long-name'} = "$file:$start_lineno:$test{'name'}";
-
- # Syntax check on specific fields
- if (defined $test{'expected-fail'}) {
- if ($test{'expected-fail'} !~ /^(yes|no)$/) {
- print STDERR
- "$prog:$test{':long-name'}: bad value for expected-fail field\n";
- return undef;
- }
- $test{'expected-fail'} = $1 eq 'yes';
- } else {
- $test{'expected-fail'} = 0;
- }
- if (defined $test{'arguments'}) {
- local($firstc) = substr($test{'arguments'}, 0, 1);
-
- if (substr($test{'arguments'}, -1, 1) ne $firstc) {
- print STDERR "$prog:$test{':long-name'}: arguments field doesn't start and end with the same character\n";
- return undef;
- }
- }
- if (defined $test{'env-setup'}) {
- local($firstc) = substr($test{'env-setup'}, 0, 1);
-
- if (substr($test{'env-setup'}, -1, 1) ne $firstc) {
- print STDERR "$prog:$test{':long-name'}: env-setup field doesn't start and end with the same character\n";
- return undef;
- }
- }
- if (defined $test{'expected-exit'}) {
- local($val) = $test{'expected-exit'};
-
- if ($val =~ /^(|-)\d+$/) {
- if ($val < 0 || $val > 255) {
- print STDERR "$prog:$test{':long-name'}: expected-exit value $val not in 0..255\n";
- return undef;
- }
- } elsif ($val !~ /^([\s<>+-=*%\/&|!()]|\b[wse]\b|\bSIG[A-Z0-9]+\b)+$/) {
- print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $val\n";
- return undef;
- }
- } else {
- $test{'expected-exit'} = 0;
- }
- if (defined $test{'expected-stdout'}
- && defined $test{'expected-stdout-pattern'})
- {
- print STDERR "$prog:$test{':long-name'}: can't use both expected-stdout and expected-stdout-pattern\n";
- return undef;
- }
- if (defined $test{'expected-stderr'}
- && defined $test{'expected-stderr-pattern'})
- {
- print STDERR "$prog:$test{':long-name'}: can't use both expected-stderr and expected-stderr-pattern\n";
- return undef;
- }
- if (defined $test{'time-limit'}) {
- if ($test{'time-limit'} !~ /^\d+$/ || $test{'time-limit'} == 0) {
- print STDERR
- "$prog:$test{':long-name'}: bad value for time-limit field\n";
- return undef;
- }
- } elsif (defined $default_time_limit) {
- $test{'time-limit'} = $default_time_limit;
- }
-
- if (defined $known_tests{$test{'name'}}) {
- print STDERR "$prog:$test{':long-name'}: warning: duplicate test name ${test{'name'}}\n";
- }
- $known_tests{$test{'name'}} = 1;
-
- return 1;
-}
-
-sub
-tty_msg
-{
- local($msg) = @_;
-
- open(TTY, "> /dev/tty") || return 0;
- print TTY $msg;
- close(TTY);
- return 1;
-}
-
-sub
-never_called_funcs
-{
- return 0;
- &tty_msg("hi\n");
- &never_called_funcs();
- &catch_sigalrm();
- $old_env{'foo'} = 'bar';
- $internal_test_fields{'foo'} = 'bar';
-}
-
-sub
-check_file_result
-{
- local(*test) = @_;
-
- return '' if (!defined $test{'file-result'});
-
- local($why) = '';
- local($i);
- local($type, $perm, $uid, $gid, $rest, $c, $len, $name);
- local(@stbuf);
-
- for ($i = 0; $i < $test{'file-result'}; $i++) {
- $val = $test{"file-result:$i"};
-
- # format is: type perm "name"
- ($type, $perm, $uid, $gid, $matchType, $rest) =
- split(' ', $val, 6);
- $c = substr($rest, 0, 1);
- $len = index($rest, $c, 1) - 1;
- $name = substr($rest, 1, $len);
- $rest = substr($rest, 2 + $len);
- $perm = oct($perm) if $perm =~ /^\d+$/;
-
- @stbuf = lstat($name);
- if (!@stbuf) {
- $why .= "\texpected $type \"$name\" not created\n";
- next;
- }
- if ($perm ne '*' && ($stbuf[2] & 07777) != $perm) {
- $why .= "\t$type \"$name\" has unexpected permissions\n";
- $why .= sprintf("\t\texpected 0%o, found 0%o\n",
- $perm, $stbuf[2] & 07777);
- }
- if ($uid ne '*' && $stbuf[4] != $uid) {
- $why .= "\t$type \"$name\" has unexpected user-id\n";
- $why .= sprintf("\t\texpected %d, found %d\n",
- $uid, $stbuf[4]);
- }
- if ($gid ne '*' && $stbuf[5] != $gid) {
- $why .= "\t$type \"$name\" has unexpected group-id\n";
- $why .= sprintf("\t\texpected %d, found %d\n",
- $gid, $stbuf[5]);
- }
-
- if ($type eq 'file') {
- if (-l _ || ! -f _) {
- $why .= "\t$type \"$name\" is not a regular file\n";
- } else {
- local $tmp = &check_output($test{'long-name'}, $name,
- "$type contents in \"$name\"",
- $matchType eq 'exact' ? $rest : undef
- $matchType eq 'pattern' ? $rest : undef);
- return undef if (!defined $tmp);
- $why .= $tmp;
- }
- } elsif ($type eq 'dir') {
- if ($rest !~ /^\s*$/) {
- print STDERR "$prog:$test{':long-name'}: file-result test for directory $name should not have content specified\n";
- return undef;
- }
- if (-l _ || ! -d _) {
- $why .= "\t$type \"$name\" is not a directory\n";
- }
- } elsif ($type eq 'symlink') {
- if (!-l _) {
- $why .= "\t$type \"$name\" is not a symlink\n";
- } else {
- local $content = readlink($name);
- if (!defined $content) {
- print STDERR "$prog:$test{':long-name'}: file-result test for $type $name failed - could not readlink - $!\n";
- return undef;
- }
- local $tmp = &compare_output($test{'long-name'},
- "$type contents in \"$name\"",
- $matchType eq 'exact' ? $rest : undef
- $matchType eq 'pattern' ? $rest : undef);
- return undef if (!defined $tmp);
- $why .= $tmp;
- }
- }
- }
-
- return $why;
-}
-
-sub
-HELP_MESSAGE
-{
- print STDERR $Usage;
- exit 0;
-}
diff --git a/mksh/src/check.t b/mksh/src/check.t
deleted file mode 100644
index c8e8caf..0000000
--- a/mksh/src/check.t
+++ /dev/null
@@ -1,7443 +0,0 @@
-# $MirOS: src/bin/mksh/check.t,v 1.388 2010/08/24 15:47:44 tg Exp $
-# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
-# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
-# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
-#-
-# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-# 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.
-#-
-# You may also want to test IFS with the script at
-# http://www.research.att.com/~gsf/public/ifs.sh
-
-expected-stdout:
- @(#)MIRBSD KSH R39 2010/08/24
-description:
- Check version of shell.
-stdin:
- echo $KSH_VERSION
-name: KSH_VERSION
----
-name: selftest-1
-description:
- Regression test self-testing
-stdin:
- echo ${foo:-baz}
-expected-stdout:
- baz
----
-name: selftest-2
-description:
- Regression test self-testing
-env-setup: !foo=bar!
-stdin:
- echo ${foo:-baz}
-expected-stdout:
- bar
----
-name: selftest-3
-description:
- Regression test self-testing
-env-setup: !ENV=fnord!
-stdin:
- echo "<$ENV>"
-expected-stdout:
- <fnord>
----
-name: selftest-env
-description:
- Just output the environment variables set (always fails)
-category: disabled
-stdin:
- set
----
-name: alias-1
-description:
- Check that recursion is detected/avoided in aliases.
-stdin:
- alias fooBar=fooBar
- fooBar
- exit 0
-expected-stderr-pattern:
- /fooBar.*not found.*/
----
-name: alias-2
-description:
- Check that recursion is detected/avoided in aliases.
-stdin:
- alias fooBar=barFoo
- alias barFoo=fooBar
- fooBar
- barFoo
- exit 0
-expected-stderr-pattern:
- /fooBar.*not found.*\n.*barFoo.*not found/
----
-name: alias-3
-description:
- Check that recursion is detected/avoided in aliases.
-stdin:
- alias Echo='echo '
- alias fooBar=barFoo
- alias barFoo=fooBar
- Echo fooBar
- unalias barFoo
- Echo fooBar
-expected-stdout:
- fooBar
- barFoo
----
-name: alias-4
-description:
- Check that alias expansion isn't done on keywords (in keyword
- postitions).
-stdin:
- alias Echo='echo '
- alias while=While
- while false; do echo hi ; done
- Echo while
-expected-stdout:
- While
----
-name: alias-5
-description:
- Check that alias expansion done after alias with trailing space.
-stdin:
- alias Echo='echo '
- alias foo='bar stuff '
- alias bar='Bar1 Bar2 '
- alias stuff='Stuff'
- alias blah='Blah'
- Echo foo blah
-expected-stdout:
- Bar1 Bar2 Stuff Blah
----
-name: alias-6
-description:
- Check that alias expansion done after alias with trailing space.
-stdin:
- alias Echo='echo '
- alias foo='bar bar'
- alias bar='Bar '
- alias blah=Blah
- Echo foo blah
-expected-stdout:
- Bar Bar Blah
----
-name: alias-7
-description:
- Check that alias expansion done after alias with trailing space
- after a keyword.
-stdin:
- alias X='case '
- alias Y=Z
- X Y in 'Y') echo is y ;; Z) echo is z ; esac
-expected-stdout:
- is z
----
-name: alias-8
-description:
- Check that newlines in an alias don't cause the command to be lost.
-stdin:
- alias foo='
-
-
- echo hi
-
-
-
- echo there
-
-
- '
- foo
-expected-stdout:
- hi
- there
----
-name: alias-9
-description:
- Check that recursion is detected/avoided in aliases.
- This check fails for slow machines or Cygwin, raise
- the time-limit clause (e.g. to 7) if this occurs.
-time-limit: 3
-stdin:
- echo -n >tf
- alias ls=ls
- ls
- echo $(ls)
- exit 0
-expected-stdout:
- tf
- tf
----
-name: alias-10
-description:
- Check that recursion is detected/avoided in aliases.
- Regression, introduced during an old bugfix.
-stdin:
- alias foo='print hello '
- alias bar='foo world'
- echo $(bar)
-expected-stdout:
- hello world
----
-name: arith-lazy-1
-description:
- Check that only one side of ternary operator is evaluated
-stdin:
- x=i+=2
- y=j+=2
- typeset -i i=1 j=1
- echo $((1 ? 20 : (x+=2)))
- echo $i,$x
- echo $((0 ? (y+=2) : 30))
- echo $j,$y
-expected-stdout:
- 20
- 1,i+=2
- 30
- 1,j+=2
----
-name: arith-lazy-2
-description:
- Check that assignments not done on non-evaluated side of ternary
- operator
-stdin:
- x=i+=2
- y=j+=2
- typeset -i i=1 j=1
- echo $((1 ? 20 : (x+=2)))
- echo $i,$x
- echo $((0 ? (y+=2) : 30))
- echo $i,$y
-expected-stdout:
- 20
- 1,i+=2
- 30
- 1,j+=2
----
-name: arith-lazy-3
-description:
- Check that assignments not done on non-evaluated side of ternary
- operator and this construct is parsed correctly (Debian #445651)
-stdin:
- x=4
- y=$((0 ? x=1 : 2))
- echo = $x $y =
-expected-stdout:
- = 4 2 =
----
-name: arith-ternary-prec-1
-description:
- Check precedence of ternary operator vs assignment
-stdin:
- typeset -i x=2
- y=$((1 ? 20 : x+=2))
-expected-exit: e != 0
-expected-stderr-pattern:
- /.*:.*1 \? 20 : x\+=2.*lvalue.*\n$/
----
-name: arith-ternary-prec-2
-description:
- Check precedence of ternary operator vs assignment
-stdin:
- typeset -i x=2
- echo $((0 ? x+=2 : 20))
-expected-stdout:
- 20
----
-name: arith-div-assoc-1
-description:
- Check associativity of division operator
-stdin:
- echo $((20 / 2 / 2))
-expected-stdout:
- 5
----
-name: arith-assop-assoc-1
-description:
- Check associativity of assignment-operator operator
-stdin:
- typeset -i i=1 j=2 k=3
- echo $((i += j += k))
- echo $i,$j,$k
-expected-stdout:
- 6
- 6,5,3
----
-name: arith-unsigned-1
-description:
- Check if unsigned arithmetics work
-stdin:
- # signed vs unsigned
- echo x1 $((-1)) $((#-1))
- # calculating
- typeset -i vs
- typeset -Ui vu
- vs=4123456789; vu=4123456789
- echo x2 $vs $vu
- (( vs %= 2147483647 ))
- (( vu %= 2147483647 ))
- echo x3 $vs $vu
- vs=4123456789; vu=4123456789
- (( # vs %= 2147483647 ))
- (( # vu %= 2147483647 ))
- echo x4 $vs $vu
- # make sure the calculation does not change unsigned flag
- vs=4123456789; vu=4123456789
- echo x5 $vs $vu
- # short form
- echo x6 $((# vs % 2147483647)) $((# vu % 2147483647))
- # array refs
- set -A va
- va[1975973142]=right
- va[4123456789]=wrong
- echo x7 ${va[#4123456789%2147483647]}
-expected-stdout:
- x1 -1 4294967295
- x2 -171510507 4123456789
- x3 -171510507 4123456789
- x4 1975973142 1975973142
- x5 -171510507 4123456789
- x6 1975973142 1975973142
- x7 right
----
-name: arith-limit32-1
-description:
- Check if arithmetics are 32 bit
-stdin:
- # signed vs unsigned
- echo x1 $((-1)) $((#-1))
- # calculating
- typeset -i vs
- typeset -Ui vu
- vs=2147483647; vu=2147483647
- echo x2 $vs $vu
- let vs++ vu++
- echo x3 $vs $vu
- vs=4294967295; vu=4294967295
- echo x4 $vs $vu
- let vs++ vu++
- echo x5 $vs $vu
- let vs++ vu++
- echo x6 $vs $vu
-expected-stdout:
- x1 -1 4294967295
- x2 2147483647 2147483647
- x3 -2147483648 2147483648
- x4 -1 4294967295
- x5 0 0
- x6 1 1
----
-name: bksl-nl-ign-1
-description:
- Check that \newline is not collasped after #
-stdin:
- echo hi #there \
- echo folks
-expected-stdout:
- hi
- folks
----
-name: bksl-nl-ign-2
-description:
- Check that \newline is not collasped inside single quotes
-stdin:
- echo 'hi \
- there'
- echo folks
-expected-stdout:
- hi \
- there
- folks
----
-name: bksl-nl-ign-3
-description:
- Check that \newline is not collasped inside single quotes
-stdin:
- cat << \EOF
- hi \
- there
- EOF
-expected-stdout:
- hi \
- there
----
-name: bksl-nl-ign-4
-description:
- Check interaction of aliases, single quotes and here-documents
- with backslash-newline
- (don't know what POSIX has to say about this)
-stdin:
- a=2
- alias x='echo hi
- cat << "EOF"
- foo\
- bar
- some'
- x
- more\
- stuff$a
- EOF
-expected-stdout:
- hi
- foo\
- bar
- some
- more\
- stuff$a
----
-name: bksl-nl-ign-5
-description:
- Check what happens with backslash at end of input
- (the old Bourne shell trashes them; so do we)
-stdin: !
- echo `echo foo\\`bar
- echo hi\
-expected-stdout:
- foobar
- hi
----
-#
-# Places \newline should be collapsed
-#
-name: bksl-nl-1
-description:
- Check that \newline is collasped before, in the middle of, and
- after words
-stdin:
- \
- echo hi\
- There, \
- folks
-expected-stdout:
- hiThere, folks
----
-name: bksl-nl-2
-description:
- Check that \newline is collasped in $ sequences
- (ksh93 fails this)
-stdin:
- a=12
- ab=19
- echo $\
- a
- echo $a\
- b
- echo $\
- {a}
- echo ${a\
- b}
- echo ${ab\
- }
-expected-stdout:
- 12
- 19
- 12
- 19
- 19
----
-name: bksl-nl-3
-description:
- Check that \newline is collasped in $(..) and `...` sequences
- (ksh93 fails this)
-stdin:
- echo $\
- (echo foobar1)
- echo $(\
- echo foobar2)
- echo $(echo foo\
- bar3)
- echo $(echo foobar4\
- )
- echo `
- echo stuff1`
- echo `echo st\
- uff2`
-expected-stdout:
- foobar1
- foobar2
- foobar3
- foobar4
- stuff1
- stuff2
----
-name: bksl-nl-4
-description:
- Check that \newline is collasped in $((..)) sequences
- (ksh93 fails this)
-stdin:
- echo $\
- ((1+2))
- echo $(\
- (1+2+3))
- echo $((\
- 1+2+3+4))
- echo $((1+\
- 2+3+4+5))
- echo $((1+2+3+4+5+6)\
- )
-expected-stdout:
- 3
- 6
- 10
- 15
- 21
----
-name: bksl-nl-5
-description:
- Check that \newline is collasped in double quoted strings
-stdin:
- echo "\
- hi"
- echo "foo\
- bar"
- echo "folks\
- "
-expected-stdout:
- hi
- foobar
- folks
----
-name: bksl-nl-6
-description:
- Check that \newline is collasped in here document delimiters
- (ksh93 fails second part of this)
-stdin:
- a=12
- cat << EO\
- F
- a=$a
- foo\
- bar
- EOF
- cat << E_O_F
- foo
- E_O_\
- F
- echo done
-expected-stdout:
- a=12
- foobar
- foo
- done
----
-name: bksl-nl-7
-description:
- Check that \newline is collasped in double-quoted here-document
- delimiter.
-stdin:
- a=12
- cat << "EO\
- F"
- a=$a
- foo\
- bar
- EOF
- echo done
-expected-stdout:
- a=$a
- foo\
- bar
- done
----
-name: bksl-nl-8
-description:
- Check that \newline is collasped in various 2+ character tokens
- delimiter.
- (ksh93 fails this)
-stdin:
- echo hi &\
- & echo there
- echo foo |\
- | echo bar
- cat <\
- < EOF
- stuff
- EOF
- cat <\
- <\
- - EOF
- more stuff
- EOF
- cat <<\
- EOF
- abcdef
- EOF
- echo hi >\
- > /dev/null
- echo $?
- i=1
- case $i in
- (\
- x|\
- 1\
- ) echo hi;\
- ;
- (*) echo oops
- esac
-expected-stdout:
- hi
- there
- foo
- stuff
- more stuff
- abcdef
- 0
- hi
----
-name: bksl-nl-9
-description:
- Check that \ at the end of an alias is collapsed when followed
- by a newline
- (don't know what POSIX has to say about this)
-stdin:
- alias x='echo hi\'
- x
- echo there
-expected-stdout:
- hiecho there
----
-name: bksl-nl-10
-description:
- Check that \newline in a keyword is collapsed
-stdin:
- i\
- f true; then\
- echo pass; el\
- se echo fail; fi
-expected-stdout:
- pass
----
-#
-# Places \newline should be collapsed (ksh extensions)
-#
-name: bksl-nl-ksh-1
-description:
- Check that \newline is collapsed in extended globbing
- (ksh93 fails this)
-stdin:
- xxx=foo
- case $xxx in
- (f*\
- (\
- o\
- )\
- ) echo ok ;;
- *) echo bad
- esac
-expected-stdout:
- ok
----
-name: bksl-nl-ksh-2
-description:
- Check that \newline is collapsed in ((...)) expressions
- (ksh93 fails this)
-stdin:
- i=1
- (\
- (\
- i=i+2\
- )\
- )
- echo $i
-expected-stdout:
- 3
----
-name: break-1
-description:
- See if break breaks out of loops
-stdin:
- for i in a b c; do echo $i; break; echo bad-$i; done
- echo end-1
- for i in a b c; do echo $i; break 1; echo bad-$i; done
- echo end-2
- for i in a b c; do
- for j in x y z; do
- echo $i:$j
- break
- echo bad-$i
- done
- echo end-$i
- done
- echo end-3
-expected-stdout:
- a
- end-1
- a
- end-2
- a:x
- end-a
- b:x
- end-b
- c:x
- end-c
- end-3
----
-name: break-2
-description:
- See if break breaks out of nested loops
-stdin:
- for i in a b c; do
- for j in x y z; do
- echo $i:$j
- break 2
- echo bad-$i
- done
- echo end-$i
- done
- echo end
-expected-stdout:
- a:x
- end
----
-name: break-3
-description:
- What if break used outside of any loops
- (ksh88,ksh93 don't print error messages here)
-stdin:
- break
-expected-stderr-pattern:
- /.*break.*/
----
-name: break-4
-description:
- What if break N used when only N-1 loops
- (ksh88,ksh93 don't print error messages here)
-stdin:
- for i in a b c; do echo $i; break 2; echo bad-$i; done
- echo end
-expected-stdout:
- a
- end
-expected-stderr-pattern:
- /.*break.*/
----
-name: break-5
-description:
- Error if break argument isn't a number
-stdin:
- for i in a b c; do echo $i; break abc; echo more-$i; done
- echo end
-expected-stdout:
- a
-expected-exit: e != 0
-expected-stderr-pattern:
- /.*break.*/
----
-name: continue-1
-description:
- See if continue continues loops
-stdin:
- for i in a b c; do echo $i; continue; echo bad-$i ; done
- echo end-1
- for i in a b c; do echo $i; continue 1; echo bad-$i; done
- echo end-2
- for i in a b c; do
- for j in x y z; do
- echo $i:$j
- continue
- echo bad-$i-$j
- done
- echo end-$i
- done
- echo end-3
-expected-stdout:
- a
- b
- c
- end-1
- a
- b
- c
- end-2
- a:x
- a:y
- a:z
- end-a
- b:x
- b:y
- b:z
- end-b
- c:x
- c:y
- c:z
- end-c
- end-3
----
-name: continue-2
-description:
- See if continue breaks out of nested loops
-stdin:
- for i in a b c; do
- for j in x y z; do
- echo $i:$j
- continue 2
- echo bad-$i-$j
- done
- echo end-$i
- done
- echo end
-expected-stdout:
- a:x
- b:x
- c:x
- end
----
-name: continue-3
-description:
- What if continue used outside of any loops
- (ksh88,ksh93 don't print error messages here)
-stdin:
- continue
-expected-stderr-pattern:
- /.*continue.*/
----
-name: continue-4
-description:
- What if continue N used when only N-1 loops
- (ksh88,ksh93 don't print error messages here)
-stdin:
- for i in a b c; do echo $i; continue 2; echo bad-$i; done
- echo end
-expected-stdout:
- a
- b
- c
- end
-expected-stderr-pattern:
- /.*continue.*/
----
-name: continue-5
-description:
- Error if continue argument isn't a number
-stdin:
- for i in a b c; do echo $i; continue abc; echo more-$i; done
- echo end
-expected-stdout:
- a
-expected-exit: e != 0
-expected-stderr-pattern:
- /.*continue.*/
----
-name: cd-history
-description:
- Test someone's CD history package (uses arrays)
-stdin:
- # go to known place before doing anything
- cd /
-
- alias cd=_cd
- function _cd
- {
- typeset -i cdlen i
- typeset t
-
- if [ $# -eq 0 ]
- then
- set -- $HOME
- fi
-
- if [ "$CDHISTFILE" -a -r "$CDHISTFILE" ] # if directory history exists
- then
- typeset CDHIST
- i=-1
- while read -r t # read directory history file
- do
- CDHIST[i=i+1]=$t
- done <$CDHISTFILE
- fi
-
- if [ "${CDHIST[0]}" != "$PWD" -a "$PWD" != "" ]
- then
- _cdins # insert $PWD into cd history
- fi
-
- cdlen=${#CDHIST[*]} # number of elements in history
-
- case "$@" in
- -) # cd to new dir
- if [ "$OLDPWD" = "" ] && ((cdlen>1))
- then
- 'print' ${CDHIST[1]}
- 'cd' ${CDHIST[1]}
- _pwd
- else
- 'cd' $@
- _pwd
- fi
- ;;
- -l) # print directory list
- typeset -R3 num
- ((i=cdlen))
- while (((i=i-1)>=0))
- do
- num=$i
- 'print' "$num ${CDHIST[i]}"
- done
- return
- ;;
- -[0-9]|-[0-9][0-9]) # cd to dir in list
- if (((i=${1#-})<cdlen))
- then
- 'print' ${CDHIST[i]}
- 'cd' ${CDHIST[i]}
- _pwd
- else
- 'cd' $@
- _pwd
- fi
- ;;
- -*) # cd to matched dir in list
- t=${1#-}
- i=1
- while ((i<cdlen))
- do
- case ${CDHIST[i]} in
- *$t*)
- 'print' ${CDHIST[i]}
- 'cd' ${CDHIST[i]}
- _pwd
- break
- ;;
- esac
- ((i=i+1))
- done
- if ((i>=cdlen))
- then
- 'cd' $@
- _pwd
- fi
- ;;
- *) # cd to new dir
- 'cd' $@
- _pwd
- ;;
- esac
-
- _cdins # insert $PWD into cd history
-
- if [ "$CDHISTFILE" ]
- then
- cdlen=${#CDHIST[*]} # number of elements in history
-
- i=0
- while ((i<cdlen))
- do
- 'print' -r ${CDHIST[i]} # update directory history
- ((i=i+1))
- done >$CDHISTFILE
- fi
- }
-
- function _cdins # insert $PWD into cd history
- { # meant to be called only by _cd
- typeset -i i
-
- ((i=0))
- while ((i<${#CDHIST[*]})) # see if dir is already in list
- do
- if [ "${CDHIST[$i]}" = "$PWD" ]
- then
- break
- fi
- ((i=i+1))
- done
-
- if ((i>22)) # limit max size of list
- then
- i=22
- fi
-
- while (((i=i-1)>=0)) # bump old dirs in list
- do
- CDHIST[i+1]=${CDHIST[i]}
- done
-
- CDHIST[0]=$PWD # insert new directory in list
- }
-
-
- function _pwd
- {
- if [ -n "$ECD" ]
- then
- pwd 1>&6
- fi
- }
- # Start of test
- cd /tmp
- cd /bin
- cd /etc
- cd -
- cd -2
- cd -l
-expected-stdout:
- /bin
- /tmp
- 3 /
- 2 /etc
- 1 /bin
- 0 /tmp
----
-name: env-prompt
-description:
- Check that prompt not printed when processing ENV
-env-setup: !ENV=./foo!
-file-setup: file 644 "foo"
- XXX=_
- PS1=X
- false && echo hmmm
-arguments: !-i!
-stdin:
- echo hi${XXX}there
-expected-stdout:
- hi_there
-expected-stderr: !
- XX
----
-name: expand-ugly
-description:
- Check that weird ${foo+bar} constructs are parsed correctly
-stdin:
- (echo 1 ${IFS+'}'z}) 2>&- || echo failed in 1
- (echo 2 "${IFS+'}'z}") 2>&- || echo failed in 2
- (echo 3 "foo ${IFS+'bar} baz") 2>&- || echo failed in 3
- (echo -n '4 '; printf '%s\n' "foo ${IFS+"b c"} baz") 2>&- || echo failed in 4
- (echo -n '5 '; printf '%s\n' "foo ${IFS+b c} baz") 2>&- || echo failed in 5
- (echo 6 ${IFS+"}"z}) 2>&- || echo failed in 6
- (echo 7 "${IFS+"}"z}") 2>&- || echo failed in 7
- (echo 8 "${IFS+\"}\"z}") 2>&- || echo failed in 8
- (echo 9 "${IFS+\"\}\"z}") 2>&- || echo failed in 9
- (echo 10 foo ${IFS+'bar} baz'}) 2>&- || echo failed in 10
- (echo 11 "$(echo "${IFS+'}'z}")") 2>&- || echo failed in 11
- (echo 12 "$(echo ${IFS+'}'z})") 2>&- || echo failed in 12
- (echo 13 ${IFS+\}z}) 2>&- || echo failed in 13
- (echo 14 "${IFS+\}z}") 2>&- || echo failed in 14
- u=x; (echo -n '15 '; printf '<%s> ' "foo ${IFS+a"b$u{ {"{{\}b} c ${IFS+d{}} bar" ${IFS-e{}} baz; echo .) 2>&- || echo failed in 15
- l=t; (echo 16 ${IFS+h`echo -n i ${IFS+$l}h`ere}) 2>&- || echo failed in 16
- l=t; (echo 17 ${IFS+h$(echo -n i ${IFS+$l}h)ere}) 2>&- || echo failed in 17
- l=t; (echo 18 "${IFS+h`echo -n i ${IFS+$l}h`ere}") 2>&- || echo failed in 18
- l=t; (echo 19 "${IFS+h$(echo -n i ${IFS+$l}h)ere}") 2>&- || echo failed in 19
- l=t; (echo 20 ${IFS+h`echo -n i "${IFS+$l}"h`ere}) 2>&- || echo failed in 20
- l=t; (echo 21 ${IFS+h$(echo -n i "${IFS+$l}"h)ere}) 2>&- || echo failed in 21
- l=t; (echo 22 "${IFS+h`echo -n i "${IFS+$l}"h`ere}") 2>&- || echo failed in 22
- l=t; (echo 23 "${IFS+h$(echo -n i "${IFS+$l}"h)ere}") 2>&- || echo failed in 23
- key=value; (echo -n '24 '; printf '%s\n' "${IFS+'$key'}") 2>&- || echo failed in 24
- key=value; (echo -n '25 '; printf '%s\n' "${IFS+"'$key'"}") 2>&- || echo failed in 25 # ksh93: “'$key'”
- key=value; (echo -n '26 '; printf '%s\n' ${IFS+'$key'}) 2>&- || echo failed in 26
- key=value; (echo -n '27 '; printf '%s\n' ${IFS+"'$key'"}) 2>&- || echo failed in 27
- (echo -n '28 '; printf '%s\n' "${IFS+"'"x ~ x'}'x"'}"x}" #') 2>&- || echo failed in 28
- u=x; (echo -n '29 '; printf '<%s> ' foo ${IFS+a"b$u{ {"{ {\}b} c ${IFS+d{}} bar ${IFS-e{}} baz; echo .) 2>&- || echo failed in 29
- (echo -n '30 '; printf '<%s> ' ${IFS+foo 'b\
- ar' baz}; echo .) 2>&- || (echo failed in 30; echo failed in 31)
- (echo -n '32 '; printf '<%s> ' ${IFS+foo "b\
- ar" baz}; echo .) 2>&- || echo failed in 32
- (echo -n '33 '; printf '<%s> ' "${IFS+foo 'b\
- ar' baz}"; echo .) 2>&- || echo failed in 33
- (echo -n '34 '; printf '<%s> ' "${IFS+foo "b\
- ar" baz}"; echo .) 2>&- || echo failed in 34
- (echo -n '35 '; printf '<%s> ' ${v=a\ b} x ${v=c\ d}; echo .) 2>&- || echo failed in 35
- (echo -n '36 '; printf '<%s> ' "${v=a\ b}" x "${v=c\ d}"; echo .) 2>&- || echo failed in 36
- (echo -n '37 '; printf '<%s> ' ${v-a\ b} x ${v-c\ d}; echo .) 2>&- || echo failed in 37
- (echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>&- || echo failed in 38
- foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>&- || echo failed in 39
- foo="a b c"; (echo -n '40 '; printf '<%s> ' "${foo#a}"; echo .) 2>&- || echo failed in 40
-expected-stdout:
- 1 }z
- 2 ''z}
- 3 foo 'bar baz
- 4 foo b c baz
- 5 foo b c baz
- 6 }z
- 7 }z
- 8 ""z}
- 9 "}"z
- 10 foo bar} baz
- 11 ''z}
- 12 }z
- 13 }z
- 14 }z
- 15 <foo abx{ {{{}b c d{} bar> <}> <baz> .
- 16 hi there
- 17 hi there
- 18 hi there
- 19 hi there
- 20 hi there
- 21 hi there
- 22 hi there
- 23 hi there
- 24 'value'
- 25 'value'
- 26 $key
- 27 'value'
- 28 'x ~ x''x}"x}" #
- 29 <foo> <abx{ {{> <{}b> <c> <d{}> <bar> <}> <baz> .
- 30 <foo> <b\
- ar> <baz> .
- 32 <foo> <bar> <baz> .
- 33 <foo 'bar' baz> .
- 34 <foo bar baz> .
- 35 <a> <b> <x> <a> <b> .
- 36 <a\ b> <x> <a\ b> .
- 37 <a b> <x> <c d> .
- 38 xay / x'a'y .
- 39 x' / x' .
- 40 < b c> .
----
-name: expand-unglob-dblq
-description:
- Check that regular "${foo+bar}" constructs are parsed correctly
-stdin:
- u=x
- tl_norm() {
- v=$2
- test x"$v" = x"-" && unset v
- (echo "$1 plus norm foo ${v+'bar'} baz")
- (echo "$1 dash norm foo ${v-'bar'} baz")
- (echo "$1 eqal norm foo ${v='bar'} baz")
- (echo "$1 qstn norm foo ${v?'bar'} baz") 2>&- || \
- echo "$1 qstn norm -> error"
- (echo "$1 PLUS norm foo ${v:+'bar'} baz")
- (echo "$1 DASH norm foo ${v:-'bar'} baz")
- (echo "$1 EQAL norm foo ${v:='bar'} baz")
- (echo "$1 QSTN norm foo ${v:?'bar'} baz") 2>&- || \
- echo "$1 QSTN norm -> error"
- }
- tl_paren() {
- v=$2
- test x"$v" = x"-" && unset v
- (echo "$1 plus parn foo ${v+(bar)} baz")
- (echo "$1 dash parn foo ${v-(bar)} baz")
- (echo "$1 eqal parn foo ${v=(bar)} baz")
- (echo "$1 qstn parn foo ${v?(bar)} baz") 2>&- || \
- echo "$1 qstn parn -> error"
- (echo "$1 PLUS parn foo ${v:+(bar)} baz")
- (echo "$1 DASH parn foo ${v:-(bar)} baz")
- (echo "$1 EQAL parn foo ${v:=(bar)} baz")
- (echo "$1 QSTN parn foo ${v:?(bar)} baz") 2>&- || \
- echo "$1 QSTN parn -> error"
- }
- tl_brace() {
- v=$2
- test x"$v" = x"-" && unset v
- (echo "$1 plus brac foo ${v+a$u{{{\}b} c ${v+d{}} baz")
- (echo "$1 dash brac foo ${v-a$u{{{\}b} c ${v-d{}} baz")
- (echo "$1 eqal brac foo ${v=a$u{{{\}b} c ${v=d{}} baz")
- (echo "$1 qstn brac foo ${v?a$u{{{\}b} c ${v?d{}} baz") 2>&- || \
- echo "$1 qstn brac -> error"
- (echo "$1 PLUS brac foo ${v:+a$u{{{\}b} c ${v:+d{}} baz")
- (echo "$1 DASH brac foo ${v:-a$u{{{\}b} c ${v:-d{}} baz")
- (echo "$1 EQAL brac foo ${v:=a$u{{{\}b} c ${v:=d{}} baz")
- (echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>&- || \
- echo "$1 QSTN brac -> error"
- }
- tl_norm 1 -
- tl_norm 2 ''
- tl_norm 3 x
- tl_paren 4 -
- tl_paren 5 ''
- tl_paren 6 x
- tl_brace 7 -
- tl_brace 8 ''
- tl_brace 9 x
-expected-stdout:
- 1 plus norm foo baz
- 1 dash norm foo 'bar' baz
- 1 eqal norm foo 'bar' baz
- 1 qstn norm -> error
- 1 PLUS norm foo baz
- 1 DASH norm foo 'bar' baz
- 1 EQAL norm foo 'bar' baz
- 1 QSTN norm -> error
- 2 plus norm foo 'bar' baz
- 2 dash norm foo baz
- 2 eqal norm foo baz
- 2 qstn norm foo baz
- 2 PLUS norm foo baz
- 2 DASH norm foo 'bar' baz
- 2 EQAL norm foo 'bar' baz
- 2 QSTN norm -> error
- 3 plus norm foo 'bar' baz
- 3 dash norm foo x baz
- 3 eqal norm foo x baz
- 3 qstn norm foo x baz
- 3 PLUS norm foo 'bar' baz
- 3 DASH norm foo x baz
- 3 EQAL norm foo x baz
- 3 QSTN norm foo x baz
- 4 plus parn foo baz
- 4 dash parn foo (bar) baz
- 4 eqal parn foo (bar) baz
- 4 qstn parn -> error
- 4 PLUS parn foo baz
- 4 DASH parn foo (bar) baz
- 4 EQAL parn foo (bar) baz
- 4 QSTN parn -> error
- 5 plus parn foo (bar) baz
- 5 dash parn foo baz
- 5 eqal parn foo baz
- 5 qstn parn foo baz
- 5 PLUS parn foo baz
- 5 DASH parn foo (bar) baz
- 5 EQAL parn foo (bar) baz
- 5 QSTN parn -> error
- 6 plus parn foo (bar) baz
- 6 dash parn foo x baz
- 6 eqal parn foo x baz
- 6 qstn parn foo x baz
- 6 PLUS parn foo (bar) baz
- 6 DASH parn foo x baz
- 6 EQAL parn foo x baz
- 6 QSTN parn foo x baz
- 7 plus brac foo c } baz
- 7 dash brac foo ax{{{}b c d{} baz
- 7 eqal brac foo ax{{{}b c ax{{{}b} baz
- 7 qstn brac -> error
- 7 PLUS brac foo c } baz
- 7 DASH brac foo ax{{{}b c d{} baz
- 7 EQAL brac foo ax{{{}b c ax{{{}b} baz
- 7 QSTN brac -> error
- 8 plus brac foo ax{{{}b c d{} baz
- 8 dash brac foo c } baz
- 8 eqal brac foo c } baz
- 8 qstn brac foo c } baz
- 8 PLUS brac foo c } baz
- 8 DASH brac foo ax{{{}b c d{} baz
- 8 EQAL brac foo ax{{{}b c ax{{{}b} baz
- 8 QSTN brac -> error
- 9 plus brac foo ax{{{}b c d{} baz
- 9 dash brac foo x c x} baz
- 9 eqal brac foo x c x} baz
- 9 qstn brac foo x c x} baz
- 9 PLUS brac foo ax{{{}b c d{} baz
- 9 DASH brac foo x c x} baz
- 9 EQAL brac foo x c x} baz
- 9 QSTN brac foo x c x} baz
----
-name: expand-unglob-unq
-description:
- Check that regular ${foo+bar} constructs are parsed correctly
-stdin:
- u=x
- tl_norm() {
- v=$2
- test x"$v" = x"-" && unset v
- (echo $1 plus norm foo ${v+'bar'} baz)
- (echo $1 dash norm foo ${v-'bar'} baz)
- (echo $1 eqal norm foo ${v='bar'} baz)
- (echo $1 qstn norm foo ${v?'bar'} baz) 2>&- || \
- echo "$1 qstn norm -> error"
- (echo $1 PLUS norm foo ${v:+'bar'} baz)
- (echo $1 DASH norm foo ${v:-'bar'} baz)
- (echo $1 EQAL norm foo ${v:='bar'} baz)
- (echo $1 QSTN norm foo ${v:?'bar'} baz) 2>&- || \
- echo "$1 QSTN norm -> error"
- }
- tl_paren() {
- v=$2
- test x"$v" = x"-" && unset v
- (echo $1 plus parn foo ${v+\(bar')'} baz)
- (echo $1 dash parn foo ${v-\(bar')'} baz)
- (echo $1 eqal parn foo ${v=\(bar')'} baz)
- (echo $1 qstn parn foo ${v?\(bar')'} baz) 2>&- || \
- echo "$1 qstn parn -> error"
- (echo $1 PLUS parn foo ${v:+\(bar')'} baz)
- (echo $1 DASH parn foo ${v:-\(bar')'} baz)
- (echo $1 EQAL parn foo ${v:=\(bar')'} baz)
- (echo $1 QSTN parn foo ${v:?\(bar')'} baz) 2>&- || \
- echo "$1 QSTN parn -> error"
- }
- tl_brace() {
- v=$2
- test x"$v" = x"-" && unset v
- (echo $1 plus brac foo ${v+a$u{{{\}b} c ${v+d{}} baz)
- (echo $1 dash brac foo ${v-a$u{{{\}b} c ${v-d{}} baz)
- (echo $1 eqal brac foo ${v=a$u{{{\}b} c ${v=d{}} baz)
- (echo $1 qstn brac foo ${v?a$u{{{\}b} c ${v?d{}} baz) 2>&- || \
- echo "$1 qstn brac -> error"
- (echo $1 PLUS brac foo ${v:+a$u{{{\}b} c ${v:+d{}} baz)
- (echo $1 DASH brac foo ${v:-a$u{{{\}b} c ${v:-d{}} baz)
- (echo $1 EQAL brac foo ${v:=a$u{{{\}b} c ${v:=d{}} baz)
- (echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>&- || \
- echo "$1 QSTN brac -> error"
- }
- tl_norm 1 -
- tl_norm 2 ''
- tl_norm 3 x
- tl_paren 4 -
- tl_paren 5 ''
- tl_paren 6 x
- tl_brace 7 -
- tl_brace 8 ''
- tl_brace 9 x
-expected-stdout:
- 1 plus norm foo baz
- 1 dash norm foo bar baz
- 1 eqal norm foo bar baz
- 1 qstn norm -> error
- 1 PLUS norm foo baz
- 1 DASH norm foo bar baz
- 1 EQAL norm foo bar baz
- 1 QSTN norm -> error
- 2 plus norm foo bar baz
- 2 dash norm foo baz
- 2 eqal norm foo baz
- 2 qstn norm foo baz
- 2 PLUS norm foo baz
- 2 DASH norm foo bar baz
- 2 EQAL norm foo bar baz
- 2 QSTN norm -> error
- 3 plus norm foo bar baz
- 3 dash norm foo x baz
- 3 eqal norm foo x baz
- 3 qstn norm foo x baz
- 3 PLUS norm foo bar baz
- 3 DASH norm foo x baz
- 3 EQAL norm foo x baz
- 3 QSTN norm foo x baz
- 4 plus parn foo baz
- 4 dash parn foo (bar) baz
- 4 eqal parn foo (bar) baz
- 4 qstn parn -> error
- 4 PLUS parn foo baz
- 4 DASH parn foo (bar) baz
- 4 EQAL parn foo (bar) baz
- 4 QSTN parn -> error
- 5 plus parn foo (bar) baz
- 5 dash parn foo baz
- 5 eqal parn foo baz
- 5 qstn parn foo baz
- 5 PLUS parn foo baz
- 5 DASH parn foo (bar) baz
- 5 EQAL parn foo (bar) baz
- 5 QSTN parn -> error
- 6 plus parn foo (bar) baz
- 6 dash parn foo x baz
- 6 eqal parn foo x baz
- 6 qstn parn foo x baz
- 6 PLUS parn foo (bar) baz
- 6 DASH parn foo x baz
- 6 EQAL parn foo x baz
- 6 QSTN parn foo x baz
- 7 plus brac foo c } baz
- 7 dash brac foo ax{{{}b c d{} baz
- 7 eqal brac foo ax{{{}b c ax{{{}b} baz
- 7 qstn brac -> error
- 7 PLUS brac foo c } baz
- 7 DASH brac foo ax{{{}b c d{} baz
- 7 EQAL brac foo ax{{{}b c ax{{{}b} baz
- 7 QSTN brac -> error
- 8 plus brac foo ax{{{}b c d{} baz
- 8 dash brac foo c } baz
- 8 eqal brac foo c } baz
- 8 qstn brac foo c } baz
- 8 PLUS brac foo c } baz
- 8 DASH brac foo ax{{{}b c d{} baz
- 8 EQAL brac foo ax{{{}b c ax{{{}b} baz
- 8 QSTN brac -> error
- 9 plus brac foo ax{{{}b c d{} baz
- 9 dash brac foo x c x} baz
- 9 eqal brac foo x c x} baz
- 9 qstn brac foo x c x} baz
- 9 PLUS brac foo ax{{{}b c d{} baz
- 9 DASH brac foo x c x} baz
- 9 EQAL brac foo x c x} baz
- 9 QSTN brac foo x c x} baz
----
-name: eglob-bad-1
-description:
- Check that globbing isn't done when glob has syntax error
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcz"
-file-setup: file 644 "bbc"
-stdin:
- echo !([*)*
- echo +(a|b[)*
-expected-stdout:
- !([*)*
- +(a|b[)*
----
-name: eglob-bad-2
-description:
- Check that globbing isn't done when glob has syntax error
- (AT&T ksh fails this test)
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcz"
-file-setup: file 644 "bbc"
-stdin:
- echo [a*(]*)z
-expected-stdout:
- [a*(]*)z
----
-name: eglob-infinite-plus
-description:
- Check that shell doesn't go into infinite loop expanding +(...)
- expressions.
-file-setup: file 644 "abc"
-time-limit: 3
-stdin:
- echo +()c
- echo +()x
- echo +(*)c
- echo +(*)x
-expected-stdout:
- +()c
- +()x
- abc
- +(*)x
----
-name: eglob-subst-1
-description:
- Check that eglobbing isn't done on substitution results
-file-setup: file 644 "abc"
-stdin:
- x='@(*)'
- echo $x
-expected-stdout:
- @(*)
----
-name: eglob-nomatch-1
-description:
- Check that the pattern doesn't match
-stdin:
- echo 1: no-file+(a|b)stuff
- echo 2: no-file+(a*(c)|b)stuff
- echo 3: no-file+((((c)))|b)stuff
-expected-stdout:
- 1: no-file+(a|b)stuff
- 2: no-file+(a*(c)|b)stuff
- 3: no-file+((((c)))|b)stuff
----
-name: eglob-match-1
-description:
- Check that the pattern matches correctly
-file-setup: file 644 "abd"
-file-setup: file 644 "acd"
-file-setup: file 644 "abac"
-stdin:
- echo 1: a+(b|c)d
- echo 2: a!(@(b|B))d
- echo 3: *(a(b|c)) # (...|...) can be used within X(..)
- echo 4: a[b*(foo|bar)]d # patterns not special inside [...]
-expected-stdout:
- 1: abd acd
- 2: acd
- 3: abac
- 4: abd
----
-name: eglob-case-1
-description:
- Simple negation tests
-stdin:
- case foo in !(foo|bar)) echo yes;; *) echo no;; esac
- case bar in !(foo|bar)) echo yes;; *) echo no;; esac
-expected-stdout:
- no
- no
----
-name: eglob-case-2
-description:
- Simple kleene tests
-stdin:
- case foo in *(a|b[)) echo yes;; *) echo no;; esac
- case foo in *(a|b[)|f*) echo yes;; *) echo no;; esac
- case '*(a|b[)' in *(a|b[)) echo yes;; *) echo no;; esac
-expected-stdout:
- no
- yes
- yes
----
-name: eglob-trim-1
-description:
- Eglobbing in trim expressions...
- (AT&T ksh fails this - docs say # matches shortest string, ## matches
- longest...)
-stdin:
- x=abcdef
- echo 1: ${x#a|abc}
- echo 2: ${x##a|abc}
- echo 3: ${x%def|f}
- echo 4: ${x%%f|def}
-expected-stdout:
- 1: bcdef
- 2: def
- 3: abcde
- 4: abc
----
-name: eglob-trim-2
-description:
- Check eglobbing works in trims...
-stdin:
- x=abcdef
- echo 1: ${x#*(a|b)cd}
- echo 2: "${x#*(a|b)cd}"
- echo 3: ${x#"*(a|b)cd"}
- echo 4: ${x#a(b|c)}
-expected-stdout:
- 1: ef
- 2: ef
- 3: abcdef
- 4: cdef
----
-name: eglob-substrpl-1
-description:
- Check eglobbing works in substs... and they work at all
-stdin:
- [[ -n $BASH_VERSION ]] && shopt -s extglob
- x=1222321_ab/cde_b/c_1221
- y=xyz
- echo 1: ${x/2}
- echo 2: ${x//2}
- echo 3: ${x/+(2)}
- echo 4: ${x//+(2)}
- echo 5: ${x/2/4}
- echo 6: ${x//2/4}
- echo 7: ${x/+(2)/4}
- echo 8: ${x//+(2)/4}
- echo 9: ${x/b/c/e/f}
- echo 10: ${x/b\/c/e/f}
- echo 11: ${x/b\/c/e\/f}
- echo 12: ${x/b\/c/e\\/f}
- echo 13: ${x/b\\/c/e\\/f}
- echo 14: ${x//b/c/e/f}
- echo 15: ${x//b\/c/e/f}
- echo 16: ${x//b\/c/e\/f}
- echo 17: ${x//b\/c/e\\/f}
- echo 18: ${x//b\\/c/e\\/f}
- echo 19: ${x/b\/*\/c/x}
- echo 20: ${x/\//.}
- echo 21: ${x//\//.}
- echo 22: ${x///.}
- echo 23: ${x//#1/9}
- echo 24: ${x//%1/9}
- echo 25: ${x//\%1/9}
- echo 26: ${x//\\%1/9}
- echo 27: ${x//\a/9}
- echo 28: ${x//\\a/9}
- echo 29: ${x/2/$y}
-expected-stdout:
- 1: 122321_ab/cde_b/c_1221
- 2: 131_ab/cde_b/c_11
- 3: 1321_ab/cde_b/c_1221
- 4: 131_ab/cde_b/c_11
- 5: 1422321_ab/cde_b/c_1221
- 6: 1444341_ab/cde_b/c_1441
- 7: 14321_ab/cde_b/c_1221
- 8: 14341_ab/cde_b/c_141
- 9: 1222321_ac/e/f/cde_b/c_1221
- 10: 1222321_ae/fde_b/c_1221
- 11: 1222321_ae/fde_b/c_1221
- 12: 1222321_ae\/fde_b/c_1221
- 13: 1222321_ab/cde_b/c_1221
- 14: 1222321_ac/e/f/cde_c/e/f/c_1221
- 15: 1222321_ae/fde_e/f_1221
- 16: 1222321_ae/fde_e/f_1221
- 17: 1222321_ae\/fde_e\/f_1221
- 18: 1222321_ab/cde_b/c_1221
- 19: 1222321_ax_1221
- 20: 1222321_ab.cde_b/c_1221
- 21: 1222321_ab.cde_b.c_1221
- 22: 1222321_ab/cde_b/c_1221
- 23: 9222321_ab/cde_b/c_1221
- 24: 1222321_ab/cde_b/c_1229
- 25: 1222321_ab/cde_b/c_1229
- 26: 1222321_ab/cde_b/c_1221
- 27: 1222321_9b/cde_b/c_1221
- 28: 1222321_9b/cde_b/c_1221
- 29: 1xyz22321_ab/cde_b/c_1221
----
-name: eglob-substrpl-2
-description:
- Check anchored substring replacement works, corner cases
-stdin:
- foo=123
- echo 1: ${foo/#/x}
- echo 2: ${foo/%/x}
- echo 3: ${foo/#/}
- echo 4: ${foo/#}
- echo 5: ${foo/%/}
- echo 6: ${foo/%}
-expected-stdout:
- 1: x123
- 2: 123x
- 3: 123
- 4: 123
- 5: 123
- 6: 123
----
-name: eglob-substrpl-3a
-description:
- Check substring replacement works with variables and slashes, too
-stdin:
- pfx=/home/user
- wd=/home/user/tmp
- echo "${wd/#$pfx/~}"
- echo "${wd/#\$pfx/~}"
- echo "${wd/#"$pfx"/~}"
- echo "${wd/#'$pfx'/~}"
- echo "${wd/#"\$pfx"/~}"
- echo "${wd/#'\$pfx'/~}"
-expected-stdout:
- ~/tmp
- /home/user/tmp
- ~/tmp
- /home/user/tmp
- /home/user/tmp
- /home/user/tmp
----
-name: eglob-substrpl-3b
-description:
- More of this, bash fails it (bash4 passes)
-stdin:
- pfx=/home/user
- wd=/home/user/tmp
- echo "${wd/#$(echo /home/user)/~}"
- echo "${wd/#"$(echo /home/user)"/~}"
- echo "${wd/#'$(echo /home/user)'/~}"
-expected-stdout:
- ~/tmp
- ~/tmp
- /home/user/tmp
----
-name: eglob-substrpl-3c
-description:
- Even more weird cases
-stdin:
- pfx=/home/user
- wd='$pfx/tmp'
- echo 1: ${wd/#$pfx/~}
- echo 2: ${wd/#\$pfx/~}
- echo 3: ${wd/#"$pfx"/~}
- echo 4: ${wd/#'$pfx'/~}
- echo 5: ${wd/#"\$pfx"/~}
- echo 6: ${wd/#'\$pfx'/~}
- ts='a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)'
- tp=a/b
- tr=c/d
- [[ -n $BASH_VERSION ]] && shopt -s extglob
- echo 7: ${ts/a\/b/$tr}
- echo 8: ${ts/a\/b/\$tr}
- echo 9: ${ts/$tp/$tr}
- echo 10: ${ts/\$tp/$tr}
- echo 11: ${ts/\\$tp/$tr}
- echo 12: ${ts/$tp/c/d}
- echo 13: ${ts/$tp/c\/d}
- echo 14: ${ts/$tp/c\\/d}
- echo 15: ${ts/+(a\/b)/$tr}
- echo 16: ${ts/+(a\/b)/\$tr}
- echo 17: ${ts/+($tp)/$tr}
- echo 18: ${ts/+($tp)/c/d}
- echo 19: ${ts/+($tp)/c\/d}
- echo 25: ${ts//a\/b/$tr}
- echo 26: ${ts//a\/b/\$tr}
- echo 27: ${ts//$tp/$tr}
- echo 28: ${ts//$tp/c/d}
- echo 29: ${ts//$tp/c\/d}
- echo 30: ${ts//+(a\/b)/$tr}
- echo 31: ${ts//+(a\/b)/\$tr}
- echo 32: ${ts//+($tp)/$tr}
- echo 33: ${ts//+($tp)/c/d}
- echo 34: ${ts//+($tp)/c\/d}
- tp="+($tp)"
- echo 40: ${ts/$tp/$tr}
- echo 41: ${ts//$tp/$tr}
-expected-stdout:
- 1: $pfx/tmp
- 2: ~/tmp
- 3: $pfx/tmp
- 4: ~/tmp
- 5: ~/tmp
- 6: ~/tmp
- 7: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 8: $tra/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 9: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 10: a/ba/bc/d$tp_a/b$tp_*(a/b)_*($tp)
- 11: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 12: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 13: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 14: c\/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 15: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
- 16: $tr$tp$tp_a/b$tp_*(a/b)_*($tp)
- 17: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
- 18: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
- 19: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
- 25: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 26: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp)
- 27: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 28: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 29: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 30: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 31: $tr$tp$tp_$tr$tp_*($tr)_*($tp)
- 32: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 33: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 34: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
- 40: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
- 41: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-# This is what GNU bash does:
-# 40: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-# 41: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
----
-name: eglob-utf8-1
-description:
- UTF-8 mode differences for eglobbing
-stdin:
- s=blöd
- set +U
- print 1: ${s%???} .
- print 2: ${s/b???d/x} .
- set -U
- print 3: ${s%???} .
- print 4: ${s/b??d/x} .
- x=nö
- print 5: ${x%?} ${x%%?} .
- x=äh
- print 6: ${x#?} ${x##?} .
- x=
- print 7: ${x%?} ${x%%?} .
- x=mä
- print 8: ${x%?} ${x%%?} .
- x=何
- print 9: ${x%?} ${x%%?} .
-expected-stdout:
- 1: bl .
- 2: x .
- 3: b .
- 4: x .
- 5: n n .
- 6: h h .
- 7: .
- 8: mä mä .
- 9: .
----
-name: glob-bad-1
-description:
- Check that globbing isn't done when glob has syntax error
-file-setup: dir 755 "[x"
-file-setup: file 644 "[x/foo"
-stdin:
- echo [*
- echo *[x
- echo [x/*
-expected-stdout:
- [*
- *[x
- [x/foo
----
-name: glob-bad-2
-description:
- Check that symbolic links aren't stat()'d
-file-setup: dir 755 "dir"
-file-setup: symlink 644 "dir/abc"
- non-existent-file
-stdin:
- echo d*/*
- echo d*/abc
-expected-stdout:
- dir/abc
- dir/abc
----
-name: glob-range-1
-description:
- Test range matching
-file-setup: file 644 ".bc"
-file-setup: file 644 "abc"
-file-setup: file 644 "bbc"
-file-setup: file 644 "cbc"
-file-setup: file 644 "-bc"
-stdin:
- echo [ab-]*
- echo [-ab]*
- echo [!-ab]*
- echo [!ab]*
- echo []ab]*
-expected-stdout:
- -bc abc bbc
- -bc abc bbc
- cbc
- -bc cbc
- abc bbc
----
-name: glob-range-2
-description:
- Test range matching
- (AT&T ksh fails this; POSIX says invalid)
-file-setup: file 644 "abc"
-stdin:
- echo [a--]*
-expected-stdout:
- [a--]*
----
-name: glob-range-3
-description:
- Check that globbing matches the right things...
-# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
-category: !os:darwin
-file-setup: file 644 "aÂc"
-stdin:
- echo a[Á-Ú]*
-expected-stdout:
- aÂc
----
-name: glob-range-4
-description:
- Results unspecified according to POSIX
-file-setup: file 644 ".bc"
-stdin:
- echo [a.]*
-expected-stdout:
- [a.]*
----
-name: glob-range-5
-description:
- Results unspecified according to POSIX
- (AT&T ksh treats this like [a-cc-e]*)
-file-setup: file 644 "abc"
-file-setup: file 644 "bbc"
-file-setup: file 644 "cbc"
-file-setup: file 644 "dbc"
-file-setup: file 644 "ebc"
-file-setup: file 644 "-bc"
-stdin:
- echo [a-c-e]*
-expected-stdout:
- -bc abc bbc cbc ebc
----
-name: heredoc-1
-description:
- Check ordering/content of redundent here documents.
-stdin:
- cat << EOF1 << EOF2
- hi
- EOF1
- there
- EOF2
-expected-stdout:
- there
----
-name: heredoc-2
-description:
- Check quoted here-doc is protected.
-stdin:
- a=foo
- cat << 'EOF'
- hi\
- there$a
- stuff
- EO\
- F
- EOF
-expected-stdout:
- hi\
- there$a
- stuff
- EO\
- F
----
-name: heredoc-3
-description:
- Check that newline isn't needed after heredoc-delimiter marker.
-stdin: !
- cat << EOF
- hi
- there
- EOF
-expected-stdout:
- hi
- there
----
-name: heredoc-4
-description:
- Check that an error occurs if the heredoc-delimiter is missing.
-stdin: !
- cat << EOF
- hi
- there
-expected-exit: e > 0
-expected-stderr-pattern: /.*/
----
-name: heredoc-5
-description:
- Check that backslash quotes a $, ` and \ and kills a \newline
-stdin:
- a=BAD
- b=ok
- cat << EOF
- h\${a}i
- h\\${b}i
- th\`echo not-run\`ere
- th\\`echo is-run`ere
- fol\\ks
- more\\
- last \
- line
- EOF
-expected-stdout:
- h${a}i
- h\oki
- th`echo not-run`ere
- th\is-runere
- fol\ks
- more\
- last line
----
-name: heredoc-6
-description:
- Check that \newline in initial here-delim word doesn't imply
- a quoted here-doc.
-stdin:
- a=i
- cat << EO\
- F
- h$a
- there
- EOF
-expected-stdout:
- hi
- there
----
-name: heredoc-7
-description:
- Check that double quoted $ expressions in here delimiters are
- not expanded and match the delimiter.
- POSIX says only quote removal is applied to the delimiter.
-stdin:
- a=b
- cat << "E$a"
- hi
- h$a
- hb
- E$a
- echo done
-expected-stdout:
- hi
- h$a
- hb
- done
----
-name: heredoc-8
-description:
- Check that double quoted escaped $ expressions in here
- delimiters are not expanded and match the delimiter.
- POSIX says only quote removal is applied to the delimiter
- (\ counts as a quote).
-stdin:
- a=b
- cat << "E\$a"
- hi
- h$a
- h\$a
- hb
- h\b
- E$a
- echo done
-expected-stdout:
- hi
- h$a
- h\$a
- hb
- h\b
- done
----
-name: heredoc-9a
-description:
- Check that here strings work.
-stdin:
- bar="bar
- baz"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<foo
- "$__progname" -c "tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<foo"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$bar"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<'$bar'
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$bar
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<-foo
-expected-stdout:
- sbb
- sbb
- one
- onm
- $one
- $one
- -sbb
----
-name: heredoc-9b
-description:
- Check that a corner case of here strings works like bash
-stdin:
- fnord=42
- bar="bar
- \$fnord baz"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
-expected-stdout:
- one $sabeq onm
-category: bash
----
-name: heredoc-9c
-description:
- Check that a corner case of here strings works like ksh93, zsh
-stdin:
- fnord=42
- bar="bar
- \$fnord baz"
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
-expected-stdout:
- one
- $sabeq onm
----
-name: heredoc-9d
-description:
- Check another corner case of here strings
-stdin:
- tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
-expected-stdout:
- one
----
-name: heredoc-quoting-unsubst
-description:
- Check for correct handling of quoted characters in
- here documents without substitution (marker is quoted).
-stdin:
- foo=bar
- cat <<-'EOF'
- x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
- EOF
-expected-stdout:
- x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
----
-name: heredoc-quoting-subst
-description:
- Check for correct handling of quoted characters in
- here documents with substitution (marker is not quoted).
-stdin:
- foo=bar
- cat <<-EOF
- x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
- EOF
-expected-stdout:
- x " \" \ \ $ $ baz `echo baz` bar $foo x
----
-name: heredoc-tmpfile-1
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Heredoc in simple command.
-stdin:
- TMPDIR=$PWD
- eval '
- cat <<- EOF
- hi
- EOF
- for i in a b ; do
- cat <<- EOF
- more
- EOF
- done
- ' &
- sleep 1
- echo Left overs: *
-expected-stdout:
- hi
- more
- more
- Left overs: *
----
-name: heredoc-tmpfile-2
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Heredoc in function, multiple calls to function.
-stdin:
- TMPDIR=$PWD
- eval '
- foo() {
- cat <<- EOF
- hi
- EOF
- }
- foo
- foo
- ' &
- sleep 1
- echo Left overs: *
-expected-stdout:
- hi
- hi
- Left overs: *
----
-name: heredoc-tmpfile-3
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Heredoc in function in loop, multiple calls to function.
-stdin:
- TMPDIR=$PWD
- eval '
- foo() {
- cat <<- EOF
- hi
- EOF
- }
- for i in a b; do
- foo
- foo() {
- cat <<- EOF
- folks $i
- EOF
- }
- done
- foo
- ' &
- sleep 1
- echo Left overs: *
-expected-stdout:
- hi
- folks b
- folks b
- Left overs: *
----
-name: heredoc-tmpfile-4
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Backgrounded simple command with here doc
-stdin:
- TMPDIR=$PWD
- eval '
- cat <<- EOF &
- hi
- EOF
- ' &
- sleep 1
- echo Left overs: *
-expected-stdout:
- hi
- Left overs: *
----
-name: heredoc-tmpfile-5
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Backgrounded subshell command with here doc
-stdin:
- TMPDIR=$PWD
- eval '
- (
- sleep 1 # so parent exits
- echo A
- cat <<- EOF
- hi
- EOF
- echo B
- ) &
- ' &
- sleep 2
- echo Left overs: *
-expected-stdout:
- A
- hi
- B
- Left overs: *
----
-name: heredoc-tmpfile-6
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Heredoc in pipeline.
-stdin:
- TMPDIR=$PWD
- eval '
- cat <<- EOF | sed "s/hi/HI/"
- hi
- EOF
- ' &
- sleep 1
- echo Left overs: *
-expected-stdout:
- HI
- Left overs: *
----
-name: heredoc-tmpfile-7
-description:
- Check that heredoc temp files aren't removed too soon or too late.
- Heredoc in backgrounded pipeline.
-stdin:
- TMPDIR=$PWD
- eval '
- cat <<- EOF | sed 's/hi/HI/' &
- hi
- EOF
- ' &
- sleep 1
- echo Left overs: *
-expected-stdout:
- HI
- Left overs: *
----
-name: heredoc-tmpfile-8
-description:
- Check that heredoc temp files aren't removed too soon or too
- late. Heredoc in function, backgrounded call to function.
- This check can fail on slow machines (<100 MHz), or Cygwin,
- that's normal.
-stdin:
- TMPDIR=$PWD
- # Background eval so main shell doesn't do parsing
- eval '
- foo() {
- cat <<- EOF
- hi
- EOF
- }
- foo
- # sleep so eval can die
- (sleep 1; foo) &
- (sleep 1; foo) &
- foo
- ' &
- sleep 2
- echo Left overs: *
-expected-stdout:
- hi
- hi
- hi
- hi
- Left overs: *
----
-name: history-basic
-description:
- See if we can test history at all
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo hi
- fc -l
-expected-stdout:
- hi
- 1 echo hi
-expected-stderr-pattern:
- /^X*$/
----
-name: history-dups
-description:
- Verify duplicates and spaces are not entered
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo hi
- echo yo
- echo hi
- fc -l
-expected-stdout:
- hi
- yo
- hi
- 1 echo hi
-expected-stderr-pattern:
- /^X*$/
----
-name: history-unlink
-description:
- Check if broken HISTFILEs do not cause trouble
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=foo/hist.file!
-file-setup: file 644 "Env"
- PS1=X
-file-setup: dir 755 "foo"
-file-setup: file 644 "foo/hist.file"
- sometext
-time-limit: 5
-perl-setup: chmod(0555, "foo");
-stdin:
- echo hi
- fc -l
- chmod 0755 foo
-expected-stdout:
- hi
- 1 echo hi
-expected-stderr-pattern:
- /(.*cannot unlink HISTFILE.*\n)?X*$/
----
-name: history-e-minus-1
-description:
- Check if more recent command is executed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo hi
- echo there
- fc -e -
-expected-stdout:
- hi
- there
- there
-expected-stderr-pattern:
- /^X*echo there\nX*$/
----
-name: history-e-minus-2
-description:
- Check that repeated command is printed before command
- is re-executed.
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- exec 2>&1
- echo hi
- echo there
- fc -e -
-expected-stdout-pattern:
- /X*hi\nX*there\nX*echo there\nthere\nX*/
-expected-stderr-pattern:
- /^X*$/
----
-name: history-e-minus-3
-description:
- fc -e - fails when there is no history
- (ksh93 has a bug that causes this to fail)
- (ksh88 loops on this)
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- fc -e -
- echo ok
-expected-stdout:
- ok
-expected-stderr-pattern:
- /^X*.*:.*history.*\nX*$/
----
-name: history-e-minus-4
-description:
- Check if "fc -e -" command output goes to stdout.
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc
- fc -e - | (read x; echo "A $x")
- echo ok
-expected-stdout:
- abc
- A abc
- ok
-expected-stderr-pattern:
- /^X*echo abc\nX*/
----
-name: history-e-minus-5
-description:
- fc is replaced in history by new command.
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- echo ghi jkl
- :
- fc -e - echo
- fc -l 2 5
-expected-stdout:
- abc def
- ghi jkl
- ghi jkl
- 2 echo ghi jkl
- 3 :
- 4 echo ghi jkl
- 5 fc -l 2 5
-expected-stderr-pattern:
- /^X*echo ghi jkl\nX*$/
----
-name: history-list-1
-description:
- List lists correct range
- (ksh88 fails 'cause it lists the fc command)
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- fc -l -- -2
-expected-stdout:
- line 1
- line 2
- line 3
- 2 echo line 2
- 3 echo line 3
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-2
-description:
- Lists oldest history if given pre-historic number
- (ksh93 has a bug that causes this to fail)
- (ksh88 fails 'cause it lists the fc command)
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- fc -l -- -40
-expected-stdout:
- line 1
- line 2
- line 3
- 1 echo line 1
- 2 echo line 2
- 3 echo line 3
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-3
-description:
- Can give number 'options' to fc
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- fc -l -3 -2
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- 2 echo line 2
- 3 echo line 3
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-4
-description:
- -1 refers to previous command
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- fc -l -1 -1
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- 4 echo line 4
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-5
-description:
- List command stays in history
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- fc -l -1 -1
- fc -l -2 -1
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- 4 echo line 4
- 4 echo line 4
- 5 fc -l -1 -1
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-6
-description:
- HISTSIZE limits about of history kept.
- (ksh88 fails 'cause it lists the fc command)
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- echo line 5
- fc -l
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- line 5
- 4 echo line 4
- 5 echo line 5
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-7
-description:
- fc allows too old/new errors in range specification
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- echo line 5
- fc -l 1 30
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- line 5
- 4 echo line 4
- 5 echo line 5
- 6 fc -l 1 30
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-r-1
-description:
- test -r flag in history
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- echo line 5
- fc -l -r 2 4
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- line 5
- 4 echo line 4
- 3 echo line 3
- 2 echo line 2
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-r-2
-description:
- If first is newer than last, -r is implied.
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- echo line 5
- fc -l 4 2
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- line 5
- 4 echo line 4
- 3 echo line 3
- 2 echo line 2
-expected-stderr-pattern:
- /^X*$/
----
-name: history-list-r-3
-description:
- If first is newer than last, -r is cancelled.
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2
- echo line 3
- echo line 4
- echo line 5
- fc -l -r 4 2
-expected-stdout:
- line 1
- line 2
- line 3
- line 4
- line 5
- 2 echo line 2
- 3 echo line 3
- 4 echo line 4
-expected-stderr-pattern:
- /^X*$/
----
-name: history-subst-1
-description:
- Basic substitution
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- echo ghi jkl
- fc -e - abc=AB 'echo a'
-expected-stdout:
- abc def
- ghi jkl
- AB def
-expected-stderr-pattern:
- /^X*echo AB def\nX*$/
----
-name: history-subst-2
-description:
- Does subst find previous command?
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- echo ghi jkl
- fc -e - jkl=XYZQRT 'echo g'
-expected-stdout:
- abc def
- ghi jkl
- ghi XYZQRT
-expected-stderr-pattern:
- /^X*echo ghi XYZQRT\nX*$/
----
-name: history-subst-3
-description:
- Does subst find previous command when no arguments given
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- echo ghi jkl
- fc -e - jkl=XYZQRT
-expected-stdout:
- abc def
- ghi jkl
- ghi XYZQRT
-expected-stderr-pattern:
- /^X*echo ghi XYZQRT\nX*$/
----
-name: history-subst-4
-description:
- Global substitutions work
- (ksh88 and ksh93 do not have -g option)
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def asjj sadjhasdjh asdjhasd
- fc -e - -g a=FooBAR
-expected-stdout:
- abc def asjj sadjhasdjh asdjhasd
- FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd
-expected-stderr-pattern:
- /^X*echo FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd\nX*$/
----
-name: history-subst-5
-description:
- Make sure searches don't find current (fc) command
- (ksh88/ksh93 don't have the ? prefix thing so they fail this test)
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- echo ghi jkl
- fc -e - abc=AB \?abc
-expected-stdout:
- abc def
- ghi jkl
- AB def
-expected-stderr-pattern:
- /^X*echo AB def\nX*$/
----
-name: history-ed-1-old
-description:
- Basic (ed) editing works (assumes you have generic ed editor
- that prints no prompts). This is for oldish ed(1) which write
- the character count to stdout.
-category: stdout-ed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- fc echo
- s/abc/FOOBAR/
- w
- q
-expected-stdout:
- abc def
- 13
- 16
- FOOBAR def
-expected-stderr-pattern:
- /^X*echo FOOBAR def\nX*$/
----
-name: history-ed-2-old
-description:
- Correct command is edited when number given
-category: stdout-ed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2 is here
- echo line 3
- echo line 4
- fc 2
- s/is here/is changed/
- w
- q
-expected-stdout:
- line 1
- line 2 is here
- line 3
- line 4
- 20
- 23
- line 2 is changed
-expected-stderr-pattern:
- /^X*echo line 2 is changed\nX*$/
----
-name: history-ed-3-old
-description:
- Newly created multi line commands show up as single command
- in history.
- (NOTE: adjusted for COMPLEX HISTORY compile time option)
- (ksh88 fails 'cause it lists the fc command)
-category: stdout-ed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- fc echo
- s/abc/FOOBAR/
- $a
- echo a new line
- .
- w
- q
- fc -l
-expected-stdout:
- abc def
- 13
- 32
- FOOBAR def
- a new line
- 1 echo abc def
- 2 echo FOOBAR def
- 3 echo a new line
-expected-stderr-pattern:
- /^X*echo FOOBAR def\necho a new line\nX*$/
----
-name: history-ed-1
-description:
- Basic (ed) editing works (assumes you have generic ed editor
- that prints no prompts). This is for newish ed(1) and stderr.
-category: !no-stderr-ed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- fc echo
- s/abc/FOOBAR/
- w
- q
-expected-stdout:
- abc def
- FOOBAR def
-expected-stderr-pattern:
- /^X*13\n16\necho FOOBAR def\nX*$/
----
-name: history-ed-2
-description:
- Correct command is edited when number given
-category: !no-stderr-ed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo line 1
- echo line 2 is here
- echo line 3
- echo line 4
- fc 2
- s/is here/is changed/
- w
- q
-expected-stdout:
- line 1
- line 2 is here
- line 3
- line 4
- line 2 is changed
-expected-stderr-pattern:
- /^X*20\n23\necho line 2 is changed\nX*$/
----
-name: history-ed-3
-description:
- Newly created multi line commands show up as single command
- in history.
-category: !no-stderr-ed
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- echo abc def
- fc echo
- s/abc/FOOBAR/
- $a
- echo a new line
- .
- w
- q
- fc -l
-expected-stdout:
- abc def
- FOOBAR def
- a new line
- 1 echo abc def
- 2 echo FOOBAR def
- 3 echo a new line
-expected-stderr-pattern:
- /^X*13\n32\necho FOOBAR def\necho a new line\nX*$/
----
-name: IFS-space-1
-description:
- Simple test, default IFS
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- set -- A B C
- showargs 1 $*
- showargs 2 "$*"
- showargs 3 $@
- showargs 4 "$@"
-expected-stdout:
- <1> <A> <B> <C>
- <2> <A B C>
- <3> <A> <B> <C>
- <4> <A> <B> <C>
----
-name: IFS-colon-1
-description:
- Simple test, IFS=:
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS=:
- set -- A B C
- showargs 1 $*
- showargs 2 "$*"
- showargs 3 $@
- showargs 4 "$@"
-expected-stdout:
- <1> <A> <B> <C>
- <2> <A:B:C>
- <3> <A> <B> <C>
- <4> <A> <B> <C>
----
-name: IFS-null-1
-description:
- Simple test, IFS=""
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS=""
- set -- A B C
- showargs 1 $*
- showargs 2 "$*"
- showargs 3 $@
- showargs 4 "$@"
-expected-stdout:
- <1> <A B C>
- <2> <ABC>
- <3> <A B C>
- <4> <A B C>
----
-name: IFS-space-colon-1
-description:
- Simple test, IFS=<white-space>:
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS="$IFS:"
- set --
- showargs 1 $*
- showargs 2 "$*"
- showargs 3 $@
- showargs 4 "$@"
- showargs 5 : "$@"
-expected-stdout:
- <1>
- <2> <>
- <3>
- <4>
- <5> <:>
----
-name: IFS-space-colon-2
-description:
- Simple test, IFS=<white-space>:
- AT&T ksh fails this, POSIX says the test is correct.
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS="$IFS:"
- set --
- showargs :"$@"
-expected-stdout:
- <:>
----
-name: IFS-space-colon-3
-description:
- Simple test, IFS=<white-space>:
- pdksh fails both of these tests
- not sure whether #2 is correct
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS="$IFS:"
- x=
- set --
- showargs "$x$@" 1
- showargs "$@$x" 2
-expected-fail: yes
-expected-stdout:
- <> <1>
- <> <2>
----
-name: IFS-space-colon-4
-description:
- Simple test, IFS=<white-space>:
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS="$IFS:"
- set --
- showargs "$@$@"
-expected-stdout:
-
----
-name: IFS-space-colon-5
-description:
- Simple test, IFS=<white-space>:
- Don't know what POSIX thinks of this. AT&T ksh does not do this.
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS="$IFS:"
- set --
- showargs "${@:-}"
-expected-stdout:
- <>
----
-name: IFS-subst-1
-description:
- Simple test, IFS=<white-space>:
-stdin:
- showargs() { for i; do echo -n " <$i>"; done; echo; }
- IFS="$IFS:"
- x=":b: :"
- echo -n '1:'; for i in $x ; do echo -n " [$i]" ; done ; echo
- echo -n '2:'; for i in :b:: ; do echo -n " [$i]" ; done ; echo
- showargs 3 $x
- showargs 4 :b::
- x="a:b:"
- echo -n '5:'; for i in $x ; do echo -n " [$i]" ; done ; echo
- showargs 6 $x
- x="a::c"
- echo -n '7:'; for i in $x ; do echo -n " [$i]" ; done ; echo
- showargs 8 $x
- echo -n '9:'; for i in ${FOO-`echo -n h:i`th:ere} ; do echo -n " [$i]" ; done ; echo
- showargs 10 ${FOO-`echo -n h:i`th:ere}
- showargs 11 "${FOO-`echo -n h:i`th:ere}"
- x=" A : B::D"
- echo -n '12:'; for i in $x ; do echo -n " [$i]" ; done ; echo
- showargs 13 $x
-expected-stdout:
- 1: [] [b] []
- 2: [:b::]
- <3> <> <b> <>
- <4> <:b::>
- 5: [a] [b]
- <6> <a> <b>
- 7: [a] [] [c]
- <8> <a> <> <c>
- 9: [h] [ith] [ere]
- <10> <h> <ith> <ere>
- <11> <h:ith:ere>
- 12: [A] [B] [] [D]
- <13> <A> <B> <> <D>
----
-name: integer-base-err-1
-description:
- Can't have 0 base (causes shell to exit)
-expected-exit: e != 0
-stdin:
- typeset -i i
- i=3
- i=0#4
- echo $i
-expected-stderr-pattern:
- /^.*:.*0#4.*\n$/
----
-name: integer-base-err-2
-description:
- Can't have multiple bases in a 'constant' (causes shell to exit)
- (ksh88 fails this test)
-expected-exit: e != 0
-stdin:
- typeset -i i
- i=3
- i=2#110#11
- echo $i
-expected-stderr-pattern:
- /^.*:.*2#110#11.*\n$/
----
-name: integer-base-err-3
-description:
- Syntax errors in expressions and effects on bases
- (interactive so errors don't cause exits)
- (ksh88 fails this test - shell exits, even with -i)
-arguments: !-i!
-stdin:
- PS1= # minimise prompt hassles
- typeset -i4 a=10
- typeset -i a=2+
- echo $a
- typeset -i4 a=10
- typeset -i2 a=2+
- echo $a
-expected-stderr-pattern:
- /^([#\$] )?.*:.*2+.*\n.*:.*2+.*\n$/
-expected-stdout:
- 4#22
- 4#22
----
-name: integer-base-err-4
-description:
- Are invalid digits (according to base) errors?
- (ksh93 fails this test)
-expected-exit: e != 0
-stdin:
- typeset -i i;
- i=3#4
-expected-stderr-pattern:
- /^([#\$] )?.*:.*3#4.*\n$/
----
-name: integer-base-1
-description:
- Missing number after base is treated as 0.
-stdin:
- typeset -i i
- i=3
- i=2#
- echo $i
-expected-stdout:
- 0
----
-name: integer-base-2
-description:
- Check 'stickyness' of base in various situations
-stdin:
- typeset -i i=8
- echo $i
- echo ---------- A
- typeset -i4 j=8
- echo $j
- echo ---------- B
- typeset -i k=8
- typeset -i4 k=8
- echo $k
- echo ---------- C
- typeset -i4 l
- l=3#10
- echo $l
- echo ---------- D
- typeset -i m
- m=3#10
- echo $m
- echo ---------- E
- n=2#11
- typeset -i n
- echo $n
- n=10
- echo $n
- echo ---------- F
- typeset -i8 o=12
- typeset -i4 o
- echo $o
- echo ---------- G
- typeset -i p
- let p=8#12
- echo $p
-expected-stdout:
- 8
- ---------- A
- 4#20
- ---------- B
- 4#20
- ---------- C
- 4#3
- ---------- D
- 3#10
- ---------- E
- 2#11
- 2#1010
- ---------- F
- 4#30
- ---------- G
- 8#12
----
-name: integer-base-3
-description:
- More base parsing (hmm doesn't test much..)
-stdin:
- typeset -i aa
- aa=1+12#10+2
- echo $aa
- typeset -i bb
- bb=1+$aa
- echo $bb
- typeset -i bb
- bb=$aa
- echo $bb
- typeset -i cc
- cc=$aa
- echo $cc
-expected-stdout:
- 15
- 16
- 15
- 15
----
-name: integer-base-4
-description:
- Check that things not declared as integers are not made integers,
- also, check if base is not reset by -i with no arguments.
- (ksh93 fails - prints 10#20 - go figure)
-stdin:
- xx=20
- let xx=10
- typeset -i | grep '^xx='
- typeset -i4 a=10
- typeset -i a=20
- echo $a
-expected-stdout:
- 4#110
----
-name: integer-base-5
-description:
- More base stuff
-stdin:
- typeset -i4 a=3#10
- echo $a
- echo --
- typeset -i j=3
- j='~3'
- echo $j
- echo --
- typeset -i k=1
- x[k=k+1]=3
- echo $k
- echo --
- typeset -i l
- for l in 1 2+3 4; do echo $l; done
-expected-stdout:
- 4#3
- --
- -4
- --
- 2
- --
- 1
- 5
- 4
----
-name: integer-base-6
-description:
- Even more base stuff
- (ksh93 fails this test - prints 0)
-stdin:
- typeset -i7 i
- i=
- echo $i
-expected-stdout:
- 7#0
----
-name: integer-base-7
-description:
- Check that non-integer parameters don't get bases assigned
-stdin:
- echo $(( zz = 8#100 ))
- echo $zz
-expected-stdout:
- 64
- 64
----
-name: lineno-stdin
-description:
- See if $LINENO is updated and can be modified.
-stdin:
- echo A $LINENO
- echo B $LINENO
- LINENO=20
- echo C $LINENO
-expected-stdout:
- A 1
- B 2
- C 20
----
-name: lineno-inc
-description:
- See if $LINENO is set for .'d files.
-file-setup: file 644 "dotfile"
- echo dot A $LINENO
- echo dot B $LINENO
- LINENO=20
- echo dot C $LINENO
-stdin:
- echo A $LINENO
- echo B $LINENO
- . ./dotfile
-expected-stdout:
- A 1
- B 2
- dot A 1
- dot B 2
- dot C 20
----
-name: lineno-func
-description:
- See if $LINENO is set for commands in a function.
-stdin:
- echo A $LINENO
- echo B $LINENO
- bar() {
- echo func A $LINENO
- echo func B $LINENO
- }
- bar
- echo C $LINENO
-expected-stdout:
- A 1
- B 2
- func A 4
- func B 5
- C 8
----
-name: lineno-unset
-description:
- See if unsetting LINENO makes it non-magic.
-file-setup: file 644 "dotfile"
- echo dot A $LINENO
- echo dot B $LINENO
-stdin:
- unset LINENO
- echo A $LINENO
- echo B $LINENO
- bar() {
- echo func A $LINENO
- echo func B $LINENO
- }
- bar
- . ./dotfile
- echo C $LINENO
-expected-stdout:
- A
- B
- func A
- func B
- dot A
- dot B
- C
----
-name: lineno-unset-use
-description:
- See if unsetting LINENO makes it non-magic even
- when it is re-used.
-file-setup: file 644 "dotfile"
- echo dot A $LINENO
- echo dot B $LINENO
-stdin:
- unset LINENO
- LINENO=3
- echo A $LINENO
- echo B $LINENO
- bar() {
- echo func A $LINENO
- echo func B $LINENO
- }
- bar
- . ./dotfile
- echo C $LINENO
-expected-stdout:
- A 3
- B 3
- func A 3
- func B 3
- dot A 3
- dot B 3
- C 3
----
-name: lineno-trap
-description:
- Check if LINENO is tracked in traps
-stdin:
- fail() {
- echo "line <$1>"
- exit 1
- }
- trap 'fail $LINENO' INT ERR
- false
-expected-stdout:
- line <6>
-expected-exit: 1
----
-name: read-IFS-1
-description:
- Simple test, default IFS
-stdin:
- echo "A B " > IN
- unset x y z
- read x y z < IN
- echo 1: "x[$x] y[$y] z[$z]"
- echo 1a: ${z-z not set}
- read x < IN
- echo 2: "x[$x]"
-expected-stdout:
- 1: x[A] y[B] z[]
- 1a:
- 2: x[A B]
----
-name: read-ksh-1
-description:
- If no var specified, REPLY is used
-stdin:
- echo "abc" > IN
- read < IN
- echo "[$REPLY]";
-expected-stdout:
- [abc]
----
-name: regression-1
-description:
- Lex array code had problems with this.
-stdin:
- echo foo[
- n=bar
- echo "hi[ $n ]=1"
-expected-stdout:
- foo[
- hi[ bar ]=1
----
-name: regression-2
-description:
- When PATH is set before running a command, the new path is
- not used in doing the path search
- $ echo echo hi > /tmp/q ; chmod a+rx /tmp/q
- $ PATH=/tmp q
- q: not found
- $
- in comexec() the two lines
- while (*vp != NULL)
- (void) typeset(*vp++, xxx, 0);
- need to be moved out of the switch to before findcom() is
- called - I don't know what this will break.
-stdin:
- : ${PWD:-`pwd 2> /dev/null`}
- : ${PWD:?"PWD not set - can't do test"}
- mkdir Y
- cat > Y/xxxscript << EOF
- #!/bin/sh
- # Need to restore path so echo can be found (some shells don't have
- # it as a built-in)
- PATH=\$OLDPATH
- echo hi
- exit 0
- EOF
- chmod a+rx Y/xxxscript
- export OLDPATH="$PATH"
- PATH=$PWD/Y xxxscript
- exit $?
-expected-stdout:
- hi
----
-name: regression-6
-description:
- Parsing of $(..) expressions is non-optimal. It is
- impossible to have any parentheses inside the expression.
- I.e.,
- $ ksh -c 'echo $(echo \( )'
- no closing quote
- $ ksh -c 'echo $(echo "(" )'
- no closing quote
- $
- The solution is to hack the parsing clode in lex.c, the
- question is how to hack it: should any parentheses be
- escaped by a backslash, or should recursive parsing be done
- (so quotes could also be used to hide hem). The former is
- easier, the later better...
-stdin:
- echo $(echo \()
-expected-stdout:
- (
----
-name: regression-9
-description:
- Continue in a for loop does not work right:
- for i in a b c ; do
- if [ $i = b ] ; then
- continue
- fi
- echo $i
- done
- Prints a forever...
-stdin:
- first=yes
- for i in a b c ; do
- if [ $i = b ] ; then
- if [ $first = no ] ; then
- echo 'continue in for loop broken'
- break # hope break isn't broken too :-)
- fi
- first=no
- continue
- fi
- done
- echo bye
-expected-stdout:
- bye
----
-name: regression-10
-description:
- The following:
- set -- `false`
- echo $?
- should print 0 according to POSIX (dash, bash, ksh93, posh)
- but not 0 according to the getopt(1) manual page, ksh88, and
- Bourne sh (such as /bin/sh on Solaris).
- In mksh R39b, we honour POSIX except when -o sh is set.
-stdin:
- showf() {
- [[ -o posix ]]; FPOSIX=$((1-$?))
- [[ -o sh ]]; FSH=$((1-$?))
- echo -n "FPOSIX=$FPOSIX FSH=$FSH "
- }
- set +o posix +o sh
- showf
- set -- `false`
- echo rv=$?
- set -o sh
- showf
- set -- `false`
- echo rv=$?
- set -o posix
- showf
- set -- `false`
- echo rv=$?
-expected-stdout:
- FPOSIX=0 FSH=0 rv=0
- FPOSIX=0 FSH=1 rv=1
- FPOSIX=1 FSH=0 rv=0
----
-name: regression-11
-description:
- The following:
- x=/foo/bar/blah
- echo ${x##*/}
- should echo blah but on some machines echos /foo/bar/blah.
-stdin:
- x=/foo/bar/blah
- echo ${x##*/}
-expected-stdout:
- blah
----
-name: regression-12
-description:
- Both of the following echos produce the same output under sh/ksh.att:
- #!/bin/sh
- x="foo bar"
- echo "`echo \"$x\"`"
- echo "`echo "$x"`"
- pdksh produces different output for the former (foo instead of foo\tbar)
-stdin:
- x="foo bar"
- echo "`echo \"$x\"`"
- echo "`echo "$x"`"
-expected-stdout:
- foo bar
- foo bar
----
-name: regression-13
-description:
- The following command hangs forever:
- $ (: ; cat /etc/termcap) | sleep 2
- This is because the shell forks a shell to run the (..) command
- and this shell has the pipe open. When the sleep dies, the cat
- doesn't get a SIGPIPE 'cause a process (ie, the second shell)
- still has the pipe open.
-
- NOTE: this test provokes a bizarre bug in ksh93 (shell starts reading
- commands from /etc/termcap..)
-time-limit: 10
-stdin:
- echo A line of text that will be duplicated quite a number of times.> t1
- cat t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 t1 > t2
- cat t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 t2 > t1
- cat t1 t1 t1 t1 > t2
- (: ; cat t2 2>&-) | sleep 1
----
-name: regression-14
-description:
- The command
- $ (foobar) 2> /dev/null
- generates no output under /bin/sh, but pdksh produces the error
- foobar: not found
- Also, the command
- $ foobar 2> /dev/null
- generates an error under /bin/sh and pdksh, but AT&T ksh88 produces
- no error (redirected to /dev/null).
-stdin:
- (you/should/not/see/this/error/1) 2> /dev/null
- you/should/not/see/this/error/2 2> /dev/null
- true
----
-name: regression-15
-description:
- The command
- $ whence foobar
- generates a blank line under pdksh and sets the exit status to 0.
- AT&T ksh88 generates no output and sets the exit status to 1. Also,
- the command
- $ whence foobar cat
- generates no output under AT&T ksh88 (pdksh generates a blank line
- and /bin/cat).
-stdin:
- whence does/not/exist > /dev/null
- echo 1: $?
- echo 2: $(whence does/not/exist | wc -l)
- echo 3: $(whence does/not/exist cat | wc -l)
-expected-stdout:
- 1: 1
- 2: 0
- 3: 0
----
-name: regression-16
-description:
- ${var%%expr} seems to be broken in many places. On the mips
- the commands
- $ read line < /etc/passwd
- $ echo $line
- root:0:1:...
- $ echo ${line%%:*}
- root
- $ echo $line
- root
- $
- change the value of line. On sun4s & pas, the echo ${line%%:*} doesn't
- work. Haven't checked elsewhere...
-script:
- read x
- y=$x
- echo ${x%%:*}
- echo $x
-stdin:
- root:asdjhasdasjhs:0:1:Root:/:/bin/sh
-expected-stdout:
- root
- root:asdjhasdasjhs:0:1:Root:/:/bin/sh
----
-name: regression-17
-description:
- The command
- . /foo/bar
- should set the exit status to non-zero (sh and AT&T ksh88 do).
- XXX doting a non existent file is a fatal error for a script
-stdin:
- . does/not/exist
-expected-exit: e != 0
-expected-stderr-pattern: /.?/
----
-name: regression-19
-description:
- Both of the following echos should produce the same thing, but don't:
- $ x=foo/bar
- $ echo ${x%/*}
- foo
- $ echo "${x%/*}"
- foo/bar
-stdin:
- x=foo/bar
- echo "${x%/*}"
-expected-stdout:
- foo
----
-name: regression-21
-description:
- backslash does not work as expected in case labels:
- $ x='-x'
- $ case $x in
- -\?) echo hi
- esac
- hi
- $ x='-?'
- $ case $x in
- -\\?) echo hi
- esac
- hi
- $
-stdin:
- case -x in
- -\?) echo fail
- esac
----
-name: regression-22
-description:
- Quoting backquotes inside backquotes doesn't work:
- $ echo `echo hi \`echo there\` folks`
- asks for more info. sh and AT&T ksh88 both echo
- hi there folks
-stdin:
- echo `echo hi \`echo there\` folks`
-expected-stdout:
- hi there folks
----
-name: regression-23
-description:
- )) is not treated `correctly':
- $ (echo hi ; (echo there ; echo folks))
- missing ((
- $
- instead of (as sh and ksh.att)
- $ (echo hi ; (echo there ; echo folks))
- hi
- there
- folks
- $
-stdin:
- ( : ; ( : ; echo hi))
-expected-stdout:
- hi
----
-name: regression-25
-description:
- Check reading stdin in a while loop. The read should only read
- a single line, not a whole stdio buffer; the cat should get
- the rest.
-stdin:
- (echo a; echo b) | while read x ; do
- echo $x
- cat > /dev/null
- done
-expected-stdout:
- a
----
-name: regression-26
-description:
- Check reading stdin in a while loop. The read should read both
- lines, not just the first.
-script:
- a=
- while [ "$a" != xxx ] ; do
- last=$x
- read x
- cat /dev/null | sed 's/x/y/'
- a=x$a
- done
- echo $last
-stdin:
- a
- b
-expected-stdout:
- b
----
-name: regression-27
-description:
- The command
- . /does/not/exist
- should cause a script to exit.
-stdin:
- . does/not/exist
- echo hi
-expected-exit: e != 0
-expected-stderr-pattern: /does\/not\/exist/
----
-name: regression-28
-description:
- variable assignements not detected well
-stdin:
- a.x=1 echo hi
-expected-exit: e != 0
-expected-stderr-pattern: /a\.x=1/
----
-name: regression-29
-description:
- alias expansion different from AT&T ksh88
-stdin:
- alias a='for ' b='i in'
- a b hi ; do echo $i ; done
-expected-stdout:
- hi
----
-name: regression-30
-description:
- strange characters allowed inside ${...}
-stdin:
- echo ${a{b}}
-expected-exit: e != 0
-expected-stderr-pattern: /.?/
----
-name: regression-31
-description:
- Does read handle partial lines correctly
-script:
- a= ret=
- while [ "$a" != xxx ] ; do
- read x y z
- ret=$?
- a=x$a
- done
- echo "[$x]"
- echo $ret
-stdin: !
- a A aA
- b B Bb
- c
-expected-stdout:
- [c]
- 1
----
-name: regression-32
-description:
- Does read set variables to null at eof?
-script:
- a=
- while [ "$a" != xxx ] ; do
- read x y z
- a=x$a
- done
- echo 1: ${x-x not set} ${y-y not set} ${z-z not set}
- echo 2: ${x:+x not null} ${y:+y not null} ${z:+z not null}
-stdin:
- a A Aa
- b B Bb
-expected-stdout:
- 1:
- 2:
----
-name: regression-33
-description:
- Does umask print a leading 0 when umask is 3 digits?
-stdin:
- umask 222
- umask
-expected-stdout:
- 0222
----
-name: regression-35
-description:
- Tempory files used for here-docs in functions get trashed after
- the function is parsed (before it is executed)
-stdin:
- f1() {
- cat <<- EOF
- F1
- EOF
- f2() {
- cat <<- EOF
- F2
- EOF
- }
- }
- f1
- f2
- unset -f f1
- f2
-expected-stdout:
- F1
- F2
- F2
----
-name: regression-36
-description:
- Command substitution breaks reading in while loop
- (test from <sjg@void.zen.oz.au>)
-stdin:
- (echo abcdef; echo; echo 123) |
- while read line
- do
- # the following line breaks it
- c=`echo $line | wc -c`
- echo $c
- done
-expected-stdout:
- 7
- 1
- 4
----
-name: regression-37
-description:
- Machines with broken times() (reported by <sjg@void.zen.oz.au>)
- time does not report correct real time
-stdin:
- time sleep 1
-expected-stderr-pattern: !/^\s*0\.0[\s\d]+real|^\s*real[\s]+0+\.0/
----
-name: regression-38
-description:
- set -e doesn't ignore exit codes for if/while/until/&&/||/!.
-arguments: !-e!
-stdin:
- if false; then echo hi ; fi
- false || true
- false && true
- while false; do echo hi; done
- echo ok
-expected-stdout:
- ok
----
-name: regression-39
-description:
- set -e: errors in command substitutions aren't ignored
- Not clear if they should be or not... bash passes here
- this may actually be required for make, so changed the
- test to make this an mksh feature, not a bug
-arguments: !-e!
-stdin:
- echo `false; echo hi`
-#expected-fail: yes
-#expected-stdout:
-# hi
-expected-stdout:
-
----
-name: regression-40
-description:
- This used to cause a core dump
-env-setup: !RANDOM=12!
-stdin:
- echo hi
-expected-stdout:
- hi
----
-name: regression-41
-description:
- foo should be set to bar (should not be empty)
-stdin:
- foo=`
- echo bar`
- echo "($foo)"
-expected-stdout:
- (bar)
----
-name: regression-42
-description:
- Can't use command line assignments to assign readonly parameters.
-stdin:
- foo=bar
- readonly foo
- foo=stuff env | grep '^foo'
-expected-exit: e != 0
-expected-stderr-pattern:
- /.*read *only.*/
----
-name: regression-43
-description:
- Can subshells be prefixed by redirections (historical shells allow
- this)
-stdin:
- < /dev/null (sed 's/^/X/')
----
-name: regression-45
-description:
- Parameter assignments with [] recognised correctly
-stdin:
- FOO=*[12]
- BAR=abc[
- MORE=[abc]
- JUNK=a[bc
- echo "<$FOO>"
- echo "<$BAR>"
- echo "<$MORE>"
- echo "<$JUNK>"
-expected-stdout:
- <*[12]>
- <abc[>
- <[abc]>
- <a[bc>
----
-name: regression-46
-description:
- Check that alias expansion works in command substitutions and
- at the end of file.
-stdin:
- alias x='echo hi'
- FOO="`x` "
- echo "[$FOO]"
- x
-expected-stdout:
- [hi ]
- hi
----
-name: regression-47
-description:
- Check that aliases are fully read.
-stdin:
- alias x='echo hi;
- echo there'
- x
- echo done
-expected-stdout:
- hi
- there
- done
----
-name: regression-48
-description:
- Check that (here doc) temp files are not left behind after an exec.
-stdin:
- mkdir foo || exit 1
- TMPDIR=$PWD/foo "$__progname" <<- 'EOF'
- x() {
- sed 's/^/X /' << E_O_F
- hi
- there
- folks
- E_O_F
- echo "done ($?)"
- }
- echo=echo; [ -x /bin/echo ] && echo=/bin/echo
- exec $echo subtest-1 hi
- EOF
- echo subtest-1 foo/*
- TMPDIR=$PWD/foo "$__progname" <<- 'EOF'
- echo=echo; [ -x /bin/echo ] && echo=/bin/echo
- sed 's/^/X /' << E_O_F; exec $echo subtest-2 hi
- a
- few
- lines
- E_O_F
- EOF
- echo subtest-2 foo/*
-expected-stdout:
- subtest-1 hi
- subtest-1 foo/*
- X a
- X few
- X lines
- subtest-2 hi
- subtest-2 foo/*
----
-name: regression-49
-description:
- Check that unset params with attributes are reported by set, those
- sans attributes are not.
-stdin:
- unset FOO BAR
- echo X$FOO
- export BAR
- typeset -i BLAH
- set | grep FOO
- set | grep BAR
- set | grep BLAH
-expected-stdout:
- X
- BAR
- BLAH
----
-name: regression-50
-description:
- Check that aliases do not use continuation prompt after trailing
- semi-colon.
-file-setup: file 644 "env"
- PS1=Y
- PS2=X
-env-setup: !ENV=./env!
-arguments: !-i!
-stdin:
- alias foo='echo hi ; '
- foo
- foo echo there
-expected-stdout:
- hi
- hi
- there
-expected-stderr: !
- YYYY
----
-name: regression-51
-description:
- Check that set allows both +o and -o options on same command line.
-stdin:
- set a b c
- set -o noglob +o allexport
- echo A: $*, *
-expected-stdout:
- A: a b c, *
----
-name: regression-52
-description:
- Check that globbing works in pipelined commands
-file-setup: file 644 "env"
- PS1=P
-file-setup: file 644 "abc"
- stuff
-env-setup: !ENV=./env!
-arguments: !-i!
-stdin:
- sed 's/^/X /' < ab*
- echo mark 1
- sed 's/^/X /' < ab* | sed 's/^/Y /'
- echo mark 2
-expected-stdout:
- X stuff
- mark 1
- Y X stuff
- mark 2
-expected-stderr: !
- PPPPP
----
-name: regression-53
-description:
- Check that getopts works in functions
-stdin:
- bfunc() {
- echo bfunc: enter "(args: $*; OPTIND=$OPTIND)"
- while getopts B oc; do
- case $oc in
- (B)
- echo bfunc: B option
- ;;
- (*)
- echo bfunc: odd option "($oc)"
- ;;
- esac
- done
- echo bfunc: leave
- }
-
- function kfunc {
- echo kfunc: enter "(args: $*; OPTIND=$OPTIND)"
- while getopts K oc; do
- case $oc in
- (K)
- echo kfunc: K option
- ;;
- (*)
- echo bfunc: odd option "($oc)"
- ;;
- esac
- done
- echo kfunc: leave
- }
-
- set -- -f -b -k -l
- echo "line 1: OPTIND=$OPTIND"
- getopts kbfl optc
- echo "line 2: ret=$?, optc=$optc, OPTIND=$OPTIND"
- bfunc -BBB blah
- echo "line 3: OPTIND=$OPTIND"
- getopts kbfl optc
- echo "line 4: ret=$?, optc=$optc, OPTIND=$OPTIND"
- kfunc -KKK blah
- echo "line 5: OPTIND=$OPTIND"
- getopts kbfl optc
- echo "line 6: ret=$?, optc=$optc, OPTIND=$OPTIND"
- echo
-
- OPTIND=1
- set -- -fbkl
- echo "line 10: OPTIND=$OPTIND"
- getopts kbfl optc
- echo "line 20: ret=$?, optc=$optc, OPTIND=$OPTIND"
- bfunc -BBB blah
- echo "line 30: OPTIND=$OPTIND"
- getopts kbfl optc
- echo "line 40: ret=$?, optc=$optc, OPTIND=$OPTIND"
- kfunc -KKK blah
- echo "line 50: OPTIND=$OPTIND"
- getopts kbfl optc
- echo "line 60: ret=$?, optc=$optc, OPTIND=$OPTIND"
-expected-stdout:
- line 1: OPTIND=1
- line 2: ret=0, optc=f, OPTIND=2
- bfunc: enter (args: -BBB blah; OPTIND=2)
- bfunc: B option
- bfunc: B option
- bfunc: leave
- line 3: OPTIND=2
- line 4: ret=0, optc=b, OPTIND=3
- kfunc: enter (args: -KKK blah; OPTIND=1)
- kfunc: K option
- kfunc: K option
- kfunc: K option
- kfunc: leave
- line 5: OPTIND=3
- line 6: ret=0, optc=k, OPTIND=4
-
- line 10: OPTIND=1
- line 20: ret=0, optc=f, OPTIND=2
- bfunc: enter (args: -BBB blah; OPTIND=2)
- bfunc: B option
- bfunc: B option
- bfunc: leave
- line 30: OPTIND=2
- line 40: ret=1, optc=?, OPTIND=2
- kfunc: enter (args: -KKK blah; OPTIND=1)
- kfunc: K option
- kfunc: K option
- kfunc: K option
- kfunc: leave
- line 50: OPTIND=2
- line 60: ret=1, optc=?, OPTIND=2
----
-name: regression-54
-description:
- Check that ; is not required before the then in if (( ... )) then ...
-stdin:
- if (( 1 )) then
- echo ok dparen
- fi
- if [[ -n 1 ]] then
- echo ok dbrackets
- fi
-expected-stdout:
- ok dparen
- ok dbrackets
----
-name: regression-55
-description:
- Check ${foo:%bar} is allowed (ksh88 allows it...)
-stdin:
- x=fooXbarXblah
- echo 1 ${x%X*}
- echo 2 ${x:%X*}
- echo 3 ${x%%X*}
- echo 4 ${x:%%X*}
- echo 5 ${x#*X}
- echo 6 ${x:#*X}
- echo 7 ${x##*X}
- echo 8 ${x:##*X}
-expected-stdout:
- 1 fooXbar
- 2 fooXbar
- 3 foo
- 4 foo
- 5 barXblah
- 6 barXblah
- 7 blah
- 8 blah
----
-name: regression-57
-description:
- Check if typeset output is correct for
- uninitialised array elements.
-stdin:
- typeset -i xxx[4]
- echo A
- typeset -i | grep xxx | sed 's/^/ /'
- echo B
- typeset | grep xxx | sed 's/^/ /'
-
- xxx[1]=2+5
- echo M
- typeset -i | grep xxx | sed 's/^/ /'
- echo N
- typeset | grep xxx | sed 's/^/ /'
-expected-stdout:
- A
- xxx
- B
- typeset -i xxx
- M
- xxx[1]=7
- N
- typeset -i xxx
----
-name: regression-58
-description:
- Check if trap exit is ok (exit not mistaken for signal name)
-stdin:
- trap 'echo hi' exit
- trap exit 1
-expected-stdout:
- hi
----
-name: regression-59
-description:
- Check if ${#array[*]} is calculated correctly.
-stdin:
- a[12]=hi
- a[8]=there
- echo ${#a[*]}
-expected-stdout:
- 2
----
-name: regression-60
-description:
- Check if default exit status is previous command
-stdin:
- (true; exit)
- echo A $?
- (false; exit)
- echo B $?
- ( (exit 103) ; exit)
- echo C $?
-expected-stdout:
- A 0
- B 1
- C 103
----
-name: regression-61
-description:
- Check if EXIT trap is executed for sub shells.
-stdin:
- trap 'echo parent exit' EXIT
- echo start
- (echo A; echo A last)
- echo B
- (echo C; trap 'echo sub exit' EXIT; echo C last)
- echo parent last
-expected-stdout:
- start
- A
- A last
- B
- C
- C last
- sub exit
- parent last
- parent exit
----
-name: regression-62
-description:
- Check if test -nt/-ot succeeds if second(first) file is missing.
-stdin:
- touch a
- test a -nt b && echo nt OK || echo nt BAD
- test b -ot a && echo ot OK || echo ot BAD
-expected-stdout:
- nt OK
- ot OK
----
-name: regression-63
-description:
- Check if typeset, export, and readonly work
-stdin:
- {
- echo FNORD-0
- FNORD_A=1
- FNORD_B=2
- FNORD_C=3
- FNORD_D=4
- FNORD_E=5
- FNORD_F=6
- FNORD_G=7
- FNORD_H=8
- integer FNORD_E FNORD_F FNORD_G FNORD_H
- export FNORD_C FNORD_D FNORD_G FNORD_H
- readonly FNORD_B FNORD_D FNORD_F FNORD_H
- echo FNORD-1
- export
- echo FNORD-2
- export -p
- echo FNORD-3
- readonly
- echo FNORD-4
- readonly -p
- echo FNORD-5
- typeset
- echo FNORD-6
- typeset -p
- echo FNORD-7
- typeset -
- echo FNORD-8
- } | fgrep FNORD
-expected-stdout:
- FNORD-0
- FNORD-1
- FNORD_C
- FNORD_D
- FNORD_G
- FNORD_H
- FNORD-2
- export FNORD_C=3
- export FNORD_D=4
- export FNORD_G=7
- export FNORD_H=8
- FNORD-3
- FNORD_B
- FNORD_D
- FNORD_F
- FNORD_H
- FNORD-4
- readonly FNORD_B=2
- readonly FNORD_D=4
- readonly FNORD_F=6
- readonly FNORD_H=8
- FNORD-5
- typeset FNORD_A
- typeset -r FNORD_B
- typeset -x FNORD_C
- typeset -x -r FNORD_D
- typeset -i FNORD_E
- typeset -i -r FNORD_F
- typeset -i -x FNORD_G
- typeset -i -x -r FNORD_H
- FNORD-6
- typeset FNORD_A=1
- typeset -r FNORD_B=2
- typeset -x FNORD_C=3
- typeset -x -r FNORD_D=4
- typeset -i FNORD_E=5
- typeset -i -r FNORD_F=6
- typeset -i -x FNORD_G=7
- typeset -i -x -r FNORD_H=8
- FNORD-7
- FNORD_A=1
- FNORD_B=2
- FNORD_C=3
- FNORD_D=4
- FNORD_E=5
- FNORD_F=6
- FNORD_G=7
- FNORD_H=8
- FNORD-8
----
-name: regression-64
-description:
- Check that we can redefine functions calling time builtin
-stdin:
- t() {
- time >/dev/null
- }
- t 2>/dev/null
- t() {
- time
- }
----
-name: syntax-1
-description:
- Check that lone ampersand is a syntax error
-stdin:
- &
-expected-exit: e != 0
-expected-stderr-pattern:
- /syntax error/
----
-name: xxx-quoted-newline-1
-description:
- Check that \<newline> works inside of ${}
-stdin:
- abc=2
- echo ${ab\
- c}
-expected-stdout:
- 2
----
-name: xxx-quoted-newline-2
-description:
- Check that \<newline> works at the start of a here document
-stdin:
- cat << EO\
- F
- hi
- EOF
-expected-stdout:
- hi
----
-name: xxx-quoted-newline-3
-description:
- Check that \<newline> works at the end of a here document
-stdin:
- cat << EOF
- hi
- EO\
- F
-expected-stdout:
- hi
----
-name: xxx-multi-assignment-cmd
-description:
- Check that assignments in a command affect subsequent assignments
- in the same command
-stdin:
- FOO=abc
- FOO=123 BAR=$FOO
- echo $BAR
-expected-stdout:
- 123
----
-name: xxx-multi-assignment-posix-cmd
-description:
- Check that the behaviour for multiple assignments with a
- command name matches POSIX. See:
- http://thread.gmane.org/gmane.comp.standards.posix.austin.general/1925
-stdin:
- X=a Y=b; X=$Y Y=$X "$__progname" -c 'echo 1 $X $Y .'; echo 2 $X $Y .
- unset X Y Z
- X=a Y=${X=b} Z=$X "$__progname" -c 'echo 3 $Z .'
- unset X Y Z
- X=a Y=${X=b} Z=$X; echo 4 $Z .
-expected-stdout:
- 1 b a .
- 2 a b .
- 3 b .
- 4 a .
----
-name: xxx-multi-assignment-posix-nocmd
-description:
- Check that the behaviour for multiple assignments with no
- command name matches POSIX (Debian #334182). See:
- http://thread.gmane.org/gmane.comp.standards.posix.austin.general/1925
-stdin:
- X=a Y=b; X=$Y Y=$X; echo 1 $X $Y .
-expected-stdout:
- 1 b b .
----
-name: xxx-multi-assignment-posix-subassign
-description:
- Check that the behaviour for multiple assignments matches POSIX:
- - The assignment words shall be expanded in the current execution
- environment.
- - The assignments happen in the temporary execution environment.
-stdin:
- unset X Y Z
- Z=a Y=${X:=b} sh -c 'echo +$X+ +$Y+ +$Z+'
- echo /$X/
- # Now for the special case:
- unset X Y Z
- X= Y=${X:=b} sh -c 'echo +$X+ +$Y+'
- echo /$X/
-expected-stdout:
- ++ +b+ +a+
- /b/
- ++ +b+
- /b/
----
-name: xxx-exec-environment-1
-description:
- Check to see if exec sets it's environment correctly
-stdin:
- FOO=bar exec env
-expected-stdout-pattern:
- /(^|.*\n)FOO=bar\n/
----
-name: xxx-exec-environment-2
-description:
- Check to make sure exec doesn't change environment if a program
- isn't exec-ed
-stdin:
- sortprog=$(whence -p sort) || sortprog=cat
- env | $sortprog | grep -v '^RANDOM=' >bar1
- FOO=bar exec; env | $sortprog | grep -v '^RANDOM=' >bar2
- cmp -s bar1 bar2
----
-name: exec-function-environment-1
-description:
- Check assignments in function calls and whether they affect
- the current execution environment (ksh93, SUSv4)
-stdin:
- f() { a=2; }; g() { b=3; echo y$c-; }; a=1 f; b=2; c=1 g
- echo x$a-$b- z$c-
-expected-stdout:
- y1-
- x2-3- z1-
----
-name: xxx-what-do-you-call-this-1
-stdin:
- echo "${foo:-"a"}*"
-expected-stdout:
- a*
----
-name: xxx-prefix-strip-1
-stdin:
- foo='a cdef'
- echo ${foo#a c}
-expected-stdout:
- def
----
-name: xxx-prefix-strip-2
-stdin:
- set a c
- x='a cdef'
- echo ${x#$*}
-expected-stdout:
- def
----
-name: xxx-variable-syntax-1
-stdin:
- echo ${:}
-expected-stderr-pattern:
- /bad substitution/
-expected-exit: 1
----
-name: xxx-variable-syntax-2
-stdin:
- set 0
- echo ${*:0}
-expected-stderr-pattern:
- /bad substitution/
-expected-exit: 1
----
-name: xxx-variable-syntax-3
-stdin:
- set -A foo 0
- echo ${foo[*]:0}
-expected-stderr-pattern:
- /bad substitution/
-expected-exit: 1
----
-name: xxx-substitution-eval-order
-description:
- Check order of evaluation of expressions
-stdin:
- i=1 x= y=
- set -A A abc def GHI j G k
- echo ${A[x=(i+=1)]#${A[y=(i+=2)]}}
- echo $x $y
-expected-stdout:
- HI
- 2 4
----
-name: xxx-set-option-1
-description:
- Check option parsing in set
-stdin:
- set -vsA foo -- A 1 3 2
- echo ${foo[*]}
-expected-stderr:
- echo ${foo[*]}
-expected-stdout:
- 1 2 3 A
----
-name: xxx-exec-1
-description:
- Check that exec exits for built-ins
-arguments: !-i!
-stdin:
- exec echo hi
- echo still herre
-expected-stdout:
- hi
-expected-stderr-pattern: /.*/
----
-name: xxx-while-1
-description:
- Check the return value of while loops
- XXX need to do same for for/select/until loops
-stdin:
- i=x
- while [ $i != xxx ] ; do
- i=x$i
- if [ $i = xxx ] ; then
- false
- continue
- fi
- done
- echo loop1=$?
-
- i=x
- while [ $i != xxx ] ; do
- i=x$i
- if [ $i = xxx ] ; then
- false
- break
- fi
- done
- echo loop2=$?
-
- i=x
- while [ $i != xxx ] ; do
- i=x$i
- false
- done
- echo loop3=$?
-expected-stdout:
- loop1=0
- loop2=0
- loop3=1
----
-name: xxx-status-1
-description:
- Check that blank lines don't clear $?
-arguments: !-i!
-stdin:
- (exit 1)
- echo $?
- (exit 1)
-
- echo $?
- true
-expected-stdout:
- 1
- 1
-expected-stderr-pattern: /.*/
----
-name: xxx-status-2
-description:
- Check that $? is preserved in subshells, includes, traps.
-stdin:
- (exit 1)
-
- echo blank: $?
-
- (exit 2)
- (echo subshell: $?)
-
- echo 'echo include: $?' > foo
- (exit 3)
- . ./foo
-
- trap 'echo trap: $?' ERR
- (exit 4)
- echo exit: $?
-expected-stdout:
- blank: 1
- subshell: 2
- include: 3
- trap: 4
- exit: 4
----
-name: xxx-clean-chars-1
-description:
- Check MAGIC character is stuffed correctly
-stdin:
- echo `echo [£`
-expected-stdout:
- [£
----
-name: xxx-param-subst-qmark-1
-description:
- Check suppresion of error message with null string. According to
- POSIX, it shouldn't print the error as 'word' isn't ommitted.
- ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error,
- that's why the condition is reversed.
-stdin:
- unset foo
- x=
- echo x${foo?$x}
-expected-exit: 1
-# POSIX
-#expected-fail: yes
-#expected-stderr-pattern: !/not set/
-# common use
-expected-stderr-pattern: /parameter null or not set/
----
-name: xxx-param-_-1
-# fails due to weirdness of execv stuff
-category: !os:uwin-nt
-description:
- Check c flag is set.
-arguments: !-c!echo "[$-]"!
-expected-stdout-pattern: /^\[.*c.*\]$/
----
-name: tilde-expand-1
-description:
- Check tilde expansion after equal signs
-env-setup: !HOME=/sweet!
-stdin:
- echo ${A=a=}~ b=~ c=d~ ~
- set +o braceexpand
- echo ${A=a=}~ b=~ c=d~ ~
-expected-stdout:
- a=/sweet b=/sweet c=d~ /sweet
- a=~ b=~ c=d~ /sweet
----
-name: exit-err-1
-description:
- Check some "exit on error" conditions
-stdin:
- set -ex
- /usr/bin/env false && echo something
- echo END
-expected-stdout:
- END
-expected-stderr:
- + /usr/bin/env false
- + echo END
----
-name: exit-err-2
-description:
- Check some "exit on error" edge conditions (POSIXly)
-stdin:
- set -ex
- if /usr/bin/env true; then
- /usr/bin/env false && echo something
- fi
- echo END
-expected-stdout:
- END
-expected-stderr:
- + /usr/bin/env true
- + /usr/bin/env false
- + echo END
----
-name: exit-err-3
-description:
- pdksh regression which AT&T ksh does right
- TFM says: [set] -e | errexit
- Exit (after executing the ERR trap) ...
-stdin:
- trap 'echo EXIT' EXIT
- trap 'echo ERR' ERR
- set -e
- cd /XXXXX 2>/dev/null
- echo DONE
- exit 0
-expected-stdout:
- ERR
- EXIT
-expected-exit: e != 0
----
-name: exit-err-4
-description:
- "set -e" test suite (POSIX)
-stdin:
- set -e
- echo pre
- if true ; then
- false && echo foo
- fi
- echo bar
-expected-stdout:
- pre
- bar
----
-name: exit-err-5
-description:
- "set -e" test suite (POSIX)
-stdin:
- set -e
- foo() {
- while [ "$1" ]; do
- for E in $x; do
- [ "$1" = "$E" ] && { shift ; continue 2 ; }
- done
- x="$x $1"
- shift
- done
- echo $x
- }
- echo pre
- foo a b b c
- echo post
-expected-stdout:
- pre
- a b c
- post
----
-name: exit-err-6
-description:
- "set -e" test suite (BSD make)
-category: os:mirbsd
-stdin:
- mkdir zd zd/a zd/b
- print 'all:\n\t@echo eins\n\t@exit 42\n' >zd/a/Makefile
- print 'all:\n\t@echo zwei\n' >zd/b/Makefile
- wd=$(pwd)
- set -e
- for entry in a b; do ( set -e; if [[ -d $wd/zd/$entry.i386 ]]; then _newdir_="$entry.i386"; else _newdir_="$entry"; fi; if [[ -z $_THISDIR_ ]]; then _nextdir_="$_newdir_"; else _nextdir_="$_THISDIR_/$_newdir_"; fi; _makefile_spec_=; [[ ! -f $wd/zd/$_newdir_/Makefile.bsd-wrapper ]] || _makefile_spec_="-f Makefile.bsd-wrapper"; subskipdir=; for skipdir in ; do subentry=${skipdir#$entry}; if [[ $subentry != $skipdir ]]; then if [[ -z $subentry ]]; then echo "($_nextdir_ skipped)"; break; fi; subskipdir="$subskipdir ${subentry#/}"; fi; done; if [[ -z $skipdir || -n $subentry ]]; then echo "===> $_nextdir_"; cd $wd/zd/$_newdir_; make SKIPDIR="$subskipdir" $_makefile_spec_ _THISDIR_="$_nextdir_" all; fi; ) done 2>&1 | sed "s!$wd!WD!g"
-expected-stdout:
- ===> a
- eins
- *** Error code 42
-
- Stop in WD/zd/a (line 2 of Makefile).
----
-name: exit-enoent-1
-description:
- SUSv4 says that the shell should exit with 126/127 in some situations
-stdin:
- i=0
- (echo; echo :) >x
- "$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
- "$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
- echo exit 42 >x
- "$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
- "$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
- rm -f x
- "$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
- "$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-expected-stdout:
- 0 0 .
- 1 126 .
- 2 42 .
- 3 126 .
- 4 127 .
- 5 127 .
----
-name: exit-eval-1
-description:
- Check eval vs substitution exit codes (ksh93 alike)
-stdin:
- eval $(false)
- echo A $?
- eval ' $(false)'
- echo B $?
- eval " $(false)"
- echo C $?
- eval "eval $(false)"
- echo D $?
- eval 'eval '"$(false)"
- echo E $?
- IFS="$IFS:"
- eval $(echo :; false)
- echo F $?
-expected-stdout:
- A 0
- B 1
- C 0
- D 0
- E 0
- F 0
----
-name: test-stlt-1
-description:
- Check that test also can handle string1 < string2 etc.
-stdin:
- test 2005/10/08 '<' 2005/08/21 && echo ja || echo nein
- test 2005/08/21 \< 2005/10/08 && echo ja || echo nein
- test 2005/10/08 '>' 2005/08/21 && echo ja || echo nein
- test 2005/08/21 \> 2005/10/08 && echo ja || echo nein
-expected-stdout:
- nein
- ja
- ja
- nein
-expected-stderr-pattern: !/unexpected op/
----
-name: test-precedence-1
-description:
- Check a weird precedence case (and POSIX echo)
-stdin:
- test \( -f = -f \)
- rv=$?
- test -n "$POSH_VERSION" || set -o sh
- echo -e $rv
-expected-stdout:
- -e 0
----
-name: test-option-1
-description:
- Test the test -o operator
-stdin:
- runtest() {
- test -o $1; echo $?
- [ -o $1 ]; echo $?
- [[ -o $1 ]]; echo $?
- }
- if_test() {
- test -o $1 -o -o !$1; echo $?
- [ -o $1 -o -o !$1 ]; echo $?
- [[ -o $1 || -o !$1 ]]; echo $?
- test -o ?$1; echo $?
- }
- echo 0y $(if_test utf8-mode) =
- echo 0n $(if_test utf8-hack) =
- echo 1= $(runtest utf8-hack) =
- echo 2= $(runtest !utf8-hack) =
- echo 3= $(runtest ?utf8-hack) =
- set +U
- echo 1+ $(runtest utf8-mode) =
- echo 2+ $(runtest !utf8-mode) =
- echo 3+ $(runtest ?utf8-mode) =
- set -U
- echo 1- $(runtest utf8-mode) =
- echo 2- $(runtest !utf8-mode) =
- echo 3- $(runtest ?utf8-mode) =
- echo = short flags =
- echo 0y $(if_test -U) =
- echo 0y $(if_test +U) =
- echo 0n $(if_test -_) =
- echo 0n $(if_test -U-) =
- echo 1= $(runtest -_) =
- echo 2= $(runtest !-_) =
- echo 3= $(runtest ?-_) =
- set +U
- echo 1+ $(runtest -U) =
- echo 2+ $(runtest !-U) =
- echo 3+ $(runtest ?-U) =
- echo 1+ $(runtest +U) =
- echo 2+ $(runtest !+U) =
- echo 3+ $(runtest ?+U) =
- set -U
- echo 1- $(runtest -U) =
- echo 2- $(runtest !-U) =
- echo 3- $(runtest ?-U) =
- echo 1- $(runtest +U) =
- echo 2- $(runtest !+U) =
- echo 3- $(runtest ?+U) =
-expected-stdout:
- 0y 0 0 0 0 =
- 0n 1 1 1 1 =
- 1= 1 1 1 =
- 2= 1 1 1 =
- 3= 1 1 1 =
- 1+ 1 1 1 =
- 2+ 0 0 0 =
- 3+ 0 0 0 =
- 1- 0 0 0 =
- 2- 1 1 1 =
- 3- 0 0 0 =
- = short flags =
- 0y 0 0 0 0 =
- 0y 0 0 0 0 =
- 0n 1 1 1 1 =
- 0n 1 1 1 1 =
- 1= 1 1 1 =
- 2= 1 1 1 =
- 3= 1 1 1 =
- 1+ 1 1 1 =
- 2+ 0 0 0 =
- 3+ 0 0 0 =
- 1+ 1 1 1 =
- 2+ 0 0 0 =
- 3+ 0 0 0 =
- 1- 0 0 0 =
- 2- 1 1 1 =
- 3- 0 0 0 =
- 1- 0 0 0 =
- 2- 1 1 1 =
- 3- 0 0 0 =
----
-name: mkshrc-1
-description:
- Check that ~/.mkshrc works correctly.
- Part 1: verify user environment is not read (internal)
-stdin:
- echo x $FNORD
-expected-stdout:
- x
----
-name: mkshrc-2a
-description:
- Check that ~/.mkshrc works correctly.
- Part 2: verify mkshrc is not read (non-interactive shells)
-file-setup: file 644 ".mkshrc"
- FNORD=42
-env-setup: !HOME=.!ENV=!
-stdin:
- echo x $FNORD
-expected-stdout:
- x
----
-name: mkshrc-2b
-description:
- Check that ~/.mkshrc works correctly.
- Part 2: verify mkshrc can be read (interactive shells)
-file-setup: file 644 ".mkshrc"
- FNORD=42
-arguments: !-i!
-env-setup: !HOME=.!ENV=!PS1=!
-stdin:
- echo x $FNORD
-expected-stdout:
- x 42
-expected-stderr-pattern:
- /(# )*/
----
-name: mkshrc-3
-description:
- Check that ~/.mkshrc works correctly.
- Part 3: verify mkshrc can be turned off
-file-setup: file 644 ".mkshrc"
- FNORD=42
-env-setup: !HOME=.!ENV=nonexistant!
-stdin:
- echo x $FNORD
-expected-stdout:
- x
----
-name: sh-mode-1
-description:
- Check that sh mode turns braceexpand off
- and that that works correctly
-stdin:
- set -o braceexpand
- set +o sh
- [[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
- [[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
- echo {a,b,c}
- set +o braceexpand
- echo {a,b,c}
- set -o braceexpand
- echo {a,b,c}
- set -o sh
- echo {a,b,c}
- [[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
- [[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
- set -o braceexpand
- echo {a,b,c}
- [[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
- [[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
-expected-stdout:
- nosh
- brex
- a b c
- {a,b,c}
- a b c
- {a,b,c}
- sh
- nobrex
- a b c
- sh
- brex
----
-name: sh-mode-2a
-description:
- Check that sh mode is *not* automatically turned on
-category: !binsh
-stdin:
- ln -s "$__progname" ksh
- ln -s "$__progname" sh
- ln -s "$__progname" ./-ksh
- ln -s "$__progname" ./-sh
- for shell in {,-}{,k}sh; do
- print -- $shell $(./$shell +l -c \
- '[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh')
- done
-expected-stdout:
- sh nosh
- ksh nosh
- -sh nosh
- -ksh nosh
----
-name: sh-mode-2b
-description:
- Check that sh mode *is* automatically turned on
-category: binsh
-stdin:
- ln -s "$__progname" ksh
- ln -s "$__progname" sh
- ln -s "$__progname" ./-ksh
- ln -s "$__progname" ./-sh
- for shell in {,-}{,k}sh; do
- print -- $shell $(./$shell +l -c \
- '[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh')
- done
-expected-stdout:
- sh sh
- ksh nosh
- -sh sh
- -ksh nosh
----
-name: pipeline-1
-description:
- pdksh bug: last command of a pipeline is executed in a
- subshell - make sure it still is, scripts depend on it
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcy"
-stdin:
- echo *
- echo a | while read d; do
- echo $d
- echo $d*
- echo *
- set -o noglob
- echo $d*
- echo *
- done
- echo *
-expected-stdout:
- abcx abcy
- a
- abcx abcy
- abcx abcy
- a*
- *
- abcx abcy
----
-name: pipeline-2
-description:
- check that co-processes work with TCOMs, TPIPEs and TPARENs
-stdin:
- "$__progname" -c 'i=100; echo hi |& while read -p line; do echo "$((i++)) $line"; done'
- "$__progname" -c 'i=200; echo hi | cat |& while read -p line; do echo "$((i++)) $line"; done'
- "$__progname" -c 'i=300; (echo hi | cat) |& while read -p line; do echo "$((i++)) $line"; done'
-expected-stdout:
- 100 hi
- 200 hi
- 300 hi
----
-name: persist-history-1
-description:
- Check if persistent history saving works
-category: !no-histfile
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- cat hist.file
-expected-stdout-pattern:
- /cat hist.file/
-expected-stderr-pattern:
- /^X*$/
----
-name: typeset-padding-1
-description:
- Check if left/right justification works as per TFM
-stdin:
- typeset -L10 ln=0hall0
- typeset -R10 rn=0hall0
- typeset -ZL10 lz=0hall0
- typeset -ZR10 rz=0hall0
- typeset -Z10 rx=" hallo "
- echo "<$ln> <$rn> <$lz> <$rz> <$rx>"
-expected-stdout:
- <0hall0 > < 0hall0> <hall0 > <00000hall0> <0000 hallo>
----
-name: typeset-padding-2
-description:
- Check if base-!10 integers are padded right
-stdin:
- typeset -Uui16 -L9 ln=16#1
- typeset -Uui16 -R9 rn=16#1
- typeset -Uui16 -Z9 zn=16#1
- typeset -L9 ls=16#1
- typeset -R9 rs=16#1
- typeset -Z9 zs=16#1
- echo "<$ln> <$rn> <$zn> <$ls> <$rs> <$zs>"
-expected-stdout:
- <16#1 > < 16#1> <16#000001> <16#1 > < 16#1> <0000016#1>
----
-name: utf8bom-1
-description:
- Check that the UTF-8 Byte Order Mark is ignored as the first
- multibyte character of the shell input (with -c, from standard
- input, as file, or as eval argument), but nowhere else
-# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
-category: !os:darwin
-stdin:
- mkdir foo
- print '#!/bin/sh\necho ohne' >foo/fnord
- print '#!/bin/sh\necho mit' >foo/fnord
- print 'fnord\nfnord\nfnord\nfnord' >foo/bar
- print eval \''fnord\nfnord\nfnord\nfnord'\' >foo/zoo
- set -A anzahl -- foo/*
- echo got ${#anzahl[*]} files
- chmod +x foo/*
- export PATH=$(pwd)/foo:$PATH
- "$__progname" -c 'fnord'
- echo =
- "$__progname" -c 'fnord; fnord; fnord; fnord'
- echo =
- "$__progname" foo/bar
- echo =
- "$__progname" <foo/bar
- echo =
- "$__progname" foo/zoo
- echo =
- "$__progname" -c 'echo : $(fnord)'
- rm -rf foo
-expected-stdout:
- got 4 files
- ohne
- =
- ohne
- ohne
- mit
- ohne
- =
- ohne
- ohne
- mit
- ohne
- =
- ohne
- ohne
- mit
- ohne
- =
- ohne
- ohne
- mit
- ohne
- =
- : mit
----
-name: utf8bom-2
-description:
- Check that we can execute BOM-shebangs (failures not fatal)
- XXX if the OS can already execute them, we lose
- note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose
- note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
-category: !os:cygwin,!os:uwin-nt,!os:ultrix,!smksh
-env-setup: !FOO=BAR!
-stdin:
- print '#!'"$__progname"'\nprint "1 a=$ENV{FOO}";' >t1
- print '#!'"$__progname"'\nprint "2 a=$ENV{FOO}";' >t2
- print '#!'"$__perlname"'\nprint "3 a=$ENV{FOO}\n";' >t3
- print '#!'"$__perlname"'\nprint "4 a=$ENV{FOO}\n";' >t4
- chmod +x t?
- ./t1
- ./t2
- ./t3
- ./t4
-expected-stdout:
- 1 a=/nonexistant{FOO}
- 2 a=/nonexistant{FOO}
- 3 a=BAR
- 4 a=BAR
-expected-stderr-pattern:
- /(Unrecognized character .... ignored at \..t4 line 1)*/
----
-name: utf8bom-3
-description:
- Reading the UTF-8 BOM should enable the utf8-mode flag
-stdin:
- "$__progname" -c ':; if [[ $- = *U* ]]; then echo 1 on; else echo 1 off; fi'
- "$__progname" -c ':; if [[ $- = *U* ]]; then echo 2 on; else echo 2 off; fi'
-expected-stdout:
- 1 off
- 2 on
----
-name: utf8opt-1a
-description:
- Check that the utf8-mode flag is not set at non-interactive startup
-category: !os:hpux
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
-stdin:
- if [[ $- = *U* ]]; then
- echo is set
- else
- echo is not set
- fi
-expected-stdout:
- is not set
----
-name: utf8opt-1b
-description:
- Check that the utf8-mode flag is not set at non-interactive startup
-category: os:hpux
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
-stdin:
- if [[ $- = *U* ]]; then
- echo is set
- else
- echo is not set
- fi
-expected-stdout:
- is not set
----
-name: utf8opt-2a
-description:
- Check that the utf8-mode flag is set at interactive startup.
- -DMKSH_ASSUME_UTF8=0 => expected failure, please ignore
- -DMKSH_ASSUME_UTF8=1 => not expected, please investigate
- -UMKSH_ASSUME_UTF8 => not expected, but if your OS is old,
- try passing HAVE_SETLOCALE_CTYPE=0 to Build.sh
-category: !os:hpux
-arguments: !-i!
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
-stdin:
- if [[ $- = *U* ]]; then
- echo is set
- else
- echo is not set
- fi
-expected-stdout:
- is set
-expected-stderr-pattern:
- /(# )*/
----
-name: utf8opt-2b
-description:
- Check that the utf8-mode flag is set at interactive startup
- Expected failure if -DMKSH_ASSUME_UTF8=0
-category: os:hpux
-arguments: !-i!
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
-stdin:
- if [[ $- = *U* ]]; then
- echo is set
- else
- echo is not set
- fi
-expected-stdout:
- is set
-expected-stderr-pattern:
- /(# )*/
----
-name: utf8opt-3
-description:
- Ensure ±U on the command line is honoured
- (this test may pass falsely depending on CPPFLAGS)
-stdin:
- export i=0
- code='if [[ $- = *U* ]]; then echo $i on; else echo $i off; fi'
- let i++; "$__progname" -U -c "$code"
- let i++; "$__progname" +U -c "$code"
- let i++; "$__progname" -U -ic "$code"
- let i++; "$__progname" +U -ic "$code"
- echo $((++i)) done
-expected-stdout:
- 1 on
- 2 off
- 3 on
- 4 off
- 5 done
----
-name: aliases-1
-description:
- Check if built-in shell aliases are okay
-category: !arge
-stdin:
- alias
- typeset -f
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- suspend='kill -STOP $$'
- type='whence -v'
----
-name: aliases-1-hartz4
-description:
- Check if built-in shell aliases are okay
-category: arge
-stdin:
- alias
- typeset -f
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- type='whence -v'
----
-name: aliases-2a
-description:
- Check if “set -o sh” disables built-in aliases (except a few)
-category: disabled
-arguments: !-o!sh!
-stdin:
- alias
- typeset -f
-expected-stdout:
- integer='typeset -i'
- local=typeset
----
-name: aliases-3a
-description:
- Check if running as sh disables built-in aliases (except a few)
-category: disabled
-arguments: !-o!sh!
-stdin:
- cp "$__progname" sh
- ./sh -c 'alias; typeset -f'
- rm -f sh
-expected-stdout:
- integer='typeset -i'
- local=typeset
----
-name: aliases-2b
-description:
- Check if “set -o sh” does not influence built-in aliases
-category: !arge
-arguments: !-o!sh!
-stdin:
- alias
- typeset -f
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- suspend='kill -STOP $$'
- type='whence -v'
----
-name: aliases-3b
-description:
- Check if running as sh does not influence built-in aliases
-category: !arge
-arguments: !-o!sh!
-stdin:
- cp "$__progname" sh
- ./sh -c 'alias; typeset -f'
- rm -f sh
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- suspend='kill -STOP $$'
- type='whence -v'
----
-name: aliases-2b-hartz4
-description:
- Check if “set -o sh” does not influence built-in aliases
-category: arge
-arguments: !-o!sh!
-stdin:
- alias
- typeset -f
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- type='whence -v'
----
-name: aliases-3b-hartz4
-description:
- Check if running as sh does not influence built-in aliases
-category: arge
-arguments: !-o!sh!
-stdin:
- cp "$__progname" sh
- ./sh -c 'alias; typeset -f'
- rm -f sh
-expected-stdout:
- autoload='typeset -fu'
- functions='typeset -f'
- hash='alias -t'
- history='fc -l'
- integer='typeset -i'
- local=typeset
- login='exec login'
- nameref='typeset -n'
- nohup='nohup '
- r='fc -e -'
- source='PATH=$PATH:. command .'
- type='whence -v'
----
-name: aliases-funcdef-1
-description:
- Check if POSIX functions take precedences over aliases
-stdin:
- alias foo='echo makro'
- foo() {
- echo funktion
- }
- foo
-expected-stdout:
- funktion
----
-name: aliases-funcdef-2
-description:
- Check if POSIX functions take precedences over aliases
-stdin:
- alias foo='echo makro'
- foo () {
- echo funktion
- }
- foo
-expected-stdout:
- funktion
----
-name: aliases-funcdef-3
-description:
- Check if aliases take precedences over Korn functions
-stdin:
- alias foo='echo makro'
- function foo {
- echo funktion
- }
- foo
-expected-stdout:
- makro
----
-name: arrays-1
-description:
- Check if Korn Shell arrays work as expected
-stdin:
- v="c d"
- set -A foo -- a \$v "$v" '$v' b
- echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|"
-expected-stdout:
- 5|a|$v|c d|$v|b|
----
-name: arrays-2
-description:
- Check if bash-style arrays work as expected
-category: !smksh
-stdin:
- v="c d"
- foo=(a \$v "$v" '$v' b)
- echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|"
-expected-stdout:
- 5|a|$v|c d|$v|b|
----
-name: arrays-3
-description:
- Check if array bounds are uint32_t
-stdin:
- set -A foo a b c
- foo[4097]=d
- foo[2147483637]=e
- echo ${foo[*]}
- foo[-1]=f
- echo ${foo[4294967295]} g ${foo[*]}
-expected-stdout:
- a b c d e
- f g a b c d e f
----
-name: arrays-4
-description:
- Check if Korn Shell arrays with specified indices work as expected
-category: !smksh
-stdin:
- v="c d"
- set -A foo -- [1]=\$v [2]="$v" [4]='$v' [0]=a [5]=b
- echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|${foo[5]}|"
-expected-stdout:
- 5|a|$v|c d||$v|b|
----
-name: arrays-5
-description:
- Check if bash-style arrays with specified indices work as expected
-category: !smksh
-stdin:
- v="c d"
- foo=([1]=\$v [2]="$v" [4]='$v' [0]=a [5]=b)
- echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|${foo[5]}|"
- x=([128]=foo bar baz)
- echo k= ${!x[*]} .
- echo v= ${x[*]} .
-expected-stdout:
- 5|a|$v|c d||$v|b|
- k= 128 129 130 .
- v= foo bar baz .
----
-name: arrays-6
-description:
- Check if we can get the array keys (indices) for indexed arrays,
- Korn shell style
-stdin:
- of() {
- i=0
- for x in "$@"; do
- echo -n "$((i++))<$x>"
- done
- echo
- }
- foo[1]=eins
- set | grep '^foo'
- echo =
- foo[0]=zwei
- foo[4]=drei
- set | grep '^foo'
- echo =
- echo a $(of ${foo[*]}) = $(of ${bar[*]}) a
- echo b $(of "${foo[*]}") = $(of "${bar[*]}") b
- echo c $(of ${foo[@]}) = $(of ${bar[@]}) c
- echo d $(of "${foo[@]}") = $(of "${bar[@]}") d
- echo e $(of ${!foo[*]}) = $(of ${!bar[*]}) e
- echo f $(of "${!foo[*]}") = $(of "${!bar[*]}") f
- echo g $(of ${!foo[@]}) = $(of ${!bar[@]}) g
- echo h $(of "${!foo[@]}") = $(of "${!bar[@]}") h
-expected-stdout:
- foo[1]=eins
- =
- foo[0]=zwei
- foo[1]=eins
- foo[4]=drei
- =
- a 0<zwei>1<eins>2<drei> = a
- b 0<zwei eins drei> = 0<> b
- c 0<zwei>1<eins>2<drei> = c
- d 0<zwei>1<eins>2<drei> = d
- e 0<0>1<1>2<4> = e
- f 0<0 1 4> = 0<> f
- g 0<0>1<1>2<4> = g
- h 0<0>1<1>2<4> = h
----
-name: arrays-7
-description:
- Check if we can get the array keys (indices) for indexed arrays,
- Korn shell style, in some corner cases
-stdin:
- echo !arz: ${!arz}
- echo !arz[0]: ${!arz[0]}
- echo !arz[1]: ${!arz[1]}
- arz=foo
- echo !arz: ${!arz}
- echo !arz[0]: ${!arz[0]}
- echo !arz[1]: ${!arz[1]}
- unset arz
- echo !arz: ${!arz}
- echo !arz[0]: ${!arz[0]}
- echo !arz[1]: ${!arz[1]}
-expected-stdout:
- !arz: 0
- !arz[0]:
- !arz[1]:
- !arz: arz
- !arz[0]: 0
- !arz[1]:
- !arz: 0
- !arz[0]:
- !arz[1]:
----
-name: arrays-8
-description:
- Check some behavioural rules for arrays.
-stdin:
- fna() {
- set -A aa 9
- }
- fnb() {
- typeset ab
- set -A ab 9
- }
- fnc() {
- typeset ac
- set -A ac 91
- unset ac
- set -A ac 92
- }
- fnd() {
- set +A ad 9
- }
- fne() {
- unset ae
- set +A ae 9
- }
- fnf() {
- unset af[0]
- set +A af 9
- }
- fng() {
- unset ag[*]
- set +A ag 9
- }
- set -A aa 1 2
- set -A ab 1 2
- set -A ac 1 2
- set -A ad 1 2
- set -A ae 1 2
- set -A af 1 2
- set -A ag 1 2
- set -A ah 1 2
- typeset -Z3 aa ab ac ad ae af ag
- print 1a ${aa[*]} .
- print 1b ${ab[*]} .
- print 1c ${ac[*]} .
- print 1d ${ad[*]} .
- print 1e ${ae[*]} .
- print 1f ${af[*]} .
- print 1g ${ag[*]} .
- print 1h ${ah[*]} .
- fna
- fnb
- fnc
- fnd
- fne
- fnf
- fng
- typeset -Z5 ah[*]
- print 2a ${aa[*]} .
- print 2b ${ab[*]} .
- print 2c ${ac[*]} .
- print 2d ${ad[*]} .
- print 2e ${ae[*]} .
- print 2f ${af[*]} .
- print 2g ${ag[*]} .
- print 2h ${ah[*]} .
-expected-stdout:
- 1a 001 002 .
- 1b 001 002 .
- 1c 001 002 .
- 1d 001 002 .
- 1e 001 002 .
- 1f 001 002 .
- 1g 001 002 .
- 1h 1 2 .
- 2a 9 .
- 2b 001 002 .
- 2c 92 .
- 2d 009 002 .
- 2e 9 .
- 2f 9 002 .
- 2g 009 .
- 2h 00001 00002 .
----
-name: varexpand-substr-1
-description:
- Check if bash-style substring expansion works
- when using positive numerics
-stdin:
- x=abcdefghi
- typeset -i y=123456789
- typeset -i 16 z=123456789 # 16#75bcd15
- echo a t${x:2:2} ${y:2:3} ${z:2:3} a
- echo b ${x::3} ${y::3} ${z::3} b
- echo c ${x:2:} ${y:2:} ${z:2:} c
- echo d ${x:2} ${y:2} ${z:2} d
- echo e ${x:2:6} ${y:2:6} ${z:2:7} e
- echo f ${x:2:7} ${y:2:7} ${z:2:8} f
- echo g ${x:2:8} ${y:2:8} ${z:2:9} g
-expected-stdout:
- a tcd 345 #75 a
- b abc 123 16# b
- c c
- d cdefghi 3456789 #75bcd15 d
- e cdefgh 345678 #75bcd1 e
- f cdefghi 3456789 #75bcd15 f
- g cdefghi 3456789 #75bcd15 g
----
-name: varexpand-substr-2
-description:
- Check if bash-style substring expansion works
- when using negative numerics or expressions
-stdin:
- x=abcdefghi
- typeset -i y=123456789
- typeset -i 16 z=123456789 # 16#75bcd15
- n=2
- echo a ${x:$n:3} ${y:$n:3} ${z:$n:3} a
- echo b ${x:(n):3} ${y:(n):3} ${z:(n):3} b
- echo c ${x:(-2):1} ${y:(-2):1} ${z:(-2):1} c
- echo d t${x: n:2} ${y: n:3} ${z: n:3} d
-expected-stdout:
- a cde 345 #75 a
- b cde 345 #75 b
- c h 8 1 c
- d tcd 345 #75 d
----
-name: varexpand-substr-3
-description:
- Check that some things that work in bash fail.
- This is by design. And that some things fail in both.
-stdin:
- export x=abcdefghi n=2
- "$__progname" -c 'echo v${x:(n)}x'
- "$__progname" -c 'echo w${x: n}x'
- "$__progname" -c 'echo x${x:n}x'
- "$__progname" -c 'echo y${x:}x'
- "$__progname" -c 'echo z${x}x'
- "$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
-expected-stdout:
- vcdefghix
- wcdefghix
- zabcdefghix
- 1
-expected-stderr-pattern:
- /x:n.*bad substitution.*\n.*bad substitution/
----
-name: varexpand-substr-4
-description:
- Check corner cases for substring expansion
-stdin:
- x=abcdefghi
- integer y=2
- echo a ${x:(y == 1 ? 2 : 3):4} a
-expected-stdout:
- a defg a
----
-name: varexpand-substr-5A
-description:
- Check that substring expansions work on characters
-stdin:
- set +U
- x=mäh
- echo a ${x::1} ${x: -1} a
- echo b ${x::3} ${x: -3} b
- echo c ${x:1:2} ${x: -3:2} c
- echo d ${#x} d
-expected-stdout:
- a m h a
- b mä äh b
- c ä ä c
- d 4 d
----
-name: varexpand-substr-5W
-description:
- Check that substring expansions work on characters
-stdin:
- set -U
- x=mäh
- echo a ${x::1} ${x: -1} a
- echo b ${x::2} ${x: -2} b
- echo c ${x:1:1} ${x: -2:1} c
- echo d ${#x} d
-expected-stdout:
- a m h a
- b mä äh b
- c ä ä c
- d 3 d
----
-name: varexpand-substr-6
-description:
- Check that string substitution works correctly
-stdin:
- foo=1
- bar=2
- baz=qwertyuiop
- echo a ${baz: foo: bar}
- echo b ${baz: foo: $bar}
- echo c ${baz: $foo: bar}
- echo d ${baz: $foo: $bar}
-expected-stdout:
- a we
- b we
- c we
- d we
----
-name: varexpand-null-1
-description:
- Ensure empty strings expand emptily
-stdin:
- print x ${a} ${b} y
- print z ${a#?} ${b%?} w
- print v ${a=} ${b/c/d} u
-expected-stdout:
- x y
- z w
- v u
----
-name: varexpand-null-2
-description:
- Ensure empty strings, when quoted, are expanded as empty strings
-stdin:
- printf '<%s> ' 1 "${a}" 2 "${a#?}" + "${b%?}" 3 "${a=}" + "${b/c/d}"
- echo .
-expected-stdout:
- <1> <> <2> <> <+> <> <3> <> <+> <> .
----
-name: print-funny-chars
-description:
- Check print builtin's capability to output designated characters
-stdin:
- print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
-expected-stdout:
- <däÛÃâ¬Ã@>
----
-name: print-bksl-c
-description:
- Check print builtin's \c escape
-stdin:
- print '\ca'; print b
-expected-stdout:
- ab
----
-name: print-nul-chars
-description:
- Check handling of NUL characters for print and read
- note: second line should output “4 3” but we cannot
- handle NUL characters in strings yet
-stdin:
- print $(($(print '<\0>' | wc -c)))
- x=$(print '<\0>')
- print $(($(print "$x" | wc -c))) ${#x}
-expected-stdout:
- 4
- 3 2
----
-name: print-escapes
-description:
- Check backslash expansion by the print builtin
-stdin:
- print '\ \!\"\#\$\%\&'\\\''\(\)\*\+\,\-\.\/\0\1\2\3\4\5\6\7\8' \
- '\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T' \
- '\U\V\W\X\Y\Z\[\\\]\^\_\`\a\b \d\e\f\g\h\i\j\k\l\m\n\o\p' \
- '\q\r\s\t\u\v\w\x\y\z\{\|\}\~' '\u20acd' '\U20acd' '\x123' \
- '\0x' '\0123' '\01234' | {
- typeset -Uui16 -Z11 pos=0
- typeset -Uui16 -Z5 hv
- typeset -i1 wc=0x0A
- dasc=
- nl=${wc#1#}
- while IFS= read -r line; do
- line=$line$nl
- while [[ -n $line ]]; do
- hv=1#${line::1}
- if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
- print -n "${pos#16#} "
- dasc=' |'
- fi
- print -n "${hv#16#} "
- if (( (hv < 32) || (hv > 126) )); then
- dasc=$dasc.
- else
- dasc=$dasc${line::1}
- fi
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- line=${line:1}
- done
- done
- if (( (pos & 15) != 1 )); then
- while (( pos & 15 )); do
- print -n ' '
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- done
- print "$dasc|"
- fi
- }
-expected-stdout:
- 00000000 5C 20 5C 21 5C 22 5C 23 - 5C 24 5C 25 5C 26 5C 27 |\ \!\"\#\$\%\&\'|
- 00000010 5C 28 5C 29 5C 2A 5C 2B - 5C 2C 5C 2D 5C 2E 5C 2F |\(\)\*\+\,\-\.\/|
- 00000020 5C 31 5C 32 5C 33 5C 34 - 5C 35 5C 36 5C 37 5C 38 |\1\2\3\4\5\6\7\8|
- 00000030 20 5C 39 5C 3A 5C 3B 5C - 3C 5C 3D 5C 3E 5C 3F 5C | \9\:\;\<\=\>\?\|
- 00000040 40 5C 41 5C 42 5C 43 5C - 44 1B 5C 46 5C 47 5C 48 |@\A\B\C\D.\F\G\H|
- 00000050 5C 49 5C 4A 5C 4B 5C 4C - 5C 4D 5C 4E 5C 4F 5C 50 |\I\J\K\L\M\N\O\P|
- 00000060 5C 51 5C 52 5C 53 5C 54 - 20 5C 56 5C 57 5C 58 5C |\Q\R\S\T \V\W\X\|
- 00000070 59 5C 5A 5C 5B 5C 5C 5D - 5C 5E 5C 5F 5C 60 07 08 |Y\Z\[\]\^\_\`..|
- 00000080 20 20 5C 64 1B 0C 5C 67 - 5C 68 5C 69 5C 6A 5C 6B | \d..\g\h\i\j\k|
- 00000090 5C 6C 5C 6D 0A 5C 6F 5C - 70 20 5C 71 0D 5C 73 09 |\l\m.\o\p \q.\s.|
- 000000A0 0B 5C 77 5C 79 5C 7A 5C - 7B 5C 7C 5C 7D 5C 7E 20 |.\w\y\z\{\|\}\~ |
- 000000B0 E2 82 AC 64 20 EF BF BD - 20 12 33 20 78 20 53 20 |...d ... .3 x S |
- 000000C0 53 34 0A - |S4.|
----
-name: dollar-quoted-strings
-description:
- Check backslash expansion by $'…' strings
-stdin:
- printf '%s\n' $'\ \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/ \1\2\3\4\5\6' \
- $'a\0b' $'a\01b' $'\7\8\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I' \
- $'\J\K\L\M\N\O\P\Q\R\S\T\U1\V\W\X\Y\Z\[\\\]\^\_\`\a\b\d\e' \
- $'\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u1\v\w\x1\y\z\{\|\}\~ $x' \
- $'\u20acd' $'\U20acd' $'\x123' $'fn\x0rd' $'\0234' $'\234' \
- $'\2345' $'\ca' $'\c!' $'\c?' $'\c€' $'a\
- b' | {
- typeset -Uui16 -Z11 pos=0
- typeset -Uui16 -Z5 hv
- typeset -i1 wc=0x0A
- dasc=
- nl=${wc#1#}
- while IFS= read -r line; do
- line=$line$nl
- while [[ -n $line ]]; do
- hv=1#${line::1}
- if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
- print -n "${pos#16#} "
- dasc=' |'
- fi
- print -n "${hv#16#} "
- if (( (hv < 32) || (hv > 126) )); then
- dasc=$dasc.
- else
- dasc=$dasc${line::1}
- fi
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- line=${line:1}
- done
- done
- if (( (pos & 15) != 1 )); then
- while (( pos & 15 )); do
- print -n ' '
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- done
- print "$dasc|"
- fi
- }
-expected-stdout:
- 00000000 20 21 22 23 24 25 26 27 - 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./|
- 00000010 20 01 02 03 04 05 06 0A - 61 0A 61 01 62 0A 07 38 | .......a.a.b..8|
- 00000020 39 3A 3B 3C 3D 3E 3F 40 - 41 42 43 44 1B 46 47 48 |9:;<=>?@ABCD.FGH|
- 00000030 49 0A 4A 4B 4C 4D 4E 4F - 50 51 52 53 54 01 56 57 |I.JKLMNOPQRST.VW|
- 00000040 58 59 5A 5B 5C 5D 5E 5F - 60 07 08 64 1B 0A 0C 67 |XYZ[\]^_`..d...g|
- 00000050 68 69 6A 6B 6C 6D 0A 6F - 70 71 0D 73 09 01 0B 77 |hijklm.opq.s...w|
- 00000060 01 79 7A 7B 7C 7D 7E 20 - 24 78 0A E2 82 AC 64 0A |.yz{|}~ $x....d.|
- 00000070 EF BF BD 0A C4 A3 0A 66 - 6E 0A 13 34 0A 9C 0A 9C |.......fn..4....|
- 00000080 35 0A 01 0A 01 0A 7F 0A - 02 82 AC 0A 61 0A 62 0A |5...........a.b.|
----
-name: dollar-quotes-in-heredocs
-description:
- They are, however, not parsed in here documents
-stdin:
- cat <<EOF
- dollar = strchr(s, '$'); /* ' */
- EOF
- cat <<$'a\tb'
- a\tb
- a b
-expected-stdout:
- dollar = strchr(s, '$'); /* ' */
- a\tb
----
-name: dollar-quotes-in-herestrings
-description:
- They are, not parsed in here strings either
-stdin:
- cat <<<"dollar = strchr(s, '$'); /* ' */"
- cat <<<'dollar = strchr(s, '\''$'\''); /* '\'' */'
- x="dollar = strchr(s, '$'); /* ' */"
- cat <<<"$x"
- cat <<<$'a\E[0m\tb'
-expected-stdout:
- dollar = strchr(s, '$'); /* ' */
- dollar = strchr(s, '$'); /* ' */
- dollar = strchr(s, '$'); /* ' */
- a[0m b
----
-name: dot-needs-argument
-description:
- check Debian #415167 solution: '.' without arguments should fail
-stdin:
- "$__progname" -c .
- "$__progname" -c source
-expected-exit: e != 0
-expected-stderr-pattern:
- /\.: missing argument.*\n.*\.: missing argument/
----
-name: alias-function-no-conflict
-description:
- make aliases not conflict with functions
- note: for ksh-like functions, the order of preference is
- different; bash outputs baz instead of bar in line 2 below
-stdin:
- alias foo='echo bar'
- foo() {
- echo baz
- }
- alias korn='echo bar'
- function korn {
- echo baz
- }
- foo
- korn
- unset -f foo
- foo 2>&- || echo rab
-expected-stdout:
- baz
- bar
- rab
----
-name: bash-function-parens
-description:
- ensure the keyword function is ignored when preceding
- POSIX style function declarations (bashism)
-stdin:
- mk() {
- echo '#!'"$__progname"
- echo "$1 {"
- echo ' echo "bar='\''$0'\'\"
- echo '}'
- echo ${2:-foo}
- }
- mk 'function foo' >f-korn
- mk 'foo ()' >f-dash
- mk 'function foo ()' >f-bash
- mk 'function stop ()' stop >f-stop
- chmod +x f-*
- echo "korn: $(./f-korn)"
- echo "dash: $(./f-dash)"
- echo "bash: $(./f-bash)"
- echo "stop: $(./f-stop)"
-expected-stdout:
- korn: bar='foo'
- dash: bar='./f-dash'
- bash: bar='./f-bash'
- stop: bar='./f-stop'
----
-name: integer-base-one-1
-description:
- check if the use of fake integer base 1 works
-stdin:
- set -U
- typeset -Uui16 i0=1#ï i1=1#â¬
- typeset -i1 o0a=64
- typeset -i1 o1a=0x263A
- typeset -Uui1 o0b=0x7E
- typeset -Uui1 o1b=0xFDD0
- integer px=0xCAFE 'p0=1# ' p1=1#… pl=1#f
- echo "in <$i0> <$i1>"
- echo "out <${o0a#1#}|${o0b#1#}> <${o1a#1#}|${o1b#1#}>"
- typeset -Uui1 i0 i1
- echo "pass <$px> <$p0> <$p1> <$pl> <${i0#1#}|${i1#1#}>"
- typeset -Uui16 tv1=1#~ tv2=1# tv3=1# tv4=1# tv5=1#À tv6=1#Á tv7=1#Â tv8=1#Â
- echo "specX <${tv1#16#}> <${tv2#16#}> <${tv3#16#}> <${tv4#16#}> <${tv5#16#}> <${tv6#16#}> <${tv7#16#}> <${tv8#16#}>"
- typeset -i1 tv1 tv2 tv3 tv4 tv5 tv6 tv7 tv8
- echo "specW <${tv1#1#}> <${tv2#1#}> <${tv3#1#}> <${tv4#1#}> <${tv5#1#}> <${tv6#1#}> <${tv7#1#}> <${tv8#1#}>"
- typeset -i1 xs1=0xEF7F xs2=0xEF80 xs3=0xFDD0
- echo "specU <${xs1#1#}> <${xs2#1#}> <${xs3#1#}>"
-expected-stdout:
- in <16#EFEF> <16#20AC>
- out <@|~> <☺|>
- pass <16#cafe> <1# > <1#â¦> <1#f> <ï|â¬>
- specX <7E> <7F> <EF80> <EF81> <EFC0> <EFC1> <A0> <80>
- specW <~> <> <> <> <À> <Á> <Â > <Â>
- specU <> <> <ï·>
----
-name: integer-base-one-2a
-description:
- check if the use of fake integer base 1 stops at correct characters
-stdin:
- set -U
- integer x=1#foo
- echo /$x/
-expected-stderr-pattern:
- /1#foo: unexpected 'oo'/
-expected-exit: e != 0
----
-name: integer-base-one-2b
-description:
- check if the use of fake integer base 1 stops at correct characters
-stdin:
- set -U
- integer x=1#À
- echo /$x/
-expected-stderr-pattern:
- /1#À: unexpected ''/
-expected-exit: e != 0
----
-name: integer-base-one-2c1
-description:
- check if the use of fake integer base 1 stops at correct characters
-stdin:
- set -U
- integer x=1#…
- echo /$x/
-expected-stdout:
- /1#…/
----
-name: integer-base-one-2c2
-description:
- check if the use of fake integer base 1 stops at correct characters
-stdin:
- set +U
- integer x=1#…
- echo /$x/
-expected-stderr-pattern:
- /1#â¦: unexpected ''/
-expected-exit: e != 0
----
-name: integer-base-one-2d1
-description:
- check if the use of fake integer base 1 handles octets okay
-stdin:
- set -U
- typeset -i16 x=1#ÿ
- echo /$x/ # invalid utf-8
-expected-stdout:
- /16#efff/
----
-name: integer-base-one-2d2
-description:
- check if the use of fake integer base 1 handles octets
-stdin:
- set -U
- typeset -i16 x=1#Â
- echo /$x/ # invalid 2-byte
-expected-stdout:
- /16#efc2/
----
-name: integer-base-one-2d3
-description:
- check if the use of fake integer base 1 handles octets
-stdin:
- set -U
- typeset -i16 x=1#ï
- echo /$x/ # invalid 2-byte
-expected-stdout:
- /16#efef/
----
-name: integer-base-one-2d4
-description:
- check if the use of fake integer base 1 stops at invalid input
-stdin:
- set -U
- typeset -i16 x=1#ï¿À
- echo /$x/ # invalid 3-byte
-expected-stderr-pattern:
- /1#ï¿À: unexpected '¿'/
-expected-exit: e != 0
----
-name: integer-base-one-2d5
-description:
- check if the use of fake integer base 1 stops at invalid input
-stdin:
- set -U
- typeset -i16 x=1#À
- echo /$x/ # non-minimalistic
-expected-stderr-pattern:
- /1#À: unexpected ''/
-expected-exit: e != 0
----
-name: integer-base-one-2d6
-description:
- check if the use of fake integer base 1 stops at invalid input
-stdin:
- set -U
- typeset -i16 x=1#à
- echo /$x/ # non-minimalistic
-expected-stderr-pattern:
- /1#à: unexpected ''/
-expected-exit: e != 0
----
-name: integer-base-one-3A
-description:
- some sample code for hexdumping
-stdin:
- {
- print 'Hello, World!\\\nこんにちは!'
- typeset -Uui16 i=0x100
- # change that to 0xFF once we can handle embedded
- # NUL characters in strings / here documents
- while (( i++ < 0x1FF )); do
- print -n "\x${i#16#1}"
- done
- print
- } | {
- typeset -Uui16 -Z11 pos=0
- typeset -Uui16 -Z5 hv
- typeset -i1 wc=0x0A
- dasc=
- nl=${wc#1#}
- while IFS= read -r line; do
- line=$line$nl
- while [[ -n $line ]]; do
- hv=1#${line::1}
- if (( (pos & 15) == 0 )); then
- (( pos )) && print "$dasc|"
- print -n "${pos#16#} "
- dasc=' |'
- fi
- print -n "${hv#16#} "
- if (( (hv < 32) || (hv > 126) )); then
- dasc=$dasc.
- else
- dasc=$dasc${line::1}
- fi
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- line=${line:1}
- done
- done
- if (( (pos & 15) != 1 )); then
- while (( pos & 15 )); do
- print -n ' '
- (( (pos++ & 15) == 7 )) && print -n -- '- '
- done
- print "$dasc|"
- fi
- }
-expected-stdout:
- 00000000 48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3 |Hello, World!\..|
- 00000010 81 93 E3 82 93 E3 81 AB - E3 81 A1 E3 81 AF EF BC |................|
- 00000020 81 0A 01 02 03 04 05 06 - 07 08 09 0A 0B 0C 0D 0E |................|
- 00000030 0F 10 11 12 13 14 15 16 - 17 18 19 1A 1B 1C 1D 1E |................|
- 00000040 1F 20 21 22 23 24 25 26 - 27 28 29 2A 2B 2C 2D 2E |. !"#$%&'()*+,-.|
- 00000050 2F 30 31 32 33 34 35 36 - 37 38 39 3A 3B 3C 3D 3E |/0123456789:;<=>|
- 00000060 3F 40 41 42 43 44 45 46 - 47 48 49 4A 4B 4C 4D 4E |?@ABCDEFGHIJKLMN|
- 00000070 4F 50 51 52 53 54 55 56 - 57 58 59 5A 5B 5C 5D 5E |OPQRSTUVWXYZ[\]^|
- 00000080 5F 60 61 62 63 64 65 66 - 67 68 69 6A 6B 6C 6D 6E |_`abcdefghijklmn|
- 00000090 6F 70 71 72 73 74 75 76 - 77 78 79 7A 7B 7C 7D 7E |opqrstuvwxyz{|}~|
- 000000A0 7F 80 81 82 83 84 85 86 - 87 88 89 8A 8B 8C 8D 8E |................|
- 000000B0 8F 90 91 92 93 94 95 96 - 97 98 99 9A 9B 9C 9D 9E |................|
- 000000C0 9F A0 A1 A2 A3 A4 A5 A6 - A7 A8 A9 AA AB AC AD AE |................|
- 000000D0 AF B0 B1 B2 B3 B4 B5 B6 - B7 B8 B9 BA BB BC BD BE |................|
- 000000E0 BF C0 C1 C2 C3 C4 C5 C6 - C7 C8 C9 CA CB CC CD CE |................|
- 000000F0 CF D0 D1 D2 D3 D4 D5 D6 - D7 D8 D9 DA DB DC DD DE |................|
- 00000100 DF E0 E1 E2 E3 E4 E5 E6 - E7 E8 E9 EA EB EC ED EE |................|
- 00000110 EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE |................|
- 00000120 FF 0A - |..|
----
-name: integer-base-one-3W
-description:
- some sample code for hexdumping Unicode
-stdin:
- set -U
- {
- print 'Hello, World!\\\nこんにちは!'
- typeset -Uui16 i=0x100
- # change that to 0xFF once we can handle embedded
- # NUL characters in strings / here documents
- while (( i++ < 0x1FF )); do
- print -n "\u${i#16#1}"
- done
- print
- print \\xff # invalid utf-8
- print \\xc2 # invalid 2-byte
- print \\xef\\xbf\\xc0 # invalid 3-byte
- print \\xc0\\x80 # non-minimalistic
- print \\xe0\\x80\\x80 # non-minimalistic
- print '�' # end of range
- } | {
- typeset -Uui16 -Z11 pos=0
- typeset -Uui16 -Z7 hv
- typeset -i1 wc=0x0A
- typeset -i lpos
- dasc=
- nl=${wc#1#}
- while IFS= read -r line; do
- line=$line$nl
- lpos=0
- while (( lpos < ${#line} )); do
- wc=1#${line:(lpos++):1}
- if (( (wc < 32) || \
- ((wc > 126) && (wc < 160)) )); then
- dch=.
- elif (( (wc & 0xFF80) == 0xEF80 )); then
- dch=�
- else
- dch=${wc#1#}
- fi
- if (( (pos & 7) == 7 )); then
- dasc=$dasc$dch
- dch=
- elif (( (pos & 7) == 0 )); then
- (( pos )) && print "$dasc|"
- print -n "${pos#16#} "
- dasc=' |'
- fi
- let hv=wc
- print -n "${hv#16#} "
- (( (pos++ & 7) == 3 )) && \
- print -n -- '- '
- dasc=$dasc$dch
- done
- done
- if (( pos & 7 )); then
- while (( pos & 7 )); do
- print -n ' '
- (( (pos++ & 7) == 3 )) && print -n -- '- '
- done
- print "$dasc|"
- fi
- }
-expected-stdout:
- 00000000 0048 0065 006C 006C - 006F 002C 0020 0057 |Hello, W|
- 00000008 006F 0072 006C 0064 - 0021 005C 000A 3053 |orld!\.こ|
- 00000010 3093 306B 3061 306F - FF01 000A 0001 0002 |んにちは!...|
- 00000018 0003 0004 0005 0006 - 0007 0008 0009 000A |........|
- 00000020 000B 000C 000D 000E - 000F 0010 0011 0012 |........|
- 00000028 0013 0014 0015 0016 - 0017 0018 0019 001A |........|
- 00000030 001B 001C 001D 001E - 001F 0020 0021 0022 |..... !"|
- 00000038 0023 0024 0025 0026 - 0027 0028 0029 002A |#$%&'()*|
- 00000040 002B 002C 002D 002E - 002F 0030 0031 0032 |+,-./012|
- 00000048 0033 0034 0035 0036 - 0037 0038 0039 003A |3456789:|
- 00000050 003B 003C 003D 003E - 003F 0040 0041 0042 |;<=>?@AB|
- 00000058 0043 0044 0045 0046 - 0047 0048 0049 004A |CDEFGHIJ|
- 00000060 004B 004C 004D 004E - 004F 0050 0051 0052 |KLMNOPQR|
- 00000068 0053 0054 0055 0056 - 0057 0058 0059 005A |STUVWXYZ|
- 00000070 005B 005C 005D 005E - 005F 0060 0061 0062 |[\]^_`ab|
- 00000078 0063 0064 0065 0066 - 0067 0068 0069 006A |cdefghij|
- 00000080 006B 006C 006D 006E - 006F 0070 0071 0072 |klmnopqr|
- 00000088 0073 0074 0075 0076 - 0077 0078 0079 007A |stuvwxyz|
- 00000090 007B 007C 007D 007E - 007F 0080 0081 0082 |{|}~....|
- 00000098 0083 0084 0085 0086 - 0087 0088 0089 008A |........|
- 000000A0 008B 008C 008D 008E - 008F 0090 0091 0092 |........|
- 000000A8 0093 0094 0095 0096 - 0097 0098 0099 009A |........|
- 000000B0 009B 009C 009D 009E - 009F 00A0 00A1 00A2 |..... ¡¢|
- 000000B8 00A3 00A4 00A5 00A6 - 00A7 00A8 00A9 00AA |£¤¥¦§¨©ª|
- 000000C0 00AB 00AC 00AD 00AE - 00AF 00B0 00B1 00B2 |«¬®¯°±²|
- 000000C8 00B3 00B4 00B5 00B6 - 00B7 00B8 00B9 00BA |³´µ¶·¸¹º|
- 000000D0 00BB 00BC 00BD 00BE - 00BF 00C0 00C1 00C2 |»¼½¾¿ÀÁÂ|
- 000000D8 00C3 00C4 00C5 00C6 - 00C7 00C8 00C9 00CA |ÃÄÅÆÇÈÉÊ|
- 000000E0 00CB 00CC 00CD 00CE - 00CF 00D0 00D1 00D2 |ËÌÍÎÏÐÑÒ|
- 000000E8 00D3 00D4 00D5 00D6 - 00D7 00D8 00D9 00DA |ÓÔÕÖ×ØÙÚ|
- 000000F0 00DB 00DC 00DD 00DE - 00DF 00E0 00E1 00E2 |ÛÜÝÞßàáâ|
- 000000F8 00E3 00E4 00E5 00E6 - 00E7 00E8 00E9 00EA |ãäåæçèéê|
- 00000100 00EB 00EC 00ED 00EE - 00EF 00F0 00F1 00F2 |ëìíîïðñò|
- 00000108 00F3 00F4 00F5 00F6 - 00F7 00F8 00F9 00FA |óôõö÷øùú|
- 00000110 00FB 00FC 00FD 00FE - 00FF 000A EFFF 000A |ûüýþÿ.�.|
- 00000118 EFC2 000A EFEF EFBF - EFC0 000A EFC0 EF80 |�.���.��|
- 00000120 000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF |.���.���|
- 00000128 EFBE EFEF EFBF EFBF - 000A |����.|
----
-name: integer-base-one-4
-description:
- Check if ksh93-style base-one integers work
-category: !smksh
-stdin:
- set -U
- echo 1 $(('a'))
- (echo 2f $(('aa'))) 2>&1 | sed "s/^[^']*'/2p '/"
- echo 3 $(('…'))
- x="'a'"
- echo "4 <$x>"
- echo 5 $(($x))
- echo 6 $((x))
-expected-stdout:
- 1 97
- 2p 'aa': multi-character character constant
- 3 8230
- 4 <'a'>
- 5 97
- 6 97
----
-name: ulimit-1
-description:
- Check if we can use a specific syntax idiom for ulimit
-stdin:
- if ! x=$(ulimit -d) || [[ $x = unknown ]]; then
- #echo expected to fail on this OS
- echo okay
- else
- ulimit -dS $x && echo okay
- fi
-expected-stdout:
- okay
----
-name: bashiop-1
-description:
- Check if GNU bash-like I/O redirection works
- Part 1: this is also supported by GNU bash
-stdin:
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- threeout &>foo
- echo ===
- cat foo
-expected-stdout:
- tri
- ===
- ras
- dwa
----
-name: bashiop-2a
-description:
- Check if GNU bash-like I/O redirection works
- Part 2: this is *not* supported by GNU bash
-stdin:
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- threeout 3&>foo
- echo ===
- cat foo
-expected-stdout:
- ras
- ===
- dwa
- tri
----
-name: bashiop-2b
-description:
- Check if GNU bash-like I/O redirection works
- Part 2: this is *not* supported by GNU bash
-stdin:
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- threeout 3>foo &>&3
- echo ===
- cat foo
-expected-stdout:
- ===
- ras
- dwa
- tri
----
-name: bashiop-2c
-description:
- Check if GNU bash-like I/O redirection works
- Part 2: this is supported by GNU bash 4 only
-stdin:
- echo mir >foo
- set -o noclobber
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- threeout &>>foo
- echo ===
- cat foo
-expected-stdout:
- tri
- ===
- mir
- ras
- dwa
----
-name: bashiop-3a
-description:
- Check if GNU bash-like I/O redirection fails correctly
- Part 1: this is also supported by GNU bash
-stdin:
- echo mir >foo
- set -o noclobber
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- threeout &>foo
- echo ===
- cat foo
-expected-stdout:
- ===
- mir
-expected-stderr-pattern: /.*: cannot (create|overwrite) .*/
----
-name: bashiop-3b
-description:
- Check if GNU bash-like I/O redirection fails correctly
- Part 2: this is *not* supported by GNU bash
-stdin:
- echo mir >foo
- set -o noclobber
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- threeout &>|foo
- echo ===
- cat foo
-expected-stdout:
- tri
- ===
- ras
- dwa
----
-name: bashiop-4
-description:
- Check if GNU bash-like I/O redirection works
- Part 4: this is also supported by GNU bash,
- but failed in some mksh versions
-stdin:
- exec 3>&1
- function threeout {
- echo ras
- echo dwa >&2
- echo tri >&3
- }
- function blubb {
- [[ -e bar ]] && threeout "$bf" &>foo
- }
- blubb
- echo -n >bar
- blubb
- echo ===
- cat foo
-expected-stdout:
- tri
- ===
- ras
- dwa
----
-name: mkshiop-1
-description:
- Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
- read -u10 foo 10<<< bar
- echo x$foo
-expected-stdout:
- xbar
----
-name: mkshiop-2
-description:
- Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
- exec 12>foo
- print -u12 bar
- echo baz >&12
- cat foo
-expected-stdout:
- bar
- baz
----
-name: oksh-shcrash
-description:
- src/regress/bin/ksh/shcrash.sh,v 1.1
-stdin:
- deplibs="-lz -lpng /usr/local/lib/libjpeg.la -ltiff -lm -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -ltiff -ljpeg -lz -lpng -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk_pixbuf.la -lz -lpng /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile -L/usr/local/lib /usr/local/lib/libesd.la -lm -lz -L/usr/local/lib /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib -L/usr/local/lib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lpng /usr/local/lib/libungif.la -lz /usr/local/lib/libjpeg.la -ltiff -L/usr/local/lib -L/usr/X11R6/lib /usr/local/lib/libgdk_imlib.la -lm -L/usr/local/lib /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lICE -lSM -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lm -lz -lpng -lungif -lz -ljpeg -ltiff -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd -L/usr/local/lib /usr/local/lib/libgnomeui.la -lz -lz /usr/local/lib/libxml.la -lz -lz -lz /usr/local/lib/libxml.la -lm -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la /usr/local/lib/libgmodule.la -lintl -lglib -lgmodule /usr/local/lib/libgdk.la /usr/local/lib/libgtk.la -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libglade.la -lz -lz -lz /usr/local/lib/libxml.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile /usr/local/lib/libesd.la -lm -lz /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -lglib -lgmodule /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -lglib -lgmodule /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lz /usr/local/lib/libgdk_imlib.la /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lm -lz -lungif -lz -ljpeg -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd /usr/local/lib/libgnomeui.la -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libglade-gnome.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile -L/usr/local/lib /usr/local/lib/libesd.la -lm -lz -L/usr/local/lib /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib -L/usr/local/lib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lpng /usr/local/lib/libungif.la -lz /usr/local/lib/libjpeg.la -ltiff -L/usr/local/lib -L/usr/X11R6/lib /usr/local/lib/libgdk_imlib.la -lm -L/usr/local/lib /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lICE -lSM -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lm -lz -lpng -lungif -lz -ljpeg -ltiff -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd -L/usr/local/lib /usr/local/lib/libgnomeui.la -L/usr/X11R6/lib -L/usr/local/lib"
- specialdeplibs="-lgnomeui -lart_lgpl -lgdk_imlib -ltiff -ljpeg -lungif -lpng -lz -lSM -lICE -lgtk -lgdk -lgmodule -lintl -lXext -lX11 -lgnome -lgnomesupport -lesd -laudiofile -lm -lglib"
- for deplib in $deplibs; do
- case $deplib in
- -L*)
- new_libs="$deplib $new_libs"
- ;;
- *)
- case " $specialdeplibs " in
- *" $deplib "*)
- new_libs="$deplib $new_libs";;
- esac
- ;;
- esac
- done
----
-name: oksh-varfunction-mod1
-description:
- $OpenBSD: varfunction.sh,v 1.1 2003/12/15 05:28:40 otto Exp $
- Calling
- FOO=bar f
- where f is a ksh style function, should not set FOO in the current
- env. If f is a Bourne style function, FOO should be set. Furthermore,
- the function should receive a correct value of FOO. However, differing
- from oksh, setting FOO in the function itself must change the value in
- setting FOO in the function itself should not change the value in
- global environment.
- Inspired by PR 2450.
-stdin:
- function k {
- if [ x$FOO != xbar ]; then
- echo 1
- return 1
- fi
- x=$(env | grep FOO)
- if [ "x$x" != "xFOO=bar" ]; then
- echo 2
- return 1;
- fi
- FOO=foo
- return 0
- }
- b () {
- if [ x$FOO != xbar ]; then
- echo 3
- return 1
- fi
- x=$(env | grep FOO)
- if [ "x$x" != "xFOO=bar" ]; then
- echo 4
- return 1;
- fi
- FOO=foo
- return 0
- }
- FOO=bar k
- if [ $? != 0 ]; then
- exit 1
- fi
- if [ x$FOO != x ]; then
- exit 1
- fi
- FOO=bar b
- if [ $? != 0 ]; then
- exit 1
- fi
- if [ x$FOO != xfoo ]; then
- exit 1
- fi
- FOO=barbar
- FOO=bar k
- if [ $? != 0 ]; then
- exit 1
- fi
- if [ x$FOO != xbarbar ]; then
- exit 1
- fi
- FOO=bar b
- if [ $? != 0 ]; then
- exit 1
- fi
- if [ x$FOO != xfoo ]; then
- exit 1
- fi
----
-name: fd-cloexec-1
-description:
- Verify that file descriptors > 2 are private for Korn shells
-file-setup: file 644 "test.sh"
- print -u3 Fowl
-stdin:
- exec 3>&1
- "$__progname" test.sh
-expected-exit: e != 0
-expected-stderr:
- test.sh[1]: print: -u: 3: bad file descriptor
----
-name: fd-cloexec-2
-description:
- Verify that file descriptors > 2 are not private for POSIX shells
- See Debian Bug #154540, Closes: #499139
-file-setup: file 644 "test.sh"
- print -u3 Fowl
-stdin:
- test -n "$POSH_VERSION" || set -o sh
- exec 3>&1
- "$__progname" test.sh
-expected-stdout:
- Fowl
----
-name: comsub-1
-description:
- COMSUB are currently parsed by hacking lex.c instead of
- recursively (see regression-6): matching parenthesēs bug
- Fails on: pdksh mksh bash2 bash3 zsh
- Passes on: bash4 ksh93
-expected-fail: yes
-stdin:
- echo $(case 1 in (1) echo yes;; (2) echo no;; esac)
- echo $(case 1 in 1) echo yes;; 2) echo no;; esac)
-expected-stdout:
- yes
- yes
----
-name: comsub-2
-description:
- RedHat BZ#496791 – another case of missing recursion
- in parsing COMSUB expressions
- Fails on: pdksh mksh bash2 bash3¹ bash4¹ zsh
- Passes on: ksh93
- ① bash[34] seem to choke on comment ending with backslash-newline
-expected-fail: yes
-stdin:
- # a comment with " ' \
- x=$(
- echo yes
- # a comment with " ' \
- )
- echo $x
-expected-stdout:
- yes
----
-name: test-stnze-1
-description:
- Check that the short form [ $x ] works
-stdin:
- i=0
- [ -n $x ]
- rv=$?; echo $((++i)) $rv
- [ $x ]
- rv=$?; echo $((++i)) $rv
- [ -n "$x" ]
- rv=$?; echo $((++i)) $rv
- [ "$x" ]
- rv=$?; echo $((++i)) $rv
- x=0
- [ -n $x ]
- rv=$?; echo $((++i)) $rv
- [ $x ]
- rv=$?; echo $((++i)) $rv
- [ -n "$x" ]
- rv=$?; echo $((++i)) $rv
- [ "$x" ]
- rv=$?; echo $((++i)) $rv
- x='1 -a 1 = 2'
- [ -n $x ]
- rv=$?; echo $((++i)) $rv
- [ $x ]
- rv=$?; echo $((++i)) $rv
- [ -n "$x" ]
- rv=$?; echo $((++i)) $rv
- [ "$x" ]
- rv=$?; echo $((++i)) $rv
-expected-stdout:
- 1 0
- 2 1
- 3 1
- 4 1
- 5 0
- 6 0
- 7 0
- 8 0
- 9 1
- 10 1
- 11 0
- 12 0
----
-name: test-stnze-2
-description:
- Check that the short form [[ $x ]] works (ksh93 extension)
-stdin:
- i=0
- [[ -n $x ]]
- rv=$?; echo $((++i)) $rv
- [[ $x ]]
- rv=$?; echo $((++i)) $rv
- [[ -n "$x" ]]
- rv=$?; echo $((++i)) $rv
- [[ "$x" ]]
- rv=$?; echo $((++i)) $rv
- x=0
- [[ -n $x ]]
- rv=$?; echo $((++i)) $rv
- [[ $x ]]
- rv=$?; echo $((++i)) $rv
- [[ -n "$x" ]]
- rv=$?; echo $((++i)) $rv
- [[ "$x" ]]
- rv=$?; echo $((++i)) $rv
- x='1 -a 1 = 2'
- [[ -n $x ]]
- rv=$?; echo $((++i)) $rv
- [[ $x ]]
- rv=$?; echo $((++i)) $rv
- [[ -n "$x" ]]
- rv=$?; echo $((++i)) $rv
- [[ "$x" ]]
- rv=$?; echo $((++i)) $rv
-expected-stdout:
- 1 1
- 2 1
- 3 1
- 4 1
- 5 0
- 6 0
- 7 0
- 8 0
- 9 0
- 10 0
- 11 0
- 12 0
----
-name: event-subst-1a
-description:
- Check that '!' substitution in interactive mode works
-category: !smksh
-file-setup: file 755 "falsetto"
- #! /bin/sh
- echo molto bene
- exit 42
-file-setup: file 755 "!false"
- #! /bin/sh
- echo si
-arguments: !-i!
-stdin:
- export PATH=.:$PATH
- falsetto
- echo yeap
- !false
-expected-exit: 42
-expected-stdout:
- molto bene
- yeap
- molto bene
-expected-stderr-pattern:
- /.*/
----
-name: event-subst-1b
-description:
- Check that '!' substitution in interactive mode works
- even when a space separates it from the search command,
- which is not what GNU bash provides but required for the
- other regression tests below to check
-category: !smksh
-file-setup: file 755 "falsetto"
- #! /bin/sh
- echo molto bene
- exit 42
-file-setup: file 755 "!"
- #! /bin/sh
- echo si
-arguments: !-i!
-stdin:
- export PATH=.:$PATH
- falsetto
- echo yeap
- ! false
-expected-exit: 42
-expected-stdout:
- molto bene
- yeap
- molto bene
-expected-stderr-pattern:
- /.*/
----
-name: event-subst-2
-description:
- Check that '!' substitution in interactive mode
- does not break things
-category: !smksh
-file-setup: file 755 "falsetto"
- #! /bin/sh
- echo molto bene
- exit 42
-file-setup: file 755 "!"
- #! /bin/sh
- echo si
-arguments: !-i!
-env-setup: !ENV=./Env!
-file-setup: file 644 "Env"
- PS1=X
-stdin:
- export PATH=.:$PATH
- falsetto
- echo yeap
- !false
- echo meow
- ! false
- echo = $?
- if
- ! false; then echo foo; else echo bar; fi
-expected-stdout:
- molto bene
- yeap
- molto bene
- meow
- molto bene
- = 42
- foo
-expected-stderr-pattern:
- /.*/
----
-name: event-subst-3
-description:
- Check that '!' substitution in noninteractive mode is ignored
-category: !smksh
-file-setup: file 755 "falsetto"
- #! /bin/sh
- echo molto bene
- exit 42
-file-setup: file 755 "!false"
- #! /bin/sh
- echo si
-stdin:
- export PATH=.:$PATH
- falsetto
- echo yeap
- !false
- echo meow
- ! false
- echo = $?
- if
- ! false; then echo foo; else echo bar; fi
-expected-stdout:
- molto bene
- yeap
- si
- meow
- = 0
- foo
----
-name: nounset-1
-description:
- Check that "set -u" matches (future) SUSv4 requirement
-stdin:
- (set -u
- try() {
- local v
- eval v=\$$1
- if [[ -n $v ]]; then
- echo $1=nz
- else
- echo $1=zf
- fi
- }
- x=y
- (echo $x)
- echo =1
- (echo $y)
- echo =2
- (try x)
- echo =3
- (try y)
- echo =4
- (try 0)
- echo =5
- (try 2)
- echo =6
- (try)
- echo =7
- (echo at=$@)
- echo =8
- (echo asterisk=$*)
- echo =9
- (echo $?)
- echo =10
- (echo $!)
- echo =11
- (echo $-)
- echo =12
- #(echo $_)
- #echo =13
- (echo $#)
- echo =14
- (mypid=$$; try mypid)
- echo =15
- ) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
-expected-stdout:
- y
- =1
- y: parameter not set
- =2
- x=nz
- =3
- y: parameter not set
- =4
- 0=nz
- =5
- 2: parameter not set
- =6
- 1: parameter not set
- =7
- at=
- =8
- asterisk=
- =9
- 0
- =10
- !: parameter not set
- =11
- ush
- =12
- 0
- =14
- mypid=nz
- =15
----
-name: nameref-1
-description:
- Testsuite for nameref (bound variables)
-stdin:
- bar=global
- typeset -n ir2=bar
- typeset -n ind=ir2
- echo !ind: ${!ind}
- echo ind: $ind
- echo !ir2: ${!ir2}
- echo ir2: $ir2
- typeset +n ind
- echo !ind: ${!ind}
- echo ind: $ind
- typeset -n ir2=ind
- echo !ir2: ${!ir2}
- echo ir2: $ir2
- set|grep ^ir2|sed 's/^/s1: /'
- typeset|grep ' ir2'|sed -e 's/^/s2: /' -e 's/nameref/typeset -n/'
- set -A blub -- e1 e2 e3
- typeset -n ind=blub
- typeset -n ir2=blub[2]
- echo !ind[1]: ${!ind[1]}
- echo !ir2: $!ir2
- echo ind[1]: ${ind[1]}
- echo ir2: $ir2
-expected-stdout:
- !ind: bar
- ind: global
- !ir2: bar
- ir2: global
- !ind: ind
- ind: ir2
- !ir2: ind
- ir2: ir2
- s1: ir2=ind
- s2: typeset -n ir2
- !ind[1]: 1
- !ir2: ir2
- ind[1]: e2
- ir2: e3
----
-name: nameref-2da
-description:
- Testsuite for nameref (bound variables)
- Functions, argument given directly, after local
-stdin:
- function foo {
- typeset bar=lokal baz=auch
- typeset -n v=bar
- echo entering
- echo !v: ${!v}
- echo !bar: ${!bar}
- echo !baz: ${!baz}
- echo bar: $bar
- echo v: $v
- v=123
- echo bar: $bar
- echo v: $v
- echo exiting
- }
- bar=global
- echo bar: $bar
- foo bar
- echo bar: $bar
-expected-stdout:
- bar: global
- entering
- !v: bar
- !bar: bar
- !baz: baz
- bar: lokal
- v: lokal
- bar: 123
- v: 123
- exiting
- bar: global
----
-name: nameref-3
-description:
- Advanced testsuite for bound variables (ksh93 fails this)
-stdin:
- typeset -n foo=bar[i]
- set -A bar -- b c a
- for i in 0 1 2 3; do
- print $i $foo .
- done
-expected-stdout:
- 0 b .
- 1 c .
- 2 a .
- 3 .
----
-name: better-parens-1a
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- if ( (echo fubar) | tr u x); then
- echo ja
- else
- echo nein
- fi
-expected-stdout:
- fxbar
- ja
----
-name: better-parens-1b
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- echo $( (echo fubar) | tr u x) $?
-expected-stdout:
- fxbar 0
----
-name: better-parens-2a
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- if ((echo fubar) | tr u x); then
- echo ja
- else
- echo nein
- fi
-expected-stdout:
- fxbar
- ja
----
-name: better-parens-2b
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- echo $((echo fubar) | tr u x) $?
-expected-stdout:
- fxbar 0
----
-name: better-parens-3a
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- if ( (echo fubar) | (tr u x)); then
- echo ja
- else
- echo nein
- fi
-expected-stdout:
- fxbar
- ja
----
-name: better-parens-3b
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- echo $( (echo fubar) | (tr u x)) $?
-expected-stdout:
- fxbar 0
----
-name: better-parens-4a
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- if ((echo fubar) | (tr u x)); then
- echo ja
- else
- echo nein
- fi
-expected-stdout:
- fxbar
- ja
----
-name: better-parens-4b
-description:
- Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
- echo $((echo fubar) | (tr u x)) $?
-expected-stdout:
- fxbar 0
----
-name: echo-test-1
-description:
- Test what the echo builtin does (mksh)
-stdin:
- echo -n 'foo\x40bar'
- echo -e '\tbaz'
-expected-stdout:
- foo@bar baz
----
-name: echo-test-2
-description:
- Test what the echo builtin does (POSIX)
- Note: this follows Debian Policy 10.4 which mandates
- that -n shall be treated as an option, not XSI which
- mandates it shall be treated as string but escapes
- shall be expanded.
-stdin:
- test -n "$POSH_VERSION" || set -o sh
- echo -n 'foo\x40bar'
- echo -e '\tbaz'
-expected-stdout:
- foo\x40bar-e \tbaz
----
-name: utilities-getopts-1
-description:
- getopts sets OPTIND correctly for unparsed option
-stdin:
- set -- -a -a -x
- while getopts :a optc; do
- echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc."
- done
- echo done
-expected-stdout:
- OPTARG=, OPTIND=2, optc=a.
- OPTARG=, OPTIND=3, optc=a.
- OPTARG=x, OPTIND=4, optc=?.
- done
----
-name: utilities-getopts-2
-description:
- Check OPTARG
-stdin:
- set -- -a Mary -x
- while getopts a: optc; do
- echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc."
- done
- echo done
-expected-stdout:
- OPTARG=Mary, OPTIND=3, optc=a.
- OPTARG=, OPTIND=4, optc=?.
- done
-expected-stderr-pattern: /.*-x.*option/
----
-name: wcswidth-1
-description:
- Check the new wcswidth feature
-stdin:
- s=何
- set +U
- print octets: ${#s} .
- print 8-bit width: ${%s} .
- set -U
- print characters: ${#s} .
- print columns: ${%s} .
- s=�
- set +U
- print octets: ${#s} .
- print 8-bit width: ${%s} .
- set -U
- print characters: ${#s} .
- print columns: ${%s} .
-expected-stdout:
- octets: 3 .
- 8-bit width: -1 .
- characters: 1 .
- columns: 2 .
- octets: 3 .
- 8-bit width: 3 .
- characters: 1 .
- columns: 1 .
----
-name: wcswidth-2
-description:
- Check some corner cases
-stdin:
- print % $% .
- set -U
- x='a b'
- print c ${%x} .
- set +U
- x='a b'
- print d ${%x} .
-expected-stdout:
- % $% .
- c -1 .
- d -1 .
----
-name: wcswidth-3
-description:
- Check some corner cases
-stdin:
- print ${%} .
-expected-stderr-pattern:
- /bad substitution/
-expected-exit: 1
----
-name: wcswidth-4a
-description:
- Check some corner cases
-stdin:
- print ${%*} .
-expected-stderr-pattern:
- /bad substitution/
-expected-exit: 1
----
-name: wcswidth-4b
-description:
- Check some corner cases
-stdin:
- print ${%@} .
-expected-stderr-pattern:
- /bad substitution/
-expected-exit: 1
----
-name: wcswidth-4c
-description:
- Check some corner cases
-stdin:
- :
- print ${%?} .
-expected-stdout:
- 1 .
----
-name: realpath-1
-description:
- Check proper return values for realpath
-category: os:mirbsd
-stdin:
- wd=$(realpath .)
- mkdir dir
- :>file
- :>dir/file
- ln -s dir lndir
- ln -s file lnfile
- ln -s nix lnnix
- ln -s . lnself
- i=0
- chk() {
- typeset x y
- x=$(realpath "$wd/$1" 2>&1); y=$?
- print $((++i)) "?$1" =${x##*$wd/} !$y
- }
- chk dir
- chk dir/
- chk dir/file
- chk dir/nix
- chk file
- chk file/
- chk file/file
- chk file/nix
- chk nix
- chk nix/
- chk nix/file
- chk nix/nix
- chk lndir
- chk lndir/
- chk lndir/file
- chk lndir/nix
- chk lnfile
- chk lnfile/
- chk lnfile/file
- chk lnfile/nix
- chk lnnix
- chk lnnix/
- chk lnnix/file
- chk lnnix/nix
- chk lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself
- rm lnself
-expected-stdout:
- 1 ?dir =dir !0
- 2 ?dir/ =dir !0
- 3 ?dir/file =dir/file !0
- 4 ?dir/nix =dir/nix !0
- 5 ?file =file !0
- 6 ?file/ =file/: Not a directory !20
- 7 ?file/file =file/file: Not a directory !20
- 8 ?file/nix =file/nix: Not a directory !20
- 9 ?nix =nix !0
- 10 ?nix/ =nix !0
- 11 ?nix/file =nix/file: No such file or directory !2
- 12 ?nix/nix =nix/nix: No such file or directory !2
- 13 ?lndir =dir !0
- 14 ?lndir/ =dir !0
- 15 ?lndir/file =dir/file !0
- 16 ?lndir/nix =dir/nix !0
- 17 ?lnfile =file !0
- 18 ?lnfile/ =lnfile/: Not a directory !20
- 19 ?lnfile/file =lnfile/file: Not a directory !20
- 20 ?lnfile/nix =lnfile/nix: Not a directory !20
- 21 ?lnnix =nix !0
- 22 ?lnnix/ =nix !0
- 23 ?lnnix/file =lnnix/file: No such file or directory !2
- 24 ?lnnix/nix =lnnix/nix: No such file or directory !2
- 25 ?lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself =lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself: Too many levels of symbolic links !62
----
diff --git a/mksh/src/edit.c b/mksh/src/edit.c
deleted file mode 100644
index 905de7e..0000000
--- a/mksh/src/edit.c
+++ /dev/null
@@ -1,5249 +0,0 @@
-/* $OpenBSD: edit.c,v 1.34 2010/05/20 01:13:07 fgsch Exp $ */
-/* $OpenBSD: edit.h,v 1.8 2005/03/28 21:28:22 deraadt Exp $ */
-/* $OpenBSD: emacs.c,v 1.42 2009/06/02 06:47:47 halex Exp $ */
-/* $OpenBSD: vi.c,v 1.26 2009/06/29 22:50:19 martynas Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.196 2010/07/25 11:35:40 tg Exp $");
-
-/*
- * in later versions we might use libtermcap for this, but since external
- * dependencies are problematic, this has not yet been decided on; another
- * good string is "\033c" except on hardware terminals like the DEC VT420
- * which do a full power cycle then...
- */
-#ifndef MKSH_CLS_STRING
-#define MKSH_CLS_STRING "\033[;H\033[J"
-#endif
-#ifndef MKSH_CLRTOEOL_STRING
-#define MKSH_CLRTOEOL_STRING "\033[K"
-#endif
-
-/* tty driver characters we are interested in */
-typedef struct {
- int erase;
- int kill;
- int werase;
- int intr;
- int quit;
- int eof;
-} X_chars;
-
-static X_chars edchars;
-
-/* x_fc_glob() flags */
-#define XCF_COMMAND BIT(0) /* Do command completion */
-#define XCF_FILE BIT(1) /* Do file completion */
-#define XCF_FULLPATH BIT(2) /* command completion: store full path */
-#define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
-
-static char editmode;
-static int xx_cols; /* for Emacs mode */
-static int modified; /* buffer has been "modified" */
-static char holdbuf[LINE]; /* place to hold last edit buffer */
-
-static int x_getc(void);
-static void x_putcf(int);
-static bool x_mode(bool);
-static int x_do_comment(char *, int, int *);
-static void x_print_expansions(int, char *const *, bool);
-static int x_cf_glob(int, const char *, int, int, int *, int *, char ***,
- bool *);
-static int x_longest_prefix(int, char *const *);
-static int x_basename(const char *, const char *);
-static void x_free_words(int, char **);
-static int x_escape(const char *, size_t, int (*)(const char *, size_t));
-static int x_emacs(char *, size_t);
-static void x_init_emacs(void);
-static void x_init_prompt(void);
-#if !MKSH_S_NOVI
-static int x_vi(char *, size_t);
-#endif
-
-#define x_flush() shf_flush(shl_out)
-#ifdef MKSH_SMALL
-#define x_putc(c) x_putcf(c)
-#else
-#define x_putc(c) shf_putc((c), shl_out)
-#endif
-
-static int path_order_cmp(const void *aa, const void *bb);
-static char *add_glob(const char *, int)
- MKSH_A_NONNULL((nonnull (1)))
- MKSH_A_BOUNDED(string, 1, 2);
-static void glob_table(const char *, XPtrV *, struct table *);
-static void glob_path(int flags, const char *, XPtrV *, const char *);
-static int x_file_glob(int, const char *, int, char ***)
- MKSH_A_NONNULL((nonnull (2)))
- MKSH_A_BOUNDED(string, 2, 3);
-static int x_command_glob(int, const char *, int, char ***)
- MKSH_A_NONNULL((nonnull (2)))
- MKSH_A_BOUNDED(string, 2, 3);
-static int x_locate_word(const char *, int, int, int *, bool *);
-
-static int x_e_getmbc(char *);
-static int x_e_rebuildline(const char *);
-
-/* +++ generic editing functions +++ */
-
-/* Called from main */
-void
-x_init(void)
-{
- /* set to -2 to force initial binding */
- edchars.erase = edchars.kill = edchars.intr = edchars.quit =
- edchars.eof = -2;
- /* default value for deficient systems */
- edchars.werase = 027; /* ^W */
- x_init_emacs();
-}
-
-/*
- * read an edited command line
- */
-int
-x_read(char *buf, size_t len)
-{
- int i;
-
- x_mode(true);
- modified = 1;
- if (Flag(FEMACS) || Flag(FGMACS))
- i = x_emacs(buf, len);
-#if !MKSH_S_NOVI
- else if (Flag(FVI))
- i = x_vi(buf, len);
-#endif
- else
- i = -1; /* internal error */
- editmode = 0;
- x_mode(false);
- return (i);
-}
-
-/* tty I/O */
-
-static int
-x_getc(void)
-{
- char c;
- int n;
-
- while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
- if (trap) {
- x_mode(false);
- runtraps(0);
-#ifdef SIGWINCH
- if (got_winch) {
- change_winsz();
- if (x_cols != xx_cols && editmode == 1) {
- /* redraw line in Emacs mode */
- xx_cols = x_cols;
- x_e_rebuildline(MKSH_CLRTOEOL_STRING);
- }
- }
-#endif
- x_mode(true);
- }
- return ((n == 1) ? (int)(unsigned char)c : -1);
-}
-
-static void
-x_putcf(int c)
-{
- shf_putc(c, shl_out);
-}
-
-/*********************************
- * Misc common code for vi/emacs *
- *********************************/
-
-/* Handle the commenting/uncommenting of a line.
- * Returns:
- * 1 if a carriage return is indicated (comment added)
- * 0 if no return (comment removed)
- * -1 if there is an error (not enough room for comment chars)
- * If successful, *lenp contains the new length. Note: cursor should be
- * moved to the start of the line after (un)commenting.
- */
-static int
-x_do_comment(char *buf, int bsize, int *lenp)
-{
- int i, j, len = *lenp;
-
- if (len == 0)
- return (1); /* somewhat arbitrary - it's what AT&T ksh does */
-
- /* Already commented? */
- if (buf[0] == '#') {
- bool saw_nl = false;
-
- for (j = 0, i = 1; i < len; i++) {
- if (!saw_nl || buf[i] != '#')
- buf[j++] = buf[i];
- saw_nl = buf[i] == '\n';
- }
- *lenp = j;
- return (0);
- } else {
- int n = 1;
-
- /* See if there's room for the #s - 1 per \n */
- for (i = 0; i < len; i++)
- if (buf[i] == '\n')
- n++;
- if (len + n >= bsize)
- return (-1);
- /* Now add them... */
- for (i = len, j = len + n; --i >= 0; ) {
- if (buf[i] == '\n')
- buf[--j] = '#';
- buf[--j] = buf[i];
- }
- buf[0] = '#';
- *lenp += n;
- return (1);
- }
-}
-
-/****************************************************
- * Common file/command completion code for vi/emacs *
- ****************************************************/
-
-static void
-x_print_expansions(int nwords, char * const *words, bool is_command)
-{
- bool use_copy = false;
- int prefix_len;
- XPtrV l = { NULL, NULL, NULL };
-
- /* Check if all matches are in the same directory (in this
- * case, we want to omit the directory name)
- */
- if (!is_command &&
- (prefix_len = x_longest_prefix(nwords, words)) > 0) {
- int i;
-
- /* Special case for 1 match (prefix is whole word) */
- if (nwords == 1)
- prefix_len = x_basename(words[0], NULL);
- /* Any (non-trailing) slashes in non-common word suffixes? */
- for (i = 0; i < nwords; i++)
- if (x_basename(words[i] + prefix_len, NULL) >
- prefix_len)
- break;
- /* All in same directory? */
- if (i == nwords) {
- while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
- prefix_len--;
- use_copy = true;
- XPinit(l, nwords + 1);
- for (i = 0; i < nwords; i++)
- XPput(l, words[i] + prefix_len);
- XPput(l, NULL);
- }
- }
- /*
- * Enumerate expansions
- */
- x_putc('\r');
- x_putc('\n');
- pr_list(use_copy ? (char **)XPptrv(l) : words);
-
- if (use_copy)
- XPfree(l); /* not x_free_words() */
-}
-
-/**
- * Do file globbing:
- * - appends * to (copy of) str if no globbing chars found
- * - does expansion, checks for no match, etc.
- * - sets *wordsp to array of matching strings
- * - returns number of matching strings
- */
-static int
-x_file_glob(int flags MKSH_A_UNUSED, const char *str, int slen, char ***wordsp)
-{
- char *toglob, **words;
- int nwords, i, idx;
- bool escaping;
- XPtrV w;
- struct source *s, *sold;
-
- if (slen < 0)
- return (0);
-
- toglob = add_glob(str, slen);
-
- /* remove all escaping backward slashes */
- escaping = false;
- for (i = 0, idx = 0; toglob[i]; i++) {
- if (toglob[i] == '\\' && !escaping) {
- escaping = true;
- continue;
- }
- /* specially escape escaped [ or $ or ` for globbing */
- if (escaping && (toglob[i] == '[' ||
- toglob[i] == '$' || toglob[i] == '`'))
- toglob[idx++] = QCHAR;
-
- toglob[idx] = toglob[i];
- idx++;
- if (escaping)
- escaping = false;
- }
- toglob[idx] = '\0';
-
- /*
- * Convert "foo*" (toglob) to an array of strings (words)
- */
- sold = source;
- s = pushs(SWSTR, ATEMP);
- s->start = s->str = toglob;
- source = s;
- if (yylex(ONEWORD | LQCHAR) != LWORD) {
- source = sold;
- internal_warningf("fileglob: substitute error");
- return (0);
- }
- source = sold;
- XPinit(w, 32);
- expand(yylval.cp, &w, DOGLOB | DOTILDE | DOMARKDIRS);
- XPput(w, NULL);
- words = (char **)XPclose(w);
-
- for (nwords = 0; words[nwords]; nwords++)
- ;
- if (nwords == 1) {
- struct stat statb;
-
- /* Check if globbing failed (returned glob pattern),
- * but be careful (E.g. toglob == "ab*" when the file
- * "ab*" exists is not an error).
- * Also, check for empty result - happens if we tried
- * to glob something which evaluated to an empty
- * string (e.g., "$FOO" when there is no FOO, etc).
- */
- if ((strcmp(words[0], toglob) == 0 &&
- stat(words[0], &statb) < 0) ||
- words[0][0] == '\0') {
- x_free_words(nwords, words);
- words = NULL;
- nwords = 0;
- }
- }
- afree(toglob, ATEMP);
-
- if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
- x_free_words(nwords, words);
-
- return (nwords);
-}
-
-/* Data structure used in x_command_glob() */
-struct path_order_info {
- char *word;
- int base;
- int path_order;
-};
-
-/* Compare routine used in x_command_glob() */
-static int
-path_order_cmp(const void *aa, const void *bb)
-{
- const struct path_order_info *a = (const struct path_order_info *)aa;
- const struct path_order_info *b = (const struct path_order_info *)bb;
- int t;
-
- t = strcmp(a->word + a->base, b->word + b->base);
- return (t ? t : a->path_order - b->path_order);
-}
-
-static int
-x_command_glob(int flags, const char *str, int slen, char ***wordsp)
-{
- char *toglob, *pat, *fpath;
- int nwords;
- XPtrV w;
- struct block *l;
-
- if (slen < 0)
- return (0);
-
- toglob = add_glob(str, slen);
-
- /* Convert "foo*" (toglob) to a pattern for future use */
- pat = evalstr(toglob, DOPAT | DOTILDE);
- afree(toglob, ATEMP);
-
- XPinit(w, 32);
-
- glob_table(pat, &w, &keywords);
- glob_table(pat, &w, &aliases);
- glob_table(pat, &w, &builtins);
- for (l = e->loc; l; l = l->next)
- glob_table(pat, &w, &l->funs);
-
- glob_path(flags, pat, &w, path);
- if ((fpath = str_val(global("FPATH"))) != null)
- glob_path(flags, pat, &w, fpath);
-
- nwords = XPsize(w);
-
- if (!nwords) {
- *wordsp = NULL;
- XPfree(w);
- return (0);
- }
- /* Sort entries */
- if (flags & XCF_FULLPATH) {
- /* Sort by basename, then path order */
- struct path_order_info *info, *last_info = NULL;
- char **words = (char **)XPptrv(w);
- int i, path_order = 0;
-
- info = (struct path_order_info *)
- alloc(nwords * sizeof(struct path_order_info), ATEMP);
- for (i = 0; i < nwords; i++) {
- info[i].word = words[i];
- info[i].base = x_basename(words[i], NULL);
- if (!last_info || info[i].base != last_info->base ||
- strncmp(words[i], last_info->word, info[i].base) != 0) {
- last_info = &info[i];
- path_order++;
- }
- info[i].path_order = path_order;
- }
- qsort(info, nwords, sizeof(struct path_order_info),
- path_order_cmp);
- for (i = 0; i < nwords; i++)
- words[i] = info[i].word;
- afree(info, ATEMP);
- } else {
- /* Sort and remove duplicate entries */
- char **words = (char **)XPptrv(w);
- int i, j;
-
- qsort(words, nwords, sizeof(void *), xstrcmp);
- for (i = j = 0; i < nwords - 1; i++) {
- if (strcmp(words[i], words[i + 1]))
- words[j++] = words[i];
- else
- afree(words[i], ATEMP);
- }
- words[j++] = words[i];
- nwords = j;
- w.cur = (void **)&words[j];
- }
-
- XPput(w, NULL);
- *wordsp = (char **)XPclose(w);
-
- return (nwords);
-}
-
-#define IS_WORDC(c) (!ctype(c, C_LEX1) && (c) != '\'' && (c) != '"' && \
- (c) != '`' && (c) != '=' && (c) != ':')
-
-static int
-x_locate_word(const char *buf, int buflen, int pos, int *startp,
- bool *is_commandp)
-{
- int start, end;
-
- /* Bad call? Probably should report error */
- if (pos < 0 || pos > buflen) {
- *startp = pos;
- *is_commandp = false;
- return (0);
- }
- /* The case where pos == buflen happens to take care of itself... */
-
- start = pos;
- /* Keep going backwards to start of word (has effect of allowing
- * one blank after the end of a word)
- */
- for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
- (start > 1 && buf[start - 2] == '\\'); start--)
- ;
- /* Go forwards to end of word */
- for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
- if (buf[end] == '\\' && (end + 1) < buflen)
- end++;
- }
-
- if (is_commandp) {
- bool iscmd;
- int p = start - 1;
-
- /* Figure out if this is a command */
- while (p >= 0 && ksh_isspace(buf[p]))
- p--;
- iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
- if (iscmd) {
- /* If command has a /, path, etc. is not searched;
- * only current directory is searched which is just
- * like file globbing.
- */
- for (p = start; p < end; p++)
- if (buf[p] == '/')
- break;
- iscmd = p == end;
- }
- *is_commandp = iscmd;
- }
- *startp = start;
-
- return (end - start);
-}
-
-static int
-x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
- int *endp, char ***wordsp, bool *is_commandp)
-{
- int len, nwords;
- char **words = NULL;
- bool is_command;
-
- len = x_locate_word(buf, buflen, pos, startp, &is_command);
- if (!(flags & XCF_COMMAND))
- is_command = false;
- /* Don't do command globing on zero length strings - it takes too
- * long and isn't very useful. File globs are more likely to be
- * useful, so allow these.
- */
- if (len == 0 && is_command)
- return (0);
-
- nwords = is_command ?
- x_command_glob(flags, buf + *startp, len, &words) :
- x_file_glob(flags, buf + *startp, len, &words);
- if (nwords == 0) {
- *wordsp = NULL;
- return (0);
- }
- if (is_commandp)
- *is_commandp = is_command;
- *wordsp = words;
- *endp = *startp + len;
-
- return (nwords);
-}
-
-/* Given a string, copy it and possibly add a '*' to the end.
- * The new string is returned.
- */
-static char *
-add_glob(const char *str, int slen)
-{
- char *toglob, *s;
- bool saw_slash = false;
-
- if (slen < 0)
- return (NULL);
-
- /* for clang's static analyser, the nonnull attribute isn't enough */
- mkssert(str != NULL);
-
- strndupx(toglob, str, slen + 1, ATEMP); /* + 1 for "*" */
- toglob[slen] = '\0';
-
- /*
- * If the pathname contains a wildcard (an unquoted '*',
- * '?', or '[') or parameter expansion ('$'), or a ~username
- * with no trailing slash, then it is globbed based on that
- * value (i.e., without the appended '*').
- */
- for (s = toglob; *s; s++) {
- if (*s == '\\' && s[1])
- s++;
- else if (*s == '*' || *s == '[' || *s == '?' || *s == '$' ||
- (s[1] == '(' /*)*/ && /* *s in '*','?' already checked */
- (*s == '+' || *s == '@' || *s == '!')))
- break;
- else if (*s == '/')
- saw_slash = true;
- }
- if (!*s && (*toglob != '~' || saw_slash)) {
- toglob[slen] = '*';
- toglob[slen + 1] = '\0';
- }
- return (toglob);
-}
-
-/*
- * Find longest common prefix
- */
-static int
-x_longest_prefix(int nwords, char * const * words)
-{
- int i, j, prefix_len;
- char *p;
-
- if (nwords <= 0)
- return (0);
-
- prefix_len = strlen(words[0]);
- for (i = 1; i < nwords; i++)
- for (j = 0, p = words[i]; j < prefix_len; j++)
- if (p[j] != words[0][j]) {
- prefix_len = j;
- break;
- }
- return (prefix_len);
-}
-
-static void
-x_free_words(int nwords, char **words)
-{
- while (nwords)
- afree(words[--nwords], ATEMP);
- afree(words, ATEMP);
-}
-
-/* Return the offset of the basename of string s (which ends at se - need not
- * be null terminated). Trailing slashes are ignored. If s is just a slash,
- * then the offset is 0 (actually, length - 1).
- * s Return
- * /etc 1
- * /etc/ 1
- * /etc// 1
- * /etc/fo 5
- * foo 0
- * /// 2
- * 0
- */
-static int
-x_basename(const char *s, const char *se)
-{
- const char *p;
-
- if (se == NULL)
- se = s + strlen(s);
- if (s == se)
- return (0);
-
- /* Skip trailing slashes */
- for (p = se - 1; p > s && *p == '/'; p--)
- ;
- for (; p > s && *p != '/'; p--)
- ;
- if (*p == '/' && p + 1 < se)
- p++;
-
- return (p - s);
-}
-
-/*
- * Apply pattern matching to a table: all table entries that match a pattern
- * are added to wp.
- */
-static void
-glob_table(const char *pat, XPtrV *wp, struct table *tp)
-{
- struct tstate ts;
- struct tbl *te;
-
- ktwalk(&ts, tp);
- while ((te = ktnext(&ts)))
- if (gmatchx(te->name, pat, false)) {
- char *cp;
-
- strdupx(cp, te->name, ATEMP);
- XPput(*wp, cp);
- }
-}
-
-static void
-glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
-{
- const char *sp, *p;
- char *xp, **words;
- int staterr, pathlen, patlen, oldsize, newsize, i, j;
- XString xs;
-
- patlen = strlen(pat) + 1;
- sp = lpath;
- Xinit(xs, xp, patlen + 128, ATEMP);
- while (sp) {
- xp = Xstring(xs, xp);
- if (!(p = cstrchr(sp, ':')))
- p = sp + strlen(sp);
- pathlen = p - sp;
- if (pathlen) {
- /* Copy sp into xp, stuffing any MAGIC characters
- * on the way
- */
- const char *s = sp;
-
- XcheckN(xs, xp, pathlen * 2);
- while (s < p) {
- if (ISMAGIC(*s))
- *xp++ = MAGIC;
- *xp++ = *s++;
- }
- *xp++ = '/';
- pathlen++;
- }
- sp = p;
- XcheckN(xs, xp, patlen);
- memcpy(xp, pat, patlen);
-
- oldsize = XPsize(*wp);
- glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
- newsize = XPsize(*wp);
-
- /* Check that each match is executable... */
- words = (char **)XPptrv(*wp);
- for (i = j = oldsize; i < newsize; i++) {
- staterr = 0;
- if ((search_access(words[i], X_OK, &staterr) >= 0) ||
- (staterr == EISDIR)) {
- words[j] = words[i];
- if (!(flags & XCF_FULLPATH))
- memmove(words[j], words[j] + pathlen,
- strlen(words[j] + pathlen) + 1);
- j++;
- } else
- afree(words[i], ATEMP);
- }
- wp->cur = (void **)&words[j];
-
- if (!*sp++)
- break;
- }
- Xfree(xs, xp);
-}
-
-/*
- * if argument string contains any special characters, they will
- * be escaped and the result will be put into edit buffer by
- * keybinding-specific function
- */
-static int
-x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
-{
- size_t add = 0, wlen = len;
- const char *ifs = str_val(local("IFS", 0));
- int rval = 0;
-
- while (wlen - add > 0)
- if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
- vstrchr(ifs, s[add])) {
- if (putbuf_func(s, add) != 0) {
- rval = -1;
- break;
- }
- putbuf_func(s[add] == '\n' ? "'" : "\\", 1);
- putbuf_func(&s[add], 1);
- if (s[add] == '\n')
- putbuf_func("'", 1);
-
- add++;
- wlen -= add;
- s += add;
- add = 0;
- } else
- ++add;
- if (wlen > 0 && rval == 0)
- rval = putbuf_func(s, wlen);
-
- return (rval);
-}
-
-
-/* +++ emacs editing mode +++ */
-
-static Area aedit;
-#define AEDIT &aedit /* area for kill ring and macro defns */
-
-/* values returned by keyboard functions */
-#define KSTD 0
-#define KEOL 1 /* ^M, ^J */
-#define KINTR 2 /* ^G, ^C */
-
-struct x_ftab {
- int (*xf_func)(int c);
- const char *xf_name;
- short xf_flags;
-};
-
-struct x_defbindings {
- unsigned char xdb_func; /* XFUNC_* */
- unsigned char xdb_tab;
- unsigned char xdb_char;
-};
-
-#define XF_ARG 1 /* command takes number prefix */
-#define XF_NOBIND 2 /* not allowed to bind to function */
-#define XF_PREFIX 4 /* function sets prefix */
-
-/* Separator for completion */
-#define is_cfs(c) ((c) == ' ' || (c) == '\t' || (c) == '"' || (c) == '\'')
-/* Separator for motion */
-#define is_mfs(c) (!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80)))
-
-#define X_NTABS 3 /* normal, meta1, meta2 */
-#define X_TABSZ 256 /* size of keydef tables etc */
-
-/* Arguments for do_complete()
- * 0 = enumerate M-= complete as much as possible and then list
- * 1 = complete M-Esc
- * 2 = list M-?
- */
-typedef enum {
- CT_LIST, /* list the possible completions */
- CT_COMPLETE, /* complete to longest prefix */
- CT_COMPLIST /* complete and then list (if non-exact) */
-} Comp_type;
-
-/*
- * The following are used for my horizontal scrolling stuff
- */
-static char *xbuf; /* beg input buffer */
-static char *xend; /* end input buffer */
-static char *xcp; /* current position */
-static char *xep; /* current end */
-static char *xbp; /* start of visible portion of input buffer */
-static char *xlp; /* last char visible on screen */
-static int x_adj_ok;
-/*
- * we use x_adj_done so that functions can tell
- * whether x_adjust() has been called while they are active.
- */
-static int x_adj_done;
-
-static int x_col;
-static int x_displen;
-static int x_arg; /* general purpose arg */
-static int x_arg_defaulted; /* x_arg not explicitly set; defaulted to 1 */
-
-static int xlp_valid;
-
-static char **x_histp; /* history position */
-static int x_nextcmd; /* for newline-and-next */
-static char *xmp; /* mark pointer */
-static unsigned char x_last_command;
-static unsigned char (*x_tab)[X_TABSZ]; /* key definition */
-#ifndef MKSH_SMALL
-static char *(*x_atab)[X_TABSZ]; /* macro definitions */
-#endif
-static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8];
-#define KILLSIZE 20
-static char *killstack[KILLSIZE];
-static int killsp, killtp;
-static int x_curprefix;
-#ifndef MKSH_SMALL
-static char *macroptr = NULL; /* bind key macro active? */
-#endif
-#if !MKSH_S_NOVI
-static int cur_col; /* current column on line */
-static int pwidth; /* width of prompt */
-static int prompt_trunc; /* how much of prompt to truncate */
-static int winwidth; /* width of window */
-static char *wbuf[2]; /* window buffers */
-static int wbuf_len; /* length of window buffers (x_cols - 3) */
-static int win; /* window buffer in use */
-static char morec; /* more character at right of window */
-static int lastref; /* argument to last refresh() */
-static int holdlen; /* length of holdbuf */
-#endif
-static int prompt_redraw; /* 0 if newline forced after prompt */
-
-static int x_ins(const char *);
-static void x_delete(int, int);
-static int x_bword(void);
-static int x_fword(int);
-static void x_goto(char *);
-static void x_bs3(char **);
-static int x_size_str(char *);
-static int x_size2(char *, char **);
-static void x_zots(char *);
-static void x_zotc2(int);
-static void x_zotc3(char **);
-static void x_load_hist(char **);
-static int x_search(char *, int, int);
-#ifndef MKSH_SMALL
-static int x_search_dir(int);
-#endif
-static int x_match(char *, char *);
-static void x_redraw(int);
-static void x_push(int);
-static char *x_mapin(const char *, Area *)
- MKSH_A_NONNULL((nonnull (1)));
-static char *x_mapout(int);
-static void x_mapout2(int, char **);
-static void x_print(int, int);
-static void x_adjust(void);
-static void x_e_ungetc(int);
-static int x_e_getc(void);
-static void x_e_putc2(int);
-static void x_e_putc3(const char **);
-static void x_e_puts(const char *);
-#ifndef MKSH_SMALL
-static int x_fold_case(int);
-#endif
-static char *x_lastcp(void);
-static void do_complete(int, Comp_type);
-
-static int unget_char = -1;
-
-static int x_do_ins(const char *, size_t);
-static void bind_if_not_bound(int, int, int);
-
-enum emacs_funcs {
-#define EMACSFN_ENUMS
-#include "emacsfn.h"
- XFUNC_MAX
-};
-
-#define EMACSFN_DEFNS
-#include "emacsfn.h"
-
-static const struct x_ftab x_ftab[] = {
-#define EMACSFN_ITEMS
-#include "emacsfn.h"
- { 0, NULL, 0 }
-};
-
-static struct x_defbindings const x_defbindings[] = {
- { XFUNC_del_back, 0, CTRL('?') },
- { XFUNC_del_bword, 1, CTRL('?') },
- { XFUNC_eot_del, 0, CTRL('D') },
- { XFUNC_del_back, 0, CTRL('H') },
- { XFUNC_del_bword, 1, CTRL('H') },
- { XFUNC_del_bword, 1, 'h' },
- { XFUNC_mv_bword, 1, 'b' },
- { XFUNC_mv_fword, 1, 'f' },
- { XFUNC_del_fword, 1, 'd' },
- { XFUNC_mv_back, 0, CTRL('B') },
- { XFUNC_mv_forw, 0, CTRL('F') },
- { XFUNC_search_char_forw, 0, CTRL(']') },
- { XFUNC_search_char_back, 1, CTRL(']') },
- { XFUNC_newline, 0, CTRL('M') },
- { XFUNC_newline, 0, CTRL('J') },
- { XFUNC_end_of_text, 0, CTRL('_') },
- { XFUNC_abort, 0, CTRL('G') },
- { XFUNC_prev_com, 0, CTRL('P') },
- { XFUNC_next_com, 0, CTRL('N') },
- { XFUNC_nl_next_com, 0, CTRL('O') },
- { XFUNC_search_hist, 0, CTRL('R') },
- { XFUNC_beg_hist, 1, '<' },
- { XFUNC_end_hist, 1, '>' },
- { XFUNC_goto_hist, 1, 'g' },
- { XFUNC_mv_end, 0, CTRL('E') },
- { XFUNC_mv_begin, 0, CTRL('A') },
- { XFUNC_draw_line, 0, CTRL('L') },
- { XFUNC_cls, 1, CTRL('L') },
- { XFUNC_meta1, 0, CTRL('[') },
- { XFUNC_meta2, 0, CTRL('X') },
- { XFUNC_kill, 0, CTRL('K') },
- { XFUNC_yank, 0, CTRL('Y') },
- { XFUNC_meta_yank, 1, 'y' },
- { XFUNC_literal, 0, CTRL('^') },
- { XFUNC_comment, 1, '#' },
- { XFUNC_transpose, 0, CTRL('T') },
- { XFUNC_complete, 1, CTRL('[') },
- { XFUNC_comp_list, 0, CTRL('I') },
- { XFUNC_comp_list, 1, '=' },
- { XFUNC_enumerate, 1, '?' },
- { XFUNC_expand, 1, '*' },
- { XFUNC_comp_file, 1, CTRL('X') },
- { XFUNC_comp_comm, 2, CTRL('[') },
- { XFUNC_list_comm, 2, '?' },
- { XFUNC_list_file, 2, CTRL('Y') },
- { XFUNC_set_mark, 1, ' ' },
- { XFUNC_kill_region, 0, CTRL('W') },
- { XFUNC_xchg_point_mark, 2, CTRL('X') },
- { XFUNC_literal, 0, CTRL('V') },
- { XFUNC_version, 1, CTRL('V') },
- { XFUNC_prev_histword, 1, '.' },
- { XFUNC_prev_histword, 1, '_' },
- { XFUNC_set_arg, 1, '0' },
- { XFUNC_set_arg, 1, '1' },
- { XFUNC_set_arg, 1, '2' },
- { XFUNC_set_arg, 1, '3' },
- { XFUNC_set_arg, 1, '4' },
- { XFUNC_set_arg, 1, '5' },
- { XFUNC_set_arg, 1, '6' },
- { XFUNC_set_arg, 1, '7' },
- { XFUNC_set_arg, 1, '8' },
- { XFUNC_set_arg, 1, '9' },
-#ifndef MKSH_SMALL
- { XFUNC_fold_upper, 1, 'U' },
- { XFUNC_fold_upper, 1, 'u' },
- { XFUNC_fold_lower, 1, 'L' },
- { XFUNC_fold_lower, 1, 'l' },
- { XFUNC_fold_capitalise, 1, 'C' },
- { XFUNC_fold_capitalise, 1, 'c' },
-#endif
- /* These for ansi arrow keys: arguablely shouldn't be here by
- * default, but its simpler/faster/smaller than using termcap
- * entries.
- */
- { XFUNC_meta2, 1, '[' },
- { XFUNC_meta2, 1, 'O' },
- { XFUNC_prev_com, 2, 'A' },
- { XFUNC_next_com, 2, 'B' },
- { XFUNC_mv_forw, 2, 'C' },
- { XFUNC_mv_back, 2, 'D' },
-#ifndef MKSH_SMALL
- { XFUNC_vt_hack, 2, '1' },
- { XFUNC_mv_begin | 0x80, 2, '7' },
- { XFUNC_mv_begin, 2, 'H' },
- { XFUNC_mv_end | 0x80, 2, '4' },
- { XFUNC_mv_end | 0x80, 2, '8' },
- { XFUNC_mv_end, 2, 'F' },
- { XFUNC_del_char | 0x80, 2, '3' },
- { XFUNC_search_hist_up | 0x80, 2, '5' },
- { XFUNC_search_hist_dn | 0x80, 2, '6' },
- /* more non-standard ones */
- { XFUNC_edit_line, 2, 'e' }
-#endif
-};
-
-#ifdef MKSH_SMALL
-static void x_modified(void);
-static void
-x_modified(void)
-{
- if (!modified) {
- x_histp = histptr + 1;
- modified = 1;
- }
-}
-#define XFUNC_VALUE(f) (f)
-#else
-#define x_modified() do { \
- if (!modified) { \
- x_histp = histptr + 1; \
- modified = 1; \
- } \
-} while (/* CONSTCOND */ 0)
-#define XFUNC_VALUE(f) (f & 0x7F)
-#endif
-
-static int
-x_e_getmbc(char *sbuf)
-{
- int c, pos = 0;
- unsigned char *buf = (unsigned char *)sbuf;
-
- memset(buf, 0, 4);
- buf[pos++] = c = x_e_getc();
- if (c == -1)
- return (-1);
- if (UTFMODE) {
- if ((buf[0] >= 0xC2) && (buf[0] < 0xF0)) {
- c = x_e_getc();
- if (c == -1)
- return (-1);
- if ((c & 0xC0) != 0x80) {
- x_e_ungetc(c);
- return (1);
- }
- buf[pos++] = c;
- }
- if ((buf[0] >= 0xE0) && (buf[0] < 0xF0)) {
- /* XXX x_e_ungetc is one-octet only */
- buf[pos++] = c = x_e_getc();
- if (c == -1)
- return (-1);
- }
- }
- return (pos);
-}
-
-static void
-x_init_prompt(void)
-{
- x_col = promptlen(prompt);
- x_adj_ok = 1;
- prompt_redraw = 1;
- if (x_col >= xx_cols)
- x_col %= xx_cols;
- x_displen = xx_cols - 2 - x_col;
- x_adj_done = 0;
-
- pprompt(prompt, 0);
- if (x_displen < 1) {
- x_col = 0;
- x_displen = xx_cols - 2;
- x_e_putc2('\n');
- prompt_redraw = 0;
- }
-}
-
-static int
-x_emacs(char *buf, size_t len)
-{
- int c, i;
- unsigned char f;
-
- xbp = xbuf = buf; xend = buf + len;
- xlp = xcp = xep = buf;
- *xcp = 0;
- xlp_valid = true;
- xmp = NULL;
- x_curprefix = 0;
- x_histp = histptr + 1;
- x_last_command = XFUNC_error;
-
- xx_cols = x_cols;
- x_init_prompt();
-
- if (x_nextcmd >= 0) {
- int off = source->line - x_nextcmd;
- if (histptr - history >= off)
- x_load_hist(histptr - off);
- x_nextcmd = -1;
- }
- editmode = 1;
- while (1) {
- x_flush();
- if ((c = x_e_getc()) < 0)
- return (0);
-
- f = x_curprefix == -1 ? XFUNC_insert :
- x_tab[x_curprefix][c];
-#ifndef MKSH_SMALL
- if (f & 0x80) {
- f &= 0x7F;
- if ((i = x_e_getc()) != '~')
- x_e_ungetc(i);
- }
-
- /* avoid bind key macro recursion */
- if (macroptr && f == XFUNC_ins_string)
- f = XFUNC_insert;
-#endif
-
- if (!(x_ftab[f].xf_flags & XF_PREFIX) &&
- x_last_command != XFUNC_set_arg) {
- x_arg = 1;
- x_arg_defaulted = 1;
- }
- i = c | (x_curprefix << 8);
- x_curprefix = 0;
- switch ((*x_ftab[f].xf_func)(i)) {
- case KSTD:
- if (!(x_ftab[f].xf_flags & XF_PREFIX))
- x_last_command = f;
- break;
- case KEOL:
- i = xep - xbuf;
- return (i);
- case KINTR: /* special case for interrupt */
- trapsig(SIGINT);
- x_mode(false);
- unwind(LSHELL);
- }
- /* ad-hoc hack for fixing the cursor position */
- x_goto(xcp);
- }
-}
-
-static int
-x_insert(int c)
-{
- static int left = 0, pos, save_arg;
- static char str[4];
-
- /*
- * Should allow tab and control chars.
- */
- if (c == 0) {
- invmbs:
- left = 0;
- x_e_putc2(7);
- return (KSTD);
- }
- if (UTFMODE) {
- if (((c & 0xC0) == 0x80) && left) {
- str[pos++] = c;
- if (!--left) {
- str[pos] = '\0';
- x_arg = save_arg;
- while (x_arg--)
- x_ins(str);
- }
- return (KSTD);
- }
- if (left) {
- if (x_curprefix == -1) {
- /* flush invalid multibyte */
- str[pos] = '\0';
- while (save_arg--)
- x_ins(str);
- }
- }
- if ((c >= 0xC2) && (c < 0xE0))
- left = 1;
- else if ((c >= 0xE0) && (c < 0xF0))
- left = 2;
- else if (c > 0x7F)
- goto invmbs;
- else
- left = 0;
- if (left) {
- save_arg = x_arg;
- pos = 1;
- str[0] = c;
- return (KSTD);
- }
- }
- left = 0;
- str[0] = c;
- str[1] = '\0';
- while (x_arg--)
- x_ins(str);
- return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-static int
-x_ins_string(int c)
-{
- macroptr = x_atab[c >> 8][c & 255];
- /*
- * we no longer need to bother checking if macroptr is
- * not NULL but first char is NUL; x_e_getc() does it
- */
- return (KSTD);
-}
-#endif
-
-static int
-x_do_ins(const char *cp, size_t len)
-{
- if (xep + len >= xend) {
- x_e_putc2(7);
- return (-1);
- }
- memmove(xcp + len, xcp, xep - xcp + 1);
- memmove(xcp, cp, len);
- xcp += len;
- xep += len;
- x_modified();
- return (0);
-}
-
-static int
-x_ins(const char *s)
-{
- char *cp = xcp;
- int adj = x_adj_done;
-
- if (x_do_ins(s, strlen(s)) < 0)
- return (-1);
- /*
- * x_zots() may result in a call to x_adjust()
- * we want xcp to reflect the new position.
- */
- xlp_valid = false;
- x_lastcp();
- x_adj_ok = (xcp >= xlp);
- x_zots(cp);
- if (adj == x_adj_done) { /* has x_adjust() been called? */
- /* no */
- cp = xlp;
- while (cp > xcp)
- x_bs3(&cp);
- }
- if (xlp == xep - 1)
- x_redraw(xx_cols);
- x_adj_ok = 1;
- return (0);
-}
-
-static int
-x_del_back(int c MKSH_A_UNUSED)
-{
- int i = 0;
-
- if (xcp == xbuf) {
- x_e_putc2(7);
- return (KSTD);
- }
- do {
- x_goto(xcp - 1);
- } while ((++i < x_arg) && (xcp != xbuf));
- x_delete(i, false);
- return (KSTD);
-}
-
-static int
-x_del_char(int c MKSH_A_UNUSED)
-{
- char *cp, *cp2;
- int i = 0;
-
- cp = xcp;
- while (i < x_arg) {
- utf_ptradjx(cp, cp2);
- if (cp2 > xep)
- break;
- cp = cp2;
- i++;
- }
-
- if (!i) {
- x_e_putc2(7);
- return (KSTD);
- }
- x_delete(i, false);
- return (KSTD);
-}
-
-/* Delete nc chars to the right of the cursor (including cursor position) */
-static void
-x_delete(int nc, int push)
-{
- int i, nb, nw;
- char *cp;
-
- if (nc == 0)
- return;
-
- nw = 0;
- cp = xcp;
- for (i = 0; i < nc; ++i) {
- char *cp2;
- int j;
-
- j = x_size2(cp, &cp2);
- if (cp2 > xep)
- break;
- cp = cp2;
- nw += j;
- }
- nb = cp - xcp;
- /* nc = i; */
-
- if (xmp != NULL && xmp > xcp) {
- if (xcp + nb > xmp)
- xmp = xcp;
- else
- xmp -= nb;
- }
- /*
- * This lets us yank a word we have deleted.
- */
- if (push)
- x_push(nb);
-
- xep -= nb;
- memmove(xcp, xcp + nb, xep - xcp + 1); /* Copies the NUL */
- x_adj_ok = 0; /* don't redraw */
- xlp_valid = false;
- x_zots(xcp);
- /*
- * if we are already filling the line,
- * there is no need to ' ','\b'.
- * But if we must, make sure we do the minimum.
- */
- if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) {
- nw = i = (nw < i) ? nw : i;
- while (i--)
- x_e_putc2(' ');
- if (x_col == xx_cols - 2) {
- x_e_putc2((xep > xlp) ? '>' : (xbp > xbuf) ? '<' : ' ');
- ++nw;
- }
- while (nw--)
- x_e_putc2('\b');
- }
- /*x_goto(xcp);*/
- x_adj_ok = 1;
- xlp_valid = false;
- cp = x_lastcp();
- while (cp > xcp)
- x_bs3(&cp);
-
- x_modified();
- return;
-}
-
-static int
-x_del_bword(int c MKSH_A_UNUSED)
-{
- x_delete(x_bword(), true);
- return (KSTD);
-}
-
-static int
-x_mv_bword(int c MKSH_A_UNUSED)
-{
- x_bword();
- return (KSTD);
-}
-
-static int
-x_mv_fword(int c MKSH_A_UNUSED)
-{
- x_fword(1);
- return (KSTD);
-}
-
-static int
-x_del_fword(int c MKSH_A_UNUSED)
-{
- x_delete(x_fword(0), true);
- return (KSTD);
-}
-
-static int
-x_bword(void)
-{
- int nc = 0, nb = 0;
- char *cp = xcp;
-
- if (cp == xbuf) {
- x_e_putc2(7);
- return (0);
- }
- while (x_arg--) {
- while (cp != xbuf && is_mfs(cp[-1])) {
- cp--;
- nb++;
- }
- while (cp != xbuf && !is_mfs(cp[-1])) {
- cp--;
- nb++;
- }
- }
- x_goto(cp);
- for (cp = xcp; cp < (xcp + nb); ++nc)
- cp += utf_ptradj(cp);
- return (nc);
-}
-
-static int
-x_fword(int move)
-{
- int nc = 0;
- char *cp = xcp, *cp2;
-
- if (cp == xep) {
- x_e_putc2(7);
- return (0);
- }
- while (x_arg--) {
- while (cp != xep && is_mfs(*cp))
- cp++;
- while (cp != xep && !is_mfs(*cp))
- cp++;
- }
- for (cp2 = xcp; cp2 < cp; ++nc)
- cp2 += utf_ptradj(cp2);
- if (move)
- x_goto(cp);
- return (nc);
-}
-
-static void
-x_goto(char *cp)
-{
- if (UTFMODE)
- while ((cp > xbuf) && ((*cp & 0xC0) == 0x80))
- --cp;
- if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
- /* we are heading off screen */
- xcp = cp;
- x_adjust();
- } else if (cp < xcp) { /* move back */
- while (cp < xcp)
- x_bs3(&xcp);
- } else if (cp > xcp) { /* move forward */
- while (cp > xcp)
- x_zotc3(&xcp);
- }
-}
-
-static void
-x_bs3(char **p)
-{
- int i;
-
- (*p)--;
- if (UTFMODE)
- while (((unsigned char)**p & 0xC0) == 0x80)
- (*p)--;
-
- i = x_size2(*p, NULL);
- while (i--)
- x_e_putc2('\b');
-}
-
-static int
-x_size_str(char *cp)
-{
- int size = 0;
- while (*cp)
- size += x_size2(cp, &cp);
- return (size);
-}
-
-static int
-x_size2(char *cp, char **dcp)
-{
- int c = *(unsigned char *)cp;
-
- if (UTFMODE && (c > 0x7F))
- return (utf_widthadj(cp, (const char **)dcp));
- if (dcp)
- *dcp = cp + 1;
- if (c == '\t')
- return (4); /* Kludge, tabs are always four spaces. */
- if (c < ' ' || c == 0x7f)
- return (2); /* control unsigned char */
- return (1);
-}
-
-static void
-x_zots(char *str)
-{
- int adj = x_adj_done;
-
- x_lastcp();
- while (*str && str < xlp && adj == x_adj_done)
- x_zotc3(&str);
-}
-
-static void
-x_zotc2(int c)
-{
- if (c == '\t') {
- /* Kludge, tabs are always four spaces. */
- x_e_puts(" ");
- } else if (c < ' ' || c == 0x7f) {
- x_e_putc2('^');
- x_e_putc2(UNCTRL(c));
- } else
- x_e_putc2(c);
-}
-
-static void
-x_zotc3(char **cp)
-{
- unsigned char c = **(unsigned char **)cp;
-
- if (c == '\t') {
- /* Kludge, tabs are always four spaces. */
- x_e_puts(" ");
- (*cp)++;
- } else if (c < ' ' || c == 0x7f) {
- x_e_putc2('^');
- x_e_putc2(UNCTRL(c));
- (*cp)++;
- } else
- x_e_putc3((const char **)cp);
-}
-
-static int
-x_mv_back(int c MKSH_A_UNUSED)
-{
- if (xcp == xbuf) {
- x_e_putc2(7);
- return (KSTD);
- }
- while (x_arg--) {
- x_goto(xcp - 1);
- if (xcp == xbuf)
- break;
- }
- return (KSTD);
-}
-
-static int
-x_mv_forw(int c MKSH_A_UNUSED)
-{
- char *cp = xcp, *cp2;
-
- if (xcp == xep) {
- x_e_putc2(7);
- return (KSTD);
- }
- while (x_arg--) {
- utf_ptradjx(cp, cp2);
- if (cp2 > xep)
- break;
- cp = cp2;
- }
- x_goto(cp);
- return (KSTD);
-}
-
-static int
-x_search_char_forw(int c MKSH_A_UNUSED)
-{
- char *cp = xcp;
- char tmp[4];
-
- *xep = '\0';
- if (x_e_getmbc(tmp) < 0) {
- x_e_putc2(7);
- return (KSTD);
- }
- while (x_arg--) {
- if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
- (cp = strstr(xbuf, tmp)) == NULL) {
- x_e_putc2(7);
- return (KSTD);
- }
- }
- x_goto(cp);
- return (KSTD);
-}
-
-static int
-x_search_char_back(int c MKSH_A_UNUSED)
-{
- char *cp = xcp, *p, tmp[4];
- bool b;
-
- if (x_e_getmbc(tmp) < 0) {
- x_e_putc2(7);
- return (KSTD);
- }
- for (; x_arg--; cp = p)
- for (p = cp; ; ) {
- if (p-- == xbuf)
- p = xep;
- if (p == cp) {
- x_e_putc2(7);
- return (KSTD);
- }
- if ((tmp[1] && ((p+1) > xep)) ||
- (tmp[2] && ((p+2) > xep)))
- continue;
- b = true;
- if (*p != tmp[0])
- b = false;
- if (b && tmp[1] && p[1] != tmp[1])
- b = false;
- if (b && tmp[2] && p[2] != tmp[2])
- b = false;
- if (b)
- break;
- }
- x_goto(cp);
- return (KSTD);
-}
-
-static int
-x_newline(int c MKSH_A_UNUSED)
-{
- x_e_putc2('\r');
- x_e_putc2('\n');
- x_flush();
- *xep++ = '\n';
- return (KEOL);
-}
-
-static int
-x_end_of_text(int c MKSH_A_UNUSED)
-{
- x_zotc2(edchars.eof);
- x_putc('\r');
- x_putc('\n');
- x_flush();
- return (KEOL);
-}
-
-static int
-x_beg_hist(int c MKSH_A_UNUSED)
-{
- x_load_hist(history);
- return (KSTD);
-}
-
-static int
-x_end_hist(int c MKSH_A_UNUSED)
-{
- x_load_hist(histptr);
- return (KSTD);
-}
-
-static int
-x_prev_com(int c MKSH_A_UNUSED)
-{
- x_load_hist(x_histp - x_arg);
- return (KSTD);
-}
-
-static int
-x_next_com(int c MKSH_A_UNUSED)
-{
- x_load_hist(x_histp + x_arg);
- return (KSTD);
-}
-
-/* Goto a particular history number obtained from argument.
- * If no argument is given history 1 is probably not what you
- * want so we'll simply go to the oldest one.
- */
-static int
-x_goto_hist(int c MKSH_A_UNUSED)
-{
- if (x_arg_defaulted)
- x_load_hist(history);
- else
- x_load_hist(histptr + x_arg - source->line);
- return (KSTD);
-}
-
-static void
-x_load_hist(char **hp)
-{
- int oldsize;
- char *sp = NULL;
-
- if (hp == histptr + 1) {
- sp = holdbuf;
- modified = 0;
- } else if (hp < history || hp > histptr) {
- x_e_putc2(7);
- return;
- }
- if (sp == NULL)
- sp = *hp;
- x_histp = hp;
- oldsize = x_size_str(xbuf);
- if (modified)
- strlcpy(holdbuf, xbuf, sizeof(holdbuf));
- strlcpy(xbuf, sp, xend - xbuf);
- xbp = xbuf;
- xep = xcp = xbuf + strlen(xbuf);
- xlp_valid = false;
- if (xep <= x_lastcp()) {
- x_redraw(oldsize);
- }
- x_goto(xep);
- modified = 0;
-}
-
-static int
-x_nl_next_com(int c MKSH_A_UNUSED)
-{
- x_nextcmd = source->line - (histptr - x_histp) + 1;
- return (x_newline('\n'));
-}
-
-static int
-x_eot_del(int c)
-{
- if (xep == xbuf && x_arg_defaulted)
- return (x_end_of_text(c));
- else
- return (x_del_char(c));
-}
-
-/* reverse incremental history search */
-static int
-x_search_hist(int c)
-{
- int offset = -1; /* offset of match in xbuf, else -1 */
- char pat[256 + 1]; /* pattern buffer */
- char *p = pat;
- unsigned char f;
-
- *p = '\0';
- while (1) {
- if (offset < 0) {
- x_e_puts("\nI-search: ");
- x_e_puts(pat);
- }
- x_flush();
- if ((c = x_e_getc()) < 0)
- return (KSTD);
- f = x_tab[0][c];
- if (c == CTRL('[')) {
- if ((f & 0x7F) == XFUNC_meta1) {
- if ((c = x_e_getc()) < 0)
- return (KSTD);
- f = x_tab[1][c] & 0x7F;
- if (f == XFUNC_meta1 || f == XFUNC_meta2)
- x_meta1(CTRL('['));
- x_e_ungetc(c);
- }
- break;
- }
-#ifndef MKSH_SMALL
- if (f & 0x80) {
- f &= 0x7F;
- if ((c = x_e_getc()) != '~')
- x_e_ungetc(c);
- }
-#endif
- if (f == XFUNC_search_hist)
- offset = x_search(pat, 0, offset);
- else if (f == XFUNC_del_back) {
- if (p == pat) {
- offset = -1;
- break;
- }
- if (p > pat)
- *--p = '\0';
- if (p == pat)
- offset = -1;
- else
- offset = x_search(pat, 1, offset);
- continue;
- } else if (f == XFUNC_insert) {
- /* add char to pattern */
- /* overflow check... */
- if (p >= &pat[sizeof(pat) - 1]) {
- x_e_putc2(7);
- continue;
- }
- *p++ = c, *p = '\0';
- if (offset >= 0) {
- /* already have partial match */
- offset = x_match(xbuf, pat);
- if (offset >= 0) {
- x_goto(xbuf + offset + (p - pat) -
- (*pat == '^'));
- continue;
- }
- }
- offset = x_search(pat, 0, offset);
- } else if (f == XFUNC_abort) {
- if (offset >= 0)
- x_load_hist(histptr + 1);
- break;
- } else { /* other command */
- x_e_ungetc(c);
- break;
- }
- }
- if (offset < 0)
- x_redraw(-1);
- return (KSTD);
-}
-
-/* search backward from current line */
-static int
-x_search(char *pat, int sameline, int offset)
-{
- char **hp;
- int i;
-
- for (hp = x_histp - (sameline ? 0 : 1); hp >= history; --hp) {
- i = x_match(*hp, pat);
- if (i >= 0) {
- if (offset < 0)
- x_e_putc2('\n');
- x_load_hist(hp);
- x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
- return (i);
- }
- }
- x_e_putc2(7);
- x_histp = histptr;
- return (-1);
-}
-
-#ifndef MKSH_SMALL
-/* anchored search up from current line */
-static int
-x_search_hist_up(int c MKSH_A_UNUSED)
-{
- return (x_search_dir(-1));
-}
-
-/* anchored search down from current line */
-static int
-x_search_hist_dn(int c MKSH_A_UNUSED)
-{
- return (x_search_dir(1));
-}
-
-/* anchored search in the indicated direction */
-static int
-x_search_dir(int search_dir /* should've been bool */)
-{
- char **hp = x_histp + search_dir;
- size_t curs = xcp - xbuf;
-
- while (histptr >= hp && hp >= history) {
- if (strncmp(xbuf, *hp, curs) == 0) {
- x_load_hist(hp);
- x_goto(xbuf + curs);
- break;
- }
- hp += search_dir;
- }
- return (KSTD);
-}
-#endif
-
-/* return position of first match of pattern in string, else -1 */
-static int
-x_match(char *str, char *pat)
-{
- if (*pat == '^') {
- return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1);
- } else {
- char *q = strstr(str, pat);
- return ((q == NULL) ? -1 : q - str);
- }
-}
-
-static int
-x_del_line(int c MKSH_A_UNUSED)
-{
- int i, j;
-
- *xep = 0;
- i = xep - xbuf;
- j = x_size_str(xbuf);
- xcp = xbuf;
- x_push(i);
- xlp = xbp = xep = xbuf;
- xlp_valid = true;
- *xcp = 0;
- xmp = NULL;
- x_redraw(j);
- x_modified();
- return (KSTD);
-}
-
-static int
-x_mv_end(int c MKSH_A_UNUSED)
-{
- x_goto(xep);
- return (KSTD);
-}
-
-static int
-x_mv_begin(int c MKSH_A_UNUSED)
-{
- x_goto(xbuf);
- return (KSTD);
-}
-
-static int
-x_draw_line(int c MKSH_A_UNUSED)
-{
- x_redraw(-1);
- return (KSTD);
-}
-
-static int
-x_e_rebuildline(const char *clrstr)
-{
- shf_puts(clrstr, shl_out);
- x_adjust();
- return (KSTD);
-}
-
-static int
-x_cls(int c MKSH_A_UNUSED)
-{
- return (x_e_rebuildline(MKSH_CLS_STRING));
-}
-
-/* Redraw (part of) the line. If limit is < 0, the everything is redrawn
- * on a NEW line, otherwise limit is the screen column up to which needs
- * redrawing.
- */
-static void
-x_redraw(int limit)
-{
- int i, j, x_trunc = 0;
- char *cp;
-
- x_adj_ok = 0;
- if (limit == -1)
- x_e_putc2('\n');
- else
- x_e_putc2('\r');
- x_flush();
- if (xbp == xbuf) {
- x_col = promptlen(prompt);
- if (x_col >= xx_cols)
- x_trunc = (x_col / xx_cols) * xx_cols;
- if (prompt_redraw)
- pprompt(prompt, x_trunc);
- }
- if (x_col >= xx_cols)
- x_col %= xx_cols;
- x_displen = xx_cols - 2 - x_col;
- if (x_displen < 1) {
- x_col = 0;
- x_displen = xx_cols - 2;
- }
- xlp_valid = false;
- x_lastcp();
- x_zots(xbp);
- if (xbp != xbuf || xep > xlp)
- limit = xx_cols;
- if (limit >= 0) {
- if (xep > xlp)
- i = 0; /* we fill the line */
- else {
- char *cpl = xbp;
-
- i = limit;
- while (cpl < xlp)
- i -= x_size2(cpl, &cpl);
- }
-
- j = 0;
- while ((j < i) || (x_col < (xx_cols - 2))) {
- if (!(x_col < (xx_cols - 2)))
- break;
- x_e_putc2(' ');
- j++;
- }
- i = ' ';
- if (xep > xlp) { /* more off screen */
- if (xbp > xbuf)
- i = '*';
- else
- i = '>';
- } else if (xbp > xbuf)
- i = '<';
- x_e_putc2(i);
- j++;
- while (j--)
- x_e_putc2('\b');
- }
- cp = xlp;
- while (cp > xcp)
- x_bs3(&cp);
- x_adj_ok = 1;
- return;
-}
-
-static int
-x_transpose(int c MKSH_A_UNUSED)
-{
- unsigned int tmpa, tmpb;
-
- /* What transpose is meant to do seems to be up for debate. This
- * is a general summary of the options; the text is abcd with the
- * upper case character or underscore indicating the cursor position:
- * Who Before After Before After
- * AT&T ksh in emacs mode: abCd abdC abcd_ (bell)
- * AT&T ksh in gmacs mode: abCd baCd abcd_ abdc_
- * gnu emacs: abCd acbD abcd_ abdc_
- * Pdksh currently goes with GNU behavior since I believe this is the
- * most common version of emacs, unless in gmacs mode, in which case
- * it does the AT&T ksh gmacs mode.
- * This should really be broken up into 3 functions so users can bind
- * to the one they want.
- */
- if (xcp == xbuf) {
- x_e_putc2(7);
- return (KSTD);
- } else if (xcp == xep || Flag(FGMACS)) {
- if (xcp - xbuf == 1) {
- x_e_putc2(7);
- return (KSTD);
- }
- /* Gosling/Unipress emacs style: Swap two characters before the
- * cursor, do not change cursor position
- */
- x_bs3(&xcp);
- if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
- x_e_putc2(7);
- return (KSTD);
- }
- x_bs3(&xcp);
- if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
- x_e_putc2(7);
- return (KSTD);
- }
- utf_wctomb(xcp, tmpa);
- x_zotc3(&xcp);
- utf_wctomb(xcp, tmpb);
- x_zotc3(&xcp);
- } else {
- /* GNU emacs style: Swap the characters before and under the
- * cursor, move cursor position along one.
- */
- if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
- x_e_putc2(7);
- return (KSTD);
- }
- x_bs3(&xcp);
- if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
- x_e_putc2(7);
- return (KSTD);
- }
- utf_wctomb(xcp, tmpa);
- x_zotc3(&xcp);
- utf_wctomb(xcp, tmpb);
- x_zotc3(&xcp);
- }
- x_modified();
- return (KSTD);
-}
-
-static int
-x_literal(int c MKSH_A_UNUSED)
-{
- x_curprefix = -1;
- return (KSTD);
-}
-
-static int
-x_meta1(int c MKSH_A_UNUSED)
-{
- x_curprefix = 1;
- return (KSTD);
-}
-
-static int
-x_meta2(int c MKSH_A_UNUSED)
-{
- x_curprefix = 2;
- return (KSTD);
-}
-
-static int
-x_kill(int c MKSH_A_UNUSED)
-{
- int col = xcp - xbuf;
- int lastcol = xep - xbuf;
- int ndel;
-
- if (x_arg_defaulted)
- x_arg = lastcol;
- else if (x_arg > lastcol)
- x_arg = lastcol;
- ndel = x_arg - col;
- if (ndel < 0) {
- x_goto(xbuf + x_arg);
- ndel = -ndel;
- }
- x_delete(ndel, true);
- return (KSTD);
-}
-
-static void
-x_push(int nchars)
-{
- char *cp;
-
- strndupx(cp, xcp, nchars, AEDIT);
- if (killstack[killsp])
- afree(killstack[killsp], AEDIT);
- killstack[killsp] = cp;
- killsp = (killsp + 1) % KILLSIZE;
-}
-
-static int
-x_yank(int c MKSH_A_UNUSED)
-{
- if (killsp == 0)
- killtp = KILLSIZE;
- else
- killtp = killsp;
- killtp--;
- if (killstack[killtp] == 0) {
- x_e_puts("\nnothing to yank");
- x_redraw(-1);
- return (KSTD);
- }
- xmp = xcp;
- x_ins(killstack[killtp]);
- return (KSTD);
-}
-
-static int
-x_meta_yank(int c MKSH_A_UNUSED)
-{
- int len;
-
- if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) ||
- killstack[killtp] == 0) {
- killtp = killsp;
- x_e_puts("\nyank something first");
- x_redraw(-1);
- return (KSTD);
- }
- len = strlen(killstack[killtp]);
- x_goto(xcp - len);
- x_delete(len, false);
- do {
- if (killtp == 0)
- killtp = KILLSIZE - 1;
- else
- killtp--;
- } while (killstack[killtp] == 0);
- x_ins(killstack[killtp]);
- return (KSTD);
-}
-
-static int
-x_abort(int c MKSH_A_UNUSED)
-{
- /* x_zotc(c); */
- xlp = xep = xcp = xbp = xbuf;
- xlp_valid = true;
- *xcp = 0;
- x_modified();
- return (KINTR);
-}
-
-static int
-x_error(int c MKSH_A_UNUSED)
-{
- x_e_putc2(7);
- return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-/* special VT100 style key sequence hack */
-static int
-x_vt_hack(int c)
-{
- /* we only support PF2-'1' for now */
- if (c != (2 << 8 | '1'))
- return (x_error(c));
-
- /* what's the next character? */
- switch ((c = x_e_getc())) {
- case '~':
- x_arg = 1;
- x_arg_defaulted = 1;
- return (x_mv_begin(0));
- case ';':
- /* "interesting" sequence detected */
- break;
- default:
- goto unwind_err;
- }
-
- /* XXX x_e_ungetc is one-octet only */
- if ((c = x_e_getc()) != '5' && c != '3')
- goto unwind_err;
-
- /*-
- * At this point, we have read the following octets so far:
- * - ESC+[ or ESC+O or Ctrl-X (Præfix 2)
- * - 1 (vt_hack)
- * - ;
- * - 5 (Ctrl key combiner) or 3 (Alt key combiner)
- * We can now accept one more octet designating the key.
- */
-
- switch ((c = x_e_getc())) {
- case 'C':
- return (x_mv_fword(c));
- case 'D':
- return (x_mv_bword(c));
- }
-
- unwind_err:
- x_e_ungetc(c);
- return (x_error(c));
-}
-#endif
-
-static char *
-x_mapin(const char *cp, Area *ap)
-{
- char *news, *op;
-
- /* for clang's static analyser, the nonnull attribute isn't enough */
- mkssert(cp != NULL);
-
- strdupx(news, cp, ap);
- op = news;
- while (*cp) {
- /* XXX -- should handle \^ escape? */
- if (*cp == '^') {
- cp++;
- if (*cp >= '?') /* includes '?'; ASCII */
- *op++ = CTRL(*cp);
- else {
- *op++ = '^';
- cp--;
- }
- } else
- *op++ = *cp;
- cp++;
- }
- *op = '\0';
-
- return (news);
-}
-
-static void
-x_mapout2(int c, char **buf)
-{
- char *p = *buf;
-
- if (c < ' ' || c == 0x7f) {
- *p++ = '^';
- *p++ = UNCTRL(c);
- } else
- *p++ = c;
- *p = 0;
- *buf = p;
-}
-
-static char *
-x_mapout(int c)
-{
- static char buf[8];
- char *bp = buf;
-
- x_mapout2(c, &bp);
- return (buf);
-}
-
-static void
-x_print(int prefix, int key)
-{
- int f = x_tab[prefix][key];
-
- if (prefix)
- /* prefix == 1 || prefix == 2 */
- shf_puts(x_mapout(prefix == 1 ?
- CTRL('[') : CTRL('X')), shl_stdout);
-#ifdef MKSH_SMALL
- shprintf("%s = ", x_mapout(key));
-#else
- shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
- if (XFUNC_VALUE(f) != XFUNC_ins_string)
-#endif
- shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
-#ifndef MKSH_SMALL
- else
- shprintf("'%s'\n", x_atab[prefix][key]);
-#endif
-}
-
-int
-x_bind(const char *a1, const char *a2,
-#ifndef MKSH_SMALL
- bool macro, /* bind -m */
-#endif
- bool list) /* bind -l */
-{
- unsigned char f;
- int prefix, key;
- char *m1, *m2;
-#ifndef MKSH_SMALL
- char *sp = NULL;
- bool hastilde;
-#endif
-
- if (x_tab == NULL) {
- bi_errorf("cannot bind, not a tty");
- return (1);
- }
- /* List function names */
- if (list) {
- for (f = 0; f < NELEM(x_ftab); f++)
- if (x_ftab[f].xf_name &&
- !(x_ftab[f].xf_flags & XF_NOBIND))
- shprintf("%s\n", x_ftab[f].xf_name);
- return (0);
- }
- if (a1 == NULL) {
- for (prefix = 0; prefix < X_NTABS; prefix++)
- for (key = 0; key < X_TABSZ; key++) {
- f = XFUNC_VALUE(x_tab[prefix][key]);
- if (f == XFUNC_insert || f == XFUNC_error
-#ifndef MKSH_SMALL
- || (macro && f != XFUNC_ins_string)
-#endif
- )
- continue;
- x_print(prefix, key);
- }
- return (0);
- }
- m2 = m1 = x_mapin(a1, ATEMP);
- prefix = 0;
- for (;; m1++) {
- key = (unsigned char)*m1;
- f = XFUNC_VALUE(x_tab[prefix][key]);
- if (f == XFUNC_meta1)
- prefix = 1;
- else if (f == XFUNC_meta2)
- prefix = 2;
- else
- break;
- }
- if (*++m1
-#ifndef MKSH_SMALL
- && ((*m1 != '~') || *(m1 + 1))
-#endif
- ) {
- char msg[256] = "key sequence '";
- const char *c = a1;
- m1 = msg + strlen(msg);
- while (*c && m1 < (msg + sizeof(msg) - 3))
- x_mapout2(*c++, &m1);
- bi_errorf("%s' too long", msg);
- return (1);
- }
-#ifndef MKSH_SMALL
- hastilde = *m1;
-#endif
- afree(m2, ATEMP);
-
- if (a2 == NULL) {
- x_print(prefix, key);
- return (0);
- }
- if (*a2 == 0) {
- f = XFUNC_insert;
-#ifndef MKSH_SMALL
- } else if (macro) {
- f = XFUNC_ins_string;
- sp = x_mapin(a2, AEDIT);
-#endif
- } else {
- for (f = 0; f < NELEM(x_ftab); f++)
- if (x_ftab[f].xf_name &&
- strcmp(x_ftab[f].xf_name, a2) == 0)
- break;
- if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
- bi_errorf("%s: no such function", a2);
- return (1);
- }
- }
-
-#ifndef MKSH_SMALL
- if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
- x_atab[prefix][key])
- afree(x_atab[prefix][key], AEDIT);
-#endif
- x_tab[prefix][key] = f
-#ifndef MKSH_SMALL
- | (hastilde ? 0x80 : 0)
-#endif
- ;
-#ifndef MKSH_SMALL
- x_atab[prefix][key] = sp;
-#endif
-
- /* Track what the user has bound so x_mode(true) won't toast things */
- if (f == XFUNC_insert)
- x_bound[(prefix * X_TABSZ + key) / 8] &=
- ~(1 << ((prefix * X_TABSZ + key) % 8));
- else
- x_bound[(prefix * X_TABSZ + key) / 8] |=
- (1 << ((prefix * X_TABSZ + key) % 8));
-
- return (0);
-}
-
-static void
-x_init_emacs(void)
-{
- int i, j;
-
- ainit(AEDIT);
- x_nextcmd = -1;
-
- x_tab = alloc(X_NTABS * sizeof(*x_tab), AEDIT);
- for (j = 0; j < X_TABSZ; j++)
- x_tab[0][j] = XFUNC_insert;
- for (i = 1; i < X_NTABS; i++)
- for (j = 0; j < X_TABSZ; j++)
- x_tab[i][j] = XFUNC_error;
- for (i = 0; i < (int)NELEM(x_defbindings); i++)
- x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
- = x_defbindings[i].xdb_func;
-
-#ifndef MKSH_SMALL
- x_atab = alloc(X_NTABS * sizeof(*x_atab), AEDIT);
- for (i = 1; i < X_NTABS; i++)
- for (j = 0; j < X_TABSZ; j++)
- x_atab[i][j] = NULL;
-#endif
-}
-
-static void
-bind_if_not_bound(int p, int k, int func)
-{
- /* Has user already bound this key? If so, don't override it */
- if (x_bound[((p) * X_TABSZ + (k)) / 8] &
- (1 << (((p) * X_TABSZ + (k)) % 8)))
- return;
-
- x_tab[p][k] = func;
-}
-
-static int
-x_set_mark(int c MKSH_A_UNUSED)
-{
- xmp = xcp;
- return (KSTD);
-}
-
-static int
-x_kill_region(int c MKSH_A_UNUSED)
-{
- int rsize;
- char *xr;
-
- if (xmp == NULL) {
- x_e_putc2(7);
- return (KSTD);
- }
- if (xmp > xcp) {
- rsize = xmp - xcp;
- xr = xcp;
- } else {
- rsize = xcp - xmp;
- xr = xmp;
- }
- x_goto(xr);
- x_delete(rsize, true);
- xmp = xr;
- return (KSTD);
-}
-
-static int
-x_xchg_point_mark(int c MKSH_A_UNUSED)
-{
- char *tmp;
-
- if (xmp == NULL) {
- x_e_putc2(7);
- return (KSTD);
- }
- tmp = xmp;
- xmp = xcp;
- x_goto(tmp);
- return (KSTD);
-}
-
-static int
-x_noop(int c MKSH_A_UNUSED)
-{
- return (KSTD);
-}
-
-/*
- * File/command name completion routines
- */
-static int
-x_comp_comm(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_COMMAND, CT_COMPLETE);
- return (KSTD);
-}
-
-static int
-x_list_comm(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_COMMAND, CT_LIST);
- return (KSTD);
-}
-
-static int
-x_complete(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
- return (KSTD);
-}
-
-static int
-x_enumerate(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_COMMAND_FILE, CT_LIST);
- return (KSTD);
-}
-
-static int
-x_comp_file(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_FILE, CT_COMPLETE);
- return (KSTD);
-}
-
-static int
-x_list_file(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_FILE, CT_LIST);
- return (KSTD);
-}
-
-static int
-x_comp_list(int c MKSH_A_UNUSED)
-{
- do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
- return (KSTD);
-}
-
-static int
-x_expand(int c MKSH_A_UNUSED)
-{
- char **words;
- int start, end, nwords, i;
- bool is_command;
-
- nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf,
- &start, &end, &words, &is_command);
-
- if (nwords == 0) {
- x_e_putc2(7);
- return (KSTD);
- }
- x_goto(xbuf + start);
- x_delete(end - start, false);
- for (i = 0; i < nwords;) {
- if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
- (++i < nwords && x_ins(" ") < 0)) {
- x_e_putc2(7);
- return (KSTD);
- }
- }
- x_adjust();
-
- return (KSTD);
-}
-
-/* type == 0 for list, 1 for complete and 2 for complete-list */
-static void
-do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */
- Comp_type type)
-{
- char **words;
- int start, end, nlen, olen, nwords;
- bool is_command, completed = false;
-
- nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
- &start, &end, &words, &is_command);
- /* no match */
- if (nwords == 0) {
- x_e_putc2(7);
- return;
- }
- if (type == CT_LIST) {
- x_print_expansions(nwords, words, is_command);
- x_redraw(0);
- x_free_words(nwords, words);
- return;
- }
- olen = end - start;
- nlen = x_longest_prefix(nwords, words);
- /* complete */
- if (nwords == 1 || nlen > olen) {
- x_goto(xbuf + start);
- x_delete(olen, false);
- x_escape(words[0], nlen, x_do_ins);
- x_adjust();
- completed = true;
- }
- /* add space if single non-dir match */
- if (nwords == 1 && words[0][nlen - 1] != '/') {
- x_ins(" ");
- completed = true;
- }
- if (type == CT_COMPLIST && !completed) {
- x_print_expansions(nwords, words, is_command);
- completed = true;
- }
- if (completed)
- x_redraw(0);
-
- x_free_words(nwords, words);
-}
-
-/* NAME:
- * x_adjust - redraw the line adjusting starting point etc.
- *
- * DESCRIPTION:
- * This function is called when we have exceeded the bounds
- * of the edit window. It increments x_adj_done so that
- * functions like x_ins and x_delete know that we have been
- * called and can skip the x_bs() stuff which has already
- * been done by x_redraw.
- *
- * RETURN VALUE:
- * None
- */
-static void
-x_adjust(void)
-{
- x_adj_done++; /* flag the fact that we were called. */
- /*
- * we had a problem if the prompt length > xx_cols / 2
- */
- if ((xbp = xcp - (x_displen / 2)) < xbuf)
- xbp = xbuf;
- if (UTFMODE)
- while ((xbp > xbuf) && ((*xbp & 0xC0) == 0x80))
- --xbp;
- xlp_valid = false;
- x_redraw(xx_cols);
- x_flush();
-}
-
-static void
-x_e_ungetc(int c)
-{
- unget_char = c < 0 ? -1 : (c & 255);
-}
-
-static int
-x_e_getc(void)
-{
- int c;
-
- if (unget_char >= 0) {
- c = unget_char;
- unget_char = -1;
- return (c);
- }
-
-#ifndef MKSH_SMALL
- if (macroptr) {
- if ((c = (unsigned char)*macroptr++))
- return (c);
- macroptr = NULL;
- }
-#endif
-
- return (x_getc());
-}
-
-static void
-x_e_putc2(int c)
-{
- int width = 1;
-
- if (c == '\r' || c == '\n')
- x_col = 0;
- if (x_col < xx_cols) {
- if (UTFMODE && (c > 0x7F)) {
- char utf_tmp[3];
- size_t x;
-
- if (c < 0xA0)
- c = 0xFFFD;
- x = utf_wctomb(utf_tmp, c);
- x_putc(utf_tmp[0]);
- if (x > 1)
- x_putc(utf_tmp[1]);
- if (x > 2)
- x_putc(utf_tmp[2]);
- width = utf_wcwidth(c);
- } else
- x_putc(c);
- switch (c) {
- case 7:
- break;
- case '\r':
- case '\n':
- break;
- case '\b':
- x_col--;
- break;
- default:
- x_col += width;
- break;
- }
- }
- if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
- x_adjust();
-}
-
-static void
-x_e_putc3(const char **cp)
-{
- int width = 1, c = **(const unsigned char **)cp;
-
- if (c == '\r' || c == '\n')
- x_col = 0;
- if (x_col < xx_cols) {
- if (UTFMODE && (c > 0x7F)) {
- char *cp2;
-
- width = utf_widthadj(*cp, (const char **)&cp2);
- while (*cp < cp2)
- x_putcf(*(*cp)++);
- } else {
- (*cp)++;
- x_putc(c);
- }
- switch (c) {
- case 7:
- break;
- case '\r':
- case '\n':
- break;
- case '\b':
- x_col--;
- break;
- default:
- x_col += width;
- break;
- }
- }
- if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
- x_adjust();
-}
-
-static void
-x_e_puts(const char *s)
-{
- int adj = x_adj_done;
-
- while (*s && adj == x_adj_done)
- x_e_putc3(&s);
-}
-
-/* NAME:
- * x_set_arg - set an arg value for next function
- *
- * DESCRIPTION:
- * This is a simple implementation of M-[0-9].
- *
- * RETURN VALUE:
- * KSTD
- */
-static int
-x_set_arg(int c)
-{
- int n = 0, first = 1;
-
- c &= 255; /* strip command prefix */
- for (; c >= 0 && ksh_isdigit(c); c = x_e_getc(), first = 0)
- n = n * 10 + (c - '0');
- if (c < 0 || first) {
- x_e_putc2(7);
- x_arg = 1;
- x_arg_defaulted = 1;
- } else {
- x_e_ungetc(c);
- x_arg = n;
- x_arg_defaulted = 0;
- }
- return (KSTD);
-}
-
-/* Comment or uncomment the current line. */
-static int
-x_comment(int c MKSH_A_UNUSED)
-{
- int oldsize = x_size_str(xbuf);
- int len = xep - xbuf;
- int ret = x_do_comment(xbuf, xend - xbuf, &len);
-
- if (ret < 0)
- x_e_putc2(7);
- else {
- x_modified();
- xep = xbuf + len;
- *xep = '\0';
- xcp = xbp = xbuf;
- x_redraw(oldsize);
- if (ret > 0)
- return (x_newline('\n'));
- }
- return (KSTD);
-}
-
-static int
-x_version(int c MKSH_A_UNUSED)
-{
- char *o_xbuf = xbuf, *o_xend = xend;
- char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
- int vlen, lim = x_lastcp() - xbp;
- char *v;
-
- strdupx(v, KSH_VERSION, ATEMP);
-
- xbuf = xbp = xcp = v;
- xend = xep = v + (vlen = strlen(v));
- x_redraw(lim);
- x_flush();
-
- c = x_e_getc();
- xbuf = o_xbuf;
- xend = o_xend;
- xbp = o_xbp;
- xep = o_xep;
- xcp = o_xcp;
- x_redraw(vlen);
-
- if (c < 0)
- return (KSTD);
- /* This is what AT&T ksh seems to do... Very bizarre */
- if (c != ' ')
- x_e_ungetc(c);
-
- afree(v, ATEMP);
- return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-static int
-x_edit_line(int c MKSH_A_UNUSED)
-{
- if (x_arg_defaulted) {
- if (xep == xbuf) {
- x_e_putc2(7);
- return (KSTD);
- }
- if (modified) {
- *xep = '\0';
- histsave(&source->line, xbuf, true, true);
- x_arg = 0;
- } else
- x_arg = source->line - (histptr - x_histp);
- }
- if (x_arg)
- shf_snprintf(xbuf, xend - xbuf, "%s %d",
- "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
- else
- strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
- xep = xbuf + strlen(xbuf);
- return (x_newline('\n'));
-}
-#endif
-
-/* NAME:
- * x_prev_histword - recover word from prev command
- *
- * DESCRIPTION:
- * This function recovers the last word from the previous
- * command and inserts it into the current edit line. If a
- * numeric arg is supplied then the n'th word from the
- * start of the previous command is used.
- * As a side effect, trashes the mark in order to achieve
- * being called in a repeatable fashion.
- *
- * Bound to M-.
- *
- * RETURN VALUE:
- * KSTD
- */
-static int
-x_prev_histword(int c MKSH_A_UNUSED)
-{
- char *rcp, *cp;
- char **xhp;
- int m;
-
- if (xmp && modified > 1)
- x_kill_region(0);
- m = modified ? modified : 1;
- xhp = histptr - (m - 1);
- if ((xhp < history) || !(cp = *xhp)) {
- x_e_putc2(7);
- x_modified();
- return (KSTD);
- }
- x_set_mark(0);
- if (x_arg_defaulted) {
- rcp = &cp[strlen(cp) - 1];
- /*
- * ignore white-space after the last word
- */
- while (rcp > cp && is_cfs(*rcp))
- rcp--;
- while (rcp > cp && !is_cfs(*rcp))
- rcp--;
- if (is_cfs(*rcp))
- rcp++;
- x_ins(rcp);
- } else {
- char ch;
-
- rcp = cp;
- /*
- * ignore white-space at start of line
- */
- while (*rcp && is_cfs(*rcp))
- rcp++;
- while (x_arg-- > 1) {
- while (*rcp && !is_cfs(*rcp))
- rcp++;
- while (*rcp && is_cfs(*rcp))
- rcp++;
- }
- cp = rcp;
- while (*rcp && !is_cfs(*rcp))
- rcp++;
- ch = *rcp;
- *rcp = '\0';
- x_ins(cp);
- *rcp = ch;
- }
- modified = m + 1;
- return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-/* Uppercase N(1) words */
-static int
-x_fold_upper(int c MKSH_A_UNUSED)
-{
- return (x_fold_case('U'));
-}
-
-/* Lowercase N(1) words */
-static int
-x_fold_lower(int c MKSH_A_UNUSED)
-{
- return (x_fold_case('L'));
-}
-
-/* Lowercase N(1) words */
-static int
-x_fold_capitalise(int c MKSH_A_UNUSED)
-{
- return (x_fold_case('C'));
-}
-
-/* NAME:
- * x_fold_case - convert word to UPPER/lower/Capital case
- *
- * DESCRIPTION:
- * This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c
- * to UPPER case, lower case or Capitalise words.
- *
- * RETURN VALUE:
- * None
- */
-static int
-x_fold_case(int c)
-{
- char *cp = xcp;
-
- if (cp == xep) {
- x_e_putc2(7);
- return (KSTD);
- }
- while (x_arg--) {
- /*
- * first skip over any white-space
- */
- while (cp != xep && is_mfs(*cp))
- cp++;
- /*
- * do the first char on its own since it may be
- * a different action than for the rest.
- */
- if (cp != xep) {
- if (c == 'L') /* lowercase */
- *cp = ksh_tolower(*cp);
- else /* uppercase, capitalise */
- *cp = ksh_toupper(*cp);
- cp++;
- }
- /*
- * now for the rest of the word
- */
- while (cp != xep && !is_mfs(*cp)) {
- if (c == 'U') /* uppercase */
- *cp = ksh_toupper(*cp);
- else /* lowercase, capitalise */
- *cp = ksh_tolower(*cp);
- cp++;
- }
- }
- x_goto(cp);
- x_modified();
- return (KSTD);
-}
-#endif
-
-/* NAME:
- * x_lastcp - last visible char
- *
- * SYNOPSIS:
- * x_lastcp()
- *
- * DESCRIPTION:
- * This function returns a pointer to that char in the
- * edit buffer that will be the last displayed on the
- * screen. The sequence:
- *
- * cp = x_lastcp();
- * while (cp > xcp)
- * x_bs3(&cp);
- *
- * Will position the cursor correctly on the screen.
- *
- * RETURN VALUE:
- * cp or NULL
- */
-static char *
-x_lastcp(void)
-{
- if (!xlp_valid) {
- int i = 0, j;
- char *xlp2;
-
- xlp = xbp;
- while (xlp < xep) {
- j = x_size2(xlp, &xlp2);
- if ((i + j) > x_displen)
- break;
- i += j;
- xlp = xlp2;
- }
- }
- xlp_valid = true;
- return (xlp);
-}
-
-static bool
-x_mode(bool onoff)
-{
- static bool x_cur_mode;
- bool prev;
-
- if (x_cur_mode == onoff)
- return (x_cur_mode);
- prev = x_cur_mode;
- x_cur_mode = onoff;
-
- if (onoff) {
- struct termios cb;
-
- cb = tty_state;
-
- edchars.erase = cb.c_cc[VERASE];
- edchars.kill = cb.c_cc[VKILL];
- edchars.intr = cb.c_cc[VINTR];
- edchars.quit = cb.c_cc[VQUIT];
- edchars.eof = cb.c_cc[VEOF];
-#ifdef VWERASE
- edchars.werase = cb.c_cc[VWERASE];
-#endif
- cb.c_iflag &= ~(INLCR | ICRNL);
- cb.c_lflag &= ~(ISIG | ICANON | ECHO);
-#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
- /* osf/1 processes lnext when ~icanon */
- cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
-#endif
- /* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
-#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
- cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
-#endif
- cb.c_cc[VTIME] = 0;
- cb.c_cc[VMIN] = 1;
-
- tcsetattr(tty_fd, TCSADRAIN, &cb);
-
-#ifdef _POSIX_VDISABLE
- /* Convert unset values to internal 'unset' value */
- if (edchars.erase == _POSIX_VDISABLE)
- edchars.erase = -1;
- if (edchars.kill == _POSIX_VDISABLE)
- edchars.kill = -1;
- if (edchars.intr == _POSIX_VDISABLE)
- edchars.intr = -1;
- if (edchars.quit == _POSIX_VDISABLE)
- edchars.quit = -1;
- if (edchars.eof == _POSIX_VDISABLE)
- edchars.eof = -1;
- if (edchars.werase == _POSIX_VDISABLE)
- edchars.werase = -1;
-#endif
-
- if (edchars.erase >= 0) {
- bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
- bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
- }
- if (edchars.kill >= 0)
- bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
- if (edchars.werase >= 0)
- bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
- if (edchars.intr >= 0)
- bind_if_not_bound(0, edchars.intr, XFUNC_abort);
- if (edchars.quit >= 0)
- bind_if_not_bound(0, edchars.quit, XFUNC_noop);
- } else
- tcsetattr(tty_fd, TCSADRAIN, &tty_state);
-
- return (prev);
-}
-
-#if !MKSH_S_NOVI
-/* +++ vi editing mode +++ */
-
-#define Ctrl(c) (c&0x1f)
-
-struct edstate {
- char *cbuf;
- int winleft;
- int cbufsize;
- int linelen;
- int cursor;
-};
-
-static int vi_hook(int);
-static int nextstate(int);
-static int vi_insert(int);
-static int vi_cmd(int, const char *);
-static int domove(int, const char *, int);
-static int redo_insert(int);
-static void yank_range(int, int);
-static int bracktype(int);
-static void save_cbuf(void);
-static void restore_cbuf(void);
-static int putbuf(const char *, int, int);
-static void del_range(int, int);
-static int findch(int, int, int, int);
-static int forwword(int);
-static int backword(int);
-static int endword(int);
-static int Forwword(int);
-static int Backword(int);
-static int Endword(int);
-static int grabhist(int, int);
-static int grabsearch(int, int, int, char *);
-static void redraw_line(int);
-static void refresh(int);
-static int outofwin(void);
-static void rewindow(void);
-static int newcol(int, int);
-static void display(char *, char *, int);
-static void ed_mov_opt(int, char *);
-static int expand_word(int);
-static int complete_word(int, int);
-static int print_expansions(struct edstate *, int);
-#define char_len(c) ((c) < ' ' || (c) == 0x7F ? 2 : 1)
-static void x_vi_zotc(int);
-static void vi_error(void);
-static void vi_macro_reset(void);
-static int x_vi_putbuf(const char *, size_t);
-
-#define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
-#define M_ 0x2 /* movement command (h, l, etc.) */
-#define E_ 0x4 /* extended command (c, d, y) */
-#define X_ 0x8 /* long command (@, f, F, t, T, etc.) */
-#define U_ 0x10 /* an UN-undoable command (that isn't a M_) */
-#define B_ 0x20 /* bad command (^@) */
-#define Z_ 0x40 /* repeat count defaults to 0 (not 1) */
-#define S_ 0x80 /* search (/, ?) */
-
-#define is_bad(c) (classify[(c)&0x7f]&B_)
-#define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
-#define is_move(c) (classify[(c)&0x7f]&M_)
-#define is_extend(c) (classify[(c)&0x7f]&E_)
-#define is_long(c) (classify[(c)&0x7f]&X_)
-#define is_undoable(c) (!(classify[(c)&0x7f]&U_))
-#define is_srch(c) (classify[(c)&0x7f]&S_)
-#define is_zerocount(c) (classify[(c)&0x7f]&Z_)
-
-static const unsigned char classify[128] = {
-/* 0 1 2 3 4 5 6 7 */
-/* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */
- B_, 0, 0, 0, 0, C_|U_, C_|Z_, 0,
-/* 1 ^H ^I ^J ^K ^L ^M ^N ^O */
- M_, C_|Z_, 0, 0, C_|U_, 0, C_, 0,
-/* 2 ^P ^Q ^R ^S ^T ^U ^V ^W */
- C_, 0, C_|U_, 0, 0, 0, C_, 0,
-/* 3 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
- C_, 0, 0, C_|Z_, 0, 0, 0, 0,
-/* 4 <space> ! " # $ % & ' */
- M_, 0, 0, C_, M_, M_, 0, 0,
-/* 5 ( ) * + , - . / */
- 0, 0, C_, C_, M_, C_, 0, C_|S_,
-/* 6 0 1 2 3 4 5 6 7 */
- M_, 0, 0, 0, 0, 0, 0, 0,
-/* 7 8 9 : ; < = > ? */
- 0, 0, 0, M_, 0, C_, 0, C_|S_,
-/* 8 @ A B C D E F G */
- C_|X_, C_, M_, C_, C_, M_, M_|X_, C_|U_|Z_,
-/* 9 H I J K L M N O */
- 0, C_, 0, 0, 0, 0, C_|U_, 0,
-/* A P Q R S T U V W */
- C_, 0, C_, C_, M_|X_, C_, 0, M_,
-/* B X Y Z [ \ ] ^ _ */
- C_, C_|U_, 0, 0, C_|Z_, 0, M_, C_|Z_,
-/* C ` a b c d e f g */
- 0, C_, M_, E_, E_, M_, M_|X_, C_|Z_,
-/* D h i j k l m n o */
- M_, C_, C_|U_, C_|U_, M_, 0, C_|U_, 0,
-/* E p q r s t u v w */
- C_, 0, X_, C_, M_|X_, C_|U_, C_|U_|Z_, M_,
-/* F x y z { | } ~ ^? */
- C_, E_|U_, 0, 0, M_|Z_, 0, C_, 0
-};
-
-#define MAXVICMD 3
-#define SRCHLEN 40
-
-#define INSERT 1
-#define REPLACE 2
-
-#define VNORMAL 0 /* command, insert or replace mode */
-#define VARG1 1 /* digit prefix (first, eg, 5l) */
-#define VEXTCMD 2 /* cmd + movement (eg, cl) */
-#define VARG2 3 /* digit prefix (second, eg, 2c3l) */
-#define VXCH 4 /* f, F, t, T, @ */
-#define VFAIL 5 /* bad command */
-#define VCMD 6 /* single char command (eg, X) */
-#define VREDO 7 /* . */
-#define VLIT 8 /* ^V */
-#define VSEARCH 9 /* /, ? */
-#define VVERSION 10 /* <ESC> ^V */
-
-static char undocbuf[LINE];
-
-static struct edstate *save_edstate(struct edstate *old);
-static void restore_edstate(struct edstate *old, struct edstate *news);
-static void free_edstate(struct edstate *old);
-
-static struct edstate ebuf;
-static struct edstate undobuf = { undocbuf, 0, LINE, 0, 0 };
-
-static struct edstate *es; /* current editor state */
-static struct edstate *undo;
-
-static char ibuf[LINE]; /* input buffer */
-static int first_insert; /* set when starting in insert mode */
-static int saved_inslen; /* saved inslen for first insert */
-static int inslen; /* length of input buffer */
-static int srchlen; /* length of current search pattern */
-static char ybuf[LINE]; /* yank buffer */
-static int yanklen; /* length of yank buffer */
-static int fsavecmd = ' '; /* last find command */
-static int fsavech; /* character to find */
-static char lastcmd[MAXVICMD]; /* last non-move command */
-static int lastac; /* argcnt for lastcmd */
-static int lastsearch = ' '; /* last search command */
-static char srchpat[SRCHLEN]; /* last search pattern */
-static int insert; /* non-zero in insert mode */
-static int hnum; /* position in history */
-static int ohnum; /* history line copied (after mod) */
-static int hlast; /* 1 past last position in history */
-static int state;
-
-/* Information for keeping track of macros that are being expanded.
- * The format of buf is the alias contents followed by a NUL byte followed
- * by the name (letter) of the alias. The end of the buffer is marked by
- * a double NUL. The name of the alias is stored so recursive macros can
- * be detected.
- */
-struct macro_state {
- unsigned char *p; /* current position in buf */
- unsigned char *buf; /* pointer to macro(s) being expanded */
- int len; /* how much data in buffer */
-};
-static struct macro_state macro;
-
-enum expand_mode {
- NONE, EXPAND, COMPLETE, PRINT
-};
-static enum expand_mode expanded = NONE; /* last input was expanded */
-
-static int
-x_vi(char *buf, size_t len)
-{
- int c;
-
- state = VNORMAL;
- ohnum = hnum = hlast = histnum(-1) + 1;
- insert = INSERT;
- saved_inslen = inslen;
- first_insert = 1;
- inslen = 0;
- vi_macro_reset();
-
- es = &ebuf;
- es->cbuf = buf;
- undo = &undobuf;
- undo->cbufsize = es->cbufsize = len > LINE ? LINE : len;
-
- es->linelen = undo->linelen = 0;
- es->cursor = undo->cursor = 0;
- es->winleft = undo->winleft = 0;
-
- cur_col = promptlen(prompt);
- prompt_trunc = (cur_col / x_cols) * x_cols;
- cur_col -= prompt_trunc;
-
- pprompt(prompt, 0);
- if (cur_col > x_cols - 3 - MIN_EDIT_SPACE) {
- prompt_redraw = cur_col = 0;
- x_putc('\n');
- } else
- prompt_redraw = 1;
- pwidth = cur_col;
-
- if (!wbuf_len || wbuf_len != x_cols - 3) {
- wbuf_len = x_cols - 3;
- wbuf[0] = aresize(wbuf[0], wbuf_len, APERM);
- wbuf[1] = aresize(wbuf[1], wbuf_len, APERM);
- }
- (void)memset(wbuf[0], ' ', wbuf_len);
- (void)memset(wbuf[1], ' ', wbuf_len);
- winwidth = x_cols - pwidth - 3;
- win = 0;
- morec = ' ';
- lastref = 1;
- holdlen = 0;
-
- editmode = 2;
- x_flush();
- while (1) {
- if (macro.p) {
- c = *macro.p++;
- /* end of current macro? */
- if (!c) {
- /* more macros left to finish? */
- if (*macro.p++)
- continue;
- /* must be the end of all the macros */
- vi_macro_reset();
- c = x_getc();
- }
- } else
- c = x_getc();
-
- if (c == -1)
- break;
- if (state != VLIT) {
- if (c == edchars.intr || c == edchars.quit) {
- /* pretend we got an interrupt */
- x_vi_zotc(c);
- x_flush();
- trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
- x_mode(false);
- unwind(LSHELL);
- } else if (c == edchars.eof && state != VVERSION) {
- if (es->linelen == 0) {
- x_vi_zotc(edchars.eof);
- c = -1;
- break;
- }
- continue;
- }
- }
- if (vi_hook(c))
- break;
- x_flush();
- }
-
- x_putc('\r');
- x_putc('\n');
- x_flush();
-
- if (c == -1 || (ssize_t)len <= es->linelen)
- return (-1);
-
- if (es->cbuf != buf)
- memmove(buf, es->cbuf, es->linelen);
-
- buf[es->linelen++] = '\n';
-
- return (es->linelen);
-}
-
-static int
-vi_hook(int ch)
-{
- static char curcmd[MAXVICMD], locpat[SRCHLEN];
- static int cmdlen, argc1, argc2;
-
- switch (state) {
-
- case VNORMAL:
- if (insert != 0) {
- if (ch == Ctrl('v')) {
- state = VLIT;
- ch = '^';
- }
- switch (vi_insert(ch)) {
- case -1:
- vi_error();
- state = VNORMAL;
- break;
- case 0:
- if (state == VLIT) {
- es->cursor--;
- refresh(0);
- } else
- refresh(insert != 0);
- break;
- case 1:
- return (1);
- }
- } else {
- if (ch == '\r' || ch == '\n')
- return (1);
- cmdlen = 0;
- argc1 = 0;
- if (ch >= '1' && ch <= '9') {
- argc1 = ch - '0';
- state = VARG1;
- } else {
- curcmd[cmdlen++] = ch;
- state = nextstate(ch);
- if (state == VSEARCH) {
- save_cbuf();
- es->cursor = 0;
- es->linelen = 0;
- if (ch == '/') {
- if (putbuf("/", 1, 0) != 0)
- return (-1);
- } else if (putbuf("?", 1, 0) != 0)
- return (-1);
- refresh(0);
- }
- if (state == VVERSION) {
- save_cbuf();
- es->cursor = 0;
- es->linelen = 0;
- putbuf(KSH_VERSION,
- strlen(KSH_VERSION), 0);
- refresh(0);
- }
- }
- }
- break;
-
- case VLIT:
- if (is_bad(ch)) {
- del_range(es->cursor, es->cursor + 1);
- vi_error();
- } else
- es->cbuf[es->cursor++] = ch;
- refresh(1);
- state = VNORMAL;
- break;
-
- case VVERSION:
- restore_cbuf();
- state = VNORMAL;
- refresh(0);
- break;
-
- case VARG1:
- if (ksh_isdigit(ch))
- argc1 = argc1 * 10 + ch - '0';
- else {
- curcmd[cmdlen++] = ch;
- state = nextstate(ch);
- }
- break;
-
- case VEXTCMD:
- argc2 = 0;
- if (ch >= '1' && ch <= '9') {
- argc2 = ch - '0';
- state = VARG2;
- return (0);
- } else {
- curcmd[cmdlen++] = ch;
- if (ch == curcmd[0])
- state = VCMD;
- else if (is_move(ch))
- state = nextstate(ch);
- else
- state = VFAIL;
- }
- break;
-
- case VARG2:
- if (ksh_isdigit(ch))
- argc2 = argc2 * 10 + ch - '0';
- else {
- if (argc1 == 0)
- argc1 = argc2;
- else
- argc1 *= argc2;
- curcmd[cmdlen++] = ch;
- if (ch == curcmd[0])
- state = VCMD;
- else if (is_move(ch))
- state = nextstate(ch);
- else
- state = VFAIL;
- }
- break;
-
- case VXCH:
- if (ch == Ctrl('['))
- state = VNORMAL;
- else {
- curcmd[cmdlen++] = ch;
- state = VCMD;
- }
- break;
-
- case VSEARCH:
- if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) {
- restore_cbuf();
- /* Repeat last search? */
- if (srchlen == 0) {
- if (!srchpat[0]) {
- vi_error();
- state = VNORMAL;
- refresh(0);
- return (0);
- }
- } else {
- locpat[srchlen] = '\0';
- memcpy(srchpat, locpat, srchlen + 1);
- }
- state = VCMD;
- } else if (ch == edchars.erase || ch == Ctrl('h')) {
- if (srchlen != 0) {
- srchlen--;
- es->linelen -= char_len((unsigned char)locpat[srchlen]);
- es->cursor = es->linelen;
- refresh(0);
- return (0);
- }
- restore_cbuf();
- state = VNORMAL;
- refresh(0);
- } else if (ch == edchars.kill) {
- srchlen = 0;
- es->linelen = 1;
- es->cursor = 1;
- refresh(0);
- return (0);
- } else if (ch == edchars.werase) {
- int i, n = srchlen;
- struct edstate new_es, *save_es;
-
- new_es.cursor = n;
- new_es.cbuf = locpat;
-
- save_es = es;
- es = &new_es;
- n = backword(1);
- es = save_es;
-
- for (i = srchlen; --i >= n; )
- es->linelen -= char_len((unsigned char)locpat[i]);
- srchlen = n;
- es->cursor = es->linelen;
- refresh(0);
- return (0);
- } else {
- if (srchlen == SRCHLEN - 1)
- vi_error();
- else {
- locpat[srchlen++] = ch;
- if (ch < ' ' || ch == 0x7f) {
- if (es->linelen + 2 > es->cbufsize)
- vi_error();
- es->cbuf[es->linelen++] = '^';
- es->cbuf[es->linelen++] = ch ^ '@';
- } else {
- if (es->linelen >= es->cbufsize)
- vi_error();
- es->cbuf[es->linelen++] = ch;
- }
- es->cursor = es->linelen;
- refresh(0);
- }
- return (0);
- }
- break;
- }
-
- switch (state) {
- case VCMD:
- state = VNORMAL;
- switch (vi_cmd(argc1, curcmd)) {
- case -1:
- vi_error();
- refresh(0);
- break;
- case 0:
- if (insert != 0)
- inslen = 0;
- refresh(insert != 0);
- break;
- case 1:
- refresh(0);
- return (1);
- case 2:
- /* back from a 'v' command - don't redraw the screen */
- return (1);
- }
- break;
-
- case VREDO:
- state = VNORMAL;
- if (argc1 != 0)
- lastac = argc1;
- switch (vi_cmd(lastac, lastcmd)) {
- case -1:
- vi_error();
- refresh(0);
- break;
- case 0:
- if (insert != 0) {
- if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
- lastcmd[0] == 'C') {
- if (redo_insert(1) != 0)
- vi_error();
- } else {
- if (redo_insert(lastac) != 0)
- vi_error();
- }
- }
- refresh(0);
- break;
- case 1:
- refresh(0);
- return (1);
- case 2:
- /* back from a 'v' command - can't happen */
- break;
- }
- break;
-
- case VFAIL:
- state = VNORMAL;
- vi_error();
- break;
- }
- return (0);
-}
-
-static int
-nextstate(int ch)
-{
- if (is_extend(ch))
- return (VEXTCMD);
- else if (is_srch(ch))
- return (VSEARCH);
- else if (is_long(ch))
- return (VXCH);
- else if (ch == '.')
- return (VREDO);
- else if (ch == Ctrl('v'))
- return (VVERSION);
- else if (is_cmd(ch))
- return (VCMD);
- else
- return (VFAIL);
-}
-
-static int
-vi_insert(int ch)
-{
- int tcursor;
-
- if (ch == edchars.erase || ch == Ctrl('h')) {
- if (insert == REPLACE) {
- if (es->cursor == undo->cursor) {
- vi_error();
- return (0);
- }
- if (inslen > 0)
- inslen--;
- es->cursor--;
- if (es->cursor >= undo->linelen)
- es->linelen--;
- else
- es->cbuf[es->cursor] = undo->cbuf[es->cursor];
- } else {
- if (es->cursor == 0)
- return (0);
- if (inslen > 0)
- inslen--;
- es->cursor--;
- es->linelen--;
- memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1],
- es->linelen - es->cursor + 1);
- }
- expanded = NONE;
- return (0);
- }
- if (ch == edchars.kill) {
- if (es->cursor != 0) {
- inslen = 0;
- memmove(es->cbuf, &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen -= es->cursor;
- es->cursor = 0;
- }
- expanded = NONE;
- return (0);
- }
- if (ch == edchars.werase) {
- if (es->cursor != 0) {
- tcursor = backword(1);
- memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen -= es->cursor - tcursor;
- if (inslen < es->cursor - tcursor)
- inslen = 0;
- else
- inslen -= es->cursor - tcursor;
- es->cursor = tcursor;
- }
- expanded = NONE;
- return (0);
- }
- /* If any chars are entered before escape, trash the saved insert
- * buffer (if user inserts & deletes char, ibuf gets trashed and
- * we don't want to use it)
- */
- if (first_insert && ch != Ctrl('['))
- saved_inslen = 0;
- switch (ch) {
- case '\0':
- return (-1);
-
- case '\r':
- case '\n':
- return (1);
-
- case Ctrl('['):
- expanded = NONE;
- if (first_insert) {
- first_insert = 0;
- if (inslen == 0) {
- inslen = saved_inslen;
- return (redo_insert(0));
- }
- lastcmd[0] = 'a';
- lastac = 1;
- }
- if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
- lastcmd[0] == 'C')
- return (redo_insert(0));
- else
- return (redo_insert(lastac - 1));
-
- /* { Begin nonstandard vi commands */
- case Ctrl('x'):
- expand_word(0);
- break;
-
- case Ctrl('f'):
- complete_word(0, 0);
- break;
-
- case Ctrl('e'):
- print_expansions(es, 0);
- break;
-
- case Ctrl('i'):
- if (Flag(FVITABCOMPLETE)) {
- complete_word(0, 0);
- break;
- }
- /* FALLTHROUGH */
- /* End nonstandard vi commands } */
-
- default:
- if (es->linelen >= es->cbufsize - 1)
- return (-1);
- ibuf[inslen++] = ch;
- if (insert == INSERT) {
- memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen++;
- }
- es->cbuf[es->cursor++] = ch;
- if (insert == REPLACE && es->cursor > es->linelen)
- es->linelen++;
- expanded = NONE;
- }
- return (0);
-}
-
-static int
-vi_cmd(int argcnt, const char *cmd)
-{
- int ncursor;
- int cur, c1, c2, c3 = 0;
- int any;
- struct edstate *t;
-
- if (argcnt == 0 && !is_zerocount(*cmd))
- argcnt = 1;
-
- if (is_move(*cmd)) {
- if ((cur = domove(argcnt, cmd, 0)) >= 0) {
- if (cur == es->linelen && cur != 0)
- cur--;
- es->cursor = cur;
- } else
- return (-1);
- } else {
- /* Don't save state in middle of macro.. */
- if (is_undoable(*cmd) && !macro.p) {
- undo->winleft = es->winleft;
- memmove(undo->cbuf, es->cbuf, es->linelen);
- undo->linelen = es->linelen;
- undo->cursor = es->cursor;
- lastac = argcnt;
- memmove(lastcmd, cmd, MAXVICMD);
- }
- switch (*cmd) {
-
- case Ctrl('l'):
- case Ctrl('r'):
- redraw_line(1);
- break;
-
- case '@':
- {
- static char alias[] = "_\0";
- struct tbl *ap;
- int olen, nlen;
- char *p, *nbuf;
-
- /* lookup letter in alias list... */
- alias[1] = cmd[1];
- ap = ktsearch(&aliases, alias, hash(alias));
- if (!cmd[1] || !ap || !(ap->flag & ISSET))
- return (-1);
- /* check if this is a recursive call... */
- if ((p = (char *)macro.p))
- while ((p = strnul(p)) && p[1])
- if (*++p == cmd[1])
- return (-1);
- /* insert alias into macro buffer */
- nlen = strlen(ap->val.s) + 1;
- olen = !macro.p ? 2 :
- macro.len - (macro.p - macro.buf);
- nbuf = alloc(nlen + 1 + olen, APERM);
- memcpy(nbuf, ap->val.s, nlen);
- nbuf[nlen++] = cmd[1];
- if (macro.p) {
- memcpy(nbuf + nlen, macro.p, olen);
- afree(macro.buf, APERM);
- nlen += olen;
- } else {
- nbuf[nlen++] = '\0';
- nbuf[nlen++] = '\0';
- }
- macro.p = macro.buf = (unsigned char *)nbuf;
- macro.len = nlen;
- }
- break;
-
- case 'a':
- modified = 1;
- hnum = hlast;
- if (es->linelen != 0)
- es->cursor++;
- insert = INSERT;
- break;
-
- case 'A':
- modified = 1;
- hnum = hlast;
- del_range(0, 0);
- es->cursor = es->linelen;
- insert = INSERT;
- break;
-
- case 'S':
- es->cursor = domove(1, "^", 1);
- del_range(es->cursor, es->linelen);
- modified = 1;
- hnum = hlast;
- insert = INSERT;
- break;
-
- case 'Y':
- cmd = "y$";
- /* ahhhhhh... */
- case 'c':
- case 'd':
- case 'y':
- if (*cmd == cmd[1]) {
- c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
- c2 = es->linelen;
- } else if (!is_move(cmd[1]))
- return (-1);
- else {
- if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
- return (-1);
- if (*cmd == 'c' &&
- (cmd[1] == 'w' || cmd[1] == 'W') &&
- !ksh_isspace(es->cbuf[es->cursor])) {
- do {
- --ncursor;
- } while (ksh_isspace(es->cbuf[ncursor]));
- ncursor++;
- }
- if (ncursor > es->cursor) {
- c1 = es->cursor;
- c2 = ncursor;
- } else {
- c1 = ncursor;
- c2 = es->cursor;
- if (cmd[1] == '%')
- c2++;
- }
- }
- if (*cmd != 'c' && c1 != c2)
- yank_range(c1, c2);
- if (*cmd != 'y') {
- del_range(c1, c2);
- es->cursor = c1;
- }
- if (*cmd == 'c') {
- modified = 1;
- hnum = hlast;
- insert = INSERT;
- }
- break;
-
- case 'p':
- modified = 1;
- hnum = hlast;
- if (es->linelen != 0)
- es->cursor++;
- while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
- ;
- if (es->cursor != 0)
- es->cursor--;
- if (argcnt != 0)
- return (-1);
- break;
-
- case 'P':
- modified = 1;
- hnum = hlast;
- any = 0;
- while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
- any = 1;
- if (any && es->cursor != 0)
- es->cursor--;
- if (argcnt != 0)
- return (-1);
- break;
-
- case 'C':
- modified = 1;
- hnum = hlast;
- del_range(es->cursor, es->linelen);
- insert = INSERT;
- break;
-
- case 'D':
- yank_range(es->cursor, es->linelen);
- del_range(es->cursor, es->linelen);
- if (es->cursor != 0)
- es->cursor--;
- break;
-
- case 'g':
- if (!argcnt)
- argcnt = hlast;
- /* FALLTHROUGH */
- case 'G':
- if (!argcnt)
- argcnt = 1;
- else
- argcnt = hlast - (source->line - argcnt);
- if (grabhist(modified, argcnt - 1) < 0)
- return (-1);
- else {
- modified = 0;
- hnum = argcnt - 1;
- }
- break;
-
- case 'i':
- modified = 1;
- hnum = hlast;
- insert = INSERT;
- break;
-
- case 'I':
- modified = 1;
- hnum = hlast;
- es->cursor = domove(1, "^", 1);
- insert = INSERT;
- break;
-
- case 'j':
- case '+':
- case Ctrl('n'):
- if (grabhist(modified, hnum + argcnt) < 0)
- return (-1);
- else {
- modified = 0;
- hnum += argcnt;
- }
- break;
-
- case 'k':
- case '-':
- case Ctrl('p'):
- if (grabhist(modified, hnum - argcnt) < 0)
- return (-1);
- else {
- modified = 0;
- hnum -= argcnt;
- }
- break;
-
- case 'r':
- if (es->linelen == 0)
- return (-1);
- modified = 1;
- hnum = hlast;
- if (cmd[1] == 0)
- vi_error();
- else {
- int n;
-
- if (es->cursor + argcnt > es->linelen)
- return (-1);
- for (n = 0; n < argcnt; ++n)
- es->cbuf[es->cursor + n] = cmd[1];
- es->cursor += n - 1;
- }
- break;
-
- case 'R':
- modified = 1;
- hnum = hlast;
- insert = REPLACE;
- break;
-
- case 's':
- if (es->linelen == 0)
- return (-1);
- modified = 1;
- hnum = hlast;
- if (es->cursor + argcnt > es->linelen)
- argcnt = es->linelen - es->cursor;
- del_range(es->cursor, es->cursor + argcnt);
- insert = INSERT;
- break;
-
- case 'v':
- if (!argcnt) {
- if (es->linelen == 0)
- return (-1);
- if (modified) {
- es->cbuf[es->linelen] = '\0';
- histsave(&source->line, es->cbuf, true,
- true);
- } else
- argcnt = source->line + 1 -
- (hlast - hnum);
- }
- if (argcnt)
- shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
- "fc -e ${VISUAL:-${EDITOR:-vi}} --",
- argcnt);
- else
- strlcpy(es->cbuf,
- "fc -e ${VISUAL:-${EDITOR:-vi}} --",
- es->cbufsize);
- es->linelen = strlen(es->cbuf);
- return (2);
-
- case 'x':
- if (es->linelen == 0)
- return (-1);
- modified = 1;
- hnum = hlast;
- if (es->cursor + argcnt > es->linelen)
- argcnt = es->linelen - es->cursor;
- yank_range(es->cursor, es->cursor + argcnt);
- del_range(es->cursor, es->cursor + argcnt);
- break;
-
- case 'X':
- if (es->cursor > 0) {
- modified = 1;
- hnum = hlast;
- if (es->cursor < argcnt)
- argcnt = es->cursor;
- yank_range(es->cursor - argcnt, es->cursor);
- del_range(es->cursor - argcnt, es->cursor);
- es->cursor -= argcnt;
- } else
- return (-1);
- break;
-
- case 'u':
- t = es;
- es = undo;
- undo = t;
- break;
-
- case 'U':
- if (!modified)
- return (-1);
- if (grabhist(modified, ohnum) < 0)
- return (-1);
- modified = 0;
- hnum = ohnum;
- break;
-
- case '?':
- if (hnum == hlast)
- hnum = -1;
- /* ahhh */
- case '/':
- c3 = 1;
- srchlen = 0;
- lastsearch = *cmd;
- /* FALLTHROUGH */
- case 'n':
- case 'N':
- if (lastsearch == ' ')
- return (-1);
- if (lastsearch == '?')
- c1 = 1;
- else
- c1 = 0;
- if (*cmd == 'N')
- c1 = !c1;
- if ((c2 = grabsearch(modified, hnum,
- c1, srchpat)) < 0) {
- if (c3) {
- restore_cbuf();
- refresh(0);
- }
- return (-1);
- } else {
- modified = 0;
- hnum = c2;
- ohnum = hnum;
- }
- break;
- case '_':
- {
- int inspace;
- char *p, *sp;
-
- if (histnum(-1) < 0)
- return (-1);
- p = *histpos();
-#define issp(c) (ksh_isspace(c) || (c) == '\n')
- if (argcnt) {
- while (*p && issp(*p))
- p++;
- while (*p && --argcnt) {
- while (*p && !issp(*p))
- p++;
- while (*p && issp(*p))
- p++;
- }
- if (!*p)
- return (-1);
- sp = p;
- } else {
- sp = p;
- inspace = 0;
- while (*p) {
- if (issp(*p))
- inspace = 1;
- else if (inspace) {
- inspace = 0;
- sp = p;
- }
- p++;
- }
- p = sp;
- }
- modified = 1;
- hnum = hlast;
- if (es->cursor != es->linelen)
- es->cursor++;
- while (*p && !issp(*p)) {
- argcnt++;
- p++;
- }
- if (putbuf(" ", 1, 0) != 0)
- argcnt = -1;
- else if (putbuf(sp, argcnt, 0) != 0)
- argcnt = -1;
- if (argcnt < 0) {
- if (es->cursor != 0)
- es->cursor--;
- return (-1);
- }
- insert = INSERT;
- }
- break;
-
- case '~':
- {
- char *p;
- int i;
-
- if (es->linelen == 0)
- return (-1);
- for (i = 0; i < argcnt; i++) {
- p = &es->cbuf[es->cursor];
- if (ksh_islower(*p)) {
- modified = 1;
- hnum = hlast;
- *p = ksh_toupper(*p);
- } else if (ksh_isupper(*p)) {
- modified = 1;
- hnum = hlast;
- *p = ksh_tolower(*p);
- }
- if (es->cursor < es->linelen - 1)
- es->cursor++;
- }
- break;
- }
-
- case '#':
- {
- int ret = x_do_comment(es->cbuf, es->cbufsize,
- &es->linelen);
- if (ret >= 0)
- es->cursor = 0;
- return (ret);
- }
-
- case '=': /* AT&T ksh */
- case Ctrl('e'): /* Nonstandard vi/ksh */
- print_expansions(es, 1);
- break;
-
-
- case Ctrl('i'): /* Nonstandard vi/ksh */
- if (!Flag(FVITABCOMPLETE))
- return (-1);
- complete_word(1, argcnt);
- break;
-
- case Ctrl('['): /* some annoying AT&T kshs */
- if (!Flag(FVIESCCOMPLETE))
- return (-1);
- case '\\': /* AT&T ksh */
- case Ctrl('f'): /* Nonstandard vi/ksh */
- complete_word(1, argcnt);
- break;
-
-
- case '*': /* AT&T ksh */
- case Ctrl('x'): /* Nonstandard vi/ksh */
- expand_word(1);
- break;
- }
- if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
- es->cursor--;
- }
- return (0);
-}
-
-static int
-domove(int argcnt, const char *cmd, int sub)
-{
- int bcount, i = 0, t;
- int ncursor = 0;
-
- switch (*cmd) {
- case 'b':
- if (!sub && es->cursor == 0)
- return (-1);
- ncursor = backword(argcnt);
- break;
-
- case 'B':
- if (!sub && es->cursor == 0)
- return (-1);
- ncursor = Backword(argcnt);
- break;
-
- case 'e':
- if (!sub && es->cursor + 1 >= es->linelen)
- return (-1);
- ncursor = endword(argcnt);
- if (sub && ncursor < es->linelen)
- ncursor++;
- break;
-
- case 'E':
- if (!sub && es->cursor + 1 >= es->linelen)
- return (-1);
- ncursor = Endword(argcnt);
- if (sub && ncursor < es->linelen)
- ncursor++;
- break;
-
- case 'f':
- case 'F':
- case 't':
- case 'T':
- fsavecmd = *cmd;
- fsavech = cmd[1];
- /* drop through */
-
- case ',':
- case ';':
- if (fsavecmd == ' ')
- return (-1);
- i = fsavecmd == 'f' || fsavecmd == 'F';
- t = fsavecmd > 'a';
- if (*cmd == ',')
- t = !t;
- if ((ncursor = findch(fsavech, argcnt, t, i)) < 0)
- return (-1);
- if (sub && t)
- ncursor++;
- break;
-
- case 'h':
- case Ctrl('h'):
- if (!sub && es->cursor == 0)
- return (-1);
- ncursor = es->cursor - argcnt;
- if (ncursor < 0)
- ncursor = 0;
- break;
-
- case ' ':
- case 'l':
- if (!sub && es->cursor + 1 >= es->linelen)
- return (-1);
- if (es->linelen != 0) {
- ncursor = es->cursor + argcnt;
- if (ncursor > es->linelen)
- ncursor = es->linelen;
- }
- break;
-
- case 'w':
- if (!sub && es->cursor + 1 >= es->linelen)
- return (-1);
- ncursor = forwword(argcnt);
- break;
-
- case 'W':
- if (!sub && es->cursor + 1 >= es->linelen)
- return (-1);
- ncursor = Forwword(argcnt);
- break;
-
- case '0':
- ncursor = 0;
- break;
-
- case '^':
- ncursor = 0;
- while (ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
- ncursor++;
- break;
-
- case '|':
- ncursor = argcnt;
- if (ncursor > es->linelen)
- ncursor = es->linelen;
- if (ncursor)
- ncursor--;
- break;
-
- case '$':
- if (es->linelen != 0)
- ncursor = es->linelen;
- else
- ncursor = 0;
- break;
-
- case '%':
- ncursor = es->cursor;
- while (ncursor < es->linelen &&
- (i = bracktype(es->cbuf[ncursor])) == 0)
- ncursor++;
- if (ncursor == es->linelen)
- return (-1);
- bcount = 1;
- do {
- if (i > 0) {
- if (++ncursor >= es->linelen)
- return (-1);
- } else {
- if (--ncursor < 0)
- return (-1);
- }
- t = bracktype(es->cbuf[ncursor]);
- if (t == i)
- bcount++;
- else if (t == -i)
- bcount--;
- } while (bcount != 0);
- if (sub && i > 0)
- ncursor++;
- break;
-
- default:
- return (-1);
- }
- return (ncursor);
-}
-
-static int
-redo_insert(int count)
-{
- while (count-- > 0)
- if (putbuf(ibuf, inslen, insert == REPLACE) != 0)
- return (-1);
- if (es->cursor > 0)
- es->cursor--;
- insert = 0;
- return (0);
-}
-
-static void
-yank_range(int a, int b)
-{
- yanklen = b - a;
- if (yanklen != 0)
- memmove(ybuf, &es->cbuf[a], yanklen);
-}
-
-static int
-bracktype(int ch)
-{
- switch (ch) {
-
- case '(':
- return (1);
-
- case '[':
- return (2);
-
- case '{':
- return (3);
-
- case ')':
- return (-1);
-
- case ']':
- return (-2);
-
- case '}':
- return (-3);
-
- default:
- return (0);
- }
-}
-
-/*
- * Non user interface editor routines below here
- */
-
-static void
-save_cbuf(void)
-{
- memmove(holdbuf, es->cbuf, es->linelen);
- holdlen = es->linelen;
- holdbuf[holdlen] = '\0';
-}
-
-static void
-restore_cbuf(void)
-{
- es->cursor = 0;
- es->linelen = holdlen;
- memmove(es->cbuf, holdbuf, holdlen);
-}
-
-/* return a new edstate */
-static struct edstate *
-save_edstate(struct edstate *old)
-{
- struct edstate *news;
-
- news = alloc(sizeof(struct edstate), APERM);
- news->cbuf = alloc(old->cbufsize, APERM);
- memcpy(news->cbuf, old->cbuf, old->linelen);
- news->cbufsize = old->cbufsize;
- news->linelen = old->linelen;
- news->cursor = old->cursor;
- news->winleft = old->winleft;
- return (news);
-}
-
-static void
-restore_edstate(struct edstate *news, struct edstate *old)
-{
- memcpy(news->cbuf, old->cbuf, old->linelen);
- news->linelen = old->linelen;
- news->cursor = old->cursor;
- news->winleft = old->winleft;
- free_edstate(old);
-}
-
-static void
-free_edstate(struct edstate *old)
-{
- afree(old->cbuf, APERM);
- afree(old, APERM);
-}
-
-/*
- * this is used for calling x_escape() in complete_word()
- */
-static int
-x_vi_putbuf(const char *s, size_t len)
-{
- return (putbuf(s, len, 0));
-}
-
-static int
-putbuf(const char *buf, int len, int repl)
-{
- if (len == 0)
- return (0);
- if (repl) {
- if (es->cursor + len >= es->cbufsize)
- return (-1);
- if (es->cursor + len > es->linelen)
- es->linelen = es->cursor + len;
- } else {
- if (es->linelen + len >= es->cbufsize)
- return (-1);
- memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
- es->linelen - es->cursor);
- es->linelen += len;
- }
- memmove(&es->cbuf[es->cursor], buf, len);
- es->cursor += len;
- return (0);
-}
-
-static void
-del_range(int a, int b)
-{
- if (es->linelen != b)
- memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
- es->linelen -= b - a;
-}
-
-static int
-findch(int ch, int cnt, int forw, int incl)
-{
- int ncursor;
-
- if (es->linelen == 0)
- return (-1);
- ncursor = es->cursor;
- while (cnt--) {
- do {
- if (forw) {
- if (++ncursor == es->linelen)
- return (-1);
- } else {
- if (--ncursor < 0)
- return (-1);
- }
- } while (es->cbuf[ncursor] != ch);
- }
- if (!incl) {
- if (forw)
- ncursor--;
- else
- ncursor++;
- }
- return (ncursor);
-}
-
-static int
-forwword(int argcnt)
-{
- int ncursor;
-
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (ksh_isalnux(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
- ncursor++;
- else if (!ksh_isspace(es->cbuf[ncursor]))
- while (!ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
- ncursor++;
- while (ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
- ncursor++;
- }
- return (ncursor);
-}
-
-static int
-backword(int argcnt)
-{
- int ncursor;
-
- ncursor = es->cursor;
- while (ncursor > 0 && argcnt--) {
- while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor]))
- ;
- if (ncursor > 0) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (--ncursor >= 0 &&
- ksh_isalnux(es->cbuf[ncursor]))
- ;
- else
- while (--ncursor >= 0 &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
- ;
- ncursor++;
- }
- }
- return (ncursor);
-}
-
-static int
-endword(int argcnt)
-{
- int ncursor;
-
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- while (++ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
- ;
- if (ncursor < es->linelen - 1) {
- if (ksh_isalnux(es->cbuf[ncursor]))
- while (++ncursor < es->linelen &&
- ksh_isalnux(es->cbuf[ncursor]))
- ;
- else
- while (++ncursor < es->linelen &&
- !ksh_isalnux(es->cbuf[ncursor]) &&
- !ksh_isspace(es->cbuf[ncursor]))
- ;
- ncursor--;
- }
- }
- return (ncursor);
-}
-
-static int
-Forwword(int argcnt)
-{
- int ncursor;
-
- ncursor = es->cursor;
- while (ncursor < es->linelen && argcnt--) {
- while (!ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
- ncursor++;
- while (ksh_isspace(es->cbuf[ncursor]) &&
- ncursor < es->linelen)
- ncursor++;
- }
- return (ncursor);
-}
-
-static int
-Backword(int argcnt)
-{
- int ncursor;
-
- ncursor = es->cursor;
- while (ncursor > 0 && argcnt--) {
- while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor]))
- ;
- while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor]))
- ncursor--;
- ncursor++;
- }
- return (ncursor);
-}
-
-static int
-Endword(int argcnt)
-{
- int ncursor;
-
- ncursor = es->cursor;
- while (ncursor < es->linelen - 1 && argcnt--) {
- while (++ncursor < es->linelen - 1 &&
- ksh_isspace(es->cbuf[ncursor]))
- ;
- if (ncursor < es->linelen - 1) {
- while (++ncursor < es->linelen &&
- !ksh_isspace(es->cbuf[ncursor]))
- ;
- ncursor--;
- }
- }
- return (ncursor);
-}
-
-static int
-grabhist(int save, int n)
-{
- char *hptr;
-
- if (n < 0 || n > hlast)
- return (-1);
- if (n == hlast) {
- restore_cbuf();
- ohnum = n;
- return (0);
- }
- (void)histnum(n);
- if ((hptr = *histpos()) == NULL) {
- internal_warningf("grabhist: bad history array");
- return (-1);
- }
- if (save)
- save_cbuf();
- if ((es->linelen = strlen(hptr)) >= es->cbufsize)
- es->linelen = es->cbufsize - 1;
- memmove(es->cbuf, hptr, es->linelen);
- es->cursor = 0;
- ohnum = n;
- return (0);
-}
-
-static int
-grabsearch(int save, int start, int fwd, char *pat)
-{
- char *hptr;
- int hist;
- int anchored;
-
- if ((start == 0 && fwd == 0) || (start >= hlast - 1 && fwd == 1))
- return (-1);
- if (fwd)
- start++;
- else
- start--;
- anchored = *pat == '^' ? (++pat, 1) : 0;
- if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
- /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
- /* XXX should strcmp be strncmp? */
- if (start != 0 && fwd && strcmp(holdbuf, pat) >= 0) {
- restore_cbuf();
- return (0);
- } else
- return (-1);
- }
- if (save)
- save_cbuf();
- histnum(hist);
- hptr = *histpos();
- if ((es->linelen = strlen(hptr)) >= es->cbufsize)
- es->linelen = es->cbufsize - 1;
- memmove(es->cbuf, hptr, es->linelen);
- es->cursor = 0;
- return (hist);
-}
-
-static void
-redraw_line(int newl)
-{
- (void)memset(wbuf[win], ' ', wbuf_len);
- if (newl) {
- x_putc('\r');
- x_putc('\n');
- }
- if (prompt_redraw)
- pprompt(prompt, prompt_trunc);
- cur_col = pwidth;
- morec = ' ';
-}
-
-static void
-refresh(int leftside)
-{
- if (leftside < 0)
- leftside = lastref;
- else
- lastref = leftside;
- if (outofwin())
- rewindow();
- display(wbuf[1 - win], wbuf[win], leftside);
- win = 1 - win;
-}
-
-static int
-outofwin(void)
-{
- int cur, col;
-
- if (es->cursor < es->winleft)
- return (1);
- col = 0;
- cur = es->winleft;
- while (cur < es->cursor)
- col = newcol((unsigned char)es->cbuf[cur++], col);
- if (col >= winwidth)
- return (1);
- return (0);
-}
-
-static void
-rewindow(void)
-{
- int tcur, tcol;
- int holdcur1, holdcol1;
- int holdcur2, holdcol2;
-
- holdcur1 = holdcur2 = tcur = 0;
- holdcol1 = holdcol2 = tcol = 0;
- while (tcur < es->cursor) {
- if (tcol - holdcol2 > winwidth / 2) {
- holdcur1 = holdcur2;
- holdcol1 = holdcol2;
- holdcur2 = tcur;
- holdcol2 = tcol;
- }
- tcol = newcol((unsigned char)es->cbuf[tcur++], tcol);
- }
- while (tcol - holdcol1 > winwidth / 2)
- holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++],
- holdcol1);
- es->winleft = holdcur1;
-}
-
-static int
-newcol(int ch, int col)
-{
- if (ch == '\t')
- return ((col | 7) + 1);
- return (col + char_len(ch));
-}
-
-static void
-display(char *wb1, char *wb2, int leftside)
-{
- unsigned char ch;
- char *twb1, *twb2, mc;
- int cur, col, cnt;
- int ncol = 0;
- int moreright;
-
- col = 0;
- cur = es->winleft;
- moreright = 0;
- twb1 = wb1;
- while (col < winwidth && cur < es->linelen) {
- if (cur == es->cursor && leftside)
- ncol = col + pwidth;
- if ((ch = es->cbuf[cur]) == '\t')
- do {
- *twb1++ = ' ';
- } while (++col < winwidth && (col & 7) != 0);
- else if (col < winwidth) {
- if (ch < ' ' || ch == 0x7f) {
- *twb1++ = '^';
- if (++col < winwidth) {
- *twb1++ = ch ^ '@';
- col++;
- }
- } else {
- *twb1++ = ch;
- col++;
- }
- }
- if (cur == es->cursor && !leftside)
- ncol = col + pwidth - 1;
- cur++;
- }
- if (cur == es->cursor)
- ncol = col + pwidth;
- if (col < winwidth) {
- while (col < winwidth) {
- *twb1++ = ' ';
- col++;
- }
- } else
- moreright++;
- *twb1 = ' ';
-
- col = pwidth;
- cnt = winwidth;
- twb1 = wb1;
- twb2 = wb2;
- while (cnt--) {
- if (*twb1 != *twb2) {
- if (cur_col != col)
- ed_mov_opt(col, wb1);
- x_putc(*twb1);
- cur_col++;
- }
- twb1++;
- twb2++;
- col++;
- }
- if (es->winleft > 0 && moreright)
- /* POSIX says to use * for this but that is a globbing
- * character and may confuse people; + is more innocuous
- */
- mc = '+';
- else if (es->winleft > 0)
- mc = '<';
- else if (moreright)
- mc = '>';
- else
- mc = ' ';
- if (mc != morec) {
- ed_mov_opt(pwidth + winwidth + 1, wb1);
- x_putc(mc);
- cur_col++;
- morec = mc;
- }
- if (cur_col != ncol)
- ed_mov_opt(ncol, wb1);
-}
-
-static void
-ed_mov_opt(int col, char *wb)
-{
- if (col < cur_col) {
- if (col + 1 < cur_col - col) {
- x_putc('\r');
- if (prompt_redraw)
- pprompt(prompt, prompt_trunc);
- cur_col = pwidth;
- while (cur_col++ < col)
- x_putcf(*wb++);
- } else {
- while (cur_col-- > col)
- x_putc('\b');
- }
- } else {
- wb = &wb[cur_col - pwidth];
- while (cur_col++ < col)
- x_putcf(*wb++);
- }
- cur_col = col;
-}
-
-
-/* replace word with all expansions (ie, expand word*) */
-static int
-expand_word(int cmd)
-{
- static struct edstate *buf;
- int rval = 0;
- int nwords;
- int start, end;
- char **words;
- int i;
-
- /* Undo previous expansion */
- if (cmd == 0 && expanded == EXPAND && buf) {
- restore_edstate(es, buf);
- buf = 0;
- expanded = NONE;
- return (0);
- }
- if (buf) {
- free_edstate(buf);
- buf = 0;
- }
-
- nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
- es->cbuf, es->linelen, es->cursor,
- &start, &end, &words, NULL);
- if (nwords == 0) {
- vi_error();
- return (-1);
- }
-
- buf = save_edstate(es);
- expanded = EXPAND;
- del_range(start, end);
- es->cursor = start;
- for (i = 0; i < nwords; ) {
- if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
- rval = -1;
- break;
- }
- if (++i < nwords && putbuf(" ", 1, 0) != 0) {
- rval = -1;
- break;
- }
- }
- i = buf->cursor - end;
- if (rval == 0 && i > 0)
- es->cursor += i;
- modified = 1;
- hnum = hlast;
- insert = INSERT;
- lastac = 0;
- refresh(0);
- return (rval);
-}
-
-static int
-complete_word(int cmd, int count)
-{
- static struct edstate *buf;
- int rval, nwords, start, end, match_len;
- char **words;
- char *match;
- bool is_command, is_unique;
-
- /* Undo previous completion */
- if (cmd == 0 && expanded == COMPLETE && buf) {
- print_expansions(buf, 0);
- expanded = PRINT;
- return (0);
- }
- if (cmd == 0 && expanded == PRINT && buf) {
- restore_edstate(es, buf);
- buf = 0;
- expanded = NONE;
- return (0);
- }
- if (buf) {
- free_edstate(buf);
- buf = 0;
- }
-
- /* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
- * was done this way.
- */
- nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
- es->cbuf, es->linelen, es->cursor,
- &start, &end, &words, &is_command);
- if (nwords == 0) {
- vi_error();
- return (-1);
- }
- if (count) {
- int i;
-
- count--;
- if (count >= nwords) {
- vi_error();
- x_print_expansions(nwords, words, is_command);
- x_free_words(nwords, words);
- redraw_line(0);
- return (-1);
- }
- /*
- * Expand the count'th word to its basename
- */
- if (is_command) {
- match = words[count] +
- x_basename(words[count], NULL);
- /* If more than one possible match, use full path */
- for (i = 0; i < nwords; i++)
- if (i != count &&
- strcmp(words[i] + x_basename(words[i],
- NULL), match) == 0) {
- match = words[count];
- break;
- }
- } else
- match = words[count];
- match_len = strlen(match);
- is_unique = true;
- /* expanded = PRINT; next call undo */
- } else {
- match = words[0];
- match_len = x_longest_prefix(nwords, words);
- expanded = COMPLETE; /* next call will list completions */
- is_unique = nwords == 1;
- }
-
- buf = save_edstate(es);
- del_range(start, end);
- es->cursor = start;
-
- /* escape all shell-sensitive characters and put the result into
- * command buffer */
- rval = x_escape(match, match_len, x_vi_putbuf);
-
- if (rval == 0 && is_unique) {
- /* If exact match, don't undo. Allows directory completions
- * to be used (ie, complete the next portion of the path).
- */
- expanded = NONE;
-
- /* If not a directory, add a space to the end... */
- if (match_len > 0 && match[match_len - 1] != '/')
- rval = putbuf(" ", 1, 0);
- }
- x_free_words(nwords, words);
-
- modified = 1;
- hnum = hlast;
- insert = INSERT;
- lastac = 0; /* prevent this from being redone... */
- refresh(0);
-
- return (rval);
-}
-
-static int
-print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
-{
- int start, end, nwords;
- char **words;
- bool is_command;
-
- nwords = x_cf_glob(XCF_COMMAND_FILE | XCF_FULLPATH,
- est->cbuf, est->linelen, est->cursor,
- &start, &end, &words, &is_command);
- if (nwords == 0) {
- vi_error();
- return (-1);
- }
- x_print_expansions(nwords, words, is_command);
- x_free_words(nwords, words);
- redraw_line(0);
- return (0);
-}
-
-/* Similar to x_zotc(emacs.c), but no tab weirdness */
-static void
-x_vi_zotc(int c)
-{
- if (c < ' ' || c == 0x7f) {
- x_putc('^');
- c ^= '@';
- }
- x_putc(c);
-}
-
-static void
-vi_error(void)
-{
- /* Beem out of any macros as soon as an error occurs */
- vi_macro_reset();
- x_putc(7);
- x_flush();
-}
-
-static void
-vi_macro_reset(void)
-{
- if (macro.p) {
- afree(macro.buf, APERM);
- memset((char *)¯o, 0, sizeof(macro));
- }
-}
-#endif /* !MKSH_S_NOVI */
diff --git a/mksh/src/emacsfn.h b/mksh/src/emacsfn.h
deleted file mode 100644
index 1333399..0000000
--- a/mksh/src/emacsfn.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.5 2010/07/17 22:09:33 tg Exp $");
-#define FN(cname,sname,flags) static int x_##cname(int);
-#elif defined(EMACSFN_ENUMS)
-#define FN(cname,sname,flags) XFUNC_##cname,
-#define F0(cname,sname,flags) XFUNC_##cname = 0,
-#elif defined(EMACSFN_ITEMS)
-#define FN(cname,sname,flags) { x_##cname, sname, flags },
-#endif
-
-#ifndef F0
-#define F0 FN
-#endif
-
-F0(abort, "abort", 0)
-FN(beg_hist, "beginning-of-history", 0)
-FN(cls, "clear-screen", 0)
-FN(comment, "comment", 0)
-FN(comp_comm, "complete-command", 0)
-FN(comp_file, "complete-file", 0)
-FN(comp_list, "complete-list", 0)
-FN(complete, "complete", 0)
-FN(del_back, "delete-char-backward", XF_ARG)
-FN(del_bword, "delete-word-backward", XF_ARG)
-FN(del_char, "delete-char-forward", XF_ARG)
-FN(del_fword, "delete-word-forward", XF_ARG)
-FN(del_line, "kill-line", 0)
-FN(draw_line, "redraw", 0)
-#ifndef MKSH_SMALL
-FN(edit_line, "edit-line", XF_ARG)
-#endif
-FN(end_hist, "end-of-history", 0)
-FN(end_of_text, "eot", 0)
-FN(enumerate, "list", 0)
-FN(eot_del, "eot-or-delete", XF_ARG)
-FN(error, "error", 0)
-FN(expand, "expand-file", 0)
-#ifndef MKSH_SMALL
-FN(fold_capitalise, "capitalize-word", XF_ARG)
-FN(fold_lower, "downcase-word", XF_ARG)
-FN(fold_upper, "upcase-word", XF_ARG)
-#endif
-FN(goto_hist, "goto-history", XF_ARG)
-#ifndef MKSH_SMALL
-FN(ins_string, "macro-string", XF_NOBIND)
-#endif
-FN(insert, "auto-insert", XF_ARG)
-FN(kill, "kill-to-eol", XF_ARG)
-FN(kill_region, "kill-region", 0)
-FN(list_comm, "list-command", 0)
-FN(list_file, "list-file", 0)
-FN(literal, "quote", 0)
-FN(meta1, "prefix-1", XF_PREFIX)
-FN(meta2, "prefix-2", XF_PREFIX)
-FN(meta_yank, "yank-pop", 0)
-FN(mv_back, "backward-char", XF_ARG)
-FN(mv_begin, "beginning-of-line", 0)
-FN(mv_bword, "backward-word", XF_ARG)
-FN(mv_end, "end-of-line", 0)
-FN(mv_forw, "forward-char", XF_ARG)
-FN(mv_fword, "forward-word", XF_ARG)
-FN(newline, "newline", 0)
-FN(next_com, "down-history", XF_ARG)
-FN(nl_next_com, "newline-and-next", 0)
-FN(noop, "no-op", 0)
-FN(prev_com, "up-history", XF_ARG)
-FN(prev_histword, "prev-hist-word", XF_ARG)
-FN(search_char_back, "search-character-backward", XF_ARG)
-FN(search_char_forw, "search-character-forward", XF_ARG)
-FN(search_hist, "search-history", 0)
-#ifndef MKSH_SMALL
-FN(search_hist_dn, "search-history-down", 0)
-FN(search_hist_up, "search-history-up", 0)
-#endif
-FN(set_arg, "set-arg", XF_NOBIND)
-FN(set_mark, "set-mark-command", 0)
-FN(transpose, "transpose-chars", 0)
-FN(version, "version", 0)
-#ifndef MKSH_SMALL
-FN(vt_hack, "vt100-hack", XF_ARG)
-#endif
-FN(xchg_point_mark, "exchange-point-and-mark", 0)
-FN(yank, "yank", 0)
-
-#undef FN
-#undef F0
-#undef EMACSFN_DEFNS
-#undef EMACSFN_ENUMS
-#undef EMACSFN_ITEMS
diff --git a/mksh/src/eval.c b/mksh/src/eval.c
deleted file mode 100644
index c22e346..0000000
--- a/mksh/src/eval.c
+++ /dev/null
@@ -1,1580 +0,0 @@
-/* $OpenBSD: eval.c,v 1.35 2010/03/24 08:27:26 fgsch Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.90 2010/07/17 22:09:33 tg Exp $");
-
-/*
- * string expansion
- *
- * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
- * second pass: alternation ({,}), filename expansion (*?[]).
- */
-
-/* expansion generator state */
-typedef struct Expand {
- /* int type; */ /* see expand() */
- const char *str; /* string */
- union {
- const char **strv; /* string[] */
- struct shf *shf; /* file */
- } u; /* source */
- struct tbl *var; /* variable in ${var..} */
- short split; /* split "$@" / call waitlast $() */
-} Expand;
-
-#define XBASE 0 /* scanning original */
-#define XSUB 1 /* expanding ${} string */
-#define XARGSEP 2 /* ifs0 between "$*" */
-#define XARG 3 /* expanding $*, $@ */
-#define XCOM 4 /* expanding $() */
-#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
-#define XSUBMID 6 /* middle of expanding ${} */
-
-/* States used for field splitting */
-#define IFS_WORD 0 /* word has chars (or quotes) */
-#define IFS_WS 1 /* have seen IFS white-space */
-#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 char *trimsub(char *, char *, int);
-static void glob(char *, XPtrV *, int);
-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
-static void alt_expand(XPtrV *, char *, char *, char *, int);
-static size_t utflen(const char *);
-static void utfincptr(const char *, mksh_ari_t *);
-
-/* UTFMODE functions */
-static size_t
-utflen(const char *s)
-{
- size_t n;
-
- if (UTFMODE) {
- n = 0;
- while (*s) {
- s += utf_ptradj(s);
- ++n;
- }
- } else
- n = strlen(s);
- return (n);
-}
-
-static void
-utfincptr(const char *s, mksh_ari_t *lp)
-{
- const char *cp = s;
-
- while ((*lp)--)
- cp += utf_ptradj(cp);
- *lp = cp - s;
-}
-
-/* compile and expand word */
-char *
-substitute(const char *cp, int f)
-{
- struct source *s, *sold;
-
- sold = source;
- s = pushs(SWSTR, ATEMP);
- s->start = s->str = cp;
- source = s;
- if (yylex(ONEWORD) != LWORD)
- internal_errorf("substitute");
- source = sold;
- afree(s, ATEMP);
- return (evalstr(yylval.cp, f));
-}
-
-/*
- * expand arg-list
- */
-char **
-eval(const char **ap, int f)
-{
- XPtrV w;
-
- if (*ap == NULL) {
- union mksh_ccphack vap;
-
- vap.ro = ap;
- return (vap.rw);
- }
- XPinit(w, 32);
- XPput(w, NULL); /* space for shell name */
- while (*ap != NULL)
- expand(*ap++, &w, f);
- XPput(w, NULL);
- return ((char **)XPclose(w) + 1);
-}
-
-/*
- * expand string
- */
-char *
-evalstr(const char *cp, int f)
-{
- XPtrV w;
- char *dp = null;
-
- XPinit(w, 1);
- expand(cp, &w, f);
- if (XPsize(w))
- dp = *XPptrv(w);
- XPfree(w);
- return (dp);
-}
-
-/*
- * expand string - return only one component
- * used from iosetup to expand redirection files
- */
-char *
-evalonestr(const char *cp, int f)
-{
- XPtrV w;
- char *rv;
-
- XPinit(w, 1);
- expand(cp, &w, f);
- switch (XPsize(w)) {
- case 0:
- rv = null;
- break;
- case 1:
- rv = (char *) *XPptrv(w);
- break;
- default:
- rv = evalstr(cp, f&~DOGLOB);
- break;
- }
- XPfree(w);
- return (rv);
-}
-
-/* for nested substitution: ${var:=$var2} */
-typedef struct SubType {
- struct tbl *var; /* variable for ${var..} */
- struct SubType *prev; /* old type */
- struct SubType *next; /* poped type (to avoid re-allocating) */
- 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 ${..[+-=]..}) */
-} SubType;
-
-void
-expand(const char *cp, /* input word */
- XPtrV *wp, /* output words */
- int f) /* DO* flags */
-{
- int c = 0;
- int type; /* expansion type */
- int quote = 0; /* quoted */
- XString ds; /* destination string */
- char *dp; /* destination */
- const char *sp; /* source */
- int fdo, word; /* second pass flags; have word */
- int doblank; /* field splitting of parameter/command subst */
- Expand x = { /* expansion variables */
- NULL, { NULL }, NULL, 0
- };
- SubType st_head, *st;
- int newlines = 0; /* For trailing newlines in COMSUB */
- int saw_eq, tilde_ok;
- int make_magic;
- size_t len;
-
- if (cp == NULL)
- internal_errorf("expand(NULL)");
- /* for alias, readonly, set, typeset commands */
- if ((f & DOVACHECK) && is_wdvarassign(cp)) {
- f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
- f |= DOASNTILDE;
- }
- if (Flag(FNOGLOB))
- f &= ~DOGLOB;
- if (Flag(FMARKDIRS))
- f |= DOMARKDIRS;
- if (Flag(FBRACEEXPAND) && (f & DOGLOB))
- f |= DOBRACE_;
-
- Xinit(ds, dp, 128, ATEMP); /* init dest. string */
- type = XBASE;
- sp = cp;
- fdo = 0;
- saw_eq = 0;
- tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
- doblank = 0;
- make_magic = 0;
- word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
- /* clang doesn't know OSUBST comes before CSUBST */
- memset(&st_head, 0, sizeof(st_head));
- st = &st_head;
-
- while (1) {
- Xcheck(ds, dp);
-
- switch (type) {
- case XBASE: /* original prefixed string */
- c = *sp++;
- switch (c) {
- case EOS:
- c = 0;
- break;
- case CHAR:
- c = *sp++;
- break;
- case QCHAR:
- quote |= 2; /* temporary quote */
- c = *sp++;
- break;
- case OQUOTE:
- word = IFS_WORD;
- tilde_ok = 0;
- quote = 1;
- continue;
- case CQUOTE:
- quote = st->quotew;
- continue;
- case COMSUB:
- tilde_ok = 0;
- if (f & DONTRUNCOMMAND) {
- word = IFS_WORD;
- *dp++ = '$'; *dp++ = '(';
- while (*sp != '\0') {
- Xcheck(ds, dp);
- *dp++ = *sp++;
- }
- *dp++ = ')';
- } else {
- type = comsub(&x, sp);
- if (type == XCOM && (f&DOBLANK))
- doblank++;
- sp = strnul(sp) + 1;
- newlines = 0;
- }
- continue;
- case EXPRSUB:
- word = IFS_WORD;
- tilde_ok = 0;
- if (f & DONTRUNCOMMAND) {
- *dp++ = '$'; *dp++ = '('; *dp++ = '(';
- while (*sp != '\0') {
- Xcheck(ds, dp);
- *dp++ = *sp++;
- }
- *dp++ = ')'; *dp++ = ')';
- } else {
- struct tbl v;
- char *p;
-
- v.flag = DEFINED|ISSET|INTEGER;
- v.type = 10; /* not default */
- v.name[0] = '\0';
- v_evaluate(&v, substitute(sp, 0),
- KSH_UNWIND_ERROR, true);
- sp = strnul(sp) + 1;
- for (p = str_val(&v); *p; ) {
- Xcheck(ds, dp);
- *dp++ = *p++;
- }
- }
- continue;
- case OSUBST: { /* ${{#}var{:}[=+-?#%]word} */
- /* format is:
- * OSUBST [{x] plain-variable-part \0
- * compiled-word-part CSUBST [}x]
- * This is where all syntax checking gets done...
- */
- const char *varname = ++sp; /* skip the { or x (}) */
- int stype;
- int slen = 0;
-
- sp = cstrchr(sp, '\0') + 1; /* skip variable */
- type = varsub(&x, varname, sp, &stype, &slen);
- if (type < 0) {
- char *beg, *end, *str;
-
- unwind_substsyn:
- sp = varname - 2; /* restore sp */
- end = (beg = wdcopy(sp, ATEMP)) +
- (wdscan(sp, CSUBST) - sp);
- /* ({) the } or x is already skipped */
- if (end < wdscan(beg, EOS))
- *end = EOS;
- str = snptreef(NULL, 64, "%S", beg);
- afree(beg, ATEMP);
- errorf("%s: bad substitution", str);
- }
- if (f & DOBLANK)
- doblank++;
- tilde_ok = 0;
- if (type == XBASE) { /* expand? */
- if (!st->next) {
- SubType *newst;
-
- newst = alloc(sizeof(SubType), ATEMP);
- newst->next = NULL;
- newst->prev = st;
- st->next = newst;
- }
- st = st->next;
- st->stype = stype;
- st->base = Xsavepos(ds, dp);
- st->f = f;
- st->var = x.var;
- st->quotew = st->quotep = quote;
- /* skip qualifier(s) */
- if (stype)
- sp += slen;
- switch (stype & 0x7f) {
- case '0': {
- char *beg, *mid, *end, *stg;
- mksh_ari_t from = 0, num = -1, flen, finc = 0;
-
- beg = wdcopy(sp, ATEMP);
- mid = beg + (wdscan(sp, ADELIM) - sp);
- stg = beg + (wdscan(sp, CSUBST) - sp);
- if (mid >= stg)
- goto unwind_substsyn;
- mid[-2] = EOS;
- if (mid[-1] == /*{*/'}') {
- sp += mid - beg - 1;
- end = NULL;
- } else {
- end = mid +
- (wdscan(mid, ADELIM) - mid);
- if (end >= stg)
- goto unwind_substsyn;
- end[-2] = EOS;
- sp += end - beg - 1;
- }
- evaluate(substitute(stg = wdstrip(beg, false, false), 0),
- &from, KSH_UNWIND_ERROR, true);
- afree(stg, ATEMP);
- if (end) {
- evaluate(substitute(stg = wdstrip(mid, false, false), 0),
- &num, KSH_UNWIND_ERROR, true);
- afree(stg, ATEMP);
- }
- afree(beg, ATEMP);
- beg = str_val(st->var);
- flen = utflen(beg);
- if (from < 0) {
- if (-from < flen)
- finc = flen + from;
- } else
- finc = from < flen ? from : flen;
- if (UTFMODE)
- utfincptr(beg, &finc);
- beg += finc;
- flen = utflen(beg);
- if (num < 0 || num > flen)
- num = flen;
- if (UTFMODE)
- utfincptr(beg, &num);
- strndupx(x.str, beg, num, ATEMP);
- goto do_CSUBST;
- }
- case '/': {
- char *s, *p, *d, *sbeg, *end;
- char *pat, *rrep;
- char *tpat0, *tpat1, *tpat2;
-
- s = wdcopy(sp, ATEMP);
- p = s + (wdscan(sp, ADELIM) - sp);
- d = s + (wdscan(sp, CSUBST) - sp);
- if (p >= d)
- goto unwind_substsyn;
- p[-2] = EOS;
- if (p[-1] == /*{*/'}')
- d = NULL;
- else
- d[-2] = EOS;
- sp += (d ? d : p) - s - 1;
- tpat0 = wdstrip(s, true, true);
- pat = substitute(tpat0, 0);
- if (d) {
- d = wdstrip(p, true, false);
- rrep = substitute(d, 0);
- afree(d, ATEMP);
- } else
- rrep = null;
- afree(s, ATEMP);
- s = d = pat;
- while (*s)
- if (*s != '\\' ||
- s[1] == '%' ||
- s[1] == '#' ||
- s[1] == '\0' ||
- /* XXX really? */ s[1] == '\\' ||
- s[1] == '/')
- *d++ = *s++;
- else
- s++;
- *d = '\0';
- afree(tpat0, ATEMP);
-
- /* reject empty pattern */
- if (!*pat || gmatchx("", pat, false))
- goto no_repl;
-
- /* prepare string on which to work */
- strdupx(s, str_val(st->var), ATEMP);
- sbeg = s;
-
- /* first see if we have any match at all */
- tpat0 = pat;
- if (*pat == '#') {
- /* anchor at the beginning */
- tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC);
- tpat2 = tpat1;
- } else if (*pat == '%') {
- /* anchor at the end */
- tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0);
- tpat2 = tpat0;
- } else {
- /* float */
- tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC);
- tpat2 = tpat1 + 2;
- }
- again_repl:
- /* this would not be necessary if gmatchx would return
- * the start and end values of a match found, like re*
- */
- if (!gmatchx(sbeg, tpat1, false))
- goto end_repl;
- end = strnul(s);
- /* now anchor the beginning of the match */
- if (*pat != '#')
- while (sbeg <= end) {
- if (gmatchx(sbeg, tpat2, false))
- break;
- else
- sbeg++;
- }
- /* now anchor the end of the match */
- p = end;
- if (*pat != '%')
- while (p >= sbeg) {
- bool gotmatch;
-
- c = *p; *p = '\0';
- gotmatch = gmatchx(sbeg, tpat0, false);
- *p = c;
- if (gotmatch)
- break;
- p--;
- }
- strndupx(end, s, sbeg - s, ATEMP);
- d = shf_smprintf("%s%s%s", end, rrep, p);
- afree(end, ATEMP);
- sbeg = d + (sbeg - s) + strlen(rrep);
- afree(s, ATEMP);
- s = d;
- if (stype & 0x80)
- goto again_repl;
- end_repl:
- afree(tpat1, ATEMP);
- x.str = s;
- no_repl:
- afree(pat, ATEMP);
- if (rrep != null)
- afree(rrep, ATEMP);
- goto do_CSUBST;
- }
- case '#':
- case '%':
- /* ! DOBLANK,DOBRACE_,DOTILDE */
- f = DOPAT | (f&DONTRUNCOMMAND) |
- DOTEMP_;
- st->quotew = quote = 0;
- /* Prepend open pattern (so |
- * in a trim will work as
- * expected)
- */
- *dp++ = MAGIC;
- *dp++ = (char)('@' | 0x80);
- break;
- case '=':
- /* Enabling tilde expansion
- * after :s here is
- * non-standard ksh, but is
- * consistent with rules for
- * other assignments. Not
- * sure what POSIX thinks of
- * this.
- * Not doing tilde expansion
- * for integer variables is a
- * non-POSIX thing - makes
- * sense though, since ~ is
- * a arithmetic operator.
- */
- if (!(x.var->flag & INTEGER))
- f |= DOASNTILDE|DOTILDE;
- f |= DOTEMP_;
- /* These will be done after the
- * value has been assigned.
- */
- f &= ~(DOBLANK|DOGLOB|DOBRACE_);
- tilde_ok = 1;
- break;
- case '?':
- f &= ~DOBLANK;
- f |= DOTEMP_;
- /* FALLTHROUGH */
- default:
- /* Enable tilde expansion */
- tilde_ok = 1;
- f |= DOTILDE;
- }
- } else
- /* skip word */
- sp += wdscan(sp, CSUBST) - sp;
- continue;
- }
- case CSUBST: /* only get here if expanding word */
- do_CSUBST:
- sp++; /* ({) skip the } or x */
- tilde_ok = 0; /* in case of ${unset:-} */
- *dp = '\0';
- quote = st->quotep;
- f = st->f;
- if (f&DOBLANK)
- doblank--;
- switch (st->stype&0x7f) {
- case '#':
- case '%':
- /* Append end-pattern */
- *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
- dp = Xrestpos(ds, dp, st->base);
- /* Must use st->var since calling
- * global would break things
- * like x[i+=1].
- */
- x.str = trimsub(str_val(st->var),
- dp, st->stype);
- if (x.str[0] != '\0' || st->quotep)
- type = XSUB;
- else
- type = XNULLSUB;
- if (f&DOBLANK)
- doblank++;
- st = st->prev;
- continue;
- case '=':
- /* Restore our position and substitute
- * the value of st->var (may not be
- * the assigned value in the presence
- * of integer/right-adj/etc attributes).
- */
- dp = Xrestpos(ds, dp, st->base);
- /* Must use st->var since calling
- * global would cause with things
- * like x[i+=1] to be evaluated twice.
- */
- /* Note: not exported by FEXPORT
- * in AT&T ksh.
- */
- /* XXX POSIX says readonly is only
- * fatal for special builtins (setstr
- * does readonly check).
- */
- len = strlen(dp) + 1;
- setstr(st->var,
- debunk(alloc(len, ATEMP),
- dp, len), KSH_UNWIND_ERROR);
- x.str = str_val(st->var);
- type = XSUB;
- if (f&DOBLANK)
- doblank++;
- st = st->prev;
- continue;
- case '?': {
- char *s = Xrestpos(ds, dp, st->base);
-
- errorf("%s: %s", st->var->name,
- dp == s ?
- "parameter null or not set" :
- (debunk(s, s, strlen(s) + 1), s));
- }
- case '0':
- case '/':
- dp = Xrestpos(ds, dp, st->base);
- type = XSUB;
- if (f&DOBLANK)
- doblank++;
- st = st->prev;
- continue;
- }
- st = st->prev;
- type = XBASE;
- continue;
-
- case OPAT: /* open pattern: *(foo|bar) */
- /* Next char is the type of pattern */
- make_magic = 1;
- c = *sp++ + 0x80;
- break;
-
- case SPAT: /* pattern separator (|) */
- make_magic = 1;
- c = '|';
- break;
-
- case CPAT: /* close pattern */
- make_magic = 1;
- c = /*(*/ ')';
- break;
- }
- break;
-
- case XNULLSUB:
- /* Special case for "$@" (and "${foo[@]}") - no
- * word is generated if $# is 0 (unless there is
- * other stuff inside the quotes).
- */
- type = XBASE;
- if (f&DOBLANK) {
- doblank--;
- /* not really correct: x=; "$x$@" should
- * generate a null argument and
- * set A; "${@:+}" shouldn't.
- */
- if (dp == Xstring(ds, dp))
- word = IFS_WS;
- }
- continue;
-
- case XSUB:
- case XSUBMID:
- if ((c = *x.str++) == 0) {
- type = XBASE;
- if (f&DOBLANK)
- doblank--;
- continue;
- }
- break;
-
- case XARGSEP:
- type = XARG;
- quote = 1;
- case XARG:
- if ((c = *x.str++) == '\0') {
- /* force null words to be created so
- * set -- '' 2 ''; foo "$@" will do
- * the right thing
- */
- if (quote && x.split)
- word = IFS_WORD;
- if ((x.str = *x.u.strv++) == NULL) {
- type = XBASE;
- if (f&DOBLANK)
- doblank--;
- continue;
- }
- c = ifs0;
- if (c == 0) {
- if (quote && !x.split)
- continue;
- c = ' ';
- }
- if (quote && x.split) {
- /* terminate word for "$@" */
- type = XARGSEP;
- quote = 0;
- }
- }
- break;
-
- case XCOM:
- if (newlines) { /* Spit out saved NLs */
- c = '\n';
- --newlines;
- } else {
- while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
- if (c == '\n')
- /* Save newlines */
- newlines++;
- if (newlines && c != EOF) {
- shf_ungetc(c, x.u.shf);
- c = '\n';
- --newlines;
- }
- }
- if (c == EOF) {
- newlines = 0;
- shf_close(x.u.shf);
- if (x.split)
- subst_exstat = waitlast();
- type = XBASE;
- if (f&DOBLANK)
- doblank--;
- continue;
- }
- break;
- }
-
- /* check for end of word or IFS separation */
- if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
- !make_magic && ctype(c, C_IFS))) {
- /* How words are broken up:
- * | value of c
- * word | ws nws 0
- * -----------------------------------
- * IFS_WORD w/WS w/NWS w
- * IFS_WS -/WS w/NWS -
- * IFS_NWS -/NWS w/NWS w
- * (w means generate a word)
- * Note that IFS_NWS/0 generates a word (AT&T ksh
- * doesn't do this, but POSIX does).
- */
- if (word == IFS_WORD ||
- (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
- char *p;
-
- *dp++ = '\0';
- p = Xclose(ds, dp);
- if (fdo & DOBRACE_)
- /* also does globbing */
- alt_expand(wp, p, p,
- p + Xlength(ds, (dp - 1)),
- fdo | (f & DOMARKDIRS));
- else if (fdo & DOGLOB)
- glob(p, wp, 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;
- tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
- if (c != 0)
- Xinit(ds, dp, 128, ATEMP);
- }
- if (c == 0)
- return;
- if (word != IFS_NWS)
- word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
- } else {
- if (type == XSUB) {
- if (word == IFS_NWS &&
- Xlength(ds, dp) == 0) {
- char *p;
-
- *(p = alloc(1, ATEMP)) = '\0';
- XPput(*wp, p);
- }
- type = XSUBMID;
- }
-
- /* age tilde_ok info - ~ code tests second bit */
- tilde_ok <<= 1;
- /* mark any special second pass chars */
- if (!quote)
- switch (c) {
- case '[':
- case NOT:
- case '-':
- case ']':
- /* For character classes - doesn't hurt
- * to have magic !,-,]s outside of
- * [...] expressions.
- */
- if (f & (DOPAT | DOGLOB)) {
- fdo |= DOMAGIC_;
- if (c == '[')
- fdo |= f & DOGLOB;
- *dp++ = MAGIC;
- }
- break;
- case '*':
- case '?':
- if (f & (DOPAT | DOGLOB)) {
- fdo |= DOMAGIC_ | (f & DOGLOB);
- *dp++ = MAGIC;
- }
- break;
- case OBRACE:
- case ',':
- case CBRACE:
- if ((f & DOBRACE_) && (c == OBRACE ||
- (fdo & DOBRACE_))) {
- fdo |= DOBRACE_|DOMAGIC_;
- *dp++ = MAGIC;
- }
- break;
- case '=':
- /* Note first unquoted = for ~ */
- if (!(f & DOTEMP_) && !saw_eq &&
- (Flag(FBRACEEXPAND) ||
- (f & DOASNTILDE))) {
- saw_eq = 1;
- tilde_ok = 1;
- }
- break;
- case ':': /* : */
- /* Note unquoted : for ~ */
- if (!(f & DOTEMP_) && (f & DOASNTILDE))
- tilde_ok = 1;
- break;
- case '~':
- /* tilde_ok is reset whenever
- * any of ' " $( $(( ${ } are seen.
- * Note that tilde_ok must be preserved
- * through the sequence ${A=a=}~
- */
- if (type == XBASE &&
- (f & (DOTILDE|DOASNTILDE)) &&
- (tilde_ok & 2)) {
- const char *p;
- char *dp_x;
-
- dp_x = dp;
- p = maybe_expand_tilde(sp,
- &ds, &dp_x,
- f & DOASNTILDE);
- if (p) {
- if (dp != dp_x)
- word = IFS_WORD;
- dp = dp_x;
- sp = p;
- continue;
- }
- }
- break;
- }
- else
- quote &= ~2; /* undo temporary */
-
- if (make_magic) {
- make_magic = 0;
- fdo |= DOMAGIC_ | (f & DOGLOB);
- *dp++ = MAGIC;
- } else if (ISMAGIC(c)) {
- fdo |= DOMAGIC_;
- *dp++ = MAGIC;
- }
- *dp++ = c; /* save output char */
- word = IFS_WORD;
- }
- }
-}
-
-/*
- * Prepare to generate the string returned by ${} substitution.
- */
-static int
-varsub(Expand *xp, const char *sp, const char *word,
- int *stypep, /* becomes qualifier type */
- int *slenp) /* " " len (=, :=, etc.) valid iff *stypep != 0 */
-{
- int c;
- int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
- int stype; /* substitution type */
- int slen;
- const char *p;
- struct tbl *vp;
- bool zero_ok = false;
-
- if ((stype = sp[0]) == '\0') /* Bad variable name */
- return (-1);
-
- xp->var = NULL;
-
- /*-
- * ${#var}, string length (-U: characters, +U: octets) or array size
- * ${%var}, string width (-U: screen columns, +U: octets)
- */
- c = sp[1];
- if (stype == '%' && c == '\0')
- return (-1);
- if ((stype == '#' || stype == '%') && c != '\0') {
- /* Can't have any modifiers for ${#...} or ${%...} */
- if (*word != CSUBST)
- return (-1);
- sp++;
- /* Check for size of array */
- if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
- p[2] == ']') {
- int n = 0;
-
- if (stype != '#')
- return (-1);
- vp = global(arrayname(sp));
- if (vp->flag & (ISSET|ARRAY))
- zero_ok = true;
- for (; vp; vp = vp->u.array)
- if (vp->flag & ISSET)
- n++;
- c = n;
- } else if (c == '*' || c == '@') {
- if (stype != '#')
- return (-1);
- c = e->loc->argc;
- } else {
- p = str_val(global(sp));
- zero_ok = p != null;
- if (stype == '#')
- c = utflen(p);
- else {
- /* partial utf_mbswidth reimplementation */
- const char *s = p;
- unsigned int wc;
- size_t len;
- int cw;
-
- c = 0;
- while (*s) {
- if (!UTFMODE || (len = utf_mbtowc(&wc,
- s)) == (size_t)-1)
- /* not UTFMODE or not UTF-8 */
- wc = (unsigned char)(*s++);
- else
- /* UTFMODE and UTF-8 */
- s += len;
- /* wc == char or wchar at s++ */
- if ((cw = utf_wcwidth(wc)) == -1) {
- /* 646, 8859-1, 10646 C0/C1 */
- c = -1;
- break;
- }
- c += cw;
- }
- }
- }
- if (Flag(FNOUNSET) && c == 0 && !zero_ok)
- errorf("%s: parameter not set", sp);
- *stypep = 0; /* unqualified variable/string substitution */
- xp->str = shf_smprintf("%d", c);
- return (XSUB);
- }
-
- /* Check for qualifiers in word part */
- stype = 0;
- c = word[slen = 0] == CHAR ? word[1] : 0;
- if (c == ':') {
- slen += 2;
- stype = 0x80;
- c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
- }
- if (!stype && c == '/') {
- slen += 2;
- stype = c;
- if (word[slen] == ADELIM) {
- slen += 2;
- stype |= 0x80;
- }
- } else if (stype == 0x80 && (c == ' ' || c == '0')) {
- stype |= '0';
- } else if (ctype(c, C_SUBOP1)) {
- slen += 2;
- stype |= c;
- } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
- slen += 2;
- stype = c;
- if (word[slen + 0] == CHAR && c == word[slen + 1]) {
- stype |= 0x80;
- slen += 2;
- }
- } else if (stype) /* : is not ok */
- return (-1);
- if (!stype && *word != CSUBST)
- return (-1);
- *stypep = stype;
- *slenp = slen;
-
- c = sp[0];
- if (c == '*' || c == '@') {
- switch (stype & 0x7f) {
- case '=': /* can't assign to a vector */
- case '%': /* can't trim a vector (yet) */
- case '#':
- case '0':
- case '/':
- return (-1);
- }
- if (e->loc->argc == 0) {
- xp->str = null;
- xp->var = global(sp);
- state = c == '@' ? XNULLSUB : XSUB;
- } else {
- xp->u.strv = (const char **)e->loc->argv + 1;
- xp->str = *xp->u.strv++;
- xp->split = c == '@'; /* $@ */
- state = XARG;
- }
- zero_ok = true; /* POSIX 2009? */
- } else {
- if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
- p[2] == ']') {
- XPtrV wv;
-
- switch (stype & 0x7f) {
- case '=': /* can't assign to a vector */
- case '%': /* can't trim a vector (yet) */
- case '#':
- case '?':
- case '0':
- case '/':
- return (-1);
- }
- XPinit(wv, 32);
- if ((c = sp[0]) == '!')
- ++sp;
- vp = global(arrayname(sp));
- for (; vp; vp = vp->u.array) {
- if (!(vp->flag&ISSET))
- continue;
- XPput(wv, c == '!' ? shf_smprintf("%lu",
- arrayindex(vp)) :
- str_val(vp));
- }
- if (XPsize(wv) == 0) {
- xp->str = null;
- state = p[1] == '@' ? XNULLSUB : XSUB;
- XPfree(wv);
- } else {
- XPput(wv, 0);
- xp->u.strv = (const char **)XPptrv(wv);
- xp->str = *xp->u.strv++;
- xp->split = p[1] == '@'; /* ${foo[@]} */
- state = XARG;
- }
- } else {
- /* Can't assign things like $! or $1 */
- if ((stype & 0x7f) == '=' &&
- ctype(*sp, C_VAR1 | C_DIGIT))
- return (-1);
- if (*sp == '!' && sp[1]) {
- ++sp;
- xp->var = global(sp);
- if (cstrchr(sp, '[')) {
- if (xp->var->flag & ISSET)
- xp->str = shf_smprintf("%lu",
- arrayindex(xp->var));
- else
- xp->str = null;
- } else if (xp->var->flag & ISSET)
- xp->str = xp->var->name;
- else
- xp->str = "0"; /* ksh93 compat */
- } else {
- xp->var = global(sp);
- xp->str = str_val(xp->var);
- }
- state = XSUB;
- }
- }
-
- c = stype&0x7f;
- /* test the compiler's code generator */
- if (ctype(c, C_SUBOP2) || stype == (0x80 | '0') || c == '/' ||
- (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
- c == '=' || c == '-' || c == '?' : c == '+'))
- state = XBASE; /* expand word instead of variable value */
- if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
- (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
- errorf("%s: parameter not set", sp);
- return (state);
-}
-
-/*
- * Run the command in $(...) and read its output.
- */
-static int
-comsub(Expand *xp, const char *cp)
-{
- Source *s, *sold;
- struct op *t;
- struct shf *shf;
-
- s = pushs(SSTRING, ATEMP);
- s->start = s->str = cp;
- sold = source;
- t = compile(s);
- afree(s, ATEMP);
- source = sold;
-
- if (t == NULL)
- return (XBASE);
-
- if (t != NULL && t->type == TCOM && /* $(<file) */
- *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
- struct ioword *io = *t->ioact;
- char *name;
-
- if ((io->flag&IOTYPE) != IOREAD)
- errorf("funny $() command: %s",
- snptreef(NULL, 32, "%R", io));
- shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
- SHF_MAPHI|SHF_CLEXEC);
- if (shf == NULL)
- errorf("%s: cannot open $() input", name);
- xp->split = 0; /* no waitlast() */
- } else {
- int ofd1, pv[2];
- openpipe(pv);
- shf = shf_fdopen(pv[0], SHF_RD, NULL);
- ofd1 = savefd(1);
- if (pv[1] != 1) {
- ksh_dup2(pv[1], 1, false);
- close(pv[1]);
- }
- execute(t, XFORK|XXCOM|XPIPEO, NULL);
- restfd(1, ofd1);
- startlast();
- xp->split = 1; /* waitlast() */
- }
-
- xp->u.shf = shf;
- return (XCOM);
-}
-
-/*
- * perform #pattern and %pattern substitution in ${}
- */
-
-static char *
-trimsub(char *str, char *pat, int how)
-{
- char *end = strnul(str);
- char *p, c;
-
- switch (how & 0xFF) {
- case '#': /* shortest at beginning */
- for (p = str; p <= end; p += utf_ptradj(p)) {
- c = *p; *p = '\0';
- if (gmatchx(str, pat, false)) {
- *p = c;
- return (p);
- }
- *p = c;
- }
- break;
- case '#'|0x80: /* longest match at beginning */
- for (p = end; p >= str; p--) {
- c = *p; *p = '\0';
- if (gmatchx(str, pat, false)) {
- *p = c;
- return (p);
- }
- *p = c;
- }
- break;
- case '%': /* shortest match at end */
- p = end;
- while (p >= str) {
- if (gmatchx(p, pat, false))
- goto trimsub_match;
- if (UTFMODE) {
- char *op = p;
- while ((p-- > str) && ((*p & 0xC0) == 0x80))
- ;
- if ((p < str) || (p + utf_ptradj(p) != op))
- p = op - 1;
- } else
- --p;
- }
- break;
- case '%'|0x80: /* longest match at end */
- for (p = str; p <= end; p++)
- if (gmatchx(p, pat, false)) {
- trimsub_match:
- strndupx(end, str, p - str, ATEMP);
- return (end);
- }
- break;
- }
-
- return (str); /* no match, return string */
-}
-
-/*
- * glob
- * Name derived from V6's /etc/glob, the program that expanded filenames.
- */
-
-/* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
-static void
-glob(char *cp, XPtrV *wp, int markdirs)
-{
- int oldsize = XPsize(*wp);
-
- if (glob_str(cp, wp, markdirs) == 0)
- XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
- else
- qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize,
- sizeof(void *), xstrcmp);
-}
-
-#define GF_NONE 0
-#define GF_EXCHECK BIT(0) /* do existence check on file */
-#define GF_GLOBBED BIT(1) /* some globbing has been done */
-#define GF_MARKDIR BIT(2) /* add trailing / to directories */
-
-/* Apply file globbing to cp and store the matching files in wp. Returns
- * the number of matches found.
- */
-int
-glob_str(char *cp, XPtrV *wp, int markdirs)
-{
- int oldsize = XPsize(*wp);
- XString xs;
- char *xp;
-
- Xinit(xs, xp, 256, ATEMP);
- globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
- Xfree(xs, xp);
-
- return (XPsize(*wp) - oldsize);
-}
-
-static void
-globit(XString *xs, /* dest string */
- char **xpp, /* ptr to dest end */
- char *sp, /* source path */
- XPtrV *wp, /* output list */
- int check) /* GF_* flags */
-{
- char *np; /* next source component */
- char *xp = *xpp;
- char *se;
- char odirsep;
-
- /* This to allow long expansions to be interrupted */
- intrcheck();
-
- if (sp == NULL) { /* end of source path */
- /* We only need to check if the file exists if a pattern
- * is followed by a non-pattern (eg, foo*x/bar; no check
- * is needed for foo* since the match must exist) or if
- * any patterns were expanded and the markdirs option is set.
- * Symlinks make things a bit tricky...
- */
- if ((check & GF_EXCHECK) ||
- ((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
-#define stat_check() (stat_done ? stat_done : \
- (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
- ? -1 : 1))
- struct stat lstatb, statb;
- int stat_done = 0; /* -1: failed, 1 ok */
-
- if (lstat(Xstring(*xs, xp), &lstatb) < 0)
- return;
- /* special case for systems which strip trailing
- * slashes from regular files (eg, /etc/passwd/).
- * SunOS 4.1.3 does this...
- */
- if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
- xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&
- (!S_ISLNK(lstatb.st_mode) ||
- stat_check() < 0 || !S_ISDIR(statb.st_mode)))
- return;
- /* Possibly tack on a trailing / if there isn't already
- * one and if the file is a directory or a symlink to a
- * directory
- */
- if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
- xp > Xstring(*xs, xp) && xp[-1] != '/' &&
- (S_ISDIR(lstatb.st_mode) ||
- (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
- S_ISDIR(statb.st_mode)))) {
- *xp++ = '/';
- *xp = '\0';
- }
- }
- strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP);
- XPput(*wp, np);
- return;
- }
-
- if (xp > Xstring(*xs, xp))
- *xp++ = '/';
- while (*sp == '/') {
- Xcheck(*xs, xp);
- *xp++ = *sp++;
- }
- np = strchr(sp, '/');
- if (np != NULL) {
- se = np;
- odirsep = *np; /* don't assume '/', can be multiple kinds */
- *np++ = '\0';
- } else {
- odirsep = '\0'; /* keep gcc quiet */
- se = sp + strlen(sp);
- }
-
-
- /* Check if sp needs globbing - done to avoid pattern checks for strings
- * containing MAGIC characters, open [s without the matching close ],
- * etc. (otherwise opendir() will be called which may fail because the
- * directory isn't readable - if no globbing is needed, only execute
- * permission should be required (as per POSIX)).
- */
- if (!has_globbing(sp, se)) {
- XcheckN(*xs, xp, se - sp + 1);
- debunk(xp, sp, Xnleft(*xs, xp));
- xp += strlen(xp);
- *xpp = xp;
- globit(xs, xpp, np, wp, check);
- } else {
- DIR *dirp;
- struct dirent *d;
- char *name;
- int len;
- int prefix_len;
-
- /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
- *xp = '\0';
- prefix_len = Xlength(*xs, xp);
- dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
- if (dirp == NULL)
- goto Nodir;
- while ((d = readdir(dirp)) != NULL) {
- name = d->d_name;
- if (name[0] == '.' &&
- (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
- continue; /* always ignore . and .. */
- if ((*name == '.' && *sp != '.') ||
- !gmatchx(name, sp, true))
- continue;
-
- len = strlen(d->d_name) + 1;
- XcheckN(*xs, xp, len);
- memcpy(xp, name, len);
- *xpp = xp + len - 1;
- globit(xs, xpp, np, wp,
- (check & GF_MARKDIR) | GF_GLOBBED
- | (np ? GF_EXCHECK : GF_NONE));
- xp = Xstring(*xs, xp) + prefix_len;
- }
- closedir(dirp);
- Nodir:
- ;
- }
-
- if (np != NULL)
- *--np = odirsep;
-}
-
-/* remove MAGIC from string */
-char *
-debunk(char *dp, const char *sp, size_t dlen)
-{
- char *d;
- const char *s;
-
- if ((s = cstrchr(sp, MAGIC))) {
- if (s - sp >= (ssize_t)dlen)
- return (dp);
- memmove(dp, sp, s - sp);
- for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++)
- if (!ISMAGIC(*s) || !(*++s & 0x80) ||
- !vstrchr("*+?@! ", *s & 0x7f))
- *d++ = *s;
- else {
- /* extended pattern operators: *+?@! */
- if ((*s & 0x7f) != ' ')
- *d++ = *s & 0x7f;
- if (d - dp < (ssize_t)dlen)
- *d++ = '(';
- }
- *d = '\0';
- } else if (dp != sp)
- strlcpy(dp, sp, dlen);
- return (dp);
-}
-
-/* Check if p is an unquoted name, possibly followed by a / or :. If so
- * puts the expanded version in *dcp,dp and returns a pointer in p just
- * past the name, otherwise returns 0.
- */
-static const char *
-maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign)
-{
- XString ts;
- char *dp = *dpp;
- char *tp;
- const char *r;
-
- Xinit(ts, tp, 16, ATEMP);
- /* : only for DOASNTILDE form */
- while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))
- {
- Xcheck(ts, tp);
- *tp++ = p[1];
- p += 2;
- }
- *tp = '\0';
- r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ?
- tilde(Xstring(ts, tp)) : NULL;
- Xfree(ts, tp);
- if (r) {
- while (*r) {
- Xcheck(*dsp, dp);
- if (ISMAGIC(*r))
- *dp++ = MAGIC;
- *dp++ = *r++;
- }
- *dpp = dp;
- r = p;
- }
- return (r);
-}
-
-/*
- * tilde expansion
- *
- * based on a version by Arnold Robbins
- */
-
-static char *
-tilde(char *cp)
-{
- char *dp = null;
-
- if (cp[0] == '\0')
- dp = str_val(global("HOME"));
- else if (cp[0] == '+' && cp[1] == '\0')
- dp = str_val(global("PWD"));
- else if (cp[0] == '-' && cp[1] == '\0')
- dp = str_val(global("OLDPWD"));
-#ifndef MKSH_NOPWNAM
- else
- dp = homedir(cp);
-#endif
- /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
- return (dp == null ? NULL : dp);
-}
-
-#ifndef MKSH_NOPWNAM
-/*
- * map userid to user's home directory.
- * note that 4.3's getpw adds more than 6K to the shell,
- * and the YP version probably adds much more.
- * we might consider our own version of getpwnam() to keep the size down.
- */
-static char *
-homedir(char *name)
-{
- struct tbl *ap;
-
- ap = ktenter(&homedirs, name, hash(name));
- if (!(ap->flag & ISSET)) {
- struct passwd *pw;
-
- pw = getpwnam(name);
- if (pw == NULL)
- return (NULL);
- strdupx(ap->val.s, pw->pw_dir, APERM);
- ap->flag |= DEFINED|ISSET|ALLOC;
- }
- return (ap->val.s);
-}
-#endif
-
-static void
-alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
-{
- int count = 0;
- char *brace_start, *brace_end, *comma = NULL;
- char *field_start;
- char *p;
-
- /* search for open brace */
- for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
- ;
- brace_start = p;
-
- /* find matching close brace, if any */
- if (p) {
- comma = NULL;
- count = 1;
- for (p += 2; *p && count; p++) {
- if (ISMAGIC(*p)) {
- if (*++p == OBRACE)
- count++;
- else if (*p == CBRACE)
- --count;
- else if (*p == ',' && count == 1)
- comma = p;
- }
- }
- }
- /* no valid expansions... */
- if (!p || count != 0) {
- /* Note that given a{{b,c} we do not expand anything (this is
- * what AT&T ksh does. This may be changed to do the {b,c}
- * expansion. }
- */
- if (fdo & DOGLOB)
- glob(start, wp, fdo & DOMARKDIRS);
- else
- XPput(*wp, debunk(start, start, end - start));
- return;
- }
- brace_end = p;
- if (!comma) {
- alt_expand(wp, start, brace_end, end, fdo);
- return;
- }
-
- /* expand expression */
- field_start = brace_start + 2;
- count = 1;
- for (p = brace_start + 2; p != brace_end; p++) {
- if (ISMAGIC(*p)) {
- if (*++p == OBRACE)
- count++;
- else if ((*p == CBRACE && --count == 0) ||
- (*p == ',' && count == 1)) {
- char *news;
- int l1, l2, l3;
-
- l1 = brace_start - start;
- l2 = (p - 1) - field_start;
- l3 = end - brace_end;
- news = alloc(l1 + l2 + l3 + 1, ATEMP);
- memcpy(news, start, l1);
- memcpy(news + l1, field_start, l2);
- memcpy(news + l1 + l2, brace_end, l3);
- news[l1 + l2 + l3] = '\0';
- alt_expand(wp, news, news + l1,
- news + l1 + l2 + l3, fdo);
- field_start = p + 1;
- }
- }
- }
- return;
-}
diff --git a/mksh/src/exec.c b/mksh/src/exec.c
deleted file mode 100644
index 391321a..0000000
--- a/mksh/src/exec.c
+++ /dev/null
@@ -1,1518 +0,0 @@
-/* $OpenBSD: exec.c,v 1.49 2009/01/29 23:27:26 jaredy Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.75 2010/07/17 22:09:34 tg Exp $");
-
-#ifndef MKSH_DEFAULT_EXECSHELL
-#define MKSH_DEFAULT_EXECSHELL "/bin/sh"
-#endif
-
-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 iosetup(struct ioword *, struct tbl *);
-static int herein(const char *, int);
-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 *);
-
-/*
- * execute command tree
- */
-int
-execute(struct op *volatile t,
- volatile int flags, /* if XEXEC don't fork */
- volatile int * volatile xerrok)
-{
- int i;
- volatile int rv = 0, dummy = 0;
- int pv[2];
- const char ** volatile ap;
- char ** volatile up;
- const char *s, *cp;
- struct ioword **iowp;
- struct tbl *tp = NULL;
-
- if (t == NULL)
- return (0);
-
- /* Caller doesn't care if XERROK should propagate. */
- if (xerrok == NULL)
- xerrok = &dummy;
-
- if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
- /* run in sub-process */
- return (exchild(t, flags & ~XTIME, xerrok, -1));
-
- newenv(E_EXEC);
- if (trap)
- runtraps(0);
-
- if (t->type == TCOM) {
- /* Clear subst_exstat before argument expansion. Used by
- * null commands (see comexec() and c_eval()) and by c_set().
- */
- subst_exstat = 0;
-
- current_lineno = t->lineno; /* for $LINENO */
-
- /* POSIX says expand command words first, then redirections,
- * and assignments last..
- */
- up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);
- if (flags & XTIME)
- /* Allow option parsing (bizarre, but POSIX) */
- timex_hook(t, &up);
- ap = (const char **)up;
- if (Flag(FXTRACE) && ap[0]) {
- shf_fprintf(shl_out, "%s",
- substitute(str_val(global("PS4")), 0));
- for (i = 0; ap[i]; i++)
- shf_fprintf(shl_out, "%s%c", ap[i],
- ap[i + 1] ? ' ' : '\n');
- shf_flush(shl_out);
- }
- if (ap[0])
- tp = findcom(ap[0], FC_BI|FC_FUNC);
- }
- flags &= ~XTIME;
-
- if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) {
- e->savefd = alloc(NUFILE * sizeof(short), ATEMP);
- /* initialise to not redirected */
- memset(e->savefd, 0, NUFILE * sizeof(short));
- }
-
- /* do redirection, to be restored in quitenv() */
- if (t->ioact != NULL)
- for (iowp = t->ioact; *iowp != NULL; iowp++) {
- if (iosetup(*iowp, tp) < 0) {
- exstat = rv = 1;
- /* Redirection failures for special commands
- * cause (non-interactive) shell to exit.
- */
- if (tp && tp->type == CSHELL &&
- (tp->flag & SPEC_BI))
- errorfz();
- /* Deal with FERREXIT, quitenv(), etc. */
- goto Break;
- }
- }
-
- switch (t->type) {
- case TCOM:
- rv = comexec(t, tp, (const char **)ap, flags, xerrok);
- break;
-
- case TPAREN:
- rv = execute(t->left, flags | XFORK, xerrok);
- break;
-
- case TPIPE:
- flags |= XFORK;
- flags &= ~XEXEC;
- e->savefd[0] = savefd(0);
- e->savefd[1] = savefd(1);
- while (t->type == TPIPE) {
- openpipe(pv);
- ksh_dup2(pv[1], 1, false); /* stdout of curr */
- /**
- * Let exchild() close pv[0] in child
- * (if this isn't done, commands like
- * (: ; cat /etc/termcap) | sleep 1
- * will hang forever).
- */
- exchild(t->left, flags | XPIPEO | XCCLOSE,
- NULL, pv[0]);
- ksh_dup2(pv[0], 0, false); /* stdin of next */
- closepipe(pv);
- flags |= XPIPEI;
- t = t->right;
- }
- restfd(1, e->savefd[1]); /* stdout of last */
- e->savefd[1] = 0; /* no need to re-restore this */
- /* Let exchild() close 0 in parent, after fork, before wait */
- i = exchild(t, flags | XPCLOSE, xerrok, 0);
- if (!(flags&XBGND) && !(flags&XXCOM))
- rv = i;
- break;
-
- case TLIST:
- while (t->type == TLIST) {
- execute(t->left, flags & XERROK, NULL);
- t = t->right;
- }
- rv = execute(t, flags & XERROK, xerrok);
- break;
-
- case TCOPROC: {
- sigset_t omask;
-
- /* Block sigchild as we are using things changed in the
- * signal handler
- */
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
- e->type = E_ERRH;
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- quitenv(NULL);
- unwind(i);
- /* NOTREACHED */
- }
- /* Already have a (live) co-process? */
- if (coproc.job && coproc.write >= 0)
- errorf("coprocess already exists");
-
- /* Can we re-use the existing co-process pipe? */
- coproc_cleanup(true);
-
- /* do this before opening pipes, in case these fail */
- e->savefd[0] = savefd(0);
- e->savefd[1] = savefd(1);
-
- openpipe(pv);
- if (pv[0] != 0) {
- ksh_dup2(pv[0], 0, false);
- close(pv[0]);
- }
- coproc.write = pv[1];
- coproc.job = NULL;
-
- if (coproc.readw >= 0)
- ksh_dup2(coproc.readw, 1, false);
- else {
- openpipe(pv);
- coproc.read = pv[0];
- ksh_dup2(pv[1], 1, false);
- coproc.readw = pv[1]; /* closed before first read */
- coproc.njobs = 0;
- /* create new coprocess id */
- ++coproc.id;
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
- e->type = E_EXEC; /* no more need for error handler */
-
- /* exchild() closes coproc.* in child after fork,
- * will also increment coproc.njobs when the
- * job is actually created.
- */
- flags &= ~XEXEC;
- exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE,
- NULL, coproc.readw);
- break;
- }
-
- case TASYNC:
- /* XXX non-optimal, I think - "(foo &)", forks for (),
- * forks again for async... parent should optimise
- * this to "foo &"...
- */
- rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok);
- break;
-
- case TOR:
- case TAND:
- rv = execute(t->left, XERROK, xerrok);
- if ((rv == 0) == (t->type == TAND))
- rv = execute(t->right, XERROK, xerrok);
- flags |= XERROK;
- if (xerrok)
- *xerrok = 1;
- break;
-
- case TBANG:
- rv = !execute(t->right, XERROK, xerrok);
- flags |= XERROK;
- if (xerrok)
- *xerrok = 1;
- break;
-
- case TDBRACKET: {
- Test_env te;
-
- te.flags = TEF_DBRACKET;
- te.pos.wp = t->args;
- te.isa = dbteste_isa;
- te.getopnd = dbteste_getopnd;
- te.eval = test_eval;
- te.error = dbteste_error;
-
- rv = test_parse(&te);
- break;
- }
-
- case TFOR:
- case TSELECT: {
- volatile bool is_first = true;
- ap = (t->vars == NULL) ? e->loc->argv + 1 :
- (const char **)eval((const char **)t->vars,
- DOBLANK | DOGLOB | DOTILDE);
- e->type = E_LOOP;
- while (1) {
- i = sigsetjmp(e->jbuf, 0);
- if (!i)
- break;
- if ((e->flags&EF_BRKCONT_PASS) ||
- (i != LBREAK && i != LCONTIN)) {
- quitenv(NULL);
- unwind(i);
- } else if (i == LBREAK) {
- rv = 0;
- goto Break;
- }
- }
- rv = 0; /* in case of a continue */
- if (t->type == TFOR) {
- while (*ap != NULL) {
- setstr(global(t->str), *ap++, KSH_UNWIND_ERROR);
- rv = execute(t->left, flags & XERROK, xerrok);
- }
- } else { /* TSELECT */
- for (;;) {
- if (!(cp = do_selectargs(ap, is_first))) {
- rv = 1;
- break;
- }
- is_first = false;
- setstr(global(t->str), cp, KSH_UNWIND_ERROR);
- execute(t->left, flags & XERROK, xerrok);
- }
- }
- break;
- }
-
- case TWHILE:
- case TUNTIL:
- e->type = E_LOOP;
- while (1) {
- i = sigsetjmp(e->jbuf, 0);
- if (!i)
- break;
- if ((e->flags&EF_BRKCONT_PASS) ||
- (i != LBREAK && i != LCONTIN)) {
- quitenv(NULL);
- unwind(i);
- } else if (i == LBREAK) {
- rv = 0;
- goto Break;
- }
- }
- rv = 0; /* in case of a continue */
- while ((execute(t->left, XERROK, NULL) == 0) ==
- (t->type == TWHILE))
- rv = execute(t->right, flags & XERROK, xerrok);
- break;
-
- case TIF:
- case TELIF:
- if (t->right == NULL)
- break; /* should be error */
- rv = execute(t->left, XERROK, NULL) == 0 ?
- execute(t->right->left, flags & XERROK, xerrok) :
- execute(t->right->right, flags & XERROK, xerrok);
- break;
-
- case TCASE:
- cp = evalstr(t->str, DOTILDE);
- for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
- for (ap = (const char **)t->vars; *ap; ap++)
- if ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
- gmatchx(cp, s, false))
- goto Found;
- break;
- Found:
- rv = execute(t->left, flags & XERROK, xerrok);
- break;
-
- case TBRACE:
- rv = execute(t->left, flags & XERROK, xerrok);
- break;
-
- case TFUNCT:
- rv = define(t->str, t);
- break;
-
- case TTIME:
- /* Clear XEXEC so nested execute() call doesn't exit
- * (allows "ls -l | time grep foo").
- */
- rv = timex(t, flags & ~XEXEC, xerrok);
- break;
-
- case TEXEC: /* an eval'd TCOM */
- s = t->args[0];
- up = makenv();
- restoresigs();
- cleanup_proc_env();
- {
- union mksh_ccphack cargs;
-
- cargs.ro = t->args;
- execve(t->str, cargs.rw, up);
- rv = errno;
- }
- if (rv == ENOEXEC)
- scriptexec(t, (const char **)up);
- else
- errorf("%s: %s", s, strerror(rv));
- }
- Break:
- exstat = rv;
-
- quitenv(NULL); /* restores IO */
- if ((flags&XEXEC))
- unwind(LEXIT); /* exit child */
- if (rv != 0 && !(flags & XERROK) &&
- (xerrok == NULL || !*xerrok)) {
- trapsig(SIGERR_);
- if (Flag(FERREXIT))
- unwind(LERROR);
- }
- return (rv);
-}
-
-/*
- * execute simple command
- */
-
-static int
-comexec(struct op *t, struct tbl *volatile tp, const char **ap,
- volatile int flags, volatile int *xerrok)
-{
- int i;
- volatile int rv = 0;
- const char *cp;
- const char **lastp;
- static struct op texec; /* Must be static (XXX but why?) */
- int type_flags;
- int keepasn_ok;
- int fcflags = FC_BI|FC_FUNC|FC_PATH;
- bool bourne_function_call = false;
- struct block *l_expand, *l_assign;
-
- /* snag the last argument for $_ XXX not the same as AT&T ksh,
- * which only seems to set $_ after a newline (but not in
- * functions/dot scripts, but in interactive and script) -
- * perhaps save last arg here and set it in shell()?.
- */
- if (Flag(FTALKING) && *(lastp = ap)) {
- while (*++lastp)
- ;
- /* setstr() can't fail here */
- setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp,
- KSH_RETURN_ERROR);
- }
-
- /* Deal with the shell builtins builtin, exec and command since
- * they can be followed by other commands. This must be done before
- * we know if we should create a local block which must be done
- * before we can do a path search (in case the assignments change
- * PATH).
- * Odd cases:
- * FOO=bar exec >/dev/null FOO is kept but not exported
- * FOO=bar exec foobar FOO is exported
- * FOO=bar command exec >/dev/null FOO is neither kept nor exported
- * FOO=bar command FOO is neither kept nor exported
- * PATH=... foobar use new PATH in foobar search
- */
- keepasn_ok = 1;
- while (tp && tp->type == CSHELL) {
- fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */
- if (tp->val.f == c_builtin) {
- if ((cp = *++ap) == NULL) {
- tp = NULL;
- break;
- }
- tp = findcom(cp, FC_BI);
- if (tp == NULL)
- errorf("builtin: %s: not a builtin", cp);
- continue;
- } else if (tp->val.f == c_exec) {
- if (ap[1] == NULL)
- break;
- ap++;
- flags |= XEXEC;
- } else if (tp->val.f == c_command) {
- int optc, saw_p = 0;
-
- /* Ugly dealing with options in two places (here and
- * in c_command(), but such is life)
- */
- ksh_getopt_reset(&builtin_opt, 0);
- while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p')
- saw_p = 1;
- if (optc != EOF)
- break; /* command -vV or something */
- /* don't look for functions */
- fcflags = FC_BI|FC_PATH;
- if (saw_p) {
- if (Flag(FRESTRICTED)) {
- warningf(true,
- "command -p: restricted");
- rv = 1;
- goto Leave;
- }
- fcflags |= FC_DEFPATH;
- }
- ap += builtin_opt.optind;
- /* POSIX says special builtins lose their status
- * if accessed using command.
- */
- keepasn_ok = 0;
- if (!ap[0]) {
- /* ensure command with no args exits with 0 */
- subst_exstat = 0;
- break;
- }
- } else
- break;
- tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
- }
- l_expand = e->loc;
- if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
- type_flags = 0;
- else {
- /* create new variable/function block */
- newblock();
- /* ksh functions don't keep assignments, POSIX functions do. */
- if (keepasn_ok && tp && tp->type == CFUNC &&
- !(tp->flag & FKSH)) {
- bourne_function_call = true;
- type_flags = EXPORT;
- } else
- type_flags = LOCAL|LOCAL_COPY|EXPORT;
- }
- l_assign = e->loc;
- if (Flag(FEXPORT))
- type_flags |= EXPORT;
- for (i = 0; t->vars[i]; i++) {
- /* do NOT lookup in the new var/fn block just created */
- e->loc = l_expand;
- cp = evalstr(t->vars[i], DOASNTILDE);
- e->loc = l_assign;
- /* but assign in there as usual */
-
- if (Flag(FXTRACE)) {
- if (i == 0)
- shf_fprintf(shl_out, "%s",
- substitute(str_val(global("PS4")), 0));
- shf_fprintf(shl_out, "%s%c", cp,
- t->vars[i + 1] ? ' ' : '\n');
- if (!t->vars[i + 1])
- shf_flush(shl_out);
- }
- typeset(cp, type_flags, 0, 0, 0);
- if (bourne_function_call && !(type_flags & EXPORT))
- typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0);
- }
-
- if ((cp = *ap) == NULL) {
- rv = subst_exstat;
- goto Leave;
- } else if (!tp) {
- if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
- warningf(true, "%s: restricted", cp);
- rv = 1;
- goto Leave;
- }
- tp = findcom(cp, fcflags);
- }
-
- switch (tp->type) {
- case CSHELL: /* shell built-in */
- rv = call_builtin(tp, (const char **)ap);
- break;
-
- case CFUNC: { /* function call */
- volatile unsigned char old_xflag;
- volatile Tflag old_inuse;
- const char *volatile old_kshname;
-
- if (!(tp->flag & ISSET)) {
- struct tbl *ftp;
-
- if (!tp->u.fpath) {
- if (tp->u2.errno_) {
- warningf(true,
- "%s: can't find function "
- "definition file - %s",
- cp, strerror(tp->u2.errno_));
- rv = 126;
- } else {
- warningf(true,
- "%s: can't find function "
- "definition file", cp);
- rv = 127;
- }
- break;
- }
- if (include(tp->u.fpath, 0, NULL, 0) < 0) {
- rv = errno;
- warningf(true,
- "%s: can't open function definition file %s - %s",
- cp, tp->u.fpath, strerror(rv));
- rv = 127;
- break;
- }
- if (!(ftp = findfunc(cp, hash(cp), false)) ||
- !(ftp->flag & ISSET)) {
- warningf(true,
- "%s: function not defined by %s",
- cp, tp->u.fpath);
- rv = 127;
- break;
- }
- tp = ftp;
- }
-
- /* ksh functions set $0 to function name, POSIX functions leave
- * $0 unchanged.
- */
- old_kshname = kshname;
- if (tp->flag & FKSH)
- kshname = ap[0];
- else
- ap[0] = kshname;
- e->loc->argv = ap;
- for (i = 0; *ap++ != NULL; i++)
- ;
- e->loc->argc = i - 1;
- /* ksh-style functions handle getopts sanely,
- * Bourne/POSIX functions are insane...
- */
- if (tp->flag & FKSH) {
- e->loc->flags |= BF_DOGETOPTS;
- e->loc->getopts_state = user_opt;
- getopts_reset(1);
- }
-
- old_xflag = Flag(FXTRACE);
- Flag(FXTRACE) = tp->flag & TRACE ? 1 : 0;
-
- old_inuse = tp->flag & FINUSE;
- 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);
- i = LRETURN;
- }
- kshname = old_kshname;
- Flag(FXTRACE) = old_xflag;
- tp->flag = (tp->flag & ~FINUSE) | old_inuse;
- /* Were we deleted while executing? If so, free the execution
- * tree. todo: Unfortunately, the table entry is never re-used
- * until the lookup table is expanded.
- */
- if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
- if (tp->flag & ALLOC) {
- tp->flag &= ~ALLOC;
- tfree(tp->val.t, tp->areap);
- }
- tp->flag = 0;
- }
- switch (i) {
- case LRETURN:
- case LERROR:
- rv = exstat;
- break;
- case LINTR:
- case LEXIT:
- case LLEAVE:
- case LSHELL:
- quitenv(NULL);
- unwind(i);
- /* NOTREACHED */
- default:
- quitenv(NULL);
- internal_errorf("CFUNC %d", i);
- }
- break;
- }
-
- case CEXEC: /* executable command */
- case CTALIAS: /* tracked alias */
- if (!(tp->flag&ISSET)) {
- /* errno_ will be set if the named command was found
- * but could not be executed (permissions, no execute
- * bit, directory, etc). Print out a (hopefully)
- * useful error message and set the exit status to 126.
- */
- if (tp->u2.errno_) {
- warningf(true, "%s: cannot execute - %s", cp,
- strerror(tp->u2.errno_));
- rv = 126; /* POSIX */
- } else {
- warningf(true, "%s: not found", cp);
- rv = 127;
- }
- break;
- }
-
- /* set $_ to programme's full path */
- /* setstr() can't fail here */
- setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0),
- tp->val.s, KSH_RETURN_ERROR);
-
- if (flags&XEXEC) {
- j_exit();
- if (!(flags&XBGND)
-#ifndef MKSH_UNEMPLOYED
- || Flag(FMONITOR)
-#endif
- ) {
- setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
- setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
- }
- }
-
- /* to fork we set up a TEXEC node and call execute */
- texec.type = TEXEC;
- texec.left = t; /* for tprint */
- texec.str = tp->val.s;
- texec.args = ap;
- rv = exchild(&texec, flags, xerrok, -1);
- break;
- }
- Leave:
- if (flags & XEXEC) {
- exstat = rv;
- unwind(LLEAVE);
- }
- return (rv);
-}
-
-static void
-scriptexec(struct op *tp, const char **ap)
-{
- const char *sh;
-#ifndef MKSH_SMALL
- unsigned char *cp;
- char buf[64]; /* 64 == MAXINTERP in MirBSD <sys/param.h> */
- int fd;
-#endif
- union mksh_ccphack args, cap;
-
- sh = str_val(global("EXECSHELL"));
- if (sh && *sh)
- sh = search(sh, path, X_OK, NULL);
- if (!sh || !*sh)
- sh = MKSH_DEFAULT_EXECSHELL;
-
- *tp->args-- = tp->str;
-
-#ifndef MKSH_SMALL
- if ((fd = open(tp->str, O_RDONLY)) >= 0) {
- /* read first MAXINTERP octets from file */
- if (read(fd, buf, sizeof(buf)) <= 0)
- /* read error -> no good */
- buf[0] = '\0';
- close(fd);
- /* scan for newline (or CR) or NUL _before_ end of buffer */
- cp = (unsigned char *)buf;
- while ((char *)cp < (buf + sizeof(buf)))
- if (*cp == '\0' || *cp == '\n' || *cp == '\r') {
- *cp = '\0';
- break;
- } else
- ++cp;
- /* if the shebang line is longer than MAXINTERP, bail out */
- if ((char *)cp >= (buf + sizeof(buf)))
- goto noshebang;
- /* skip UTF-8 Byte Order Mark, if present */
- cp = (unsigned char *)buf;
- if ((cp[0] == 0xEF) && (cp[1] == 0xBB) && (cp[2] == 0xBF))
- cp += 3;
- /* bail out if read error (above) or no shebang */
- if ((cp[0] != '#') || (cp[1] != '!'))
- goto noshebang;
- cp += 2;
- /* skip whitespace before shell name */
- while (*cp == ' ' || *cp == '\t')
- ++cp;
- /* just whitespace on the line? */
- if (*cp == '\0')
- goto noshebang;
- /* no, we actually found an interpreter name */
- sh = (char *)cp;
- /* look for end of shell/interpreter name */
- while (*cp != ' ' && *cp != '\t' && *cp != '\0')
- ++cp;
- /* any arguments? */
- if (*cp) {
- *cp++ = '\0';
- /* skip spaces before arguments */
- while (*cp == ' ' || *cp == '\t')
- ++cp;
- /* pass it all in ONE argument (historic reasons) */
- if (*cp)
- *tp->args-- = (char *)cp;
- }
- noshebang:
- fd = buf[0] << 8 | buf[1];
- if ((fd == /* OMAGIC */ 0407) ||
- (fd == /* NMAGIC */ 0410) ||
- (fd == /* ZMAGIC */ 0413) ||
- (fd == /* QMAGIC */ 0314) ||
- (fd == /* ECOFF_I386 */ 0x4C01) ||
- (fd == /* ECOFF_M68K */ 0x0150 || fd == 0x5001) ||
- (fd == /* ECOFF_SH */ 0x0500 || fd == 0x0005) ||
- (fd == 0x7F45 && buf[2] == 'L' && buf[3] == 'F') ||
- (fd == /* "MZ" */ 0x4D5A) ||
- (fd == /* gzip */ 0x1F8B))
- errorf("%s: not executable: magic %04X", tp->str, fd);
- }
-#endif
- args.ro = tp->args;
- *args.ro = sh;
-
- cap.ro = 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));
-}
-
-int
-shcomexec(const char **wp)
-{
- struct tbl *tp;
-
- tp = ktsearch(&builtins, *wp, hash(*wp));
- if (tp == NULL)
- internal_errorf("shcomexec: %s", *wp);
- return (call_builtin(tp, wp));
-}
-
-/*
- * Search function tables for a function. If create set, a table entry
- * is created if none is found.
- */
-struct tbl *
-findfunc(const char *name, uint32_t h, bool create)
-{
- struct block *l;
- struct tbl *tp = NULL;
-
- for (l = e->loc; l; l = l->next) {
- tp = ktsearch(&l->funs, name, h);
- if (tp)
- break;
- if (!l->next && create) {
- tp = ktenter(&l->funs, name, h);
- tp->flag = DEFINED;
- tp->type = CFUNC;
- tp->val.t = NULL;
- break;
- }
- }
- return (tp);
-}
-
-/*
- * define function. Returns 1 if function is being undefined (t == 0) and
- * function did not exist, returns 0 otherwise.
- */
-int
-define(const char *name, struct op *t)
-{
- struct tbl *tp;
- bool was_set = false;
-
- while (1) {
- tp = findfunc(name, hash(name), true);
-
- if (tp->flag & ISSET)
- was_set = true;
- /* If this function is currently being executed, we zap this
- * table entry so findfunc() won't see it
- */
- if (tp->flag & FINUSE) {
- tp->name[0] = '\0';
- tp->flag &= ~DEFINED; /* ensure it won't be found */
- tp->flag |= FDELETE;
- } else
- break;
- }
-
- if (tp->flag & ALLOC) {
- tp->flag &= ~(ISSET|ALLOC);
- tfree(tp->val.t, tp->areap);
- }
-
- if (t == NULL) { /* undefine */
- ktdelete(tp);
- return (was_set ? 0 : 1);
- }
-
- tp->val.t = tcopy(t->left, tp->areap);
- tp->flag |= (ISSET|ALLOC);
- if (t->u.ksh_func)
- tp->flag |= FKSH;
-
- return (0);
-}
-
-/*
- * add builtin
- */
-void
-builtin(const char *name, int (*func) (const char **))
-{
- struct tbl *tp;
- Tflag flag;
-
- /* see if any flags should be set for this builtin */
- for (flag = 0; ; name++) {
- if (*name == '=') /* command does variable assignment */
- flag |= KEEPASN;
- else if (*name == '*') /* POSIX special builtin */
- flag |= SPEC_BI;
- else if (*name == '+') /* POSIX regular builtin */
- flag |= REG_BI;
- else
- break;
- }
-
- tp = ktenter(&builtins, name, hash(name));
- tp->flag = DEFINED | flag;
- tp->type = CSHELL;
- tp->val.f = func;
-}
-
-/*
- * find command
- * either function, hashed command, or built-in (in that order)
- */
-struct tbl *
-findcom(const char *name, int flags)
-{
- static struct tbl temp;
- uint32_t h = hash(name);
- struct tbl *tp = NULL, *tbi;
- unsigned char insert = Flag(FTRACKALL); /* insert if not found */
- char *fpath; /* for function autoloading */
- union mksh_cchack npath;
-
- if (vstrchr(name, '/')) {
- insert = 0;
- /* prevent FPATH search below */
- flags &= ~FC_FUNC;
- goto Search;
- }
- tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL;
- /* POSIX says special builtins first, then functions, then
- * POSIX regular builtins, then search path...
- */
- if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI))
- tp = tbi;
- if (!tp && (flags & FC_FUNC)) {
- tp = findfunc(name, h, false);
- if (tp && !(tp->flag & ISSET)) {
- if ((fpath = str_val(global("FPATH"))) == null) {
- tp->u.fpath = NULL;
- tp->u2.errno_ = 0;
- } else
- tp->u.fpath = search(name, fpath, R_OK,
- &tp->u2.errno_);
- }
- }
- if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI))
- tp = tbi;
- if (!tp && (flags & FC_UNREGBI) && tbi)
- tp = tbi;
- if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) {
- tp = ktsearch(&taliases, name, h);
- if (tp && (tp->flag & ISSET) && access(tp->val.s, X_OK) != 0) {
- if (tp->flag & ALLOC) {
- tp->flag &= ~ALLOC;
- afree(tp->val.s, APERM);
- }
- tp->flag &= ~ISSET;
- }
- }
-
- Search:
- if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) &&
- (flags & FC_PATH)) {
- if (!tp) {
- if (insert && !(flags & FC_DEFPATH)) {
- tp = ktenter(&taliases, name, h);
- tp->type = CTALIAS;
- } else {
- tp = &temp;
- tp->type = CEXEC;
- }
- tp->flag = DEFINED; /* make ~ISSET */
- }
- npath.ro = search(name, flags & FC_DEFPATH ? def_path : path,
- X_OK, &tp->u2.errno_);
- if (npath.ro) {
- strdupx(tp->val.s, npath.ro, APERM);
- if (npath.ro != name)
- afree(npath.rw, ATEMP);
- tp->flag |= ISSET|ALLOC;
- } else if ((flags & FC_FUNC) &&
- (fpath = str_val(global("FPATH"))) != null &&
- (npath.ro = search(name, fpath, R_OK,
- &tp->u2.errno_)) != NULL) {
- /* An undocumented feature of AT&T ksh is that it
- * searches FPATH if a command is not found, even
- * if the command hasn't been set up as an autoloaded
- * function (ie, no typeset -uf).
- */
- tp = &temp;
- tp->type = CFUNC;
- tp->flag = DEFINED; /* make ~ISSET */
- tp->u.fpath = npath.ro;
- }
- }
- return (tp);
-}
-
-/*
- * flush executable commands with relative paths
- */
-void
-flushcom(int all) /* just relative or all */
-{
- struct tbl *tp;
- struct tstate ts;
-
- for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; )
- if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) {
- if (tp->flag&ALLOC) {
- tp->flag &= ~(ALLOC|ISSET);
- afree(tp->val.s, APERM);
- }
- tp->flag &= ~ISSET;
- }
-}
-
-/* Check if path is something we want to find. Returns -1 for failure. */
-int
-search_access(const char *lpath, int mode,
- int *errnop) /* set if candidate found, but not suitable */
-{
- int ret, err = 0;
- struct stat statb;
-
- if (stat(lpath, &statb) < 0)
- return (-1);
- ret = access(lpath, mode);
- if (ret < 0)
- err = errno; /* File exists, but we can't access it */
- else if (mode == X_OK && (!S_ISREG(statb.st_mode) ||
- !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
- /* This 'cause access() says root can execute everything */
- ret = -1;
- err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES;
- }
- if (err && errnop && !*errnop)
- *errnop = err;
- return (ret);
-}
-
-/*
- * search for command with PATH
- */
-const char *
-search(const char *name, const char *lpath,
- int mode, /* R_OK or X_OK */
- int *errnop) /* set if candidate found, but not suitable */
-{
- const char *sp, *p;
- char *xp;
- XString xs;
- int namelen;
-
- if (errnop)
- *errnop = 0;
- if (vstrchr(name, '/')) {
- if (search_access(name, mode, errnop) == 0)
- return (name);
- return (NULL);
- }
-
- namelen = strlen(name) + 1;
- Xinit(xs, xp, 128, ATEMP);
-
- sp = lpath;
- while (sp != NULL) {
- xp = Xstring(xs, xp);
- if (!(p = cstrchr(sp, ':')))
- p = sp + strlen(sp);
- if (p != sp) {
- XcheckN(xs, xp, p - sp);
- memcpy(xp, sp, p - sp);
- xp += p - sp;
- *xp++ = '/';
- }
- sp = p;
- XcheckN(xs, xp, namelen);
- memcpy(xp, name, namelen);
- if (search_access(Xstring(xs, xp), mode, errnop) == 0)
- return (Xclose(xs, xp + namelen));
- if (*sp++ == '\0')
- sp = NULL;
- }
- Xfree(xs, xp);
- return (NULL);
-}
-
-static int
-call_builtin(struct tbl *tp, const char **wp)
-{
- int rv;
-
- builtin_argv0 = wp[0];
- builtin_flag = tp->flag;
- shf_reopen(1, SHF_WR, shl_stdout);
- shl_stdout_ok = 1;
- ksh_getopt_reset(&builtin_opt, GF_ERROR);
- rv = (*tp->val.f)(wp);
- shf_flush(shl_stdout);
- shl_stdout_ok = 0;
- builtin_flag = 0;
- builtin_argv0 = NULL;
- return (rv);
-}
-
-/*
- * set up redirection, saving old fds in e->savefd
- */
-static int
-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;
- struct ioword iotmp;
- struct stat statb;
-
- if (iotype != IOHERE)
- cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));
-
- /* Used for tracing and error messages to print expanded cp */
- iotmp = *iop;
- iotmp.name = (iotype == IOHERE) ? NULL : cp;
- iotmp.flag |= IONAMEXP;
-
- if (Flag(FXTRACE))
- shellf("%s%s\n",
- substitute(str_val(global("PS4")), 0),
- snptreef(NULL, 32, "%R", &iotmp));
-
- switch (iotype) {
- case IOREAD:
- flags = O_RDONLY;
- break;
-
- case IOCAT:
- flags = O_WRONLY | O_APPEND | O_CREAT;
- break;
-
- case IOWRITE:
- flags = O_WRONLY | O_CREAT | O_TRUNC;
- /* The stat() is here to allow redirections to
- * things like /dev/null without error.
- */
- if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
- (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
- flags |= O_EXCL;
- break;
-
- case IORDWR:
- flags = O_RDWR | O_CREAT;
- break;
-
- case IOHERE:
- do_open = 0;
- /* herein() returns -2 if error has been printed */
- u = herein(iop->heredoc, iop->flag & IOEVAL);
- /* cp may have wrong name */
- break;
-
- case IODUP: {
- const char *emsg;
-
- do_open = 0;
- if (*cp == '-' && !cp[1]) {
- u = 1009; /* prevent error return below */
- do_close = 1;
- } else if ((u = check_fd(cp,
- X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
- &emsg)) < 0) {
- warningf(true, "%s: %s",
- snptreef(NULL, 32, "%R", &iotmp), emsg);
- return (-1);
- }
- if (u == iop->unit)
- return (0); /* "dup from" == "dup to" */
- break;
- }
- }
-
- if (do_open) {
- if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
- warningf(true, "%s: restricted", cp);
- return (-1);
- }
- u = open(cp, flags, 0666);
- }
- if (u < 0) {
- /* herein() may already have printed message */
- if (u == -1) {
- u = errno;
- warningf(true, "cannot %s %s: %s",
- iotype == IODUP ? "dup" :
- (iotype == IOREAD || iotype == IOHERE) ?
- "open" : "create", cp, strerror(u));
- }
- return (-1);
- }
- /* Do not save if it has already been redirected (i.e. "cat >x >y"). */
- if (e->savefd[iop->unit] == 0) {
- /* If these are the same, it means unit was previously closed */
- if (u == iop->unit)
- e->savefd[iop->unit] = -1;
- else
- /* c_exec() assumes e->savefd[fd] set for any
- * redirections. Ask savefd() not to close iop->unit;
- * this allows error messages to be seen if iop->unit
- * is 2; also means we can't lose the fd (eg, both
- * dup2 below and dup2 in restfd() failing).
- */
- e->savefd[iop->unit] = savefd(iop->unit);
- }
-
- if (do_close)
- close(iop->unit);
- else if (u != iop->unit) {
- if (ksh_dup2(u, iop->unit, true) < 0) {
- int ev;
-
- ev = errno;
- warningf(true,
- "could not finish (dup) redirection %s: %s",
- snptreef(NULL, 32, "%R", &iotmp),
- strerror(ev));
- if (iotype != IODUP)
- close(u);
- return (-1);
- }
- if (iotype != IODUP)
- close(u);
- /* Touching any co-process fd in an empty exec
- * causes the shell to close its copies
- */
- else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
- if (iop->flag & IORDUP) /* possible exec <&p */
- coproc_read_close(u);
- else /* possible exec >&p */
- coproc_write_close(u);
- }
- }
- if (u == 2) /* Clear any write errors */
- shf_reopen(2, SHF_WR, shl_out);
- return (0);
-}
-
-/*
- * open here document temp file.
- * if unquoted here, expand here temp file into second temp file.
- */
-static int
-herein(const char *content, int sub)
-{
- volatile int fd = -1;
- struct source *s, *volatile osource;
- struct shf *volatile shf;
- struct temp *h;
- int i;
-
- /* ksh -c 'cat << EOF' can cause this... */
- if (content == NULL) {
- warningf(true, "here document missing");
- return (-2); /* special to iosetup(): don't print error */
- }
-
- /* 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) {
- fd = errno;
- warningf(true, "can't %s temporary file %s: %s",
- !shf ? "create" : "open",
- h->name, strerror(fd));
- if (shf)
- shf_close(shf);
- return (-2 /* special to iosetup(): don't print error */);
- }
-
- osource = source;
- newenv(E_ERRH);
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
- source = osource;
- quitenv(shf);
- close(fd);
- return (-2); /* special to iosetup(): don't print error */
- }
- if (sub) {
- /* Do substitutions on the content of heredoc */
- s = pushs(SSTRING, ATEMP);
- s->start = s->str = content;
- source = s;
- if (yylex(ONEWORD|HEREDOC) != LWORD)
- internal_errorf("herein: yylex");
- source = osource;
- shf_puts(evalstr(yylval.cp, 0), shf);
- } else
- shf_puts(content, shf);
-
- quitenv(NULL);
-
- if (shf_close(shf) == EOF) {
- i = errno;
- close(fd);
- fd = errno;
- warningf(true, "error writing %s: %s, %s", h->name,
- strerror(i), strerror(fd));
- return (-2); /* special to iosetup(): don't print error */
- }
-
- return (fd);
-}
-
-/*
- * ksh special - the select command processing section
- * print the args in column form - assuming that we can
- */
-static const char *
-do_selectargs(const char **ap, bool print_menu)
-{
- static const char *read_args[] = {
- "read", "-r", "REPLY", NULL
- };
- char *s;
- int i, argct;
-
- for (argct = 0; ap[argct]; argct++)
- ;
- while (1) {
- /* Menu is printed if
- * - this is the first time around the select loop
- * - the user enters a blank line
- * - the REPLY parameter is empty
- */
- 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))
- return (NULL);
- s = str_val(global("REPLY"));
- if (*s) {
- getn(s, &i);
- return ((i >= 1 && i <= argct) ? ap[i - 1] : null);
- }
- print_menu = 1;
- }
-}
-
-struct select_menu_info {
- const char * const *args;
- int num_width;
-};
-
-static char *select_fmt_entry(char *, int, int, const void *);
-
-/* format a single select menu item */
-static char *
-select_fmt_entry(char *buf, int buflen, int i, const void *arg)
-{
- const struct select_menu_info *smi =
- (const struct select_menu_info *)arg;
-
- shf_snprintf(buf, buflen, "%*d) %s",
- smi->num_width, i + 1, smi->args[i]);
- return (buf);
-}
-
-/*
- * print a select style menu
- */
-int
-pr_menu(const char * const *ap)
-{
- struct select_menu_info smi;
- const char * const *pp;
- int acols = 0, aocts = 0, i, n;
-
- /*
- * width/column calculations were done once and saved, but this
- * means select can't be used recursively so we re-calculate
- * each time (could save in a structure that is returned, but
- * it's probably not worth the bother)
- */
-
- /*
- * get dimensions of the list
- */
- for (n = 0, pp = ap; *pp; n++, pp++) {
- i = strlen(*pp);
- if (i > aocts)
- aocts = i;
- i = utf_mbswidth(*pp);
- if (i > acols)
- acols = i;
- }
-
- /*
- * we will print an index of the form "%d) " in front of
- * each entry, so get the maximum width of this
- */
- for (i = n, smi.num_width = 1; i >= 10; i /= 10)
- smi.num_width++;
-
- smi.args = 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 *, int, int, const void *);
-
-static char *
-plain_fmt_entry(char *buf, int buflen, int i, const void *arg)
-{
- shf_snprintf(buf, buflen, "%s", ((const char * const *)arg)[i]);
- return (buf);
-}
-
-int
-pr_list(char * const *ap)
-{
- int acols = 0, aocts = 0, i, n;
- char * const *pp;
-
- for (n = 0, pp = ap; *pp; n++, pp++) {
- i = strlen(*pp);
- if (i > aocts)
- aocts = i;
- i = utf_mbswidth(*pp);
- if (i > acols)
- acols = i;
- }
-
- print_columns(shl_out, n, plain_fmt_entry, (const void *)ap,
- aocts, acols, false);
-
- return (n);
-}
-
-/*
- * [[ ... ]] evaluation routines
- */
-
-/*
- * Test if the current token is a whatever. Accepts the current token if
- * it is. Returns 0 if it is not, non-zero if it is (in the case of
- * TM_UNOP and TM_BINOP, the returned value is a Test_op).
- */
-static Test_op
-dbteste_isa(Test_env *te, Test_meta meta)
-{
- Test_op ret = TO_NONOP;
- int uqword;
- const char *p;
-
- if (!*te->pos.wp)
- return (meta == TM_END ? TO_NONNULL : TO_NONOP);
-
- /* unquoted word? */
- for (p = *te->pos.wp; *p == CHAR; p += 2)
- ;
- uqword = *p == EOS;
-
- if (meta == TM_UNOP || meta == TM_BINOP) {
- if (uqword) {
- char buf[8]; /* longer than the longest operator */
- char *q = buf;
- for (p = *te->pos.wp;
- *p == CHAR && q < &buf[sizeof(buf) - 1]; p += 2)
- *q++ = p[1];
- *q = '\0';
- ret = test_isop(meta, buf);
- }
- } else if (meta == TM_END)
- ret = TO_NONOP;
- else
- ret = (uqword && !strcmp(*te->pos.wp,
- dbtest_tokens[(int)meta])) ? TO_NONNULL : TO_NONOP;
-
- /* Accept the token? */
- if (ret != TO_NONOP)
- te->pos.wp++;
-
- return (ret);
-}
-
-static const char *
-dbteste_getopnd(Test_env *te, Test_op op, bool do_eval)
-{
- const char *s = *te->pos.wp;
-
- if (!s)
- return (NULL);
-
- te->pos.wp++;
-
- if (!do_eval)
- return (null);
-
- if (op == TO_STEQL || op == TO_STNEQ)
- s = evalstr(s, DOTILDE | DOPAT);
- else
- s = evalstr(s, DOTILDE);
-
- return (s);
-}
-
-static void
-dbteste_error(Test_env *te, int offset, const char *msg)
-{
- te->flags |= TEF_ERROR;
- internal_warningf("dbteste_error: %s (offset %d)", msg, offset);
-}
diff --git a/mksh/src/expr.c b/mksh/src/expr.c
deleted file mode 100644
index 6c5710c..0000000
--- a/mksh/src/expr.c
+++ /dev/null
@@ -1,895 +0,0 @@
-/* $OpenBSD: expr.c,v 1.21 2009/06/01 19:00:57 deraadt Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.44 2010/08/14 21:35:13 tg Exp $");
-
-/* The order of these enums is constrained by the order of opinfo[] */
-enum token {
- /* some (long) unary operators */
- O_PLUSPLUS = 0, O_MINUSMINUS,
- /* binary operators */
- O_EQ, O_NE,
- /* assignments are assumed to be in range O_ASN .. O_BORASN */
- O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
- O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
- O_LSHIFT, O_RSHIFT,
- O_LE, O_GE, O_LT, O_GT,
- O_LAND,
- O_LOR,
- O_TIMES, O_DIV, O_MOD,
- O_PLUS, O_MINUS,
- O_BAND,
- O_BXOR,
- O_BOR,
- O_TERN,
- O_COMMA,
- /* things after this aren't used as binary operators */
- /* unary that are not also binaries */
- O_BNOT, O_LNOT,
- /* misc */
- OPEN_PAREN, CLOSE_PAREN, CTERN,
- /* things that don't appear in the opinfo[] table */
- VAR, LIT, END, BAD
-};
-#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
-#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
-
-/* precisions; used to be enum prec but we do arithmetics on it */
-#define P_PRIMARY 0 /* VAR, LIT, (), ~ ! - + */
-#define P_MULT 1 /* * / % */
-#define P_ADD 2 /* + - */
-#define P_SHIFT 3 /* << >> */
-#define P_RELATION 4 /* < <= > >= */
-#define P_EQUALITY 5 /* == != */
-#define P_BAND 6 /* & */
-#define P_BXOR 7 /* ^ */
-#define P_BOR 8 /* | */
-#define P_LAND 9 /* && */
-#define P_LOR 10 /* || */
-#define P_TERN 11 /* ?: */
-#define P_ASSIGN 12 /* = *= /= %= += -= <<= >>= &= ^= |= */
-#define P_COMMA 13 /* , */
-#define MAX_PREC P_COMMA
-
-struct opinfo {
- char name[4];
- int len; /* name length */
- int prec; /* precedence: lower is higher */
-};
-
-/* Tokens in this table must be ordered so the longest are first
- * (eg, += before +). If you change something, change the order
- * of enum token too.
- */
-static const struct opinfo opinfo[] = {
- { "++", 2, P_PRIMARY }, /* before + */
- { "--", 2, P_PRIMARY }, /* before - */
- { "==", 2, P_EQUALITY }, /* before = */
- { "!=", 2, P_EQUALITY }, /* before ! */
- { "=", 1, P_ASSIGN }, /* keep assigns in a block */
- { "*=", 2, P_ASSIGN },
- { "/=", 2, P_ASSIGN },
- { "%=", 2, P_ASSIGN },
- { "+=", 2, P_ASSIGN },
- { "-=", 2, P_ASSIGN },
- { "<<=", 3, P_ASSIGN },
- { ">>=", 3, P_ASSIGN },
- { "&=", 2, P_ASSIGN },
- { "^=", 2, P_ASSIGN },
- { "|=", 2, P_ASSIGN },
- { "<<", 2, P_SHIFT },
- { ">>", 2, P_SHIFT },
- { "<=", 2, P_RELATION },
- { ">=", 2, P_RELATION },
- { "<", 1, P_RELATION },
- { ">", 1, P_RELATION },
- { "&&", 2, P_LAND },
- { "||", 2, P_LOR },
- { "*", 1, P_MULT },
- { "/", 1, P_MULT },
- { "%", 1, P_MULT },
- { "+", 1, P_ADD },
- { "-", 1, P_ADD },
- { "&", 1, P_BAND },
- { "^", 1, P_BXOR },
- { "|", 1, P_BOR },
- { "?", 1, P_TERN },
- { ",", 1, P_COMMA },
- { "~", 1, P_PRIMARY },
- { "!", 1, P_PRIMARY },
- { "(", 1, P_PRIMARY },
- { ")", 1, P_PRIMARY },
- { ":", 1, P_PRIMARY },
- { "", 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 */
-};
-
-#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) \
-)
-#define chvui(x, op) do { \
- if (es->natural) \
- (x)->val.u = op (x)->val.u; \
- else \
- (x)->val.i = op (x)->val.i; \
-} while (/* CONSTCOND */ 0)
-#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,
- ET_LVALUE, ET_RDONLY, ET_STR
-};
-
-static void evalerr(Expr_state *, enum error_type, const char *)
- MKSH_A_NORETURN;
-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 *);
-
-/*
- * parse and evaluate expression
- */
-int
-evaluate(const char *expr, mksh_ari_t *rval, int error_ok, bool arith)
-{
- struct tbl v;
- int ret;
-
- v.flag = DEFINED|INTEGER;
- v.type = 0;
- ret = v_evaluate(&v, expr, error_ok, arith);
- *rval = v.val.i;
- return (ret);
-}
-
-/*
- * parse and evaluate expression, storing result in vp.
- */
-int
-v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
- bool arith)
-{
- struct tbl *v;
- Expr_state curstate;
- Expr_state * const es = &curstate;
- int i;
-
- /* save state to allow recursive calls */
- curstate.expression = curstate.tokp = expr;
- curstate.noassign = 0;
- curstate.arith = arith;
- curstate.evaling = NULL;
- curstate.natural = false;
-
- newenv(E_ERRH);
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
- /* Clear EXPRINEVAL in of any variables we were playing with */
- if (curstate.evaling)
- curstate.evaling->flag &= ~EXPRINEVAL;
- quitenv(NULL);
- if (i == LAEXPR) {
- if (error_ok == KSH_RETURN_ERROR)
- return (0);
- errorfz();
- }
- unwind(i);
- /* NOTREACHED */
- }
-
- exprtoken(es);
- if (es->tok == END) {
- es->tok = LIT;
- es->val = tempvar();
- }
- v = intvar(es, evalexpr(es, MAX_PREC));
-
- if (es->tok != END)
- evalerr(es, ET_UNEXPECTED, NULL);
-
- if (es->arith && es->natural)
- vp->flag |= INT_U;
- if (vp->flag & INTEGER)
- setint_v(vp, v, es->arith);
- else
- /* can fail if readonly */
- setstr(vp, str_val(v), error_ok);
-
- quitenv(NULL);
-
- return (1);
-}
-
-static void
-evalerr(Expr_state *es, enum error_type type, const char *str)
-{
- char tbuf[2];
- const char *s;
-
- es->arith = false;
- switch (type) {
- case ET_UNEXPECTED:
- switch (es->tok) {
- case VAR:
- s = es->val->name;
- break;
- case LIT:
- s = str_val(es->val);
- break;
- case END:
- s = "end of expression";
- break;
- case BAD:
- tbuf[0] = *es->tokp;
- tbuf[1] = '\0';
- s = tbuf;
- break;
- default:
- s = opinfo[(int)es->tok].name;
- }
- warningf(true, "%s: unexpected '%s'", es->expression, s);
- break;
-
- case ET_BADLIT:
- warningf(true, "%s: bad number '%s'", es->expression, str);
- break;
-
- case ET_RECURSIVE:
- warningf(true, "%s: expression recurses on parameter '%s'",
- es->expression, str);
- break;
-
- case ET_LVALUE:
- warningf(true, "%s: %s requires lvalue",
- es->expression, str);
- break;
-
- case ET_RDONLY:
- warningf(true, "%s: %s applied to read only variable",
- es->expression, str);
- break;
-
- default: /* keep gcc happy */
- case ET_STR:
- warningf(true, "%s: %s", es->expression, str);
- break;
- }
- unwind(LAEXPR);
-}
-
-static struct tbl *
-evalexpr(Expr_state *es, int prec)
-{
- struct tbl *vl, *vr = NULL, *vasn;
- enum token op;
- mksh_ari_t res = 0;
-
- if (prec == P_PRIMARY) {
- op = es->tok;
- if (op == O_BNOT || op == O_LNOT || op == O_MINUS ||
- op == O_PLUS) {
- exprtoken(es);
- vl = intvar(es, evalexpr(es, P_PRIMARY));
- if (op == O_BNOT)
- chvui(vl, ~);
- else if (op == O_LNOT)
- chvui(vl, !);
- else if (op == O_MINUS)
- chvui(vl, -);
- /* op == O_PLUS is a no-op */
- } else if (op == OPEN_PAREN) {
- exprtoken(es);
- vl = evalexpr(es, MAX_PREC);
- if (es->tok != CLOSE_PAREN)
- evalerr(es, ET_STR, "missing )");
- exprtoken(es);
- } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
- exprtoken(es);
- vl = do_ppmm(es, op, es->val, true);
- exprtoken(es);
- } else if (op == VAR || op == LIT) {
- vl = es->val;
- exprtoken(es);
- } else {
- evalerr(es, ET_UNEXPECTED, NULL);
- /* NOTREACHED */
- }
- if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
- vl = do_ppmm(es, es->tok, vl, false);
- exprtoken(es);
- }
- return (vl);
- }
- vl = evalexpr(es, prec - 1);
- for (op = es->tok; IS_BINOP(op) && opinfo[(int)op].prec == prec;
- op = es->tok) {
- exprtoken(es);
- vasn = vl;
- if (op != O_ASN) /* vl may not have a value yet */
- vl = intvar(es, vl);
- if (IS_ASSIGNOP(op)) {
- 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));
- if ((op == O_DIV || op == O_MOD || op == O_DIVASN ||
- op == O_MODASN) && vr->val.i == 0) {
- if (es->noassign)
- vr->val.i = 1;
- else
- evalerr(es, ET_STR, "zero divisor");
- }
- switch ((int)op) {
- case O_TIMES:
- case O_TIMESASN:
- res = bivui(vl, *, vr);
- break;
- case O_DIV:
- case O_DIVASN:
- res = bivui(vl, /, vr);
- break;
- case O_MOD:
- case O_MODASN:
- res = bivui(vl, %, vr);
- break;
- case O_PLUS:
- case O_PLUSASN:
- res = bivui(vl, +, vr);
- break;
- case O_MINUS:
- case O_MINUSASN:
- res = bivui(vl, -, vr);
- break;
- case O_LSHIFT:
- case O_LSHIFTASN:
- res = bivui(vl, <<, vr);
- break;
- case O_RSHIFT:
- case O_RSHIFTASN:
- res = bivui(vl, >>, vr);
- break;
- case O_LT:
- res = bivui(vl, <, vr);
- break;
- case O_LE:
- res = bivui(vl, <=, vr);
- break;
- case O_GT:
- res = bivui(vl, >, vr);
- break;
- case O_GE:
- res = bivui(vl, >=, vr);
- break;
- case O_EQ:
- res = bivui(vl, ==, vr);
- break;
- case O_NE:
- res = bivui(vl, !=, vr);
- break;
- case O_BAND:
- case O_BANDASN:
- res = bivui(vl, &, vr);
- break;
- case O_BXOR:
- case O_BXORASN:
- res = bivui(vl, ^, vr);
- break;
- case O_BOR:
- case O_BORASN:
- res = bivui(vl, |, vr);
- break;
- case O_LAND:
- if (!vl->val.i)
- es->noassign++;
- vr = intvar(es, evalexpr(es, prec - 1));
- res = bivui(vl, &&, vr);
- if (!vl->val.i)
- es->noassign--;
- break;
- case O_LOR:
- if (vl->val.i)
- es->noassign++;
- vr = intvar(es, evalexpr(es, prec - 1));
- res = bivui(vl, ||, vr);
- if (vl->val.i)
- es->noassign--;
- break;
- case O_TERN:
- {
- bool ev = vl->val.i != 0;
-
- if (!ev)
- es->noassign++;
- vl = evalexpr(es, MAX_PREC);
- if (!ev)
- es->noassign--;
- if (es->tok != CTERN)
- evalerr(es, ET_STR, "missing :");
- exprtoken(es);
- if (ev)
- es->noassign++;
- vr = evalexpr(es, P_TERN);
- if (ev)
- es->noassign--;
- vl = ev ? vl : vr;
- }
- break;
- case O_ASN:
- res = vr->val.i;
- break;
- case O_COMMA:
- res = vr->val.i;
- break;
- }
- if (IS_ASSIGNOP(op)) {
- stvui(vr, res);
- if (!es->noassign) {
- if (vasn->flag & INTEGER)
- setint_v(vasn, vr, es->arith);
- else
- setint(vasn, res);
- }
- vl = vr;
- } else if (op != O_TERN)
- stvui(vl, res);
- }
- return (vl);
-}
-
-static void
-exprtoken(Expr_state *es)
-{
- const char *cp = es->tokp;
- int c;
- char *tvar;
-
- /* skip white space */
- skip_spaces:
- while ((c = *cp), ksh_isspace(c))
- ++cp;
- if (es->tokp == es->expression && c == '#') {
- /* expression begins with # */
- es->natural = true; /* switch to unsigned */
- ++cp;
- goto skip_spaces;
- }
- es->tokp = cp;
-
- if (c == '\0')
- es->tok = END;
- else if (ksh_isalphx(c)) {
- for (; ksh_isalnux(c); c = *cp)
- cp++;
- if (c == '[') {
- int len;
-
- len = array_ref_len(cp);
- if (len == 0)
- evalerr(es, ET_STR, "missing ]");
- cp += len;
- } else if (c == '(' /*)*/ ) {
- /* todo: add math functions (all take single argument):
- * abs acos asin atan cos cosh exp int log sin sinh sqrt
- * tan tanh
- */
- ;
- }
- if (es->noassign) {
- es->val = tempvar();
- es->val->flag |= EXPRLVALUE;
- } else {
- strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
- es->val = global(tvar);
- afree(tvar, ATEMP);
- }
- es->tok = VAR;
- } else if (c == '1' && cp[1] == '#') {
- cp += 2;
- cp += utf_ptradj(cp);
- strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
- goto process_tvar;
-#ifndef MKSH_SMALL
- } else if (c == '\'') {
- ++cp;
- cp += utf_ptradj(cp);
- if (*cp++ != '\'')
- evalerr(es, ET_STR,
- "multi-character character constant");
- /* 'x' -> 1#x (x = one multibyte character) */
- c = cp - es->tokp;
- tvar = alloc(c + /* NUL */ 1, ATEMP);
- tvar[0] = '1';
- tvar[1] = '#';
- memcpy(tvar + 2, es->tokp + 1, c - 2);
- tvar[c] = '\0';
- goto process_tvar;
-#endif
- } else if (ksh_isdigit(c)) {
- while (c != '_' && (ksh_isalnux(c) || c == '#'))
- c = *cp++;
- strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP);
- process_tvar:
- es->val = tempvar();
- es->val->flag &= ~INTEGER;
- es->val->type = 0;
- es->val->val.s = tvar;
- if (setint_v(es->val, es->val, es->arith) == NULL)
- evalerr(es, ET_BADLIT, tvar);
- afree(tvar, ATEMP);
- es->tok = LIT;
- } else {
- int i, n0;
-
- for (i = 0; (n0 = opinfo[i].name[0]); i++)
- if (c == n0 && strncmp(cp, opinfo[i].name,
- (size_t)opinfo[i].len) == 0) {
- es->tok = (enum token)i;
- cp += opinfo[i].len;
- break;
- }
- if (!n0)
- es->tok = BAD;
- }
- es->tokp = cp;
-}
-
-/* Do a ++ or -- operation */
-static struct tbl *
-do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
-{
- struct tbl *vl;
- mksh_ari_t oval;
-
- assign_check(es, op, vasn);
-
- vl = intvar(es, vasn);
- oval = vl->val.i;
- if (op == O_PLUSPLUS) {
- if (es->natural)
- ++vl->val.u;
- else
- ++vl->val.i;
- } else {
- if (es->natural)
- --vl->val.u;
- else
- --vl->val.i;
- }
- if (vasn->flag & INTEGER)
- setint_v(vasn, vl, es->arith);
- else
- setint(vasn, vl->val.i);
- if (!is_prefix) /* undo the inc/dec */
- vl->val.i = oval;
-
- return (vl);
-}
-
-static void
-assign_check(Expr_state *es, enum token op, struct tbl *vasn)
-{
- if (es->tok == END ||
- (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 *
-tempvar(void)
-{
- struct tbl *vp;
-
- vp = alloc(sizeof(struct tbl), ATEMP);
- vp->flag = ISSET|INTEGER;
- vp->type = 0;
- vp->areap = ATEMP;
- vp->ua.hval = 0;
- vp->val.i = 0;
- vp->name[0] = '\0';
- return (vp);
-}
-
-/* cast (string) variable to temporary integer variable */
-static struct tbl *
-intvar(Expr_state *es, struct tbl *vp)
-{
- struct tbl *vq;
-
- /* try to avoid replacing a temp var with another temp var */
- if (vp->name[0] == '\0' &&
- (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
- return (vp);
-
- vq = tempvar();
- if (setint_v(vq, vp, es->arith) == NULL) {
- if (vp->flag & EXPRINEVAL)
- evalerr(es, ET_RECURSIVE, vp->name);
- es->evaling = vp;
- vp->flag |= EXPRINEVAL;
- v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR, es->arith);
- vp->flag &= ~EXPRINEVAL;
- es->evaling = NULL;
- }
- return (vq);
-}
-
-
-/*
- * UTF-8 support code: high-level functions
- */
-
-int
-utf_widthadj(const char *src, const char **dst)
-{
- size_t len;
- unsigned int wc;
- int width;
-
- if (!UTFMODE || (len = utf_mbtowc(&wc, src)) == (size_t)-1 ||
- wc == 0)
- len = width = 1;
- else if ((width = utf_wcwidth(wc)) < 0)
- /* XXX use 2 for x_zotc3 here? */
- width = 1;
-
- if (dst)
- *dst = src + len;
- return (width);
-}
-
-int
-utf_mbswidth(const char *s)
-{
- size_t len;
- unsigned int wc;
- int width = 0, cw;
-
- if (!UTFMODE)
- return (strlen(s));
-
- while (*s)
- if (((len = utf_mbtowc(&wc, s)) == (size_t)-1) ||
- ((cw = utf_wcwidth(wc)) == -1)) {
- s++;
- width += 1;
- } else {
- s += len;
- width += cw;
- }
- return (width);
-}
-
-const char *
-utf_skipcols(const char *p, int cols)
-{
- int c = 0;
-
- while (c < cols) {
- if (!*p)
- return (p + cols - c);
- c += utf_widthadj(p, &p);
- }
- return (p);
-}
-
-size_t
-utf_ptradj(const char *src)
-{
- register size_t n;
-
- if (!UTFMODE ||
- *(const unsigned char *)(src) < 0xC2 ||
- (n = utf_mbtowc(NULL, src)) == (size_t)-1)
- n = 1;
- return (n);
-}
-
-/*
- * UTF-8 support code: low-level functions
- */
-
-/* CESU-8 multibyte and wide character conversion crafted for mksh */
-
-size_t
-utf_mbtowc(unsigned int *dst, const char *src)
-{
- const unsigned char *s = (const unsigned char *)src;
- unsigned int c, wc;
-
- if ((wc = *s++) < 0x80) {
- out:
- if (dst != NULL)
- *dst = wc;
- return (wc ? ((const char *)s - src) : 0);
- }
- if (wc < 0xC2 || wc >= 0xF0)
- /* < 0xC0: spurious second byte */
- /* < 0xC2: non-minimalistic mapping error in 2-byte seqs */
- /* > 0xEF: beyond BMP */
- goto ilseq;
-
- if (wc < 0xE0) {
- wc = (wc & 0x1F) << 6;
- if (((c = *s++) & 0xC0) != 0x80)
- goto ilseq;
- wc |= c & 0x3F;
- goto out;
- }
-
- wc = (wc & 0x0F) << 12;
-
- if (((c = *s++) & 0xC0) != 0x80)
- goto ilseq;
- wc |= (c & 0x3F) << 6;
-
- if (((c = *s++) & 0xC0) != 0x80)
- goto ilseq;
- wc |= c & 0x3F;
-
- /* Check for non-minimalistic mapping error in 3-byte seqs */
- if (wc >= 0x0800 && wc <= 0xFFFD)
- goto out;
- ilseq:
- return ((size_t)(-1));
-}
-
-size_t
-utf_wctomb(char *dst, unsigned int wc)
-{
- unsigned char *d;
-
- if (wc < 0x80) {
- *dst = wc;
- return (1);
- }
-
- d = (unsigned char *)dst;
- if (wc < 0x0800)
- *d++ = (wc >> 6) | 0xC0;
- else {
- *d++ = ((wc = wc > 0xFFFD ? 0xFFFD : wc) >> 12) | 0xE0;
- *d++ = ((wc >> 6) & 0x3F) | 0x80;
- }
- *d++ = (wc & 0x3F) | 0x80;
- return ((char *)d - dst);
-}
-
-
-#ifndef MKSH_mirbsd_wcwidth
-/* --- begin of wcwidth.c excerpt --- */
-/*-
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- */
-
-__RCSID("$miros: src/lib/libc/i18n/wcwidth.c,v 1.8 2008/09/20 12:01:18 tg Exp $");
-
-int
-utf_wcwidth(unsigned int c)
-{
- static const struct cbset {
- unsigned short first;
- unsigned short last;
- } comb[] = {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 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 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 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 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
- { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
- { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }
- };
- size_t min = 0, mid, max = NELEM(comb) - 1;
-
- /* test for 8-bit control characters */
- if (c < 32 || (c >= 0x7f && c < 0xa0))
- return (c ? -1 : 0);
-
- /* binary search in table of non-spacing characters */
- if (c >= comb[0].first && c <= comb[max].last)
- while (max >= min) {
- mid = (min + max) / 2;
- if (c > comb[mid].last)
- min = mid + 1;
- else if (c < comb[mid].first)
- max = mid - 1;
- else
- return (0);
- }
-
- /* if we arrive here, c is not a combining or C0/C1 control char */
- return ((c >= 0x1100 && (
- c <= 0x115f || /* Hangul Jamo init. consonants */
- c == 0x2329 || c == 0x232a ||
- (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
- (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
- (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
- (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
- (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
- (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
- (c >= 0xffe0 && c <= 0xffe6))) ? 2 : 1);
-}
-/* --- end of wcwidth.c excerpt --- */
-#endif
diff --git a/mksh/src/funcs.c b/mksh/src/funcs.c
deleted file mode 100644
index 9d9c03a..0000000
--- a/mksh/src/funcs.c
+++ /dev/null
@@ -1,3429 +0,0 @@
-/* $OpenBSD: c_ksh.c,v 1.33 2009/02/07 14:03:24 kili Exp $ */
-/* $OpenBSD: c_sh.c,v 1.41 2010/03/27 09:10:01 jmc Exp $ */
-/* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */
-/* $OpenBSD: c_ulimit.c,v 1.17 2008/03/21 12:51:19 millert Exp $ */
-
-/*-
- * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.157 2010/08/24 14:42:01 tg Exp $");
-
-#if HAVE_KILLPG
-/*
- * use killpg if < -1 since -1 does special things
- * for some non-killpg-endowed kills
- */
-#define mksh_kill(p,s) ((p) < -1 ? killpg(-(p), (s)) : kill((p), (s)))
-#else
-/* cross fingers and hope kill is killpg-endowed */
-#define mksh_kill kill
-#endif
-
-/* XXX conditions correct? */
-#if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
-#define MKSH_NO_LIMITS
-#endif
-
-#ifdef MKSH_NO_LIMITS
-#define c_ulimit c_label
-#endif
-
-extern uint8_t set_refflag;
-
-/* A leading = means assignments before command are kept;
- * a leading * means a POSIX special builtin;
- * a leading + means a POSIX regular builtin
- * (* and + should not be combined).
- */
-const struct builtin mkshbuiltins[] = {
- {"*=.", c_dot},
- {"*=:", c_label},
- {"[", c_test},
- {"*=break", c_brkcont},
- {"=builtin", c_builtin},
- {"*=continue", c_brkcont},
- {"*=eval", c_eval},
- {"*=exec", c_exec},
- {"*=exit", c_exitreturn},
- {"+false", c_label},
- {"*=return", c_exitreturn},
- {"*=set", c_set},
- {"*=shift", c_shift},
- {"=times", c_times},
- {"*=trap", c_trap},
- {"+=wait", c_wait},
- {"+read", c_read},
- {"test", c_test},
- {"+true", c_label},
- {"ulimit", c_ulimit},
- {"+umask", c_umask},
- {"*=unset", c_unset},
- {"+alias", c_alias}, /* no =: AT&T manual wrong */
- {"+cd", c_cd},
- {"chdir", c_cd}, /* dash compatibility hack */
- {"+command", c_command},
- {"echo", c_print},
- {"*=export", c_typeset},
- {"+fc", c_fc},
- {"+getopts", c_getopts},
- {"+jobs", c_jobs},
- {"+kill", c_kill},
- {"let", c_let},
- {"print", c_print},
-#ifdef MKSH_PRINTF_BUILTIN
- {"printf", c_printf},
-#endif
- {"pwd", c_pwd},
- {"*=readonly", c_typeset},
- {T__typeset, c_typeset},
- {"+unalias", c_unalias},
- {"whence", c_whence},
-#ifndef MKSH_UNEMPLOYED
- {"+bg", c_fgbg},
- {"+fg", c_fgbg},
-#endif
- {"bind", c_bind},
-#if HAVE_MKNOD
- {"mknod", c_mknod},
-#endif
- {"realpath", c_realpath},
- {"rename", c_rename},
- {NULL, (int (*)(const char **))NULL}
-};
-
-struct kill_info {
- int num_width;
- int name_width;
-};
-
-static const struct t_op {
- char op_text[4];
- Test_op op_num;
-} u_ops[] = {
- {"-a", TO_FILAXST },
- {"-b", TO_FILBDEV },
- {"-c", TO_FILCDEV },
- {"-d", TO_FILID },
- {"-e", TO_FILEXST },
- {"-f", TO_FILREG },
- {"-G", TO_FILGID },
- {"-g", TO_FILSETG },
- {"-h", TO_FILSYM },
- {"-H", TO_FILCDF },
- {"-k", TO_FILSTCK },
- {"-L", TO_FILSYM },
- {"-n", TO_STNZE },
- {"-O", TO_FILUID },
- {"-o", TO_OPTION },
- {"-p", TO_FILFIFO },
- {"-r", TO_FILRD },
- {"-s", TO_FILGZ },
- {"-S", TO_FILSOCK },
- {"-t", TO_FILTT },
- {"-u", TO_FILSETU },
- {"-w", TO_FILWR },
- {"-x", TO_FILEX },
- {"-z", TO_STZER },
- {"", TO_NONOP }
-};
-static const struct t_op b_ops[] = {
- {"=", TO_STEQL },
- {"==", TO_STEQL },
- {"!=", TO_STNEQ },
- {"<", TO_STLT },
- {">", TO_STGT },
- {"-eq", TO_INTEQ },
- {"-ne", TO_INTNE },
- {"-gt", TO_INTGT },
- {"-ge", TO_INTGE },
- {"-lt", TO_INTLT },
- {"-le", TO_INTLE },
- {"-ef", TO_FILEQ },
- {"-nt", TO_FILNT },
- {"-ot", TO_FILOT },
- {"", TO_NONOP }
-};
-
-static int test_eaccess(const char *, int);
-static int test_oexpr(Test_env *, bool);
-static int test_aexpr(Test_env *, bool);
-static int test_nexpr(Test_env *, bool);
-static int test_primary(Test_env *, bool);
-static Test_op ptest_isa(Test_env *, Test_meta);
-static const char *ptest_getopnd(Test_env *, Test_op, bool);
-static void ptest_error(Test_env *, int, const char *);
-static char *kill_fmt_entry(char *, int, int, const void *);
-static void p_time(struct shf *, bool, long, int, int,
- const char *, const char *)
- MKSH_A_NONNULL((nonnull (6, 7)));
-static char *do_realpath(const char *);
-
-static char *
-do_realpath(const char *upath)
-{
- char *xp, *ip, *tp, *ipath, *ldest = NULL;
- XString xs;
- ptrdiff_t pos;
- size_t len;
- int symlinks = 32; /* max. recursion depth */
- int llen;
- struct stat sb;
-#ifdef NO_PATH_MAX
- size_t ldestlen = 0;
-#define pathlen sb.st_size
-#define pathcnd (ldestlen < (pathlen + 1))
-#else
-#define pathlen PATH_MAX
-#define pathcnd (!ldest)
-#endif
-
- if (upath[0] == '/') {
- /* upath is an absolute pathname */
- strdupx(ipath, upath, ATEMP);
- } else {
- /* upath is a relative pathname, prepend cwd */
- if ((tp = ksh_get_wd(NULL)) == NULL || tp[0] != '/')
- return (NULL);
- ipath = shf_smprintf("%s/%s", tp, upath);
- afree(tp, ATEMP);
- }
-
- Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
-
- while (*ip) {
- /* skip slashes in input */
- while (*ip == '/')
- ++ip;
- if (!*ip)
- break;
-
- /* get next pathname component from input */
- tp = ip;
- while (*ip && *ip != '/')
- ++ip;
- len = ip - tp;
-
- /* check input for "." and ".." */
- if (tp[0] == '.') {
- if (len == 1)
- /* just continue with the next one */
- continue;
- else if (len == 2 && tp[1] == '.') {
- /* strip off last pathname component */
- while (xp > Xstring(xs, xp))
- if (*--xp == '/')
- break;
- /* then continue with the next one */
- continue;
- }
- }
-
- /* store output position away, then append slash to output */
- pos = Xsavepos(xs, xp);
- /* 1 for the '/' and len + 1 for tp and the NUL from below */
- XcheckN(xs, xp, 1 + len + 1);
- Xput(xs, xp, '/');
-
- /* append next pathname component to output */
- memcpy(xp, tp, len);
- xp += len;
- *xp = '\0';
-
- /* lstat the current output, see if it's a symlink */
- if (lstat(Xstring(xs, xp), &sb)) {
- /* lstat failed */
- if (errno == ENOENT) {
- /* because the pathname does not exist */
- while (*ip == '/')
- /* skip any trailing slashes */
- ++ip;
- /* no more components left? */
- if (!*ip)
- /* we can still return successfully */
- break;
- /* more components left? fall through */
- }
- /* not ENOENT or not at the end of ipath */
- goto notfound;
- }
-
- /* check if we encountered a symlink? */
- if (S_ISLNK(sb.st_mode)) {
- /* reached maximum recursion depth? */
- if (!symlinks--) {
- /* yep, prevent infinite loops */
- errno = ELOOP;
- goto notfound;
- }
-
- /* get symlink(7) target */
- if (pathcnd)
- ldest = aresize(ldest, pathlen + 1, ATEMP);
- llen = readlink(Xstring(xs, xp), ldest, pathlen);
- if (llen < 0)
- /* oops... */
- goto notfound;
- ldest[llen] = '\0';
-
- /*
- * restart if symlink target is an absolute path,
- * otherwise continue with currently resolved prefix
- */
- xp = (ldest[0] == '/') ? Xstring(xs, xp) :
- Xrestpos(xs, xp, pos);
- tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
- afree(ipath, ATEMP);
- ip = ipath = tp;
- }
- /* otherwise (no symlink) merely go on */
- }
-
- /*
- * either found the target and successfully resolved it,
- * or found its parent directory and may create it
- */
- if (Xlength(xs, xp) == 0)
- /*
- * if the resolved pathname is "", make it "/",
- * otherwise do not add a trailing slash
- */
- Xput(xs, xp, '/');
- Xput(xs, xp, '\0');
-
- /*
- * if source path had a trailing slash, check if target path
- * is not a non-directory existing file
- */
- if (ip > ipath && ip[-1] == '/') {
- if (stat(Xstring(xs, xp), &sb)) {
- if (errno != ENOENT)
- goto notfound;
- } else if (!S_ISDIR(sb.st_mode)) {
- errno = ENOTDIR;
- goto notfound;
- }
- /* target now either does not exist or is a directory */
- }
-
- /* return target path */
- if (ldest != NULL)
- afree(ldest, ATEMP);
- afree(ipath, ATEMP);
- return (Xclose(xs, xp));
-
- notfound:
- llen = errno; /* save; free(3) might trash it */
- if (ldest != NULL)
- afree(ldest, ATEMP);
- afree(ipath, ATEMP);
- Xfree(xs, xp);
- errno = llen;
- return (NULL);
-
-#undef pathlen
-#undef pathcnd
-}
-
-int
-c_cd(const char **wp)
-{
- int optc, rv, phys_path;
- bool physical = Flag(FPHYSICAL) ? true : false;
- int cdnode; /* was a node from cdpath added in? */
- bool printpath = false; /* print where we cd'd? */
- struct tbl *pwd_s, *oldpwd_s;
- XString xs;
- char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
- switch (optc) {
- case 'L':
- physical = false;
- break;
- case 'P':
- physical = true;
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
-
- if (Flag(FRESTRICTED)) {
- bi_errorf("restricted shell - can't cd");
- return (1);
- }
-
- pwd_s = global("PWD");
- oldpwd_s = global("OLDPWD");
-
- if (!wp[0]) {
- /* No arguments - go home */
- if ((dir = str_val(global("HOME"))) == null) {
- bi_errorf("no home directory (HOME not set)");
- return (1);
- }
- } else if (!wp[1]) {
- /* One argument: - or dir */
- strdupx(allocd, wp[0], ATEMP);
- if (ksh_isdash((dir = allocd))) {
- afree(allocd, ATEMP);
- allocd = NULL;
- dir = str_val(oldpwd_s);
- if (dir == null) {
- bi_errorf("no OLDPWD");
- return (1);
- }
- printpath = true;
- }
- } else if (!wp[2]) {
- /* Two arguments - substitute arg1 in PWD for arg2 */
- int ilen, olen, nlen, elen;
- char *cp;
-
- if (!current_wd[0]) {
- bi_errorf("don't know current directory");
- return (1);
- }
- /* substitute arg1 for arg2 in current path.
- * if the first substitution fails because the cd fails
- * we could try to find another substitution. For now
- * we don't
- */
- if ((cp = strstr(current_wd, wp[0])) == NULL) {
- bi_errorf("bad substitution");
- return (1);
- }
- ilen = cp - current_wd;
- olen = strlen(wp[0]);
- nlen = strlen(wp[1]);
- elen = strlen(current_wd + ilen + olen) + 1;
- dir = allocd = alloc(ilen + nlen + elen, ATEMP);
- memcpy(dir, current_wd, ilen);
- memcpy(dir + ilen, wp[1], nlen);
- memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
- printpath = true;
- } else {
- bi_errorf("too many arguments");
- return (1);
- }
-
-#ifdef NO_PATH_MAX
- /* only a first guess; make_path will enlarge xs if necessary */
- XinitN(xs, 1024, ATEMP);
-#else
- XinitN(xs, PATH_MAX, ATEMP);
-#endif
-
- cdpath = str_val(global("CDPATH"));
- do {
- cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
- if (physical)
- rv = chdir(tryp = Xstring(xs, xp) + phys_path);
- else {
- simplify_path(Xstring(xs, xp));
- rv = chdir(tryp = Xstring(xs, xp));
- }
- } while (rv < 0 && cdpath != NULL);
-
- if (rv < 0) {
- if (cdnode)
- bi_errorf("%s: bad directory", dir);
- else
- bi_errorf("%s - %s", tryp, strerror(errno));
- afree(allocd, ATEMP);
- return (1);
- }
-
- /* allocd (above) => dir, which is no longer used */
- afree(allocd, ATEMP);
- allocd = NULL;
-
- /* Clear out tracked aliases with relative paths */
- flushcom(0);
-
- /* Set OLDPWD (note: unsetting OLDPWD does not disable this
- * setting in AT&T ksh)
- */
- if (current_wd[0])
- /* Ignore failure (happens if readonly or integer) */
- setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
-
- if (Xstring(xs, xp)[0] != '/') {
- pwd = NULL;
- } else if (!physical || !(pwd = allocd = do_realpath(Xstring(xs, xp))))
- pwd = Xstring(xs, xp);
-
- /* Set PWD */
- if (pwd) {
- char *ptmp = pwd;
-
- set_current_wd(ptmp);
- /* Ignore failure (happens if readonly or integer) */
- setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
- } else {
- set_current_wd(null);
- pwd = Xstring(xs, xp);
- /* XXX unset $PWD? */
- }
- if (printpath || cdnode)
- shprintf("%s\n", pwd);
-
- afree(allocd, ATEMP);
- return (0);
-}
-
-int
-c_pwd(const char **wp)
-{
- int optc;
- bool physical = Flag(FPHYSICAL) ? true : false;
- char *p, *allocd = NULL;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
- switch (optc) {
- case 'L':
- physical = false;
- break;
- case 'P':
- physical = true;
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
-
- if (wp[0]) {
- bi_errorf("too many arguments");
- return (1);
- }
- p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
- current_wd) : NULL;
- if (p && access(p, R_OK) < 0)
- p = NULL;
- if (!p && !(p = allocd = ksh_get_wd(NULL))) {
- bi_errorf("can't get current directory - %s", strerror(errno));
- return (1);
- }
- shprintf("%s\n", p);
- afree(allocd, ATEMP);
- return (0);
-}
-
-static const char *s_ptr;
-static int s_get(void);
-static void s_put(int);
-
-int
-c_print(const char **wp)
-{
-#define PO_NL BIT(0) /* print newline */
-#define PO_EXPAND BIT(1) /* expand backslash sequences */
-#define PO_PMINUSMINUS BIT(2) /* print a -- argument */
-#define PO_HIST BIT(3) /* print to history instead of stdout */
-#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
- int fd = 1, c;
- int flags = PO_EXPAND|PO_NL;
- const char *s, *emsg;
- XString xs;
- char *xp;
-
- if (wp[0][0] == 'e') {
- /* echo builtin */
- wp++;
- if (Flag(FPOSIX) || Flag(FSH)) {
- /* Debian Policy 10.4 compliant "echo" builtin */
- if (*wp && !strcmp(*wp, "-n")) {
- /* we recognise "-n" only as the first arg */
- flags = 0;
- wp++;
- } else
- /* otherwise, we print everything as-is */
- flags = PO_NL;
- } else {
- int nflags = flags;
-
- /**
- * a compromise between sysV and BSD echo commands:
- * escape sequences are enabled by default, and -n,
- * -e and -E are recognised if they appear in argu-
- * ments with no illegal options (ie, echo -nq will
- * print -nq).
- * Different from sysV echo since options are reco-
- * gnised, different from BSD echo since escape se-
- * quences are enabled by default.
- */
-
- while ((s = *wp) && *s == '-' && s[1]) {
- while (*++s)
- if (*s == 'n')
- nflags &= ~PO_NL;
- else if (*s == 'e')
- nflags |= PO_EXPAND;
- else if (*s == 'E')
- nflags &= ~PO_EXPAND;
- else
- /*
- * bad option: don't use
- * nflags, print argument
- */
- break;
-
- if (*s)
- break;
- wp++;
- flags = nflags;
- }
- }
- } else {
- int optc;
- const char *opts = "Rnprsu,";
-
- while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
- switch (optc) {
- case 'R': /* fake BSD echo command */
- flags |= PO_PMINUSMINUS;
- flags &= ~PO_EXPAND;
- opts = "ne";
- break;
- case 'e':
- flags |= PO_EXPAND;
- break;
- case 'n':
- flags &= ~PO_NL;
- break;
- case 'p':
- if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
- bi_errorf("-p: %s", emsg);
- return (1);
- }
- break;
- case 'r':
- flags &= ~PO_EXPAND;
- break;
- case 's':
- flags |= PO_HIST;
- break;
- case 'u':
- if (!*(s = builtin_opt.optarg))
- fd = 0;
- else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
- bi_errorf("-u: %s: %s", s, emsg);
- return (1);
- }
- break;
- case '?':
- return (1);
- }
-
- if (!(builtin_opt.info & GI_MINUSMINUS)) {
- /* treat a lone - like -- */
- if (wp[builtin_opt.optind] &&
- ksh_isdash(wp[builtin_opt.optind]))
- builtin_opt.optind++;
- } else if (flags & PO_PMINUSMINUS)
- builtin_opt.optind--;
- wp += builtin_opt.optind;
- }
-
- Xinit(xs, xp, 128, ATEMP);
-
- while (*wp != NULL) {
- s = *wp;
- while ((c = *s++) != '\0') {
- Xcheck(xs, xp);
- if ((flags & PO_EXPAND) && c == '\\') {
- s_ptr = s;
- c = unbksl(false, s_get, s_put);
- s = s_ptr;
- if (c == -1) {
- /* rejected by generic function */
- switch ((c = *s++)) {
- case 'c':
- flags &= ~PO_NL;
- /* AT&T brain damage */
- continue;
- case '\0':
- s--;
- c = '\\';
- break;
- default:
- Xput(xs, xp, '\\');
- }
- } else if ((unsigned int)c > 0xFF) {
- /* generic function returned Unicode */
- char ts[4];
-
- c = utf_wctomb(ts, c - 0x100);
- ts[c] = 0;
- for (c = 0; ts[c]; ++c)
- Xput(xs, xp, ts[c]);
- continue;
- }
- }
- Xput(xs, xp, c);
- }
- if (*++wp != NULL)
- Xput(xs, xp, ' ');
- }
- if (flags & PO_NL)
- Xput(xs, xp, '\n');
-
- if (flags & PO_HIST) {
- Xput(xs, xp, '\0');
- histsave(&source->line, Xstring(xs, xp), true, false);
- Xfree(xs, xp);
- } else {
- int len = Xlength(xs, xp);
- int opipe = 0;
-
- /* Ensure we aren't killed by a SIGPIPE while writing to
- * a coprocess. AT&T ksh doesn't seem to do this (seems
- * to just check that the co-process is alive which is
- * not enough).
- */
- if (coproc.write >= 0 && coproc.write == fd) {
- flags |= PO_COPROC;
- opipe = block_pipe();
- }
- for (s = Xstring(xs, xp); len > 0; ) {
- if ((c = write(fd, s, len)) < 0) {
- if (flags & PO_COPROC)
- restore_pipe(opipe);
- if (errno == EINTR) {
- /* allow user to ^C out */
- intrcheck();
- if (flags & PO_COPROC)
- opipe = block_pipe();
- continue;
- }
- return (1);
- }
- s += c;
- len -= c;
- }
- if (flags & PO_COPROC)
- restore_pipe(opipe);
- }
-
- return (0);
-}
-
-static int
-s_get(void)
-{
- return (*s_ptr++);
-}
-
-static void
-s_put(int c MKSH_A_UNUSED)
-{
- --s_ptr;
-}
-
-int
-c_whence(const char **wp)
-{
- struct tbl *tp;
- const char *id;
- bool pflag = false, vflag = false, Vflag = false;
- int rv = 0, optc, fcflags;
- bool iam_whence = wp[0][0] == 'w';
- const char *opts = iam_whence ? "pv" : "pvV";
-
- while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
- switch (optc) {
- case 'p':
- pflag = true;
- break;
- case 'v':
- vflag = true;
- break;
- case 'V':
- Vflag = true;
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
-
- fcflags = FC_BI | FC_PATH | FC_FUNC;
- if (!iam_whence) {
- /* Note that -p on its own is deal with in comexec() */
- if (pflag)
- fcflags |= FC_DEFPATH;
- /* Convert command options to whence options - note that
- * command -pV uses a different path search than whence -v
- * or whence -pv. This should be considered a feature.
- */
- vflag = Vflag;
- }
- if (pflag)
- fcflags &= ~(FC_BI | FC_FUNC);
-
- while ((vflag || rv == 0) && (id = *wp++) != NULL) {
- uint32_t h = 0;
-
- tp = NULL;
- if ((iam_whence || vflag) && !pflag)
- tp = ktsearch(&keywords, id, h = hash(id));
- if (!tp && !pflag) {
- tp = ktsearch(&aliases, id, h ? h : hash(id));
- if (tp && !(tp->flag & ISSET))
- tp = NULL;
- }
- if (!tp)
- tp = findcom(id, fcflags);
- if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
- tp->type != CTALIAS))
- shf_puts(id, shl_stdout);
- switch (tp->type) {
- case CKEYWD:
- if (vflag)
- shf_puts(" is a reserved word", shl_stdout);
- break;
- case CALIAS:
- if (vflag)
- shprintf(" is an %salias for ",
- (tp->flag & EXPORT) ? "exported " : null);
- if (!iam_whence && !vflag)
- shprintf("alias %s=", id);
- print_value_quoted(tp->val.s);
- break;
- case CFUNC:
- if (vflag) {
- shf_puts(" is a", shl_stdout);
- if (tp->flag & EXPORT)
- shf_puts("n exported", shl_stdout);
- if (tp->flag & TRACE)
- shf_puts(" traced", shl_stdout);
- if (!(tp->flag & ISSET)) {
- shf_puts(" undefined", shl_stdout);
- if (tp->u.fpath)
- shprintf(" (autoload from %s)",
- tp->u.fpath);
- }
- shf_puts(" function", shl_stdout);
- }
- break;
- case CSHELL:
- if (vflag)
- shprintf(" is a%s shell builtin",
- (tp->flag & SPEC_BI) ? " special" : null);
- break;
- case CTALIAS:
- case CEXEC:
- if (tp->flag & ISSET) {
- if (vflag) {
- shf_puts(" is ", shl_stdout);
- if (tp->type == CTALIAS)
- shprintf("a tracked %salias for ",
- (tp->flag & EXPORT) ?
- "exported " : null);
- }
- shf_puts(tp->val.s, shl_stdout);
- } else {
- if (vflag)
- shf_puts(" not found", shl_stdout);
- rv = 1;
- }
- break;
- default:
- shprintf("%s is *GOK*", id);
- break;
- }
- if (vflag || !rv)
- shf_putc('\n', shl_stdout);
- }
- return (rv);
-}
-
-/* Deal with command -vV - command -p dealt with in comexec() */
-int
-c_command(const char **wp)
-{
- /* Let c_whence do the work. Note that c_command() must be
- * a distinct function from c_whence() (tested in comexec()).
- */
- return (c_whence(wp));
-}
-
-/* typeset, export, and readonly */
-int
-c_typeset(const char **wp)
-{
- struct block *l;
- struct tbl *vp, **p;
- Tflag fset = 0, fclr = 0, flag;
- int thing = 0, field, base, optc;
- const char *opts;
- const char *fieldstr, *basestr;
- bool localv = false, func = false, pflag = false, istset = true;
-
- switch (**wp) {
- case 'e': /* export */
- fset |= EXPORT;
- istset = false;
- break;
- case 'r': /* readonly */
- fset |= RDONLY;
- istset = false;
- break;
- case 's': /* set */
- /* called with 'typeset -' */
- break;
- case 't': /* typeset */
- localv = true;
- break;
- }
-
- /* see comment below regarding possible opions */
- opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
-
- fieldstr = basestr = NULL;
- builtin_opt.flags |= GF_PLUSOPT;
- /* AT&T ksh seems to have 0-9 as options which are multiplied
- * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
- * sets right justify in a field of 12). This allows options
- * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
- * does not allow the number to be specified as a separate argument
- * Here, the number must follow the RLZi option, but is optional
- * (see the # kludge in ksh_getopt()).
- */
- while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
- flag = 0;
- switch (optc) {
- case 'L':
- flag = LJUST;
- fieldstr = builtin_opt.optarg;
- break;
- case 'R':
- flag = RJUST;
- fieldstr = builtin_opt.optarg;
- break;
- case 'U':
- /* AT&T ksh uses u, but this conflicts with
- * upper/lower case. If this option is changed,
- * need to change the -U below as well
- */
- flag = INT_U;
- break;
- case 'Z':
- flag = ZEROFIL;
- fieldstr = builtin_opt.optarg;
- break;
- case 'a':
- /*
- * this is supposed to set (-a) or unset (+a) the
- * indexed array attribute; it does nothing on an
- * existing regular string or indexed array though
- */
- break;
- case 'f':
- func = true;
- break;
- case 'i':
- flag = INTEGER;
- basestr = builtin_opt.optarg;
- break;
- case 'l':
- flag = LCASEV;
- break;
- case 'n':
- set_refflag = (builtin_opt.info & GI_PLUS) ? 2 : 1;
- break;
- case 'p':
- /* export, readonly: POSIX -p flag */
- /* typeset: show values as well */
- pflag = true;
- if (istset)
- continue;
- break;
- case 'r':
- flag = RDONLY;
- break;
- case 't':
- flag = TRACE;
- break;
- case 'u':
- flag = UCASEV_AL; /* upper case / autoload */
- break;
- case 'x':
- flag = EXPORT;
- break;
- case '?':
- return (1);
- }
- if (builtin_opt.info & GI_PLUS) {
- fclr |= flag;
- fset &= ~flag;
- thing = '+';
- } else {
- fset |= flag;
- fclr &= ~flag;
- thing = '-';
- }
- }
-
- field = 0;
- if (fieldstr && !bi_getn(fieldstr, &field))
- return (1);
- base = 0;
- if (basestr && !bi_getn(basestr, &base))
- return (1);
-
- if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
- (wp[builtin_opt.optind][0] == '-' ||
- wp[builtin_opt.optind][0] == '+') &&
- wp[builtin_opt.optind][1] == '\0') {
- thing = wp[builtin_opt.optind][0];
- builtin_opt.optind++;
- }
-
- if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) || set_refflag)) {
- bi_errorf("only -t, -u and -x options may be used with -f");
- set_refflag = 0;
- return (1);
- }
- if (wp[builtin_opt.optind]) {
- /* Take care of exclusions.
- * At this point, flags in fset are cleared in fclr and vice
- * versa. This property should be preserved.
- */
- if (fset & LCASEV) /* LCASEV has priority over UCASEV_AL */
- fset &= ~UCASEV_AL;
- if (fset & LJUST) /* LJUST has priority over RJUST */
- fset &= ~RJUST;
- if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */
- fset |= RJUST;
- fclr &= ~RJUST;
- }
- /* Setting these attributes clears the others, unless they
- * are also set in this command
- */
- if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
- INTEGER | INT_U | INT_L)) || set_refflag)
- fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
- LCASEV | INTEGER | INT_U | INT_L);
- }
-
- /* set variables and attributes */
- if (wp[builtin_opt.optind]) {
- int i, rv = 0;
- struct tbl *f;
-
- if (localv && !func)
- fset |= LOCAL;
- for (i = builtin_opt.optind; wp[i]; i++) {
- if (func) {
- f = findfunc(wp[i], hash(wp[i]),
- (fset&UCASEV_AL) ? true : false);
- if (!f) {
- /* AT&T ksh does ++rv: bogus */
- rv = 1;
- continue;
- }
- if (fset | fclr) {
- f->flag |= fset;
- f->flag &= ~fclr;
- } else
- fptreef(shl_stdout, 0,
- f->flag & FKSH ?
- "function %s %T\n" :
- "%s() %T\n", wp[i], f->val.t);
- } else if (!typeset(wp[i], fset, fclr, field, base)) {
- bi_errorf("%s: not identifier", wp[i]);
- set_refflag = 0;
- return (1);
- }
- }
- set_refflag = 0;
- return (rv);
- }
-
- /* list variables and attributes */
- flag = fset | fclr; /* no difference at this point.. */
- if (func) {
- for (l = e->loc; l; l = l->next) {
- for (p = ktsort(&l->funs); (vp = *p++); ) {
- if (flag && (vp->flag & flag) == 0)
- continue;
- if (thing == '-')
- fptreef(shl_stdout, 0, vp->flag & FKSH ?
- "function %s %T\n" : "%s() %T\n",
- vp->name, vp->val.t);
- else
- shprintf("%s\n", vp->name);
- }
- }
- } else {
- for (l = e->loc; l; l = l->next) {
- for (p = ktsort(&l->vars); (vp = *p++); ) {
- struct tbl *tvp;
- bool any_set = false;
- /*
- * See if the parameter is set (for arrays, if any
- * element is set).
- */
- for (tvp = vp; tvp; tvp = tvp->u.array)
- if (tvp->flag & ISSET) {
- any_set = true;
- break;
- }
-
- /*
- * Check attributes - note that all array elements
- * have (should have?) the same attributes, so checking
- * the first is sufficient.
- *
- * Report an unset param only if the user has
- * explicitly given it some attribute (like export);
- * otherwise, after "echo $FOO", we would report FOO...
- */
- if (!any_set && !(vp->flag & USERATTRIB))
- continue;
- if (flag && (vp->flag & flag) == 0)
- continue;
- for (; vp; vp = vp->u.array) {
- /* Ignore array elements that aren't
- * set unless there are no set elements,
- * in which case the first is reported on */
- if ((vp->flag&ARRAY) && any_set &&
- !(vp->flag & ISSET))
- continue;
- /* no arguments */
- if (thing == 0 && flag == 0) {
- /* AT&T ksh prints things
- * like export, integer,
- * leftadj, zerofill, etc.,
- * but POSIX says must
- * be suitable for re-entry...
- */
- shf_puts("typeset ", shl_stdout);
- if (((vp->flag&(ARRAY|ASSOC))==ASSOC))
- shf_puts("-n ", shl_stdout);
- if ((vp->flag&INTEGER))
- shf_puts("-i ", shl_stdout);
- if ((vp->flag&EXPORT))
- shf_puts("-x ", shl_stdout);
- if ((vp->flag&RDONLY))
- shf_puts("-r ", shl_stdout);
- if ((vp->flag&TRACE))
- shf_puts("-t ", shl_stdout);
- if ((vp->flag&LJUST))
- shprintf("-L%d ", vp->u2.field);
- if ((vp->flag&RJUST))
- shprintf("-R%d ", vp->u2.field);
- if ((vp->flag&ZEROFIL))
- shf_puts("-Z ", shl_stdout);
- if ((vp->flag&LCASEV))
- shf_puts("-l ", shl_stdout);
- if ((vp->flag&UCASEV_AL))
- shf_puts("-u ", shl_stdout);
- if ((vp->flag&INT_U))
- shf_puts("-U ", shl_stdout);
- shf_puts(vp->name, shl_stdout);
- if (pflag) {
- char *s = str_val(vp);
-
- shf_putc('=', shl_stdout);
- /* AT&T ksh can't have
- * justified integers.. */
- if ((vp->flag &
- (INTEGER|LJUST|RJUST)) ==
- INTEGER)
- shf_puts(s, shl_stdout);
- else
- print_value_quoted(s);
- }
- shf_putc('\n', shl_stdout);
- if (vp->flag & ARRAY)
- break;
- } else {
- if (pflag)
- shf_puts(istset ?
- "typeset " :
- (flag & EXPORT) ?
- "export " :
- "readonly ",
- shl_stdout);
- if ((vp->flag&ARRAY) && any_set)
- shprintf("%s[%lu]",
- vp->name,
- arrayindex(vp));
- else
- shf_puts(vp->name, shl_stdout);
- if (thing == '-' && (vp->flag&ISSET)) {
- char *s = str_val(vp);
-
- shf_putc('=', shl_stdout);
- /* AT&T ksh can't have
- * justified integers.. */
- if ((vp->flag &
- (INTEGER|LJUST|RJUST)) ==
- INTEGER)
- shf_puts(s, shl_stdout);
- else
- print_value_quoted(s);
- }
- shf_putc('\n', shl_stdout);
- }
- /* Only report first 'element' of an array with
- * no set elements.
- */
- if (!any_set)
- break;
- }
- }
- }
- }
- return (0);
-}
-
-int
-c_alias(const char **wp)
-{
- struct table *t = &aliases;
- int rv = 0, prefix = 0;
- bool rflag = false, tflag, Uflag = false, pflag = false;
- Tflag xflag = 0;
- int optc;
-
- builtin_opt.flags |= GF_PLUSOPT;
- while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
- prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
- switch (optc) {
- case 'd':
-#ifdef MKSH_NOPWNAM
- t = NULL; /* fix "alias -dt" */
-#else
- t = &homedirs;
-#endif
- break;
- case 'p':
- pflag = true;
- break;
- case 'r':
- rflag = true;
- break;
- case 't':
- t = &taliases;
- break;
- case 'U':
- /*
- * kludge for tracked alias initialization
- * (don't do a path search, just make an entry)
- */
- Uflag = true;
- break;
- case 'x':
- xflag = EXPORT;
- break;
- case '?':
- return (1);
- }
- }
-#ifdef MKSH_NOPWNAM
- if (t == NULL)
- return (0);
-#endif
- wp += builtin_opt.optind;
-
- if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
- (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
- prefix = wp[0][0];
- wp++;
- }
-
- tflag = t == &taliases;
-
- /* "hash -r" means reset all the tracked aliases.. */
- if (rflag) {
- static const char *args[] = {
- "unalias", "-ta", NULL
- };
-
- if (!tflag || *wp) {
- shf_puts("alias: -r flag can only be used with -t"
- " and without arguments\n", shl_stdout);
- return (1);
- }
- ksh_getopt_reset(&builtin_opt, GF_ERROR);
- return (c_unalias(args));
- }
-
- if (*wp == NULL) {
- struct tbl *ap, **p;
-
- for (p = ktsort(t); (ap = *p++) != NULL; )
- if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
- if (pflag)
- shf_puts("alias ", shl_stdout);
- shf_puts(ap->name, shl_stdout);
- if (prefix != '+') {
- shf_putc('=', shl_stdout);
- print_value_quoted(ap->val.s);
- }
- shf_putc('\n', shl_stdout);
- }
- }
-
- for (; *wp != NULL; wp++) {
- const char *alias = *wp, *val, *newval;
- char *xalias = NULL;
- struct tbl *ap;
- uint32_t h;
-
- if ((val = cstrchr(alias, '='))) {
- strndupx(xalias, alias, val++ - alias, ATEMP);
- alias = xalias;
- }
- h = hash(alias);
- if (val == NULL && !tflag && !xflag) {
- ap = ktsearch(t, alias, h);
- if (ap != NULL && (ap->flag&ISSET)) {
- if (pflag)
- shf_puts("alias ", shl_stdout);
- shf_puts(ap->name, shl_stdout);
- if (prefix != '+') {
- shf_putc('=', shl_stdout);
- print_value_quoted(ap->val.s);
- }
- shf_putc('\n', shl_stdout);
- } else {
- shprintf("%s alias not found\n", alias);
- rv = 1;
- }
- continue;
- }
- ap = ktenter(t, alias, h);
- ap->type = tflag ? CTALIAS : CALIAS;
- /* Are we setting the value or just some flags? */
- if ((val && !tflag) || (!val && tflag && !Uflag)) {
- if (ap->flag&ALLOC) {
- ap->flag &= ~(ALLOC|ISSET);
- afree(ap->val.s, APERM);
- }
- /* ignore values for -t (AT&T ksh does this) */
- newval = tflag ? search(alias, path, X_OK, NULL) : val;
- if (newval) {
- strdupx(ap->val.s, newval, APERM);
- ap->flag |= ALLOC|ISSET;
- } else
- ap->flag &= ~ISSET;
- }
- ap->flag |= DEFINED;
- if (prefix == '+')
- ap->flag &= ~xflag;
- else
- ap->flag |= xflag;
- afree(xalias, ATEMP);
- }
-
- return (rv);
-}
-
-int
-c_unalias(const char **wp)
-{
- struct table *t = &aliases;
- struct tbl *ap;
- int optc, rv = 0;
- bool all = false;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
- switch (optc) {
- case 'a':
- all = true;
- break;
- case 'd':
-#ifdef MKSH_NOPWNAM
- t = NULL; /* fix "unalias -dt" */
-#else
- t = &homedirs;
-#endif
- break;
- case 't':
- t = &taliases;
- break;
- case '?':
- return (1);
- }
-#ifdef MKSH_NOPWNAM
- if (t == NULL)
- return (0);
-#endif
- wp += builtin_opt.optind;
-
- for (; *wp != NULL; wp++) {
- ap = ktsearch(t, *wp, hash(*wp));
- if (ap == NULL) {
- rv = 1; /* POSIX */
- continue;
- }
- if (ap->flag&ALLOC) {
- ap->flag &= ~(ALLOC|ISSET);
- afree(ap->val.s, APERM);
- }
- ap->flag &= ~(DEFINED|ISSET|EXPORT);
- }
-
- if (all) {
- struct tstate ts;
-
- for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
- if (ap->flag&ALLOC) {
- ap->flag &= ~(ALLOC|ISSET);
- afree(ap->val.s, APERM);
- }
- ap->flag &= ~(DEFINED|ISSET|EXPORT);
- }
- }
-
- return (rv);
-}
-
-int
-c_let(const char **wp)
-{
- int rv = 1;
- mksh_ari_t val;
-
- if (wp[1] == NULL) /* AT&T ksh does this */
- bi_errorf("no arguments");
- else
- for (wp++; *wp; wp++)
- if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
- rv = 2; /* distinguish error from zero result */
- break;
- } else
- rv = val == 0;
- return (rv);
-}
-
-int
-c_jobs(const char **wp)
-{
- int optc, flag = 0, nflag = 0, rv = 0;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
- switch (optc) {
- case 'l':
- flag = 1;
- break;
- case 'p':
- flag = 2;
- break;
- case 'n':
- nflag = 1;
- break;
- case 'z': /* debugging: print zombies */
- nflag = -1;
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
- if (!*wp) {
- if (j_jobs(NULL, flag, nflag))
- rv = 1;
- } else {
- for (; *wp; wp++)
- if (j_jobs(*wp, flag, nflag))
- rv = 1;
- }
- return (rv);
-}
-
-#ifndef MKSH_UNEMPLOYED
-int
-c_fgbg(const char **wp)
-{
- bool bg = strcmp(*wp, "bg") == 0;
- int rv = 0;
-
- if (!Flag(FMONITOR)) {
- bi_errorf("job control not enabled");
- return (1);
- }
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- wp += builtin_opt.optind;
- if (*wp)
- for (; *wp; wp++)
- rv = j_resume(*wp, bg);
- else
- rv = j_resume("%%", bg);
- return (bg ? 0 : rv);
-}
-#endif
-
-/* format a single kill item */
-static char *
-kill_fmt_entry(char *buf, int buflen, int i, const void *arg)
-{
- const struct kill_info *ki = (const struct kill_info *)arg;
-
- i++;
- shf_snprintf(buf, buflen, "%*d %*s %s",
- ki->num_width, i,
- ki->name_width, sigtraps[i].name,
- sigtraps[i].mess);
- return (buf);
-}
-
-int
-c_kill(const char **wp)
-{
- Trap *t = NULL;
- const char *p;
- bool lflag = false;
- int i, n, rv, sig;
-
- /* assume old style options if -digits or -UPPERCASE */
- if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
- ksh_isupper(p[1]))) {
- if (!(t = gettrap(p + 1, true))) {
- bi_errorf("bad signal '%s'", p + 1);
- return (1);
- }
- i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
- } else {
- int optc;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
- switch (optc) {
- case 'l':
- lflag = true;
- break;
- case 's':
- if (!(t = gettrap(builtin_opt.optarg, true))) {
- bi_errorf("bad signal '%s'",
- builtin_opt.optarg);
- return (1);
- }
- break;
- case '?':
- return (1);
- }
- i = builtin_opt.optind;
- }
- if ((lflag && t) || (!wp[i] && !lflag)) {
-#ifndef MKSH_SMALL
- shf_puts("usage:\tkill [-s signame | -signum | -signame]"
- " { job | pid | pgrp } ...\n"
- "\tkill -l [exit_status ...]\n", shl_out);
-#endif
- bi_errorfz();
- return (1);
- }
-
- if (lflag) {
- if (wp[i]) {
- for (; wp[i]; i++) {
- if (!bi_getn(wp[i], &n))
- return (1);
- if (n > 128 && n < 128 + NSIG)
- n -= 128;
- if (n > 0 && n < NSIG)
- shprintf("%s\n", sigtraps[n].name);
- else
- shprintf("%d\n", n);
- }
- } else {
- int w, j, mess_cols, mess_octs;
- struct kill_info ki;
-
- for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
- ki.num_width++;
- ki.name_width = mess_cols = mess_octs = 0;
- for (j = 0; j < NSIG; j++) {
- w = strlen(sigtraps[j].name);
- if (w > ki.name_width)
- ki.name_width = w;
- w = strlen(sigtraps[j].mess);
- if (w > mess_octs)
- mess_octs = w;
- w = utf_mbswidth(sigtraps[j].mess);
- if (w > mess_cols)
- mess_cols = w;
- }
-
- print_columns(shl_stdout, NSIG - 1,
- kill_fmt_entry, (void *)&ki,
- ki.num_width + 1 + ki.name_width + 1 + mess_octs,
- ki.num_width + 1 + ki.name_width + 1 + mess_cols,
- true);
- }
- return (0);
- }
- rv = 0;
- sig = t ? t->signal : SIGTERM;
- for (; (p = wp[i]); i++) {
- if (*p == '%') {
- if (j_kill(p, sig))
- rv = 1;
- } else if (!getn(p, &n)) {
- bi_errorf("%s: arguments must be jobs or process IDs",
- p);
- rv = 1;
- } else {
- if (mksh_kill(n, sig) < 0) {
- bi_errorf("%s: %s", p, strerror(errno));
- rv = 1;
- }
- }
- }
- return (rv);
-}
-
-void
-getopts_reset(int val)
-{
- if (val >= 1) {
- ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
- user_opt.optind = user_opt.uoptind = val;
- }
-}
-
-int
-c_getopts(const char **wp)
-{
- int argc, optc, rv;
- const char *opts, *var;
- char buf[3];
- struct tbl *vq, *voptarg;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- wp += builtin_opt.optind;
-
- opts = *wp++;
- if (!opts) {
- bi_errorf("missing options argument");
- return (1);
- }
-
- var = *wp++;
- if (!var) {
- bi_errorf("missing name argument");
- return (1);
- }
- if (!*var || *skip_varname(var, true)) {
- bi_errorf("%s: is not an identifier", var);
- return (1);
- }
-
- if (e->loc->next == NULL) {
- internal_warningf("c_getopts: no argv");
- return (1);
- }
- /* Which arguments are we parsing... */
- if (*wp == NULL)
- wp = e->loc->next->argv;
- else
- *--wp = e->loc->next->argv[0];
-
- /* Check that our saved state won't cause a core dump... */
- for (argc = 0; wp[argc]; argc++)
- ;
- if (user_opt.optind > argc ||
- (user_opt.p != 0 &&
- user_opt.p > strlen(wp[user_opt.optind - 1]))) {
- bi_errorf("arguments changed since last call");
- return (1);
- }
-
- user_opt.optarg = NULL;
- optc = ksh_getopt(wp, &user_opt, opts);
-
- if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
- buf[0] = '+';
- buf[1] = optc;
- buf[2] = '\0';
- } else {
- /* POSIX says var is set to ? at end-of-options, AT&T ksh
- * sets it to null - we go with POSIX...
- */
- buf[0] = optc < 0 ? '?' : optc;
- buf[1] = '\0';
- }
-
- /* AT&T ksh93 in fact does change OPTIND for unknown options too */
- user_opt.uoptind = user_opt.optind;
-
- voptarg = global("OPTARG");
- voptarg->flag &= ~RDONLY; /* AT&T ksh clears ro and int */
- /* Paranoia: ensure no bizarre results. */
- if (voptarg->flag & INTEGER)
- typeset("OPTARG", 0, INTEGER, 0, 0);
- if (user_opt.optarg == NULL)
- unset(voptarg, 1);
- else
- /* This can't fail (have cleared readonly/integer) */
- setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
-
- rv = 0;
-
- vq = global(var);
- /* Error message already printed (integer, readonly) */
- if (!setstr(vq, buf, KSH_RETURN_ERROR))
- rv = 1;
- if (Flag(FEXPORT))
- typeset(var, EXPORT, 0, 0, 0);
-
- return (optc < 0 ? 1 : rv);
-}
-
-int
-c_bind(const char **wp)
-{
- int optc, rv = 0;
-#ifndef MKSH_SMALL
- bool macro = false;
-#endif
- bool list = false;
- const char *cp;
- char *up;
-
- while ((optc = ksh_getopt(wp, &builtin_opt,
-#ifndef MKSH_SMALL
- "lm"
-#else
- "l"
-#endif
- )) != -1)
- switch (optc) {
- case 'l':
- list = true;
- break;
-#ifndef MKSH_SMALL
- case 'm':
- macro = true;
- break;
-#endif
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
-
- if (*wp == NULL) /* list all */
- rv = x_bind(NULL, NULL,
-#ifndef MKSH_SMALL
- false,
-#endif
- list);
-
- for (; *wp != NULL; wp++) {
- if ((cp = cstrchr(*wp, '=')) == NULL)
- up = NULL;
- else {
- strdupx(up, *wp, ATEMP);
- up[cp++ - *wp] = '\0';
- }
- if (x_bind(up ? up : *wp, cp,
-#ifndef MKSH_SMALL
- macro,
-#endif
- false))
- rv = 1;
- afree(up, ATEMP);
- }
-
- return (rv);
-}
-
-/* :, false and true (and ulimit if MKSH_NO_LIMITS) */
-int
-c_label(const char **wp)
-{
- return (wp[0][0] == 'f' ? 1 : 0);
-}
-
-int
-c_shift(const char **wp)
-{
- struct block *l = e->loc;
- int n;
- mksh_ari_t val;
- const char *arg;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- arg = wp[builtin_opt.optind];
-
- if (arg) {
- evaluate(arg, &val, KSH_UNWIND_ERROR, false);
- n = val;
- } else
- n = 1;
- if (n < 0) {
- bi_errorf("%s: bad number", arg);
- return (1);
- }
- if (l->argc < n) {
- bi_errorf("nothing to shift");
- return (1);
- }
- l->argv[n] = l->argv[0];
- l->argv += n;
- l->argc -= n;
- return (0);
-}
-
-int
-c_umask(const char **wp)
-{
- int i, optc;
- const char *cp;
- bool symbolic = false;
- mode_t old_umask;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1)
- switch (optc) {
- case 'S':
- symbolic = true;
- break;
- case '?':
- return (1);
- }
- cp = wp[builtin_opt.optind];
- if (cp == NULL) {
- old_umask = umask((mode_t)0);
- umask(old_umask);
- if (symbolic) {
- char buf[18], *p;
- int j;
-
- old_umask = ~old_umask;
- p = buf;
- for (i = 0; i < 3; i++) {
- *p++ = "ugo"[i];
- *p++ = '=';
- for (j = 0; j < 3; j++)
- if (old_umask & (1 << (8 - (3*i + j))))
- *p++ = "rwx"[j];
- *p++ = ',';
- }
- p[-1] = '\0';
- shprintf("%s\n", buf);
- } else
- shprintf("%#3.3o\n", (unsigned int)old_umask);
- } else {
- mode_t new_umask;
-
- if (ksh_isdigit(*cp)) {
- for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
- new_umask = new_umask * 8 + (*cp - '0');
- if (*cp) {
- bi_errorf("bad number");
- return (1);
- }
- } else {
- /* symbolic format */
- int positions, new_val;
- char op;
-
- old_umask = umask((mode_t)0);
- umask(old_umask); /* in case of error */
- old_umask = ~old_umask;
- new_umask = old_umask;
- positions = 0;
- while (*cp) {
- while (*cp && vstrchr("augo", *cp))
- switch (*cp++) {
- case 'a':
- positions |= 0111;
- break;
- case 'u':
- positions |= 0100;
- break;
- case 'g':
- positions |= 0010;
- break;
- case 'o':
- positions |= 0001;
- break;
- }
- if (!positions)
- positions = 0111; /* default is a */
- if (!vstrchr("=+-", op = *cp))
- break;
- cp++;
- new_val = 0;
- while (*cp && vstrchr("rwxugoXs", *cp))
- switch (*cp++) {
- case 'r': new_val |= 04; break;
- case 'w': new_val |= 02; break;
- case 'x': new_val |= 01; break;
- case 'u':
- new_val |= old_umask >> 6;
- break;
- case 'g':
- new_val |= old_umask >> 3;
- break;
- case 'o':
- new_val |= old_umask >> 0;
- break;
- case 'X':
- if (old_umask & 0111)
- new_val |= 01;
- break;
- case 's':
- /* ignored */
- break;
- }
- new_val = (new_val & 07) * positions;
- switch (op) {
- case '-':
- new_umask &= ~new_val;
- break;
- case '=':
- new_umask = new_val |
- (new_umask & ~(positions * 07));
- break;
- case '+':
- new_umask |= new_val;
- }
- if (*cp == ',') {
- positions = 0;
- cp++;
- } else if (!vstrchr("=+-", *cp))
- break;
- }
- if (*cp) {
- bi_errorf("bad mask");
- return (1);
- }
- new_umask = ~new_umask;
- }
- umask(new_umask);
- }
- return (0);
-}
-
-int
-c_dot(const char **wp)
-{
- const char *file, *cp, **argv;
- int argc, i, errcode;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
-
- if ((cp = wp[builtin_opt.optind]) == NULL) {
- bi_errorf("missing argument");
- return (1);
- }
- if ((file = search(cp, path, R_OK, &errcode)) == NULL) {
- bi_errorf("%s: %s", cp,
- errcode ? strerror(errcode) : "not found");
- return (1);
- }
-
- /* Set positional parameters? */
- if (wp[builtin_opt.optind + 1]) {
- argv = wp + builtin_opt.optind;
- argv[0] = e->loc->argv[0]; /* preserve $0 */
- for (argc = 0; argv[argc + 1]; argc++)
- ;
- } else {
- argc = 0;
- argv = NULL;
- }
- if ((i = include(file, argc, argv, 0)) < 0) {
- /* should not happen */
- bi_errorf("%s: %s", cp, strerror(errno));
- return (1);
- }
- return (i);
-}
-
-int
-c_wait(const char **wp)
-{
- int rv = 0, sig;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- wp += builtin_opt.optind;
- if (*wp == NULL) {
- while (waitfor(NULL, &sig) >= 0)
- ;
- rv = sig;
- } else {
- for (; *wp; wp++)
- rv = waitfor(*wp, &sig);
- if (rv < 0)
- rv = sig ? sig : 127; /* magic exit code: bad job-id */
- }
- return (rv);
-}
-
-int
-c_read(const char **wp)
-{
- int c = 0, ecode = 0, fd = 0, optc;
- bool expande = true, historyr = false, expanding;
- const char *cp, *emsg;
- struct shf *shf;
- XString cs, xs = { NULL, NULL, 0, NULL};
- struct tbl *vp;
- char *ccp, *xp = NULL, *wpalloc = NULL;
- static char REPLY[] = "REPLY";
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != -1)
- switch (optc) {
- case 'p':
- if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
- bi_errorf("-p: %s", emsg);
- return (1);
- }
- break;
- case 'r':
- expande = false;
- break;
- case 's':
- historyr = true;
- break;
- case 'u':
- if (!*(cp = builtin_opt.optarg))
- fd = 0;
- else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) {
- bi_errorf("-u: %s: %s", cp, emsg);
- return (1);
- }
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
-
- if (*wp == NULL)
- *--wp = REPLY;
-
- /* Since we can't necessarily seek backwards on non-regular files,
- * don't buffer them so we can't read too much.
- */
- shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare);
-
- if ((cp = cstrchr(*wp, '?')) != NULL) {
- strdupx(wpalloc, *wp, ATEMP);
- wpalloc[cp - *wp] = '\0';
- *wp = wpalloc;
- if (isatty(fd)) {
- /* AT&T ksh says it prints prompt on fd if it's open
- * for writing and is a tty, but it doesn't do it
- * (it also doesn't check the interactive flag,
- * as is indicated in the Kornshell book).
- */
- shellf("%s", cp+1);
- }
- }
-
- /* If we are reading from the co-process for the first time,
- * make sure the other side of the pipe is closed first. This allows
- * the detection of eof.
- *
- * This is not compatible with AT&T ksh... the fd is kept so another
- * coproc can be started with same output, however, this means eof
- * can't be detected... This is why it is closed here.
- * If this call is removed, remove the eof check below, too.
- * coproc_readw_close(fd);
- */
-
- if (historyr)
- Xinit(xs, xp, 128, ATEMP);
- expanding = false;
- Xinit(cs, ccp, 128, ATEMP);
- for (; *wp != NULL; wp++) {
- for (ccp = Xstring(cs, ccp); ; ) {
- if (c == '\n' || c == EOF)
- break;
- while (1) {
- c = shf_getc(shf);
- if (c == '\0')
- continue;
- if (c == EOF && shf_error(shf) &&
- shf_errno(shf) == EINTR) {
- /* Was the offending signal one that
- * would normally kill a process?
- * If so, pretend the read was killed.
- */
- ecode = fatal_trap_check();
-
- /* non fatal (eg, CHLD), carry on */
- if (!ecode) {
- shf_clearerr(shf);
- continue;
- }
- }
- break;
- }
- if (historyr) {
- Xcheck(xs, xp);
- Xput(xs, xp, c);
- }
- Xcheck(cs, ccp);
- if (expanding) {
- expanding = false;
- if (c == '\n') {
- c = 0;
- if (Flag(FTALKING_I) && isatty(fd)) {
- /* set prompt in case this is
- * called from .profile or $ENV
- */
- set_prompt(PS2, NULL);
- pprompt(prompt, 0);
- }
- } else if (c != EOF)
- Xput(cs, ccp, c);
- continue;
- }
- if (expande && c == '\\') {
- expanding = true;
- continue;
- }
- if (c == '\n' || c == EOF)
- break;
- if (ctype(c, C_IFS)) {
- if (Xlength(cs, ccp) == 0 && ctype(c, C_IFSWS))
- continue;
- if (wp[1])
- break;
- }
- Xput(cs, ccp, c);
- }
- /* strip trailing IFS white space from last variable */
- if (!wp[1])
- while (Xlength(cs, ccp) && ctype(ccp[-1], C_IFS) &&
- ctype(ccp[-1], C_IFSWS))
- ccp--;
- Xput(cs, ccp, '\0');
- vp = global(*wp);
- /* Must be done before setting export. */
- if (vp->flag & RDONLY) {
- shf_flush(shf);
- bi_errorf("%s is read only", *wp);
- afree(wpalloc, ATEMP);
- return (1);
- }
- if (Flag(FEXPORT))
- typeset(*wp, EXPORT, 0, 0, 0);
- if (!setstr(vp, Xstring(cs, ccp), KSH_RETURN_ERROR)) {
- shf_flush(shf);
- afree(wpalloc, ATEMP);
- return (1);
- }
- }
-
- shf_flush(shf);
- if (historyr) {
- Xput(xs, xp, '\0');
- histsave(&source->line, Xstring(xs, xp), true, false);
- Xfree(xs, xp);
- }
- /* if this is the co-process fd, close the file descriptor
- * (can get eof if and only if all processes are have died, ie,
- * coproc.njobs is 0 and the pipe is closed).
- */
- if (c == EOF && !ecode)
- coproc_read_close(fd);
-
- afree(wpalloc, ATEMP);
- return (ecode ? ecode : c == EOF);
-}
-
-int
-c_eval(const char **wp)
-{
- struct source *s, *saves = source;
- unsigned char savef;
- int rv;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- s = pushs(SWORDS, ATEMP);
- s->u.strv = wp + builtin_opt.optind;
-
- /*-
- * The following code handles the case where the command is
- * empty due to failed command substitution, for example by
- * eval "$(false)"
- * This has historically returned 1 by AT&T ksh88. In this
- * case, shell() will not set or change exstat because the
- * compiled tree is empty, so it will use the value we pass
- * from subst_exstat, which is cleared in execute(), so it
- * should have been 0 if there were no substitutions.
- *
- * POSIX however says we don't do this, even though it is
- * traditionally done. AT&T ksh93 agrees with POSIX, so we
- * do. The following is an excerpt from SUSv4 [1003.2-2008]:
- *
- * 2.9.1: Simple Commands
- * ... If there is a command name, execution shall
- * continue as described in 2.9.1.1 [Command Search
- * and Execution]. If there is no command name, but
- * the command contained a command substitution, the
- * command shall complete with the exit status of the
- * last command substitution performed.
- * 2.9.1.1: Command Search and Execution
- * (1) a. If the command name matches the name of a
- * special built-in utility, that special built-in
- * utility shall be invoked.
- * 2.14.5: eval
- * If there are no arguments, or only null arguments,
- * eval shall return a zero exit status; ...
- */
- /* exstat = subst_exstat; */ /* AT&T ksh88 */
- exstat = 0; /* SUSv4 */
-
- savef = Flag(FERREXIT);
- Flag(FERREXIT) = 0;
- rv = shell(s, false);
- Flag(FERREXIT) = savef;
- source = saves;
- afree(s, ATEMP);
- return (rv);
-}
-
-int
-c_trap(const char **wp)
-{
- int i;
- const char *s;
- Trap *p;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- wp += builtin_opt.optind;
-
- if (*wp == NULL) {
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
- if (p->trap != NULL) {
- shf_puts("trap -- ", shl_stdout);
- print_value_quoted(p->trap);
- shprintf(" %s\n", p->name);
- }
- return (0);
- }
-
- /*
- * Use case sensitive lookup for first arg so the
- * command 'exit' isn't confused with the pseudo-signal
- * 'EXIT'.
- */
- s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; /* get command */
- if (s != NULL && s[0] == '-' && s[1] == '\0')
- s = NULL;
-
- /* set/clear traps */
- while (*wp != NULL) {
- p = gettrap(*wp++, true);
- if (p == NULL) {
- bi_errorf("bad signal %s", wp[-1]);
- return (1);
- }
- settrap(p, s);
- }
- return (0);
-}
-
-int
-c_exitreturn(const char **wp)
-{
- int n, how = LEXIT;
- const char *arg;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- arg = wp[builtin_opt.optind];
-
- if (arg) {
- if (!getn(arg, &n)) {
- exstat = 1;
- warningf(true, "%s: bad number", arg);
- } else
- exstat = n;
- }
- if (wp[0][0] == 'r') { /* return */
- struct env *ep;
-
- /* need to tell if this is exit or return so trap exit will
- * work right (POSIX)
- */
- for (ep = e; ep; ep = ep->oenv)
- if (STOP_RETURN(ep->type)) {
- how = LRETURN;
- break;
- }
- }
-
- if (how == LEXIT && !really_exit && j_stopped_running()) {
- really_exit = 1;
- how = LSHELL;
- }
-
- quitenv(NULL); /* get rid of any i/o redirections */
- unwind(how);
- /* NOTREACHED */
-}
-
-int
-c_brkcont(const char **wp)
-{
- int n, quit;
- struct env *ep, *last_ep = NULL;
- const char *arg;
-
- if (ksh_getopt(wp, &builtin_opt, null) == '?')
- return (1);
- arg = wp[builtin_opt.optind];
-
- if (!arg)
- n = 1;
- else if (!bi_getn(arg, &n))
- return (1);
- quit = n;
- if (quit <= 0) {
- /* AT&T ksh does this for non-interactive shells only - weird */
- bi_errorf("%s: bad value", arg);
- return (1);
- }
-
- /* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
- for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
- if (ep->type == E_LOOP) {
- if (--quit == 0)
- break;
- ep->flags |= EF_BRKCONT_PASS;
- last_ep = ep;
- }
-
- if (quit) {
- /* AT&T ksh doesn't print a message - just does what it
- * can. We print a message 'cause it helps in debugging
- * scripts, but don't generate an error (ie, keep going).
- */
- if (n == quit) {
- warningf(true, "%s: cannot %s", wp[0], wp[0]);
- return (0);
- }
- /* POSIX says if n is too big, the last enclosing loop
- * shall be used. Doesn't say to print an error but we
- * do anyway 'cause the user messed up.
- */
- if (last_ep)
- last_ep->flags &= ~EF_BRKCONT_PASS;
- warningf(true, "%s: can only %s %d level(s)",
- wp[0], wp[0], n - quit);
- }
-
- unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
- /* NOTREACHED */
-}
-
-int
-c_set(const char **wp)
-{
- int argi;
- bool setargs;
- struct block *l = e->loc;
- const char **owp;
-
- if (wp[1] == NULL) {
- static const char *args[] = { "set", "-", NULL };
- return (c_typeset(args));
- }
-
- argi = parse_args(wp, OF_SET, &setargs);
- if (argi < 0)
- return (1);
- /* set $# and $* */
- if (setargs) {
- wp += argi - 1;
- owp = wp;
- wp[0] = l->argv[0]; /* save $0 */
- while (*++wp != NULL)
- strdupx(*wp, *wp, &l->area);
- l->argc = wp - owp - 1;
- l->argv = alloc((l->argc + 2) * sizeof(char *), &l->area);
- for (wp = l->argv; (*wp++ = *owp++) != NULL; )
- ;
- }
- /*-
- * POSIX says set exit status is 0, but old scripts that use
- * getopt(1) use the construct
- * set -- $(getopt ab:c "$@")
- * which assumes the exit value set will be that of the $()
- * (subst_exstat is cleared in execute() so that it will be 0
- * if there are no command substitutions).
- * Switched ksh (!posix !sh) to POSIX in mksh R39b.
- */
- return (Flag(FSH) ? subst_exstat : 0);
-}
-
-int
-c_unset(const char **wp)
-{
- const char *id;
- int optc;
- bool unset_var = true;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1)
- switch (optc) {
- case 'f':
- unset_var = false;
- break;
- case 'v':
- unset_var = true;
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
- for (; (id = *wp) != NULL; wp++)
- if (unset_var) { /* unset variable */
- struct tbl *vp;
- char *cp = NULL;
- size_t n;
-
- n = strlen(id);
- if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
- id[n-1] == ']') {
- strndupx(cp, id, n - 3, ATEMP);
- id = cp;
- optc = 3;
- } else
- optc = vstrchr(id, '[') ? 0 : 1;
-
- vp = global(id);
- afree(cp, ATEMP);
-
- if ((vp->flag&RDONLY)) {
- bi_errorf("%s is read only", vp->name);
- return (1);
- }
- unset(vp, optc);
- } else /* unset function */
- define(id, NULL);
- return (0);
-}
-
-static void
-p_time(struct shf *shf, bool posix, long tv_sec, int tv_usec, int width,
- const char *prefix, const char *suffix)
-{
- tv_usec /= 10000;
- if (posix)
- shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
- tv_sec, tv_usec, suffix);
- else
- shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
- tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
-}
-
-int
-c_times(const char **wp MKSH_A_UNUSED)
-{
- struct rusage usage;
-
- getrusage(RUSAGE_SELF, &usage);
- p_time(shl_stdout, false, usage.ru_utime.tv_sec,
- usage.ru_utime.tv_usec, 0, null, " ");
- p_time(shl_stdout, false, usage.ru_stime.tv_sec,
- usage.ru_stime.tv_usec, 0, null, "\n");
-
- getrusage(RUSAGE_CHILDREN, &usage);
- p_time(shl_stdout, false, usage.ru_utime.tv_sec,
- usage.ru_utime.tv_usec, 0, null, " ");
- p_time(shl_stdout, false, usage.ru_stime.tv_sec,
- usage.ru_stime.tv_usec, 0, null, "\n");
-
- return (0);
-}
-
-/*
- * time pipeline (really a statement, not a built-in command)
- */
-int
-timex(struct op *t, int f, volatile int *xerrok)
-{
-#define TF_NOARGS BIT(0)
-#define TF_NOREAL BIT(1) /* don't report real time */
-#define TF_POSIX BIT(2) /* report in POSIX format */
- int rv = 0, tf = 0;
- struct rusage ru0, ru1, cru0, cru1;
- struct timeval usrtime, systime, tv0, tv1;
-
- gettimeofday(&tv0, NULL);
- getrusage(RUSAGE_SELF, &ru0);
- getrusage(RUSAGE_CHILDREN, &cru0);
- if (t->left) {
- /*
- * Two ways of getting cpu usage of a command: just use t0
- * and t1 (which will get cpu usage from other jobs that
- * finish while we are executing t->left), or get the
- * cpu usage of t->left. AT&T ksh does the former, while
- * pdksh tries to do the later (the j_usrtime hack doesn't
- * really work as it only counts the last job).
- */
- timerclear(&j_usrtime);
- timerclear(&j_systime);
- rv = execute(t->left, f | XTIME, xerrok);
- if (t->left->type == TCOM)
- tf |= t->left->str[0];
- gettimeofday(&tv1, NULL);
- getrusage(RUSAGE_SELF, &ru1);
- getrusage(RUSAGE_CHILDREN, &cru1);
- } else
- tf = TF_NOARGS;
-
- if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */
- tf |= TF_NOREAL;
- timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
- timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
- } else {
- timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
- timeradd(&usrtime, &j_usrtime, &usrtime);
- timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
- timeradd(&systime, &j_systime, &systime);
- }
-
- if (!(tf & TF_NOREAL)) {
- timersub(&tv1, &tv0, &tv1);
- if (tf & TF_POSIX)
- p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
- 5, "real ", "\n");
- else
- p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
- 5, null, " real ");
- }
- if (tf & TF_POSIX)
- p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
- 5, "user ", "\n");
- else
- p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
- 5, null, " user ");
- if (tf & TF_POSIX)
- p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
- 5, "sys ", "\n");
- else
- p_time(shl_out, false, systime.tv_sec, systime.tv_usec,
- 5, null, " system\n");
- shf_flush(shl_out);
-
- return (rv);
-}
-
-void
-timex_hook(struct op *t, char **volatile *app)
-{
- char **wp = *app;
- int optc, i, j;
- Getopt opt;
-
- ksh_getopt_reset(&opt, 0);
- opt.optind = 0; /* start at the start */
- while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
- switch (optc) {
- case 'p':
- t->str[0] |= TF_POSIX;
- break;
- case '?':
- errorf("time: -%s unknown option", opt.optarg);
- case ':':
- errorf("time: -%s requires an argument",
- opt.optarg);
- }
- /* Copy command words down over options. */
- if (opt.optind != 0) {
- for (i = 0; i < opt.optind; i++)
- afree(wp[i], ATEMP);
- for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
- ;
- }
- if (!wp[0])
- t->str[0] |= TF_NOARGS;
- *app = wp;
-}
-
-/* exec with no args - args case is taken care of in comexec() */
-int
-c_exec(const char **wp MKSH_A_UNUSED)
-{
- int i;
-
- /* make sure redirects stay in place */
- if (e->savefd != NULL) {
- for (i = 0; i < NUFILE; i++) {
- if (e->savefd[i] > 0)
- close(e->savefd[i]);
- /*
- * keep all file descriptors > 2 private for ksh,
- * but not for POSIX or legacy/kludge sh
- */
- if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
- e->savefd[i])
- fcntl(i, F_SETFD, FD_CLOEXEC);
- }
- e->savefd = NULL;
- }
- return (0);
-}
-
-#if HAVE_MKNOD
-int
-c_mknod(const char **wp)
-{
- int argc, optc, rv = 0;
- bool ismkfifo = false;
- const char **argv;
- void *set = NULL;
- mode_t mode = 0, oldmode = 0;
-
- while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) {
- switch (optc) {
- case 'm':
- set = setmode(builtin_opt.optarg);
- if (set == NULL) {
- bi_errorf("invalid file mode");
- return (1);
- }
- mode = getmode(set, (mode_t)(DEFFILEMODE));
- free(set);
- break;
- default:
- goto c_mknod_usage;
- }
- }
- argv = &wp[builtin_opt.optind];
- if (argv[0] == NULL)
- goto c_mknod_usage;
- for (argc = 0; argv[argc]; argc++)
- ;
- if (argc == 2 && argv[1][0] == 'p')
- ismkfifo = true;
- else if (argc != 4 || (argv[1][0] != 'b' && argv[1][0] != 'c'))
- goto c_mknod_usage;
-
- if (set != NULL)
- oldmode = umask((mode_t)0);
- else
- mode = DEFFILEMODE;
-
- mode |= (argv[1][0] == 'b') ? S_IFBLK :
- (argv[1][0] == 'c') ? S_IFCHR : 0;
-
- if (!ismkfifo) {
- unsigned long majnum, minnum;
- dev_t dv;
- char *c;
-
- majnum = strtoul(argv[2], &c, 0);
- if ((c == argv[2]) || (*c != '\0')) {
- bi_errorf("non-numeric device major '%s'", argv[2]);
- goto c_mknod_err;
- }
- minnum = strtoul(argv[3], &c, 0);
- if ((c == argv[3]) || (*c != '\0')) {
- bi_errorf("non-numeric device minor '%s'", argv[3]);
- goto c_mknod_err;
- }
- dv = makedev(majnum, minnum);
- if ((unsigned long)(major(dv)) != majnum) {
- bi_errorf("device major too large: %lu", majnum);
- goto c_mknod_err;
- }
- if ((unsigned long)(minor(dv)) != minnum) {
- bi_errorf("device minor too large: %lu", minnum);
- goto c_mknod_err;
- }
- if (mknod(argv[0], mode, dv))
- goto c_mknod_failed;
- } else if (mkfifo(argv[0], mode)) {
- c_mknod_failed:
- bi_errorf("%s: %s", *wp, strerror(errno));
- c_mknod_err:
- rv = 1;
- }
-
- if (set)
- umask(oldmode);
- return (rv);
- c_mknod_usage:
- bi_errorf("usage: mknod [-m mode] name b|c major minor");
- bi_errorf("usage: mknod [-m mode] name p");
- return (1);
-}
-#endif
-
-/* dummy function, special case in comexec() */
-int
-c_builtin(const char **wp MKSH_A_UNUSED)
-{
- return (0);
-}
-
-/* test(1) accepts the following grammar:
- oexpr ::= aexpr | aexpr "-o" oexpr ;
- aexpr ::= nexpr | nexpr "-a" aexpr ;
- nexpr ::= primary | "!" nexpr ;
- primary ::= unary-operator operand
- | operand binary-operator operand
- | operand
- | "(" oexpr ")"
- ;
-
- unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
- "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
- "-L"|"-h"|"-S"|"-H";
-
- binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
- "-nt"|"-ot"|"-ef"|
- "<"|">" # rules used for [[ .. ]] expressions
- ;
- operand ::= <any thing>
-*/
-
-#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */
-
-int
-c_test(const char **wp)
-{
- int argc, res;
- Test_env te;
-
- te.flags = 0;
- te.isa = ptest_isa;
- te.getopnd = ptest_getopnd;
- te.eval = test_eval;
- te.error = ptest_error;
-
- for (argc = 0; wp[argc]; argc++)
- ;
-
- if (strcmp(wp[0], "[") == 0) {
- if (strcmp(wp[--argc], "]") != 0) {
- bi_errorf("missing ]");
- return (T_ERR_EXIT);
- }
- }
-
- te.pos.wp = wp + 1;
- te.wp_end = wp + argc;
-
- /*
- * Handle the special cases from POSIX.2, section 4.62.4.
- * Implementation of all the rules isn't necessary since
- * our parser does the right thing for the omitted steps.
- */
- if (argc <= 5) {
- const char **owp = wp;
- int invert = 0;
- Test_op op;
- const char *opnd1, *opnd2;
-
- while (--argc >= 0) {
- if ((*te.isa)(&te, TM_END))
- return (!0);
- if (argc == 3) {
- opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
- if ((op = (*te.isa)(&te, TM_BINOP))) {
- opnd2 = (*te.getopnd)(&te, op, 1);
- res = (*te.eval)(&te, op, opnd1,
- opnd2, 1);
- if (te.flags & TEF_ERROR)
- return (T_ERR_EXIT);
- if (invert & 1)
- res = !res;
- return (!res);
- }
- /* back up to opnd1 */
- te.pos.wp--;
- }
- if (argc == 1) {
- opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
- if (strcmp(opnd1, "-t") == 0)
- break;
- res = (*te.eval)(&te, TO_STNZE, opnd1,
- NULL, 1);
- if (invert & 1)
- res = !res;
- return (!res);
- }
- if ((*te.isa)(&te, TM_NOT)) {
- invert++;
- } else
- break;
- }
- te.pos.wp = owp + 1;
- }
-
- return (test_parse(&te));
-}
-
-/*
- * Generic test routines.
- */
-
-Test_op
-test_isop(Test_meta meta, const char *s)
-{
- char sc1;
- const struct t_op *tbl;
-
- tbl = meta == TM_UNOP ? u_ops : b_ops;
- if (*s) {
- sc1 = s[1];
- for (; tbl->op_text[0]; tbl++)
- if (sc1 == tbl->op_text[1] && !strcmp(s, tbl->op_text))
- return (tbl->op_num);
- }
- return (TO_NONOP);
-}
-
-int
-test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
- bool do_eval)
-{
- int i, s;
- size_t k;
- struct stat b1, b2;
- mksh_ari_t v1, v2;
-
- if (!do_eval)
- return (0);
-
- switch ((int)op) {
- /*
- * Unary Operators
- */
- case TO_STNZE: /* -n */
- return (*opnd1 != '\0');
- case TO_STZER: /* -z */
- return (*opnd1 == '\0');
- case TO_OPTION: /* -o */
- if ((i = *opnd1) == '!' || i == '?')
- opnd1++;
- if ((k = option(opnd1)) == (size_t)-1)
- return (0);
- return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
- case TO_FILRD: /* -r */
- return (test_eaccess(opnd1, R_OK) == 0);
- case TO_FILWR: /* -w */
- return (test_eaccess(opnd1, W_OK) == 0);
- case TO_FILEX: /* -x */
- return (test_eaccess(opnd1, X_OK) == 0);
- case TO_FILAXST: /* -a */
- case TO_FILEXST: /* -e */
- return (stat(opnd1, &b1) == 0);
- case TO_FILREG: /* -r */
- return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
- case TO_FILID: /* -d */
- return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
- case TO_FILCDEV: /* -c */
- return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
- case TO_FILBDEV: /* -b */
- return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
- case TO_FILFIFO: /* -p */
- return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
- case TO_FILSYM: /* -h -L */
- return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
- case TO_FILSOCK: /* -S */
- return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
- case TO_FILCDF:/* -H HP context dependent files (directories) */
- return (0);
- case TO_FILSETU: /* -u */
- return (stat(opnd1, &b1) == 0 &&
- (b1.st_mode & S_ISUID) == S_ISUID);
- case TO_FILSETG: /* -g */
- return (stat(opnd1, &b1) == 0 &&
- (b1.st_mode & S_ISGID) == S_ISGID);
- case TO_FILSTCK: /* -k */
-#ifdef S_ISVTX
- return (stat(opnd1, &b1) == 0 &&
- (b1.st_mode & S_ISVTX) == S_ISVTX);
-#else
- return (0);
-#endif
- case TO_FILGZ: /* -s */
- return (stat(opnd1, &b1) == 0 && b1.st_size > 0L);
- case TO_FILTT: /* -t */
- if (opnd1 && !bi_getn(opnd1, &i)) {
- te->flags |= TEF_ERROR;
- i = 0;
- } else
- i = isatty(opnd1 ? i : 0);
- return (i);
- case TO_FILUID: /* -O */
- return (stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid);
- case TO_FILGID: /* -G */
- return (stat(opnd1, &b1) == 0 && b1.st_gid == getegid());
- /*
- * Binary Operators
- */
- case TO_STEQL: /* = */
- if (te->flags & TEF_DBRACKET)
- return (gmatchx(opnd1, opnd2, false));
- return (strcmp(opnd1, opnd2) == 0);
- case TO_STNEQ: /* != */
- if (te->flags & TEF_DBRACKET)
- return (!gmatchx(opnd1, opnd2, false));
- return (strcmp(opnd1, opnd2) != 0);
- case TO_STLT: /* < */
- return (strcmp(opnd1, opnd2) < 0);
- case TO_STGT: /* > */
- return (strcmp(opnd1, opnd2) > 0);
- case TO_INTEQ: /* -eq */
- case TO_INTNE: /* -ne */
- case TO_INTGE: /* -ge */
- case TO_INTGT: /* -gt */
- case TO_INTLE: /* -le */
- case TO_INTLT: /* -lt */
- if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
- !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
- /* error already printed.. */
- te->flags |= TEF_ERROR;
- return (1);
- }
- switch ((int)op) {
- case TO_INTEQ:
- return (v1 == v2);
- case TO_INTNE:
- return (v1 != v2);
- case TO_INTGE:
- return (v1 >= v2);
- case TO_INTGT:
- return (v1 > v2);
- case TO_INTLE:
- return (v1 <= v2);
- case TO_INTLT:
- return (v1 < v2);
- }
- case TO_FILNT: /* -nt */
- /* ksh88/ksh93 succeed if file2 can't be stated
- * (subtly different from 'does not exist').
- */
- return (stat(opnd1, &b1) == 0 &&
- (((s = stat(opnd2, &b2)) == 0 &&
- b1.st_mtime > b2.st_mtime) || s < 0));
- case TO_FILOT: /* -ot */
- /* ksh88/ksh93 succeed if file1 can't be stated
- * (subtly different from 'does not exist').
- */
- return (stat(opnd2, &b2) == 0 &&
- (((s = stat(opnd1, &b1)) == 0 &&
- b1.st_mtime < b2.st_mtime) || s < 0));
- case TO_FILEQ: /* -ef */
- return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
- b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
- }
- (*te->error)(te, 0, "internal error: unknown op");
- return (1);
-}
-
-/* On most/all unixen, access() says everything is executable for root... */
-static int
-test_eaccess(const char *pathl, int mode)
-{
- int rv;
-
- if ((rv = access(pathl, mode)) == 0 && ksheuid == 0 && (mode & X_OK)) {
- struct stat statb;
-
- if (stat(pathl, &statb) < 0)
- rv = -1;
- else if (S_ISDIR(statb.st_mode))
- rv = 0;
- else
- rv = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ?
- 0 : -1;
- }
- return (rv);
-}
-
-int
-test_parse(Test_env *te)
-{
- int rv;
-
- rv = test_oexpr(te, 1);
-
- if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
- (*te->error)(te, 0, "unexpected operator/operand");
-
- return ((te->flags & TEF_ERROR) ? T_ERR_EXIT : !rv);
-}
-
-static int
-test_oexpr(Test_env *te, bool do_eval)
-{
- int rv;
-
- if ((rv = test_aexpr(te, do_eval)))
- do_eval = false;
- if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
- return (test_oexpr(te, do_eval) || rv);
- return (rv);
-}
-
-static int
-test_aexpr(Test_env *te, bool do_eval)
-{
- int rv;
-
- if (!(rv = test_nexpr(te, do_eval)))
- do_eval = false;
- if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
- return (test_aexpr(te, do_eval) && rv);
- return (rv);
-}
-
-static int
-test_nexpr(Test_env *te, bool do_eval)
-{
- if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
- return (!test_nexpr(te, do_eval));
- return (test_primary(te, do_eval));
-}
-
-static int
-test_primary(Test_env *te, bool do_eval)
-{
- const char *opnd1, *opnd2;
- int rv;
- Test_op op;
-
- if (te->flags & TEF_ERROR)
- return (0);
- if ((*te->isa)(te, TM_OPAREN)) {
- rv = test_oexpr(te, do_eval);
- if (te->flags & TEF_ERROR)
- return (0);
- if (!(*te->isa)(te, TM_CPAREN)) {
- (*te->error)(te, 0, "missing closing paren");
- return (0);
- }
- return (rv);
- }
- /*
- * Binary should have precedence over unary in this case
- * so that something like test \( -f = -f \) is accepted
- */
- if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end &&
- !test_isop(TM_BINOP, te->pos.wp[1]))) {
- if ((op = (*te->isa)(te, TM_UNOP))) {
- /* unary expression */
- opnd1 = (*te->getopnd)(te, op, do_eval);
- if (!opnd1) {
- (*te->error)(te, -1, "missing argument");
- return (0);
- }
-
- return ((*te->eval)(te, op, opnd1, NULL, do_eval));
- }
- }
- opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
- if (!opnd1) {
- (*te->error)(te, 0, "expression expected");
- return (0);
- }
- if ((op = (*te->isa)(te, TM_BINOP))) {
- /* binary expression */
- opnd2 = (*te->getopnd)(te, op, do_eval);
- if (!opnd2) {
- (*te->error)(te, -1, "missing second argument");
- return (0);
- }
-
- return ((*te->eval)(te, op, opnd1, opnd2, do_eval));
- }
- return ((*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval));
-}
-
-/*
- * Plain test (test and [ .. ]) specific routines.
- */
-
-/*
- * Test if the current token is a whatever. Accepts the current token if
- * it is. Returns 0 if it is not, non-zero if it is (in the case of
- * TM_UNOP and TM_BINOP, the returned value is a Test_op).
- */
-static Test_op
-ptest_isa(Test_env *te, Test_meta meta)
-{
- /* Order important - indexed by Test_meta values */
- static const char *const tokens[] = {
- "-o", "-a", "!", "(", ")"
- };
- Test_op rv;
-
- if (te->pos.wp >= te->wp_end)
- return (meta == TM_END ? TO_NONNULL : TO_NONOP);
-
- if (meta == TM_UNOP || meta == TM_BINOP)
- rv = test_isop(meta, *te->pos.wp);
- else if (meta == TM_END)
- rv = TO_NONOP;
- else
- rv = !strcmp(*te->pos.wp, tokens[(int)meta]) ?
- TO_NONNULL : TO_NONOP;
-
- /* Accept the token? */
- if (rv != TO_NONOP)
- te->pos.wp++;
-
- return (rv);
-}
-
-static const char *
-ptest_getopnd(Test_env *te, Test_op op, bool do_eval MKSH_A_UNUSED)
-{
- if (te->pos.wp >= te->wp_end)
- return (op == TO_FILTT ? "1" : NULL);
- return (*te->pos.wp++);
-}
-
-static void
-ptest_error(Test_env *te, int ofs, const char *msg)
-{
- const char *op;
-
- te->flags |= TEF_ERROR;
- if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
- bi_errorf("%s: %s", op, msg);
- else
- bi_errorf("%s", msg);
-}
-
-#ifndef MKSH_NO_LIMITS
-#define SOFT 0x1
-#define HARD 0x2
-
-struct limits {
- const char *name;
- int resource; /* resource to get/set */
- int factor; /* multiply by to get rlim_{cur,max} values */
- char option;
-};
-
-static void print_ulimit(const struct limits *, int);
-static int set_ulimit(const struct limits *, const char *, int);
-
-/* Magic to divine the 'm' and 'v' limits */
-
-#ifdef RLIMIT_AS
-#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
- !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
-#define ULIMIT_V_IS_AS
-#elif defined(RLIMIT_VMEM)
-#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
-#define ULIMIT_V_IS_AS
-#else
-#define ULIMIT_V_IS_VMEM
-#endif
-#endif
-#endif
-
-#ifdef RLIMIT_RSS
-#ifdef ULIMIT_V_IS_VMEM
-#define ULIMIT_M_IS_RSS
-#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
-#define ULIMIT_M_IS_VMEM
-#else
-#define ULIMIT_M_IS_RSS
-#endif
-#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
-#undef ULIMIT_M_IS_RSS
-#endif
-#endif
-
-#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
-#define ULIMIT_V_IS_VMEM
-#endif
-
-#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
- (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
-#define ULIMIT_M_IS_VMEM
-#endif
-
-#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
- (RLIMIT_VMEM == RLIMIT_AS)
-#undef ULIMIT_M_IS_VMEM
-#endif
-
-
-int
-c_ulimit(const char **wp)
-{
- static const struct limits limits[] = {
- /* do not use options -H, -S or -a or change the order */
-#ifdef RLIMIT_CPU
- { "time(cpu-seconds)", RLIMIT_CPU, 1, 't' },
-#endif
-#ifdef RLIMIT_FSIZE
- { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
-#endif
-#ifdef RLIMIT_CORE
- { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
-#endif
-#ifdef RLIMIT_DATA
- { "data(KiB)", RLIMIT_DATA, 1024, 'd' },
-#endif
-#ifdef RLIMIT_STACK
- { "stack(KiB)", RLIMIT_STACK, 1024, 's' },
-#endif
-#ifdef RLIMIT_MEMLOCK
- { "lockedmem(KiB)", RLIMIT_MEMLOCK, 1024, 'l' },
-#endif
-#ifdef RLIMIT_NOFILE
- { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
-#endif
-#ifdef RLIMIT_NPROC
- { "processes", RLIMIT_NPROC, 1, 'p' },
-#endif
-#ifdef RLIMIT_SWAP
- { "swap(KiB)", RLIMIT_SWAP, 1024, 'w' },
-#endif
-#ifdef RLIMIT_LOCKS
- { "flocks", RLIMIT_LOCKS, -1, 'L' },
-#endif
-#ifdef RLIMIT_TIME
- { "humantime(seconds)", RLIMIT_TIME, 1, 'T' },
-#endif
-#ifdef RLIMIT_NOVMON
- { "vnodemonitors", RLIMIT_NOVMON, 1, 'V' },
-#endif
-#ifdef RLIMIT_SIGPENDING
- { "sigpending", RLIMIT_SIGPENDING, 1, 'i' },
-#endif
-#ifdef RLIMIT_MSGQUEUE
- { "msgqueue(bytes)", RLIMIT_MSGQUEUE, 1, 'q' },
-#endif
-#ifdef RLIMIT_AIO_MEM
- { "AIOlockedmem(KiB)", RLIMIT_AIO_MEM, 1024, 'M' },
-#endif
-#ifdef RLIMIT_AIO_OPS
- { "AIOoperations", RLIMIT_AIO_OPS, 1, 'O' },
-#endif
-#ifdef RLIMIT_TCACHE
- { "cachedthreads", RLIMIT_TCACHE, 1, 'C' },
-#endif
-#ifdef RLIMIT_SBSIZE
- { "sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024, 'B' },
-#endif
-#ifdef RLIMIT_PTHREAD
- { "threadsperprocess", RLIMIT_PTHREAD, 1, 'P' },
-#endif
-#ifdef RLIMIT_NICE
- { "maxnice", RLIMIT_NICE, 1, 'e' },
-#endif
-#ifdef RLIMIT_RTPRIO
- { "maxrtprio", RLIMIT_RTPRIO, 1, 'r' },
-#endif
-#if defined(ULIMIT_M_IS_RSS)
- { "resident-set(KiB)", RLIMIT_RSS, 1024, 'm' },
-#elif defined(ULIMIT_M_IS_VMEM)
- { "memory(KiB)", RLIMIT_VMEM, 1024, 'm' },
-#endif
-#if defined(ULIMIT_V_IS_VMEM)
- { "virtual-memory(KiB)", RLIMIT_VMEM, 1024, 'v' },
-#elif defined(ULIMIT_V_IS_AS)
- { "address-space(KiB)", RLIMIT_AS, 1024, 'v' },
-#endif
- { NULL, 0, 0, 0 }
- };
- static char opts[3 + NELEM(limits)];
- int how = SOFT | HARD, optc, what = 'f';
- bool all = false;
- const struct limits *l;
-
- if (!opts[0]) {
- /* build options string on first call - yuck */
- char *p = opts;
-
- *p++ = 'H'; *p++ = 'S'; *p++ = 'a';
- for (l = limits; l->name; l++)
- *p++ = l->option;
- *p = '\0';
- }
-
- while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
- switch (optc) {
- case 'H':
- how = HARD;
- break;
- case 'S':
- how = SOFT;
- break;
- case 'a':
- all = true;
- break;
- case '?':
- bi_errorf("usage: ulimit [-acdfHLlmnpSsTtvw] [value]");
- return (1);
- default:
- what = optc;
- }
-
- for (l = limits; l->name && l->option != what; l++)
- ;
- if (!l->name) {
- internal_warningf("ulimit: %c", what);
- return (1);
- }
-
- if (wp[builtin_opt.optind]) {
- if (all || wp[builtin_opt.optind + 1]) {
- bi_errorf("too many arguments");
- return (1);
- }
- return (set_ulimit(l, wp[builtin_opt.optind], how));
- }
- if (!all)
- print_ulimit(l, how);
- else for (l = limits; l->name; l++) {
- shprintf("%-20s ", l->name);
- print_ulimit(l, how);
- }
- return (0);
-}
-
-static int
-set_ulimit(const struct limits *l, const char *v, int how)
-{
- rlim_t val = (rlim_t)0;
- struct rlimit limit;
-
- if (strcmp(v, "unlimited") == 0)
- val = (rlim_t)RLIM_INFINITY;
- else {
- mksh_ari_t rval;
-
- if (!evaluate(v, &rval, KSH_RETURN_ERROR, false))
- return (1);
- /*
- * Avoid problems caused by typos that evaluate misses due
- * to evaluating unset parameters to 0...
- * If this causes problems, will have to add parameter to
- * evaluate() to control if unset params are 0 or an error.
- */
- if (!rval && !ksh_isdigit(v[0])) {
- bi_errorf("invalid %s limit: %s", l->name, v);
- return (1);
- }
- val = (rlim_t)((rlim_t)rval * l->factor);
- }
-
- if (getrlimit(l->resource, &limit) < 0) {
- /* some cannot be read, e.g. Linux RLIMIT_LOCKS */
- limit.rlim_cur = RLIM_INFINITY;
- limit.rlim_max = RLIM_INFINITY;
- }
- if (how & SOFT)
- limit.rlim_cur = val;
- if (how & HARD)
- limit.rlim_max = val;
- if (!setrlimit(l->resource, &limit))
- return (0);
- if (errno == EPERM)
- bi_errorf("%s exceeds allowable %s limit", v, l->name);
- else
- bi_errorf("bad %s limit: %s", l->name, strerror(errno));
- return (1);
-}
-
-static void
-print_ulimit(const struct limits *l, int how)
-{
- rlim_t val = (rlim_t)0;
- struct rlimit limit;
-
- if (getrlimit(l->resource, &limit)) {
- shf_puts("unknown\n", shl_stdout);
- return;
- }
- if (how & SOFT)
- val = limit.rlim_cur;
- else if (how & HARD)
- val = limit.rlim_max;
- if (val == (rlim_t)RLIM_INFINITY)
- shf_puts("unlimited\n", shl_stdout);
- else
- shprintf("%ld\n", (long)(val / l->factor));
-}
-#endif
-
-int
-c_rename(const char **wp)
-{
- int rv = 1;
-
- if (wp == NULL /* argv */ ||
- wp[0] == NULL /* name of builtin */ ||
- wp[1] == NULL /* first argument */ ||
- wp[2] == NULL /* second argument */ ||
- wp[3] != NULL /* no further args please */)
- bi_errorf(T_synerr);
- else if ((rv = rename(wp[1], wp[2])) != 0) {
- rv = errno;
- bi_errorf("failed: %s", strerror(rv));
- }
-
- return (rv);
-}
-
-int
-c_realpath(const char **wp)
-{
- int rv = 1;
- char *buf;
-
- if (wp != NULL && wp[0] != NULL && wp[1] != NULL) {
- if (strcmp(wp[1], "--")) {
- if (wp[2] == NULL) {
- wp += 1;
- rv = 0;
- }
- } else {
- if (wp[2] != NULL && wp[3] == NULL) {
- wp += 2;
- rv = 0;
- }
- }
- }
-
- if (rv)
- bi_errorf(T_synerr);
- else if ((buf = do_realpath(*wp)) == NULL) {
- rv = errno;
- bi_errorf("%s: %s", *wp, strerror(rv));
- if ((unsigned int)rv > 255)
- rv = 255;
- } else {
- shprintf("%s\n", buf);
- afree(buf, ATEMP);
- }
-
- return (rv);
-}
diff --git a/mksh/src/histrap.c b/mksh/src/histrap.c
deleted file mode 100644
index 2ac4c38..0000000
--- a/mksh/src/histrap.c
+++ /dev/null
@@ -1,1483 +0,0 @@
-/* $OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $ */
-/* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-#if HAVE_PERSISTENT_HISTORY
-#include <sys/file.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.98 2010/07/24 17:08:29 tg Exp $");
-
-/*-
- * MirOS: This is the default mapping type, and need not be specified.
- * IRIX doesn't have this constant.
- */
-#ifndef MAP_FILE
-#define MAP_FILE 0
-#endif
-
-Trap sigtraps[NSIG + 1];
-static struct sigaction Sigact_ign;
-
-#if HAVE_PERSISTENT_HISTORY
-static int hist_count_lines(unsigned char *, int);
-static int hist_shrink(unsigned char *, int);
-static unsigned char *hist_skip_back(unsigned char *,int *,int);
-static void histload(Source *, unsigned char *, int);
-static void histinsert(Source *, int, const char *);
-static void writehistfile(int, char *);
-static int sprinkle(int);
-#endif
-
-static int hist_execute(char *);
-static int hist_replace(char **, const char *, const char *, bool);
-static char **hist_get(const char *, bool, bool);
-static char **hist_get_oldest(void);
-static void histbackup(void);
-
-static char **current; /* current position in history[] */
-static int hstarted; /* set after hist_init() called */
-static Source *hist_source;
-
-#if HAVE_PERSISTENT_HISTORY
-static char *hname; /* current name of history file */
-static int histfd;
-static int hsize;
-#endif
-
-int
-c_fc(const char **wp)
-{
- struct shf *shf;
- struct temp *tf;
- const char *p;
- char *editor = NULL;
- bool gflag = false, lflag = false, nflag = false, rflag = false,
- sflag = false;
- int optc;
- const char *first = NULL, *last = NULL;
- char **hfirst, **hlast, **hp;
-
- if (!Flag(FTALKING_I)) {
- bi_errorf("history functions not available");
- return (1);
- }
-
- while ((optc = ksh_getopt(wp, &builtin_opt,
- "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
- switch (optc) {
- case 'e':
- p = builtin_opt.optarg;
- if (ksh_isdash(p))
- sflag = true;
- else {
- size_t len = strlen(p);
- editor = alloc(len + 4, ATEMP);
- memcpy(editor, p, len);
- memcpy(editor + len, " $_", 4);
- }
- break;
- case 'g': /* non-AT&T ksh */
- gflag = true;
- break;
- case 'l':
- lflag = true;
- break;
- case 'n':
- nflag = true;
- break;
- case 'r':
- rflag = true;
- break;
- case 's': /* POSIX version of -e - */
- sflag = true;
- break;
- /* kludge city - accept -num as -- -num (kind of) */
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- p = shf_smprintf("-%c%s",
- optc, builtin_opt.optarg);
- if (!first)
- first = p;
- else if (!last)
- last = p;
- else {
- bi_errorf("too many arguments");
- return (1);
- }
- break;
- case '?':
- return (1);
- }
- wp += builtin_opt.optind;
-
- /* Substitute and execute command */
- if (sflag) {
- char *pat = NULL, *rep = NULL;
-
- if (editor || lflag || nflag || rflag) {
- bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
- return (1);
- }
-
- /* Check for pattern replacement argument */
- if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) {
- strdupx(pat, *wp, ATEMP);
- rep = pat + (p - *wp);
- *rep++ = '\0';
- wp++;
- }
- /* Check for search prefix */
- if (!first && (first = *wp))
- wp++;
- if (last || *wp) {
- bi_errorf("too many arguments");
- return (1);
- }
-
- hp = first ? hist_get(first, false, false) :
- hist_get_newest(false);
- if (!hp)
- return (1);
- return (hist_replace(hp, pat, rep, gflag));
- }
-
- if (editor && (lflag || nflag)) {
- bi_errorf("can't use -l, -n with -e");
- return (1);
- }
-
- if (!first && (first = *wp))
- wp++;
- if (!last && (last = *wp))
- wp++;
- if (*wp) {
- bi_errorf("too many arguments");
- return (1);
- }
- if (!first) {
- hfirst = lflag ? hist_get("-16", true, true) :
- hist_get_newest(false);
- if (!hfirst)
- return (1);
- /* can't fail if hfirst didn't fail */
- hlast = hist_get_newest(false);
- } else {
- /* POSIX says not an error if first/last out of bounds
- * when range is specified; AT&T ksh and pdksh allow out of
- * bounds for -l as well.
- */
- hfirst = hist_get(first, (lflag || last) ? true : false, lflag);
- if (!hfirst)
- return (1);
- hlast = last ? hist_get(last, true, lflag) :
- (lflag ? hist_get_newest(false) : hfirst);
- if (!hlast)
- return (1);
- }
- if (hfirst > hlast) {
- char **temp;
-
- temp = hfirst; hfirst = hlast; hlast = temp;
- rflag = !rflag; /* POSIX */
- }
-
- /* List history */
- if (lflag) {
- char *s, *t;
-
- for (hp = rflag ? hlast : hfirst;
- hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
- if (!nflag)
- shf_fprintf(shl_stdout, "%d",
- hist_source->line - (int)(histptr - hp));
- shf_putc('\t', shl_stdout);
- /* print multi-line commands correctly */
- s = *hp;
- while ((t = strchr(s, '\n'))) {
- *t = '\0';
- shf_fprintf(shl_stdout, "%s\n\t", s);
- *t++ = '\n';
- s = t;
- }
- shf_fprintf(shl_stdout, "%s\n", s);
- }
- shf_flush(shl_stdout);
- return (0);
- }
-
- /* Run editor on selected lines, then run resulting commands */
-
- tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
- if (!(shf = tf->shf)) {
- bi_errorf("cannot create temp file %s - %s",
- tf->name, strerror(errno));
- return (1);
- }
- for (hp = rflag ? hlast : hfirst;
- hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
- shf_fprintf(shf, "%s\n", *hp);
- if (shf_close(shf) == EOF) {
- bi_errorf("error writing temporary file - %s", strerror(errno));
- return (1);
- }
-
- /* Ignore setstr errors here (arbitrary) */
- setstr(local("_", false), tf->name, KSH_RETURN_ERROR);
-
- /* XXX: source should not get trashed by this.. */
- {
- Source *sold = source;
- int ret;
-
- ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0);
- source = sold;
- if (ret)
- return (ret);
- }
-
- {
- struct stat statb;
- XString xs;
- char *xp;
- int n;
-
- if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) {
- bi_errorf("cannot open temp file %s", tf->name);
- return (1);
- }
-
- n = stat(tf->name, &statb) < 0 ? 128 : statb.st_size + 1;
- Xinit(xs, xp, n, hist_source->areap);
- while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
- xp += n;
- if (Xnleft(xs, xp) <= 0)
- XcheckN(xs, xp, Xlength(xs, xp));
- }
- if (n < 0) {
- bi_errorf("error reading temp file %s - %s",
- tf->name, strerror(shf_errno(shf)));
- shf_close(shf);
- return (1);
- }
- shf_close(shf);
- *xp = '\0';
- strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
- return (hist_execute(Xstring(xs, xp)));
- }
-}
-
-/* Save cmd in history, execute cmd (cmd gets trashed) */
-static int
-hist_execute(char *cmd)
-{
- Source *sold;
- int ret;
- char *p, *q;
-
- histbackup();
-
- for (p = cmd; p; p = q) {
- if ((q = strchr(p, '\n'))) {
- *q++ = '\0'; /* kill the newline */
- if (!*q) /* ignore trailing newline */
- q = NULL;
- }
- histsave(&hist_source->line, p, true, true);
-
- shellf("%s\n", p); /* POSIX doesn't say this is done... */
- if (q) /* restore \n (trailing \n not restored) */
- q[-1] = '\n';
- }
-
- /*
- * Commands are executed here instead of pushing them onto the
- * input 'cause POSIX says the redirection and variable assignments
- * in
- * X=y fc -e - 42 2> /dev/null
- * are to effect the repeated commands environment.
- */
- /* XXX: source should not get trashed by this.. */
- sold = source;
- ret = command(cmd, 0);
- source = sold;
- return (ret);
-}
-
-static int
-hist_replace(char **hp, const char *pat, const char *rep, bool globr)
-{
- char *line;
-
- if (!pat)
- strdupx(line, *hp, ATEMP);
- else {
- char *s, *s1;
- int pat_len = strlen(pat);
- int rep_len = strlen(rep);
- int len;
- XString xs;
- char *xp;
- bool any_subst = false;
-
- Xinit(xs, xp, 128, ATEMP);
- for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || globr);
- s = s1 + pat_len) {
- any_subst = true;
- len = s1 - s;
- XcheckN(xs, xp, len + rep_len);
- memcpy(xp, s, len); /* first part */
- xp += len;
- memcpy(xp, rep, rep_len); /* replacement */
- xp += rep_len;
- }
- if (!any_subst) {
- bi_errorf("substitution failed");
- return (1);
- }
- len = strlen(s) + 1;
- XcheckN(xs, xp, len);
- memcpy(xp, s, len);
- xp += len;
- line = Xclose(xs, xp);
- }
- return (hist_execute(line));
-}
-
-/*
- * get pointer to history given pattern
- * pattern is a number or string
- */
-static char **
-hist_get(const char *str, bool approx, bool allow_cur)
-{
- char **hp = NULL;
- int n;
-
- if (getn(str, &n)) {
- hp = histptr + (n < 0 ? n : (n - hist_source->line));
- if ((ptrdiff_t)hp < (ptrdiff_t)history) {
- if (approx)
- hp = hist_get_oldest();
- else {
- bi_errorf("%s: not in history", str);
- hp = NULL;
- }
- } else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) {
- if (approx)
- hp = hist_get_newest(allow_cur);
- else {
- bi_errorf("%s: not in history", str);
- hp = NULL;
- }
- } else if (!allow_cur && hp == histptr) {
- bi_errorf("%s: invalid range", str);
- hp = NULL;
- }
- } else {
- int anchored = *str == '?' ? (++str, 0) : 1;
-
- /* the -1 is to avoid the current fc command */
- if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
- bi_errorf("%s: not in history", str);
- else
- hp = &history[n];
- }
- return (hp);
-}
-
-/* Return a pointer to the newest command in the history */
-char **
-hist_get_newest(bool allow_cur)
-{
- if (histptr < history || (!allow_cur && histptr == history)) {
- bi_errorf("no history (yet)");
- return (NULL);
- }
- return (allow_cur ? histptr : histptr - 1);
-}
-
-/* Return a pointer to the oldest command in the history */
-static char **
-hist_get_oldest(void)
-{
- if (histptr <= history) {
- bi_errorf("no history (yet)");
- return (NULL);
- }
- return (history);
-}
-
-/******************************/
-/* Back up over last histsave */
-/******************************/
-static void
-histbackup(void)
-{
- static int last_line = -1;
-
- if (histptr >= history && last_line != hist_source->line) {
- hist_source->line--;
- afree(*histptr, APERM);
- histptr--;
- last_line = hist_source->line;
- }
-}
-
-/*
- * Return the current position.
- */
-char **
-histpos(void)
-{
- return (current);
-}
-
-int
-histnum(int n)
-{
- int last = histptr - history;
-
- if (n < 0 || n >= last) {
- current = histptr;
- return (last);
- } else {
- current = &history[n];
- return (n);
- }
-}
-
-/*
- * This will become unnecessary if hist_get is modified to allow
- * searching from positions other than the end, and in either
- * direction.
- */
-int
-findhist(int start, int fwd, const char *str, int anchored)
-{
- char **hp;
- int maxhist = histptr - history;
- int incr = fwd ? 1 : -1;
- int len = strlen(str);
-
- if (start < 0 || start >= maxhist)
- start = maxhist;
-
- hp = &history[start];
- for (; hp >= history && hp <= histptr; hp += incr)
- if ((anchored && strncmp(*hp, str, len) == 0) ||
- (!anchored && strstr(*hp, str)))
- return (hp - history);
-
- return (-1);
-}
-
-int
-findhistrel(const char *str)
-{
- int maxhist = histptr - history;
- int start = maxhist - 1;
- int rec;
-
- getn(str, &rec);
- if (rec == 0)
- return (-1);
- if (rec > 0) {
- if (rec > maxhist)
- return (-1);
- return (rec - 1);
- }
- if (rec > maxhist)
- return (-1);
- return (start + rec + 1);
-}
-
-/*
- * set history
- * this means reallocating the dataspace
- */
-void
-sethistsize(int n)
-{
- if (n > 0 && n != histsize) {
- int cursize = histptr - history;
-
- /* save most recent history */
- if (n < cursize) {
- memmove(history, histptr - n, n * sizeof(char *));
- cursize = n;
- }
-
- history = aresize(history, n * sizeof(char *), APERM);
-
- histsize = n;
- histptr = history + cursize;
- }
-}
-
-#if HAVE_PERSISTENT_HISTORY
-/*
- * set history file
- * This can mean reloading/resetting/starting history file
- * maintenance
- */
-void
-sethistfile(const char *name)
-{
- /* if not started then nothing to do */
- if (hstarted == 0)
- return;
-
- /* if the name is the same as the name we have */
- if (hname && strcmp(hname, name) == 0)
- return;
-
- /*
- * its a new name - possibly
- */
- if (histfd) {
- /* yes the file is open */
- (void)close(histfd);
- histfd = 0;
- hsize = 0;
- afree(hname, APERM);
- hname = NULL;
- /* let's reset the history */
- histptr = history - 1;
- hist_source->line = 0;
- }
-
- hist_init(hist_source);
-}
-#endif
-
-/*
- * initialise the history vector
- */
-void
-init_histvec(void)
-{
- if (history == (char **)NULL) {
- histsize = HISTORYSIZE;
- history = alloc(histsize * sizeof(char *), APERM);
- histptr = history - 1;
- }
-}
-
-
-/*
- * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
- * a) permit HISTSIZE to control number of lines of history stored
- * b) maintain a physical history file
- *
- * It turns out that there is a lot of ghastly hackery here
- */
-
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
-/* do not save command in history but possibly sync */
-bool
-histsync(void)
-{
- bool changed = false;
-
- if (histfd) {
- int lno = hist_source->line;
-
- hist_source->line++;
- writehistfile(0, NULL);
- hist_source->line--;
-
- if (lno != hist_source->line)
- changed = true;
- }
-
- return (changed);
-}
-#endif
-
-/*
- * save command in history
- */
-void
-histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
-{
- char **hp;
- char *c, *cp;
-
- strdupx(c, cmd, APERM);
- if ((cp = strchr(c, '\n')) != NULL)
- *cp = '\0';
-
- if (ignoredups && !strcmp(c, *histptr)
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
- && !histsync()
-#endif
- ) {
- afree(c, APERM);
- return;
- }
- ++*lnp;
-
-#if HAVE_PERSISTENT_HISTORY
- if (histfd && dowrite)
- writehistfile(*lnp, c);
-#endif
-
- hp = histptr;
-
- if (++hp >= history + histsize) { /* remove oldest command */
- afree(*history, APERM);
- for (hp = history; hp < history + histsize - 1; hp++)
- hp[0] = hp[1];
- }
- *hp = c;
- histptr = hp;
-}
-
-/*
- * Write history data to a file nominated by HISTFILE
- * if HISTFILE is unset then history still happens, but
- * the data is not written to a file
- * All copies of ksh looking at the file will maintain the
- * same history. This is ksh behaviour.
- *
- * This stuff uses mmap()
- * if your system ain't got it - then you'll have to undef HISTORYFILE
- */
-
-/*
- * Open a history file
- * Format is:
- * Bytes 1, 2:
- * HMAGIC - just to check that we are dealing with
- * the correct object
- * Then follows a number of stored commands
- * Each command is
- * <command byte><command number(4 bytes)><bytes><null>
- */
-#define HMAGIC1 0xab
-#define HMAGIC2 0xcd
-#define COMMAND 0xff
-
-void
-hist_init(Source *s)
-{
-#if HAVE_PERSISTENT_HISTORY
- unsigned char *base;
- int lines, fd, rv = 0;
-#endif
-
- if (Flag(FTALKING) == 0)
- return;
-
- hstarted = 1;
-
- hist_source = s;
-
-#if HAVE_PERSISTENT_HISTORY
- if ((hname = str_val(global("HISTFILE"))) == NULL)
- return;
- strdupx(hname, hname, APERM);
-
- retry:
- /* we have a file and are interactive */
- if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
- return;
-
- histfd = savefd(fd);
- if (histfd != fd)
- close(fd);
-
- (void)flock(histfd, LOCK_EX);
-
- hsize = lseek(histfd, (off_t)0, SEEK_END);
-
- if (hsize == 0) {
- /* add magic */
- if (sprinkle(histfd)) {
- hist_finish();
- return;
- }
- } else if (hsize > 0) {
- /*
- * we have some data
- */
- base = (void *)mmap(NULL, hsize, PROT_READ,
- MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
- /*
- * check on its validity
- */
- if (base == (unsigned char *)MAP_FAILED ||
- *base != HMAGIC1 || base[1] != HMAGIC2) {
- if (base != (unsigned char *)MAP_FAILED)
- munmap((caddr_t)base, hsize);
- hist_finish();
- if (unlink(hname) /* fails */)
- goto hiniterr;
- goto retry;
- }
- if (hsize > 2) {
- lines = hist_count_lines(base+2, hsize-2);
- if (lines > histsize) {
- /* we need to make the file smaller */
- if (hist_shrink(base, hsize))
- rv = unlink(hname);
- munmap((caddr_t)base, hsize);
- hist_finish();
- if (rv) {
- hiniterr:
- bi_errorf("cannot unlink HISTFILE %s"
- " - %s", hname, strerror(errno));
- hsize = 0;
- return;
- }
- goto retry;
- }
- }
- histload(hist_source, base+2, hsize-2);
- munmap((caddr_t)base, hsize);
- }
- (void)flock(histfd, LOCK_UN);
- hsize = lseek(histfd, (off_t)0, SEEK_END);
-#endif
-}
-
-#if HAVE_PERSISTENT_HISTORY
-typedef enum state {
- shdr, /* expecting a header */
- sline, /* looking for a null byte to end the line */
- sn1, /* bytes 1 to 4 of a line no */
- sn2, sn3, sn4
-} State;
-
-static int
-hist_count_lines(unsigned char *base, int bytes)
-{
- State state = shdr;
- int lines = 0;
-
- while (bytes--) {
- switch (state) {
- case shdr:
- if (*base == COMMAND)
- state = sn1;
- break;
- case sn1:
- state = sn2; break;
- case sn2:
- state = sn3; break;
- case sn3:
- state = sn4; break;
- case sn4:
- state = sline; break;
- case sline:
- if (*base == '\0') {
- lines++;
- state = shdr;
- }
- }
- base++;
- }
- return (lines);
-}
-
-/*
- * Shrink the history file to histsize lines
- */
-static int
-hist_shrink(unsigned char *oldbase, int oldbytes)
-{
- int fd, rv = 0;
- char *nfile = NULL;
- struct stat statb;
- unsigned char *nbase = oldbase;
- int nbytes = oldbytes;
-
- nbase = hist_skip_back(nbase, &nbytes, histsize);
- if (nbase == NULL)
- return (1);
- if (nbase == oldbase)
- return (0);
-
- /*
- * create temp file
- */
- nfile = shf_smprintf("%s.%d", hname, (int)procpid);
- if ((fd = open(nfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0)
- goto errout;
- if (fstat(histfd, &statb) >= 0 &&
- chown(nfile, statb.st_uid, statb.st_gid))
- goto errout;
-
- if (sprinkle(fd) || write(fd, nbase, nbytes) != nbytes)
- goto errout;
- close(fd);
- fd = -1;
-
- /*
- * rename
- */
- if (rename(nfile, hname) < 0) {
- errout:
- if (fd >= 0) {
- close(fd);
- if (nfile)
- unlink(nfile);
- }
- rv = 1;
- }
- afree(nfile, ATEMP);
- return (rv);
-}
-
-/*
- * find a pointer to the data 'no' back from the end of the file
- * return the pointer and the number of bytes left
- */
-static unsigned char *
-hist_skip_back(unsigned char *base, int *bytes, int no)
-{
- int lines = 0;
- unsigned char *ep;
-
- for (ep = base + *bytes; --ep > base; ) {
- /*
- * this doesn't really work: the 4 byte line number that
- * is encoded after the COMMAND byte can itself contain
- * the COMMAND byte....
- */
- for (; ep > base && *ep != COMMAND; ep--)
- ;
- if (ep == base)
- break;
- if (++lines == no) {
- *bytes = *bytes - ((char *)ep - (char *)base);
- return (ep);
- }
- }
- return (NULL);
-}
-
-/*
- * load the history structure from the stored data
- */
-static void
-histload(Source *s, unsigned char *base, int bytes)
-{
- State state;
- int lno = 0;
- unsigned char *line = NULL;
-
- for (state = shdr; bytes-- > 0; base++) {
- switch (state) {
- case shdr:
- if (*base == COMMAND)
- state = sn1;
- break;
- case sn1:
- lno = (((*base)&0xff)<<24);
- state = sn2;
- break;
- case sn2:
- lno |= (((*base)&0xff)<<16);
- state = sn3;
- break;
- case sn3:
- lno |= (((*base)&0xff)<<8);
- state = sn4;
- break;
- case sn4:
- lno |= (*base)&0xff;
- line = base+1;
- state = sline;
- break;
- case sline:
- if (*base == '\0') {
- /* worry about line numbers */
- if (histptr >= history && lno-1 != s->line) {
- /* a replacement ? */
- histinsert(s, lno, (char *)line);
- } else {
- s->line = lno--;
- histsave(&lno, (char *)line, false,
- false);
- }
- state = shdr;
- }
- }
- }
-}
-
-/*
- * Insert a line into the history at a specified number
- */
-static void
-histinsert(Source *s, int lno, const char *line)
-{
- char **hp;
-
- if (lno >= s->line - (histptr - history) && lno <= s->line) {
- hp = &histptr[lno - s->line];
- if (*hp)
- afree(*hp, APERM);
- strdupx(*hp, line, APERM);
- }
-}
-
-/*
- * write a command to the end of the history file
- * This *MAY* seem easy but it's also necessary to check
- * that the history file has not changed in size.
- * If it has - then some other shell has written to it
- * and we should read those commands to update our history
- */
-static void
-writehistfile(int lno, char *cmd)
-{
- int sizenow;
- unsigned char *base;
- unsigned char *news;
- int bytes;
- unsigned char hdr[5];
-
- (void)flock(histfd, LOCK_EX);
- sizenow = lseek(histfd, (off_t)0, SEEK_END);
- if (sizenow != hsize) {
- /*
- * Things have changed
- */
- if (sizenow > hsize) {
- /* someone has added some lines */
- bytes = sizenow - hsize;
- base = (void *)mmap(NULL, sizenow, PROT_READ,
- MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
- if (base == (unsigned char *)MAP_FAILED)
- goto bad;
- news = base + hsize;
- if (*news != COMMAND) {
- munmap((caddr_t)base, sizenow);
- goto bad;
- }
- hist_source->line--;
- histload(hist_source, news, bytes);
- hist_source->line++;
- lno = hist_source->line;
- munmap((caddr_t)base, sizenow);
- hsize = sizenow;
- } else {
- /* it has shrunk */
- /* but to what? */
- /* we'll give up for now */
- goto bad;
- }
- }
- if (cmd) {
- /*
- * we can write our bit now
- */
- hdr[0] = COMMAND;
- hdr[1] = (lno>>24)&0xff;
- hdr[2] = (lno>>16)&0xff;
- hdr[3] = (lno>>8)&0xff;
- hdr[4] = lno&0xff;
- bytes = strlen(cmd) + 1;
- if ((write(histfd, hdr, 5) != 5) ||
- (write(histfd, cmd, bytes) != bytes))
- goto bad;
- hsize = lseek(histfd, (off_t)0, SEEK_END);
- }
- (void)flock(histfd, LOCK_UN);
- return;
- bad:
- hist_finish();
-}
-
-void
-hist_finish(void)
-{
- (void)flock(histfd, LOCK_UN);
- (void)close(histfd);
- histfd = 0;
-}
-
-/*
- * add magic to the history file
- */
-static int
-sprinkle(int fd)
-{
- static const unsigned char mag[] = { HMAGIC1, HMAGIC2 };
-
- return (write(fd, mag, 2) != 2);
-}
-#endif
-
-#if !HAVE_SYS_SIGNAME
-static const struct mksh_sigpair {
- const char *const name;
- int nr;
-} mksh_sigpairs[] = {
-#include "signames.inc"
- { NULL, 0 }
-};
-#endif
-
-void
-inittraps(void)
-{
- int i;
- const char *cs;
-
- /* Populate sigtraps based on sys_signame and sys_siglist. */
- for (i = 0; i <= NSIG; i++) {
- sigtraps[i].signal = i;
- if (i == SIGERR_) {
- sigtraps[i].name = "ERR";
- sigtraps[i].mess = "Error handler";
- } else {
-#if HAVE_SYS_SIGNAME
- cs = sys_signame[i];
-#else
- const struct mksh_sigpair *pair = mksh_sigpairs;
- while ((pair->nr != i) && (pair->name != NULL))
- ++pair;
- cs = pair->name;
-#endif
- if ((cs == NULL) ||
- (cs[0] == '\0'))
- sigtraps[i].name = shf_smprintf("%d", i);
- else {
- char *s;
-
- if (!strncasecmp(cs, "SIG", 3))
- cs += 3;
- strdupx(s, cs, APERM);
- sigtraps[i].name = s;
- while ((*s = ksh_toupper(*s)))
- ++s;
- }
-#if HAVE_SYS_SIGLIST
- sigtraps[i].mess = sys_siglist[i];
-#elif HAVE_STRSIGNAL
- sigtraps[i].mess = strsignal(i);
-#else
- sigtraps[i].mess = NULL;
-#endif
- if ((sigtraps[i].mess == NULL) ||
- (sigtraps[i].mess[0] == '\0'))
- sigtraps[i].mess = shf_smprintf("Signal %d", i);
- }
- }
- sigtraps[SIGEXIT_].name = "EXIT"; /* our name for signal 0 */
-
- (void)sigemptyset(&Sigact_ign.sa_mask);
- Sigact_ign.sa_flags = 0; /* interruptible */
- Sigact_ign.sa_handler = SIG_IGN;
-
- sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
- sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
- sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
- sigtraps[SIGHUP].flags |= TF_FATAL;
- sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
-
- /* these are always caught so we can clean up any temporary files. */
- setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
- setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
- setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
- setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
-}
-
-static void alarm_catcher(int sig);
-
-void
-alarm_init(void)
-{
- sigtraps[SIGALRM].flags |= TF_SHELL_USES;
- setsig(&sigtraps[SIGALRM], alarm_catcher,
- SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
-}
-
-/* ARGSUSED */
-static void
-alarm_catcher(int sig MKSH_A_UNUSED)
-{
- /* this runs inside interrupt context, with errno saved */
-
- if (ksh_tmout_state == TMOUT_READING) {
- int left = alarm(0);
-
- if (left == 0) {
- ksh_tmout_state = TMOUT_LEAVING;
- intrsig = 1;
- } else
- alarm(left);
- }
-}
-
-Trap *
-gettrap(const char *name, int igncase)
-{
- int n = NSIG + 1;
- Trap *p;
- const char *n2;
- int (*cmpfunc)(const char *, const char *) = strcmp;
-
- if (ksh_isdigit(*name)) {
- if (getn(name, &n) && 0 <= n && n < NSIG)
- return (&sigtraps[n]);
- else
- return (NULL);
- }
-
- n2 = strncasecmp(name, "SIG", 3) ? NULL : name + 3;
- if (igncase)
- cmpfunc = strcasecmp;
- for (p = sigtraps; --n >= 0; p++)
- if (!cmpfunc(p->name, name) || (n2 && !cmpfunc(p->name, n2)))
- return (p);
- return (NULL);
-}
-
-/*
- * trap signal handler
- */
-void
-trapsig(int i)
-{
- Trap *p = &sigtraps[i];
- int errno_ = errno;
-
- trap = p->set = 1;
- if (p->flags & TF_DFL_INTR)
- intrsig = 1;
- if ((p->flags & TF_FATAL) && !p->trap) {
- fatal_trap = 1;
- intrsig = 1;
- }
- if (p->shtrap)
- (*p->shtrap)(i);
- errno = errno_;
-}
-
-/*
- * called when we want to allow the user to ^C out of something - won't
- * work if user has trapped SIGINT.
- */
-void
-intrcheck(void)
-{
- if (intrsig)
- runtraps(TF_DFL_INTR|TF_FATAL);
-}
-
-/*
- * called after EINTR to check if a signal with normally causes process
- * termination has been received.
- */
-int
-fatal_trap_check(void)
-{
- int i;
- Trap *p;
-
- /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
- if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
- /* return value is used as an exit code */
- return (128 + p->signal);
- return (0);
-}
-
-/*
- * Returns the signal number of any pending traps: ie, a signal which has
- * occurred for which a trap has been set or for which the TF_DFL_INTR flag
- * is set.
- */
-int
-trap_pending(void)
-{
- int i;
- Trap *p;
-
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
- if (p->set && ((p->trap && p->trap[0]) ||
- ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
- return (p->signal);
- return (0);
-}
-
-/*
- * run any pending traps. If intr is set, only run traps that
- * can interrupt commands.
- */
-void
-runtraps(int flag)
-{
- int i;
- Trap *p;
-
- if (ksh_tmout_state == TMOUT_LEAVING) {
- ksh_tmout_state = TMOUT_EXECUTING;
- warningf(false, "timed out waiting for input");
- unwind(LEXIT);
- } else
- /*
- * XXX: this means the alarm will have no effect if a trap
- * is caught after the alarm() was started...not good.
- */
- ksh_tmout_state = TMOUT_EXECUTING;
- if (!flag)
- trap = 0;
- if (flag & TF_DFL_INTR)
- intrsig = 0;
- if (flag & TF_FATAL)
- fatal_trap = 0;
- for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
- if (p->set && (!flag ||
- ((p->flags & flag) && p->trap == NULL)))
- runtrap(p);
-}
-
-void
-runtrap(Trap *p)
-{
- int i = p->signal;
- char *trapstr = p->trap;
- int oexstat;
- int old_changed = 0;
-
- p->set = 0;
- if (trapstr == NULL) { /* SIG_DFL */
- if (p->flags & TF_FATAL) {
- /* eg, SIGHUP */
- exstat = 128 + i;
- unwind(LLEAVE);
- }
- if (p->flags & TF_DFL_INTR) {
- /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
- exstat = 128 + i;
- unwind(LINTR);
- }
- return;
- }
- if (trapstr[0] == '\0') /* SIG_IGN */
- return;
- if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
- old_changed = p->flags & TF_CHANGED;
- p->flags &= ~TF_CHANGED;
- p->trap = NULL;
- }
- oexstat = exstat;
- /*
- * Note: trapstr is fully parsed before anything is executed, thus
- * no problem with afree(p->trap) in settrap() while still in use.
- */
- command(trapstr, current_lineno);
- exstat = oexstat;
- if (i == SIGEXIT_ || i == SIGERR_) {
- if (p->flags & TF_CHANGED)
- /* don't clear TF_CHANGED */
- afree(trapstr, APERM);
- else
- p->trap = trapstr;
- p->flags |= old_changed;
- }
-}
-
-/* clear pending traps and reset user's trap handlers; used after fork(2) */
-void
-cleartraps(void)
-{
- int i;
- Trap *p;
-
- trap = 0;
- intrsig = 0;
- fatal_trap = 0;
- for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
- p->set = 0;
- if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
- settrap(p, NULL);
- }
-}
-
-/* restore signals just before an exec(2) */
-void
-restoresigs(void)
-{
- int i;
- Trap *p;
-
- for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
- if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
- setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
- SS_RESTORE_CURR|SS_FORCE);
-}
-
-void
-settrap(Trap *p, const char *s)
-{
- sig_t f;
-
- if (p->trap)
- afree(p->trap, APERM);
- strdupx(p->trap, s, APERM); /* handles s == 0 */
- p->flags |= TF_CHANGED;
- f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
-
- p->flags |= TF_USER_SET;
- if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
- f = trapsig;
- else if (p->flags & TF_SHELL_USES) {
- if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
- /* do what user wants at exec time */
- p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
- if (f == SIG_IGN)
- p->flags |= TF_EXEC_IGN;
- else
- p->flags |= TF_EXEC_DFL;
- }
-
- /*
- * assumes handler already set to what shell wants it
- * (normally trapsig, but could be j_sigchld() or SIG_IGN)
- */
- return;
- }
-
- /* todo: should we let user know signal is ignored? how? */
- setsig(p, f, SS_RESTORE_CURR|SS_USER);
-}
-
-/*
- * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
- * kill shell (unless user catches it and exits)
- */
-int
-block_pipe(void)
-{
- int restore_dfl = 0;
- Trap *p = &sigtraps[SIGPIPE];
-
- if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
- setsig(p, SIG_IGN, SS_RESTORE_CURR);
- if (p->flags & TF_ORIG_DFL)
- restore_dfl = 1;
- } else if (p->cursig == SIG_DFL) {
- setsig(p, SIG_IGN, SS_RESTORE_CURR);
- restore_dfl = 1; /* restore to SIG_DFL */
- }
- return (restore_dfl);
-}
-
-/* Called by c_print() to undo whatever block_pipe() did */
-void
-restore_pipe(int restore_dfl)
-{
- if (restore_dfl)
- setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
-}
-
-/*
- * Set action for a signal. Action may not be set if original
- * action was SIG_IGN, depending on the value of flags and FTALKING.
- */
-int
-setsig(Trap *p, sig_t f, int flags)
-{
- struct sigaction sigact;
-
- if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
- return (1);
-
- /*
- * First time setting this signal? If so, get and note the current
- * setting.
- */
- if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
- sigaction(p->signal, &Sigact_ign, &sigact);
- p->flags |= sigact.sa_handler == SIG_IGN ?
- TF_ORIG_IGN : TF_ORIG_DFL;
- p->cursig = SIG_IGN;
- }
-
- /*-
- * Generally, an ignored signal stays ignored, except if
- * - the user of an interactive shell wants to change it
- * - the shell wants for force a change
- */
- if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
- (!(flags & SS_USER) || !Flag(FTALKING)))
- return (0);
-
- setexecsig(p, flags & SS_RESTORE_MASK);
-
- /*
- * This is here 'cause there should be a way of clearing
- * shtraps, but don't know if this is a sane way of doing
- * it. At the moment, all users of shtrap are lifetime
- * users (SIGALRM, SIGCHLD, SIGWINCH).
- */
- if (!(flags & SS_USER))
- p->shtrap = (sig_t)NULL;
- if (flags & SS_SHTRAP) {
- p->shtrap = f;
- f = trapsig;
- }
-
- if (p->cursig != f) {
- p->cursig = f;
- (void)sigemptyset(&sigact.sa_mask);
- sigact.sa_flags = 0 /* interruptible */;
- sigact.sa_handler = f;
- sigaction(p->signal, &sigact, NULL);
- }
-
- return (1);
-}
-
-/* control what signal is set to before an exec() */
-void
-setexecsig(Trap *p, int restore)
-{
- /* XXX debugging */
- if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
- internal_errorf("setexecsig: unset signal %d(%s)",
- p->signal, p->name);
-
- /* restore original value for exec'd kids */
- p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
- switch (restore & SS_RESTORE_MASK) {
- case SS_RESTORE_CURR: /* leave things as they currently are */
- break;
- case SS_RESTORE_ORIG:
- p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
- break;
- case SS_RESTORE_DFL:
- p->flags |= TF_EXEC_DFL;
- break;
- case SS_RESTORE_IGN:
- p->flags |= TF_EXEC_IGN;
- break;
- }
-}
diff --git a/mksh/src/jobs.c b/mksh/src/jobs.c
deleted file mode 100644
index 47326a1..0000000
--- a/mksh/src/jobs.c
+++ /dev/null
@@ -1,1648 +0,0 @@
-/* $OpenBSD: jobs.c,v 1.38 2009/12/12 04:28:44 deraadt Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.69 2010/07/04 17:33:54 tg Exp $");
-
-#if HAVE_KILLPG
-#define mksh_killpg killpg
-#else
-/* cross fingers and hope kill is killpg-endowed */
-#define mksh_killpg(p,s) kill(-(p), (s))
-#endif
-
-/* Order important! */
-#define PRUNNING 0
-#define PEXITED 1
-#define PSIGNALLED 2
-#define PSTOPPED 3
-
-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 */
-};
-
-/* Notify/print flag - j_print() argument */
-#define JP_NONE 0 /* don't print anything */
-#define JP_SHORT 1 /* print signals processes were killed by */
-#define JP_MEDIUM 2 /* print [job-num] -/+ command */
-#define JP_LONG 3 /* print [job-num] -/+ pid command */
-#define JP_PGRP 4 /* print pgrp */
-
-/* put_job() flags */
-#define PJ_ON_FRONT 0 /* at very front */
-#define PJ_PAST_STOPPED 1 /* just past any stopped jobs */
-
-/* Job.flags values */
-#define JF_STARTED 0x001 /* set when all processes in job are started */
-#define JF_WAITING 0x002 /* set if j_waitj() is waiting on job */
-#define JF_W_ASYNCNOTIFY 0x004 /* set if waiting and async notification ok */
-#define JF_XXCOM 0x008 /* set for $(command) jobs */
-#define JF_FG 0x010 /* running in foreground (also has tty pgrp) */
-#define JF_SAVEDTTY 0x020 /* j->ttystate is valid */
-#define JF_CHANGED 0x040 /* process has changed state */
-#define JF_KNOWN 0x080 /* $! referenced */
-#define JF_ZOMBIE 0x100 /* known, unwaited process */
-#define JF_REMOVE 0x200 /* flagged for removal (j_jobs()/j_noityf()) */
-#define JF_USETTYMODE 0x400 /* tty mode saved if process exits normally */
-#define JF_SAVEDTTYPGRP 0x800 /* j->saved_ttypgrp is valid */
-
-typedef struct job Job;
-struct job {
- Job *next; /* next job in list */
- Proc *proc_list; /* process list */
- Proc *last_proc; /* last process in list */
- struct timeval systime; /* system time used by job */
- struct timeval usrtime; /* user time used by job */
- pid_t pgrp; /* process group of job */
- pid_t ppid; /* pid of process that forked job */
- int job; /* job number: %n */
- int flags; /* see JF_* */
- volatile int state; /* job state */
- int status; /* exit status of last process */
- int32_t age; /* number of jobs started */
- Coproc_id coproc_id; /* 0 or id of coprocess output pipe */
-#ifndef MKSH_UNEMPLOYED
- struct termios ttystate;/* saved tty state for stopped jobs */
- pid_t saved_ttypgrp; /* saved tty process group for stopped jobs */
-#endif
-};
-
-/* Flags for j_waitj() */
-#define JW_NONE 0x00
-#define JW_INTERRUPT 0x01 /* ^C will stop the wait */
-#define JW_ASYNCNOTIFY 0x02 /* asynchronous notification during wait ok */
-#define JW_STOPPEDWAIT 0x04 /* wait even if job stopped */
-
-/* Error codes for j_lookup() */
-#define JL_OK 0
-#define JL_NOSUCH 1 /* no such 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[] = {
- null,
- "no such job",
- "ambiguous",
- "argument must be %job or process id",
- NULL
-};
-
-static Job *job_list; /* job list */
-static Job *last_job;
-static Job *async_job;
-static pid_t async_pid;
-
-static int nzombie; /* # of zombies owned by this process */
-static int32_t njobs; /* # of jobs started */
-
-#ifndef CHILD_MAX
-#define CHILD_MAX 25
-#endif
-
-/* held_sigchld is set if sigchld occurs before a job is completely started */
-static volatile sig_atomic_t held_sigchld;
-
-#ifndef MKSH_UNEMPLOYED
-static struct shf *shl_j;
-static bool ttypgrp_ok; /* set if can use tty pgrps */
-static pid_t restore_ttypgrp = -1;
-static int const tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU };
-#endif
-
-static void j_set_async(Job *);
-static void j_startjob(Job *);
-static int j_waitj(Job *, int, const char *);
-static void j_sigchld(int);
-static void j_print(Job *, int, struct shf *);
-static Job *j_lookup(const char *, int *);
-static Job *new_job(void);
-static Proc *new_proc(void);
-static void check_job(Job *);
-static void put_job(Job *, int);
-static void remove_job(Job *, const char *);
-static int kill_job(Job *, int);
-
-/* initialise job control */
-void
-j_init(void)
-{
-#ifndef MKSH_UNEMPLOYED
- bool mflagset = Flag(FMONITOR) != 127;
-
- Flag(FMONITOR) = 0;
-#endif
-
- (void)sigemptyset(&sm_default);
- sigprocmask(SIG_SETMASK, &sm_default, NULL);
-
- (void)sigemptyset(&sm_sigchld);
- (void)sigaddset(&sm_sigchld, SIGCHLD);
-
- setsig(&sigtraps[SIGCHLD], j_sigchld,
- SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
-
-#ifndef MKSH_UNEMPLOYED
- if (!mflagset && Flag(FTALKING))
- Flag(FMONITOR) = 1;
-
- /*
- * shl_j is used to do asynchronous notification (used in
- * an interrupt handler, so need a distinct shf)
- */
- shl_j = shf_fdopen(2, SHF_WR, NULL);
-
- if (Flag(FMONITOR) || Flag(FTALKING)) {
- int i;
-
- /*
- * the TF_SHELL_USES test is a kludge that lets us know if
- * if the signals have been changed by the shell.
- */
- for (i = NELEM(tt_sigs); --i >= 0; ) {
- sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;
- /* j_change() sets this to SS_RESTORE_DFL if FMONITOR */
- setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
- SS_RESTORE_IGN|SS_FORCE);
- }
- }
-
- /* j_change() calls tty_init() */
- if (Flag(FMONITOR))
- j_change();
- else
-#endif
- if (Flag(FTALKING))
- tty_init(true, true);
-}
-
-/* job cleanup before shell exit */
-void
-j_exit(void)
-{
- /* kill stopped, and possibly running, jobs */
- Job *j;
- int killed = 0;
-
- for (j = job_list; j != NULL; j = j->next) {
- if (j->ppid == procpid &&
- (j->state == PSTOPPED ||
- (j->state == PRUNNING &&
- ((j->flags & JF_FG) ||
- (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) {
- killed = 1;
- if (j->pgrp == 0)
- kill_job(j, SIGHUP);
- else
- mksh_killpg(j->pgrp, SIGHUP);
-#ifndef MKSH_UNEMPLOYED
- if (j->state == PSTOPPED) {
- if (j->pgrp == 0)
- kill_job(j, SIGCONT);
- else
- mksh_killpg(j->pgrp, SIGCONT);
- }
-#endif
- }
- }
- if (killed)
- sleep(1);
- j_notify();
-
-#ifndef MKSH_UNEMPLOYED
- if (kshpid == procpid && restore_ttypgrp >= 0) {
- /*
- * Need to restore the tty pgrp to what it was when the
- * shell started up, so that the process that started us
- * will be able to access the tty when we are done.
- * Also need to restore our process group in case we are
- * about to do an exec so that both our parent and the
- * process we are to become will be able to access the tty.
- */
- tcsetpgrp(tty_fd, restore_ttypgrp);
- setpgid(0, restore_ttypgrp);
- }
- if (Flag(FMONITOR)) {
- Flag(FMONITOR) = 0;
- j_change();
- }
-#endif
-}
-
-#ifndef MKSH_UNEMPLOYED
-/* turn job control on or off according to Flag(FMONITOR) */
-void
-j_change(void)
-{
- int i;
-
- if (Flag(FMONITOR)) {
- bool use_tty = Flag(FTALKING);
-
- /* Don't call tcgetattr() 'til we own the tty process group */
- if (use_tty)
- tty_init(false, true);
-
- /* no controlling tty, no SIGT* */
- 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) */
- while (1) {
- pid_t ttypgrp;
-
- if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
- warningf(false,
- "j_init: tcgetpgrp() failed: %s",
- strerror(errno));
- ttypgrp_ok = false;
- break;
- }
- if (ttypgrp == kshpgrp)
- break;
- kill(0, SIGTTIN);
- }
- }
- for (i = NELEM(tt_sigs); --i >= 0; )
- setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
- SS_RESTORE_DFL|SS_FORCE);
- if (ttypgrp_ok && kshpgrp != kshpid) {
- if (setpgid(0, kshpid) < 0) {
- warningf(false,
- "j_init: setpgid() failed: %s",
- strerror(errno));
- ttypgrp_ok = false;
- } else {
- if (tcsetpgrp(tty_fd, kshpid) < 0) {
- warningf(false,
- "j_init: tcsetpgrp() failed: %s",
- strerror(errno));
- ttypgrp_ok = false;
- } else
- restore_ttypgrp = kshpgrp;
- kshpgrp = kshpid;
- }
- }
- if (use_tty && !ttypgrp_ok)
- warningf(false, "warning: won't have full job control");
- if (tty_fd >= 0)
- tcgetattr(tty_fd, &tty_state);
- } else {
- ttypgrp_ok = false;
- if (Flag(FTALKING))
- for (i = NELEM(tt_sigs); --i >= 0; )
- setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
- SS_RESTORE_IGN|SS_FORCE);
- else
- for (i = NELEM(tt_sigs); --i >= 0; ) {
- if (sigtraps[tt_sigs[i]].flags &
- (TF_ORIG_IGN | TF_ORIG_DFL))
- setsig(&sigtraps[tt_sigs[i]],
- (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ?
- SIG_IGN : SIG_DFL,
- SS_RESTORE_ORIG|SS_FORCE);
- }
- if (!Flag(FTALKING))
- tty_close();
- }
-}
-#endif
-
-/* execute tree in child subprocess */
-int
-exchild(struct op *t, int flags,
- volatile int *xerrok,
- /* used if XPCLOSE or XCCLOSE */ int close_fd)
-{
- static Proc *last_proc; /* for pipelines */
-
- int rv = 0, forksleep;
- sigset_t omask;
- struct {
- Proc *p;
- Job *j;
- pid_t cldpid;
- } pi;
-
- if (flags & XEXEC)
- /*
- * Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
- * (also done in another execute() below)
- */
- return (execute(t, flags & (XEXEC | XERROK), xerrok));
-
- /* no SIGCHLDs while messing with job and process lists */
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- pi.p = new_proc();
- pi.p->next = NULL;
- pi.p->state = PRUNNING;
- pi.p->status = 0;
- pi.p->pid = 0;
-
- /* link process into jobs list */
- if (flags & XPIPEI) {
- /* continuing with a pipe */
- if (!last_job)
- internal_errorf(
- "exchild: XPIPEI and no last_job - pid %d",
- (int)procpid);
- pi.j = last_job;
- if (last_proc)
- last_proc->next = pi.p;
- last_proc = pi.p;
- } else {
- pi.j = new_job(); /* fills in pi.j->job */
- /*
- * we don't consider XXCOMs foreground since they don't get
- * tty process group and we don't save or restore tty modes.
- */
- pi.j->flags = (flags & XXCOM) ? JF_XXCOM :
- ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
- timerclear(&pi.j->usrtime);
- timerclear(&pi.j->systime);
- pi.j->state = PRUNNING;
- pi.j->pgrp = 0;
- pi.j->ppid = procpid;
- pi.j->age = ++njobs;
- pi.j->proc_list = pi.p;
- pi.j->coproc_id = 0;
- last_job = pi.j;
- last_proc = pi.p;
- put_job(pi.j, PJ_PAST_STOPPED);
- }
-
- snptreef(pi.p->command, sizeof(pi.p->command), "%T", t);
-
- /* create child process */
- forksleep = 1;
- while ((pi.cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
- if (intrsig) /* allow user to ^C out... */
- break;
- sleep(forksleep);
- forksleep <<= 1;
- }
- if (pi.cldpid < 0) {
- kill_job(pi.j, SIGKILL);
- remove_job(pi.j, "fork failed");
- sigprocmask(SIG_SETMASK, &omask, NULL);
- errorf("cannot fork - try again");
- }
- pi.p->pid = pi.cldpid ? pi.cldpid : (procpid = getpid());
-
- /*
- * ensure next child gets a (slightly) different $RANDOM sequence
- * from its parent process and other child processes
- */
- change_random(&pi, sizeof(pi));
-
-#ifndef MKSH_UNEMPLOYED
- /* job control set up */
- if (Flag(FMONITOR) && !(flags&XXCOM)) {
- int dotty = 0;
- if (pi.j->pgrp == 0) { /* First process */
- pi.j->pgrp = pi.p->pid;
- dotty = 1;
- }
-
- /* set pgrp in both parent and child to deal with race
- * condition
- */
- setpgid(pi.p->pid, pi.j->pgrp);
- if (ttypgrp_ok && dotty && !(flags & XBGND))
- tcsetpgrp(tty_fd, pi.j->pgrp);
- }
-#endif
-
- /* used to close pipe input fd */
- if (close_fd >= 0 && (((flags & XPCLOSE) && pi.cldpid) ||
- ((flags & XCCLOSE) && !pi.cldpid)))
- close(close_fd);
- if (!pi.cldpid) {
- /* child */
-
- /* Do this before restoring signal */
- if (flags & XCOPROC)
- coproc_cleanup(false);
- sigprocmask(SIG_SETMASK, &omask, NULL);
- cleanup_parents_env();
-#ifndef MKSH_UNEMPLOYED
- /* If FMONITOR or FTALKING is set, these signals are ignored,
- * if neither FMONITOR nor FTALKING are set, the signals have
- * their inherited values.
- */
- if (Flag(FMONITOR) && !(flags & XXCOM)) {
- for (forksleep = NELEM(tt_sigs); --forksleep >= 0; )
- setsig(&sigtraps[tt_sigs[forksleep]], SIG_DFL,
- SS_RESTORE_DFL|SS_FORCE);
- }
-#endif
-#if HAVE_NICE
- if (Flag(FBGNICE) && (flags & XBGND))
- (void)nice(4);
-#endif
- if ((flags & XBGND)
-#ifndef MKSH_UNEMPLOYED
- && !Flag(FMONITOR)
-#endif
- ) {
- setsig(&sigtraps[SIGINT], SIG_IGN,
- SS_RESTORE_IGN|SS_FORCE);
- setsig(&sigtraps[SIGQUIT], SIG_IGN,
- SS_RESTORE_IGN|SS_FORCE);
- if ((!(flags & (XPIPEI | XCOPROC))) &&
- ((forksleep = open("/dev/null", 0)) > 0)) {
- (void)ksh_dup2(forksleep, 0, true);
- close(forksleep);
- }
- }
- remove_job(pi.j, "child"); /* in case of $(jobs) command */
- nzombie = 0;
-#ifndef MKSH_UNEMPLOYED
- ttypgrp_ok = false;
- Flag(FMONITOR) = 0;
-#endif
- Flag(FTALKING) = 0;
- tty_close();
- cleartraps();
- /* no return */
- execute(t, (flags & XERROK) | XEXEC, NULL);
-#ifndef MKSH_SMALL
- if (t->type == TPIPE)
- unwind(LLEAVE);
- internal_warningf("exchild: execute() returned");
- fptreef(shl_out, 2, "exchild: tried to execute {\n%T\n}\n", t);
- shf_flush(shl_out);
-#endif
- unwind(LLEAVE);
- /* NOTREACHED */
- }
-
- /* shell (parent) stuff */
- if (!(flags & XPIPEO)) { /* last process in a job */
- j_startjob(pi.j);
- if (flags & XCOPROC) {
- pi.j->coproc_id = coproc.id;
- /* n jobs using co-process output */
- coproc.njobs++;
- /* j using co-process input */
- coproc.job = (void *)pi.j;
- }
- if (flags & XBGND) {
- j_set_async(pi.j);
- if (Flag(FTALKING)) {
- shf_fprintf(shl_out, "[%d]", pi.j->job);
- for (pi.p = pi.j->proc_list; pi.p;
- pi.p = pi.p->next)
- shf_fprintf(shl_out, " %d",
- (int)pi.p->pid);
- shf_putchar('\n', shl_out);
- shf_flush(shl_out);
- }
- } else
- rv = j_waitj(pi.j, JW_NONE, "jw:last proc");
- }
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- return (rv);
-}
-
-/* start the last job: only used for $(command) jobs */
-void
-startlast(void)
-{
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- if (last_job) { /* no need to report error - waitlast() will do it */
- /* ensure it isn't removed by check_job() */
- last_job->flags |= JF_WAITING;
- j_startjob(last_job);
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
-}
-
-/* wait for last job: only used for $(command) jobs */
-int
-waitlast(void)
-{
- int rv;
- Job *j;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- j = last_job;
- if (!j || !(j->flags & JF_STARTED)) {
- if (!j)
- warningf(true, "waitlast: no last job");
- else
- internal_warningf("waitlast: not started");
- sigprocmask(SIG_SETMASK, &omask, NULL);
- return (125); /* not so arbitrary, non-zero value */
- }
-
- rv = j_waitj(j, JW_NONE, "jw:waitlast");
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- return (rv);
-}
-
-/* wait for child, interruptable. */
-int
-waitfor(const char *cp, int *sigp)
-{
- int rv;
- Job *j;
- int ecode;
- int flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- *sigp = 0;
-
- if (cp == NULL) {
- /*
- * wait for an unspecified job - always returns 0, so
- * don't have to worry about exited/signaled jobs
- */
- for (j = job_list; j; j = j->next)
- /* AT&T ksh will wait for stopped jobs - we don't */
- if (j->ppid == procpid && j->state == PRUNNING)
- break;
- if (!j) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- return (-1);
- }
- } else if ((j = j_lookup(cp, &ecode))) {
- /* don't report normal job completion */
- flags &= ~JW_ASYNCNOTIFY;
- if (j->ppid != procpid) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- return (-1);
- }
- } else {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- if (ecode != JL_NOSUCH)
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
- return (-1);
- }
-
- /* AT&T ksh will wait for stopped jobs - we don't */
- rv = j_waitj(j, flags, "jw:waitfor");
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- if (rv < 0) /* we were interrupted */
- *sigp = 128 + -rv;
-
- return (rv);
-}
-
-/* kill (built-in) a job */
-int
-j_kill(const char *cp, int sig)
-{
- Job *j;
- int rv = 0;
- int ecode;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- if ((j = j_lookup(cp, &ecode)) == NULL) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
- return (1);
- }
-
- if (j->pgrp == 0) { /* started when !Flag(FMONITOR) */
- if (kill_job(j, sig) < 0) {
- bi_errorf("%s: %s", cp, strerror(errno));
- rv = 1;
- }
- } else {
-#ifndef MKSH_UNEMPLOYED
- if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP))
- mksh_killpg(j->pgrp, SIGCONT);
-#endif
- if (mksh_killpg(j->pgrp, sig) < 0) {
- bi_errorf("%s: %s", cp, strerror(errno));
- rv = 1;
- }
- }
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- return (rv);
-}
-
-#ifndef MKSH_UNEMPLOYED
-/* fg and bg built-ins: called only if Flag(FMONITOR) set */
-int
-j_resume(const char *cp, int bg)
-{
- Job *j;
- Proc *p;
- int ecode;
- int running;
- int rv = 0;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- if ((j = j_lookup(cp, &ecode)) == NULL) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
- return (1);
- }
-
- if (j->pgrp == 0) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("job not job-controlled");
- return (1);
- }
-
- if (bg)
- shprintf("[%d] ", j->job);
-
- running = 0;
- for (p = j->proc_list; p != NULL; p = p->next) {
- if (p->state == PSTOPPED) {
- p->state = PRUNNING;
- p->status = 0;
- running = 1;
- }
- shf_puts(p->command, shl_stdout);
- if (p->next)
- shf_puts("| ", shl_stdout);
- }
- shf_putc('\n', shl_stdout);
- shf_flush(shl_stdout);
- if (running)
- j->state = PRUNNING;
-
- put_job(j, PJ_PAST_STOPPED);
- if (bg)
- j_set_async(j);
- else {
- /* attach tty to job */
- if (j->state == PRUNNING) {
- if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
- tcsetattr(tty_fd, TCSADRAIN, &j->ttystate);
- /* 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);
- sigprocmask(SIG_SETMASK, &omask,
- NULL);
- bi_errorf("1st tcsetpgrp(%d, %d) failed: %s",
- tty_fd,
- (int)((j->flags & JF_SAVEDTTYPGRP) ?
- j->saved_ttypgrp : j->pgrp),
- strerror(rv));
- return (1);
- }
- }
- j->flags |= JF_FG;
- j->flags &= ~JF_KNOWN;
- if (j == async_job)
- async_job = NULL;
- }
-
- if (j->state == PRUNNING && mksh_killpg(j->pgrp, SIGCONT) < 0) {
- int err = errno;
-
- if (!bg) {
- j->flags &= ~JF_FG;
- if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
- tcsetattr(tty_fd, TCSADRAIN, &tty_state);
- if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
- warningf(true,
- "fg: 2nd tcsetpgrp(%d, %ld) failed: %s",
- tty_fd, (long)kshpgrp, strerror(errno));
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("cannot continue job %s: %s",
- cp, strerror(err));
- return (1);
- }
- if (!bg) {
- if (ttypgrp_ok) {
- j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP);
- }
- rv = j_waitj(j, JW_NONE, "jw:resume");
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
- return (rv);
-}
-#endif
-
-/* are there any running or stopped jobs ? */
-int
-j_stopped_running(void)
-{
- Job *j;
- int which = 0;
-
- for (j = job_list; j != NULL; j = j->next) {
-#ifndef MKSH_UNEMPLOYED
- if (j->ppid == procpid && j->state == PSTOPPED)
- which |= 1;
-#endif
- if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid &&
- j->ppid == procpid && j->state == PRUNNING)
- which |= 2;
- }
- if (which) {
- shellf("You have %s%s%s jobs\n",
- which & 1 ? "stopped" : "",
- which == 3 ? " and " : "",
- which & 2 ? "running" : "");
- return (1);
- }
-
- return (0);
-}
-
-int
-j_njobs(void)
-{
- Job *j;
- int nj = 0;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
- for (j = job_list; j; j = j->next)
- nj++;
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
- return (nj);
-}
-
-
-/* list jobs for jobs built-in */
-int
-j_jobs(const char *cp, int slp,
- int nflag) /* 0: short, 1: long, 2: pgrp */
-{
- Job *j, *tmp;
- int how;
- int zflag = 0;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- if (nflag < 0) { /* kludge: print zombies */
- nflag = 0;
- zflag = 1;
- }
- if (cp) {
- int ecode;
-
- if ((j = j_lookup(cp, &ecode)) == NULL) {
- sigprocmask(SIG_SETMASK, &omask, NULL);
- bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
- return (1);
- }
- } else
- j = job_list;
- how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP);
- for (; j; j = j->next) {
- if ((!(j->flags & JF_ZOMBIE) || zflag) &&
- (!nflag || (j->flags & JF_CHANGED))) {
- j_print(j, how, shl_stdout);
- if (j->state == PEXITED || j->state == PSIGNALLED)
- j->flags |= JF_REMOVE;
- }
- if (cp)
- break;
- }
- /* Remove jobs after printing so there won't be multiple + or - jobs */
- for (j = job_list; j; j = tmp) {
- tmp = j->next;
- if (j->flags & JF_REMOVE)
- remove_job(j, "jobs");
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
- return (0);
-}
-
-/* list jobs for top-level notification */
-void
-j_notify(void)
-{
- Job *j, *tmp;
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
- for (j = job_list; j; j = j->next) {
-#ifndef MKSH_UNEMPLOYED
- if (Flag(FMONITOR) && (j->flags & JF_CHANGED))
- j_print(j, JP_MEDIUM, shl_out);
-#endif
- /* Remove job after doing reports so there aren't
- * multiple +/- jobs.
- */
- if (j->state == PEXITED || j->state == PSIGNALLED)
- j->flags |= JF_REMOVE;
- }
- for (j = job_list; j; j = tmp) {
- tmp = j->next;
- if (j->flags & JF_REMOVE)
- remove_job(j, "notify");
- }
- shf_flush(shl_out);
- sigprocmask(SIG_SETMASK, &omask, NULL);
-}
-
-/* Return pid of last process in last asynchronous job */
-pid_t
-j_async(void)
-{
- sigset_t omask;
-
- sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
- if (async_job)
- async_job->flags |= JF_KNOWN;
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- return (async_pid);
-}
-
-/*
- * Make j the last async process
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-j_set_async(Job *j)
-{
- Job *jl, *oldest;
-
- if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
- remove_job(async_job, "async");
- if (!(j->flags & JF_STARTED)) {
- internal_warningf("j_async: job not started");
- return;
- }
- async_job = j;
- async_pid = j->last_proc->pid;
- while (nzombie > CHILD_MAX) {
- oldest = NULL;
- for (jl = job_list; jl; jl = jl->next)
- if (jl != async_job && (jl->flags & JF_ZOMBIE) &&
- (!oldest || jl->age < oldest->age))
- oldest = jl;
- if (!oldest) {
- /* XXX debugging */
- if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) {
- internal_warningf("j_async: bad nzombie (%d)",
- nzombie);
- nzombie = 0;
- }
- break;
- }
- remove_job(oldest, "zombie");
- }
-}
-
-/*
- * Start a job: set STARTED, check for held signals and set j->last_proc
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-j_startjob(Job *j)
-{
- Proc *p;
-
- j->flags |= JF_STARTED;
- for (p = j->proc_list; p->next; p = p->next)
- ;
- j->last_proc = p;
-
- if (held_sigchld) {
- held_sigchld = 0;
- /* Don't call j_sigchld() as it may remove job... */
- kill(procpid, SIGCHLD);
- }
-}
-
-/*
- * wait for job to complete or change state
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static int
-j_waitj(Job *j,
- int flags, /* see JW_* */
- const char *where)
-{
- int rv;
-
- /*
- * No auto-notify on the job we are waiting on.
- */
- j->flags |= JF_WAITING;
- if (flags & JW_ASYNCNOTIFY)
- j->flags |= JF_W_ASYNCNOTIFY;
-
-#ifndef MKSH_UNEMPLOYED
- if (!Flag(FMONITOR))
-#endif
- flags |= JW_STOPPEDWAIT;
-
- while (j->state == PRUNNING ||
- ((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) {
- sigsuspend(&sm_default);
- if (fatal_trap) {
- int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);
- j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
- runtraps(TF_FATAL);
- j->flags |= oldf; /* not reached... */
- }
- if ((flags & JW_INTERRUPT) && (rv = trap_pending())) {
- j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
- return (-rv);
- }
- }
- j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
-
- if (j->flags & JF_FG) {
- j->flags &= ~JF_FG;
-#ifndef MKSH_UNEMPLOYED
- if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) {
- /*
- * Save the tty's current pgrp so it can be restored
- * when the job is foregrounded. This is to
- * deal with things like the GNU su which does
- * a fork/exec instead of an exec (the fork means
- * the execed shell gets a different pid from its
- * pgrp, so naturally it sets its pgrp and gets hosed
- * when it gets foregrounded by the parent shell which
- * has restored the tty's pgrp to that of the su
- * process).
- */
- if (j->state == PSTOPPED &&
- (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
- j->flags |= JF_SAVEDTTYPGRP;
- if (tcsetpgrp(tty_fd, kshpgrp) < 0)
- warningf(true,
- "j_waitj: tcsetpgrp(%d, %ld) failed: %s",
- tty_fd, (long)kshpgrp, strerror(errno));
- if (j->state == PSTOPPED) {
- j->flags |= JF_SAVEDTTY;
- tcgetattr(tty_fd, &j->ttystate);
- }
- }
-#endif
- if (tty_fd >= 0) {
- /*
- * Only restore tty settings if job was originally
- * started in the foreground. Problems can be
- * caused by things like 'more foobar &' which will
- * typically get and save the shell's vi/emacs tty
- * settings before setting up the tty for itself;
- * when more exits, it restores the 'original'
- * settings, and things go down hill from there...
- */
- if (j->state == PEXITED && j->status == 0 &&
- (j->flags & JF_USETTYMODE)) {
- tcgetattr(tty_fd, &tty_state);
- } else {
- tcsetattr(tty_fd, TCSADRAIN, &tty_state);
- /*-
- * Don't use tty mode if job is stopped and
- * later restarted and exits. Consider
- * the sequence:
- * vi foo (stopped)
- * ...
- * stty something
- * ...
- * fg (vi; ZZ)
- * mode should be that of the stty, not what
- * was before the vi started.
- */
- if (j->state == PSTOPPED)
- j->flags &= ~JF_USETTYMODE;
- }
- }
-#ifndef MKSH_UNEMPLOYED
- /*
- * If it looks like user hit ^C to kill a job, pretend we got
- * one too to break out of for loops, etc. (AT&T ksh does this
- * even when not monitoring, but this doesn't make sense since
- * a tty generated ^C goes to the whole process group)
- */
- {
- int status;
-
- status = j->last_proc->status;
- if (Flag(FMONITOR) && j->state == PSIGNALLED &&
- WIFSIGNALED(status) &&
- (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))
- trapsig(WTERMSIG(status));
- }
-#endif
- }
-
- j_usrtime = j->usrtime;
- j_systime = j->systime;
- rv = j->status;
-
- if (!(flags & JW_ASYNCNOTIFY)
-#ifndef MKSH_UNEMPLOYED
- && (!Flag(FMONITOR) || j->state != PSTOPPED)
-#endif
- ) {
- j_print(j, JP_SHORT, shl_out);
- shf_flush(shl_out);
- }
- if (j->state != PSTOPPED
-#ifndef MKSH_UNEMPLOYED
- && (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY))
-#endif
- )
- remove_job(j, where);
-
- return (rv);
-}
-
-/*
- * SIGCHLD handler to reap children and update job states
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-/* ARGSUSED */
-static void
-j_sigchld(int sig MKSH_A_UNUSED)
-{
- /* this runs inside interrupt context, with errno saved */
-
- Job *j;
- Proc *p = NULL;
- pid_t pid;
- int status;
- struct rusage ru0, ru1;
-
- /*
- * Don't wait for any processes if a job is partially started.
- * This is so we don't do away with the process group leader
- * before all the processes in a pipe line are started (so the
- * setpgid() won't fail)
- */
- for (j = job_list; j; j = j->next)
- if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
- held_sigchld = 1;
- return;
- }
-
- getrusage(RUSAGE_CHILDREN, &ru0);
- do {
- pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
-
- /*
- * return if this would block (0) or no children
- * or interrupted (-1)
- */
- if (pid <= 0)
- return;
-
- getrusage(RUSAGE_CHILDREN, &ru1);
-
- /* find job and process structures for this pid */
- for (j = job_list; j != NULL; j = j->next)
- for (p = j->proc_list; p != NULL; p = p->next)
- if (p->pid == pid)
- goto found;
- found:
- if (j == NULL) {
- /* Can occur if process has kids, then execs shell
- warningf(true, "bad process waited for (pid = %d)",
- pid);
- */
- ru0 = ru1;
- continue;
- }
-
- timeradd(&j->usrtime, &ru1.ru_utime, &j->usrtime);
- timersub(&j->usrtime, &ru0.ru_utime, &j->usrtime);
- timeradd(&j->systime, &ru1.ru_stime, &j->systime);
- timersub(&j->systime, &ru0.ru_stime, &j->systime);
- ru0 = ru1;
- p->status = status;
-#ifndef MKSH_UNEMPLOYED
- if (WIFSTOPPED(status))
- p->state = PSTOPPED;
- else
-#endif
- if (WIFSIGNALED(status))
- p->state = PSIGNALLED;
- else
- p->state = PEXITED;
-
- check_job(j); /* check to see if entire job is done */
- } while (1);
-}
-
-/*
- * Called only when a process in j has exited/stopped (ie, called only
- * from j_sigchld()). If no processes are running, the job status
- * and state are updated, asynchronous job notification is done and,
- * if unneeded, the job is removed.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-check_job(Job *j)
-{
- int jstate;
- Proc *p;
-
- /* XXX debugging (nasty - interrupt routine using shl_out) */
- if (!(j->flags & JF_STARTED)) {
- internal_warningf("check_job: job started (flags 0x%x)",
- j->flags);
- return;
- }
-
- jstate = PRUNNING;
- for (p=j->proc_list; p != NULL; p = p->next) {
- if (p->state == PRUNNING)
- return; /* some processes still running */
- if (p->state > jstate)
- jstate = p->state;
- }
- j->state = jstate;
-
- switch (j->last_proc->state) {
- case PEXITED:
- j->status = WEXITSTATUS(j->last_proc->status);
- break;
- case PSIGNALLED:
- j->status = 128 + WTERMSIG(j->last_proc->status);
- break;
- default:
- j->status = 0;
- break;
- }
-
- /*
- * Note when co-process dies: can't be done in j_wait() nor
- * remove_job() since neither may be called for non-interactive
- * shells.
- */
- if (j->state == PEXITED || j->state == PSIGNALLED) {
- /*
- * No need to keep co-process input any more
- * (at least, this is what ksh93d thinks)
- */
- if (coproc.job == j) {
- coproc.job = NULL;
- /*
- * XXX would be nice to get the closes out of here
- * so they aren't done in the signal handler.
- * Would mean a check in coproc_getfd() to
- * do "if job == 0 && write >= 0, close write".
- */
- coproc_write_close(coproc.write);
- }
- /* Do we need to keep the output? */
- if (j->coproc_id && j->coproc_id == coproc.id &&
- --coproc.njobs == 0)
- coproc_readw_close(coproc.read);
- }
-
- j->flags |= JF_CHANGED;
-#ifndef MKSH_UNEMPLOYED
- if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) {
- /*
- * Only put stopped jobs at the front to avoid confusing
- * the user (don't want finished jobs effecting %+ or %-)
- */
- if (j->state == PSTOPPED)
- put_job(j, PJ_ON_FRONT);
- if (Flag(FNOTIFY) &&
- (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) {
- /* Look for the real file descriptor 2 */
- {
- struct env *ep;
- int fd = 2;
-
- for (ep = e; ep; ep = ep->oenv)
- if (ep->savefd && ep->savefd[2])
- fd = ep->savefd[2];
- shf_reopen(fd, SHF_WR, shl_j);
- }
- /*
- * Can't call j_notify() as it removes jobs. The job
- * must stay in the job list as j_waitj() may be
- * running with this job.
- */
- j_print(j, JP_MEDIUM, shl_j);
- shf_flush(shl_j);
- if (!(j->flags & JF_WAITING) && j->state != PSTOPPED)
- remove_job(j, "notify");
- }
- }
-#endif
- if (
-#ifndef MKSH_UNEMPLOYED
- !Flag(FMONITOR) &&
-#endif
- !(j->flags & (JF_WAITING|JF_FG)) &&
- j->state != PSTOPPED) {
- if (j == async_job || (j->flags & JF_KNOWN)) {
- j->flags |= JF_ZOMBIE;
- j->job = -1;
- nzombie++;
- } else
- remove_job(j, "checkjob");
- }
-}
-
-/*
- * Print job status in either short, medium or long format.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-j_print(Job *j, int how, struct shf *shf)
-{
- Proc *p;
- int state;
- int status;
- int coredumped;
- char jobchar = ' ';
- char buf[64];
- const char *filler;
- int output = 0;
-
- if (how == JP_PGRP) {
- /*
- * POSIX doesn't say what to do it there is no process
- * group leader (ie, !FMONITOR). We arbitrarily return
- * last pid (which is what $! returns).
- */
- shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
- (j->last_proc ? j->last_proc->pid : 0)));
- return;
- }
- j->flags &= ~JF_CHANGED;
- filler = j->job > 10 ? "\n " : "\n ";
- if (j == job_list)
- jobchar = '+';
- else if (j == job_list->next)
- jobchar = '-';
-
- for (p = j->proc_list; p != NULL;) {
- coredumped = 0;
- switch (p->state) {
- case PRUNNING:
- memcpy(buf, "Running", 8);
- break;
- case PSTOPPED:
- strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess,
- sizeof(buf));
- break;
- case PEXITED:
- if (how == JP_SHORT)
- buf[0] = '\0';
- else if (WEXITSTATUS(p->status) == 0)
- memcpy(buf, "Done", 5);
- else
- shf_snprintf(buf, sizeof(buf), "Done (%d)",
- WEXITSTATUS(p->status));
- break;
- case PSIGNALLED:
-#ifdef WCOREDUMP
- if (WCOREDUMP(p->status))
- coredumped = 1;
-#endif
- /*
- * kludge for not reporting 'normal termination
- * signals' (i.e. SIGINT, SIGPIPE)
- */
- if (how == JP_SHORT && !coredumped &&
- (WTERMSIG(p->status) == SIGINT ||
- WTERMSIG(p->status) == SIGPIPE)) {
- buf[0] = '\0';
- } else
- strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,
- sizeof(buf));
- break;
- }
-
- if (how != JP_SHORT) {
- if (p == j->proc_list)
- shf_fprintf(shf, "[%d] %c ", j->job, jobchar);
- else
- shf_fprintf(shf, "%s", filler);
- }
-
- if (how == JP_LONG)
- shf_fprintf(shf, "%5d ", (int)p->pid);
-
- if (how == JP_SHORT) {
- if (buf[0]) {
- output = 1;
- shf_fprintf(shf, "%s%s ",
- buf, coredumped ? " (core dumped)" : null);
- }
- } else {
- output = 1;
- shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
- p->next ? "|" : null,
- coredumped ? " (core dumped)" : null);
- }
-
- state = p->state;
- status = p->status;
- p = p->next;
- while (p && p->state == state && p->status == status) {
- if (how == JP_LONG)
- shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
- (int)p->pid, " ", p->command,
- p->next ? "|" : null);
- else if (how == JP_MEDIUM)
- shf_fprintf(shf, " %s%s", p->command,
- p->next ? "|" : null);
- p = p->next;
- }
- }
- if (output)
- shf_putc('\n', shf);
-}
-
-/*
- * Convert % sequence to job
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static Job *
-j_lookup(const char *cp, int *ecodep)
-{
- Job *j, *last_match;
- Proc *p;
- int len, job = 0;
-
- if (ksh_isdigit(*cp)) {
- getn(cp, &job);
- /* Look for last_proc->pid (what $! returns) first... */
- for (j = job_list; j != NULL; j = j->next)
- if (j->last_proc && j->last_proc->pid == job)
- return (j);
- /*
- * ...then look for process group (this is non-POSIX,
- * but should not break anything
- */
- for (j = job_list; j != NULL; j = j->next)
- if (j->pgrp && j->pgrp == job)
- return (j);
- if (ecodep)
- *ecodep = JL_NOSUCH;
- return (NULL);
- }
- if (*cp != '%') {
- if (ecodep)
- *ecodep = JL_INVALID;
- return (NULL);
- }
- switch (*++cp) {
- case '\0': /* non-standard */
- case '+':
- case '%':
- if (job_list != NULL)
- return (job_list);
- break;
-
- case '-':
- if (job_list != NULL && job_list->next)
- return (job_list->next);
- break;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- getn(cp, &job);
- for (j = job_list; j != NULL; j = j->next)
- if (j->job == job)
- return (j);
- break;
-
- case '?': /* %?string */
- last_match = NULL;
- for (j = job_list; j != NULL; j = j->next)
- for (p = j->proc_list; p != NULL; p = p->next)
- if (strstr(p->command, cp+1) != NULL) {
- if (last_match) {
- if (ecodep)
- *ecodep = JL_AMBIG;
- return (NULL);
- }
- last_match = j;
- }
- if (last_match)
- return (last_match);
- break;
-
- default: /* %string */
- len = strlen(cp);
- last_match = NULL;
- for (j = job_list; j != NULL; j = j->next)
- if (strncmp(cp, j->proc_list->command, len) == 0) {
- if (last_match) {
- if (ecodep)
- *ecodep = JL_AMBIG;
- return (NULL);
- }
- last_match = j;
- }
- if (last_match)
- return (last_match);
- break;
- }
- if (ecodep)
- *ecodep = JL_NOSUCH;
- return (NULL);
-}
-
-static Job *free_jobs;
-static Proc *free_procs;
-
-/*
- * allocate a new job and fill in the job number.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static Job *
-new_job(void)
-{
- int i;
- Job *newj, *j;
-
- if (free_jobs != NULL) {
- newj = free_jobs;
- free_jobs = free_jobs->next;
- } else
- newj = alloc(sizeof(Job), APERM);
-
- /* brute force method */
- for (i = 1; ; i++) {
- for (j = job_list; j && j->job != i; j = j->next)
- ;
- if (j == NULL)
- break;
- }
- newj->job = i;
-
- return (newj);
-}
-
-/*
- * Allocate new process struct
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static Proc *
-new_proc(void)
-{
- Proc *p;
-
- if (free_procs != NULL) {
- p = free_procs;
- free_procs = free_procs->next;
- } else
- p = alloc(sizeof(Proc), APERM);
-
- return (p);
-}
-
-/*
- * Take job out of job_list and put old structures into free list.
- * Keeps nzombies, last_job and async_job up to date.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-remove_job(Job *j, const char *where)
-{
- Proc *p, *tmp;
- Job **prev, *curr;
-
- prev = &job_list;
- curr = *prev;
- for (; curr != NULL && curr != j; prev = &curr->next, curr = *prev)
- ;
- if (curr != j) {
- internal_warningf("remove_job: job not found (%s)", where);
- return;
- }
- *prev = curr->next;
-
- /* free up proc structures */
- for (p = j->proc_list; p != NULL; ) {
- tmp = p;
- p = p->next;
- tmp->next = free_procs;
- free_procs = tmp;
- }
-
- if ((j->flags & JF_ZOMBIE) && j->ppid == procpid)
- --nzombie;
- j->next = free_jobs;
- free_jobs = j;
-
- if (j == last_job)
- last_job = NULL;
- if (j == async_job)
- async_job = NULL;
-}
-
-/*
- * put j in a particular location (taking it out job_list if it is there
- * already)
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-put_job(Job *j, int where)
-{
- Job **prev, *curr;
-
- /* Remove job from list (if there) */
- prev = &job_list;
- curr = job_list;
- for (; curr && curr != j; prev = &curr->next, curr = *prev)
- ;
- if (curr == j)
- *prev = curr->next;
-
- switch (where) {
- case PJ_ON_FRONT:
- j->next = job_list;
- job_list = j;
- break;
-
- case PJ_PAST_STOPPED:
- prev = &job_list;
- curr = job_list;
- for (; curr && curr->state == PSTOPPED; prev = &curr->next,
- curr = *prev)
- ;
- j->next = curr;
- *prev = j;
- break;
- }
-}
-
-/*
- * nuke a job (called when unable to start full job).
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static int
-kill_job(Job *j, int sig)
-{
- Proc *p;
- int rval = 0;
-
- for (p = j->proc_list; p != NULL; p = p->next)
- if (p->pid != 0)
- if (kill(p->pid, sig) < 0)
- rval = -1;
- return (rval);
-}
diff --git a/mksh/src/lalloc.c b/mksh/src/lalloc.c
deleted file mode 100644
index 79627d1..0000000
--- a/mksh/src/lalloc.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*-
- * Copyright © 2009
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.11 2009/08/08 13:08:51 tg Exp $");
-
-/* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */
-#if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0)
-#define remalloc(p,n) ((p) == NULL ? malloc(n) : realloc((p), (n)))
-#else
-#define remalloc(p,n) realloc((p), (n))
-#endif
-
-#define ALLOC_ISUNALIGNED(p) (((ptrdiff_t)(p)) % ALLOC_SIZE)
-
-static ALLOC_ITEM *findptr(ALLOC_ITEM **, char *, Area *);
-
-void
-ainit(Area *ap)
-{
- /* area pointer is an ALLOC_ITEM, just the head of the list */
- ap->next = NULL;
-}
-
-static ALLOC_ITEM *
-findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap)
-{
- void *lp;
-
-#ifndef MKSH_SMALL
- if (ALLOC_ISUNALIGNED(ptr))
- goto fail;
-#endif
- /* get address of ALLOC_ITEM from user item */
- /*
- * note: the alignment of "ptr" to ALLOC_SIZE is checked
- * above; the "void *" gets us rid of a gcc 2.95 warning
- */
- *lpp = (lp = ptr - ALLOC_SIZE);
- /* search for allocation item in group list */
- while (ap->next != lp)
- if ((ap = ap->next) == NULL) {
-#ifndef MKSH_SMALL
- fail:
-#endif
- internal_errorf("rogue pointer %p", ptr);
- }
- return (ap);
-}
-
-void *
-aresize(void *ptr, size_t numb, Area *ap)
-{
- ALLOC_ITEM *lp = NULL;
-
- /* resizing (true) or newly allocating? */
- if (ptr != NULL) {
- ALLOC_ITEM *pp;
-
- pp = findptr(&lp, ptr, ap);
- pp->next = lp->next;
- }
-
- if ((numb >= SIZE_MAX - ALLOC_SIZE) ||
- (lp = remalloc(lp, numb + ALLOC_SIZE)) == NULL
-#ifndef MKSH_SMALL
- || ALLOC_ISUNALIGNED(lp)
-#endif
- )
- internal_errorf("cannot allocate %lu data bytes",
- (unsigned long)numb);
- /* this only works because Area is an ALLOC_ITEM */
- lp->next = ap->next;
- ap->next = lp;
- /* return user item address */
- return ((char *)lp + ALLOC_SIZE);
-}
-
-void
-afree(void *ptr, Area *ap)
-{
- if (ptr != NULL) {
- ALLOC_ITEM *lp, *pp;
-
- pp = findptr(&lp, ptr, ap);
- /* unhook */
- pp->next = lp->next;
- /* now free ALLOC_ITEM */
- free(lp);
- }
-}
-
-void
-afreeall(Area *ap)
-{
- ALLOC_ITEM *lp;
-
- /* traverse group (linked list) */
- while ((lp = ap->next) != NULL) {
- /* make next ALLOC_ITEM head of list */
- ap->next = lp->next;
- /* free old head */
- free(lp);
- }
-}
diff --git a/mksh/src/lex.c b/mksh/src/lex.c
deleted file mode 100644
index d0219e7..0000000
--- a/mksh/src/lex.c
+++ /dev/null
@@ -1,1782 +0,0 @@
-/* $OpenBSD: lex.c,v 1.44 2008/07/03 17:52:08 otto Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.118 2010/07/25 11:35:41 tg Exp $");
-
-/*
- * states while lexing word
- */
-#define SBASE 0 /* outside any lexical constructs */
-#define SWORD 1 /* implicit quoting for substitute() */
-#define SLETPAREN 2 /* inside (( )), implicit quoting */
-#define SSQUOTE 3 /* inside '' */
-#define SDQUOTE 4 /* inside "" */
-#define SEQUOTE 5 /* inside $'' */
-#define SBRACE 6 /* inside ${} */
-#define SQBRACE 7 /* inside "${}" */
-#define SCSPAREN 8 /* inside $() */
-#define SBQUOTE 9 /* inside `` */
-#define SASPAREN 10 /* inside $(( )) */
-#define SHEREDELIM 11 /* parsing <<,<<- delimiter */
-#define SHEREDQUOTE 12 /* parsing " in <<,<<- delimiter */
-#define SPATTERN 13 /* parsing *(...|...) pattern (*+?@!) */
-#define STBRACE 14 /* parsing ${...[#%]...} */
-#define SLETARRAY 15 /* inside =( ), just copy */
-#define SADELIM 16 /* like SBASE, looking for delimiter */
-#define SHERESTRING 17 /* parsing <<< string */
-
-/* Structure to keep track of the lexing state and the various pieces of info
- * needed for each particular state. */
-typedef struct lex_state Lex_state;
-struct lex_state {
- int ls_state;
- union {
- /* $(...) */
- struct scsparen_info {
- int nparen; /* count open parenthesis */
- int csstate; /* XXX remove */
-#define ls_scsparen ls_info.u_scsparen
- } u_scsparen;
-
- /* $((...)) */
- struct sasparen_info {
- int nparen; /* count open parenthesis */
- int start; /* marks start of $(( in output str */
-#define ls_sasparen ls_info.u_sasparen
- } u_sasparen;
-
- /* ((...)) */
- struct sletparen_info {
- int nparen; /* count open parenthesis */
-#define ls_sletparen ls_info.u_sletparen
- } u_sletparen;
-
- /* `...` */
- struct sbquote_info {
- int indquotes; /* true if in double quotes: "`...`" */
-#define ls_sbquote ls_info.u_sbquote
- } u_sbquote;
-
-#ifndef MKSH_SMALL
- /* =(...) */
- struct sletarray_info {
- int nparen; /* count open parentheses */
-#define ls_sletarray ls_info.u_sletarray
- } u_sletarray;
-#endif
-
- /* ADELIM */
- struct sadelim_info {
- unsigned char nparen; /* count open parentheses */
-#define SADELIM_BASH 0
-#define SADELIM_MAKE 1
- unsigned char style;
- unsigned char delimiter;
- unsigned char num;
- unsigned char flags; /* ofs. into sadelim_flags[] */
-#define ls_sadelim ls_info.u_sadelim
- } u_sadelim;
-
- /* $'...' */
- struct sequote_info {
- bool got_NUL; /* ignore rest of string */
-#define ls_sequote ls_info.u_sequote
- } u_sequote;
-
- Lex_state *base; /* used to point to next state block */
- } ls_info;
-};
-
-typedef struct {
- Lex_state *base;
- Lex_state *end;
-} State_info;
-
-static void readhere(struct ioword *);
-static int getsc__(void);
-static void getsc_line(Source *);
-static int getsc_bn(void);
-static int s_get(void);
-static void s_put(int);
-static char *get_brace_var(XString *, char *);
-static int arraysub(char **);
-static const char *ungetsc(int);
-static void gethere(bool);
-static Lex_state *push_state_(State_info *, Lex_state *);
-static Lex_state *pop_state_(State_info *, Lex_state *);
-
-static int dopprompt(const char *, int, bool);
-
-static int backslash_skip;
-static int ignore_backslash_newline;
-
-/* optimised getsc_bn() */
-#define _getsc() (*source->str != '\0' && *source->str != '\\' \
- && !backslash_skip && !(source->flags & SF_FIRST) \
- ? *source->str++ : getsc_bn())
-/* optimised getsc__() */
-#define _getsc_() ((*source->str != '\0') && !(source->flags & SF_FIRST) \
- ? *source->str++ : getsc__())
-
-#ifdef MKSH_SMALL
-static int getsc(void);
-static int getsc_(void);
-
-static int
-getsc(void)
-{
- return (_getsc());
-}
-
-static int
-getsc_(void)
-{
- return (_getsc_());
-}
-#else
-/* !MKSH_SMALL: use them inline */
-#define getsc() _getsc()
-#define getsc_() _getsc_()
-#endif
-
-#define STATE_BSIZE 32
-
-#define PUSH_STATE(s) do { \
- if (++statep == state_info.end) \
- statep = push_state_(&state_info, statep); \
- state = statep->ls_state = (s); \
-} while (0)
-
-#define POP_STATE() do { \
- if (--statep == state_info.base) \
- statep = pop_state_(&state_info, statep); \
- state = statep->ls_state; \
-} while (0)
-
-/**
- * Lexical analyser
- *
- * tokens are not regular expressions, they are LL(1).
- * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
- * hence the state stack.
- */
-
-int
-yylex(int cf)
-{
- Lex_state states[STATE_BSIZE], *statep, *s2, *base;
- State_info state_info;
- int c, c2, state;
- XString ws; /* expandable output word */
- char *wp; /* output word pointer */
- char *sp, *dp;
-
- Again:
- states[0].ls_state = -1;
- states[0].ls_info.base = NULL;
- statep = &states[1];
- state_info.base = states;
- state_info.end = &state_info.base[STATE_BSIZE];
-
- Xinit(ws, wp, 64, ATEMP);
-
- backslash_skip = 0;
- ignore_backslash_newline = 0;
-
- if (cf&ONEWORD)
- state = SWORD;
- else if (cf&LETEXPR) {
- /* enclose arguments in (double) quotes */
- *wp++ = OQUOTE;
- state = SLETPAREN;
- statep->ls_sletparen.nparen = 0;
-#ifndef MKSH_SMALL
- } else if (cf&LETARRAY) {
- state = SLETARRAY;
- statep->ls_sletarray.nparen = 0;
-#endif
- } else { /* normal lexing */
- state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
- while ((c = getsc()) == ' ' || c == '\t')
- ;
- if (c == '#') {
- ignore_backslash_newline++;
- while ((c = getsc()) != '\0' && c != '\n')
- ;
- ignore_backslash_newline--;
- }
- ungetsc(c);
- }
- if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */
- source->flags &= ~SF_ALIAS;
- cf |= ALIAS;
- }
-
- /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
- statep->ls_state = state;
-
- /* check for here string */
- if (state == SHEREDELIM) {
- c = getsc();
- if (c == '<') {
- state = SHERESTRING;
- while ((c = getsc()) == ' ' || c == '\t')
- ;
- ungetsc(c);
- c = '<';
- goto accept_nonword;
- }
- ungetsc(c);
- }
-
- /* collect non-special or quoted characters to form word */
- while (!((c = getsc()) == 0 ||
- ((state == SBASE || state == SHEREDELIM || state == SHERESTRING) &&
- ctype(c, C_LEX1)))) {
- accept_nonword:
- Xcheck(ws, wp);
- switch (state) {
- case SADELIM:
- if (c == '(')
- statep->ls_sadelim.nparen++;
- else if (c == ')')
- statep->ls_sadelim.nparen--;
- else if (statep->ls_sadelim.nparen == 0 &&
- (c == /*{*/ '}' || c == statep->ls_sadelim.delimiter)) {
- *wp++ = ADELIM;
- *wp++ = c;
- if (c == /*{*/ '}' || --statep->ls_sadelim.num == 0)
- POP_STATE();
- if (c == /*{*/ '}')
- POP_STATE();
- break;
- }
- /* FALLTHROUGH */
- case SBASE:
- if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
- *wp = EOS; /* temporary */
- if (is_wdvarname(Xstring(ws, wp), false)) {
- char *p, *tmp;
-
- if (arraysub(&tmp)) {
- *wp++ = CHAR;
- *wp++ = c;
- for (p = tmp; *p; ) {
- Xcheck(ws, wp);
- *wp++ = CHAR;
- *wp++ = *p++;
- }
- afree(tmp, ATEMP);
- break;
- } else {
- Source *s;
-
- s = pushs(SREREAD,
- source->areap);
- s->start = s->str =
- s->u.freeme = tmp;
- s->next = source;
- source = s;
- }
- }
- *wp++ = CHAR;
- *wp++ = c;
- break;
- }
- /* FALLTHROUGH */
- Sbase1: /* includes *(...|...) pattern (*+?@!) */
- if (c == '*' || c == '@' || c == '+' || c == '?' ||
- c == '!') {
- c2 = getsc();
- if (c2 == '(' /*)*/ ) {
- *wp++ = OPAT;
- *wp++ = c;
- PUSH_STATE(SPATTERN);
- break;
- }
- ungetsc(c2);
- }
- /* FALLTHROUGH */
- Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */
- switch (c) {
- case '\\':
- getsc_qchar:
- if ((c = getsc())) {
- /* trailing \ is lost */
- *wp++ = QCHAR;
- *wp++ = c;
- }
- break;
- case '\'':
- open_ssquote:
- *wp++ = OQUOTE;
- ignore_backslash_newline++;
- PUSH_STATE(SSQUOTE);
- break;
- case '"':
- open_sdquote:
- *wp++ = OQUOTE;
- PUSH_STATE(SDQUOTE);
- break;
- default:
- goto Subst;
- }
- break;
-
- Subst:
- switch (c) {
- case '\\':
- c = getsc();
- switch (c) {
- case '"':
- if ((cf & HEREDOC))
- goto heredocquote;
- /* FALLTHROUGH */
- case '\\':
- case '$': case '`':
- store_qchar:
- *wp++ = QCHAR;
- *wp++ = c;
- break;
- default:
- heredocquote:
- Xcheck(ws, wp);
- if (c) {
- /* trailing \ is lost */
- *wp++ = CHAR;
- *wp++ = '\\';
- *wp++ = CHAR;
- *wp++ = c;
- }
- break;
- }
- break;
- case '$':
- subst_dollar:
- c = getsc();
- if (c == '(') /*)*/ {
- c = getsc();
- if (c == '(') /*)*/ {
- PUSH_STATE(SASPAREN);
- statep->ls_sasparen.nparen = 2;
- statep->ls_sasparen.start =
- Xsavepos(ws, wp);
- *wp++ = EXPRSUB;
- } else {
- ungetsc(c);
- PUSH_STATE(SCSPAREN);
- statep->ls_scsparen.nparen = 1;
- statep->ls_scsparen.csstate = 0;
- *wp++ = COMSUB;
- }
- } else if (c == '{') /*}*/ {
- *wp++ = OSUBST;
- *wp++ = '{'; /*}*/
- wp = get_brace_var(&ws, wp);
- c = getsc();
- /* allow :# and :% (ksh88 compat) */
- if (c == ':') {
- *wp++ = CHAR;
- *wp++ = c;
- c = getsc();
- if (c == ':') {
- *wp++ = CHAR;
- *wp++ = '0';
- *wp++ = ADELIM;
- *wp++ = ':';
- PUSH_STATE(SBRACE);
- PUSH_STATE(SADELIM);
- statep->ls_sadelim.style = SADELIM_BASH;
- statep->ls_sadelim.delimiter = ':';
- statep->ls_sadelim.num = 1;
- statep->ls_sadelim.nparen = 0;
- break;
- } else if (ksh_isdigit(c) ||
- c == '('/*)*/ || c == ' ' ||
- c == '$' /* XXX what else? */) {
- /* substring subst. */
- if (c != ' ') {
- *wp++ = CHAR;
- *wp++ = ' ';
- }
- ungetsc(c);
- PUSH_STATE(SBRACE);
- PUSH_STATE(SADELIM);
- statep->ls_sadelim.style = SADELIM_BASH;
- statep->ls_sadelim.delimiter = ':';
- statep->ls_sadelim.num = 2;
- statep->ls_sadelim.nparen = 0;
- break;
- }
- } else if (c == '/') {
- *wp++ = CHAR;
- *wp++ = c;
- if ((c = getsc()) == '/') {
- *wp++ = ADELIM;
- *wp++ = c;
- } else
- ungetsc(c);
- PUSH_STATE(SBRACE);
- PUSH_STATE(SADELIM);
- statep->ls_sadelim.style = SADELIM_BASH;
- statep->ls_sadelim.delimiter = '/';
- statep->ls_sadelim.num = 1;
- statep->ls_sadelim.nparen = 0;
- break;
- }
- /* If this is a trim operation,
- * treat (,|,) specially in STBRACE.
- */
- if (ctype(c, C_SUBOP2)) {
- ungetsc(c);
- PUSH_STATE(STBRACE);
- } else {
- ungetsc(c);
- if (state == SDQUOTE)
- PUSH_STATE(SQBRACE);
- else
- PUSH_STATE(SBRACE);
- }
- } else if (ksh_isalphx(c)) {
- *wp++ = OSUBST;
- *wp++ = 'X';
- do {
- Xcheck(ws, wp);
- *wp++ = c;
- c = getsc();
- } while (ksh_isalnux(c));
- *wp++ = '\0';
- *wp++ = CSUBST;
- *wp++ = 'X';
- ungetsc(c);
- } else if (ctype(c, C_VAR1 | C_DIGIT)) {
- Xcheck(ws, wp);
- *wp++ = OSUBST;
- *wp++ = 'X';
- *wp++ = c;
- *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_sequote.got_NUL = false;
- break;
- } else {
- *wp++ = CHAR;
- *wp++ = '$';
- ungetsc(c);
- }
- break;
- case '`':
- subst_gravis:
- PUSH_STATE(SBQUOTE);
- *wp++ = COMSUB;
- /* Need to know if we are inside double quotes
- * since sh/AT&T-ksh translate the \" to " in
- * "`...\"...`".
- * This is not done in POSIX mode (section
- * 3.2.3, Double Quotes: "The backquote shall
- * retain its special meaning introducing the
- * other form of command substitution (see
- * 3.6.3). The portion of the quoted string
- * from the initial backquote and the
- * characters up to the next backquote that
- * is not preceded by a backslash (having
- * escape characters removed) defines that
- * command whose output replaces `...` when
- * the word is expanded."
- * Section 3.6.3, Command Substitution:
- * "Within the backquoted style of command
- * substitution, backslash shall retain its
- * literal meaning, except when followed by
- * $ ` \.").
- */
- statep->ls_sbquote.indquotes = 0;
- s2 = statep;
- base = state_info.base;
- while (1) {
- for (; s2 != base; s2--) {
- if (s2->ls_state == SDQUOTE) {
- statep->ls_sbquote.indquotes = 1;
- break;
- }
- }
- if (s2 != base)
- break;
- if (!(s2 = s2->ls_info.base))
- break;
- base = s2-- - STATE_BSIZE;
- }
- break;
- case QCHAR:
- if (cf & LQCHAR) {
- *wp++ = QCHAR;
- *wp++ = getsc();
- break;
- }
- /* FALLTHROUGH */
- default:
- store_char:
- *wp++ = CHAR;
- *wp++ = c;
- }
- break;
-
- case SEQUOTE:
- if (c == '\'') {
- POP_STATE();
- *wp++ = CQUOTE;
- ignore_backslash_newline--;
- } else if (c == '\\') {
- if ((c2 = unbksl(true, s_get, s_put)) == -1)
- c2 = s_get();
- if (c2 == 0)
- statep->ls_sequote.got_NUL = true;
- if (!statep->ls_sequote.got_NUL) {
- char ts[4];
-
- if ((unsigned int)c2 < 0x100) {
- *wp++ = QCHAR;
- *wp++ = c2;
- } else {
- c = utf_wctomb(ts, c2 - 0x100);
- ts[c] = 0;
- for (c = 0; ts[c]; ++c) {
- *wp++ = QCHAR;
- *wp++ = ts[c];
- }
- }
- }
- } else if (!statep->ls_sequote.got_NUL) {
- *wp++ = QCHAR;
- *wp++ = c;
- }
- break;
-
- case SSQUOTE:
- if (c == '\'') {
- POP_STATE();
- *wp++ = CQUOTE;
- ignore_backslash_newline--;
- } else {
- *wp++ = QCHAR;
- *wp++ = c;
- }
- break;
-
- case SDQUOTE:
- if (c == '"') {
- POP_STATE();
- *wp++ = CQUOTE;
- } else
- goto Subst;
- break;
-
- case SCSPAREN: /* $( ... ) */
- /* todo: deal with $(...) quoting properly
- * kludge to partly fake quoting inside $(...): doesn't
- * really work because nested $(...) or ${...} inside
- * double quotes aren't dealt with.
- */
- switch (statep->ls_scsparen.csstate) {
- case 0: /* normal */
- switch (c) {
- case '(':
- statep->ls_scsparen.nparen++;
- break;
- case ')':
- statep->ls_scsparen.nparen--;
- break;
- case '\\':
- statep->ls_scsparen.csstate = 1;
- break;
- case '"':
- statep->ls_scsparen.csstate = 2;
- break;
- case '\'':
- statep->ls_scsparen.csstate = 4;
- ignore_backslash_newline++;
- break;
- }
- break;
-
- case 1: /* backslash in normal mode */
- case 3: /* backslash in double quotes */
- --statep->ls_scsparen.csstate;
- break;
-
- case 2: /* double quotes */
- if (c == '"')
- statep->ls_scsparen.csstate = 0;
- else if (c == '\\')
- statep->ls_scsparen.csstate = 3;
- break;
-
- case 4: /* single quotes */
- if (c == '\'') {
- statep->ls_scsparen.csstate = 0;
- ignore_backslash_newline--;
- }
- break;
- }
- if (statep->ls_scsparen.nparen == 0) {
- POP_STATE();
- *wp++ = 0; /* end of COMSUB */
- } else
- *wp++ = c;
- break;
-
- case SASPAREN: /* $(( ... )) */
- /* XXX should nest using existing state machine
- * (embed "...", $(...), etc.) */
- if (c == '(')
- statep->ls_sasparen.nparen++;
- else if (c == ')') {
- statep->ls_sasparen.nparen--;
- if (statep->ls_sasparen.nparen == 1) {
- /*(*/
- if ((c2 = getsc()) == ')') {
- POP_STATE();
- /* end of EXPRSUB */
- *wp++ = 0;
- break;
- } else {
- char *s;
-
- ungetsc(c2);
- /* mismatched parenthesis -
- * assume we were really
- * parsing a $(...) expression
- */
- s = Xrestpos(ws, wp,
- statep->ls_sasparen.start);
- memmove(s + 1, s, wp - s);
- *s++ = COMSUB;
- *s = '('; /*)*/
- wp++;
- statep->ls_scsparen.nparen = 1;
- statep->ls_scsparen.csstate = 0;
- state = statep->ls_state =
- SCSPAREN;
- }
- }
- }
- *wp++ = c;
- break;
-
- case SQBRACE:
- if (c == '\\') {
- /*
- * perform POSIX "quote removal" if the back-
- * slash is "special", i.e. same cases as the
- * {case '\\':} in Subst: plus closing brace;
- * in mksh code "quote removal" on '\c' means
- * write QCHAR+c, otherwise CHAR+\+CHAR+c are
- * emitted (in heredocquote:)
- */
- if ((c = getsc()) == '"' || c == '\\' ||
- c == '$' || c == '`' || c == /*{*/'}')
- goto store_qchar;
- goto heredocquote;
- }
- goto common_SQBRACE;
-
- case SBRACE:
- if (c == '\'')
- goto open_ssquote;
- else if (c == '\\')
- goto getsc_qchar;
- common_SQBRACE:
- if (c == '"')
- goto open_sdquote;
- else if (c == '$')
- goto subst_dollar;
- else if (c == '`')
- goto subst_gravis;
- else if (c != /*{*/ '}')
- goto store_char;
- POP_STATE();
- *wp++ = CSUBST;
- *wp++ = /*{*/ '}';
- break;
-
- case STBRACE:
- /* Same as SBASE, except (,|,) treated specially */
- if (c == /*{*/ '}') {
- POP_STATE();
- *wp++ = CSUBST;
- *wp++ = /*{*/ '}';
- } else if (c == '|') {
- *wp++ = SPAT;
- } else if (c == '(') {
- *wp++ = OPAT;
- *wp++ = ' '; /* simile for @ */
- PUSH_STATE(SPATTERN);
- } else
- goto Sbase1;
- break;
-
- case SBQUOTE:
- if (c == '`') {
- *wp++ = 0;
- POP_STATE();
- } else if (c == '\\') {
- switch (c = getsc()) {
- case '\\':
- case '$': case '`':
- *wp++ = c;
- break;
- case '"':
- if (statep->ls_sbquote.indquotes) {
- *wp++ = c;
- break;
- }
- /* FALLTHROUGH */
- default:
- if (c) {
- /* trailing \ is lost */
- *wp++ = '\\';
- *wp++ = c;
- }
- break;
- }
- } else
- *wp++ = c;
- break;
-
- case SWORD: /* ONEWORD */
- goto Subst;
-
- case SLETPAREN: /* LETEXPR: (( ... )) */
- /*(*/
- if (c == ')') {
- if (statep->ls_sletparen.nparen > 0)
- --statep->ls_sletparen.nparen;
- else if ((c2 = getsc()) == /*(*/ ')') {
- c = 0;
- *wp++ = CQUOTE;
- goto Done;
- } else {
- Source *s;
-
- ungetsc(c2);
- /* mismatched parenthesis -
- * assume we were really
- * parsing a $(...) expression
- */
- *wp = EOS;
- sp = Xstring(ws, wp);
- dp = wdstrip(sp, true, false);
- s = pushs(SREREAD, source->areap);
- s->start = s->str = s->u.freeme = dp;
- s->next = source;
- source = s;
- return ('('/*)*/);
- }
- } else if (c == '(')
- /* parenthesis inside quotes and backslashes
- * are lost, but AT&T ksh doesn't count them
- * either
- */
- ++statep->ls_sletparen.nparen;
- goto Sbase2;
-
-#ifndef MKSH_SMALL
- case SLETARRAY: /* LETARRAY: =( ... ) */
- if (c == '('/*)*/)
- ++statep->ls_sletarray.nparen;
- else if (c == /*(*/')')
- if (statep->ls_sletarray.nparen-- == 0) {
- c = 0;
- goto Done;
- }
- *wp++ = CHAR;
- *wp++ = c;
- break;
-#endif
-
- case SHERESTRING: /* <<< delimiter */
- if (c == '\\') {
- c = getsc();
- if (c) {
- /* trailing \ is lost */
- *wp++ = QCHAR;
- *wp++ = c;
- }
- /* invoke quoting mode */
- Xstring(ws, wp)[0] = QCHAR;
- } else if (c == '$') {
- if ((c2 = getsc()) == '\'') {
- PUSH_STATE(SEQUOTE);
- statep->ls_sequote.got_NUL = false;
- goto sherestring_quoted;
- }
- ungetsc(c2);
- goto sherestring_regular;
- } else if (c == '\'') {
- PUSH_STATE(SSQUOTE);
- sherestring_quoted:
- *wp++ = OQUOTE;
- ignore_backslash_newline++;
- /* invoke quoting mode */
- Xstring(ws, wp)[0] = QCHAR;
- } else if (c == '"') {
- state = statep->ls_state = SHEREDQUOTE;
- *wp++ = OQUOTE;
- /* just don't IFS split; no quoting mode */
- } else {
- sherestring_regular:
- *wp++ = CHAR;
- *wp++ = c;
- }
- break;
-
- case SHEREDELIM: /* <<,<<- delimiter */
- /* 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) {
- /* trailing \ is lost */
- *wp++ = QCHAR;
- *wp++ = c;
- }
- } else if (c == '$') {
- if ((c2 = getsc()) == '\'') {
- PUSH_STATE(SEQUOTE);
- statep->ls_sequote.got_NUL = false;
- goto sheredelim_quoted;
- }
- ungetsc(c2);
- goto sheredelim_regular;
- } else if (c == '\'') {
- PUSH_STATE(SSQUOTE);
- sheredelim_quoted:
- *wp++ = OQUOTE;
- ignore_backslash_newline++;
- } else if (c == '"') {
- state = statep->ls_state = SHEREDQUOTE;
- *wp++ = OQUOTE;
- } else {
- sheredelim_regular:
- *wp++ = CHAR;
- *wp++ = c;
- }
- break;
-
- case SHEREDQUOTE: /* " in <<,<<- delimiter */
- if (c == '"') {
- *wp++ = CQUOTE;
- state = statep->ls_state =
- /* dp[1] == '<' means here string */
- Xstring(ws, wp)[1] == '<' ?
- SHERESTRING : SHEREDELIM;
- } else {
- if (c == '\\') {
- switch (c = getsc()) {
- case '\\': case '"':
- case '$': case '`':
- break;
- default:
- if (c) {
- /* trailing \ lost */
- *wp++ = CHAR;
- *wp++ = '\\';
- }
- break;
- }
- }
- *wp++ = CHAR;
- *wp++ = c;
- }
- break;
-
- case SPATTERN: /* in *(...|...) pattern (*+?@!) */
- if ( /*(*/ c == ')') {
- *wp++ = CPAT;
- POP_STATE();
- } else if (c == '|') {
- *wp++ = SPAT;
- } else if (c == '(') {
- *wp++ = OPAT;
- *wp++ = ' '; /* simile for @ */
- PUSH_STATE(SPATTERN);
- } else
- goto Sbase1;
- break;
- }
- }
- Done:
- Xcheck(ws, wp);
- if (statep != &states[1])
- /* XXX figure out what is missing */
- yyerror("no closing quote\n");
-
-#ifndef MKSH_SMALL
- if (state == SLETARRAY && statep->ls_sletarray.nparen != -1)
- yyerror("%s: ')' missing\n", T_synerr);
-#endif
-
- /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
- if (state == SHEREDELIM || state == SHERESTRING)
- state = SBASE;
-
- dp = Xstring(ws, wp);
- if ((c == '<' || c == '>' || c == '&') && state == SBASE) {
- struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
-
- if (Xlength(ws, wp) == 0)
- iop->unit = c == '<' ? 0 : 1;
- else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) {
- if (dp[c2] != CHAR)
- goto no_iop;
- if (!ksh_isdigit(dp[c2 + 1]))
- goto no_iop;
- iop->unit = (iop->unit * 10) + dp[c2 + 1] - '0';
- }
-
- if (iop->unit >= FDBASE)
- goto no_iop;
-
- if (c == '&') {
- if ((c2 = getsc()) != '>') {
- ungetsc(c2);
- goto no_iop;
- }
- c = c2;
- iop->flag = IOBASH;
- } else
- iop->flag = 0;
-
- c2 = getsc();
- /* <<, >>, <> are ok, >< is not */
- if (c == c2 || (c == '<' && c2 == '>')) {
- iop->flag |= c == c2 ?
- (c == '>' ? IOCAT : IOHERE) : IORDWR;
- if (iop->flag == IOHERE) {
- if ((c2 = getsc()) == '-')
- iop->flag |= IOSKIP;
- else
- ungetsc(c2);
- }
- } else if (c2 == '&')
- iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
- else {
- iop->flag |= c == '>' ? IOWRITE : IOREAD;
- if (c == '>' && c2 == '|')
- iop->flag |= IOCLOB;
- else
- ungetsc(c2);
- }
-
- iop->name = NULL;
- iop->delim = NULL;
- iop->heredoc = NULL;
- Xfree(ws, wp); /* free word */
- yylval.iop = iop;
- return (REDIR);
- no_iop:
- ;
- }
-
- if (wp == dp && state == SBASE) {
- Xfree(ws, wp); /* free word */
- /* no word, process LEX1 character */
- if ((c == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) {
- if ((c2 = getsc()) == c)
- c = (c == ';') ? BREAK :
- (c == '|') ? LOGOR :
- (c == '&') ? LOGAND :
- /* c == '(' ) */ MDPAREN;
- else if (c == '|' && c2 == '&')
- c = COPROC;
- else
- ungetsc(c2);
- } else if (c == '\n') {
- gethere(false);
- if (cf & CONTIN)
- goto Again;
- } else if (c == '\0')
- /* need here strings at EOF */
- gethere(true);
- return (c);
- }
-
- *wp++ = EOS; /* terminate word */
- yylval.cp = Xclose(ws, wp);
- if (state == SWORD || state == SLETPAREN
- /* XXX ONEWORD? */
-#ifndef MKSH_SMALL
- || state == SLETARRAY
-#endif
- )
- return (LWORD);
-
- /* unget terminator */
- ungetsc(c);
-
- /*
- * note: the alias-vs-function code below depends on several
- * interna: starting from here, source->str is not modified;
- * the way getsc() and ungetsc() operate; etc.
- */
-
- /* copy word to unprefixed string ident */
- sp = yylval.cp;
- dp = ident;
- if ((cf & HEREDELIM) && (sp[1] == '<'))
- 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)
- *dp++ = *sp++;
- /* Make sure the ident array stays '\0' padded */
- memset(dp, 0, (ident+IDENT) - dp + 1);
- if (c != EOS)
- *ident = '\0'; /* word is not unquoted */
-
- if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
- struct tbl *p;
- uint32_t h = hash(ident);
-
- /* { */
- if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
- (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
- afree(yylval.cp, ATEMP);
- return (p->val.i);
- }
- if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
- (p->flag & ISSET)) {
- /*
- * this still points to the same character as the
- * ungetsc'd terminator from above
- */
- const char *cp = source->str;
-
- /* prefer POSIX but not Korn functions over aliases */
- while (*cp == ' ' || *cp == '\t')
- /*
- * this is like getsc() without skipping
- * over Source boundaries (including not
- * parsing ungetsc'd characters that got
- * pushed into an SREREAD) which is what
- * we want here anyway: find out whether
- * the alias name is followed by a POSIX
- * function definition (only the opening
- * parenthesis is checked though)
- */
- ++cp;
- /* prefer functions over aliases */
- if (*cp == '(' /*)*/)
- /*
- * delete alias upon encountering function
- * definition
- */
- ktdelete(p);
- else {
- Source *s = source;
-
- while (s && (s->flags & SF_HASALIAS))
- if (s->u.tblp == p)
- return (LWORD);
- else
- s = s->next;
- /* push alias expansion */
- s = pushs(SALIAS, source->areap);
- s->start = s->str = p->val.s;
- s->u.tblp = p;
- s->flags |= SF_HASALIAS;
- s->next = source;
- if (source->type == SEOF) {
- /* prevent infinite recursion at EOS */
- source->u.tblp = p;
- source->flags |= SF_HASALIAS;
- }
- source = s;
- afree(yylval.cp, ATEMP);
- goto Again;
- }
- }
- }
-
- return (LWORD);
-}
-
-static void
-gethere(bool iseof)
-{
- struct ioword **p;
-
- for (p = heres; p < herep; p++)
- if (iseof && (*p)->delim[1] != '<')
- /* only here strings at EOF */
- return;
- else
- readhere(*p);
- herep = heres;
-}
-
-/*
- * read "<<word" text into temp file
- */
-
-static void
-readhere(struct ioword *iop)
-{
- int c;
- char *volatile eof;
- char *eofp;
- int skiptabs;
- XString xs;
- char *xp;
- int xpos;
-
- if (iop->delim[1] == '<') {
- /* process the here string */
- xp = iop->heredoc = evalstr(iop->delim, DOBLANK);
- c = strlen(xp) - 1;
- memmove(xp, xp + 1, c);
- xp[c] = '\n';
- return;
- }
-
- eof = evalstr(iop->delim, 0);
-
- if (!(iop->flag & IOEVAL))
- ignore_backslash_newline++;
-
- Xinit(xs, xp, 256, ATEMP);
-
- for (;;) {
- eofp = eof;
- skiptabs = iop->flag & IOSKIP;
- xpos = Xsavepos(xs, xp);
- while ((c = getsc()) != 0) {
- if (skiptabs) {
- if (c == '\t')
- continue;
- skiptabs = 0;
- }
- if (c != *eofp)
- break;
- Xcheck(xs, xp);
- Xput(xs, xp, c);
- eofp++;
- }
- /* Allow EOF here so commands with out trailing newlines
- * will work (eg, ksh -c '...', $(...), etc).
- */
- if (*eofp == '\0' && (c == 0 || c == '\n')) {
- xp = Xrestpos(xs, xp, xpos);
- break;
- }
- ungetsc(c);
- while ((c = getsc()) != '\n') {
- if (c == 0)
- yyerror("here document '%s' unclosed\n", eof);
- Xcheck(xs, xp);
- Xput(xs, xp, c);
- }
- Xcheck(xs, xp);
- Xput(xs, xp, c);
- }
- Xput(xs, xp, '\0');
- iop->heredoc = Xclose(xs, xp);
-
- if (!(iop->flag & IOEVAL))
- ignore_backslash_newline--;
-}
-
-void
-yyerror(const char *fmt, ...)
-{
- va_list va;
-
- /* pop aliases and re-reads */
- while (source->type == SALIAS || source->type == SREREAD)
- source = source->next;
- source->str = null; /* zap pending input */
-
- error_prefix(true);
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- errorfz();
-}
-
-/*
- * input for yylex with alias expansion
- */
-
-Source *
-pushs(int type, Area *areap)
-{
- Source *s;
-
- s = alloc(sizeof(Source), areap);
- memset(s, 0, sizeof(Source));
- s->type = type;
- s->str = null;
- s->areap = areap;
- if (type == SFILE || type == SSTDIN)
- XinitN(s->xs, 256, s->areap);
- return (s);
-}
-
-static int
-getsc__(void)
-{
- Source *s = source;
- int c;
-
- getsc_again:
- while ((c = *s->str++) == 0) {
- s->str = NULL; /* return 0 for EOF by default */
- switch (s->type) {
- case SEOF:
- s->str = null;
- return (0);
-
- case SSTDIN:
- case SFILE:
- getsc_line(s);
- break;
-
- case SWSTR:
- break;
-
- case SSTRING:
- break;
-
- case SWORDS:
- s->start = s->str = *s->u.strv++;
- s->type = SWORDSEP;
- break;
-
- case SWORDSEP:
- if (*s->u.strv == NULL) {
- s->start = s->str = "\n";
- s->type = SEOF;
- } else {
- s->start = s->str = " ";
- s->type = SWORDS;
- }
- break;
-
- case SALIAS:
- if (s->flags & SF_ALIASEND) {
- /* pass on an unused SF_ALIAS flag */
- source = s->next;
- source->flags |= s->flags & SF_ALIAS;
- s = source;
- } else if (*s->u.tblp->val.s &&
- (c = strnul(s->u.tblp->val.s)[-1], ksh_isspace(c))) {
- source = s = s->next; /* pop source stack */
- /* Note that this alias ended with a space,
- * enabling alias expansion on the following
- * word.
- */
- s->flags |= SF_ALIAS;
- } else {
- /* At this point, we need to keep the current
- * alias in the source list so recursive
- * aliases can be detected and we also need
- * to return the next character. Do this
- * by temporarily popping the alias to get
- * the next character and then put it back
- * in the source list with the SF_ALIASEND
- * flag set.
- */
- source = s->next; /* pop source stack */
- source->flags |= s->flags & SF_ALIAS;
- c = getsc__();
- if (c) {
- s->flags |= SF_ALIASEND;
- s->ugbuf[0] = c; s->ugbuf[1] = '\0';
- s->start = s->str = s->ugbuf;
- s->next = source;
- source = s;
- } else {
- s = source;
- /* avoid reading eof twice */
- s->str = NULL;
- break;
- }
- }
- continue;
-
- case SREREAD:
- if (s->start != s->ugbuf) /* yuck */
- afree(s->u.freeme, ATEMP);
- source = s = s->next;
- continue;
- }
- if (s->str == NULL) {
- s->type = SEOF;
- s->start = s->str = null;
- return ('\0');
- }
- if (s->flags & SF_ECHO) {
- shf_puts(s->str, shl_out);
- shf_flush(shl_out);
- }
- }
- /* check for UTF-8 byte order mark */
- if (s->flags & SF_FIRST) {
- s->flags &= ~SF_FIRST;
- if (((unsigned char)c == 0xEF) &&
- (((const unsigned char *)(s->str))[0] == 0xBB) &&
- (((const unsigned char *)(s->str))[1] == 0xBF)) {
- s->str += 2;
- UTFMODE = 1;
- goto getsc_again;
- }
- }
- return (c);
-}
-
-static void
-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);
-
- /* Done here to ensure nothing odd happens when a timeout occurs */
- XcheckN(s->xs, xp, LINE);
- *xp = '\0';
- s->start = s->str = xp;
-
- if (have_tty && ksh_tmout) {
- ksh_tmout_state = TMOUT_READING;
- alarm(ksh_tmout);
- }
- if (interactive)
- change_winsz();
- if (have_tty && (
-#if !MKSH_S_NOVI
- Flag(FVI) ||
-#endif
- Flag(FEMACS) || Flag(FGMACS))) {
- int nread;
-
- nread = x_read(xp, LINE);
- if (nread < 0) /* read error */
- nread = 0;
- xp[nread] = '\0';
- xp += nread;
- } else {
- if (interactive)
- pprompt(prompt, 0);
- else
- s->line++;
-
- while (1) {
- char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
-
- if (!p && shf_error(s->u.shf) &&
- shf_errno(s->u.shf) == EINTR) {
- shf_clearerr(s->u.shf);
- if (trap)
- runtraps(0);
- continue;
- }
- if (!p || (xp = p, xp[-1] == '\n'))
- break;
- /* double buffer size */
- xp++; /* move past NUL so doubling works... */
- XcheckN(s->xs, xp, Xlength(s->xs, xp));
- xp--; /* ...and move back again */
- }
- /* flush any unwanted input so other programs/builtins
- * can read it. Not very optimal, but less error prone
- * than flushing else where, dealing with redirections,
- * etc.
- * todo: reduce size of shf buffer (~128?) if SSTDIN
- */
- if (s->type == SSTDIN)
- shf_flush(s->u.shf);
- }
- /* XXX: temporary kludge to restore source after a
- * trap may have been executed.
- */
- source = s;
- if (have_tty && ksh_tmout) {
- ksh_tmout_state = TMOUT_EXECUTING;
- 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, fc_e_n + /* 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 + fc_e_n, cp, linelen + /* NUL */ 1);
- xp += fc_e_n;
- /* prepend it with "fc -e -" */
- memcpy(cp, fc_e_, fc_e_n);
- }
-#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 */
- if (Xlength(s->xs, xp) == 0) {
- /* EOF */
- if (s->type == SFILE)
- shf_fdclose(s->u.shf);
- s->str = NULL;
- } else if (interactive && *s->str &&
- (cur_prompt != PS1 || !ctype(*s->str, C_IFS | C_IFSWS))) {
- histsave(&s->line, s->str, true, true);
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
- } else if (interactive && cur_prompt == PS1) {
- cp = Xstring(s->xs, xp);
- while (*cp && ctype(*cp, C_IFSWS))
- ++cp;
- if (!*cp)
- histsync();
-#endif
- }
- if (interactive)
- set_prompt(PS2, NULL);
-}
-
-void
-set_prompt(int to, Source *s)
-{
- cur_prompt = to;
-
- switch (to) {
- case PS1: /* command */
- /* Substitute ! and !! here, before substitutions are done
- * so ! in expanded variables are not expanded.
- * NOTE: this is not what AT&T ksh does (it does it after
- * substitutions, POSIX doesn't say which is to be done.
- */
- {
- struct shf *shf;
- char * volatile ps1;
- Area *saved_atemp;
-
- ps1 = str_val(global("PS1"));
- shf = shf_sopen(NULL, strlen(ps1) * 2,
- SHF_WR | SHF_DYNAMIC, NULL);
- while (*ps1)
- if (*ps1 != '!' || *++ps1 == '!')
- shf_putchar(*ps1++, shf);
- else
- shf_fprintf(shf, "%d",
- s ? s->line + 1 : 0);
- ps1 = shf_sclose(shf);
- saved_atemp = ATEMP;
- newenv(E_ERRH);
- if (sigsetjmp(e->jbuf, 0)) {
- prompt = safe_prompt;
- /* Don't print an error - assume it has already
- * been printed. Reason is we may have forked
- * to run a command and the child may be
- * unwinding its stack through this code as it
- * exits.
- */
- } else {
- char *cp = substitute(ps1, 0);
- strdupx(prompt, cp, saved_atemp);
- }
- quitenv(NULL);
- }
- break;
- case PS2: /* command continuation */
- prompt = str_val(global("PS2"));
- break;
- }
-}
-
-static int
-dopprompt(const char *cp, int ntruncate, bool doprint)
-{
- int columns = 0, lines = 0, indelimit = 0;
- char delimiter = 0;
-
- /* Undocumented AT&T ksh feature:
- * If the second char in the prompt string is \r then the first char
- * is taken to be a non-printing delimiter and any chars between two
- * instances of the delimiter are not considered to be part of the
- * prompt length
- */
- if (*cp && cp[1] == '\r') {
- delimiter = *cp;
- cp += 2;
- }
- for (; *cp; cp++) {
- if (indelimit && *cp != delimiter)
- ;
- else if (*cp == '\n' || *cp == '\r') {
- lines += columns / x_cols + ((*cp == '\n') ? 1 : 0);
- columns = 0;
- } else if (*cp == '\t') {
- columns = (columns | 7) + 1;
- } else if (*cp == '\b') {
- if (columns > 0)
- columns--;
- } else if (*cp == delimiter)
- indelimit = !indelimit;
- else if (UTFMODE && ((unsigned char)*cp > 0x7F)) {
- const char *cp2;
- columns += utf_widthadj(cp, &cp2);
- if (doprint && (indelimit ||
- (ntruncate < (x_cols * lines + columns))))
- shf_write(cp, cp2 - cp, shl_out);
- cp = cp2 - /* loop increment */ 1;
- continue;
- } else
- columns++;
- if (doprint && (*cp != delimiter) &&
- (indelimit || (ntruncate < (x_cols * lines + columns))))
- shf_putc(*cp, shl_out);
- }
- if (doprint)
- shf_flush(shl_out);
- return (x_cols * lines + columns);
-}
-
-
-void
-pprompt(const char *cp, int ntruncate)
-{
- dopprompt(cp, ntruncate, true);
-}
-
-int
-promptlen(const char *cp)
-{
- return (dopprompt(cp, 0, false));
-}
-
-/* Read the variable part of a ${...} expression (ie, up to but not including
- * the :[-+?=#%] or close-brace.
- */
-static char *
-get_brace_var(XString *wsp, char *wp)
-{
- enum parse_state {
- PS_INITIAL, PS_SAW_HASH, PS_IDENT,
- PS_NUMBER, PS_VAR1
- } state;
- char c;
-
- state = PS_INITIAL;
- while (1) {
- c = getsc();
- /* State machine to figure out where the variable part ends. */
- switch (state) {
- case PS_INITIAL:
- if (c == '#' || c == '!' || c == '%') {
- state = PS_SAW_HASH;
- break;
- }
- /* FALLTHROUGH */
- case PS_SAW_HASH:
- if (ksh_isalphx(c))
- state = PS_IDENT;
- else if (ksh_isdigit(c))
- state = PS_NUMBER;
- else if (ctype(c, C_VAR1))
- state = PS_VAR1;
- else
- goto out;
- break;
- case PS_IDENT:
- if (!ksh_isalnux(c)) {
- if (c == '[') {
- char *tmp, *p;
-
- if (!arraysub(&tmp))
- yyerror("missing ]\n");
- *wp++ = c;
- for (p = tmp; *p; ) {
- Xcheck(*wsp, wp);
- *wp++ = *p++;
- }
- afree(tmp, ATEMP);
- c = getsc(); /* the ] */
- }
- goto out;
- }
- break;
- case PS_NUMBER:
- if (!ksh_isdigit(c))
- goto out;
- break;
- case PS_VAR1:
- goto out;
- }
- Xcheck(*wsp, wp);
- *wp++ = c;
- }
- out:
- *wp++ = '\0'; /* end of variable part */
- ungetsc(c);
- return (wp);
-}
-
-/*
- * Save an array subscript - returns true if matching bracket found, false
- * if eof or newline was found.
- * (Returned string double null terminated)
- */
-static int
-arraysub(char **strp)
-{
- XString ws;
- char *wp;
- char c;
- int depth = 1; /* we are just past the initial [ */
-
- Xinit(ws, wp, 32, ATEMP);
-
- do {
- c = getsc();
- Xcheck(ws, wp);
- *wp++ = c;
- if (c == '[')
- depth++;
- else if (c == ']')
- depth--;
- } while (depth > 0 && c && c != '\n');
-
- *wp++ = '\0';
- *strp = Xclose(ws, wp);
-
- return (depth == 0 ? 1 : 0);
-}
-
-/* Unget a char: handles case when we are already at the start of the buffer */
-static const char *
-ungetsc(int c)
-{
- if (backslash_skip)
- backslash_skip--;
- /* Don't unget eof... */
- if (source->str == null && c == '\0')
- return (source->str);
- if (source->str > source->start)
- source->str--;
- else {
- Source *s;
-
- s = pushs(SREREAD, source->areap);
- s->ugbuf[0] = c; s->ugbuf[1] = '\0';
- s->start = s->str = s->ugbuf;
- s->next = source;
- source = s;
- }
- return (source->str);
-}
-
-
-/* Called to get a char that isn't a \newline sequence. */
-static int
-getsc_bn(void)
-{
- int c, c2;
-
- if (ignore_backslash_newline)
- return (getsc_());
-
- if (backslash_skip == 1) {
- backslash_skip = 2;
- return (getsc_());
- }
-
- backslash_skip = 0;
-
- while (1) {
- c = getsc_();
- if (c == '\\') {
- if ((c2 = getsc_()) == '\n')
- /* ignore the \newline; get the next char... */
- continue;
- ungetsc(c2);
- backslash_skip = 1;
- }
- return (c);
- }
-}
-
-static Lex_state *
-push_state_(State_info *si, Lex_state *old_end)
-{
- Lex_state *news = alloc(STATE_BSIZE * sizeof(Lex_state), ATEMP);
-
- news[0].ls_info.base = old_end;
- si->base = &news[0];
- si->end = &news[STATE_BSIZE];
- return (&news[1]);
-}
-
-static Lex_state *
-pop_state_(State_info *si, Lex_state *old_end)
-{
- Lex_state *old_base = si->base;
-
- si->base = old_end->ls_info.base - STATE_BSIZE;
- si->end = old_end->ls_info.base;
-
- afree(old_base, ATEMP);
-
- return (si->base + STATE_BSIZE - 1);
-}
-
-static int
-s_get(void)
-{
- return (getsc());
-}
-
-static void
-s_put(int c)
-{
- ungetsc(c);
-}
diff --git a/mksh/src/main.c b/mksh/src/main.c
deleted file mode 100644
index f962dd4..0000000
--- a/mksh/src/main.c
+++ /dev/null
@@ -1,1479 +0,0 @@
-/* $OpenBSD: main.c,v 1.46 2010/05/19 17:36:08 jasper Exp $ */
-/* $OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $ */
-/* $OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $ */
-/* $OpenBSD: table.c,v 1.13 2009/01/17 22:06:44 millert Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#define EXTERN
-#include "sh.h"
-
-#if HAVE_LANGINFO_CODESET
-#include <langinfo.h>
-#endif
-#if HAVE_SETLOCALE_CTYPE
-#include <locale.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.167 2010/07/04 17:45:15 tg Exp $");
-
-extern char **environ;
-
-#if !HAVE_SETRESUGID
-extern uid_t kshuid;
-extern gid_t kshgid, kshegid;
-#endif
-
-#ifndef MKSHRC_PATH
-#define MKSHRC_PATH "~/.mkshrc"
-#endif
-
-#ifndef MKSH_DEFAULT_TMPDIR
-#define MKSH_DEFAULT_TMPDIR "/tmp"
-#endif
-
-static void reclaim(void);
-static void remove_temps(struct temp *);
-void chvt_reinit(void);
-Source *mksh_init(int, const char *[]);
-#ifdef SIGWINCH
-static void x_sigwinch(int);
-#endif
-
-static const char initifs[] = "IFS= \t\n";
-
-static const char initsubs[] =
- "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0}";
-
-static const char *initcoms[] = {
- T_typeset, "-r", initvsn, NULL,
- T_typeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
- T_typeset, "-i10", "COLUMNS", "LINES", "OPTIND", "PGRP", "PPID",
- "RANDOM", "SECONDS", "TMOUT", "USER_ID", NULL,
- "alias",
- "integer=typeset -i",
- T_local_typeset,
- "hash=alias -t", /* not "alias -t --": hash -r needs to work */
- "type=whence -v",
-#ifndef MKSH_UNEMPLOYED
- "suspend=kill -STOP $$",
-#endif
- "autoload=typeset -fu",
- "functions=typeset -f",
- "history=fc -l",
- "nameref=typeset -n",
- "nohup=nohup ",
- r_fc_e_,
- "source=PATH=$PATH:. command .",
- "login=exec login",
- NULL,
- /* this is what AT&T ksh seems to track, with the addition of emacs */
- "alias", "-tU",
- "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
- "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
- NULL
-};
-
-static int initio_done;
-
-struct env *e = &kshstate_v.env_;
-
-void
-chvt_reinit(void)
-{
- kshpid = procpid = getpid();
- ksheuid = geteuid();
- kshpgrp = getpgrp();
- kshppid = getppid();
-}
-
-Source *
-mksh_init(int argc, const char *argv[])
-{
- int argi, i;
- Source *s;
- struct block *l;
- unsigned char restricted, errexit, utf_flag;
- const char **wp;
- struct tbl *vp;
- struct stat s_stdin;
-#if !defined(_PATH_DEFPATH) && defined(_CS_PATH)
- size_t k;
- char *cp;
-#endif
-
- /* do things like getpgrp() et al. */
- chvt_reinit();
-
- /* make sure argv[] is sane */
- if (!*argv) {
- static const char *empty_argv[] = {
- "mksh", NULL
- };
-
- argv = empty_argv;
- argc = 1;
- }
- kshname = *argv;
-
- ainit(&aperm); /* initialise permanent Area */
-
- /* set up base environment */
- kshstate_v.env_.type = E_NONE;
- ainit(&kshstate_v.env_.area);
- newblock(); /* set up global l->vars and l->funs */
-
- /* Do this first so output routines (eg, errorf, shellf) can work */
- initio();
-
- argi = parse_args(argv, OF_FIRSTTIME, NULL);
- if (argi < 0)
- return (NULL);
-
- initvar();
-
- initctypes();
-
- inittraps();
-
- coproc_init();
-
- /* set up variable and command dictionaries */
- ktinit(&taliases, APERM, 0);
- ktinit(&aliases, APERM, 0);
-#ifndef MKSH_NOPWNAM
- ktinit(&homedirs, APERM, 0);
-#endif
-
- /* define shell keywords */
- initkeywords();
-
- /* define built-in commands */
- ktinit(&builtins, APERM,
- /* must be 80% of 2^n (currently 44 builtins) */ 64);
- for (i = 0; mkshbuiltins[i].name != NULL; i++)
- builtin(mkshbuiltins[i].name, mkshbuiltins[i].func);
-
- init_histvec();
-
-#ifdef _PATH_DEFPATH
- def_path = _PATH_DEFPATH;
-#else
-#ifdef _CS_PATH
- if ((k = confstr(_CS_PATH, NULL, 0)) != (size_t)-1 && k > 0 &&
- confstr(_CS_PATH, cp = alloc(k + 1, APERM), k + 1) == k + 1)
- def_path = cp;
- else
-#endif
- /*
- * this is uniform across all OSes unless it
- * breaks somewhere; don't try to optimise,
- * e.g. add stuff for Interix or remove /usr
- * for HURD, because e.g. Debian GNU/HURD is
- * "keeping a regular /usr"; this is supposed
- * to be a sane 'basic' default PATH
- */
- def_path = "/bin:/usr/bin:/sbin:/usr/sbin";
-#endif
-
- /* Set PATH to def_path (will set the path global variable).
- * (import of environment below will probably change this setting).
- */
- vp = global("PATH");
- /* setstr can't fail here */
- setstr(vp, def_path, KSH_RETURN_ERROR);
-
- /* Turn on nohup by default for now - will change to off
- * by default once people are aware of its existence
- * (AT&T ksh does not have a nohup option - it always sends
- * the hup).
- */
- Flag(FNOHUP) = 1;
-
- /* Turn on brace expansion by default. AT&T kshs that have
- * alternation always have it on.
- */
- Flag(FBRACEEXPAND) = 1;
-
- /* Set edit mode to emacs by default, may be overridden
- * by the environment or the user. Also, we want tab completion
- * on in vi by default. */
- change_flag(FEMACS, OF_SPECIAL, 1);
-#if !MKSH_S_NOVI
- Flag(FVITABCOMPLETE) = 1;
-#endif
-
-#ifdef MKSH_BINSHREDUCED
- /* set FSH if we're called as -sh or /bin/sh or so */
- {
- const char *cc;
-
- cc = kshname;
- i = 0; argi = 0;
- while (cc[i] != '\0')
- /* the following line matches '-' and '/' ;-) */
- if ((cc[i++] | 2) == '/')
- argi = i;
- if (((cc[argi] | 0x20) == 's') && ((cc[argi + 1] | 0x20) == 'h'))
- change_flag(FSH, OF_FIRSTTIME, 1);
- }
-#endif
-
- /* import environment */
- if (environ != NULL)
- for (wp = (const char **)environ; *wp != NULL; wp++)
- typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
-
- typeset(initifs, 0, 0, 0, 0); /* for security */
-
- /* assign default shell variable values */
- substitute(initsubs, 0);
-
- /* Figure out the current working directory and set $PWD */
- {
- struct stat s_pwd, s_dot;
- struct tbl *pwd_v = global("PWD");
- char *pwd = str_val(pwd_v);
- char *pwdx = pwd;
-
- /* Try to use existing $PWD if it is valid */
- if (pwd[0] != '/' ||
- stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0 ||
- s_pwd.st_dev != s_dot.st_dev ||
- s_pwd.st_ino != s_dot.st_ino)
- pwdx = NULL;
- set_current_wd(pwdx);
- if (current_wd[0])
- simplify_path(current_wd);
- /* Only set pwd if we know where we are or if it had a
- * bogus value
- */
- if (current_wd[0] || pwd != null)
- /* setstr can't fail here */
- setstr(pwd_v, current_wd, KSH_RETURN_ERROR);
- }
-
- for (wp = initcoms; *wp != NULL; wp++) {
- shcomexec(wp);
- while (*wp != NULL)
- wp++;
- }
- setint(global("COLUMNS"), 0);
- setint(global("LINES"), 0);
- setint(global("OPTIND"), 1);
-
- safe_prompt = ksheuid ? "$ " : "# ";
- vp = global("PS1");
- /* Set PS1 if unset or we are root and prompt doesn't contain a # */
- if (!(vp->flag & ISSET) ||
- (!ksheuid && !strchr(str_val(vp), '#')))
- /* setstr can't fail here */
- setstr(vp, safe_prompt, KSH_RETURN_ERROR);
- setint((vp = global("PGRP")), (mksh_uari_t)kshpgrp);
- vp->flag |= INT_U;
- setint((vp = global("PPID")), (mksh_uari_t)kshppid);
- vp->flag |= INT_U;
- setint((vp = global("RANDOM")), (mksh_uari_t)evilhash(kshname));
- vp->flag |= INT_U;
- setint((vp = global("USER_ID")), (mksh_uari_t)ksheuid);
- vp->flag |= INT_U;
-
- /* Set this before parsing arguments */
-#if HAVE_SETRESUGID
- Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid();
-#else
- Flag(FPRIVILEGED) = (kshuid = getuid()) != ksheuid ||
- (kshgid = getgid()) != (kshegid = getegid());
-#endif
-
- /* this to note if monitor is set on command line (see below) */
-#ifndef MKSH_UNEMPLOYED
- Flag(FMONITOR) = 127;
-#endif
- /* this to note if utf-8 mode is set on command line (see below) */
- UTFMODE = 2;
-
- argi = parse_args(argv, OF_CMDLINE, NULL);
- if (argi < 0)
- return (NULL);
-
- /* process this later only, default to off (hysterical raisins) */
- utf_flag = UTFMODE;
- UTFMODE = 0;
-
- if (Flag(FCOMMAND)) {
- s = pushs(SSTRING, ATEMP);
- if (!(s->start = s->str = argv[argi++]))
- errorf("-c requires an argument");
-#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
- /* compatibility to MidnightBSD 0.1 /bin/sh (kludge) */
- if (Flag(FSH) && argv[argi] && !strcmp(argv[argi], "--"))
- ++argi;
-#endif
- if (argv[argi])
- kshname = argv[argi++];
- } else if (argi < argc && !Flag(FSTDIN)) {
- s = pushs(SFILE, ATEMP);
- s->file = argv[argi++];
- s->u.shf = shf_open(s->file, O_RDONLY, 0,
- SHF_MAPHI | SHF_CLEXEC);
- if (s->u.shf == NULL) {
- shl_stdout_ok = 0;
- warningf(true, "%s: %s", s->file, strerror(errno));
- /* mandated by SUSv4 */
- exstat = 127;
- unwind(LERROR);
- }
- kshname = s->file;
- } else {
- Flag(FSTDIN) = 1;
- s = pushs(SSTDIN, ATEMP);
- s->file = "<stdin>";
- s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
- NULL);
- if (isatty(0) && isatty(2)) {
- Flag(FTALKING) = Flag(FTALKING_I) = 1;
- /* The following only if isatty(0) */
- s->flags |= SF_TTY;
- s->u.shf->flags |= SHF_INTERRUPT;
- s->file = NULL;
- }
- }
-
- /* this bizarreness is mandated by POSIX */
- if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
- Flag(FTALKING))
- reset_nonblock(0);
-
- /* initialise job control */
- j_init();
- /* set: 0/1; unset: 2->0 */
- UTFMODE = utf_flag & 1;
- /* Do this after j_init(), as tty_fd is not initialised until then */
- if (Flag(FTALKING)) {
- if (utf_flag == 2) {
-#ifndef MKSH_ASSUME_UTF8
-#define isuc(x) (((x) != NULL) && \
- (stristr((x), "UTF-8") || stristr((x), "utf8")))
- /* Check if we're in a UTF-8 locale */
- const char *ccp;
-
-#if HAVE_SETLOCALE_CTYPE
- ccp = setlocale(LC_CTYPE, "");
-#if HAVE_LANGINFO_CODESET
- if (!isuc(ccp))
- ccp = nl_langinfo(CODESET);
-#endif
-#else
- /* these were imported from environ earlier */
- ccp = str_val(global("LC_ALL"));
- if (ccp == null)
- ccp = str_val(global("LC_CTYPE"));
- if (ccp == null)
- ccp = str_val(global("LANG"));
-#endif
- UTFMODE = isuc(ccp);
-#undef isuc
-#elif MKSH_ASSUME_UTF8
- UTFMODE = 1;
-#else
- UTFMODE = 0;
-#endif
- }
- x_init();
- }
-
-#ifdef SIGWINCH
- sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
- setsig(&sigtraps[SIGWINCH], x_sigwinch,
- SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
-#endif
-
- l = e->loc;
- l->argv = &argv[argi - 1];
- l->argc = argc - argi;
- l->argv[0] = kshname;
- getopts_reset(1);
-
- /* Disable during .profile/ENV reading */
- restricted = Flag(FRESTRICTED);
- Flag(FRESTRICTED) = 0;
- errexit = Flag(FERREXIT);
- Flag(FERREXIT) = 0;
-
- /* Do this before profile/$ENV so that if it causes problems in them,
- * user will know why things broke.
- */
- if (!current_wd[0] && Flag(FTALKING))
- warningf(false, "Cannot determine current working directory");
-
- if (Flag(FLOGIN)) {
- include(KSH_SYSTEM_PROFILE, 0, NULL, 1);
- if (!Flag(FPRIVILEGED))
- include(substitute("$HOME/.profile", 0), 0,
- NULL, 1);
- }
- if (Flag(FPRIVILEGED))
- include("/etc/suid_profile", 0, NULL, 1);
- else if (Flag(FTALKING)) {
- char *env_file;
-
- /* include $ENV */
- env_file = substitute(substitute("${ENV:-" MKSHRC_PATH "}", 0),
- DOTILDE);
- if (*env_file != '\0')
- include(env_file, 0, NULL, 1);
- }
-
- if (restricted) {
- static const char *restr_com[] = {
- T_typeset, "-r", "PATH",
- "ENV", "SHELL",
- NULL
- };
- shcomexec(restr_com);
- /* After typeset command... */
- Flag(FRESTRICTED) = 1;
- }
- Flag(FERREXIT) = errexit;
-
- if (Flag(FTALKING)) {
- hist_init(s);
- alarm_init();
- } else
- Flag(FTRACKALL) = 1; /* set after ENV */
-
- return (s);
-}
-
-int
-main(int argc, const char *argv[])
-{
- Source *s;
-
- kshstate_v.lcg_state_ = 5381;
-
- if ((s = mksh_init(argc, argv))) {
- /* put more entropy into the LCG */
- change_random(s, sizeof(*s));
- /* doesn’t return */
- shell(s, true);
- }
- return (1);
-}
-
-int
-include(const char *name, int argc, const char **argv, int intr_ok)
-{
- Source *volatile s = NULL;
- struct shf *shf;
- const char **volatile old_argv;
- volatile int old_argc;
- int i;
-
- shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
- if (shf == NULL)
- return (-1);
-
- if (argv) {
- old_argv = e->loc->argv;
- old_argc = e->loc->argc;
- } else {
- old_argv = NULL;
- old_argc = 0;
- }
- newenv(E_INCL);
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
- quitenv(s ? s->u.shf : NULL);
- if (old_argv) {
- e->loc->argv = old_argv;
- e->loc->argc = old_argc;
- }
- switch (i) {
- case LRETURN:
- case LERROR:
- return (exstat & 0xff); /* see below */
- case LINTR:
- /* intr_ok is set if we are including .profile or $ENV.
- * If user ^Cs out, we don't want to kill the shell...
- */
- if (intr_ok && (exstat - 128) != SIGTERM)
- return (1);
- /* FALLTHROUGH */
- case LEXIT:
- case LLEAVE:
- case LSHELL:
- unwind(i);
- /* NOTREACHED */
- default:
- internal_errorf("include: %d", i);
- /* NOTREACHED */
- }
- }
- if (argv) {
- e->loc->argv = argv;
- e->loc->argc = argc;
- }
- s = pushs(SFILE, ATEMP);
- s->u.shf = shf;
- strdupx(s->file, name, ATEMP);
- i = shell(s, false);
- quitenv(s->u.shf);
- if (old_argv) {
- e->loc->argv = old_argv;
- e->loc->argc = old_argc;
- }
- return (i & 0xff); /* & 0xff to ensure value not -1 */
-}
-
-/* spawn a command into a shell optionally keeping track of the line number */
-int
-command(const char *comm, int line)
-{
- Source *s;
-
- s = pushs(SSTRING, ATEMP);
- s->start = s->str = comm;
- s->line = line;
- return (shell(s, false));
-}
-
-/*
- * run the commands from the input source, returning status.
- */
-int
-shell(Source * volatile s, volatile int toplevel)
-{
- struct op *t;
- volatile int wastty = s->flags & SF_TTY;
- volatile int attempts = 13;
- volatile int interactive = Flag(FTALKING) && toplevel;
- Source *volatile old_source = source;
- int i;
-
- s->flags |= SF_FIRST; /* enable UTF-8 BOM check */
-
- newenv(E_PARSE);
- if (interactive)
- really_exit = 0;
- i = sigsetjmp(e->jbuf, 0);
- if (i) {
- switch (i) {
- case LINTR: /* we get here if SIGINT not caught or ignored */
- case LERROR:
- case LSHELL:
- if (interactive) {
- if (i == LINTR)
- shellf("\n");
- /* Reset any eof that was read as part of a
- * multiline command.
- */
- if (Flag(FIGNOREEOF) && s->type == SEOF &&
- wastty)
- s->type = SSTDIN;
- /* Used by exit command to get back to
- * top level shell. Kind of strange since
- * interactive is set if we are reading from
- * a tty, but to have stopped jobs, one only
- * needs FMONITOR set (not FTALKING/SF_TTY)...
- */
- /* toss any input we have so far */
- s->start = s->str = null;
- break;
- }
- /* FALLTHROUGH */
- case LEXIT:
- case LLEAVE:
- case LRETURN:
- source = old_source;
- quitenv(NULL);
- unwind(i); /* keep on going */
- /* NOTREACHED */
- default:
- source = old_source;
- quitenv(NULL);
- internal_errorf("shell: %d", i);
- /* NOTREACHED */
- }
- }
- while (1) {
- if (trap)
- runtraps(0);
-
- if (s->next == NULL) {
- if (Flag(FVERBOSE))
- s->flags |= SF_ECHO;
- else
- s->flags &= ~SF_ECHO;
- }
- if (interactive) {
- j_notify();
- set_prompt(PS1, s);
- }
- t = compile(s);
- if (t != NULL && t->type == TEOF) {
- if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
- shellf("Use 'exit' to leave ksh\n");
- s->type = SSTDIN;
- } else if (wastty && !really_exit &&
- j_stopped_running()) {
- really_exit = 1;
- s->type = SSTDIN;
- } else {
- /* this for POSIX which says EXIT traps
- * shall be taken in the environment
- * immediately after the last command
- * executed.
- */
- if (toplevel)
- unwind(LEXIT);
- break;
- }
- }
- if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
- exstat = execute(t, 0, NULL);
-
- if (t != NULL && t->type != TEOF && interactive && really_exit)
- really_exit = 0;
-
- reclaim();
- }
- quitenv(NULL);
- source = old_source;
- return (exstat);
-}
-
-/* return to closest error handler or shell(), exit if none found */
-void
-unwind(int i)
-{
- /* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
- if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) &&
- sigtraps[SIGEXIT_].trap)) {
- runtrap(&sigtraps[SIGEXIT_]);
- i = LLEAVE;
- } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
- runtrap(&sigtraps[SIGERR_]);
- i = LLEAVE;
- }
- while (1) {
- switch (e->type) {
- case E_PARSE:
- case E_FUNC:
- case E_INCL:
- case E_LOOP:
- case E_ERRH:
- siglongjmp(e->jbuf, i);
- /* NOTREACHED */
- case E_NONE:
- if (i == LINTR)
- e->flags |= EF_FAKE_SIGDIE;
- /* FALLTHROUGH */
- default:
- quitenv(NULL);
- }
- }
-}
-
-void
-newenv(int type)
-{
- struct env *ep;
- char *cp;
-
- /*
- * struct env includes ALLOC_ITEM for alignment constraints
- * so first get the actually used memory, then assign it
- */
- cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
- ep = (void *)(cp - ALLOC_SIZE); /* undo what alloc() did */
- /* initialise public members of struct env (not the ALLOC_ITEM) */
- ainit(&ep->area);
- ep->oenv = e;
- ep->loc = e->loc;
- ep->savefd = NULL;
- ep->temps = NULL;
- ep->type = type;
- ep->flags = 0;
- /* jump buffer is invalid because flags == 0 */
- e = ep;
-}
-
-void
-quitenv(struct shf *shf)
-{
- struct env *ep = e;
- char *cp;
- int fd;
-
- if (ep->oenv && ep->oenv->loc != ep->loc)
- popblock();
- if (ep->savefd != NULL) {
- for (fd = 0; fd < NUFILE; fd++)
- /* if ep->savefd[fd] < 0, means fd was closed */
- if (ep->savefd[fd])
- restfd(fd, ep->savefd[fd]);
- if (ep->savefd[2]) /* Clear any write errors */
- shf_reopen(2, SHF_WR, shl_out);
- }
- /* Bottom of the stack.
- * Either main shell is exiting or cleanup_parents_env() was called.
- */
- if (ep->oenv == NULL) {
- if (ep->type == E_NONE) { /* Main shell exiting? */
-#if HAVE_PERSISTENT_HISTORY
- if (Flag(FTALKING))
- hist_finish();
-#endif
- j_exit();
- if (ep->flags & EF_FAKE_SIGDIE) {
- int sig = exstat - 128;
-
- /* ham up our death a bit (AT&T ksh
- * only seems to do this for SIGTERM)
- * Don't do it for SIGQUIT, since we'd
- * dump a core..
- */
- if ((sig == SIGINT || sig == SIGTERM) &&
- (kshpgrp == kshpid)) {
- setsig(&sigtraps[sig], SIG_DFL,
- SS_RESTORE_CURR | SS_FORCE);
- kill(0, sig);
- }
- }
- }
- if (shf)
- shf_close(shf);
- reclaim();
- exit(exstat);
- }
- if (shf)
- shf_close(shf);
- reclaim();
-
- e = e->oenv;
-
- /* free the struct env - tricky due to the ALLOC_ITEM inside */
- cp = (void *)ep;
- afree(cp + ALLOC_SIZE, ATEMP);
-}
-
-/* Called after a fork to cleanup stuff left over from parents environment */
-void
-cleanup_parents_env(void)
-{
- struct env *ep;
- int fd;
-
- mkssert(e != NULL);
-
- /*
- * Don't clean up temporary files - parent will probably need them.
- * Also, can't easily reclaim memory since variables, etc. could be
- * anywhere.
- */
-
- /* close all file descriptors hiding in savefd */
- for (ep = e; ep; ep = ep->oenv) {
- if (ep->savefd) {
- for (fd = 0; fd < NUFILE; fd++)
- if (ep->savefd[fd] > 0)
- close(ep->savefd[fd]);
- afree(ep->savefd, &ep->area);
- ep->savefd = NULL;
- }
- }
- e->oenv = NULL;
-}
-
-/* Called just before an execve cleanup stuff temporary files */
-void
-cleanup_proc_env(void)
-{
- struct env *ep;
-
- for (ep = e; ep; ep = ep->oenv)
- remove_temps(ep->temps);
-}
-
-/* remove temp files and free ATEMP Area */
-static void
-reclaim(void)
-{
- remove_temps(e->temps);
- e->temps = NULL;
- afreeall(&e->area);
-}
-
-static void
-remove_temps(struct temp *tp)
-{
- for (; tp != NULL; tp = tp->next)
- if (tp->pid == procpid)
- unlink(tp->name);
-}
-
-/* Initialise tty_fd. Used for saving/reseting tty modes upon
- * foreground job completion and for setting up tty process group.
- */
-void
-tty_init(bool init_ttystate, bool need_tty)
-{
- bool do_close = true;
- int tfd;
-
- if (tty_fd >= 0) {
- close(tty_fd);
- tty_fd = -1;
- }
- tty_devtty = 1;
-
-#ifdef _UWIN
- /* XXX imake style */
- if (isatty(3))
- tfd = 3;
- else
-#endif
- if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
- tty_devtty = 0;
- if (need_tty)
- warningf(false,
- "No controlling tty (open /dev/tty: %s)",
- strerror(errno));
- }
- if (tfd < 0) {
- do_close = false;
- if (isatty(0))
- tfd = 0;
- else if (isatty(2))
- tfd = 2;
- else {
- if (need_tty)
- warningf(false,
- "Can't find tty file descriptor");
- return;
- }
- }
- if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) < 0) {
- if (need_tty)
- warningf(false, "j_ttyinit: dup of tty fd failed: %s",
- strerror(errno));
- } else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 0) {
- if (need_tty)
- warningf(false,
- "j_ttyinit: can't set close-on-exec flag: %s",
- strerror(errno));
- close(tty_fd);
- tty_fd = -1;
- } else if (init_ttystate)
- tcgetattr(tty_fd, &tty_state);
- if (do_close)
- close(tfd);
-}
-
-void
-tty_close(void)
-{
- if (tty_fd >= 0) {
- close(tty_fd);
- tty_fd = -1;
- }
-}
-
-/* A shell error occurred (eg, syntax error, etc.) */
-void
-errorf(const char *fmt, ...)
-{
- va_list va;
-
- shl_stdout_ok = 0; /* debugging: note that stdout not valid */
- exstat = 1;
- if (*fmt != 1) {
- error_prefix(true);
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- shf_putchar('\n', shl_out);
- }
- shf_flush(shl_out);
- unwind(LERROR);
-}
-
-/* like errorf(), but no unwind is done */
-void
-warningf(bool fileline, const char *fmt, ...)
-{
- va_list va;
-
- error_prefix(fileline);
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- shf_putchar('\n', shl_out);
- shf_flush(shl_out);
-}
-
-/* Used by built-in utilities to prefix shell and utility name to message
- * (also unwinds environments for special builtins).
- */
-void
-bi_errorf(const char *fmt, ...)
-{
- va_list va;
-
- shl_stdout_ok = 0; /* debugging: note that stdout not valid */
- exstat = 1;
- if (*fmt != 1) {
- error_prefix(true);
- /* not set when main() calls parse_args() */
- if (builtin_argv0)
- shf_fprintf(shl_out, "%s: ", builtin_argv0);
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- shf_putchar('\n', shl_out);
- }
- shf_flush(shl_out);
- /* POSIX special builtins and ksh special builtins cause
- * non-interactive shells to exit.
- * XXX odd use of KEEPASN; also may not want LERROR here
- */
- if (builtin_flag & SPEC_BI) {
- builtin_argv0 = NULL;
- unwind(LERROR);
- }
-}
-
-/* Called when something that shouldn't happen does */
-void
-internal_verrorf(const char *fmt, va_list ap)
-{
- shf_fprintf(shl_out, "internal error: ");
- shf_vfprintf(shl_out, fmt, ap);
- shf_putchar('\n', shl_out);
- shf_flush(shl_out);
-}
-
-void
-internal_errorf(const char *fmt, ...)
-{
- va_list va;
-
- va_start(va, fmt);
- internal_verrorf(fmt, va);
- va_end(va);
- unwind(LERROR);
-}
-
-void
-internal_warningf(const char *fmt, ...)
-{
- va_list va;
-
- va_start(va, fmt);
- internal_verrorf(fmt, va);
- va_end(va);
-}
-
-/* used by error reporting functions to print "ksh: .kshrc[25]: " */
-void
-error_prefix(bool fileline)
-{
- /* Avoid foo: foo[2]: ... */
- if (!fileline || !source || !source->file ||
- strcmp(source->file, kshname) != 0)
- shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
- if (fileline && source && source->file != NULL) {
- shf_fprintf(shl_out, "%s[%d]: ", source->file,
- source->errline > 0 ? source->errline : source->line);
- source->errline = 0;
- }
-}
-
-/* printf to shl_out (stderr) with flush */
-void
-shellf(const char *fmt, ...)
-{
- va_list va;
-
- if (!initio_done) /* shl_out may not be set up yet... */
- return;
- va_start(va, fmt);
- shf_vfprintf(shl_out, fmt, va);
- va_end(va);
- shf_flush(shl_out);
-}
-
-/* printf to shl_stdout (stdout) */
-void
-shprintf(const char *fmt, ...)
-{
- va_list va;
-
- if (!shl_stdout_ok)
- internal_errorf("shl_stdout not valid");
- va_start(va, fmt);
- shf_vfprintf(shl_stdout, fmt, va);
- va_end(va);
-}
-
-/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
-int
-can_seek(int fd)
-{
- struct stat statb;
-
- return (fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
- SHF_UNBUF : 0);
-}
-
-struct shf shf_iob[3];
-
-void
-initio(void)
-{
- shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */
- shf_fdopen(2, SHF_WR, shl_out);
- shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */
- initio_done = 1;
-}
-
-/* A dup2() with error checking */
-int
-ksh_dup2(int ofd, int nfd, bool errok)
-{
- int rv;
-
- if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF))
- errorf("too many files open in shell");
-
-#ifdef __ultrix
- /* XXX imake style */
- if (rv >= 0)
- fcntl(nfd, F_SETFD, 0);
-#endif
-
- return (rv);
-}
-
-/*
- * move fd from user space (0<=fd<10) to shell space (fd>=10),
- * set close-on-exec flag.
- */
-short
-savefd(int fd)
-{
- int nfd = fd;
-
- if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 &&
- errno == EBADF)
- return (-1);
- if (nfd < 0 || nfd > SHRT_MAX)
- errorf("too many files open in shell");
- fcntl(nfd, F_SETFD, FD_CLOEXEC);
- return ((short)nfd);
-}
-
-void
-restfd(int fd, int ofd)
-{
- if (fd == 2)
- shf_flush(&shf_iob[fd]);
- if (ofd < 0) /* original fd closed */
- close(fd);
- else if (fd != ofd) {
- ksh_dup2(ofd, fd, true); /* XXX: what to do if this fails? */
- close(ofd);
- }
-}
-
-void
-openpipe(int *pv)
-{
- int lpv[2];
-
- if (pipe(lpv) < 0)
- errorf("can't create pipe - try again");
- pv[0] = savefd(lpv[0]);
- if (pv[0] != lpv[0])
- close(lpv[0]);
- pv[1] = savefd(lpv[1]);
- if (pv[1] != lpv[1])
- close(lpv[1]);
-}
-
-void
-closepipe(int *pv)
-{
- close(pv[0]);
- close(pv[1]);
-}
-
-/* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
- * a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
- */
-int
-check_fd(const char *name, int mode, const char **emsgp)
-{
- int fd, fl;
-
- if (name[0] == 'p' && !name[1])
- return (coproc_getfd(mode, emsgp));
- for (fd = 0; ksh_isdigit(*name); ++name)
- fd = (fd * 10) + *name - '0';
- if (*name || fd >= FDBASE) {
- if (emsgp)
- *emsgp = "illegal file descriptor name";
- return (-1);
- }
- if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
- if (emsgp)
- *emsgp = "bad file descriptor";
- return (-1);
- }
- fl &= O_ACCMODE;
- /* X_OK is a kludge to disable this check for dups (x<&1):
- * historical shells never did this check (XXX don't know what
- * POSIX has to say).
- */
- if (!(mode & X_OK) && fl != O_RDWR && (
- ((mode & R_OK) && fl != O_RDONLY) ||
- ((mode & W_OK) && fl != O_WRONLY))) {
- if (emsgp)
- *emsgp = (fl == O_WRONLY) ?
- "fd not open for reading" :
- "fd not open for writing";
- return (-1);
- }
- return (fd);
-}
-
-/* Called once from main */
-void
-coproc_init(void)
-{
- coproc.read = coproc.readw = coproc.write = -1;
- coproc.njobs = 0;
- coproc.id = 0;
-}
-
-/* Called by c_read() when eof is read - close fd if it is the co-process fd */
-void
-coproc_read_close(int fd)
-{
- if (coproc.read >= 0 && fd == coproc.read) {
- coproc_readw_close(fd);
- close(coproc.read);
- coproc.read = -1;
- }
-}
-
-/* Called by c_read() and by iosetup() to close the other side of the
- * read pipe, so reads will actually terminate.
- */
-void
-coproc_readw_close(int fd)
-{
- if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) {
- close(coproc.readw);
- coproc.readw = -1;
- }
-}
-
-/* Called by c_print when a write to a fd fails with EPIPE and by iosetup
- * when co-process input is dup'd
- */
-void
-coproc_write_close(int fd)
-{
- if (coproc.write >= 0 && fd == coproc.write) {
- close(coproc.write);
- coproc.write = -1;
- }
-}
-
-/* Called to check for existence of/value of the co-process file descriptor.
- * (Used by check_fd() and by c_read/c_print to deal with -p option).
- */
-int
-coproc_getfd(int mode, const char **emsgp)
-{
- int fd = (mode & R_OK) ? coproc.read : coproc.write;
-
- if (fd >= 0)
- return (fd);
- if (emsgp)
- *emsgp = "no coprocess";
- return (-1);
-}
-
-/* called to close file descriptors related to the coprocess (if any)
- * Should be called with SIGCHLD blocked.
- */
-void
-coproc_cleanup(int reuse)
-{
- /* This to allow co-processes to share output pipe */
- if (!reuse || coproc.readw < 0 || coproc.read < 0) {
- if (coproc.read >= 0) {
- close(coproc.read);
- coproc.read = -1;
- }
- if (coproc.readw >= 0) {
- close(coproc.readw);
- coproc.readw = -1;
- }
- }
- if (coproc.write >= 0) {
- close(coproc.write);
- coproc.write = -1;
- }
-}
-
-struct temp *
-maketemp(Area *ap, Temp_type type, struct temp **tlist)
-{
- struct temp *tp;
- int len;
- int fd;
- char *pathname;
- const char *dir;
-
- dir = tmpdir ? tmpdir : MKSH_DEFAULT_TMPDIR;
-#if HAVE_MKSTEMP
- len = strlen(dir) + 6 + 10 + 1;
-#else
- pathname = tempnam(dir, "mksh.");
- len = ((pathname == NULL) ? 0 : strlen(pathname)) + 1;
-#endif
- tp = alloc(sizeof(struct temp) + len, ap);
- tp->name = (char *)&tp[1];
-#if !HAVE_MKSTEMP
- if (pathname == NULL)
- tp->name[0] = '\0';
- else {
- memcpy(tp->name, pathname, len);
- free(pathname);
- }
-#endif
- pathname = tp->name;
- tp->shf = NULL;
- tp->type = type;
-#if HAVE_MKSTEMP
- shf_snprintf(pathname, len, "%s/mksh.XXXXXXXXXX", dir);
- if ((fd = mkstemp(pathname)) >= 0)
-#else
- if (tp->name[0] && (fd = open(tp->name, O_CREAT | O_RDWR, 0600)) >= 0)
-#endif
- tp->shf = shf_fdopen(fd, SHF_WR, NULL);
- tp->pid = procpid;
-
- tp->next = *tlist;
- *tlist = tp;
- return (tp);
-}
-
-/*
- * We use a similar collision resolution algorithm as Python 2.5.4
- * but with a slightly tweaked implementation written from scratch.
- */
-
-#define INIT_TBLS 8 /* initial table size (power of 2) */
-#define PERTURB_SHIFT 5 /* see Python 2.5.4 Objects/dictobject.c */
-
-static void texpand(struct table *, size_t);
-static int tnamecmp(const void *, const void *);
-static struct tbl *ktscan(struct table *, const char *, uint32_t,
- struct tbl ***);
-
-static void
-texpand(struct table *tp, size_t nsize)
-{
- size_t i, j, osize = tp->size, perturb;
- struct tbl *tblp, **pp;
- struct tbl **ntblp, **otblp = tp->tbls;
-
- ntblp = alloc(nsize * sizeof(struct tbl *), tp->areap);
- for (i = 0; i < nsize; i++)
- ntblp[i] = NULL;
- tp->size = nsize;
- tp->nfree = (nsize * 4) / 5; /* table can get 80% full */
- tp->tbls = ntblp;
- if (otblp == NULL)
- return;
- nsize--; /* from here on nsize := mask */
- for (i = 0; i < osize; i++)
- if ((tblp = otblp[i]) != NULL) {
- if ((tblp->flag & DEFINED)) {
- /* search for free hash table slot */
- j = (perturb = tblp->ua.hval) & nsize;
- goto find_first_empty_slot;
- find_next_empty_slot:
- j = (j << 2) + j + perturb + 1;
- perturb >>= PERTURB_SHIFT;
- find_first_empty_slot:
- pp = &ntblp[j & nsize];
- if (*pp != NULL)
- goto find_next_empty_slot;
- /* found an empty hash table slot */
- *pp = tblp;
- tp->nfree--;
- } else if (!(tblp->flag & FINUSE)) {
- afree(tblp, tp->areap);
- }
- }
- afree(otblp, tp->areap);
-}
-
-void
-ktinit(struct table *tp, Area *ap, size_t tsize)
-{
- tp->areap = ap;
- tp->tbls = NULL;
- tp->size = tp->nfree = 0;
- if (tsize)
- texpand(tp, tsize);
-}
-
-/* table, name (key) to search for, hash(name), rv pointer to tbl ptr */
-static struct tbl *
-ktscan(struct table *tp, const char *name, uint32_t h, struct tbl ***ppp)
-{
- size_t j, perturb, mask;
- struct tbl **pp, *p;
-
- mask = tp->size - 1;
- /* search for hash table slot matching name */
- j = (perturb = h) & mask;
- goto find_first_slot;
- find_next_slot:
- j = (j << 2) + j + perturb + 1;
- perturb >>= PERTURB_SHIFT;
- find_first_slot:
- pp = &tp->tbls[j & mask];
- if ((p = *pp) != NULL && (p->ua.hval != h || !(p->flag & DEFINED) ||
- strcmp(p->name, name)))
- goto find_next_slot;
- /* p == NULL if not found, correct found entry otherwise */
- if (ppp)
- *ppp = pp;
- return (p);
-}
-
-/* table, name (key) to search for, hash(n) */
-struct tbl *
-ktsearch(struct table *tp, const char *n, uint32_t h)
-{
- return (tp->size ? ktscan(tp, n, h, NULL) : NULL);
-}
-
-/* table, name (key) to enter, hash(n) */
-struct tbl *
-ktenter(struct table *tp, const char *n, uint32_t h)
-{
- struct tbl **pp, *p;
- int len;
-
- if (tp->size == 0)
- texpand(tp, INIT_TBLS);
- Search:
- if ((p = ktscan(tp, n, h, &pp)))
- return (p);
-
- if (tp->nfree <= 0) {
- /* too full */
- texpand(tp, 2 * tp->size);
- goto Search;
- }
-
- /* create new tbl entry */
- len = strlen(n) + 1;
- p = alloc(offsetof(struct tbl, name[0]) + len, tp->areap);
- p->flag = 0;
- p->type = 0;
- p->areap = tp->areap;
- p->ua.hval = h;
- p->u2.field = 0;
- p->u.array = NULL;
- memcpy(p->name, n, len);
-
- /* enter in tp->tbls */
- tp->nfree--;
- *pp = p;
- return (p);
-}
-
-void
-ktwalk(struct tstate *ts, struct table *tp)
-{
- ts->left = tp->size;
- ts->next = tp->tbls;
-}
-
-struct tbl *
-ktnext(struct tstate *ts)
-{
- while (--ts->left >= 0) {
- struct tbl *p = *ts->next++;
- if (p != NULL && (p->flag & DEFINED))
- return (p);
- }
- return (NULL);
-}
-
-static int
-tnamecmp(const void *p1, const void *p2)
-{
- const struct tbl *a = *((const struct tbl * const *)p1);
- const struct tbl *b = *((const struct tbl * const *)p2);
-
- return (strcmp(a->name, b->name));
-}
-
-struct tbl **
-ktsort(struct table *tp)
-{
- size_t i;
- struct tbl **p, **sp, **dp;
-
- p = alloc((tp->size + 1) * sizeof(struct tbl *), ATEMP);
- sp = tp->tbls; /* source */
- dp = p; /* dest */
- i = (size_t)tp->size;
- while (i--)
- if ((*dp = *sp++) != NULL && (((*dp)->flag & DEFINED) ||
- ((*dp)->flag & ARRAY)))
- dp++;
- qsort(p, (i = dp - p), sizeof(void *), tnamecmp);
- p[i] = NULL;
- return (p);
-}
-
-#ifdef SIGWINCH
-static void
-x_sigwinch(int sig MKSH_A_UNUSED)
-{
- /* this runs inside interrupt context, with errno saved */
-
- got_winch = 1;
-}
-#endif
diff --git a/mksh/src/misc.c b/mksh/src/misc.c
deleted file mode 100644
index 75a4de1..0000000
--- a/mksh/src/misc.c
+++ /dev/null
@@ -1,1579 +0,0 @@
-/* $OpenBSD: misc.c,v 1.37 2009/04/19 20:34:05 sthen Exp $ */
-/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-#if !HAVE_GETRUSAGE
-#include <sys/times.h>
-#endif
-#if HAVE_GRP_H
-#include <grp.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.141 2010/07/17 22:09:36 tg Exp $");
-
-unsigned char chtypes[UCHAR_MAX + 1]; /* type bits for unsigned char */
-
-#if !HAVE_SETRESUGID
-uid_t kshuid;
-gid_t kshgid, kshegid;
-#endif
-
-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 void chvt(const char *);
-#endif
-
-/*
- * Fast character classes
- */
-void
-setctypes(const char *s, int t)
-{
- unsigned int i;
-
- if (t & C_IFS) {
- for (i = 0; i < UCHAR_MAX + 1; i++)
- chtypes[i] &= ~C_IFS;
- chtypes[0] |= C_IFS; /* include \0 in C_IFS */
- }
- while (*s != 0)
- chtypes[(unsigned char)*s++] |= t;
-}
-
-void
-initctypes(void)
-{
- int c;
-
- for (c = 'a'; c <= 'z'; c++)
- chtypes[c] |= C_ALPHA;
- for (c = 'A'; c <= 'Z'; c++)
- chtypes[c] |= C_ALPHA;
- chtypes['_'] |= C_ALPHA;
- setctypes("0123456789", C_DIGIT);
- setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */
- setctypes("*@#!$-?", C_VAR1);
- setctypes(" \t\n", C_IFSWS);
- setctypes("=-+?", C_SUBOP1);
- setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE);
-}
-
-/* called from XcheckN() to grow buffer */
-char *
-Xcheck_grow_(XString *xsp, const char *xp, unsigned int more)
-{
- const char *old_beg = xsp->beg;
-
- xsp->len += more > xsp->len ? more : xsp->len;
- xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap);
- xsp->end = xsp->beg + xsp->len;
- return (xsp->beg + (xp - old_beg));
-}
-
-#define SHFLAGS_DEFNS
-#include "sh_flags.h"
-
-const struct shoption options[] = {
-#define SHFLAGS_ITEMS
-#include "sh_flags.h"
-};
-
-/*
- * translate -o option into F* constant (also used for test -o option)
- */
-size_t
-option(const char *n)
-{
- size_t i;
-
- if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2]) {
- for (i = 0; i < NELEM(options); i++)
- if (options[i].c == n[1])
- return (i);
- } else for (i = 0; i < NELEM(options); i++)
- if (options[i].name && strcmp(options[i].name, n) == 0)
- return (i);
-
- return ((size_t)-1);
-}
-
-struct options_info {
- int opt_width;
- int opts[NELEM(options)];
-};
-
-static char *options_fmt_entry(char *, int, int, const void *);
-static void printoptions(bool);
-
-/* format a single select menu item */
-static char *
-options_fmt_entry(char *buf, int buflen, int i, const void *arg)
-{
- const struct options_info *oi = (const struct options_info *)arg;
-
- shf_snprintf(buf, buflen, "%-*s %s",
- oi->opt_width, options[oi->opts[i]].name,
- Flag(oi->opts[i]) ? "on" : "off");
- return (buf);
-}
-
-static void
-printoptions(bool verbose)
-{
- int i = 0;
-
- if (verbose) {
- int n = 0, len, octs = 0;
- struct options_info oi;
-
- /* verbose version */
- shf_puts("Current option settings\n", shl_stdout);
-
- oi.opt_width = 0;
- while (i < (int)NELEM(options)) {
- if (options[i].name) {
- oi.opts[n++] = i;
- len = strlen(options[i].name);
- if (len > octs)
- octs = len;
- len = utf_mbswidth(options[i].name);
- if (len > oi.opt_width)
- oi.opt_width = len;
- }
- ++i;
- }
- print_columns(shl_stdout, n, options_fmt_entry, &oi,
- octs + 4, oi.opt_width + 4, true);
- } else {
- /* short version á la AT&T ksh93 */
- shf_puts("set", shl_stdout);
- while (i < (int)NELEM(options)) {
- if (Flag(i) && options[i].name)
- shprintf(" -o %s", options[i].name);
- ++i;
- }
- shf_putc('\n', shl_stdout);
- }
-}
-
-char *
-getoptions(void)
-{
- unsigned int i;
- char m[(int) FNFLAGS + 1];
- char *cp = m;
-
- for (i = 0; i < NELEM(options); i++)
- if (options[i].c && Flag(i))
- *cp++ = options[i].c;
- strndupx(cp, m, cp - m, ATEMP);
- return (cp);
-}
-
-/* change a Flag(*) value; takes care of special actions */
-void
-change_flag(enum sh_flag f, int what, unsigned int newval)
-{
- unsigned char oldval;
-
- oldval = Flag(f);
- Flag(f) = newval ? 1 : 0; /* needed for tristates */
-#ifndef MKSH_UNEMPLOYED
- if (f == FMONITOR) {
- if (what != OF_CMDLINE && newval != oldval)
- j_change();
- } else
-#endif
- if ((
-#if !MKSH_S_NOVI
- f == FVI ||
-#endif
- f == FEMACS || f == FGMACS) && newval) {
-#if !MKSH_S_NOVI
- Flag(FVI) =
-#endif
- Flag(FEMACS) = Flag(FGMACS) = 0;
- Flag(f) = (unsigned char)newval;
- } else if (f == FPRIVILEGED && oldval && !newval) {
- /* Turning off -p? */
-#if HAVE_SETRESUGID
- gid_t kshegid = getgid();
-
- setresgid(kshegid, kshegid, kshegid);
-#if HAVE_SETGROUPS
- setgroups(1, &kshegid);
-#endif
- setresuid(ksheuid, ksheuid, ksheuid);
-#else
- seteuid(ksheuid = kshuid = getuid());
- setuid(ksheuid);
- setegid(kshegid = kshgid = getgid());
- setgid(kshegid);
-#endif
- } else if ((f == FPOSIX || f == FSH) && newval) {
- Flag(FPOSIX) = Flag(FSH) = Flag(FBRACEEXPAND) = 0;
- Flag(f) = (unsigned char)newval;
- }
- /* Changing interactive flag? */
- if (f == FTALKING) {
- if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
- Flag(FTALKING_I) = (unsigned char)newval;
- }
-}
-
-/* Parse command line & set command arguments. Returns the index of
- * non-option arguments, -1 if there is an error.
- */
-int
-parse_args(const char **argv,
- int what, /* OF_CMDLINE or OF_SET */
- bool *setargsp)
-{
- static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */
- static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */
- char set, *opts;
- const char *array = NULL;
- Getopt go;
- size_t i;
- int optc, sortargs = 0, arrayset = 0;
-
- /* First call? Build option strings... */
- if (cmd_opts[0] == '\0') {
- char *p = cmd_opts, *q = set_opts;
-
- /* see cmd_opts[] declaration */
- *p++ = 'o';
- *p++ = ':';
-#if !defined(MKSH_SMALL) || defined(TIOCSCTTY)
- *p++ = 'T';
- *p++ = ':';
-#endif
- /* see set_opts[] declaration */
- *q++ = 'A';
- *q++ = ':';
- *q++ = 'o';
- *q++ = ';';
- *q++ = 's';
-
- for (i = 0; i < NELEM(options); i++) {
- if (options[i].c) {
- if (options[i].flags & OF_CMDLINE)
- *p++ = options[i].c;
- if (options[i].flags & OF_SET)
- *q++ = options[i].c;
- }
- }
- *p = '\0';
- *q = '\0';
- }
-
- if (what == OF_CMDLINE) {
- const char *p = argv[0], *q;
- /* Set FLOGIN before parsing options so user can clear
- * flag using +l.
- */
- if (*p != '-')
- for (q = p; *q; )
- if (*q++ == '/')
- p = q;
- Flag(FLOGIN) = (*p == '-');
- opts = cmd_opts;
- } else if (what == OF_FIRSTTIME) {
- opts = cmd_opts;
- } else
- 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;
- switch (optc) {
- case 'A':
- if (what == OF_FIRSTTIME)
- break;
- arrayset = set ? 1 : -1;
- array = go.optarg;
- break;
-
- case 'o':
- if (what == OF_FIRSTTIME)
- break;
- if (go.optarg == NULL) {
- /* lone -o: print options
- *
- * Note that on the command line, -o requires
- * an option (ie, can't get here if what is
- * OF_CMDLINE).
- */
- printoptions(set);
- break;
- }
- i = option(go.optarg);
- if ((enum sh_flag)i == FARC4RANDOM) {
- warningf(true, "Do not use set ±o arc4random,"
- " it will be removed in the next version"
- " of mksh!");
- return (0);
- }
- if ((i != (size_t)-1) && set == Flag(i))
- /* Don't check the context if the flag
- * isn't changing - makes "set -o interactive"
- * work if you're already interactive. Needed
- * if the output of "set +o" is to be used.
- */
- ;
- else if ((i != (size_t)-1) && (options[i].flags & what))
- change_flag((enum sh_flag)i, what, set);
- else {
- bi_errorf("%s: bad option", go.optarg);
- return (-1);
- }
- break;
-
-#if !defined(MKSH_SMALL) || defined(TIOCSCTTY)
- case 'T':
- if (what != OF_FIRSTTIME)
- break;
-#ifndef TIOCSCTTY
- errorf("no TIOCSCTTY ioctl");
-#else
- change_flag(FTALKING, OF_CMDLINE, 1);
- chvt(go.optarg);
- break;
-#endif
-#endif
-
- case '?':
- return (-1);
-
- default:
- if (what == OF_FIRSTTIME)
- break;
- /* -s: sort positional params (AT&T ksh stupidity) */
- if (what == OF_SET && optc == 's') {
- sortargs = 1;
- break;
- }
- for (i = 0; i < NELEM(options); i++)
- if (optc == options[i].c &&
- (what & options[i].flags)) {
- change_flag((enum sh_flag)i, what, set);
- break;
- }
- if (i == NELEM(options))
- internal_errorf("parse_args: '%c'", optc);
- }
- }
- if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
- (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
- argv[go.optind][1] == '\0') {
- /* lone - clears -v and -x flags */
- if (argv[go.optind][0] == '-')
- Flag(FVERBOSE) = Flag(FXTRACE) = 0;
- /* set skips lone - or + option */
- go.optind++;
- }
- if (setargsp)
- /* -- means set $#/$* even if there are no arguments */
- *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
- argv[go.optind]);
-
- if (arrayset && (!*array || *skip_varname(array, false))) {
- bi_errorf("%s: is not an identifier", array);
- return (-1);
- }
- if (sortargs) {
- for (i = go.optind; argv[i]; i++)
- ;
- qsort(&argv[go.optind], i - go.optind, sizeof(void *),
- xstrcmp);
- }
- if (arrayset)
- go.optind += set_array(array, arrayset > 0 ? true : false,
- argv + go.optind);
-
- return (go.optind);
-}
-
-/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
-int
-getn(const char *s, int *ai)
-{
- int i, c, rv = 0;
- bool neg = false;
-
- do {
- c = *s++;
- } while (ksh_isspace(c));
- if (c == '-') {
- neg = true;
- c = *s++;
- } else if (c == '+')
- c = *s++;
- *ai = i = 0;
- do {
- if (!ksh_isdigit(c))
- goto getn_out;
- i *= 10;
- if (i < *ai)
- /* overflow */
- goto getn_out;
- i += c - '0';
- *ai = i;
- } while ((c = *s++));
- rv = 1;
-
- getn_out:
- if (neg)
- *ai = -*ai;
- return (rv);
-}
-
-/* getn() that prints error */
-int
-bi_getn(const char *as, int *ai)
-{
- int rv;
-
- if (!(rv = getn(as, ai)))
- bi_errorf("%s: bad number", as);
- return (rv);
-}
-
-/* -------- gmatch.c -------- */
-
-/*
- * int gmatch(string, pattern)
- * char *string, *pattern;
- *
- * Match a pattern as in sh(1).
- * pattern character are prefixed with MAGIC by expand.
- */
-
-int
-gmatchx(const char *s, const char *p, bool isfile)
-{
- const char *se, *pe;
-
- if (s == NULL || p == NULL)
- return (0);
-
- se = s + strlen(s);
- pe = p + strlen(p);
- /* isfile is false iff no syntax check has been done on
- * the pattern. If check fails, just to a strcmp().
- */
- if (!isfile && !has_globbing(p, pe)) {
- size_t len = pe - p + 1;
- char tbuf[64];
- char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
- debunk(t, p, len);
- return (!strcmp(t, s));
- }
- return (do_gmatch((const unsigned char *) s, (const unsigned char *) se,
- (const unsigned char *) p, (const unsigned char *) pe));
-}
-
-/* Returns if p is a syntacticly correct globbing pattern, false
- * if it contains no pattern characters or if there is a syntax error.
- * Syntax errors are:
- * - [ with no closing ]
- * - imbalanced $(...) expression
- * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
- */
-/*XXX
-- if no magic,
- if dest given, copy to dst
- return ?
-- if magic && (no globbing || syntax error)
- debunk to dst
- return ?
-- return ?
-*/
-int
-has_globbing(const char *xp, const char *xpe)
-{
- const unsigned char *p = (const unsigned char *) xp;
- const unsigned char *pe = (const unsigned char *) xpe;
- int c;
- int nest = 0, bnest = 0;
- int saw_glob = 0;
- int in_bracket = 0; /* inside [...] */
-
- for (; p < pe; p++) {
- if (!ISMAGIC(*p))
- continue;
- if ((c = *++p) == '*' || c == '?')
- saw_glob = 1;
- else if (c == '[') {
- if (!in_bracket) {
- saw_glob = 1;
- in_bracket = 1;
- if (ISMAGIC(p[1]) && p[2] == NOT)
- p += 2;
- if (ISMAGIC(p[1]) && p[2] == ']')
- p += 2;
- }
- /* XXX Do we need to check ranges here? POSIX Q */
- } else if (c == ']') {
- if (in_bracket) {
- if (bnest) /* [a*(b]) */
- return (0);
- in_bracket = 0;
- }
- } else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) {
- saw_glob = 1;
- if (in_bracket)
- bnest++;
- else
- nest++;
- } else if (c == '|') {
- if (in_bracket && !bnest) /* *(a[foo|bar]) */
- return (0);
- } else if (c == /*(*/ ')') {
- if (in_bracket) {
- if (!bnest--) /* *(a[b)c] */
- return (0);
- } else if (nest)
- nest--;
- }
- /*
- * else must be a MAGIC-MAGIC, or MAGIC-!,
- * MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-}
- */
- }
- return (saw_glob && !in_bracket && !nest);
-}
-
-/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
-static int
-do_gmatch(const unsigned char *s, const unsigned char *se,
- const unsigned char *p, const unsigned char *pe)
-{
- int sc, pc;
- const unsigned char *prest, *psub, *pnext;
- const unsigned char *srest;
-
- if (s == NULL || p == NULL)
- return (0);
- while (p < pe) {
- pc = *p++;
- sc = s < se ? *s : '\0';
- s++;
- if (!ISMAGIC(pc)) {
- if (sc != pc)
- return (0);
- continue;
- }
- switch (*p++) {
- case '[':
- if (sc == 0 || (p = cclass(p, sc)) == NULL)
- return (0);
- break;
-
- case '?':
- if (sc == 0)
- return (0);
- if (UTFMODE) {
- --s;
- s += utf_ptradj((const void *)s);
- }
- break;
-
- case '*':
- if (p == pe)
- return (1);
- s--;
- do {
- if (do_gmatch(s, se, p, pe))
- return (1);
- } while (s++ < se);
- return (0);
-
- /**
- * [*+?@!](pattern|pattern|..)
- * This is also needed for ${..%..}, etc.
- */
- case 0x80|'+': /* matches one or more times */
- case 0x80|'*': /* matches zero or more times */
- if (!(prest = pat_scan(p, pe, 0)))
- return (0);
- s--;
- /* take care of zero matches */
- if (p[-1] == (0x80 | '*') &&
- do_gmatch(s, se, prest, pe))
- return (1);
- for (psub = p; ; psub = pnext) {
- pnext = pat_scan(psub, pe, 1);
- for (srest = s; srest <= se; srest++) {
- if (do_gmatch(s, srest, psub, pnext - 2) &&
- (do_gmatch(srest, se, prest, pe) ||
- (s != srest && do_gmatch(srest,
- se, p - 2, pe))))
- return (1);
- }
- if (pnext == prest)
- break;
- }
- return (0);
-
- case 0x80|'?': /* matches zero or once */
- case 0x80|'@': /* matches one of the patterns */
- case 0x80|' ': /* simile for @ */
- if (!(prest = pat_scan(p, pe, 0)))
- return (0);
- s--;
- /* Take care of zero matches */
- if (p[-1] == (0x80 | '?') &&
- do_gmatch(s, se, prest, pe))
- return (1);
- for (psub = p; ; psub = pnext) {
- pnext = pat_scan(psub, pe, 1);
- srest = prest == pe ? se : s;
- for (; srest <= se; srest++) {
- if (do_gmatch(s, srest, psub, pnext - 2) &&
- do_gmatch(srest, se, prest, pe))
- return (1);
- }
- if (pnext == prest)
- break;
- }
- return (0);
-
- case 0x80|'!': /* matches none of the patterns */
- if (!(prest = pat_scan(p, pe, 0)))
- return (0);
- s--;
- for (srest = s; srest <= se; srest++) {
- int matched = 0;
-
- for (psub = p; ; psub = pnext) {
- pnext = pat_scan(psub, pe, 1);
- if (do_gmatch(s, srest, psub,
- pnext - 2)) {
- matched = 1;
- break;
- }
- if (pnext == prest)
- break;
- }
- if (!matched &&
- do_gmatch(srest, se, prest, pe))
- return (1);
- }
- return (0);
-
- default:
- if (sc != p[-1])
- return (0);
- break;
- }
- }
- return (s == se);
-}
-
-static const unsigned char *
-cclass(const unsigned char *p, int sub)
-{
- int c, d, notp, found = 0;
- const unsigned char *orig_p = p;
-
- if ((notp = (ISMAGIC(*p) && *++p == NOT)))
- p++;
- do {
- c = *p++;
- if (ISMAGIC(c)) {
- c = *p++;
- if ((c & 0x80) && !ISMAGIC(c)) {
- c &= 0x7f;/* extended pattern matching: *+?@! */
- /* XXX the ( char isn't handled as part of [] */
- if (c == ' ') /* simile for @: plain (..) */
- c = '(' /*)*/;
- }
- }
- if (c == '\0')
- /* No closing ] - act as if the opening [ was quoted */
- return (sub == '[' ? orig_p : NULL);
- if (ISMAGIC(p[0]) && p[1] == '-' &&
- (!ISMAGIC(p[2]) || p[3] != ']')) {
- p += 2; /* MAGIC- */
- d = *p++;
- if (ISMAGIC(d)) {
- d = *p++;
- if ((d & 0x80) && !ISMAGIC(d))
- d &= 0x7f;
- }
- /* POSIX says this is an invalid expression */
- if (c > d)
- return (NULL);
- } else
- d = c;
- if (c == sub || (c <= sub && sub <= d))
- found = 1;
- } while (!(ISMAGIC(p[0]) && p[1] == ']'));
-
- return ((found != notp) ? p+2 : NULL);
-}
-
-/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
-const unsigned char *
-pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep)
-{
- int nest = 0;
-
- for (; p < pe; p++) {
- if (!ISMAGIC(*p))
- continue;
- if ((*++p == /*(*/ ')' && nest-- == 0) ||
- (*p == '|' && match_sep && nest == 0))
- return (p + 1);
- if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f))
- nest++;
- }
- return (NULL);
-}
-
-int
-xstrcmp(const void *p1, const void *p2)
-{
- return (strcmp(*(const char * const *)p1, *(const char * const *)p2));
-}
-
-/* Initialise a Getopt structure */
-void
-ksh_getopt_reset(Getopt *go, int flags)
-{
- go->optind = 1;
- go->optarg = NULL;
- go->p = 0;
- go->flags = flags;
- go->info = 0;
- go->buf[1] = '\0';
-}
-
-
-/* getopt() used for shell built-in commands, the getopts command, and
- * command line options.
- * A leading ':' in options means don't print errors, instead return '?'
- * or ':' and set go->optarg to the offending option character.
- * If GF_ERROR is set (and option doesn't start with :), errors result in
- * a call to bi_errorf().
- *
- * Non-standard features:
- * - ';' is like ':' in options, except the argument is optional
- * (if it isn't present, optarg is set to 0).
- * Used for 'set -o'.
- * - ',' is like ':' in options, except the argument always immediately
- * follows the option character (optarg is set to the null string if
- * the option is missing).
- * Used for 'read -u2', 'print -u2' and fc -40.
- * - '#' is like ':' in options, expect that the argument is optional
- * and must start with a digit. If the argument doesn't start with a
- * digit, it is assumed to be missing and normal option processing
- * continues (optarg is set to 0 if the option is missing).
- * Used for 'typeset -LZ4'.
- * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
- * option starting with + is accepted, the GI_PLUS flag will be set
- * in go->info.
- */
-int
-ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
-{
- char c;
- const char *o;
-
- if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
- const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
-
- go->p = 1;
- if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
- go->optind++;
- go->p = 0;
- go->info |= GI_MINUSMINUS;
- return (-1);
- }
- if (arg == NULL ||
- ((flag != '-' ) && /* neither a - nor a + (if + allowed) */
- (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
- (c = arg[1]) == '\0') {
- go->p = 0;
- return (-1);
- }
- go->optind++;
- go->info &= ~(GI_MINUS|GI_PLUS);
- go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
- }
- go->p++;
- if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
- !(o = cstrchr(optionsp, c))) {
- if (optionsp[0] == ':') {
- go->buf[0] = c;
- go->optarg = go->buf;
- } else {
- warningf(true, "%s%s-%c: unknown option",
- (go->flags & GF_NONAME) ? "" : argv[0],
- (go->flags & GF_NONAME) ? "" : ": ", c);
- if (go->flags & GF_ERROR)
- bi_errorfz();
- }
- return ('?');
- }
- /* : means argument must be present, may be part of option argument
- * or the next argument
- * ; same as : but argument may be missing
- * , means argument is part of option argument, and may be null.
- */
- if (*++o == ':' || *o == ';') {
- if (argv[go->optind - 1][go->p])
- go->optarg = argv[go->optind - 1] + go->p;
- else if (argv[go->optind])
- go->optarg = argv[go->optind++];
- else if (*o == ';')
- go->optarg = NULL;
- else {
- if (optionsp[0] == ':') {
- go->buf[0] = c;
- go->optarg = go->buf;
- return (':');
- }
- warningf(true, "%s%s-'%c' requires argument",
- (go->flags & GF_NONAME) ? "" : argv[0],
- (go->flags & GF_NONAME) ? "" : ": ", c);
- if (go->flags & GF_ERROR)
- bi_errorfz();
- return ('?');
- }
- go->p = 0;
- } else if (*o == ',') {
- /* argument is attached to option character, even if null */
- go->optarg = argv[go->optind - 1] + go->p;
- go->p = 0;
- } else if (*o == '#') {
- /* argument is optional and may be attached or unattached
- * but must start with a digit. optarg is set to 0 if the
- * argument is missing.
- */
- if (argv[go->optind - 1][go->p]) {
- if (ksh_isdigit(argv[go->optind - 1][go->p])) {
- go->optarg = argv[go->optind - 1] + go->p;
- go->p = 0;
- } else
- go->optarg = NULL;
- } else {
- if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
- go->optarg = argv[go->optind++];
- go->p = 0;
- } else
- go->optarg = NULL;
- }
- }
- return (c);
-}
-
-/* print variable/alias value using necessary quotes
- * (POSIX says they should be suitable for re-entry...)
- * No trailing newline is printed.
- */
-void
-print_value_quoted(const char *s)
-{
- const char *p;
- int inquote = 0;
-
- /* Test if any quotes are needed */
- for (p = s; *p; p++)
- if (ctype(*p, C_QUOTE))
- break;
- if (!*p) {
- shf_puts(s, shl_stdout);
- return;
- }
- for (p = s; *p; p++) {
- if (*p == '\'') {
- if (inquote)
- shf_putc('\'', shl_stdout);
- shf_putc('\\', shl_stdout);
- inquote = 0;
- } else if (!inquote) {
- shf_putc('\'', shl_stdout);
- inquote = 1;
- }
- shf_putc(*p, shl_stdout);
- }
- if (inquote)
- shf_putc('\'', shl_stdout);
-}
-
-/*
- * Print things in columns and rows - func() is called to format
- * the i-th element
- */
-void
-print_columns(struct shf *shf, int n,
- char *(*func)(char *, int, int, const void *),
- const void *arg, int max_oct, int max_col, bool prefcol)
-{
- int i, r, c, rows, cols, nspace;
- char *str;
-
- if (n <= 0) {
-#ifndef MKSH_SMALL
- internal_warningf("print_columns called with n=%d <= 0", n);
-#endif
- return;
- }
-
- ++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
- * to avoid problems with terminals that have auto-wrap.
- */
- cols = x_cols / (max_col + 1);
-
- /* if we can only print one column anyway, skip the goo */
- if (cols < 2) {
- for (i = 0; i < n; ++i)
- shf_fprintf(shf, "%s \n",
- (*func)(str, max_oct, i, arg));
- goto out;
- }
-
- rows = (n + cols - 1) / cols;
- if (prefcol && cols > rows) {
- i = rows;
- rows = cols > n ? n : cols;
- cols = i;
- }
-
- max_col = -max_col;
- nspace = (x_cols + max_col * cols) / cols;
- if (nspace <= 0)
- nspace = 1;
- for (r = 0; r < rows; r++) {
- for (c = 0; c < cols; c++) {
- i = c * rows + r;
- if (i < n) {
- shf_fprintf(shf, "%*s", max_col,
- (*func)(str, max_oct, i, arg));
- if (c + 1 < cols)
- shf_fprintf(shf, "%*s", nspace, null);
- }
- }
- shf_putchar('\n', shf);
- }
- out:
- afree(str, ATEMP);
-}
-
-/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
-void
-strip_nuls(char *buf, int nbytes)
-{
- char *dst;
-
- /* nbytes check because some systems (older FreeBSDs) have a buggy
- * memchr()
- */
- if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
- char *end = buf + nbytes;
- char *p, *q;
-
- for (p = dst; p < end; p = q) {
- /* skip a block of nulls */
- while (++p < end && *p == '\0')
- ;
- /* find end of non-null block */
- if (!(q = memchr(p, '\0', end - p)))
- q = end;
- memmove(dst, p, q - p);
- dst += q - p;
- }
- *dst = '\0';
- }
-}
-
-/* Like read(2), but if read fails due to non-blocking flag, resets flag
- * and restarts read.
- */
-int
-blocking_read(int fd, char *buf, int nbytes)
-{
- int ret;
- int tried_reset = 0;
-
- while ((ret = read(fd, buf, nbytes)) < 0) {
- if (!tried_reset && errno == EAGAIN) {
- if (reset_nonblock(fd) > 0) {
- tried_reset = 1;
- continue;
- }
- errno = EAGAIN;
- }
- break;
- }
- return (ret);
-}
-
-/* Reset the non-blocking flag on the specified file descriptor.
- * Returns -1 if there was an error, 0 if non-blocking wasn't set,
- * 1 if it was.
- */
-int
-reset_nonblock(int fd)
-{
- int flags;
-
- if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
- return (-1);
- if (!(flags & O_NONBLOCK))
- return (0);
- flags &= ~O_NONBLOCK;
- if (fcntl(fd, F_SETFL, flags) < 0)
- return (-1);
- return (1);
-}
-
-
-/* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */
-char *
-ksh_get_wd(size_t *dlen)
-{
- char *ret, *b;
- size_t len = 1;
-
-#ifdef NO_PATH_MAX
- if ((b = get_current_dir_name())) {
- len = strlen(b) + 1;
- strndupx(ret, b, len - 1, ATEMP);
- free(b);
- } else
- ret = NULL;
-#else
- if ((ret = getcwd((b = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)))
- ret = aresize(b, len = (strlen(b) + 1), ATEMP);
- else
- afree(b, ATEMP);
-#endif
-
- if (dlen)
- *dlen = len;
- return (ret);
-}
-
-/*
- * Makes a filename into result using the following algorithm.
- * - make result NULL
- * - if file starts with '/', append file to result & set cdpathp to NULL
- * - if file starts with ./ or ../ append cwd and file to result
- * and set cdpathp to NULL
- * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
- * then cwd is appended to result.
- * - the first element of cdpathp is appended to result
- * - file is appended to result
- * - cdpathp is set to the start of the next element in cdpathp (or NULL
- * if there are no more elements.
- * The return value indicates whether a non-null element from cdpathp
- * was appended to result.
- */
-int
-make_path(const char *cwd, const char *file,
- char **cdpathp, /* & of : separated list */
- XString *xsp,
- int *phys_pathp)
-{
- int rval = 0;
- bool use_cdpath = true;
- char *plist;
- int len, plen = 0;
- char *xp = Xstring(*xsp, xp);
-
- if (!file)
- file = null;
-
- if (file[0] == '/') {
- *phys_pathp = 0;
- use_cdpath = false;
- } else {
- if (file[0] == '.') {
- char c = file[1];
-
- if (c == '.')
- c = file[2];
- if (c == '/' || c == '\0')
- use_cdpath = false;
- }
-
- plist = *cdpathp;
- if (!plist)
- use_cdpath = false;
- else if (use_cdpath) {
- char *pend;
-
- for (pend = plist; *pend && *pend != ':'; pend++)
- ;
- plen = pend - plist;
- *cdpathp = *pend ? pend + 1 : NULL;
- }
-
- if ((!use_cdpath || !plen || plist[0] != '/') &&
- (cwd && *cwd)) {
- len = strlen(cwd);
- XcheckN(*xsp, xp, len);
- memcpy(xp, cwd, len);
- xp += len;
- if (cwd[len - 1] != '/')
- Xput(*xsp, xp, '/');
- }
- *phys_pathp = Xlength(*xsp, xp);
- if (use_cdpath && plen) {
- XcheckN(*xsp, xp, plen);
- memcpy(xp, plist, plen);
- xp += plen;
- if (plist[plen - 1] != '/')
- Xput(*xsp, xp, '/');
- rval = 1;
- }
- }
-
- len = strlen(file) + 1;
- XcheckN(*xsp, xp, len);
- memcpy(xp, file, len);
-
- if (!use_cdpath)
- *cdpathp = NULL;
-
- return (rval);
-}
-
-/*
- * Simplify pathnames containing "." and ".." entries.
- * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
- */
-void
-simplify_path(char *pathl)
-{
- char *cur, *t;
- bool isrooted;
- char *very_start = pathl, *start;
-
- if (!*pathl)
- return;
-
- if ((isrooted = pathl[0] == '/'))
- very_start++;
-
- /* Before After
- * /foo/ /foo
- * /foo/../../bar /bar
- * /foo/./blah/.. /foo
- * . .
- * .. ..
- * ./foo foo
- * foo/../../../bar ../../bar
- */
-
- for (cur = t = start = very_start; ; ) {
- /* treat multiple '/'s as one '/' */
- while (*t == '/')
- t++;
-
- if (*t == '\0') {
- if (cur == pathl)
- /* convert empty path to dot */
- *cur++ = '.';
- *cur = '\0';
- break;
- }
-
- if (t[0] == '.') {
- if (!t[1] || t[1] == '/') {
- t += 1;
- continue;
- } else if (t[1] == '.' && (!t[2] || t[2] == '/')) {
- if (!isrooted && cur == start) {
- if (cur != very_start)
- *cur++ = '/';
- *cur++ = '.';
- *cur++ = '.';
- start = cur;
- } else if (cur != start)
- while (--cur > start && *cur != '/')
- ;
- t += 2;
- continue;
- }
- }
-
- if (cur != very_start)
- *cur++ = '/';
-
- /* find/copy next component of pathname */
- while (*t && *t != '/')
- *cur++ = *t++;
- }
-}
-
-
-void
-set_current_wd(char *pathl)
-{
- size_t len = 1;
- char *p = pathl;
-
- if (p == NULL) {
- if ((p = ksh_get_wd(&len)) == NULL)
- p = null;
- } else
- len = strlen(p) + 1;
-
- if (len > current_wd_size) {
- afree(current_wd, APERM);
- current_wd = alloc(current_wd_size = len, APERM);
- }
- memcpy(current_wd, p, len);
- if (p != pathl && p != null)
- afree(p, ATEMP);
-}
-
-#ifdef TIOCSCTTY
-extern void chvt_reinit(void);
-
-static void
-chvt(const char *fn)
-{
- char dv[20];
- struct stat sb;
- int fd;
-
- /* for entropy */
- kshstate_f.h = evilhash(fn);
-
- if (*fn == '-') {
- memcpy(dv, "-/dev/null", sizeof("-/dev/null"));
- fn = dv + 1;
- } else {
- if (stat(fn, &sb)) {
- memcpy(dv, "/dev/ttyC", 9);
- strlcpy(dv + 9, fn, sizeof(dv) - 9);
- if (stat(dv, &sb)) {
- strlcpy(dv + 8, fn, sizeof(dv) - 8);
- if (stat(dv, &sb))
- errorf("chvt: can't find tty %s", fn);
- }
- fn = dv;
- }
- if (!(sb.st_mode & S_IFCHR))
- errorf("chvt: not a char device: %s", fn);
- if ((sb.st_uid != 0) && chown(fn, 0, 0))
- warningf(false, "chvt: cannot chown root %s", fn);
- if (((sb.st_mode & 07777) != 0600) && chmod(fn, (mode_t)0600))
- warningf(false, "chvt: cannot chmod 0600 %s", fn);
-#if HAVE_REVOKE
- if (revoke(fn))
-#endif
- warningf(false, "chvt: cannot revoke %s, new shell is"
- " potentially insecure", fn);
- }
- if ((fd = open(fn, O_RDWR)) == -1) {
- sleep(1);
- if ((fd = open(fn, O_RDWR)) == -1)
- errorf("chvt: cannot open %s", fn);
- }
- switch (fork()) {
- case -1:
- errorf("chvt: %s failed", "fork");
- case 0:
- break;
- default:
- exit(0);
- }
- if (setsid() == -1)
- errorf("chvt: %s failed", "setsid");
- if (fn != dv + 1) {
- if (ioctl(fd, TIOCSCTTY, NULL) == -1)
- errorf("chvt: %s failed", "TIOCSCTTY");
- if (tcflush(fd, TCIOFLUSH))
- errorf("chvt: %s failed", "TCIOFLUSH");
- }
- ksh_dup2(fd, 0, false);
- ksh_dup2(fd, 1, false);
- ksh_dup2(fd, 2, false);
- if (fd > 2)
- close(fd);
- chvt_reinit();
-}
-#endif
-
-#ifdef DEBUG
-char longsizes_are_okay[sizeof(long) == sizeof(unsigned long) ? 1 : -1];
-char arisize_is_okay[sizeof(mksh_ari_t) == 4 ? 1 : -1];
-char uarisize_is_okay[sizeof(mksh_uari_t) == 4 ? 1 : -1];
-
-char *
-strchr(char *p, int ch)
-{
- for (;; ++p) {
- if (*p == ch)
- return (p);
- if (!*p)
- return (NULL);
- }
- /* NOTREACHED */
-}
-
-char *
-strstr(char *b, const char *l)
-{
- char first, c;
- size_t n;
-
- if ((first = *l++) == '\0')
- return (b);
- n = strlen(l);
- strstr_look:
- while ((c = *b++) != first)
- if (c == '\0')
- return (NULL);
- if (strncmp(b, l, n))
- goto strstr_look;
- return (b - 1);
-}
-#endif
-
-#ifndef MKSH_ASSUME_UTF8
-#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
-#endif
-
-#ifdef MKSH_SMALL
-char *
-strndup_(const char *src, size_t len, Area *ap)
-{
- char *dst = NULL;
-
- if (src != NULL) {
- dst = alloc(len + 1, ap);
- memcpy(dst, src, len);
- dst[len] = '\0';
- }
- return (dst);
-}
-
-char *
-strdup_(const char *src, Area *ap)
-{
- return (src == NULL ? NULL : strndup_(src, strlen(src), ap));
-}
-#endif
-
-#if !HAVE_GETRUSAGE
-#define INVTCK(r,t) do { \
- r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \
- r.tv_sec = (t) / CLK_TCK; \
-} while (/* CONSTCOND */ 0)
-
-int
-getrusage(int what, struct rusage *ru)
-{
- struct tms tms;
- clock_t u, s;
-
- if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
- return (-1);
-
- switch (what) {
- case RUSAGE_SELF:
- u = tms.tms_utime;
- s = tms.tms_stime;
- break;
- case RUSAGE_CHILDREN:
- u = tms.tms_cutime;
- s = tms.tms_cstime;
- break;
- default:
- errno = EINVAL;
- return (-1);
- }
- INVTCK(ru->ru_utime, u);
- INVTCK(ru->ru_stime, s);
- return (0);
-}
-#endif
-
-/*
- * process the string available via fg (get a char)
- * and fp (put back a char) for backslash escapes,
- * assuming the first call to *fg gets the char di-
- * rectly after the backslash; return the character
- * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
- * escape sequence was found
- */
-int
-unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
-{
- int wc, i, c, fc;
-
- fc = (*fg)();
- switch (fc) {
- case 'a':
- /*
- * according to the comments in pdksh, \007 seems
- * to be more portable than \a (due to HP-UX cc,
- * Ultrix cc, old pcc, etc.) so we avoid the escape
- * sequence altogether in mksh and assume ASCII
- */
- wc = 7;
- break;
- case 'b':
- wc = '\b';
- break;
- case 'c':
- if (!cstyle)
- goto unknown_escape;
- c = (*fg)();
- wc = CTRL(c);
- break;
- case 'E':
- case 'e':
- wc = 033;
- break;
- case 'f':
- wc = '\f';
- break;
- case 'n':
- wc = '\n';
- break;
- case 'r':
- wc = '\r';
- break;
- case 't':
- wc = '\t';
- break;
- case 'v':
- /* assume ASCII here as well */
- wc = 11;
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- if (!cstyle)
- goto unknown_escape;
- /* FALLTHROUGH */
- case '0':
- if (cstyle)
- (*fp)(fc);
- /*
- * look for an octal number with up to three
- * digits, not counting the leading zero;
- * convert it to a raw octet
- */
- wc = 0;
- i = 3;
- while (i--)
- if ((c = (*fg)()) >= '0' && c <= '7')
- wc = (wc << 3) + (c - '0');
- else {
- (*fp)(c);
- break;
- }
- break;
- case 'U':
- i = 8;
- if (0)
- /* FALLTHROUGH */
- case 'u':
- i = 4;
- if (0)
- /* FALLTHROUGH */
- case 'x':
- i = cstyle ? -1 : 2;
- /*
- * x: look for a hexadecimal number with up to
- * two (C style: arbitrary) digits; convert
- * to raw octet (C style: Unicode if >0xFF)
- * u/U: look for a hexadecimal number with up to
- * four (U: eight) digits; convert to Unicode
- */
- wc = 0;
- while (i--) {
- wc <<= 4;
- if ((c = (*fg)()) >= '0' && c <= '9')
- wc += c - '0';
- else if (c >= 'A' && c <= 'F')
- wc += c - 'A' + 10;
- else if (c >= 'a' && c <= 'f')
- wc += c - 'a' + 10;
- else {
- wc >>= 4;
- (*fp)(c);
- break;
- }
- }
- if ((cstyle && wc > 0xFF) || fc != 'x')
- /* Unicode marker */
- wc += 0x100;
- break;
- case '\'':
- if (!cstyle)
- goto unknown_escape;
- wc = '\'';
- break;
- case '\\':
- wc = '\\';
- break;
- default:
- unknown_escape:
- (*fp)(fc);
- return (-1);
- }
-
- return (wc);
-}
diff --git a/mksh/src/sh.h b/mksh/src/sh.h
deleted file mode 100644
index 11588c9..0000000
--- a/mksh/src/sh.h
+++ /dev/null
@@ -1,1752 +0,0 @@
-/* $OpenBSD: sh.h,v 1.30 2010/01/04 18:07:11 deraadt Exp $ */
-/* $OpenBSD: shf.h,v 1.6 2005/12/11 18:53:51 deraadt Exp $ */
-/* $OpenBSD: table.h,v 1.7 2005/12/11 20:31:21 otto Exp $ */
-/* $OpenBSD: tree.h,v 1.10 2005/03/28 21:28:22 deraadt Exp $ */
-/* $OpenBSD: expand.h,v 1.6 2005/03/30 17:16:37 deraadt Exp $ */
-/* $OpenBSD: lex.h,v 1.11 2006/05/29 18:22:24 otto Exp $ */
-/* $OpenBSD: proto.h,v 1.33 2010/05/19 17:36:08 jasper Exp $ */
-/* $OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $ */
-/* $OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#ifdef __dietlibc__
-/* XXX imake style */
-#define _BSD_SOURCE /* live, BSD, live! */
-#endif
-
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/ioctl.h>
-#if HAVE_SYS_SYSMACROS_H
-#include <sys/sysmacros.h>
-#endif
-#if HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-#if HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#if HAVE_LIBGEN_H
-#include <libgen.h>
-#endif
-#if HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-#include <limits.h>
-#if HAVE_PATHS_H
-#include <paths.h>
-#endif
-#include <pwd.h>
-#include <setjmp.h>
-#include <signal.h>
-#include <stdarg.h>
-#if HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif
-#include <stddef.h>
-#if HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#if HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#include <termios.h>
-#include <time.h>
-#if HAVE_ULIMIT_H
-#include <ulimit.h>
-#endif
-#include <unistd.h>
-#if HAVE_VALUES_H
-#include <values.h>
-#endif
-
-#undef __attribute__
-#if HAVE_ATTRIBUTE_BOUNDED
-#define MKSH_A_BOUNDED(x,y,z) __attribute__((bounded (x, y, z)))
-#else
-#define MKSH_A_BOUNDED(x,y,z) /* nothing */
-#endif
-#if HAVE_ATTRIBUTE_FORMAT
-#define MKSH_A_FORMAT(x,y,z) __attribute__((format (x, y, z)))
-#else
-#define MKSH_A_FORMAT(x,y,z) /* nothing */
-#endif
-#if HAVE_ATTRIBUTE_NONNULL
-#define MKSH_A_NONNULL(a) __attribute__(a)
-#else
-#define MKSH_A_NONNULL(a) /* nothing */
-#endif
-#if HAVE_ATTRIBUTE_NORETURN
-#define MKSH_A_NORETURN __attribute__((noreturn))
-#else
-#define MKSH_A_NORETURN /* nothing */
-#endif
-#if HAVE_ATTRIBUTE_UNUSED
-#define MKSH_A_UNUSED __attribute__((unused))
-#else
-#define MKSH_A_UNUSED /* nothing */
-#endif
-#if HAVE_ATTRIBUTE_USED
-#define MKSH_A_USED __attribute__((used))
-#else
-#define MKSH_A_USED /* nothing */
-#endif
-
-#if defined(MirBSD) && (MirBSD >= 0x09A1) && \
- defined(__ELF__) && defined(__GNUC__) && \
- !defined(__llvm__) && !defined(__NWCC__)
-/*
- * We got usable __IDSTRING __COPYRIGHT __RCSID __SCCSID macros
- * which work for all cases; no need to redefine them using the
- * "portable" macros from below when we might have the "better"
- * gcc+ELF specific macros or other system dependent ones.
- */
-#else
-#undef __IDSTRING
-#undef __IDSTRING_CONCAT
-#undef __IDSTRING_EXPAND
-#undef __COPYRIGHT
-#undef __RCSID
-#undef __SCCSID
-#define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p
-#define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p)
-#define __IDSTRING(prefix, string) \
- static const char __IDSTRING_EXPAND(__LINE__,prefix) [] \
- MKSH_A_USED = "@(""#)" #prefix ": " string
-#define __COPYRIGHT(x) __IDSTRING(copyright,x)
-#define __RCSID(x) __IDSTRING(rcsid,x)
-#define __SCCSID(x) __IDSTRING(sccsid,x)
-#endif
-
-#ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.405 2010/08/24 15:19:54 tg Exp $");
-#endif
-#define MKSH_VERSION "R39 2010/08/24"
-
-#ifndef MKSH_INCLUDES_ONLY
-
-/* extra types */
-
-#if !HAVE_GETRUSAGE
-#undef rusage
-#undef RUSAGE_SELF
-#undef RUSAGE_CHILDREN
-#define rusage mksh_rusage
-#define RUSAGE_SELF 0
-#define RUSAGE_CHILDREN -1
-
-struct rusage {
- struct timeval ru_utime;
- struct timeval ru_stime;
-};
-#endif
-
-#if !HAVE_RLIM_T
-typedef long rlim_t;
-#endif
-
-#if !HAVE_SIG_T
-#undef sig_t
-typedef void (*sig_t)(int);
-#endif
-
-#if !HAVE_STDBOOL_H
-/* kludge, but enough for mksh */
-typedef int bool;
-#define false 0
-#define true 1
-#endif
-
-#if !HAVE_CAN_INTTYPES
-#if !HAVE_CAN_UCBINTS
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-#else
-typedef u_int32_t uint32_t;
-#endif
-#endif
-
-#if !HAVE_CAN_INT8TYPE
-#if !HAVE_CAN_UCBINT8
-typedef unsigned char uint8_t;
-#else
-typedef u_int8_t uint8_t;
-#endif
-#endif
-
-/* extra macros */
-
-#ifndef timerclear
-#define timerclear(tvp) \
- do { \
- (tvp)->tv_sec = (tvp)->tv_usec = 0; \
- } while (/* CONSTCOND */ 0)
-#endif
-#ifndef timeradd
-#define timeradd(tvp, uvp, vvp) \
- do { \
- (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
- (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
- if ((vvp)->tv_usec >= 1000000) { \
- (vvp)->tv_sec++; \
- (vvp)->tv_usec -= 1000000; \
- } \
- } while (/* CONSTCOND */ 0)
-#endif
-#ifndef timersub
-#define timersub(tvp, uvp, vvp) \
- do { \
- (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
- (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
- if ((vvp)->tv_usec < 0) { \
- (vvp)->tv_sec--; \
- (vvp)->tv_usec += 1000000; \
- } \
- } while (/* CONSTCOND */ 0)
-#endif
-
-#define ksh_isdigit(c) (((c) >= '0') && ((c) <= '9'))
-#define ksh_islower(c) (((c) >= 'a') && ((c) <= 'z'))
-#define ksh_isupper(c) (((c) >= 'A') && ((c) <= 'Z'))
-#define ksh_tolower(c) (((c) >= 'A') && ((c) <= 'Z') ? (c) - 'A' + 'a' : (c))
-#define ksh_toupper(c) (((c) >= 'a') && ((c) <= 'z') ? (c) - 'a' + 'A' : (c))
-#define ksh_isdash(s) (((s) != NULL) && ((s)[0] == '-') && ((s)[1] == '\0'))
-#define ksh_isspace(c) ((((c) >= 0x09) && ((c) <= 0x0D)) || ((c) == 0x20))
-
-#ifdef NO_PATH_MAX
-#undef PATH_MAX
-#else
-#ifndef PATH_MAX
-#define PATH_MAX 1024
-#endif
-#endif
-#ifndef SIZE_MAX
-#ifdef SIZE_T_MAX
-#define SIZE_MAX SIZE_T_MAX
-#else
-#define SIZE_MAX ((size_t)-1)
-#endif
-#endif
-#ifndef S_ISLNK
-#define S_ISLNK(m) ((m & 0170000) == 0120000)
-#endif
-#ifndef S_ISSOCK
-#define S_ISSOCK(m) ((m & 0170000) == 0140000)
-#endif
-#ifndef DEFFILEMODE
-#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
-#endif
-
-#if !defined(MAP_FAILED)
-/* XXX imake style */
-# if defined(__linux)
-#define MAP_FAILED ((void *)-1)
-# elif defined(__bsdi__) || defined(__osf__) || defined(__ultrix)
-#define MAP_FAILED ((caddr_t)-1)
-# endif
-#endif
-
-#ifndef NSIG
-#if defined(_NSIG)
-#define NSIG _NSIG
-#elif defined(SIGMAX)
-#define NSIG (SIGMAX+1)
-#endif
-#endif
-
-#undef BAD /* AIX defines that somewhere */
-
-/* OS-dependent additions (functions, variables, by OS) */
-
-#if !HAVE_FLOCK_DECL
-extern int flock(int, int);
-#endif
-
-#if !HAVE_GETRUSAGE
-extern int getrusage(int, struct rusage *);
-#endif
-
-#if !HAVE_REVOKE_DECL
-extern int revoke(const char *);
-#endif
-
-#if !HAVE_SETMODE
-mode_t getmode(const void *, mode_t);
-void *setmode(const char *);
-#endif
-
-#ifdef __ultrix
-/* XXX imake style */
-int strcasecmp(const char *, const char *);
-#endif
-
-#if !HAVE_STRCASESTR
-const char *stristr(const char *, const char *);
-#endif
-
-#if !HAVE_STRLCPY
-size_t strlcpy(char *, const char *, size_t);
-#endif
-
-#if !HAVE_SYS_SIGLIST_DECL
-extern const char *const sys_siglist[];
-#endif
-
-#ifdef __INTERIX
-/* XXX imake style */
-#define makedev mkdev
-extern int __cdecl seteuid(uid_t);
-extern int __cdecl setegid(gid_t);
-#endif
-
-/* remove redundances */
-
-#if defined(MirBSD) && (MirBSD >= 0x08A8)
-#define MKSH_mirbsd_wcwidth
-#define utf_wcwidth(i) wcwidth((__WCHAR_TYPE__)i)
-extern int wcwidth(__WCHAR_TYPE__);
-#endif
-
-
-/* some useful #defines */
-#ifdef EXTERN
-# define I__(i) = i
-#else
-# define I__(i)
-# define EXTERN extern
-# define EXTERN_DEFINED
-#endif
-
-#define NELEM(a) (sizeof(a) / sizeof((a)[0]))
-#define BIT(i) (1 << (i)) /* define bit in flag */
-
-/* Table flag type - needs > 16 and < 32 bits */
-typedef int32_t Tflag;
-
-/* arithmetics types */
-typedef int32_t mksh_ari_t;
-typedef uint32_t mksh_uari_t;
-
-/* these shall be smaller than 100 */
-#ifdef MKSH_CONSERVATIVE_FDS
-#define NUFILE 32 /* Number of user-accessible files */
-#define FDBASE 10 /* First file usable by Shell */
-#else
-#define NUFILE 56 /* Number of user-accessible files */
-#define FDBASE 24 /* First file usable by Shell */
-#endif
-
-/* Make MAGIC a char that might be printed to make bugs more obvious, but
- * not a char that is used often. Also, can't use the high bit as it causes
- * portability problems (calling strchr(x, 0x80|'x') is error prone).
- */
-#define MAGIC (7) /* prefix for *?[!{,} during expand */
-#define ISMAGIC(c) ((unsigned char)(c) == MAGIC)
-#define NOT '!' /* might use ^ (ie, [!...] vs [^..]) */
-
-#define LINE 4096 /* input line size */
-
-EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
-EXTERN const char initvsn[] I__("KSH_VERSION=@(#)MIRBSD KSH " MKSH_VERSION);
-#define KSH_VERSION (initvsn + /* "KSH_VERSION=@(#)" */ 16)
-
-EXTERN const char digits_uc[] I__("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-EXTERN const char digits_lc[] I__("0123456789abcdefghijklmnopqrstuvwxyz");
-
-/*
- * Evil hack for const correctness due to API brokenness
- */
-union mksh_cchack {
- char *rw;
- const char *ro;
-};
-union mksh_ccphack {
- char **rw;
- const char **ro;
-};
-
-/* for const debugging */
-#if defined(DEBUG) && defined(__GNUC__) && !defined(__ICC) && \
- !defined(__INTEL_COMPILER) && !defined(__SUNPRO_C)
-char *ucstrchr(char *, int);
-char *ucstrstr(char *, const char *);
-#undef strchr
-#define strchr ucstrchr
-#define strstr ucstrstr
-#define cstrchr(s,c) ({ \
- union mksh_cchack in, out; \
- \
- in.ro = (s); \
- out.rw = ucstrchr(in.rw, (c)); \
- (out.ro); \
-})
-#define cstrstr(b,l) ({ \
- union mksh_cchack in, out; \
- \
- in.ro = (b); \
- out.rw = ucstrstr(in.rw, (l)); \
- (out.ro); \
-})
-#define vstrchr(s,c) (cstrchr((s), (c)) != NULL)
-#define vstrstr(b,l) (cstrstr((b), (l)) != NULL)
-#define mkssert(e) ((e) ? (void)0 : exit(255))
-#else /* !DEBUG, !gcc */
-#define cstrchr(s,c) ((const char *)strchr((s), (c)))
-#define cstrstr(s,c) ((const char *)strstr((s), (c)))
-#define vstrchr(s,c) (strchr((s), (c)) != NULL)
-#define vstrstr(b,l) (strstr((b), (l)) != NULL)
-#define mkssert(e) ((void)0)
-#endif
-
-/* use this ipv strchr(s, 0) but no side effects in s! */
-#define strnul(s) ((s) + strlen(s))
-
-#define utf_ptradjx(src, dst) do { \
- (dst) = (src) + utf_ptradj(src); \
-} while (/* CONSTCOND */ 0)
-
-#ifdef MKSH_SMALL
-#define strdupx(d, s, ap) do { \
- (d) = strdup_((s), (ap)); \
-} while (/* CONSTCOND */ 0)
-#define strndupx(d, s, n, ap) do { \
- (d) = strndup_((s), (n), (ap)); \
-} while (/* CONSTCOND */ 0)
-#else
-/* be careful to evaluate arguments only once! */
-#define strdupx(d, s, ap) do { \
- const char *strdup_src = (s); \
- char *strdup_dst = NULL; \
- \
- if (strdup_src != NULL) { \
- size_t strdup_len = strlen(strdup_src) + 1; \
- strdup_dst = alloc(strdup_len, (ap)); \
- memcpy(strdup_dst, strdup_src, strdup_len); \
- } \
- (d) = strdup_dst; \
-} while (/* CONSTCOND */ 0)
-#define strndupx(d, s, n, ap) do { \
- const char *strdup_src = (s); \
- char *strdup_dst = NULL; \
- \
- if (strdup_src != NULL) { \
- size_t strndup_len = (n); \
- strdup_dst = alloc(strndup_len + 1, (ap)); \
- memcpy(strdup_dst, strdup_src, strndup_len); \
- strdup_dst[strndup_len] = '\0'; \
- } \
- (d) = strdup_dst; \
-} while (/* CONSTCOND */ 0)
-#endif
-
-#if HAVE_STRCASESTR
-#define stristr(b,l) ((const char *)strcasestr((b), (l)))
-#endif
-
-#ifdef MKSH_SMALL
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS /* defined */
-#endif
-#ifndef MKSH_NOPWNAM
-#define MKSH_NOPWNAM /* defined */
-#endif
-#ifndef MKSH_S_NOVI
-#define MKSH_S_NOVI 1
-#endif
-#endif
-
-#ifndef MKSH_S_NOVI
-#define MKSH_S_NOVI 0
-#endif
-
-/*
- * simple grouping allocator
- */
-
-/* 1. internal structure */
-struct lalloc {
- struct lalloc *next;
-};
-
-/* 2. sizes */
-#define ALLOC_ITEM struct lalloc
-#define ALLOC_SIZE (sizeof(ALLOC_ITEM))
-
-/* 3. group structure (only the same for lalloc.c) */
-typedef struct lalloc Area;
-
-
-EXTERN Area aperm; /* permanent object space */
-#define APERM &aperm
-#define ATEMP &e->area
-
-/*
- * flags (the order of these enums MUST match the order in misc.c(options[]))
- */
-enum sh_flag {
-#define SHFLAGS_ENUMS
-#include "sh_flags.h"
- FNFLAGS /* (place holder: how many flags are there) */
-};
-
-#define Flag(f) (kshstate_v.shell_flags_[(int)(f)])
-#define UTFMODE Flag(FUNICODE)
-
-/*
- * parsing & execution environment
- */
-extern struct env {
- ALLOC_ITEM __alloc_i; /* internal, do not touch */
- Area area; /* temporary allocation area */
- struct env *oenv; /* link to previous environment */
- struct block *loc; /* local variables and functions */
- short *savefd; /* original redirected fds */
- struct temp *temps; /* temp files */
- sigjmp_buf jbuf; /* long jump back to env creator */
- short type; /* environment type - see below */
- short flags; /* EF_* */
-} *e;
-
-/* struct env.type values */
-#define E_NONE 0 /* dummy environment */
-#define E_PARSE 1 /* parsing command # */
-#define E_FUNC 2 /* executing function # */
-#define E_INCL 3 /* including a file via . # */
-#define E_EXEC 4 /* executing command tree */
-#define E_LOOP 5 /* executing for/while # */
-#define E_ERRH 6 /* general error handler # */
-/* # indicates env has valid jbuf (see unwind()) */
-
-/* struct env.flag values */
-#define EF_FUNC_PARSE BIT(0) /* function being parsed */
-#define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */
-#define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */
-
-/* Do breaks/continues stop at env type e? */
-#define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE \
- || (t) == E_FUNC || (t) == E_INCL)
-/* Do returns stop at env type e? */
-#define STOP_RETURN(t) ((t) == E_FUNC || (t) == E_INCL)
-
-/* values for siglongjmp(e->jbuf, 0) */
-#define LRETURN 1 /* return statement */
-#define LEXIT 2 /* exit statement */
-#define LERROR 3 /* errorf() called */
-#define LLEAVE 4 /* untrappable exit/error */
-#define LINTR 5 /* ^C noticed */
-#define LBREAK 6 /* break statement */
-#define LCONTIN 7 /* continue statement */
-#define LSHELL 8 /* return to interactive shell() */
-#define LAEXPR 9 /* error in arithmetic expression */
-
-/*
- * some kind of global shell state, for change_random() mostly
- */
-
-EXTERN struct mksh_kshstate_v {
- /* for change_random */
- struct timeval cr_tv; /* timestamp */
- const void *cr_dp; /* argument address */
- size_t cr_dsz; /* argument length */
- uint32_t lcg_state_; /* previous LCG state */
- /* global state */
- pid_t procpid_; /* PID of executing process */
- int exstat_; /* exit status */
- int subst_exstat_; /* exit status of last $(..)/`..` */
- struct env env_; /* top-level parsing & execution env. */
- uint8_t shell_flags_[FNFLAGS];
-} kshstate_v;
-EXTERN struct mksh_kshstate_f {
- const char *kshname_; /* $0 */
- pid_t kshpid_; /* $$, shell PID */
- pid_t kshpgrp_; /* process group of shell */
- uid_t ksheuid_; /* effective UID of shell */
- pid_t kshppid_; /* PID of parent of shell */
- uint32_t h; /* some kind of hash */
-} kshstate_f;
-#define kshname kshstate_f.kshname_
-#define kshpid kshstate_f.kshpid_
-#define procpid kshstate_v.procpid_
-#define kshpgrp kshstate_f.kshpgrp_
-#define ksheuid kshstate_f.ksheuid_
-#define kshppid kshstate_f.kshppid_
-#define exstat kshstate_v.exstat_
-#define subst_exstat kshstate_v.subst_exstat_
-
-/* evil hack: return hash(kshstate_f concat (kshstate_f'.h:=hash(arg))) */
-uint32_t evilhash(const char *);
-
-
-/* option processing */
-#define OF_CMDLINE 0x01 /* command line */
-#define OF_SET 0x02 /* set builtin */
-#define OF_SPECIAL 0x04 /* a special variable changing */
-#define OF_INTERNAL 0x08 /* set internally by shell */
-#define OF_FIRSTTIME 0x10 /* as early as possible, once */
-#define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL)
-
-struct shoption {
- const char *name; /* long name of option */
- char c; /* character flag (if any) */
- unsigned char flags; /* OF_* */
-};
-extern const struct shoption options[];
-
-/* null value for variable; comparision pointer for unset */
-EXTERN char null[] I__("");
-/* helpers for string pooling */
-#define T_synerr "syntax error"
-EXTERN const char r_fc_e_[] I__("r=fc -e -");
-#define fc_e_ (r_fc_e_ + 2) /* "fc -e -" */
-#define fc_e_n 7 /* strlen(fc_e_) */
-EXTERN const char T_local_typeset[] I__("local=typeset");
-#define T__typeset (T_local_typeset + 5) /* "=typeset" */
-#define T_typeset (T_local_typeset + 6) /* "typeset" */
-
-enum temp_type {
- TT_HEREDOC_EXP, /* expanded heredoc */
- TT_HIST_EDIT /* temp file used for history editing (fc -e) */
-};
-typedef enum temp_type Temp_type;
-/* temp/heredoc files. The file is removed when the struct is freed. */
-struct temp {
- struct temp *next;
- struct shf *shf;
- char *name;
- int pid; /* pid of process parsed here-doc */
- Temp_type type;
-};
-
-/*
- * stdio and our IO routines
- */
-
-#define shl_spare (&shf_iob[0]) /* for c_read()/c_print() */
-#define shl_stdout (&shf_iob[1])
-#define shl_out (&shf_iob[2])
-EXTERN int shl_stdout_ok;
-
-/*
- * trap handlers
- */
-typedef struct trap {
- const char *name; /* short name */
- const char *mess; /* descriptive name */
- char *trap; /* trap command */
- sig_t cursig; /* current handler (valid if TF_ORIG_* set) */
- sig_t shtrap; /* shell signal handler */
- int signal; /* signal number */
- int flags; /* TF_* */
- volatile sig_atomic_t set; /* trap pending */
-} Trap;
-
-/* values for Trap.flags */
-#define TF_SHELL_USES BIT(0) /* shell uses signal, user can't change */
-#define TF_USER_SET BIT(1) /* user has (tried to) set trap */
-#define TF_ORIG_IGN BIT(2) /* original action was SIG_IGN */
-#define TF_ORIG_DFL BIT(3) /* original action was SIG_DFL */
-#define TF_EXEC_IGN BIT(4) /* restore SIG_IGN just before exec */
-#define TF_EXEC_DFL BIT(5) /* restore SIG_DFL just before exec */
-#define TF_DFL_INTR BIT(6) /* when received, default action is LINTR */
-#define TF_TTY_INTR BIT(7) /* tty generated signal (see j_waitj) */
-#define TF_CHANGED BIT(8) /* used by runtrap() to detect trap changes */
-#define TF_FATAL BIT(9) /* causes termination if not trapped */
-
-/* values for setsig()/setexecsig() flags argument */
-#define SS_RESTORE_MASK 0x3 /* how to restore a signal before an exec() */
-#define SS_RESTORE_CURR 0 /* leave current handler in place */
-#define SS_RESTORE_ORIG 1 /* restore original handler */
-#define SS_RESTORE_DFL 2 /* restore to SIG_DFL */
-#define SS_RESTORE_IGN 3 /* restore to SIG_IGN */
-#define SS_FORCE BIT(3) /* set signal even if original signal ignored */
-#define SS_USER BIT(4) /* user is doing the set (ie, trap command) */
-#define SS_SHTRAP BIT(5) /* trap for internal use (ALRM, CHLD, WINCH) */
-
-#define SIGEXIT_ 0 /* for trap EXIT */
-#define SIGERR_ NSIG /* for trap ERR */
-
-EXTERN volatile sig_atomic_t trap; /* traps pending? */
-EXTERN volatile sig_atomic_t intrsig; /* pending trap interrupts command */
-EXTERN volatile sig_atomic_t fatal_trap;/* received a fatal signal */
-extern Trap sigtraps[NSIG+1];
-
-/* got_winch = 1 when we need to re-adjust the window size */
-#ifdef SIGWINCH
-EXTERN volatile sig_atomic_t got_winch I__(1);
-#else
-#define got_winch true
-#endif
-
-/*
- * TMOUT support
- */
-/* values for ksh_tmout_state */
-enum tmout_enum {
- TMOUT_EXECUTING = 0, /* executing commands */
- TMOUT_READING, /* waiting for input */
- TMOUT_LEAVING /* have timed out */
-};
-EXTERN unsigned int ksh_tmout;
-EXTERN enum tmout_enum ksh_tmout_state I__(TMOUT_EXECUTING);
-
-/* For "You have stopped jobs" message */
-EXTERN int really_exit;
-
-/*
- * fast character classes
- */
-#define C_ALPHA BIT(0) /* a-z_A-Z */
-#define C_DIGIT BIT(1) /* 0-9 */
-#define C_LEX1 BIT(2) /* \t \n\0|&;<>() */
-#define C_VAR1 BIT(3) /* *@#!$-? */
-#define C_IFSWS BIT(4) /* \t \n (IFS white space) */
-#define C_SUBOP1 BIT(5) /* "=-+?" */
-#define C_QUOTE BIT(6) /* \t\n "#$&'()*;<=>?[\]`| (needing quoting) */
-#define C_IFS BIT(7) /* $IFS */
-#define C_SUBOP2 BIT(8) /* "#%" (magic, see below) */
-
-extern unsigned char chtypes[];
-
-#define ctype(c, t) !!( ((t) == C_SUBOP2) ? \
- (((c) == '#' || (c) == '%') ? 1 : 0) : \
- (chtypes[(unsigned char)(c)]&(t)) )
-#define ksh_isalphx(c) ctype((c), C_ALPHA)
-#define ksh_isalnux(c) ctype((c), C_ALPHA | C_DIGIT)
-
-EXTERN int ifs0 I__(' '); /* for "$*" */
-
-/* Argument parsing for built-in commands and getopts command */
-
-/* Values for Getopt.flags */
-#define GF_ERROR BIT(0) /* call errorf() if there is an error */
-#define GF_PLUSOPT BIT(1) /* allow +c as an option */
-#define GF_NONAME BIT(2) /* don't print argv[0] in errors */
-
-/* Values for Getopt.info */
-#define GI_MINUS BIT(0) /* an option started with -... */
-#define GI_PLUS BIT(1) /* an option started with +... */
-#define GI_MINUSMINUS BIT(2) /* arguments were ended with -- */
-
-typedef struct {
- const char *optarg;
- int optind;
- int uoptind;/* what user sees in $OPTIND */
- int flags; /* see GF_* */
- int info; /* see GI_* */
- unsigned int p; /* 0 or index into argv[optind - 1] */
- char buf[2]; /* for bad option OPTARG value */
-} Getopt;
-
-EXTERN Getopt builtin_opt; /* for shell builtin commands */
-EXTERN Getopt user_opt; /* parsing state for getopts builtin command */
-
-/* This for co-processes */
-
-typedef int32_t Coproc_id; /* something that won't (realisticly) wrap */
-struct coproc {
- void *job; /* 0 or job of co-process using input pipe */
- int read; /* pipe from co-process's stdout */
- int readw; /* other side of read (saved temporarily) */
- int write; /* pipe to co-process's stdin */
- int njobs; /* number of live jobs using output pipe */
- Coproc_id id; /* id of current output pipe */
-};
-EXTERN struct coproc coproc;
-
-/* Used in jobs.c and by coprocess stuff in exec.c */
-EXTERN sigset_t sm_default, sm_sigchld;
-
-/* name of called builtin function (used by error functions) */
-EXTERN const char *builtin_argv0;
-EXTERN Tflag builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */
-
-/* current working directory, and size of memory allocated for same */
-EXTERN char *current_wd;
-EXTERN size_t current_wd_size;
-
-/* Minimum required space to work with on a line - if the prompt leaves less
- * space than this on a line, the prompt is truncated.
- */
-#define MIN_EDIT_SPACE 7
-/* Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
- */
-#define MIN_COLS (2 + MIN_EDIT_SPACE + 3)
-#define MIN_LINS 3
-EXTERN mksh_ari_t x_cols I__(80); /* tty columns */
-EXTERN mksh_ari_t x_lins I__(-1); /* tty lines */
-
-/* These to avoid bracket matching problems */
-#define OPAREN '('
-#define CPAREN ')'
-#define OBRACK '['
-#define CBRACK ']'
-#define OBRACE '{'
-#define CBRACE '}'
-
-/* Determine the location of the system (common) profile */
-#define KSH_SYSTEM_PROFILE "/etc/profile"
-
-/* Used by v_evaluate() and setstr() to control action when error occurs */
-#define KSH_UNWIND_ERROR 0 /* unwind the stack (longjmp) */
-#define KSH_RETURN_ERROR 1 /* return 1/0 for success/failure */
-
-/*
- * Shell file I/O routines
- */
-
-#define SHF_BSIZE 512
-
-#define shf_fileno(shf) ((shf)->fd)
-#define shf_setfileno(shf,nfd) ((shf)->fd = (nfd))
-#ifdef MKSH_SMALL
-int shf_getc(struct shf *);
-int shf_putc(int, struct shf *);
-#else
-#define shf_getc(shf) ((shf)->rnleft > 0 ? \
- (shf)->rnleft--, *(shf)->rp++ : \
- shf_getchar(shf))
-#define shf_putc(c, shf) ((shf)->wnleft == 0 ? \
- shf_putchar((c), (shf)) : \
- ((shf)->wnleft--, *(shf)->wp++ = (c)))
-#endif
-#define shf_eof(shf) ((shf)->flags & SHF_EOF)
-#define shf_error(shf) ((shf)->flags & SHF_ERROR)
-#define shf_errno(shf) ((shf)->errno_)
-#define shf_clearerr(shf) ((shf)->flags &= ~(SHF_EOF | SHF_ERROR))
-
-/* Flags passed to shf_*open() */
-#define SHF_RD 0x0001
-#define SHF_WR 0x0002
-#define SHF_RDWR (SHF_RD|SHF_WR)
-#define SHF_ACCMODE 0x0003 /* mask */
-#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
-#define SHF_UNBUF 0x0008 /* unbuffered I/O */
-#define SHF_CLEXEC 0x0010 /* set close on exec flag */
-#define SHF_MAPHI 0x0020 /* make fd > FDBASE (and close orig)
- * (shf_open() only) */
-#define SHF_DYNAMIC 0x0040 /* string: increase buffer as needed */
-#define SHF_INTERRUPT 0x0080 /* EINTR in read/write causes error */
-/* Flags used internally */
-#define SHF_STRING 0x0100 /* a string, not a file */
-#define SHF_ALLOCS 0x0200 /* shf and shf->buf were alloc()ed */
-#define SHF_ALLOCB 0x0400 /* shf->buf was alloc()ed */
-#define SHF_ERROR 0x0800 /* read()/write() error */
-#define SHF_EOF 0x1000 /* read eof (sticky) */
-#define SHF_READING 0x2000 /* currently reading: rnleft,rp valid */
-#define SHF_WRITING 0x4000 /* currently writing: wnleft,wp valid */
-
-
-struct shf {
- Area *areap; /* area shf/buf were allocated in */
- unsigned char *rp; /* read: current position in buffer */
- unsigned char *wp; /* write: current position in buffer */
- unsigned char *buf; /* buffer */
- int flags; /* see SHF_* */
- int rbsize; /* size of buffer (1 if SHF_UNBUF) */
- int rnleft; /* read: how much data left in buffer */
- int wbsize; /* size of buffer (0 if SHF_UNBUF) */
- int wnleft; /* write: how much space left in buffer */
- int fd; /* file descriptor */
- int errno_; /* saved value of errno after error */
- int bsize; /* actual size of buf */
-};
-
-extern struct shf shf_iob[];
-
-struct table {
- Area *areap; /* area to allocate entries */
- struct tbl **tbls; /* hashed table items */
- short size, nfree; /* hash size (always 2^^n), free entries */
-};
-
-struct tbl { /* table item */
- Area *areap; /* area to allocate from */
- union {
- char *s; /* string */
- mksh_ari_t i; /* integer */
- mksh_uari_t u; /* unsigned integer */
- int (*f)(const char **);/* int function */
- struct op *t; /* "function" tree */
- } val; /* value */
- union {
- struct tbl *array; /* array values */
- const char *fpath; /* temporary path to undef function */
- } u;
- union {
- int field; /* field with for -L/-R/-Z */
- int errno_; /* CEXEC/CTALIAS */
- } u2;
- int type; /* command type (see below), base (if INTEGER),
- * or offset from val.s of value (if EXPORT) */
- Tflag flag; /* flags */
- union {
- uint32_t hval; /* hash(name) */
- uint32_t index; /* index for an array */
- } ua;
- char name[4]; /* name -- variable length */
-};
-
-/* common flag bits */
-#define ALLOC BIT(0) /* val.s has been allocated */
-#define DEFINED BIT(1) /* is defined in block */
-#define ISSET BIT(2) /* has value, vp->val.[si] */
-#define EXPORT BIT(3) /* exported variable/function */
-#define TRACE BIT(4) /* var: user flagged, func: execution tracing */
-/* (start non-common flags at 8) */
-/* flag bits used for variables */
-#define SPECIAL BIT(8) /* PATH, IFS, SECONDS, etc */
-#define INTEGER BIT(9) /* val.i contains integer value */
-#define RDONLY BIT(10) /* read-only variable */
-#define LOCAL BIT(11) /* for local typeset() */
-#define ARRAY BIT(13) /* array */
-#define LJUST BIT(14) /* left justify */
-#define RJUST BIT(15) /* right justify */
-#define ZEROFIL BIT(16) /* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */
-#define LCASEV BIT(17) /* convert to lower case */
-#define UCASEV_AL BIT(18) /* convert to upper case / autoload function */
-#define INT_U BIT(19) /* unsigned integer */
-#define INT_L BIT(20) /* long integer (no-op) */
-#define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */
-#define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */
-#define EXPRINEVAL BIT(23) /* contents currently being evaluated */
-#define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */
-#define AINDEX BIT(25) /* array index >0 = ua.index filled in */
-#define ASSOC BIT(26) /* ARRAY ? associative : reference */
-/* flag bits used for taliases/builtins/aliases/keywords/functions */
-#define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */
-#define FINUSE BIT(9) /* function being executed */
-#define FDELETE BIT(10) /* function deleted while it was executing */
-#define FKSH BIT(11) /* function defined with function x (vs x()) */
-#define SPEC_BI BIT(12) /* a POSIX special builtin */
-#define REG_BI BIT(13) /* a POSIX regular builtin */
-/* Attributes that can be set by the user (used to decide if an unset param
- * should be repoted by set/typeset). Does not include ARRAY or LOCAL.
- */
-#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
- LCASEV|UCASEV_AL|INT_U|INT_L)
-
-#define arrayindex(vp) ((unsigned long)((vp)->flag & AINDEX ? \
- (vp)->ua.index : 0))
-
-/* command types */
-#define CNONE 0 /* undefined */
-#define CSHELL 1 /* built-in */
-#define CFUNC 2 /* function */
-#define CEXEC 4 /* executable command */
-#define CALIAS 5 /* alias */
-#define CKEYWD 6 /* keyword */
-#define CTALIAS 7 /* tracked alias */
-
-/* Flags for findcom()/comexec() */
-#define FC_SPECBI BIT(0) /* special builtin */
-#define FC_FUNC BIT(1) /* function builtin */
-#define FC_REGBI BIT(2) /* regular builtin */
-#define FC_UNREGBI BIT(3) /* un-regular builtin (!special,!regular) */
-#define FC_BI (FC_SPECBI|FC_REGBI|FC_UNREGBI)
-#define FC_PATH BIT(4) /* do path search */
-#define FC_DEFPATH BIT(5) /* use default path in path search */
-
-
-#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
-#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
-#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
-#define AI_ARGC(a) ((a).argc_ - (a).skip)
-
-/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
-struct arg_info {
- const char **argv;
- int flags; /* AF_* */
- int argc_;
- int skip; /* first arg is argv[0], second is argv[1 + skip] */
-};
-
-/*
- * activation record for function blocks
- */
-struct block {
- Area area; /* area to allocate things */
- const char **argv;
- char *error; /* error handler */
- char *exit; /* exit handler */
- struct block *next; /* enclosing block */
- struct table vars; /* local variables */
- struct table funs; /* local functions */
- Getopt getopts_state;
- int argc;
- int flags; /* see BF_* */
-};
-
-/* Values for struct block.flags */
-#define BF_DOGETOPTS BIT(0) /* save/restore getopts state */
-
-/*
- * Used by ktwalk() and ktnext() routines.
- */
-struct tstate {
- struct tbl **next;
- ssize_t left;
-};
-
-EXTERN struct table taliases; /* tracked aliases */
-EXTERN struct table builtins; /* built-in commands */
-EXTERN struct table aliases; /* aliases */
-EXTERN struct table keywords; /* keywords */
-#ifndef MKSH_NOPWNAM
-EXTERN struct table homedirs; /* homedir() cache */
-#endif
-
-struct builtin {
- const char *name;
- int (*func)(const char **);
-};
-
-extern const struct builtin mkshbuiltins[];
-
-/* values for set_prompt() */
-#define PS1 0 /* command */
-#define PS2 1 /* command continuation */
-
-EXTERN char *path; /* copy of either PATH or def_path */
-EXTERN const char *def_path; /* path to use if PATH not set */
-EXTERN char *tmpdir; /* TMPDIR value */
-EXTERN const char *prompt;
-EXTERN int cur_prompt; /* PS1 or PS2 */
-EXTERN int current_lineno; /* LINENO value */
-
-#define NOBLOCK ((struct op *)NULL)
-#define NOWORD ((char *)NULL)
-#define NOWORDS ((char **)NULL)
-
-/*
- * Description of a command or an operation on commands.
- */
-struct op {
- const char **args; /* arguments to a command */
- char **vars; /* variable assignments */
- struct ioword **ioact; /* IO actions (eg, < > >>) */
- struct op *left, *right; /* descendents */
- char *str; /* word for case; identifier for for,
- * select, and functions;
- * path to execute for TEXEC;
- * time hook for TCOM.
- */
- int lineno; /* TCOM/TFUNC: LINENO for this */
- short type; /* operation type, see below */
- union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
- short evalflags; /* TCOM: arg expansion eval() flags */
- short ksh_func; /* TFUNC: function x (vs x()) */
- } u;
-};
-
-/* Tree.type values */
-#define TEOF 0
-#define TCOM 1 /* command */
-#define TPAREN 2 /* (c-list) */
-#define TPIPE 3 /* a | b */
-#define TLIST 4 /* a ; b */
-#define TOR 5 /* || */
-#define TAND 6 /* && */
-#define TBANG 7 /* ! */
-#define TDBRACKET 8 /* [[ .. ]] */
-#define TFOR 9
-#define TSELECT 10
-#define TCASE 11
-#define TIF 12
-#define TWHILE 13
-#define TUNTIL 14
-#define TELIF 15
-#define TPAT 16 /* pattern in case */
-#define TBRACE 17 /* {c-list} */
-#define TASYNC 18 /* c & */
-#define TFUNCT 19 /* function name { command; } */
-#define TTIME 20 /* time pipeline */
-#define TEXEC 21 /* fork/exec eval'd TCOM */
-#define TCOPROC 22 /* coprocess |& */
-
-/*
- * prefix codes for words in command tree
- */
-#define EOS 0 /* end of string */
-#define CHAR 1 /* unquoted character */
-#define QCHAR 2 /* quoted character */
-#define COMSUB 3 /* $() substitution (0 terminated) */
-#define EXPRSUB 4 /* $(()) substitution (0 terminated) */
-#define OQUOTE 5 /* opening " or ' */
-#define CQUOTE 6 /* closing " or ' */
-#define OSUBST 7 /* opening ${ subst (followed by { or X) */
-#define CSUBST 8 /* closing } of above (followed by } or X) */
-#define OPAT 9 /* open pattern: *(, @(, etc. */
-#define SPAT 10 /* separate pattern: | */
-#define CPAT 11 /* close pattern: ) */
-#define ADELIM 12 /* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */
-
-/*
- * IO redirection
- */
-struct ioword {
- int unit; /* unit affected */
- int flag; /* action (below) */
- char *name; /* file name (unused if heredoc) */
- char *delim; /* delimiter for <<,<<- */
- char *heredoc;/* content of heredoc */
-};
-
-/* ioword.flag - type of redirection */
-#define IOTYPE 0xF /* type: bits 0:3 */
-#define IOREAD 0x1 /* < */
-#define IOWRITE 0x2 /* > */
-#define IORDWR 0x3 /* <>: todo */
-#define IOHERE 0x4 /* << (here file) */
-#define IOCAT 0x5 /* >> */
-#define IODUP 0x6 /* <&/>& */
-#define IOEVAL BIT(4) /* expand in << */
-#define IOSKIP BIT(5) /* <<-, skip ^\t* */
-#define IOCLOB BIT(6) /* >|, override -o noclobber */
-#define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */
-#define IONAMEXP BIT(8) /* name has been expanded */
-#define IOBASH BIT(9) /* &> etc. */
-
-/* execute/exchild flags */
-#define XEXEC BIT(0) /* execute without forking */
-#define XFORK BIT(1) /* fork before executing */
-#define XBGND BIT(2) /* command & */
-#define XPIPEI BIT(3) /* input is pipe */
-#define XPIPEO BIT(4) /* output is pipe */
-#define XPIPE (XPIPEI|XPIPEO) /* member of pipe */
-#define XXCOM BIT(5) /* `...` command */
-#define XPCLOSE BIT(6) /* exchild: close close_fd in parent */
-#define XCCLOSE BIT(7) /* exchild: close close_fd in child */
-#define XERROK BIT(8) /* non-zero exit ok (for set -e) */
-#define XCOPROC BIT(9) /* starting a co-process */
-#define XTIME BIT(10) /* timing TCOM command */
-
-/*
- * flags to control expansion of words (assumed by t->evalflags to fit
- * in a short)
- */
-#define DOBLANK BIT(0) /* perform blank interpretation */
-#define DOGLOB BIT(1) /* expand [?* */
-#define DOPAT BIT(2) /* quote *?[ */
-#define DOTILDE BIT(3) /* normal ~ expansion (first char) */
-#define DONTRUNCOMMAND BIT(4) /* do not run $(command) things */
-#define DOASNTILDE BIT(5) /* assignment ~ expansion (after =, :) */
-#define DOBRACE_ BIT(6) /* used by expand(): do brace expansion */
-#define DOMAGIC_ BIT(7) /* used by expand(): string contains MAGIC */
-#define DOTEMP_ BIT(8) /* ditto : in word part of ${..[%#=?]..} */
-#define DOVACHECK BIT(9) /* var assign check (for typeset, set, etc) */
-#define DOMARKDIRS BIT(10) /* force markdirs behaviour */
-
-/*
- * The arguments of [[ .. ]] expressions are kept in t->args[] and flags
- * indicating how the arguments have been munged are kept in t->vars[].
- * The contents of t->vars[] are stuffed strings (so they can be treated
- * like all other t->vars[]) in which the second character is the one that
- * is examined. The DB_* defines are the values for these second characters.
- */
-#define DB_NORM 1 /* normal argument */
-#define DB_OR 2 /* || -> -o conversion */
-#define DB_AND 3 /* && -> -a conversion */
-#define DB_BE 4 /* an inserted -BE */
-#define DB_PAT 5 /* a pattern argument */
-
-#define X_EXTRA 8 /* this many extra bytes in X string */
-
-typedef struct XString {
- char *end, *beg; /* end, begin of string */
- size_t len; /* length */
- Area *areap; /* area to allocate/free from */
-} XString;
-
-typedef char *XStringP;
-
-/* initialise expandable string */
-#define XinitN(xs, length, area) do { \
- (xs).len = (length); \
- (xs).areap = (area); \
- (xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \
- (xs).end = (xs).beg + (xs).len; \
-} while (/* CONSTCOND */ 0)
-#define Xinit(xs, xp, length, area) do { \
- XinitN((xs), (length), (area)); \
- (xp) = (xs).beg; \
-} while (/* CONSTCOND */ 0)
-
-/* stuff char into string */
-#define Xput(xs, xp, c) (*xp++ = (c))
-
-/* check if there are at least n bytes left */
-#define XcheckN(xs, xp, n) do { \
- int more = ((xp) + (n)) - (xs).end; \
- if (more > 0) \
- (xp) = Xcheck_grow_(&(xs), (xp), more); \
-} while (/* CONSTCOND */ 0)
-
-/* check for overflow, expand string */
-#define Xcheck(xs, xp) XcheckN((xs), (xp), 1)
-
-/* free string */
-#define Xfree(xs, xp) afree((xs).beg, (xs).areap)
-
-/* close, return string */
-#define Xclose(xs, xp) aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
-
-/* begin of string */
-#define Xstring(xs, xp) ((xs).beg)
-
-#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
-#define Xlength(xs, xp) ((xp) - (xs).beg)
-#define Xsize(xs, xp) ((xs).end - (xs).beg)
-#define Xsavepos(xs, xp) ((xp) - (xs).beg)
-#define Xrestpos(xs, xp, n) ((xs).beg + (n))
-
-char *Xcheck_grow_(XString *, const char *, unsigned int);
-
-/*
- * expandable vector of generic pointers
- */
-
-typedef struct XPtrV {
- void **cur; /* next avail pointer */
- void **beg, **end; /* begin, end of vector */
-} XPtrV;
-
-#define XPinit(x, n) do { \
- void **vp__; \
- vp__ = alloc((n) * sizeof(void *), ATEMP); \
- (x).cur = (x).beg = vp__; \
- (x).end = vp__ + (n); \
-} while (/* CONSTCOND */ 0)
-
-#define XPput(x, p) do { \
- if ((x).cur >= (x).end) { \
- size_t n = XPsize(x); \
- (x).beg = aresize((x).beg, \
- n * 2 * sizeof(void *), ATEMP); \
- (x).cur = (x).beg + n; \
- (x).end = (x).cur + n; \
- } \
- *(x).cur++ = (p); \
-} while (/* CONSTCOND */ 0)
-
-#define XPptrv(x) ((x).beg)
-#define XPsize(x) ((x).cur - (x).beg)
-#define XPclose(x) aresize((x).beg, XPsize(x) * sizeof(void *), ATEMP)
-#define XPfree(x) afree((x).beg, ATEMP)
-
-#define IDENT 64
-
-typedef struct source Source;
-struct source {
- const char *str; /* input pointer */
- const char *start; /* start of current buffer */
- union {
- const char **strv; /* string [] */
- struct shf *shf; /* shell file */
- struct tbl *tblp; /* alias (SF_HASALIAS) */
- char *freeme; /* also for SREREAD */
- } u;
- const char *file; /* input file name */
- int type; /* input type */
- int line; /* line number */
- int errline; /* line the error occurred on (0 if not set) */
- int flags; /* SF_* */
- Area *areap;
- Source *next; /* stacked source */
- XString xs; /* input buffer */
- char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and
- * alias (SALIAS) */
-};
-
-/* Source.type values */
-#define SEOF 0 /* input EOF */
-#define SFILE 1 /* file input */
-#define SSTDIN 2 /* read stdin */
-#define SSTRING 3 /* string */
-#define SWSTR 4 /* string without \n */
-#define SWORDS 5 /* string[] */
-#define SWORDSEP 6 /* string[] separator */
-#define SALIAS 7 /* alias expansion */
-#define SREREAD 8 /* read ahead to be re-scanned */
-
-/* Source.flags values */
-#define SF_ECHO BIT(0) /* echo input to shlout */
-#define SF_ALIAS BIT(1) /* faking space at end of alias */
-#define SF_ALIASEND BIT(2) /* faking space at end of alias */
-#define SF_TTY BIT(3) /* type == SSTDIN & it is a tty */
-#define SF_FIRST BIT(4) /* initial state (to ignore UTF-8 BOM) */
-#define SF_HASALIAS BIT(5) /* u.tblp valid (SALIAS, SEOF) */
-
-typedef union {
- int i;
- char *cp;
- char **wp;
- struct op *o;
- struct ioword *iop;
-} YYSTYPE;
-
-/* If something is added here, add it to tokentab[] in syn.c as well */
-#define LWORD 256
-#define LOGAND 257 /* && */
-#define LOGOR 258 /* || */
-#define BREAK 259 /* ;; */
-#define IF 260
-#define THEN 261
-#define ELSE 262
-#define ELIF 263
-#define FI 264
-#define CASE 265
-#define ESAC 266
-#define FOR 267
-#define SELECT 268
-#define WHILE 269
-#define UNTIL 270
-#define DO 271
-#define DONE 272
-#define IN 273
-#define FUNCTION 274
-#define TIME 275
-#define REDIR 276
-#define MDPAREN 277 /* (( )) */
-#define BANG 278 /* ! */
-#define DBRACKET 279 /* [[ .. ]] */
-#define COPROC 280 /* |& */
-#define YYERRCODE 300
-
-/* flags to yylex */
-#define CONTIN BIT(0) /* skip new lines to complete command */
-#define ONEWORD BIT(1) /* single word for substitute() */
-#define ALIAS BIT(2) /* recognise alias */
-#define KEYWORD BIT(3) /* recognise keywords */
-#define LETEXPR BIT(4) /* get expression inside (( )) */
-#define VARASN BIT(5) /* check for var=word */
-#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */
-#define ESACONLY BIT(7) /* only accept esac keyword */
-#define CMDWORD BIT(8) /* parsing simple command (alias related) */
-#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
-#define LQCHAR BIT(10) /* source string contains QCHAR */
-#define HEREDOC BIT(11) /* parsing a here document */
-#define LETARRAY BIT(12) /* copy expression inside =( ) */
-
-#define HERES 10 /* max << in line */
-
-#undef CTRL
-#define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */
-#define UNCTRL(x) ((x) ^ 0x40) /* ASCII */
-
-EXTERN Source *source; /* yyparse/yylex source */
-EXTERN YYSTYPE yylval; /* result from yylex */
-EXTERN struct ioword *heres [HERES], **herep;
-EXTERN char ident [IDENT+1];
-
-#define HISTORYSIZE 500 /* size of saved history */
-
-EXTERN char **history; /* saved commands */
-EXTERN char **histptr; /* last history item */
-EXTERN int histsize; /* history size */
-
-/* user and system time of last j_waitjed job */
-EXTERN struct timeval j_usrtime, j_systime;
-
-/* lalloc.c */
-void ainit(Area *);
-void afreeall(Area *);
-/* these cannot fail and can take NULL (not for ap) */
-#define alloc(n, ap) aresize(NULL, (n), (ap))
-void *aresize(void *, size_t, Area *);
-void afree(void *, Area *); /* can take NULL */
-/* edit.c */
-#ifndef MKSH_SMALL
-int x_bind(const char *, const char *, bool, bool);
-#else
-int x_bind(const char *, const char *, bool);
-#endif
-void x_init(void);
-int x_read(char *, size_t);
-/* eval.c */
-char *substitute(const char *, int);
-char **eval(const char **, int);
-char *evalstr(const char *cp, int);
-char *evalonestr(const char *cp, int);
-char *debunk(char *, const char *, size_t);
-void expand(const char *, XPtrV *, int);
-int glob_str(char *, XPtrV *, int);
-/* exec.c */
-int execute(struct op * volatile, volatile int, volatile int * volatile);
-int shcomexec(const char **);
-struct tbl *findfunc(const char *, uint32_t, bool);
-int define(const char *, struct op *);
-void builtin(const char *, int (*)(const char **));
-struct tbl *findcom(const char *, int);
-void flushcom(int);
-const char *search(const char *, const char *, int, int *);
-int search_access(const char *, int, int *);
-int pr_menu(const char * const *);
-int pr_list(char * const *);
-/* expr.c */
-int evaluate(const char *, mksh_ari_t *, int, bool);
-int v_evaluate(struct tbl *, const char *, volatile int, bool);
-/* UTF-8 stuff */
-size_t utf_mbtowc(unsigned int *, const char *);
-size_t utf_wctomb(char *, unsigned int);
-int utf_widthadj(const char *, const char **);
-int utf_mbswidth(const char *);
-const char *utf_skipcols(const char *, int);
-size_t utf_ptradj(const char *);
-#ifndef MKSH_mirbsd_wcwidth
-int utf_wcwidth(unsigned int);
-#endif
-/* funcs.c */
-int c_hash(const char **);
-int c_cd(const char **);
-int c_pwd(const char **);
-int c_print(const char **);
-#ifdef MKSH_PRINTF_BUILTIN
-int c_printf(const char **);
-#endif
-int c_whence(const char **);
-int c_command(const char **);
-int c_typeset(const char **);
-int c_alias(const char **);
-int c_unalias(const char **);
-int c_let(const char **);
-int c_jobs(const char **);
-#ifndef MKSH_UNEMPLOYED
-int c_fgbg(const char **);
-#endif
-int c_kill(const char **);
-void getopts_reset(int);
-int c_getopts(const char **);
-int c_bind(const char **);
-int c_label(const char **);
-int c_shift(const char **);
-int c_umask(const char **);
-int c_dot(const char **);
-int c_wait(const char **);
-int c_read(const char **);
-int c_eval(const char **);
-int c_trap(const char **);
-int c_brkcont(const char **);
-int c_exitreturn(const char **);
-int c_set(const char **);
-int c_unset(const char **);
-int c_ulimit(const char **);
-int c_times(const char **);
-int timex(struct op *, int, volatile int *);
-void timex_hook(struct op *, char ** volatile *);
-int c_exec(const char **);
-int c_builtin(const char **);
-int c_test(const char **);
-#if HAVE_MKNOD
-int c_mknod(const char **);
-#endif
-int c_realpath(const char **);
-int c_rename(const char **);
-/* histrap.c */
-void init_histvec(void);
-void hist_init(Source *);
-#if HAVE_PERSISTENT_HISTORY
-void hist_finish(void);
-#endif
-void histsave(int *, const char *, bool, bool);
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
-bool histsync(void);
-#endif
-int c_fc(const char **);
-void sethistsize(int);
-#if HAVE_PERSISTENT_HISTORY
-void sethistfile(const char *);
-#endif
-char **histpos(void);
-int histnum(int);
-int findhist(int, int, const char *, int);
-int findhistrel(const char *);
-char **hist_get_newest(bool);
-void inittraps(void);
-void alarm_init(void);
-Trap *gettrap(const char *, int);
-void trapsig(int);
-void intrcheck(void);
-int fatal_trap_check(void);
-int trap_pending(void);
-void runtraps(int intr);
-void runtrap(Trap *);
-void cleartraps(void);
-void restoresigs(void);
-void settrap(Trap *, const char *);
-int block_pipe(void);
-void restore_pipe(int);
-int setsig(Trap *, sig_t, int);
-void setexecsig(Trap *, int);
-/* jobs.c */
-void j_init(void);
-void j_exit(void);
-#ifndef MKSH_UNEMPLOYED
-void j_change(void);
-#endif
-int exchild(struct op *, int, volatile int *, int);
-void startlast(void);
-int waitlast(void);
-int waitfor(const char *, int *);
-int j_kill(const char *, int);
-#ifndef MKSH_UNEMPLOYED
-int j_resume(const char *, int);
-#endif
-int j_jobs(const char *, int, int);
-int j_njobs(void);
-void j_notify(void);
-pid_t j_async(void);
-int j_stopped_running(void);
-/* lex.c */
-int yylex(int);
-void yyerror(const char *, ...)
- MKSH_A_NORETURN
- MKSH_A_FORMAT(printf, 1, 2);
-Source *pushs(int, Area *);
-void set_prompt(int, Source *);
-void pprompt(const char *, int);
-int promptlen(const char *);
-/* main.c */
-int include(const char *, int, const char **, int);
-int command(const char *, int);
-int shell(Source *volatile, int volatile);
-void unwind(int) MKSH_A_NORETURN;
-void newenv(int);
-void quitenv(struct shf *);
-void cleanup_parents_env(void);
-void cleanup_proc_env(void);
-void errorf(const char *, ...)
- MKSH_A_NORETURN
- MKSH_A_FORMAT(printf, 1, 2);
-void warningf(bool, const char *, ...)
- MKSH_A_FORMAT(printf, 2, 3);
-void bi_errorf(const char *, ...)
- MKSH_A_FORMAT(printf, 1, 2);
-#define errorfz() errorf("\1")
-#define bi_errorfz() bi_errorf("\1")
-void internal_verrorf(const char *, va_list)
- MKSH_A_FORMAT(printf, 1, 0);
-void internal_errorf(const char *, ...)
- MKSH_A_NORETURN
- MKSH_A_FORMAT(printf, 1, 2);
-void internal_warningf(const char *, ...)
- MKSH_A_FORMAT(printf, 1, 2);
-void error_prefix(bool);
-void shellf(const char *, ...)
- MKSH_A_FORMAT(printf, 1, 2);
-void shprintf(const char *, ...)
- MKSH_A_FORMAT(printf, 1, 2);
-int can_seek(int);
-void initio(void);
-int ksh_dup2(int, int, bool);
-short savefd(int);
-void restfd(int, int);
-void openpipe(int *);
-void closepipe(int *);
-int check_fd(const char *, int, const char **);
-void coproc_init(void);
-void coproc_read_close(int);
-void coproc_readw_close(int);
-void coproc_write_close(int);
-int coproc_getfd(int, const char **);
-void coproc_cleanup(int);
-struct temp *maketemp(Area *, Temp_type, struct temp **);
-#define hash(s) oaathash_full((const uint8_t *)(s))
-uint32_t oaathash_full(register const uint8_t *);
-uint32_t hashmem(const void *, size_t);
-void ktinit(struct table *, Area *, size_t);
-struct tbl *ktsearch(struct table *, const char *, uint32_t);
-struct tbl *ktenter(struct table *, const char *, uint32_t);
-#define ktdelete(p) do { p->flag = 0; } while (/* CONSTCOND */ 0)
-void ktwalk(struct tstate *, struct table *);
-struct tbl *ktnext(struct tstate *);
-struct tbl **ktsort(struct table *);
-/* misc.c */
-void setctypes(const char *, int);
-void initctypes(void);
-size_t option(const char *);
-char *getoptions(void);
-void change_flag(enum sh_flag, int, unsigned int);
-int parse_args(const char **, int, bool *);
-int getn(const char *, int *);
-int bi_getn(const char *, int *);
-int gmatchx(const char *, const char *, bool);
-int has_globbing(const char *, const char *);
-const unsigned char *pat_scan(const unsigned char *, const unsigned char *, int);
-int xstrcmp(const void *, const void *);
-void ksh_getopt_reset(Getopt *, int);
-int ksh_getopt(const char **, Getopt *, const char *);
-void print_value_quoted(const char *);
-void print_columns(struct shf *, int,
- char *(*)(char *, int, int, const void *),
- const void *, int, int, bool);
-void strip_nuls(char *, int);
-int blocking_read(int, char *, int)
- MKSH_A_BOUNDED(buffer, 2, 3);
-int reset_nonblock(int);
-char *ksh_get_wd(size_t *);
-int make_path(const char *, const char *, char **, XString *, int *);
-void simplify_path(char *);
-void set_current_wd(char *);
-#ifdef MKSH_SMALL
-char *strdup_(const char *, Area *);
-char *strndup_(const char *, size_t, Area *);
-#endif
-int unbksl(bool, int (*)(void), void (*)(int));
-/* shf.c */
-struct shf *shf_open(const char *, int, int, int);
-struct shf *shf_fdopen(int, int, struct shf *);
-struct shf *shf_reopen(int, int, struct shf *);
-struct shf *shf_sopen(char *, int, int, struct shf *);
-int shf_close(struct shf *);
-int shf_fdclose(struct shf *);
-char *shf_sclose(struct shf *);
-int shf_flush(struct shf *);
-int shf_read(char *, int, struct shf *);
-char *shf_getse(char *, int, struct shf *);
-int shf_getchar(struct shf *s);
-int shf_ungetc(int, struct shf *);
-int shf_putchar(int, struct shf *);
-int shf_puts(const char *, struct shf *);
-int shf_write(const char *, int, struct shf *);
-int shf_fprintf(struct shf *, const char *, ...)
- MKSH_A_FORMAT(printf, 2, 3);
-int shf_snprintf(char *, int, const char *, ...)
- MKSH_A_FORMAT(printf, 3, 4)
- MKSH_A_BOUNDED(string, 1, 2);
-char *shf_smprintf(const char *, ...)
- MKSH_A_FORMAT(printf, 1, 2);
-int shf_vfprintf(struct shf *, const char *, va_list)
- MKSH_A_FORMAT(printf, 2, 0);
-/* syn.c */
-void initkeywords(void);
-struct op *compile(Source *);
-/* tree.c */
-int fptreef(struct shf *, int, const char *, ...);
-char *snptreef(char *, int, const char *, ...);
-struct op *tcopy(struct op *, Area *);
-char *wdcopy(const char *, Area *);
-const char *wdscan(const char *, int);
-char *wdstrip(const char *, bool, bool);
-void tfree(struct op *, Area *);
-/* var.c */
-void newblock(void);
-void popblock(void);
-void initvar(void);
-struct tbl *global(const char *);
-struct tbl *local(const char *, bool);
-char *str_val(struct tbl *);
-int setstr(struct tbl *, const char *, int);
-struct tbl *setint_v(struct tbl *, struct tbl *, bool);
-void setint(struct tbl *, mksh_ari_t);
-struct tbl *typeset(const char *, Tflag, Tflag, int, int)
- MKSH_A_NONNULL((nonnull (1)));
-void unset(struct tbl *, int);
-const char *skip_varname(const char *, int);
-const char *skip_wdvarname(const char *, int);
-int is_wdvarname(const char *, int);
-int is_wdvarassign(const char *);
-char **makenv(void);
-void change_random(const void *, size_t);
-void change_winsz(void);
-int array_ref_len(const char *);
-char *arrayname(const char *);
-mksh_uari_t set_array(const char *, bool, const char **);
-
-enum Test_op {
- TO_NONOP = 0, /* non-operator */
- /* unary operators */
- TO_STNZE, TO_STZER, TO_OPTION,
- TO_FILAXST,
- TO_FILEXST,
- TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
- TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID,
- TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX,
- /* binary operators */
- TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT,
- TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT,
- /* not an operator */
- TO_NONNULL /* !TO_NONOP */
-};
-typedef enum Test_op Test_op;
-
-/* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */
-enum Test_meta {
- TM_OR, /* -o or || */
- TM_AND, /* -a or && */
- TM_NOT, /* ! */
- TM_OPAREN, /* ( */
- TM_CPAREN, /* ) */
- TM_UNOP, /* unary operator */
- TM_BINOP, /* binary operator */
- TM_END /* end of input */
-};
-typedef enum Test_meta Test_meta;
-
-#define TEF_ERROR BIT(0) /* set if we've hit an error */
-#define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */
-
-typedef struct test_env {
- union {
- const char **wp;/* used by ptest_* */
- XPtrV *av; /* used by dbtestp_* */
- } pos;
- const char **wp_end; /* used by ptest_* */
- Test_op (*isa)(struct test_env *, Test_meta);
- const char *(*getopnd) (struct test_env *, Test_op, bool);
- int (*eval)(struct test_env *, Test_op, const char *, const char *, bool);
- void (*error)(struct test_env *, int, const char *);
- int flags; /* TEF_* */
-} Test_env;
-
-extern const char *const dbtest_tokens[];
-
-Test_op test_isop(Test_meta, const char *);
-int test_eval(Test_env *, Test_op, const char *, const char *, bool);
-int test_parse(Test_env *);
-
-EXTERN int tty_fd I__(-1); /* dup'd tty file descriptor */
-EXTERN int tty_devtty; /* true if tty_fd is from /dev/tty */
-EXTERN struct termios tty_state; /* saved tty state */
-
-extern void tty_init(bool, bool);
-extern void tty_close(void);
-
-/* be sure not to interfere with anyone else's idea about EXTERN */
-#ifdef EXTERN_DEFINED
-# undef EXTERN_DEFINED
-# undef EXTERN
-#endif
-#undef I__
-
-#endif /* !MKSH_INCLUDES_ONLY */
diff --git a/mksh/src/sh_flags.h b/mksh/src/sh_flags.h
deleted file mode 100644
index aa5481e..0000000
--- a/mksh/src/sh_flags.h
+++ /dev/null
@@ -1,145 +0,0 @@
-#if defined(SHFLAGS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/sh_flags.h,v 1.7 2010/07/13 13:07:58 tg Exp $");
-#define FN(sname,cname,ochar,flags) /* nothing */
-#elif defined(SHFLAGS_ENUMS)
-#define FN(sname,cname,ochar,flags) cname,
-#define F0(sname,cname,ochar,flags) cname = 0,
-#elif defined(SHFLAGS_ITEMS)
-#define FN(sname,cname,ochar,flags) { sname, ochar, flags },
-#endif
-
-#ifndef F0
-#define F0 FN
-#endif
-
-/*
- * special cases (see parse_args()): -A, -o, -s
- *
- * options are sorted by their longnames
- */
-
-/* -a all new parameters are created with the export attribute */
-F0("allexport", FEXPORT, 'a', OF_ANY)
-
-/* ./. backwards compat: dummy, emits a warning */
-FN("arc4random", FARC4RANDOM, 0, OF_ANY)
-
-#if HAVE_NICE
-/* ./. bgnice */
-FN("bgnice", FBGNICE, 0, OF_ANY)
-#endif
-
-/* ./. enable {} globbing (non-standard) */
-FN("braceexpand", FBRACEEXPAND, 0, OF_ANY)
-
-/* ./. Emacs command line editing mode */
-FN("emacs", FEMACS, 0, OF_ANY)
-
-/* -e quit on error */
-FN("errexit", FERREXIT, 'e', OF_ANY)
-
-/* ./. Emacs command line editing mode, gmacs variant */
-FN("gmacs", FGMACS, 0, OF_ANY)
-
-/* ./. reading EOF does not exit */
-FN("ignoreeof", FIGNOREEOF, 0, OF_ANY)
-
-/* -i interactive shell */
-FN("interactive", FTALKING, 'i', OF_CMDLINE)
-
-/* -k name=value are recognised anywhere */
-FN("keyword", FKEYWORD, 'k', OF_ANY)
-
-/* -l login shell */
-FN("login", FLOGIN, 'l', OF_CMDLINE)
-
-/* -X mark dirs with / in file name completion */
-FN("markdirs", FMARKDIRS, 'X', OF_ANY)
-
-#ifndef MKSH_UNEMPLOYED
-/* -m job control monitoring */
-FN("monitor", FMONITOR, 'm', OF_ANY)
-#endif
-
-/* -C don't overwrite existing files */
-FN("noclobber", FNOCLOBBER, 'C', OF_ANY)
-
-/* -n don't execute any commands */
-FN("noexec", FNOEXEC, 'n', OF_ANY)
-
-/* -f don't do file globbing */
-FN("noglob", FNOGLOB, 'f', OF_ANY)
-
-/* ./. don't kill running jobs when login shell exits */
-FN("nohup", FNOHUP, 0, OF_ANY)
-
-/* ./. don't save functions in history (no effect) */
-FN("nolog", FNOLOG, 0, OF_ANY)
-
-#ifndef MKSH_UNEMPLOYED
-/* -b asynchronous job completion notification */
-FN("notify", FNOTIFY, 'b', OF_ANY)
-#endif
-
-/* -u using an unset variable is an error */
-FN("nounset", FNOUNSET, 'u', OF_ANY)
-
-/* ./. don't do logical cds/pwds (non-standard) */
-FN("physical", FPHYSICAL, 0, OF_ANY)
-
-/* ./. pdksh compat: somewhat more POSIXish mode (non-standard) */
-FN("posix", FPOSIX, 0, OF_ANY)
-
-/* -p use suid_profile; privileged shell */
-FN("privileged", FPRIVILEGED, 'p', OF_ANY)
-
-/* -r restricted shell */
-FN("restricted", FRESTRICTED, 'r', OF_CMDLINE)
-
-/* ./. pdksh compat: called as sh not mksh; kludge mode (non-standard) */
-FN("sh", FSH, 0, OF_ANY)
-
-/* -s (invocation) parse stdin (pseudo non-standard) */
-FN("stdin", FSTDIN, 's', OF_CMDLINE)
-
-/* -h create tracked aliases for all commands */
-FN("trackall", FTRACKALL, 'h', OF_ANY)
-
-/* -U enable UTF-8 processing (non-standard) */
-FN("utf8-mode", FUNICODE, 'U', OF_ANY)
-
-/* -v echo input */
-FN("verbose", FVERBOSE, 'v', OF_ANY)
-
-#if !MKSH_S_NOVI
-/* ./. Vi command line editing mode */
-FN("vi", FVI, 0, OF_ANY)
-
-/* ./. enable ESC as file name completion character (non-standard) */
-FN("vi-esccomplete", FVIESCCOMPLETE, 0, OF_ANY)
-
-/* ./. enable Tab as file name completion character (non-standard) */
-FN("vi-tabcomplete", FVITABCOMPLETE, 0, OF_ANY)
-
-/* ./. always read in raw mode (no effect) */
-FN("viraw", FVIRAW, 0, OF_ANY)
-#endif
-
-/* -x execution trace (display commands as they are run) */
-FN("xtrace", FXTRACE, 'x', OF_ANY)
-
-/* -c (invocation) execute specified command */
-FN(NULL, FCOMMAND, 'c', OF_CMDLINE)
-
-/*
- * anonymous flags: used internally by shell only (not visible to user)
- */
-
-/* ./. (internal) initial shell was interactive */
-FN(NULL, FTALKING_I, 0, OF_INTERNAL)
-
-#undef FN
-#undef F0
-#undef SHFLAGS_DEFNS
-#undef SHFLAGS_ENUMS
-#undef SHFLAGS_ITEMS
diff --git a/mksh/src/shf.c b/mksh/src/shf.c
deleted file mode 100644
index 0962752..0000000
--- a/mksh/src/shf.c
+++ /dev/null
@@ -1,1042 +0,0 @@
-/* $OpenBSD: shf.c,v 1.15 2006/04/02 00:48:33 deraadt Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.36 2010/07/19 22:41:04 tg Exp $");
-
-/* flags to shf_emptybuf() */
-#define EB_READSW 0x01 /* about to switch to reading */
-#define EB_GROW 0x02 /* grow buffer if necessary (STRING+DYNAMIC) */
-
-/*
- * Replacement stdio routines. Stdio is too flakey on too many machines
- * to be useful when you have multiple processes using the same underlying
- * file descriptors.
- */
-
-static int shf_fillbuf(struct shf *);
-static int shf_emptybuf(struct shf *, int);
-
-/* Open a file. First three args are for open(), last arg is flags for
- * this package. Returns NULL if file could not be opened, or if a dup
- * fails.
- */
-struct shf *
-shf_open(const char *name, int oflags, int mode, int sflags)
-{
- struct shf *shf;
- int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
- int fd;
-
- /* Done before open so if alloca fails, fd won't be lost. */
- shf = alloc(sizeof(struct shf) + bsize, ATEMP);
- shf->areap = ATEMP;
- shf->buf = (unsigned char *)&shf[1];
- shf->bsize = bsize;
- shf->flags = SHF_ALLOCS;
- /* Rest filled in by reopen. */
-
- fd = open(name, oflags, mode);
- if (fd < 0) {
- afree(shf, shf->areap);
- return (NULL);
- }
- if ((sflags & SHF_MAPHI) && fd < FDBASE) {
- int nfd;
-
- nfd = fcntl(fd, F_DUPFD, FDBASE);
- close(fd);
- if (nfd < 0) {
- afree(shf, shf->areap);
- return (NULL);
- }
- fd = nfd;
- }
- sflags &= ~SHF_ACCMODE;
- sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD :
- ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR : SHF_RDWR);
-
- return (shf_reopen(fd, sflags, shf));
-}
-
-/* Set up the shf structure for a file descriptor. Doesn't fail. */
-struct shf *
-shf_fdopen(int fd, int sflags, struct shf *shf)
-{
- int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
-
- /* use fcntl() to figure out correct read/write flags */
- if (sflags & SHF_GETFL) {
- int flags = fcntl(fd, F_GETFL, 0);
-
- if (flags < 0)
- /* will get an error on first read/write */
- sflags |= SHF_RDWR;
- else {
- switch (flags & O_ACCMODE) {
- case O_RDONLY:
- sflags |= SHF_RD;
- break;
- case O_WRONLY:
- sflags |= SHF_WR;
- break;
- case O_RDWR:
- sflags |= SHF_RDWR;
- break;
- }
- }
- }
-
- if (!(sflags & (SHF_RD | SHF_WR)))
- internal_errorf("shf_fdopen: missing read/write");
-
- if (shf) {
- if (bsize) {
- shf->buf = alloc(bsize, ATEMP);
- sflags |= SHF_ALLOCB;
- } else
- shf->buf = NULL;
- } else {
- shf = alloc(sizeof(struct shf) + bsize, ATEMP);
- shf->buf = (unsigned char *)&shf[1];
- sflags |= SHF_ALLOCS;
- }
- shf->areap = ATEMP;
- shf->fd = fd;
- shf->rp = shf->wp = shf->buf;
- shf->rnleft = 0;
- shf->rbsize = bsize;
- shf->wnleft = 0; /* force call to shf_emptybuf() */
- shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
- shf->flags = sflags;
- shf->errno_ = 0;
- shf->bsize = bsize;
- if (sflags & SHF_CLEXEC)
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- return (shf);
-}
-
-/* Set up an existing shf (and buffer) to use the given fd */
-struct shf *
-shf_reopen(int fd, int sflags, struct shf *shf)
-{
- int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
-
- /* use fcntl() to figure out correct read/write flags */
- if (sflags & SHF_GETFL) {
- int flags = fcntl(fd, F_GETFL, 0);
-
- if (flags < 0)
- /* will get an error on first read/write */
- sflags |= SHF_RDWR;
- else {
- switch (flags & O_ACCMODE) {
- case O_RDONLY:
- sflags |= SHF_RD;
- break;
- case O_WRONLY:
- sflags |= SHF_WR;
- break;
- case O_RDWR:
- sflags |= SHF_RDWR;
- break;
- }
- }
- }
-
- if (!(sflags & (SHF_RD | SHF_WR)))
- internal_errorf("shf_reopen: missing read/write");
- if (!shf || !shf->buf || shf->bsize < bsize)
- internal_errorf("shf_reopen: bad shf/buf/bsize");
-
- /* assumes shf->buf and shf->bsize already set up */
- shf->fd = fd;
- shf->rp = shf->wp = shf->buf;
- shf->rnleft = 0;
- shf->rbsize = bsize;
- shf->wnleft = 0; /* force call to shf_emptybuf() */
- shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
- shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
- shf->errno_ = 0;
- if (sflags & SHF_CLEXEC)
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- return (shf);
-}
-
-/* Open a string for reading or writing. If reading, bsize is the number
- * of bytes that can be read. If writing, bsize is the maximum number of
- * bytes that can be written. If shf is not null, it is filled in and
- * returned, if it is null, shf is allocated. If writing and buf is null
- * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is
- * used for the initial size). Doesn't fail.
- * When writing, a byte is reserved for a trailing null - see shf_sclose().
- */
-struct shf *
-shf_sopen(char *buf, int bsize, int sflags, struct shf *shf)
-{
- /* can't have a read+write string */
- if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR)))
- internal_errorf("shf_sopen: flags 0x%x", sflags);
-
- if (!shf) {
- shf = alloc(sizeof(struct shf), ATEMP);
- sflags |= SHF_ALLOCS;
- }
- shf->areap = ATEMP;
- if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {
- if (bsize <= 0)
- bsize = 64;
- sflags |= SHF_ALLOCB;
- buf = alloc(bsize, shf->areap);
- }
- shf->fd = -1;
- shf->buf = shf->rp = shf->wp = (unsigned char *)buf;
- shf->rnleft = bsize;
- shf->rbsize = bsize;
- shf->wnleft = bsize - 1; /* space for a '\0' */
- shf->wbsize = bsize;
- shf->flags = sflags | SHF_STRING;
- shf->errno_ = 0;
- shf->bsize = bsize;
-
- return (shf);
-}
-
-/* Flush and close file descriptor, free the shf structure */
-int
-shf_close(struct shf *shf)
-{
- int ret = 0;
-
- if (shf->fd >= 0) {
- ret = shf_flush(shf);
- if (close(shf->fd) < 0)
- ret = EOF;
- }
- if (shf->flags & SHF_ALLOCS)
- afree(shf, shf->areap);
- else if (shf->flags & SHF_ALLOCB)
- afree(shf->buf, shf->areap);
-
- return (ret);
-}
-
-/* Flush and close file descriptor, don't free file structure */
-int
-shf_fdclose(struct shf *shf)
-{
- int ret = 0;
-
- if (shf->fd >= 0) {
- ret = shf_flush(shf);
- if (close(shf->fd) < 0)
- ret = EOF;
- shf->rnleft = 0;
- shf->rp = shf->buf;
- shf->wnleft = 0;
- shf->fd = -1;
- }
-
- return (ret);
-}
-
-/* Close a string - if it was opened for writing, it is null terminated;
- * returns a pointer to the string and frees shf if it was allocated
- * (does not free string if it was allocated).
- */
-char *
-shf_sclose(struct shf *shf)
-{
- unsigned char *s = shf->buf;
-
- /* null terminate */
- if (shf->flags & SHF_WR) {
- shf->wnleft++;
- shf_putc('\0', shf);
- }
- if (shf->flags & SHF_ALLOCS)
- afree(shf, shf->areap);
- return ((char *)s);
-}
-
-/* Un-read what has been read but not examined, or write what has been
- * buffered. Returns 0 for success, EOF for (write) error.
- */
-int
-shf_flush(struct shf *shf)
-{
- if (shf->flags & SHF_STRING)
- return ((shf->flags & SHF_WR) ? EOF : 0);
-
- if (shf->fd < 0)
- internal_errorf("shf_flush: no fd");
-
- if (shf->flags & SHF_ERROR) {
- errno = shf->errno_;
- return (EOF);
- }
-
- if (shf->flags & SHF_READING) {
- shf->flags &= ~(SHF_EOF | SHF_READING);
- if (shf->rnleft > 0) {
- lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR);
- shf->rnleft = 0;
- shf->rp = shf->buf;
- }
- return (0);
- } else if (shf->flags & SHF_WRITING)
- return (shf_emptybuf(shf, 0));
-
- return (0);
-}
-
-/* Write out any buffered data. If currently reading, flushes the read
- * buffer. Returns 0 for success, EOF for (write) error.
- */
-static int
-shf_emptybuf(struct shf *shf, int flags)
-{
- int ret = 0;
-
- if (!(shf->flags & SHF_STRING) && shf->fd < 0)
- internal_errorf("shf_emptybuf: no fd");
-
- if (shf->flags & SHF_ERROR) {
- errno = shf->errno_;
- return (EOF);
- }
-
- if (shf->flags & SHF_READING) {
- if (flags & EB_READSW) /* doesn't happen */
- return (0);
- ret = shf_flush(shf);
- shf->flags &= ~SHF_READING;
- }
- if (shf->flags & SHF_STRING) {
- unsigned char *nbuf;
-
- /* Note that we assume SHF_ALLOCS is not set if SHF_ALLOCB
- * is set... (changing the shf pointer could cause problems)
- */
- if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) ||
- !(shf->flags & SHF_ALLOCB))
- return (EOF);
- /* allocate more space for buffer */
- nbuf = aresize(shf->buf, 2 * shf->wbsize, shf->areap);
- shf->rp = nbuf + (shf->rp - shf->buf);
- shf->wp = nbuf + (shf->wp - shf->buf);
- shf->rbsize += shf->wbsize;
- shf->wnleft += shf->wbsize;
- shf->wbsize *= 2;
- shf->buf = nbuf;
- } else {
- if (shf->flags & SHF_WRITING) {
- int ntowrite = shf->wp - shf->buf;
- unsigned char *buf = shf->buf;
- int n;
-
- while (ntowrite > 0) {
- n = write(shf->fd, buf, ntowrite);
- if (n < 0) {
- if (errno == EINTR &&
- !(shf->flags & SHF_INTERRUPT))
- continue;
- shf->flags |= SHF_ERROR;
- shf->errno_ = errno;
- shf->wnleft = 0;
- if (buf != shf->buf) {
- /* allow a second flush
- * to work */
- memmove(shf->buf, buf,
- ntowrite);
- shf->wp = shf->buf + ntowrite;
- }
- return (EOF);
- }
- buf += n;
- ntowrite -= n;
- }
- if (flags & EB_READSW) {
- shf->wp = shf->buf;
- shf->wnleft = 0;
- shf->flags &= ~SHF_WRITING;
- return (0);
- }
- }
- shf->wp = shf->buf;
- shf->wnleft = shf->wbsize;
- }
- shf->flags |= SHF_WRITING;
-
- return (ret);
-}
-
-/* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */
-static int
-shf_fillbuf(struct shf *shf)
-{
- if (shf->flags & SHF_STRING)
- return (0);
-
- if (shf->fd < 0)
- internal_errorf("shf_fillbuf: no fd");
-
- if (shf->flags & (SHF_EOF | SHF_ERROR)) {
- if (shf->flags & SHF_ERROR)
- errno = shf->errno_;
- return (EOF);
- }
-
- if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
- return (EOF);
-
- shf->flags |= SHF_READING;
-
- shf->rp = shf->buf;
- while (1) {
- shf->rnleft = blocking_read(shf->fd, (char *) shf->buf,
- shf->rbsize);
- if (shf->rnleft < 0 && errno == EINTR &&
- !(shf->flags & SHF_INTERRUPT))
- continue;
- break;
- }
- if (shf->rnleft <= 0) {
- if (shf->rnleft < 0) {
- shf->flags |= SHF_ERROR;
- shf->errno_ = errno;
- shf->rnleft = 0;
- shf->rp = shf->buf;
- return (EOF);
- }
- shf->flags |= SHF_EOF;
- }
- return (0);
-}
-
-/* Read a buffer from shf. Returns the number of bytes read into buf,
- * if no bytes were read, returns 0 if end of file was seen, EOF if
- * a read error occurred.
- */
-int
-shf_read(char *buf, int bsize, struct shf *shf)
-{
- int orig_bsize = bsize;
- int ncopy;
-
- if (!(shf->flags & SHF_RD))
- internal_errorf("shf_read: flags %x", shf->flags);
-
- if (bsize <= 0)
- internal_errorf("shf_read: bsize %d", bsize);
-
- while (bsize > 0) {
- if (shf->rnleft == 0 &&
- (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
- break;
- ncopy = shf->rnleft;
- if (ncopy > bsize)
- ncopy = bsize;
- memcpy(buf, shf->rp, ncopy);
- buf += ncopy;
- bsize -= ncopy;
- shf->rp += ncopy;
- shf->rnleft -= ncopy;
- }
- /* Note: fread(3S) returns 0 for errors - this doesn't */
- return (orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) :
- orig_bsize - bsize);
-}
-
-/* Read up to a newline or EOF. The newline is put in buf; buf is always
- * null terminated. Returns NULL on read error or if nothing was read before
- * end of file, returns a pointer to the null byte in buf otherwise.
- */
-char *
-shf_getse(char *buf, int bsize, struct shf *shf)
-{
- unsigned char *end;
- int ncopy;
- char *orig_buf = buf;
-
- if (!(shf->flags & SHF_RD))
- internal_errorf("shf_getse: flags %x", shf->flags);
-
- if (bsize <= 0)
- return (NULL);
-
- --bsize; /* save room for null */
- do {
- if (shf->rnleft == 0) {
- if (shf_fillbuf(shf) == EOF)
- return (NULL);
- if (shf->rnleft == 0) {
- *buf = '\0';
- return (buf == orig_buf ? NULL : buf);
- }
- }
- end = (unsigned char *)memchr((char *) shf->rp, '\n',
- shf->rnleft);
- ncopy = end ? end - shf->rp + 1 : shf->rnleft;
- if (ncopy > bsize)
- ncopy = bsize;
- memcpy(buf, (char *) shf->rp, ncopy);
- shf->rp += ncopy;
- shf->rnleft -= ncopy;
- buf += ncopy;
- bsize -= ncopy;
- } while (!end && bsize);
- *buf = '\0';
- return (buf);
-}
-
-/* Returns the char read. Returns EOF for error and end of file. */
-int
-shf_getchar(struct shf *shf)
-{
- if (!(shf->flags & SHF_RD))
- internal_errorf("shf_getchar: flags %x", shf->flags);
-
- if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
- return (EOF);
- --shf->rnleft;
- return (*shf->rp++);
-}
-
-/* Put a character back in the input stream. Returns the character if
- * successful, EOF if there is no room.
- */
-int
-shf_ungetc(int c, struct shf *shf)
-{
- if (!(shf->flags & SHF_RD))
- internal_errorf("shf_ungetc: flags %x", shf->flags);
-
- if ((shf->flags & SHF_ERROR) || c == EOF ||
- (shf->rp == shf->buf && shf->rnleft))
- return (EOF);
-
- if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
- return (EOF);
-
- if (shf->rp == shf->buf)
- shf->rp = shf->buf + shf->rbsize;
- if (shf->flags & SHF_STRING) {
- /* Can unget what was read, but not something different - we
- * don't want to modify a string.
- */
- if (shf->rp[-1] != c)
- return (EOF);
- shf->flags &= ~SHF_EOF;
- shf->rp--;
- shf->rnleft++;
- return (c);
- }
- shf->flags &= ~SHF_EOF;
- *--(shf->rp) = c;
- shf->rnleft++;
- return (c);
-}
-
-/* Write a character. Returns the character if successful, EOF if
- * the char could not be written.
- */
-int
-shf_putchar(int c, struct shf *shf)
-{
- if (!(shf->flags & SHF_WR))
- internal_errorf("shf_putchar: flags %x", shf->flags);
-
- if (c == EOF)
- return (EOF);
-
- if (shf->flags & SHF_UNBUF) {
- unsigned char cc = (unsigned char)c;
- int n;
-
- if (shf->fd < 0)
- internal_errorf("shf_putchar: no fd");
- if (shf->flags & SHF_ERROR) {
- errno = shf->errno_;
- return (EOF);
- }
- while ((n = write(shf->fd, &cc, 1)) != 1)
- if (n < 0) {
- if (errno == EINTR &&
- !(shf->flags & SHF_INTERRUPT))
- continue;
- shf->flags |= SHF_ERROR;
- shf->errno_ = errno;
- return (EOF);
- }
- } else {
- /* Flush deals with strings and sticky errors */
- if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
- return (EOF);
- shf->wnleft--;
- *shf->wp++ = c;
- }
-
- return (c);
-}
-
-/* Write a string. Returns the length of the string if successful, EOF if
- * the string could not be written.
- */
-int
-shf_puts(const char *s, struct shf *shf)
-{
- if (!s)
- return (EOF);
-
- return (shf_write(s, strlen(s), shf));
-}
-
-/* Write a buffer. Returns nbytes if successful, EOF if there is an error. */
-int
-shf_write(const char *buf, int nbytes, struct shf *shf)
-{
- int n, ncopy, orig_nbytes = nbytes;
-
- if (!(shf->flags & SHF_WR))
- internal_errorf("shf_write: flags %x", shf->flags);
-
- if (nbytes < 0)
- internal_errorf("shf_write: nbytes %d", nbytes);
-
- /* Don't buffer if buffer is empty and we're writting a large amount. */
- if ((ncopy = shf->wnleft) &&
- (shf->wp != shf->buf || nbytes < shf->wnleft)) {
- if (ncopy > nbytes)
- ncopy = nbytes;
- memcpy(shf->wp, buf, ncopy);
- nbytes -= ncopy;
- buf += ncopy;
- shf->wp += ncopy;
- shf->wnleft -= ncopy;
- }
- if (nbytes > 0) {
- if (shf->flags & SHF_STRING) {
- /* resize buffer until there's enough space left */
- while (nbytes > shf->wnleft)
- if (shf_emptybuf(shf, EB_GROW) == EOF)
- return (EOF);
- /* then write everything into the buffer */
- } else {
- /* flush deals with sticky errors */
- if (shf_emptybuf(shf, EB_GROW) == EOF)
- return (EOF);
- /* write chunks larger than window size directly */
- if (nbytes > shf->wbsize) {
- ncopy = nbytes;
- if (shf->wbsize)
- ncopy -= nbytes % shf->wbsize;
- nbytes -= ncopy;
- while (ncopy > 0) {
- n = write(shf->fd, buf, ncopy);
- if (n < 0) {
- if (errno == EINTR &&
- !(shf->flags & SHF_INTERRUPT))
- continue;
- shf->flags |= SHF_ERROR;
- shf->errno_ = errno;
- shf->wnleft = 0;
- /*
- * Note: fwrite(3) returns 0
- * for errors - this doesn't
- */
- return (EOF);
- }
- buf += n;
- ncopy -= n;
- }
- }
- /* ... and buffer the rest */
- }
- if (nbytes > 0) {
- /* write remaining bytes to buffer */
- memcpy(shf->wp, buf, nbytes);
- shf->wp += nbytes;
- shf->wnleft -= nbytes;
- }
- }
-
- return (orig_nbytes);
-}
-
-int
-shf_fprintf(struct shf *shf, const char *fmt, ...)
-{
- va_list args;
- int n;
-
- va_start(args, fmt);
- n = shf_vfprintf(shf, fmt, args);
- va_end(args);
-
- return (n);
-}
-
-int
-shf_snprintf(char *buf, int bsize, const char *fmt, ...)
-{
- struct shf shf;
- va_list args;
- int n;
-
- if (!buf || bsize <= 0)
- internal_errorf("shf_snprintf: buf %p, bsize %d", buf, bsize);
-
- shf_sopen(buf, bsize, SHF_WR, &shf);
- va_start(args, fmt);
- n = shf_vfprintf(&shf, fmt, args);
- va_end(args);
- shf_sclose(&shf); /* null terminates */
- return (n);
-}
-
-char *
-shf_smprintf(const char *fmt, ...)
-{
- struct shf shf;
- va_list args;
-
- shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
- va_start(args, fmt);
- shf_vfprintf(&shf, fmt, args);
- va_end(args);
- return (shf_sclose(&shf)); /* null terminates */
-}
-
-#undef FP /* if you want floating point stuff */
-
-#ifndef DMAXEXP
-# define DMAXEXP 128 /* should be big enough */
-#endif
-
-#define BUF_SIZE 128
-/* must be > MAX(DMAXEXP, log10(pow(2, DSIGNIF))) + ceil(log10(DMAXEXP)) + 8
- * (I think); since it's hard to express as a constant, just use a large buffer
- */
-#define FPBUF_SIZE (DMAXEXP+16)
-
-#define FL_HASH 0x001 /* '#' seen */
-#define FL_PLUS 0x002 /* '+' seen */
-#define FL_RIGHT 0x004 /* '-' seen */
-#define FL_BLANK 0x008 /* ' ' seen */
-#define FL_SHORT 0x010 /* 'h' seen */
-#define FL_LONG 0x020 /* 'l' seen */
-#define FL_ZERO 0x040 /* '0' seen */
-#define FL_DOT 0x080 /* '.' seen */
-#define FL_UPPER 0x100 /* format character was uppercase */
-#define FL_NUMBER 0x200 /* a number was formated %[douxefg] */
-
-
-int
-shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
-{
- const char *s;
- char c, *cp;
- int tmp = 0, field, precision, len, flags;
- unsigned long lnum;
- /* %#o produces the longest output */
- char numbuf[(8 * sizeof(long) + 2) / 3 + 1];
- /* this stuff for dealing with the buffer */
- int nwritten = 0;
-
- if (!fmt)
- return (0);
-
- while ((c = *fmt++)) {
- if (c != '%') {
- shf_putc(c, shf);
- nwritten++;
- continue;
- }
- /*
- * This will accept flags/fields in any order - not
- * just the order specified in printf(3), but this is
- * the way _doprnt() seems to work (on bsd and sysV).
- * The only restriction is that the format character must
- * come last :-).
- */
- flags = field = precision = 0;
- for ( ; (c = *fmt++) ; ) {
- switch (c) {
- case '#':
- flags |= FL_HASH;
- continue;
-
- case '+':
- flags |= FL_PLUS;
- continue;
-
- case '-':
- flags |= FL_RIGHT;
- continue;
-
- case ' ':
- flags |= FL_BLANK;
- continue;
-
- case '0':
- if (!(flags & FL_DOT))
- flags |= FL_ZERO;
- continue;
-
- case '.':
- flags |= FL_DOT;
- precision = 0;
- continue;
-
- case '*':
- tmp = va_arg(args, int);
- if (flags & FL_DOT)
- precision = tmp;
- else if ((field = tmp) < 0) {
- field = -field;
- flags |= FL_RIGHT;
- }
- continue;
-
- case 'l':
- flags |= FL_LONG;
- continue;
-
- case 'h':
- flags |= FL_SHORT;
- continue;
- }
- if (ksh_isdigit(c)) {
- tmp = c - '0';
- while (c = *fmt++, ksh_isdigit(c))
- tmp = tmp * 10 + c - '0';
- --fmt;
- if (tmp < 0) /* overflow? */
- tmp = 0;
- if (flags & FL_DOT)
- precision = tmp;
- else
- field = tmp;
- continue;
- }
- break;
- }
-
- if (precision < 0)
- precision = 0;
-
- if (!c) /* nasty format */
- break;
-
- if (c >= 'A' && c <= 'Z') {
- flags |= FL_UPPER;
- c = ksh_tolower(c);
- }
-
- switch (c) {
- case 'p': /* pointer */
- flags &= ~(FL_LONG | FL_SHORT);
- flags |= (sizeof(char *) > sizeof(int)) ?
- /* hope it fits.. */ FL_LONG : 0;
- /* aaahhh... */
- case 'd':
- case 'i':
- case 'o':
- case 'u':
- case 'x':
- flags |= FL_NUMBER;
- cp = numbuf + sizeof(numbuf);
- /*-
- * XXX any better way to do this?
- * XXX hopefully the compiler optimises this out
- *
- * For shorts, we want sign extend for %d but not
- * for %[oxu] - on 16 bit machines it doesn't matter.
- * Assumes C compiler has converted shorts to ints
- * before pushing them. XXX optimise this -tg
- */
- if (flags & FL_LONG)
- lnum = va_arg(args, unsigned long);
- else if ((sizeof(int) < sizeof(long)) && (c == 'd'))
- lnum = (long)va_arg(args, int);
- else
- lnum = va_arg(args, unsigned int);
- switch (c) {
- case 'd':
- case 'i':
- if (0 > (long)lnum) {
- lnum = -(long)lnum;
- tmp = 1;
- } else
- tmp = 0;
- /* FALLTHROUGH */
- case 'u':
- do {
- *--cp = lnum % 10 + '0';
- lnum /= 10;
- } while (lnum);
-
- if (c != 'u') {
- if (tmp)
- *--cp = '-';
- else if (flags & FL_PLUS)
- *--cp = '+';
- else if (flags & FL_BLANK)
- *--cp = ' ';
- }
- break;
-
- case 'o':
- do {
- *--cp = (lnum & 0x7) + '0';
- lnum >>= 3;
- } while (lnum);
-
- if ((flags & FL_HASH) && *cp != '0')
- *--cp = '0';
- break;
-
- case 'p':
- case 'x': {
- const char *digits = (flags & FL_UPPER) ?
- digits_uc : digits_lc;
- do {
- *--cp = digits[lnum & 0xf];
- lnum >>= 4;
- } while (lnum);
-
- if (flags & FL_HASH) {
- *--cp = (flags & FL_UPPER) ? 'X' : 'x';
- *--cp = '0';
- }
- }
- }
- len = numbuf + sizeof(numbuf) - (s = cp);
- if (flags & FL_DOT) {
- if (precision > len) {
- field = precision;
- flags |= FL_ZERO;
- } else
- precision = len; /* no loss */
- }
- break;
-
- case 's':
- if (!(s = va_arg(args, const char *)))
- s = "(null)";
- len = utf_mbswidth(s);
- break;
-
- case 'c':
- flags &= ~FL_DOT;
- numbuf[0] = (char)(va_arg(args, int));
- s = numbuf;
- len = 1;
- break;
-
- case '%':
- default:
- numbuf[0] = c;
- s = numbuf;
- len = 1;
- break;
- }
-
- /*
- * At this point s should point to a string that is to be
- * formatted, and len should be the length of the string.
- */
- if (!(flags & FL_DOT) || len < precision)
- precision = len;
- if (field > precision) {
- field -= precision;
- if (!(flags & FL_RIGHT)) {
- field = -field;
- /* skip past sign or 0x when padding with 0 */
- if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
- if (*s == '+' || *s == '-' ||
- *s == ' ') {
- shf_putc(*s, shf);
- s++;
- precision--;
- nwritten++;
- } else if (*s == '0') {
- shf_putc(*s, shf);
- s++;
- nwritten++;
- if (--precision > 0 &&
- (*s | 0x20) == 'x') {
- shf_putc(*s, shf);
- s++;
- precision--;
- nwritten++;
- }
- }
- c = '0';
- } else
- c = flags & FL_ZERO ? '0' : ' ';
- if (field < 0) {
- nwritten += -field;
- for ( ; field < 0 ; field++)
- shf_putc(c, shf);
- }
- } else
- c = ' ';
- } else
- field = 0;
-
- if (precision > 0) {
- const char *q;
-
- nwritten += precision;
- q = utf_skipcols(s, precision);
- do {
- shf_putc(*s, shf);
- } while (++s < q);
- }
- if (field > 0) {
- nwritten += field;
- for ( ; field > 0 ; --field)
- shf_putc(c, shf);
- }
- }
-
- return (shf_error(shf) ? EOF : nwritten);
-}
-
-#ifdef MKSH_SMALL
-int
-shf_getc(struct shf *shf)
-{
- return ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ :
- shf_getchar(shf));
-}
-
-int
-shf_putc(int c, struct shf *shf)
-{
- return ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) :
- ((shf)->wnleft--, *(shf)->wp++ = (c)));
-}
-#endif
diff --git a/mksh/src/syn.c b/mksh/src/syn.c
deleted file mode 100644
index 64b2867..0000000
--- a/mksh/src/syn.c
+++ /dev/null
@@ -1,1004 +0,0 @@
-/* $OpenBSD: syn.c,v 1.28 2008/07/23 16:34:38 jaredy Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.49 2010/07/17 22:09:39 tg Exp $");
-
-struct nesting_state {
- int start_token; /* token than began nesting (eg, FOR) */
- int start_line; /* line nesting began on */
-};
-
-static void yyparse(void);
-static struct op *pipeline(int);
-static struct op *andor(void);
-static struct op *c_list(int);
-static struct ioword *synio(int);
-static struct op *nested(int, int, int);
-static struct op *get_command(int);
-static struct op *dogroup(void);
-static struct op *thenpart(void);
-static struct op *elsepart(void);
-static struct op *caselist(void);
-static struct op *casepart(int);
-static struct op *function_body(char *, bool);
-static char **wordlist(void);
-static struct op *block(int, struct op *, struct op *, char **);
-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 inalias(struct source *);
-static Test_op dbtestp_isa(Test_env *, Test_meta);
-static const char *dbtestp_getopnd(Test_env *, Test_op, bool);
-static int dbtestp_eval(Test_env *, Test_op, const char *,
- const char *, bool);
-static void dbtestp_error(Test_env *, int, const char *) MKSH_A_NORETURN;
-
-static struct op *outtree; /* yyparse output */
-static struct nesting_state nesting; /* \n changed to ; */
-
-static int reject; /* token(cf) gets symbol again */
-static int symbol; /* yylex value */
-
-#define REJECT (reject = 1)
-#define ACCEPT (reject = 0)
-#define token(cf) ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
-#define tpeek(cf) ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
-#define musthave(c,cf) do { if (token(cf) != (c)) syntaxerr(NULL); } while (0)
-
-static void
-yyparse(void)
-{
- int c;
-
- ACCEPT;
-
- outtree = c_list(source->type == SSTRING);
- c = tpeek(0);
- if (c == 0 && !outtree)
- outtree = newtp(TEOF);
- else if (c != '\n' && c != 0)
- syntaxerr(NULL);
-}
-
-static struct op *
-pipeline(int cf)
-{
- struct op *t, *p, *tl = NULL;
-
- t = get_command(cf);
- if (t != NULL) {
- while (token(0) == '|') {
- if ((p = get_command(CONTIN)) == NULL)
- syntaxerr(NULL);
- if (tl == NULL)
- t = tl = block(TPIPE, t, p, NOWORDS);
- else
- tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
- }
- REJECT;
- }
- return (t);
-}
-
-static struct op *
-andor(void)
-{
- struct op *t, *p;
- int c;
-
- t = pipeline(0);
- if (t != NULL) {
- while ((c = token(0)) == LOGAND || c == LOGOR) {
- if ((p = pipeline(CONTIN)) == NULL)
- syntaxerr(NULL);
- t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
- }
- REJECT;
- }
- return (t);
-}
-
-static struct op *
-c_list(int multi)
-{
- struct op *t = NULL, *p, *tl = NULL;
- int c, have_sep;
-
- while (1) {
- p = andor();
- /* Token has always been read/rejected at this point, so
- * we don't worry about what flags to pass token()
- */
- c = token(0);
- have_sep = 1;
- if (c == '\n' && (multi || inalias(source))) {
- if (!p) /* ignore blank lines */
- continue;
- } else if (!p)
- break;
- else if (c == '&' || c == COPROC)
- p = block(c == '&' ? TASYNC : TCOPROC,
- p, NOBLOCK, NOWORDS);
- else if (c != ';')
- have_sep = 0;
- if (!t)
- t = p;
- else if (!tl)
- t = tl = block(TLIST, t, p, NOWORDS);
- else
- tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
- if (!have_sep)
- break;
- }
- REJECT;
- return (t);
-}
-
-static struct ioword *
-synio(int cf)
-{
- struct ioword *iop;
- static struct ioword *nextiop = NULL;
- bool ishere;
-
- if (nextiop != NULL) {
- iop = nextiop;
- nextiop = NULL;
- return (iop);
- }
-
- if (tpeek(cf) != REDIR)
- return (NULL);
- ACCEPT;
- iop = yylval.iop;
- ishere = (iop->flag&IOTYPE) == IOHERE;
- musthave(LWORD, ishere ? HEREDELIM : 0);
- if (ishere) {
- iop->delim = yylval.cp;
- if (*ident != 0) /* unquoted */
- iop->flag |= IOEVAL;
- if (herep > &heres[HERES - 1])
- yyerror("too many <<s\n");
- *herep++ = iop;
- } else
- iop->name = yylval.cp;
-
- if (iop->flag & IOBASH) {
- char *cp;
-
- nextiop = alloc(sizeof(*iop), ATEMP);
- nextiop->name = cp = alloc(5, ATEMP);
-
- if (iop->unit > 9) {
- *cp++ = CHAR;
- *cp++ = '0' + (iop->unit / 10);
- }
- *cp++ = CHAR;
- *cp++ = '0' + (iop->unit % 10);
- *cp = EOS;
-
- iop->flag &= ~IOBASH;
- nextiop->unit = 2;
- nextiop->flag = IODUP;
- nextiop->delim = NULL;
- nextiop->heredoc = NULL;
- }
- return (iop);
-}
-
-static struct op *
-nested(int type, int smark, int emark)
-{
- struct op *t;
- struct nesting_state old_nesting;
-
- nesting_push(&old_nesting, smark);
- t = c_list(true);
- musthave(emark, KEYWORD|ALIAS);
- nesting_pop(&old_nesting);
- return (block(type, t, NOBLOCK, NOWORDS));
-}
-
-static struct op *
-get_command(int cf)
-{
- struct op *t;
- int c, iopn = 0, syniocf;
- struct ioword *iop, **iops;
- XPtrV args, vars;
- struct nesting_state old_nesting;
-
- iops = alloc((NUFILE + 1) * sizeof(struct ioword *), ATEMP);
- XPinit(args, 16);
- XPinit(vars, 16);
-
- syniocf = KEYWORD|ALIAS;
- switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
- default:
- REJECT;
- afree(iops, ATEMP);
- XPfree(args);
- XPfree(vars);
- return (NULL); /* empty line */
-
- case LWORD:
- case REDIR:
- REJECT;
- syniocf &= ~(KEYWORD|ALIAS);
- t = newtp(TCOM);
- t->lineno = source->line;
- while (1) {
- cf = (t->u.evalflags ? ARRAYVAR : 0) |
- (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
- switch (tpeek(cf)) {
- case REDIR:
- while ((iop = synio(cf)) != NULL) {
- if (iopn >= NUFILE)
- yyerror("too many redirections\n");
- iops[iopn++] = iop;
- }
- break;
-
- case LWORD:
- ACCEPT;
- /* the iopn == 0 and XPsize(vars) == 0 are
- * dubious but AT&T ksh acts this way
- */
- if (iopn == 0 && XPsize(vars) == 0 &&
- XPsize(args) == 0 &&
- assign_command(ident))
- t->u.evalflags = DOVACHECK;
- if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
- is_wdvarassign(yylval.cp))
- XPput(vars, yylval.cp);
- else
- XPput(args, yylval.cp);
- break;
-
- case '(':
- /* 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;
- }
-#ifndef MKSH_SMALL
- if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
- XPsize(vars) == 1 && is_wdvarassign(yylval.cp))
- goto is_wdarrassign;
-#endif
- /* 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, 'e', CHAR, 'v',
- CHAR, 'a', CHAR, 'l', EOS
- };
- static const char set_cmd1[] = {
- CHAR, 's', CHAR, 'e',
- CHAR, 't', CHAR, ' ',
- CHAR, '-', CHAR, 'A', EOS
- };
- static const char set_cmd2[] = {
- CHAR, '-', CHAR, '-', EOS
- };
- char *tcp;
- XPfree(vars);
- XPinit(vars, 16);
- /*
- * we know (or rather hope) that yylval.cp
- * contains a string "varname="
- */
- tcp = wdcopy(yylval.cp, ATEMP);
- tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
- /* now make an array assignment command */
- t = newtp(TCOM);
- t->lineno = source->line;
- ACCEPT;
- XPput(args, wdcopy(set_cmd0, ATEMP));
- XPput(args, wdcopy(set_cmd1, ATEMP));
- XPput(args, tcp);
- XPput(args, wdcopy(set_cmd2, ATEMP));
- musthave(LWORD,LETARRAY);
- XPput(args, yylval.cp);
- break;
- }
-#endif
-
- default:
- goto Leave;
- }
- }
- Leave:
- break;
-
- case '(':
- Subshell:
- t = nested(TPAREN, '(', ')');
- break;
-
- case '{': /*}*/
- t = nested(TBRACE, '{', '}');
- break;
-
- case MDPAREN: {
- int lno;
- static const char let_cmd[] = {
- CHAR, 'l', CHAR, 'e',
- CHAR, 't', EOS
- };
-
- /* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
- lno = source->line;
- ACCEPT;
- switch (token(LETEXPR)) {
- case LWORD:
- break;
- case '(': /* ) */
- goto Subshell;
- default:
- syntaxerr(NULL);
- }
- t = newtp(TCOM);
- t->lineno = lno;
- XPput(args, wdcopy(let_cmd, ATEMP));
- XPput(args, yylval.cp);
- break;
- }
-
- case DBRACKET: /* [[ .. ]] */
- /* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
- t = newtp(TDBRACKET);
- ACCEPT;
- {
- Test_env te;
-
- te.flags = TEF_DBRACKET;
- te.pos.av = &args;
- te.isa = dbtestp_isa;
- te.getopnd = dbtestp_getopnd;
- te.eval = dbtestp_eval;
- te.error = dbtestp_error;
-
- test_parse(&te);
- }
- break;
-
- case FOR:
- case SELECT:
- t = newtp((c == FOR) ? TFOR : TSELECT);
- musthave(LWORD, ARRAYVAR);
- if (!is_wdvarname(yylval.cp, true))
- yyerror("%s: bad identifier\n",
- c == FOR ? "for" : "select");
- strdupx(t->str, ident, ATEMP);
- nesting_push(&old_nesting, c);
- t->vars = wordlist();
- t->left = dogroup();
- nesting_pop(&old_nesting);
- break;
-
- case WHILE:
- case UNTIL:
- nesting_push(&old_nesting, c);
- t = newtp((c == WHILE) ? TWHILE : TUNTIL);
- t->left = c_list(true);
- t->right = dogroup();
- nesting_pop(&old_nesting);
- break;
-
- case CASE:
- t = newtp(TCASE);
- musthave(LWORD, 0);
- t->str = yylval.cp;
- nesting_push(&old_nesting, c);
- t->left = caselist();
- nesting_pop(&old_nesting);
- break;
-
- case IF:
- nesting_push(&old_nesting, c);
- t = newtp(TIF);
- t->left = c_list(true);
- t->right = thenpart();
- musthave(FI, KEYWORD|ALIAS);
- nesting_pop(&old_nesting);
- break;
-
- case BANG:
- syniocf &= ~(KEYWORD|ALIAS);
- t = pipeline(0);
- if (t == NULL)
- syntaxerr(NULL);
- t = block(TBANG, NOBLOCK, t, NOWORDS);
- break;
-
- case TIME:
- syniocf &= ~(KEYWORD|ALIAS);
- t = pipeline(0);
- if (t) {
- t->str = alloc(2, ATEMP);
- t->str[0] = '\0'; /* TF_* flags */
- t->str[1] = '\0';
- }
- t = block(TTIME, t, NOBLOCK, NOWORDS);
- break;
-
- case FUNCTION:
- musthave(LWORD, 0);
- t = function_body(yylval.cp, true);
- break;
- }
-
- while ((iop = synio(syniocf)) != NULL) {
- if (iopn >= NUFILE)
- yyerror("too many redirections\n");
- iops[iopn++] = iop;
- }
-
- if (iopn == 0) {
- afree(iops, ATEMP);
- t->ioact = NULL;
- } else {
- iops[iopn++] = NULL;
- iops = aresize(iops, iopn * sizeof(struct ioword *), ATEMP);
- t->ioact = iops;
- }
-
- if (t->type == TCOM || t->type == TDBRACKET) {
- XPput(args, NULL);
- t->args = (const char **)XPclose(args);
- XPput(vars, NULL);
- t->vars = (char **) XPclose(vars);
- } else {
- XPfree(args);
- XPfree(vars);
- }
-
- return (t);
-}
-
-static struct op *
-dogroup(void)
-{
- int c;
- struct op *list;
-
- c = token(CONTIN|KEYWORD|ALIAS);
- /* 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
- * is a while loop because it would have been parsed as part of
- * the conditional command list...
- */
- if (c == DO)
- c = DONE;
- else if (c == '{')
- c = '}';
- else
- syntaxerr(NULL);
- list = c_list(true);
- musthave(c, KEYWORD|ALIAS);
- return (list);
-}
-
-static struct op *
-thenpart(void)
-{
- struct op *t;
-
- musthave(THEN, KEYWORD|ALIAS);
- t = newtp(0);
- t->left = c_list(true);
- if (t->left == NULL)
- syntaxerr(NULL);
- t->right = elsepart();
- return (t);
-}
-
-static struct op *
-elsepart(void)
-{
- struct op *t;
-
- switch (token(KEYWORD|ALIAS|VARASN)) {
- case ELSE:
- if ((t = c_list(true)) == NULL)
- syntaxerr(NULL);
- return (t);
-
- case ELIF:
- t = newtp(TELIF);
- t->left = c_list(true);
- t->right = thenpart();
- return (t);
-
- default:
- REJECT;
- }
- return (NULL);
-}
-
-static struct op *
-caselist(void)
-{
- struct op *t, *tl;
- int c;
-
- c = token(CONTIN|KEYWORD|ALIAS);
- /* A {...} can be used instead of in...esac for case statements */
- if (c == IN)
- c = ESAC;
- else if (c == '{')
- c = '}';
- else
- syntaxerr(NULL);
- t = tl = NULL;
- while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */
- struct op *tc = casepart(c);
- if (tl == NULL)
- t = tl = tc, tl->right = NULL;
- else
- tl->right = tc, tl = tc;
- }
- musthave(c, KEYWORD|ALIAS);
- return (t);
-}
-
-static struct op *
-casepart(int endtok)
-{
- struct op *t;
- XPtrV ptns;
-
- XPinit(ptns, 16);
- t = newtp(TPAT);
- /* no ALIAS here */
- if (token(CONTIN | KEYWORD) != '(')
- REJECT;
- do {
- musthave(LWORD, 0);
- XPput(ptns, yylval.cp);
- } while (token(0) == '|');
- REJECT;
- XPput(ptns, NULL);
- t->vars = (char **) XPclose(ptns);
- musthave(')', 0);
-
- t->left = c_list(true);
- /* Note: POSIX requires the ;; */
- if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
- musthave(BREAK, CONTIN|KEYWORD|ALIAS);
- return (t);
-}
-
-static struct op *
-function_body(char *name,
- bool ksh_func) /* function foo { ... } vs foo() { .. } */
-{
- char *sname, *p;
- struct op *t;
- bool old_func_parse;
-
- sname = wdstrip(name, false, false);
- /* Check for valid characters in name. POSIX and AT&T ksh93 say only
- * allow [a-zA-Z_0-9] but this allows more as old pdkshs have
- * allowed more (the following were never allowed:
- * NUL TAB NL SP " $ & ' ( ) ; < = > \ ` |
- * C_QUOTE covers all but adds # * ? [ ]
- */
- for (p = sname; *p; p++)
- if (ctype(*p, C_QUOTE))
- yyerror("%s: invalid function name\n", sname);
-
- /* Note that POSIX allows only compound statements after foo(), sh and
- * AT&T ksh allow any command, go with the later since it shouldn't
- * break anything. However, for function foo, AT&T ksh only accepts
- * an open-brace.
- */
- if (ksh_func) {
- if (tpeek(CONTIN|KEYWORD|ALIAS) == '(' /* ) */) {
- struct tbl *tp;
-
- /* function foo () { */
- ACCEPT;
- musthave(')', 0);
- /* degrade to POSIX function */
- ksh_func = false;
- if ((tp = ktsearch(&aliases, sname, hash(sname))))
- ktdelete(tp);
- }
- musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
- REJECT;
- }
-
- t = newtp(TFUNCT);
- t->str = sname;
- t->u.ksh_func = 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;
- /*
- * Probably something like foo() followed by eof or ;.
- * This is accepted by sh and ksh88.
- * To make "typeset -f foo" work reliably (so its output can
- * be used as input), we pretend there is a colon here.
- */
- t->left = newtp(TCOM);
- t->left->args = alloc(2 * sizeof(char *), ATEMP);
- t->left->args[0] = tv = alloc(3, ATEMP);
- tv[0] = CHAR;
- tv[1] = ':';
- tv[2] = EOS;
- t->left->args[1] = NULL;
- t->left->vars = alloc(sizeof(char *), ATEMP);
- t->left->vars[0] = NULL;
- t->left->lineno = 1;
- }
- if (!old_func_parse)
- e->flags &= ~EF_FUNC_PARSE;
-
- return (t);
-}
-
-static char **
-wordlist(void)
-{
- int c;
- XPtrV args;
-
- XPinit(args, 16);
- /* POSIX does not do alias expansion here... */
- if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
- if (c != ';') /* non-POSIX, but AT&T ksh accepts a ; here */
- REJECT;
- return (NULL);
- }
- while ((c = token(0)) == LWORD)
- XPput(args, yylval.cp);
- if (c != '\n' && c != ';')
- syntaxerr(NULL);
- if (XPsize(args) == 0) {
- XPfree(args);
- return (NULL);
- } else {
- XPput(args, NULL);
- return ((char **)XPclose(args));
- }
-}
-
-/*
- * supporting functions
- */
-
-static struct op *
-block(int type, struct op *t1, struct op *t2, char **wp)
-{
- struct op *t;
-
- t = newtp(type);
- t->left = t1;
- t->right = t2;
- t->vars = wp;
- return (t);
-}
-
-const struct tokeninfo {
- const char *name;
- short val;
- short reserved;
-} tokentab[] = {
- /* Reserved words */
- { "if", IF, true },
- { "then", THEN, true },
- { "else", ELSE, true },
- { "elif", ELIF, true },
- { "fi", FI, true },
- { "case", CASE, true },
- { "esac", ESAC, true },
- { "for", FOR, true },
- { "select", SELECT, true },
- { "while", WHILE, true },
- { "until", UNTIL, true },
- { "do", DO, true },
- { "done", DONE, true },
- { "in", IN, true },
- { "function", FUNCTION, true },
- { "time", TIME, true },
- { "{", '{', true },
- { "}", '}', true },
- { "!", BANG, true },
- { "[[", DBRACKET, true },
- /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
- { "&&", LOGAND, false },
- { "||", LOGOR, false },
- { ";;", BREAK, false },
- { "((", MDPAREN, false },
- { "|&", COPROC, false },
- /* and some special cases... */
- { "newline", '\n', false },
- { NULL, 0, false }
-};
-
-void
-initkeywords(void)
-{
- struct tokeninfo const *tt;
- struct tbl *p;
-
- ktinit(&keywords, APERM,
- /* must be 80% of 2^n (currently 20 keywords) */ 32);
- for (tt = tokentab; tt->name; tt++) {
- if (tt->reserved) {
- p = ktenter(&keywords, tt->name, hash(tt->name));
- p->flag |= DEFINED|ISSET;
- p->type = CKEYWD;
- p->val.i = tt->val;
- }
- }
-}
-
-static void
-syntaxerr(const char *what)
-{
- char redir[6]; /* 2<<- is the longest redirection, I think */
- const char *s;
- struct tokeninfo const *tt;
- int c;
-
- if (!what)
- what = "unexpected";
- REJECT;
- c = token(0);
- Again:
- switch (c) {
- case 0:
- if (nesting.start_token) {
- c = nesting.start_token;
- source->errline = nesting.start_line;
- what = "unmatched";
- goto Again;
- }
- /* don't quote the EOF */
- yyerror("%s: unexpected EOF\n", T_synerr);
- /* NOTREACHED */
-
- case LWORD:
- s = snptreef(NULL, 32, "%S", yylval.cp);
- break;
-
- case REDIR:
- s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
- break;
-
- default:
- for (tt = tokentab; tt->name; tt++)
- if (tt->val == c)
- break;
- if (tt->name)
- s = tt->name;
- else {
- if (c > 0 && c < 256) {
- redir[0] = c;
- redir[1] = '\0';
- } else
- shf_snprintf(redir, sizeof(redir),
- "?%d", c);
- s = redir;
- }
- }
- yyerror("%s: '%s' %s\n", T_synerr, s, what);
-}
-
-static void
-nesting_push(struct nesting_state *save, int tok)
-{
- *save = nesting;
- nesting.start_token = tok;
- nesting.start_line = source->line;
-}
-
-static void
-nesting_pop(struct nesting_state *saved)
-{
- nesting = *saved;
-}
-
-static struct op *
-newtp(int type)
-{
- struct op *t;
-
- t = alloc(sizeof(struct op), ATEMP);
- t->type = type;
- t->u.evalflags = 0;
- t->args = NULL;
- t->vars = NULL;
- t->ioact = NULL;
- t->left = t->right = NULL;
- t->str = NULL;
- return (t);
-}
-
-struct op *
-compile(Source *s)
-{
- nesting.start_token = 0;
- nesting.start_line = 0;
- herep = heres;
- source = s;
- yyparse();
- return (outtree);
-}
-
-/* This kludge exists to take care of sh/AT&T ksh oddity in which
- * the arguments of alias/export/readonly/typeset have no field
- * splitting, file globbing, or (normal) tilde expansion done.
- * AT&T ksh seems to do something similar to this since
- * $ touch a=a; typeset a=[ab]; echo "$a"
- * a=[ab]
- * $ x=typeset; $x a=[ab]; echo "$a"
- * a=a
- * $
- */
-static int
-assign_command(char *s)
-{
- if (!*s)
- return (0);
- return ((strcmp(s, "alias") == 0) ||
- (strcmp(s, "export") == 0) ||
- (strcmp(s, "readonly") == 0) ||
- (strcmp(s, T_typeset) == 0));
-}
-
-/* Check if we are in the middle of reading an alias */
-static int
-inalias(struct source *s)
-{
- for (; s && s->type == SALIAS; s = s->next)
- if (!(s->flags & SF_ALIASEND))
- return (1);
- return (0);
-}
-
-
-/* Order important - indexed by Test_meta values
- * Note that ||, &&, ( and ) can't appear in as unquoted strings
- * in normal shell input, so these can be interpreted unambiguously
- * in the evaluation pass.
- */
-static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
-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[] = {
- 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 };
-
-/*
- * Test if the current token is a whatever. Accepts the current token if
- * it is. Returns 0 if it is not, non-zero if it is (in the case of
- * TM_UNOP and TM_BINOP, the returned value is a Test_op).
- */
-static Test_op
-dbtestp_isa(Test_env *te, Test_meta meta)
-{
- int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
- int uqword;
- char *save = NULL;
- Test_op ret = TO_NONOP;
-
- /* unquoted word? */
- uqword = c == LWORD && *ident;
-
- if (meta == TM_OR)
- ret = c == LOGOR ? TO_NONNULL : TO_NONOP;
- else if (meta == TM_AND)
- ret = c == LOGAND ? TO_NONNULL : TO_NONOP;
- else if (meta == TM_NOT)
- ret = (uqword && !strcmp(yylval.cp,
- dbtest_tokens[(int)TM_NOT])) ? TO_NONNULL : TO_NONOP;
- else if (meta == TM_OPAREN)
- ret = c == '(' /*)*/ ? TO_NONNULL : TO_NONOP;
- else if (meta == TM_CPAREN)
- ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP;
- else if (meta == TM_UNOP || meta == TM_BINOP) {
- if (meta == TM_BINOP && c == REDIR &&
- (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) {
- ret = TO_NONNULL;
- save = wdcopy(yylval.iop->flag == IOREAD ?
- db_lthan : db_gthan, ATEMP);
- } else if (uqword && (ret = test_isop(meta, ident)))
- save = yylval.cp;
- } else /* meta == TM_END */
- ret = (uqword && !strcmp(yylval.cp,
- db_close)) ? TO_NONNULL : TO_NONOP;
- if (ret != TO_NONOP) {
- ACCEPT;
- if (meta < NELEM(dbtest_tokens))
- save = wdcopy(dbtest_tokens[(int)meta], ATEMP);
- if (save)
- XPput(*te->pos.av, save);
- }
- return (ret);
-}
-
-static const char *
-dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED,
- bool do_eval MKSH_A_UNUSED)
-{
- int c = tpeek(ARRAYVAR);
-
- if (c != LWORD)
- return (NULL);
-
- ACCEPT;
- XPput(*te->pos.av, yylval.cp);
-
- return (null);
-}
-
-static int
-dbtestp_eval(Test_env *te MKSH_A_UNUSED, Test_op op MKSH_A_UNUSED,
- const char *opnd1 MKSH_A_UNUSED, const char *opnd2 MKSH_A_UNUSED,
- bool do_eval MKSH_A_UNUSED)
-{
- return (1);
-}
-
-static void
-dbtestp_error(Test_env *te, int offset, const char *msg)
-{
- te->flags |= TEF_ERROR;
-
- if (offset < 0) {
- REJECT;
- /* Kludgy to say the least... */
- symbol = LWORD;
- yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) +
- offset);
- }
- syntaxerr(msg);
-}
diff --git a/mksh/src/tree.c b/mksh/src/tree.c
deleted file mode 100644
index aa861db..0000000
--- a/mksh/src/tree.c
+++ /dev/null
@@ -1,716 +0,0 @@
-/* $OpenBSD: tree.c,v 1.19 2008/08/11 21:50:35 jaredy Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.30 2010/02/25 20:18:19 tg Exp $");
-
-#define INDENT 4
-
-#define tputc(c, shf) shf_putchar(c, shf);
-static void ptree(struct op *, int, struct shf *);
-static void pioact(struct shf *, int, struct ioword *);
-static void tputC(int, struct shf *);
-static void tputS(char *, struct shf *);
-static void vfptreef(struct shf *, int, const char *, va_list);
-static struct ioword **iocopy(struct ioword **, Area *);
-static void iofree(struct ioword **, Area *);
-
-/*
- * print a command tree
- */
-static void
-ptree(struct op *t, int indent, struct shf *shf)
-{
- const char **w;
- struct ioword **ioact;
- struct op *t1;
-
- Chain:
- if (t == NULL)
- return;
- switch (t->type) {
- case TCOM:
- if (t->vars)
- for (w = (const char **)t->vars; *w != NULL; )
- fptreef(shf, indent, "%S ", *w++);
- else
- shf_puts("#no-vars# ", shf);
- if (t->args)
- for (w = t->args; *w != NULL; )
- fptreef(shf, indent, "%S ", *w++);
- else
- shf_puts("#no-args# ", shf);
- break;
- case TEXEC:
- t = t->left;
- goto Chain;
- case TPAREN:
- fptreef(shf, indent + 2, "( %T) ", t->left);
- break;
- case TPIPE:
- fptreef(shf, indent, "%T| ", t->left);
- t = t->right;
- goto Chain;
- case TLIST:
- fptreef(shf, indent, "%T%;", t->left);
- t = t->right;
- goto Chain;
- case TOR:
- case TAND:
- fptreef(shf, indent, "%T%s %T",
- t->left, (t->type==TOR) ? "||" : "&&", t->right);
- break;
- case TBANG:
- shf_puts("! ", shf);
- t = t->right;
- goto Chain;
- case TDBRACKET: {
- int i;
-
- shf_puts("[[", shf);
- for (i = 0; t->args[i]; i++)
- fptreef(shf, indent, " %S", t->args[i]);
- shf_puts(" ]] ", shf);
- break;
- }
- case TSELECT:
- fptreef(shf, indent, "select %s ", t->str);
- /* FALLTHROUGH */
- case TFOR:
- if (t->type == TFOR)
- fptreef(shf, indent, "for %s ", t->str);
- if (t->vars != NULL) {
- shf_puts("in ", shf);
- for (w = (const char **)t->vars; *w; )
- fptreef(shf, indent, "%S ", *w++);
- fptreef(shf, indent, "%;");
- }
- fptreef(shf, indent + INDENT, "do%N%T", t->left);
- fptreef(shf, indent, "%;done ");
- break;
- case TCASE:
- fptreef(shf, indent, "case %S in", t->str);
- for (t1 = t->left; t1 != NULL; t1 = t1->right) {
- fptreef(shf, indent, "%N(");
- for (w = (const char **)t1->vars; *w != NULL; w++)
- fptreef(shf, indent, "%S%c", *w,
- (w[1] != NULL) ? '|' : ')');
- fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
- }
- fptreef(shf, indent, "%Nesac ");
- break;
- case TIF:
- case TELIF:
- /* 3 == strlen("if ") */
- fptreef(shf, indent + 3, "if %T", t->left);
- for (;;) {
- t = t->right;
- if (t->left != NULL) {
- fptreef(shf, indent, "%;");
- fptreef(shf, indent + INDENT, "then%N%T",
- t->left);
- }
- if (t->right == NULL || t->right->type != TELIF)
- break;
- t = t->right;
- fptreef(shf, indent, "%;");
- /* 5 == strlen("elif ") */
- fptreef(shf, indent + 5, "elif %T", t->left);
- }
- if (t->right != NULL) {
- fptreef(shf, indent, "%;");
- fptreef(shf, indent + INDENT, "else%;%T", t->right);
- }
- fptreef(shf, indent, "%;fi ");
- break;
- case TWHILE:
- case TUNTIL:
- /* 6 == strlen("while"/"until") */
- fptreef(shf, indent + 6, "%s %T",
- (t->type==TWHILE) ? "while" : "until",
- t->left);
- fptreef(shf, indent, "%;do");
- fptreef(shf, indent + INDENT, "%;%T", t->right);
- fptreef(shf, indent, "%;done ");
- break;
- case TBRACE:
- fptreef(shf, indent + INDENT, "{%;%T", t->left);
- fptreef(shf, indent, "%;} ");
- break;
- case TCOPROC:
- fptreef(shf, indent, "%T|& ", t->left);
- break;
- case TASYNC:
- fptreef(shf, indent, "%T& ", t->left);
- break;
- case TFUNCT:
- fptreef(shf, indent,
- t->u.ksh_func ? "function %s %T" : "%s() %T",
- t->str, t->left);
- break;
- case TTIME:
- fptreef(shf, indent, "time %T", t->left);
- break;
- default:
- shf_puts("<botch>", shf);
- break;
- }
- if ((ioact = t->ioact) != NULL) {
- int need_nl = 0;
-
- while (*ioact != NULL)
- pioact(shf, indent, *ioact++);
- /* Print here documents after everything else... */
- for (ioact = t->ioact; *ioact != NULL; ) {
- struct ioword *iop = *ioact++;
-
- /* heredoc is 0 when tracing (set -x) */
- if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc &&
- /* iop->delim[1] == '<' means here string */
- (!iop->delim || iop->delim[1] != '<')) {
- tputc('\n', shf);
- shf_puts(iop->heredoc, shf);
- fptreef(shf, indent, "%s",
- evalstr(iop->delim, 0));
- need_nl = 1;
- }
- }
- /* Last delimiter must be followed by a newline (this often
- * leads to an extra blank line, but its not worth worrying
- * about)
- */
- if (need_nl)
- tputc('\n', shf);
- }
-}
-
-static void
-pioact(struct shf *shf, int indent, struct ioword *iop)
-{
- int flag = iop->flag;
- int type = flag & IOTYPE;
- int expected;
-
- expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
- (type == IOCAT || type == IOWRITE) ? 1 :
- (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
- iop->unit + 1;
- if (iop->unit != expected)
- shf_fprintf(shf, "%d", iop->unit);
-
- switch (type) {
- case IOREAD:
- shf_puts("< ", shf);
- break;
- case IOHERE:
- shf_puts(flag & IOSKIP ? "<<-" : "<<", shf);
- break;
- case IOCAT:
- shf_puts(">> ", shf);
- break;
- case IOWRITE:
- shf_puts(flag & IOCLOB ? ">| " : "> ", shf);
- break;
- case IORDWR:
- shf_puts("<> ", shf);
- break;
- case IODUP:
- shf_puts(flag & IORDUP ? "<&" : ">&", shf);
- break;
- }
- /* name/delim are 0 when printing syntax errors */
- if (type == IOHERE) {
- if (iop->delim)
- fptreef(shf, indent, "%s%S ",
- /* here string */ iop->delim[1] == '<' ? "" : " ",
- iop->delim);
- else
- tputc(' ', shf);
- } else if (iop->name)
- fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
- iop->name);
-}
-
-
-/*
- * variants of fputc, fputs for ptreef and snptreef
- */
-static void
-tputC(int c, struct shf *shf)
-{
- if ((c&0x60) == 0) { /* C0|C1 */
- tputc((c&0x80) ? '$' : '^', shf);
- tputc(((c&0x7F)|0x40), shf);
- } else if ((c&0x7F) == 0x7F) { /* DEL */
- tputc((c&0x80) ? '$' : '^', shf);
- tputc('?', shf);
- } else
- tputc(c, shf);
-}
-
-static void
-tputS(char *wp, struct shf *shf)
-{
- int c, quotelevel = 0;
-
- /* problems:
- * `...` -> $(...)
- * 'foo' -> "foo"
- * could change encoding to:
- * OQUOTE ["'] ... CQUOTE ["']
- * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
- */
- while (1)
- switch (*wp++) {
- case EOS:
- return;
- case ADELIM:
- case CHAR:
- tputC(*wp++, shf);
- break;
- case QCHAR:
- c = *wp++;
- if (!quotelevel || (c == '"' || c == '`' || c == '$'))
- tputc('\\', shf);
- tputC(c, shf);
- break;
- case COMSUB:
- shf_puts("$(", shf);
- while (*wp != 0)
- tputC(*wp++, shf);
- tputc(')', shf);
- wp++;
- break;
- case EXPRSUB:
- shf_puts("$((", shf);
- while (*wp != 0)
- tputC(*wp++, shf);
- shf_puts("))", shf);
- wp++;
- break;
- case OQUOTE:
- quotelevel++;
- tputc('"', shf);
- break;
- case CQUOTE:
- if (quotelevel)
- quotelevel--;
- tputc('"', shf);
- break;
- case OSUBST:
- tputc('$', shf);
- if (*wp++ == '{')
- tputc('{', shf);
- while ((c = *wp++) != 0)
- tputC(c, shf);
- break;
- case CSUBST:
- if (*wp++ == '}')
- tputc('}', shf);
- break;
- case OPAT:
- tputc(*wp++, shf);
- tputc('(', shf);
- break;
- case SPAT:
- tputc('|', shf);
- break;
- case CPAT:
- tputc(')', shf);
- break;
- }
-}
-
-/*
- * this is the _only_ way to reliably handle
- * variable args with an ANSI compiler
- */
-/* VARARGS */
-int
-fptreef(struct shf *shf, int indent, const char *fmt, ...)
-{
- va_list va;
-
- va_start(va, fmt);
-
- vfptreef(shf, indent, fmt, va);
- va_end(va);
- return (0);
-}
-
-/* VARARGS */
-char *
-snptreef(char *s, int n, const char *fmt, ...)
-{
- va_list va;
- struct shf shf;
-
- shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
-
- va_start(va, fmt);
- vfptreef(&shf, 0, fmt, va);
- va_end(va);
-
- return (shf_sclose(&shf)); /* null terminates */
-}
-
-static void
-vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
-{
- int c;
-
- while ((c = *fmt++)) {
- if (c == '%') {
- switch ((c = *fmt++)) {
- case 'c':
- tputc(va_arg(va, int), shf);
- break;
- case 's':
- shf_puts(va_arg(va, char *), shf);
- break;
- case 'S': /* word */
- tputS(va_arg(va, char *), shf);
- break;
- case 'd': /* decimal */
- shf_fprintf(shf, "%d", va_arg(va, int));
- break;
- case 'u': /* decimal */
- shf_fprintf(shf, "%u", va_arg(va, unsigned int));
- break;
- case 'T': /* format tree */
- ptree(va_arg(va, struct op *), indent, shf);
- break;
- case ';': /* newline or ; */
- case 'N': /* newline or space */
- if (shf->flags & SHF_STRING) {
- if (c == ';')
- tputc(';', shf);
- tputc(' ', shf);
- } else {
- int i;
-
- tputc('\n', shf);
- for (i = indent; i >= 8; i -= 8)
- tputc('\t', shf);
- for (; i > 0; --i)
- tputc(' ', shf);
- }
- break;
- case 'R':
- pioact(shf, indent, va_arg(va, struct ioword *));
- break;
- default:
- tputc(c, shf);
- break;
- }
- } else
- tputc(c, shf);
- }
-}
-
-/*
- * copy tree (for function definition)
- */
-struct op *
-tcopy(struct op *t, Area *ap)
-{
- struct op *r;
- const char **tw;
- char **rw;
-
- if (t == NULL)
- return (NULL);
-
- r = alloc(sizeof(struct op), ap);
-
- r->type = t->type;
- r->u.evalflags = t->u.evalflags;
-
- if (t->type == TCASE)
- r->str = wdcopy(t->str, ap);
- else
- strdupx(r->str, t->str, ap);
-
- if (t->vars == NULL)
- r->vars = NULL;
- else {
- for (tw = (const char **)t->vars; *tw++ != NULL; )
- ;
- rw = r->vars = alloc((tw - (const char **)t->vars + 1) *
- sizeof(*tw), ap);
- for (tw = (const char **)t->vars; *tw != NULL; )
- *rw++ = wdcopy(*tw++, ap);
- *rw = NULL;
- }
-
- if (t->args == NULL)
- r->args = NULL;
- else {
- for (tw = t->args; *tw++ != NULL; )
- ;
- r->args = (const char **)(rw = alloc((tw - t->args + 1) *
- sizeof(*tw), ap));
- for (tw = t->args; *tw != NULL; )
- *rw++ = wdcopy(*tw++, ap);
- *rw = NULL;
- }
-
- r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
-
- r->left = tcopy(t->left, ap);
- r->right = tcopy(t->right, ap);
- r->lineno = t->lineno;
-
- return (r);
-}
-
-char *
-wdcopy(const char *wp, Area *ap)
-{
- size_t len = wdscan(wp, EOS) - wp;
- return (memcpy(alloc(len, ap), wp, len));
-}
-
-/* return the position of prefix c in wp plus 1 */
-const char *
-wdscan(const char *wp, int c)
-{
- int nest = 0;
-
- while (1)
- switch (*wp++) {
- case EOS:
- return (wp);
- case ADELIM:
- if (c == ADELIM)
- return (wp + 1);
- /* FALLTHROUGH */
- case CHAR:
- case QCHAR:
- wp++;
- break;
- case COMSUB:
- case EXPRSUB:
- while (*wp++ != 0)
- ;
- break;
- case OQUOTE:
- case CQUOTE:
- break;
- case OSUBST:
- nest++;
- while (*wp++ != '\0')
- ;
- break;
- case CSUBST:
- wp++;
- if (c == CSUBST && nest == 0)
- return (wp);
- nest--;
- break;
- case OPAT:
- nest++;
- wp++;
- break;
- case SPAT:
- case CPAT:
- if (c == wp[-1] && nest == 0)
- return (wp);
- if (wp[-1] == CPAT)
- nest--;
- break;
- default:
- internal_warningf(
- "wdscan: unknown char 0x%x (carrying on)",
- wp[-1]);
- }
-}
-
-/* return a copy of wp without any of the mark up characters and
- * with quote characters (" ' \) stripped.
- * (string is allocated from ATEMP)
- */
-char *
-wdstrip(const char *wp, bool keepq, bool make_magic)
-{
- struct shf shf;
- int c;
-
- shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
-
- /* problems:
- * `...` -> $(...)
- * x${foo:-"hi"} -> x${foo:-hi}
- * x${foo:-'hi'} -> x${foo:-hi} unless keepq
- */
- while (1)
- switch (*wp++) {
- case EOS:
- return (shf_sclose(&shf)); /* null terminates */
- case ADELIM:
- case CHAR:
- c = *wp++;
- if (make_magic && (ISMAGIC(c) || c == '[' || c == NOT ||
- c == '-' || c == ']' || c == '*' || c == '?'))
- shf_putchar(MAGIC, &shf);
- shf_putchar(c, &shf);
- break;
- case QCHAR:
- c = *wp++;
- if (keepq && (c == '"' || c == '`' || c == '$' || c == '\\'))
- shf_putchar('\\', &shf);
- shf_putchar(c, &shf);
- break;
- case COMSUB:
- shf_puts("$(", &shf);
- while (*wp != 0)
- shf_putchar(*wp++, &shf);
- shf_putchar(')', &shf);
- break;
- case EXPRSUB:
- shf_puts("$((", &shf);
- while (*wp != 0)
- shf_putchar(*wp++, &shf);
- shf_puts("))", &shf);
- break;
- case OQUOTE:
- break;
- case CQUOTE:
- break;
- case OSUBST:
- shf_putchar('$', &shf);
- if (*wp++ == '{')
- shf_putchar('{', &shf);
- while ((c = *wp++) != 0)
- shf_putchar(c, &shf);
- break;
- case CSUBST:
- if (*wp++ == '}')
- shf_putchar('}', &shf);
- break;
- case OPAT:
- if (make_magic) {
- shf_putchar(MAGIC, &shf);
- shf_putchar(*wp++ | 0x80, &shf);
- } else {
- shf_putchar(*wp++, &shf);
- shf_putchar('(', &shf);
- }
- break;
- case SPAT:
- if (make_magic)
- shf_putchar(MAGIC, &shf);
- shf_putchar('|', &shf);
- break;
- case CPAT:
- if (make_magic)
- shf_putchar(MAGIC, &shf);
- shf_putchar(')', &shf);
- break;
- }
-}
-
-static struct ioword **
-iocopy(struct ioword **iow, Area *ap)
-{
- struct ioword **ior;
- int i;
-
- for (ior = iow; *ior++ != NULL; )
- ;
- ior = alloc((ior - iow + 1) * sizeof(struct ioword *), ap);
-
- for (i = 0; iow[i] != NULL; i++) {
- struct ioword *p, *q;
-
- p = iow[i];
- q = alloc(sizeof(struct ioword), ap);
- ior[i] = q;
- *q = *p;
- if (p->name != NULL)
- q->name = wdcopy(p->name, ap);
- if (p->delim != NULL)
- q->delim = wdcopy(p->delim, ap);
- if (p->heredoc != NULL)
- strdupx(q->heredoc, p->heredoc, ap);
- }
- ior[i] = NULL;
-
- return (ior);
-}
-
-/*
- * free tree (for function definition)
- */
-void
-tfree(struct op *t, Area *ap)
-{
- char **w;
-
- if (t == NULL)
- return;
-
- if (t->str != NULL)
- afree(t->str, ap);
-
- if (t->vars != NULL) {
- for (w = t->vars; *w != NULL; w++)
- afree(*w, ap);
- afree(t->vars, ap);
- }
-
- if (t->args != NULL) {
- union mksh_ccphack cw;
- /* XXX we assume the caller is right */
- cw.ro = t->args;
- for (w = cw.rw; *w != NULL; w++)
- afree(*w, ap);
- afree(t->args, ap);
- }
-
- if (t->ioact != NULL)
- iofree(t->ioact, ap);
-
- tfree(t->left, ap);
- tfree(t->right, ap);
-
- afree(t, ap);
-}
-
-static void
-iofree(struct ioword **iow, Area *ap)
-{
- struct ioword **iop;
- struct ioword *p;
-
- for (iop = iow; (p = *iop++) != NULL; ) {
- if (p->name != NULL)
- afree(p->name, ap);
- if (p->delim != NULL)
- afree(p->delim, ap);
- if (p->heredoc != NULL)
- afree(p->heredoc, ap);
- afree(p, ap);
- }
- afree(iow, ap);
-}
diff --git a/mksh/src/var.c b/mksh/src/var.c
deleted file mode 100644
index 4e9729e..0000000
--- a/mksh/src/var.c
+++ /dev/null
@@ -1,1490 +0,0 @@
-/* $OpenBSD: var.c,v 1.34 2007/10/15 02:16:35 deraadt Exp $ */
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * 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.
- */
-
-#include "sh.h"
-
-#if defined(__OpenBSD__)
-#include <sys/sysctl.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.110 2010/07/25 11:35:43 tg Exp $");
-
-/*
- * Variables
- *
- * WARNING: unreadable code, needs a rewrite
- *
- * if (flag&INTEGER), val.i contains integer value, and type contains base.
- * otherwise, (val.s + type) contains string value.
- * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
- */
-static struct tbl vtemp;
-static struct table specials;
-static char *formatstr(struct tbl *, const char *);
-static void exportprep(struct tbl *, const char *);
-static int special(const char *);
-static void unspecial(const char *);
-static void getspec(struct tbl *);
-static void setspec(struct tbl *);
-static void unsetspec(struct tbl *);
-static int getint(struct tbl *, mksh_ari_t *, bool);
-static mksh_ari_t intval(struct tbl *);
-static struct tbl *arraysearch(struct tbl *, uint32_t);
-static const char *array_index_calc(const char *, bool *, uint32_t *);
-static uint32_t oaathash_update(register uint32_t, register const uint8_t *,
- register size_t);
-static uint32_t oaathash_finalise(register uint32_t);
-
-uint8_t set_refflag = 0;
-
-/*
- * create a new block for function calls and simple commands
- * assume caller has allocated and set up e->loc
- */
-void
-newblock(void)
-{
- struct block *l;
- static const char *empty[] = { null };
-
- l = alloc(sizeof(struct block), ATEMP);
- l->flags = 0;
- ainit(&l->area); /* todo: could use e->area (l->area => l->areap) */
- if (!e->loc) {
- l->argc = 0;
- l->argv = empty;
- } else {
- l->argc = e->loc->argc;
- l->argv = e->loc->argv;
- }
- l->exit = l->error = NULL;
- ktinit(&l->vars, &l->area, 0);
- ktinit(&l->funs, &l->area, 0);
- l->next = e->loc;
- e->loc = l;
-}
-
-/*
- * pop a block handling special variables
- */
-void
-popblock(void)
-{
- struct block *l = e->loc;
- struct tbl *vp, **vpp = l->vars.tbls, *vq;
- int i;
-
- e->loc = l->next; /* pop block */
- for (i = l->vars.size; --i >= 0; )
- if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
- if ((vq = global(vp->name))->flag & ISSET)
- setspec(vq);
- else
- unsetspec(vq);
- }
- if (l->flags & BF_DOGETOPTS)
- user_opt = l->getopts_state;
- afreeall(&l->area);
- afree(l, ATEMP);
-}
-
-/* called by main() to initialise variable data structures */
-#define VARSPEC_DEFNS
-#include "var_spec.h"
-
-enum var_specs {
-#define VARSPEC_ENUMS
-#include "var_spec.h"
- V_MAX
-};
-
-static const char * const initvar_names[] = {
-#define VARSPEC_ITEMS
-#include "var_spec.h"
-};
-
-void
-initvar(void)
-{
- int i = 0;
- struct tbl *tp;
-
- ktinit(&specials, APERM,
- /* must be 80% of 2^n (currently 12 specials) */ 16);
- while (i < V_MAX - 1) {
- tp = ktenter(&specials, initvar_names[i],
- hash(initvar_names[i]));
- tp->flag = DEFINED|ISSET;
- tp->type = ++i;
- }
-}
-
-/* Used to calculate an array index for global()/local(). Sets *arrayp to
- * true if this is an array, sets *valp to the array index, returns
- * the basename of the array.
- */
-static const char *
-array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
-{
- const char *p;
- int len;
- char *ap = NULL;
-
- *arrayp = false;
- redo_from_ref:
- p = skip_varname(n, false);
- if (!set_refflag && (p != n) && ksh_isalphx(n[0])) {
- struct block *l = e->loc;
- struct tbl *vp;
- char *vn;
- uint32_t h;
-
- strndupx(vn, n, p - n, ATEMP);
- h = hash(vn);
- /* check if this is a reference */
- do {
- vp = ktsearch(&l->vars, vn, h);
- } while (!vp && (l = l->next));
- afree(vn, ATEMP);
- if (vp && (vp->flag & (DEFINED|ASSOC|ARRAY)) ==
- (DEFINED|ASSOC)) {
- char *cp;
-
- /* gotcha! */
- cp = shf_smprintf("%s%s", str_val(vp), p);
- afree(ap, ATEMP);
- n = ap = cp;
- goto redo_from_ref;
- }
- }
-
- if (p != n && *p == '[' && (len = array_ref_len(p))) {
- char *sub, *tmp;
- mksh_ari_t rval;
-
- /* Calculate the value of the subscript */
- *arrayp = true;
- strndupx(tmp, p + 1, len - 2, ATEMP);
- sub = substitute(tmp, 0);
- afree(tmp, ATEMP);
- strndupx(n, n, p - n, ATEMP);
- evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
- *valp = (uint32_t)rval;
- afree(sub, ATEMP);
- }
- return (n);
-}
-
-/*
- * Search for variable, if not found create globally.
- */
-struct tbl *
-global(const char *n)
-{
- struct block *l = e->loc;
- struct tbl *vp;
- int c;
- bool array;
- uint32_t h, val;
-
- /* Check to see if this is an array */
- n = array_index_calc(n, &array, &val);
- h = hash(n);
- c = n[0];
- if (!ksh_isalphx(c)) {
- if (array)
- errorf("bad substitution");
- vp = &vtemp;
- vp->flag = DEFINED;
- vp->type = 0;
- vp->areap = ATEMP;
- *vp->name = c;
- if (ksh_isdigit(c)) {
- for (c = 0; ksh_isdigit(*n); n++)
- c = c*10 + *n-'0';
- if (c <= l->argc)
- /* setstr can't fail here */
- setstr(vp, l->argv[c], KSH_RETURN_ERROR);
- vp->flag |= RDONLY;
- return (vp);
- }
- vp->flag |= RDONLY;
- if (n[1] != '\0')
- return (vp);
- vp->flag |= ISSET|INTEGER;
- switch (c) {
- case '$':
- vp->val.i = kshpid;
- break;
- case '!':
- /* If no job, expand to nothing */
- if ((vp->val.i = j_async()) == 0)
- vp->flag &= ~(ISSET|INTEGER);
- break;
- case '?':
- vp->val.i = exstat;
- break;
- case '#':
- vp->val.i = l->argc;
- break;
- case '-':
- vp->flag &= ~INTEGER;
- vp->val.s = getoptions();
- break;
- default:
- vp->flag &= ~(ISSET|INTEGER);
- }
- return (vp);
- }
- for (l = e->loc; ; l = l->next) {
- vp = ktsearch(&l->vars, n, h);
- if (vp != NULL) {
- if (array)
- return (arraysearch(vp, val));
- else
- return (vp);
- }
- if (l->next == NULL)
- break;
- }
- vp = ktenter(&l->vars, n, h);
- if (array)
- vp = arraysearch(vp, val);
- vp->flag |= DEFINED;
- if (special(n))
- vp->flag |= SPECIAL;
- return (vp);
-}
-
-/*
- * Search for local variable, if not found create locally.
- */
-struct tbl *
-local(const char *n, bool copy)
-{
- struct block *l = e->loc;
- struct tbl *vp;
- bool array;
- uint32_t h, val;
-
- /* Check to see if this is an array */
- n = array_index_calc(n, &array, &val);
- h = hash(n);
- if (!ksh_isalphx(*n)) {
- vp = &vtemp;
- vp->flag = DEFINED|RDONLY;
- vp->type = 0;
- vp->areap = ATEMP;
- return (vp);
- }
- vp = ktenter(&l->vars, n, h);
- if (copy && !(vp->flag & DEFINED)) {
- struct block *ll = l;
- struct tbl *vq = NULL;
-
- while ((ll = ll->next) && !(vq = ktsearch(&ll->vars, n, h)))
- ;
- if (vq) {
- vp->flag |= vq->flag &
- (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
- ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
- if (vq->flag & INTEGER)
- vp->type = vq->type;
- vp->u2.field = vq->u2.field;
- }
- }
- if (array)
- vp = arraysearch(vp, val);
- vp->flag |= DEFINED;
- if (special(n))
- vp->flag |= SPECIAL;
- return (vp);
-}
-
-/* get variable string value */
-char *
-str_val(struct tbl *vp)
-{
- char *s;
-
- if ((vp->flag&SPECIAL))
- getspec(vp);
- if (!(vp->flag&ISSET))
- s = null; /* special to dollar() */
- else if (!(vp->flag&INTEGER)) /* string source */
- s = vp->val.s + vp->type;
- else { /* integer source */
- /* worst case number length is when base=2 */
- /* 1 (minus) + 2 (base, up to 36) + 1 ('#') + number of bits
- * in the mksh_uari_t + 1 (NUL) */
- char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1];
- const char *digits = (vp->flag & UCASEV_AL) ?
- digits_uc : digits_lc;
- mksh_uari_t n;
- int base;
-
- s = strbuf + sizeof(strbuf);
- if (vp->flag & INT_U)
- n = vp->val.u;
- else
- n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;
- base = (vp->type == 0) ? 10 : vp->type;
-
- if (base == 1) {
- size_t sz = 1;
-
- *(s = strbuf) = '1';
- s[1] = '#';
- if (!UTFMODE || ((n & 0xFF80) == 0xEF80))
- /* OPTU-16 -> raw octet */
- s[2] = n & 0xFF;
- else
- sz = utf_wctomb(s + 2, n);
- s[2 + sz] = '\0';
- } else {
- *--s = '\0';
- do {
- *--s = digits[n % base];
- n /= base;
- } while (n != 0);
- if (base != 10) {
- *--s = '#';
- *--s = digits[base % 10];
- if (base >= 10)
- *--s = digits[base / 10];
- }
- if (!(vp->flag & INT_U) && vp->val.i < 0)
- *--s = '-';
- }
- if (vp->flag & (RJUST|LJUST)) /* case already dealt with */
- s = formatstr(vp, s);
- else
- strdupx(s, s, ATEMP);
- }
- return (s);
-}
-
-/* get variable integer value, with error checking */
-static mksh_ari_t
-intval(struct tbl *vp)
-{
- mksh_ari_t num;
- int base;
-
- base = getint(vp, &num, false);
- if (base == -1)
- /* XXX check calls - is error here ok by POSIX? */
- errorf("%s: bad number", str_val(vp));
- return (num);
-}
-
-/* set variable to string value */
-int
-setstr(struct tbl *vq, const char *s, int error_ok)
-{
- char *salloc = NULL;
- int no_ro_check = error_ok & 0x4;
-
- error_ok &= ~0x4;
- if ((vq->flag & RDONLY) && !no_ro_check) {
- warningf(true, "%s: is read only", vq->name);
- if (!error_ok)
- errorfz();
- return (0);
- }
- if (!(vq->flag&INTEGER)) { /* string dest */
- if ((vq->flag&ALLOC)) {
- /* debugging */
- if (s >= vq->val.s &&
- s <= vq->val.s + strlen(vq->val.s))
- internal_errorf(
- "setstr: %s=%s: assigning to self",
- vq->name, s);
- afree(vq->val.s, vq->areap);
- }
- vq->flag &= ~(ISSET|ALLOC);
- vq->type = 0;
- if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
- s = salloc = formatstr(vq, s);
- if ((vq->flag&EXPORT))
- exportprep(vq, s);
- else {
- strdupx(vq->val.s, s, vq->areap);
- vq->flag |= ALLOC;
- }
- } else { /* integer dest */
- if (!v_evaluate(vq, s, error_ok, true))
- return (0);
- }
- vq->flag |= ISSET;
- if ((vq->flag&SPECIAL))
- setspec(vq);
- afree(salloc, ATEMP);
- return (1);
-}
-
-/* set variable to integer */
-void
-setint(struct tbl *vq, mksh_ari_t n)
-{
- if (!(vq->flag&INTEGER)) {
- struct tbl *vp = &vtemp;
- vp->flag = (ISSET|INTEGER);
- vp->type = 0;
- vp->areap = ATEMP;
- vp->val.i = n;
- /* setstr can't fail here */
- setstr(vq, str_val(vp), KSH_RETURN_ERROR);
- } else
- vq->val.i = n;
- vq->flag |= ISSET;
- if ((vq->flag&SPECIAL))
- setspec(vq);
-}
-
-static int
-getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
-{
- char *s;
- int c, base, neg;
- bool have_base = false;
- mksh_ari_t num;
-
- if (vp->flag&SPECIAL)
- getspec(vp);
- /* XXX is it possible for ISSET to be set and val.s to be 0? */
- if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
- return (-1);
- if (vp->flag&INTEGER) {
- *nump = vp->val.i;
- return (vp->type);
- }
- s = vp->val.s + vp->type;
- if (s == NULL) /* redundant given initial test */
- s = null;
- 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;
- have_base = true;
- }
- for (c = *s++; c ; c = *s++) {
- if (c == '-') {
- neg++;
- continue;
- } else if (c == '#') {
- base = (int)num;
- if (have_base || base < 1 || base > 36)
- return (-1);
- if (base == 1) {
- unsigned int wc;
-
- if (!UTFMODE)
- wc = *(unsigned char *)s;
- else if (utf_mbtowc(&wc, s) == (size_t)-1)
- /* OPTU-8 -> OPTU-16 */
- /*
- * (with a twist: 1#\uEF80 converts
- * the same as 1#\x80 does, thus is
- * not round-tripping correctly XXX)
- */
- wc = 0xEF00 + *(unsigned char *)s;
- *nump = (mksh_ari_t)wc;
- return (1);
- }
- num = 0;
- have_base = true;
- continue;
- } else if (ksh_isdigit(c))
- c -= '0';
- else if (ksh_islower(c))
- c -= 'a' - 10;
- else if (ksh_isupper(c))
- c -= 'A' - 10;
- else
- return (-1);
- if (c < 0 || c >= base)
- return (-1);
- num = num * base + c;
- }
- if (neg)
- num = -num;
- *nump = num;
- return (base);
-}
-
-/* convert variable vq to integer variable, setting its value from vp
- * (vq and vp may be the same)
- */
-struct tbl *
-setint_v(struct tbl *vq, struct tbl *vp, bool arith)
-{
- int base;
- mksh_ari_t num;
-
- if ((base = getint(vp, &num, arith)) == -1)
- return (NULL);
- if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
- vq->flag &= ~ALLOC;
- afree(vq->val.s, vq->areap);
- }
- vq->val.i = num;
- if (vq->type == 0) /* default base */
- vq->type = base;
- vq->flag |= ISSET|INTEGER;
- if (vq->flag&SPECIAL)
- setspec(vq);
- return (vq);
-}
-
-static char *
-formatstr(struct tbl *vp, const char *s)
-{
- int olen, nlen;
- char *p, *q;
- size_t psiz;
-
- olen = utf_mbswidth(s);
-
- if (vp->flag & (RJUST|LJUST)) {
- if (!vp->u2.field) /* default field width */
- vp->u2.field = olen;
- nlen = vp->u2.field;
- } else
- nlen = olen;
-
- p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
- if (vp->flag & (RJUST|LJUST)) {
- int slen = olen, i = 0;
-
- if (vp->flag & RJUST) {
- const char *qq = s;
- int n = 0;
-
- while (i < slen)
- i += utf_widthadj(qq, &qq);
- /* strip trailing spaces (AT&T uses qq[-1] == ' ') */
- while (qq > s && ksh_isspace(qq[-1])) {
- --qq;
- --slen;
- }
- if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
- if (s[1] == '#')
- n = 2;
- else if (s[2] == '#')
- n = 3;
- if (vp->u2.field <= n)
- n = 0;
- }
- if (n) {
- memcpy(p, s, n);
- s += n;
- }
- while (slen > vp->u2.field)
- slen -= utf_widthadj(s, &s);
- if (vp->u2.field - slen)
- memset(p + n, (vp->flag & ZEROFIL) ? '0' : ' ',
- vp->u2.field - slen);
- slen -= n;
- shf_snprintf(p + vp->u2.field - slen,
- psiz - (vp->u2.field - slen),
- "%.*s", slen, s);
- } else {
- /* strip leading spaces/zeros */
- while (ksh_isspace(*s))
- s++;
- if (vp->flag & ZEROFIL)
- while (*s == '0')
- s++;
- shf_snprintf(p, nlen + 1, "%-*.*s",
- vp->u2.field, vp->u2.field, s);
- }
- } else
- memcpy(p, s, strlen(s) + 1);
-
- if (vp->flag & UCASEV_AL) {
- for (q = p; *q; q++)
- *q = ksh_toupper(*q);
- } else if (vp->flag & LCASEV) {
- for (q = p; *q; q++)
- *q = ksh_tolower(*q);
- }
-
- return (p);
-}
-
-/*
- * make vp->val.s be "name=value" for quick exporting.
- */
-static void
-exportprep(struct tbl *vp, const char *val)
-{
- char *xp;
- char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
- int namelen = strlen(vp->name);
- int vallen = strlen(val) + 1;
-
- vp->flag |= ALLOC;
- xp = alloc(namelen + 1 + vallen, vp->areap);
- memcpy(vp->val.s = xp, vp->name, namelen);
- xp += namelen;
- *xp++ = '=';
- vp->type = xp - vp->val.s; /* offset to value */
- memcpy(xp, val, vallen);
- if (op != NULL)
- afree(op, vp->areap);
-}
-
-/*
- * lookup variable (according to (set&LOCAL)),
- * set its attributes (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL,
- * LCASEV, UCASEV_AL), and optionally set its value if an assignment.
- */
-struct tbl *
-typeset(const char *var, Tflag set, Tflag clr, int field, int base)
-{
- struct tbl *vp;
- struct tbl *vpbase, *t;
- char *tvar;
- const char *val;
- int len;
-
- /* 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)
- errorf("%s: reference variable cannot be an array",
- var);
- len = array_ref_len(val);
- if (len == 0)
- return (NULL);
- /* 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 would be a major
- * security hole.
- */
- if (set & IMPORT) {
- int i;
- for (i = 1; i < len - 1; i++)
- if (!ksh_isdigit(val[i]))
- return (NULL);
- }
- val += len;
- }
- if (*val == '=')
- strndupx(tvar, var, val++ - var, ATEMP);
- else {
- /* Importing from original environment: must have an = */
- if (set & IMPORT)
- return (NULL);
- strdupx(tvar, var, ATEMP);
- val = NULL;
- /* handle foo[*] ⇒ foo (whole array) mapping for R39b */
- len = strlen(tvar);
- if (len > 3 && tvar[len-3] == '[' && tvar[len-2] == '*' &&
- tvar[len-1] == ']')
- tvar[len-3] = '\0';
- }
-
- /* Prevent typeset from creating a local PATH/ENV/SHELL */
- if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
- strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
- errorf("%s: restricted", tvar);
-
- vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? true : false) :
- global(tvar);
- if (set_refflag == 2 && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
- vp->flag &= ~ASSOC;
- else if (set_refflag == 1) {
- if (vp->flag & ARRAY) {
- struct tbl *a, *tmp;
-
- /* Free up entire array */
- for (a = vp->u.array; a; ) {
- tmp = a;
- a = a->u.array;
- if (tmp->flag & ALLOC)
- afree(tmp->val.s, tmp->areap);
- afree(tmp, tmp->areap);
- }
- vp->u.array = NULL;
- vp->flag &= ~ARRAY;
- }
- vp->flag |= ASSOC;
- }
-
- set &= ~(LOCAL|LOCAL_COPY);
-
- vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
-
- /* only allow export flag to be set. AT&T ksh allows any attribute to
- * be changed which means it can be truncated or modified (-L/-R/-Z/-i)
- */
- if ((vpbase->flag&RDONLY) &&
- (val || clr || (set & ~EXPORT)))
- /* XXX check calls - is error here ok by POSIX? */
- errorf("%s: is read only", tvar);
- afree(tvar, ATEMP);
-
- /* most calls are with set/clr == 0 */
- if (set | clr) {
- bool ok = true;
-
- /* XXX if x[0] isn't set, there will be problems: need to have
- * one copy of attributes for arrays...
- */
- for (t = vpbase; t; t = t->u.array) {
- bool fake_assign;
- char *s = NULL;
- char *free_me = NULL;
-
- fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
- ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
- ((t->flag & INTEGER) && (clr & INTEGER)) ||
- (!(t->flag & INTEGER) && (set & INTEGER)));
- if (fake_assign) {
- if (t->flag & INTEGER) {
- s = str_val(t);
- free_me = NULL;
- } else {
- s = t->val.s + t->type;
- free_me = (t->flag & ALLOC) ? t->val.s :
- NULL;
- }
- t->flag &= ~ALLOC;
- }
- if (!(t->flag & INTEGER) && (set & INTEGER)) {
- t->type = 0;
- t->flag &= ~ALLOC;
- }
- t->flag = (t->flag | set) & ~clr;
- /* Don't change base if assignment is to be done,
- * in case assignment fails.
- */
- if ((set & INTEGER) && base > 0 && (!val || t != vp))
- t->type = base;
- if (set & (LJUST|RJUST|ZEROFIL))
- t->u2.field = field;
- if (fake_assign) {
- if (!setstr(t, s, KSH_RETURN_ERROR)) {
- /* Somewhat arbitrary action here:
- * zap contents of variable, but keep
- * the flag settings.
- */
- ok = false;
- if (t->flag & INTEGER)
- t->flag &= ~ISSET;
- else {
- if (t->flag & ALLOC)
- afree(t->val.s, t->areap);
- t->flag &= ~(ISSET|ALLOC);
- t->type = 0;
- }
- }
- if (free_me)
- afree(free_me, t->areap);
- }
- }
- if (!ok)
- errorfz();
- }
-
- if (val != NULL) {
- if (vp->flag&INTEGER) {
- /* do not zero base before assignment */
- setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
- /* Done after assignment to override default */
- if (base > 0)
- vp->type = base;
- } else
- /* setstr can't fail (readonly check already done) */
- setstr(vp, val, KSH_RETURN_ERROR | 0x4);
- }
-
- /* only x[0] is ever exported, so use vpbase */
- if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
- vpbase->type == 0)
- exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
-
- return (vp);
-}
-
-/**
- * Unset a variable. The flags can be:
- * |1 = tear down entire array
- * |2 = keep attributes, only unset content
- */
-void
-unset(struct tbl *vp, int flags)
-{
- if (vp->flag & ALLOC)
- afree(vp->val.s, vp->areap);
- if ((vp->flag & ARRAY) && (flags & 1)) {
- struct tbl *a, *tmp;
-
- /* Free up entire array */
- for (a = vp->u.array; a; ) {
- tmp = a;
- a = a->u.array;
- if (tmp->flag & ALLOC)
- afree(tmp->val.s, tmp->areap);
- afree(tmp, tmp->areap);
- }
- vp->u.array = NULL;
- }
- if (flags & 2) {
- vp->flag &= ~(ALLOC|ISSET);
- return;
- }
- /* If foo[0] is being unset, the remainder of the array is kept... */
- vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
- if (vp->flag & SPECIAL)
- unsetspec(vp); /* responsible for 'unspecial'ing var */
-}
-
-/* return a pointer to the first char past a legal variable name (returns the
- * argument if there is no legal name, returns a pointer to the terminating
- * NUL if whole string is legal).
- */
-const char *
-skip_varname(const char *s, int aok)
-{
- int alen;
-
- if (s && ksh_isalphx(*s)) {
- while (*++s && ksh_isalnux(*s))
- ;
- if (aok && *s == '[' && (alen = array_ref_len(s)))
- s += alen;
- }
- return (s);
-}
-
-/* Return a pointer to the first character past any legal variable name */
-const char *
-skip_wdvarname(const char *s,
- int aok) /* skip array de-reference? */
-{
- if (s[0] == CHAR && ksh_isalphx(s[1])) {
- do {
- s += 2;
- } while (s[0] == CHAR && ksh_isalnux(s[1]));
- if (aok && s[0] == CHAR && s[1] == '[') {
- /* skip possible array de-reference */
- const char *p = s;
- char c;
- int depth = 0;
-
- while (1) {
- if (p[0] != CHAR)
- break;
- c = p[1];
- p += 2;
- if (c == '[')
- depth++;
- else if (c == ']' && --depth == 0) {
- s = p;
- break;
- }
- }
- }
- }
- return (s);
-}
-
-/* Check if coded string s is a variable name */
-int
-is_wdvarname(const char *s, int aok)
-{
- const char *p = skip_wdvarname(s, aok);
-
- return (p != s && p[0] == EOS);
-}
-
-/* Check if coded string s is a variable assignment */
-int
-is_wdvarassign(const char *s)
-{
- const char *p = skip_wdvarname(s, true);
-
- return (p != s && p[0] == CHAR && p[1] == '=');
-}
-
-/*
- * Make the exported environment from the exported names in the dictionary.
- */
-char **
-makenv(void)
-{
- struct block *l;
- XPtrV denv;
- struct tbl *vp, **vpp;
- int i;
-
- XPinit(denv, 64);
- for (l = e->loc; l != NULL; l = l->next)
- for (vpp = l->vars.tbls, i = l->vars.size; --i >= 0; )
- if ((vp = *vpp++) != NULL &&
- (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
- struct block *l2;
- struct tbl *vp2;
- uint32_t h = hash(vp->name);
-
- /* unexport any redefined instances */
- for (l2 = l->next; l2 != NULL; l2 = l2->next) {
- vp2 = ktsearch(&l2->vars, vp->name, h);
- if (vp2 != NULL)
- vp2->flag &= ~EXPORT;
- }
- if ((vp->flag&INTEGER)) {
- /* integer to string */
- char *val;
- val = str_val(vp);
- vp->flag &= ~(INTEGER|RDONLY|SPECIAL);
- /* setstr can't fail here */
- setstr(vp, val, KSH_RETURN_ERROR);
- }
- XPput(denv, vp->val.s);
- }
- XPput(denv, NULL);
- return ((char **)XPclose(denv));
-}
-
-/* Bob Jenkins' one-at-a-time hash */
-static uint32_t
-oaathash_update(register uint32_t h, register const uint8_t *cp,
- register size_t n)
-{
- while (n--) {
- h += *cp++;
- h += h << 10;
- h ^= h >> 6;
- }
-
- return (h);
-}
-
-static uint32_t
-oaathash_finalise(register uint32_t h)
-{
- h += h << 3;
- h ^= h >> 11;
- h += h << 15;
-
- return (h);
-}
-
-uint32_t
-oaathash_full(register const uint8_t *bp)
-{
- register uint32_t h = 0;
- register uint8_t c;
-
- while ((c = *bp++)) {
- h += c;
- h += h << 10;
- h ^= h >> 6;
- }
-
- return (oaathash_finalise(h));
-}
-
-void
-change_random(const void *vp, size_t n)
-{
- register uint32_t h = 0x100;
-#if defined(__OpenBSD__)
- int mib[2];
- uint8_t k[3];
- size_t klen;
-#endif
-
- kshstate_v.cr_dp = vp;
- kshstate_v.cr_dsz = n;
- gettimeofday(&kshstate_v.cr_tv, NULL);
- h = oaathash_update(oaathash_update(h, (void *)&kshstate_v,
- sizeof(kshstate_v)), vp, n);
- kshstate_v.lcg_state_ = oaathash_finalise(h);
-
-#if defined(__OpenBSD__)
- /* OpenBSD, MirBSD: proper kernel entropy comes at zero cost */
-
- mib[0] = CTL_KERN;
- mib[1] = KERN_ARND;
- klen = sizeof(k);
- sysctl(mib, 2, k, &klen, &kshstate_v.lcg_state_,
- sizeof(kshstate_v.lcg_state_));
- /* we ignore failures and take in k anyway */
- h = oaathash_update(h, k, sizeof(k));
- kshstate_v.lcg_state_ = oaathash_finalise(h);
-#elif defined(MKSH_A4PB)
- /* forced by the user to use arc4random_pushb(3) • Cygwin? */
- {
- uint32_t prv;
-
- prv = arc4random_pushb(&kshstate_v.lcg_state_,
- sizeof(kshstate_v.lcg_state_));
- h = oaathash_update(h, &prv, sizeof(prv));
- }
- kshstate_v.lcg_state_ = oaathash_finalise(h);
-#endif
-}
-
-/*
- * handle special variables with side effects - PATH, SECONDS.
- */
-
-/* Test if name is a special parameter */
-static int
-special(const char *name)
-{
- struct tbl *tp;
-
- tp = ktsearch(&specials, name, hash(name));
- return (tp && (tp->flag & ISSET) ? tp->type : V_NONE);
-}
-
-/* Make a variable non-special */
-static void
-unspecial(const char *name)
-{
- struct tbl *tp;
-
- tp = ktsearch(&specials, name, hash(name));
- if (tp)
- ktdelete(tp);
-}
-
-static time_t seconds; /* time SECONDS last set */
-static int user_lineno; /* what user set $LINENO to */
-
-static void
-getspec(struct tbl *vp)
-{
- register mksh_ari_t i;
- int st;
-
- 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 = ((kshstate_v.lcg_state_ =
- 22695477 * kshstate_v.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:
- /*
- * Do NOT export COLUMNS/LINES. Many applications
- * check COLUMNS/LINES before checking ws.ws_col/row,
- * so if the app is started with C/L in the environ
- * 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;
- break;
- default:
- /* do nothing, do not touch vp at all */
- return;
- }
- vp->flag &= ~SPECIAL;
- setint(vp, i);
- vp->flag |= SPECIAL;
-}
-
-static void
-setspec(struct tbl *vp)
-{
- mksh_ari_t i;
- char *s;
- int st;
-
- switch ((st = special(vp->name))) {
- case V_PATH:
- if (path)
- afree(path, APERM);
- s = str_val(vp);
- strdupx(path, s, APERM);
- flushcom(1); /* clear tracked aliases */
- return;
- case V_IFS:
- setctypes(s = str_val(vp), C_IFS);
- ifs0 = *s;
- return;
- case V_TMPDIR:
- if (tmpdir) {
- afree(tmpdir, APERM);
- tmpdir = NULL;
- }
- /* Use tmpdir iff it is an absolute path, is writable and
- * searchable and is a directory...
- */
- {
- struct stat statb;
-
- s = str_val(vp);
- if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&
- stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
- strdupx(tmpdir, s, APERM);
- }
- break;
-#if HAVE_PERSISTENT_HISTORY
- case V_HISTFILE:
- sethistfile(str_val(vp));
- break;
-#endif
- case V_TMOUT:
- /* AT&T ksh seems to do this (only listen if integer) */
- if (vp->flag & INTEGER)
- ksh_tmout = vp->val.i >= 0 ? vp->val.i : 0;
- break;
-
- /* common sub-cases */
- case V_OPTIND:
- case V_HISTSIZE:
- case V_COLUMNS:
- case V_LINES:
- case V_RANDOM:
- case V_SECONDS:
- case V_LINENO:
- vp->flag &= ~SPECIAL;
- i = intval(vp);
- vp->flag |= SPECIAL;
- break;
- default:
- /* do nothing, do not touch vp at all */
- return;
- }
-
- /* 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_LINES:
- if (i >= MIN_LINS)
- x_lins = i;
- break;
- case V_RANDOM:
- /*
- * mksh R39d+ no longer has the traditional repeatability
- * of $RANDOM sequences, but always retains state
- */
- change_random(&i, sizeof(i));
- break;
- case V_SECONDS:
- {
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
- 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;
- }
-}
-
-static void
-unsetspec(struct tbl *vp)
-{
- switch (special(vp->name)) {
- case V_PATH:
- if (path)
- afree(path, APERM);
- strdupx(path, def_path, APERM);
- flushcom(1); /* clear tracked aliases */
- break;
- case V_IFS:
- setctypes(" \t\n", C_IFS);
- ifs0 = ' ';
- break;
- case V_TMPDIR:
- /* should not become unspecial */
- if (tmpdir) {
- afree(tmpdir, APERM);
- tmpdir = NULL;
- }
- break;
- case V_LINENO:
- case V_RANDOM:
- case V_SECONDS:
- case V_TMOUT: /* 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
- */
- }
-}
-
-/*
- * Search for (and possibly create) a table entry starting with
- * vp, indexed by val.
- */
-static struct tbl *
-arraysearch(struct tbl *vp, uint32_t val)
-{
- struct tbl *prev, *curr, *news;
- size_t len;
-
- vp->flag = (vp->flag | (ARRAY|DEFINED)) & ~ASSOC;
- /* The table entry is always [0] */
- if (val == 0)
- return (vp);
- prev = vp;
- curr = vp->u.array;
- while (curr && curr->ua.index < val) {
- prev = curr;
- curr = curr->u.array;
- }
- if (curr && curr->ua.index == val) {
- if (curr->flag&ISSET)
- return (curr);
- news = curr;
- } else
- news = NULL;
- len = strlen(vp->name) + 1;
- if (!news) {
- news = alloc(offsetof(struct tbl, name[0]) + len, vp->areap);
- memcpy(news->name, vp->name, len);
- }
- news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
- news->type = vp->type;
- news->areap = vp->areap;
- news->u2.field = vp->u2.field;
- news->ua.index = val;
-
- if (curr != news) { /* not reusing old array entry */
- prev->u.array = news;
- news->u.array = curr;
- }
- return (news);
-}
-
-/* Return the length of an array reference (eg, [1+2]) - cp is assumed
- * to point to the open bracket. Returns 0 if there is no matching closing
- * bracket.
- */
-int
-array_ref_len(const char *cp)
-{
- const char *s = cp;
- int c;
- int depth = 0;
-
- while ((c = *s++) && (c != ']' || --depth))
- if (c == '[')
- depth++;
- if (!c)
- return (0);
- return (s - cp);
-}
-
-/*
- * Make a copy of the base of an array name
- */
-char *
-arrayname(const char *str)
-{
- const char *p;
- char *rv;
-
- if ((p = cstrchr(str, '[')) == 0)
- /* Shouldn't happen, but why worry? */
- strdupx(rv, str, ATEMP);
- else
- strndupx(rv, str, p - str, ATEMP);
-
- return (rv);
-}
-
-/* set (or overwrite, if reset) the array variable var to the values in vals */
-mksh_uari_t
-set_array(const char *var, bool reset, const char **vals)
-{
- struct tbl *vp, *vq;
- mksh_uari_t i;
- const char *ccp;
-#ifndef MKSH_SMALL
- char *cp;
- mksh_uari_t j;
-#endif
-
- /* to get local array, use "typeset foo; set -A foo" */
- vp = global(var);
-
- /* Note: AT&T ksh allows set -A but not set +A of a read-only var */
- if ((vp->flag&RDONLY))
- errorf("%s: is read only", var);
- /* This code is quite non-optimal */
- if (reset)
- /* trash existing values and attributes */
- unset(vp, 1);
- /* todo: would be nice for assignment to completely succeed or
- * completely fail. Only really effects integer arrays:
- * evaluation of some of vals[] may fail...
- */
- i = 0;
-#ifndef MKSH_SMALL
- j = 0;
-#else
-#define j i
-#endif
- while ((ccp = vals[i])) {
-#ifndef MKSH_SMALL
- if (*ccp == '[') {
- int level = 0;
-
- while (*ccp) {
- if (*ccp == ']' && --level == 0)
- break;
- if (*ccp == '[')
- ++level;
- ++ccp;
- }
- if (*ccp == ']' && level == 0 && ccp[1] == '=') {
- strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
- ATEMP);
- evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
- KSH_UNWIND_ERROR, true);
- afree(cp, ATEMP);
- ccp += 2;
- } else
- ccp = vals[i];
- }
-#endif
-
- vq = arraysearch(vp, j);
- /* would be nice to deal with errors here... (see above) */
- setstr(vq, ccp, KSH_RETURN_ERROR);
- i++;
-#ifndef MKSH_SMALL
- j++;
-#endif
- }
-
- return (i);
-}
-
-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(false, false);
-#endif
- x_cols = -1;
- }
-
-#ifdef TIOCGWINSZ
- /* check if window size has changed */
- if (tty_fd >= 0) {
- struct winsize ws;
-
- if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
- if (ws.ws_col)
- x_cols = ws.ws_col;
- if (ws.ws_row)
- x_lins = ws.ws_row;
- }
- }
-#endif
-
- /* bounds check for sane values, use defaults otherwise */
- if (x_cols < MIN_COLS)
- x_cols = 80;
- if (x_lins < MIN_LINS)
- x_lins = 24;
-
-#ifdef SIGWINCH
- got_winch = 0;
-#endif
-}
-
-uint32_t
-evilhash(const char *s)
-{
- register uint32_t h = 0x100;
-
- h = oaathash_update(h, (void *)&kshstate_f, sizeof(kshstate_f));
- kshstate_f.h = oaathash_full((const uint8_t *)s);
- return (oaathash_finalise(oaathash_update(h,
- (void *)&kshstate_f.h, sizeof(kshstate_f.h))));
-}
diff --git a/mksh/src/var_spec.h b/mksh/src/var_spec.h
deleted file mode 100644
index 4035cc9..0000000
--- a/mksh/src/var_spec.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#if defined(VARSPEC_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.1 2009/09/26 03:40:03 tg Exp $");
-#define FN(name) /* nothing */
-#elif defined(VARSPEC_ENUMS)
-#define FN(name) V_##name,
-#define F0(name) V_##name = 0,
-#elif defined(VARSPEC_ITEMS)
-#define F0(name) /* nothing */
-#define FN(name) #name,
-#endif
-
-#ifndef F0
-#define F0 FN
-#endif
-
-/* 0 is always V_NONE */
-F0(NONE)
-
-/* 1 and up are special variables */
-FN(COLUMNS)
-#if HAVE_PERSISTENT_HISTORY
-FN(HISTFILE)
-#endif
-FN(HISTSIZE)
-FN(IFS)
-FN(LINENO)
-FN(LINES)
-FN(OPTIND)
-FN(PATH)
-FN(RANDOM)
-FN(SECONDS)
-FN(TMOUT)
-FN(TMPDIR)
-
-#undef FN
-#undef F0
-#undef VARSPEC_DEFNS
-#undef VARSPEC_ENUMS
-#undef VARSPEC_ITEMS