diff --git a/mksh/Android.mk b/mksh/Android.mk deleted file mode 100644 index e53b863c0..000000000 --- a/mksh/Android.mk +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright © 2010 -# Thorsten Glaser -# 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 e69de29bb..000000000 diff --git a/mksh/NOTICE b/mksh/NOTICE deleted file mode 100644 index 350061fe1..000000000 --- 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 - * - * 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 15d04320d..000000000 --- a/mksh/mkmf.sh +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright © 2010 -# Thorsten Glaser -# 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 0da5ea63b..000000000 --- a/mksh/mkshrc +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2010 -# Thorsten Glaser -# 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 b51d54117..000000000 --- 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 c98b1cac6..000000000 --- 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 -# -# 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} " /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='' - ui='' - ao='' -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>" &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 - ac_flags - 1 stackon "${ccpc}/GZ" 'if stack checks can be enabled' - #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 - #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 - #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 -/* 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" - #include - 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 - #include - 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 - #include - 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 - #include - int main(int ac, char **av) { return ((u_int8_t)(ptrdiff_t)av[ac]); } -EOF - -ac_test rlim_t <<-'EOF' - #include - #include - #include - #include - int main(void) { return ((int)(rlim_t)0); } -EOF - -# only testn: added later below -ac_testn sig_t <<-'EOF' - #include - #include - #include - int main(void) { return ((int)(ptrdiff_t)(sig_t)kill(0,0)); } -EOF - -ac_testn sighandler_t '!' sig_t 0 <<-'EOF' - #include - #include - #include - 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 - #include - #include - 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 - #include - int main(void) { return (strsignal(1)[0]); } -EOF - -# -# Environment: library functions -# -ac_testn flock_ex '' 'flock and mmap' <<-'EOF' - #include - #include - #include - #include - #include - 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 - 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 - #include - int main(void) { char tmpl[] = "X"; return (mkstemp(tmpl)); } -EOF - -ac_test nice <<-'EOF' - #include - int main(void) { return (nice(4)); } -EOF - -ac_test revoke <<-'EOF' - #include - #if HAVE_LIBUTIL_H - #include - #endif - #include - int main(int ac, char *av[]) { return (ac + revoke(av[0])); } -EOF - -ac_test setlocale_ctype '' 'setlocale(LC_CTYPE, "")' <<-'EOF' - #include - #include - int main(void) { return ((int)(ptrdiff_t)(void *)setlocale(LC_CTYPE, "")); } -EOF - -ac_test langinfo_codeset setlocale_ctype 0 'nl_langinfo(CODESET)' <<-'EOF' - #include - #include - 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 - #include - int main(int ac, char *av[]) { return (getmode(setmode(av[0]), - (mode_t)ac)); } - #endif -EOF - -ac_test setresugid <<-'EOF' - #include - #include - int main(void) { setresuid(0,0,0); return (setresgid(0,0,0)); } -EOF - -ac_test setgroups setresugid 0 <<-'EOF' - #include - #if HAVE_GRP_H - #include - #endif - #include - int main(void) { gid_t gid = 0; return (setgroups(0, &gid)); } -EOF - -ac_test strcasestr <<-'EOF' - #include - #include - #include - #if HAVE_STRINGS_H - #include - #endif - int main(int ac, char *av[]) { - return ((int)(ptrdiff_t)(void *)strcasestr(*av, av[ac])); - } -EOF - -ac_test strlcpy <<-'EOF' - #include - 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 /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 -#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 ' >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 - $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 e793e9572..000000000 --- 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 -# -# 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 = < 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 () { - $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 c8e8cafaa..000000000 --- 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 -# -# 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: - ---- -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 - '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$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 <}> . - 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 <{}b> <}> . - 30 . - 32 . - 33 . - 34 . - 35 . - 36 . - 37 . - 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 "ac" -stdin: - echo a[-]* -expected-stdout: - ac ---- -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 <<&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> - <2> - <3> - <4> ---- -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> - <2> - <3> - <4> ---- -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> - <2> - <3> - <4> ---- -name: IFS-space-colon-1 -description: - Simple test, IFS=: -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=: - 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=: - 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=: -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=: - 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=: -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> <> <> - <4> <:b::> - 5: [a] [b] - <6> - 7: [a] [] [c] - <8> <> - 9: [h] [ith] [ere] - <10> - <11> - 12: [A] [B] [] [D] - <13> <> ---- -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 ) -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 ) - 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]> - ---- -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 \ works inside of ${} -stdin: - abc=2 - echo ${ab\ - c} -expected-stdout: - 2 ---- -name: xxx-quoted-newline-2 -description: - Check that \ 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 \ 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> <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" 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 012 = a - b 0 = 0<> b - c 012 = c - d 012 = 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: - ---- -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 <&- || 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> <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 905de7e02..000000000 --- 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 - * - * 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 ! " # $ % & ' */ - 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 /* ^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 1333399c4..000000000 --- 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 c22e34693..000000000 --- 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 - * - * 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 && /* $(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 391321a14..000000000 --- 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 - * - * 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 */ - 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 6c5710c23..000000000 --- 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 - * - * 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 9d9c03a9d..000000000 --- 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 - * - * 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 ::= -*/ - -#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 2ac4c380e..000000000 --- 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 - * - * 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 -#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 - * - */ -#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 47326a15b..000000000 --- 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 - * - * 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 79627d175..000000000 --- a/mksh/src/lalloc.c +++ /dev/null @@ -1,123 +0,0 @@ -/*- - * Copyright © 2009 - * Thorsten Glaser - * - * 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 d0219e759..000000000 --- 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 - * - * 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 "<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 f962dd4b3..000000000 --- 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 - * - * 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 -#endif -#if HAVE_SETLOCALE_CTYPE -#include -#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 = ""; - 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 75a4de1b2..000000000 --- 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 - * - * 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 -#endif -#if HAVE_GRP_H -#include -#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 11588c9dd..000000000 --- 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 - * - * 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 -#endif -#include -#include -#include -#if HAVE_SYS_SYSMACROS_H -#include -#endif -#if HAVE_SYS_MKDEV_H -#include -#endif -#if HAVE_SYS_MMAN_H -#include -#endif -#include -#include -#include -#include -#include -#include -#if HAVE_LIBGEN_H -#include -#endif -#if HAVE_LIBUTIL_H -#include -#endif -#include -#if HAVE_PATHS_H -#include -#endif -#include -#include -#include -#include -#if HAVE_STDBOOL_H -#include -#endif -#include -#if HAVE_STDINT_H -#include -#endif -#include -#include -#include -#if HAVE_STRINGS_H -#include -#endif -#include -#include -#if HAVE_ULIMIT_H -#include -#endif -#include -#if HAVE_VALUES_H -#include -#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 aa5481ed5..000000000 --- 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 096275293..000000000 --- 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 - * - * 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 64b28672d..000000000 --- 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 - * - * 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 <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 aa861db7c..000000000 --- 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 - * - * 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("", 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 4e9729e05..000000000 --- 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 - * - * 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 -#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 4035cc992..000000000 --- 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