Move mksh to a separate project.
This was originally in system/core/mksh commit ba2627c6cdb3aaa40aebd362170c382b55b7b511 Author: Thorsten Glaser <tg@mirbsd.org> Date: Tue Aug 24 18:21:37 2010 +0200 Add mksh from CVS 2010/08/24 as system/core/mksh module Both shells (ash from system/core/sh, and mksh) are built by default but only the one where $(TARGET_SHELL) is set to is actually installed (the shell and the mkshrc configuration file are tagged shell_mksh for this to work). commit f41986bbc79055a4feed7266cac5c1b540296daf Author: Jeff Hamilton <jham@android.com> Date: Fri Sep 10 10:46:06 2010 -0500 Don't alias 'stop' to 'kill -STOP' Android has already has a stop command used to stop the main runtime and the alias interferes with testing tools that expect stop to kill the runtime. Change-Id: I5ddf28dbd0221148d3b8f55eaf4f1e7d046c9288
This commit is contained in:
parent
38fa6a9a7b
commit
5155f1c743
28 changed files with 37586 additions and 0 deletions
64
Android.mk
Normal file
64
Android.mk
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Copyright © 2010
|
||||
# Thorsten Glaser <t.glaser@tarent.de>
|
||||
# This file is provided under the same terms as mksh.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
|
||||
# /system/etc/mkshrc
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE:= mkshrc
|
||||
LOCAL_MODULE_TAGS:= shell_mksh
|
||||
LOCAL_MODULE_CLASS:= ETC
|
||||
LOCAL_MODULE_PATH:= $(TARGET_OUT)/etc
|
||||
LOCAL_SRC_FILES:= $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
|
||||
# /system/bin/mksh
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE:= mksh
|
||||
LOCAL_MODULE_TAGS:= shell_mksh
|
||||
|
||||
# mksh source files
|
||||
LOCAL_SRC_FILES:= src/lalloc.c src/edit.c src/eval.c src/exec.c \
|
||||
src/expr.c src/funcs.c src/histrap.c src/jobs.c \
|
||||
src/lex.c src/main.c src/misc.c src/shf.c \
|
||||
src/syn.c src/tree.c src/var.c
|
||||
|
||||
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc
|
||||
|
||||
LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src
|
||||
# additional flags first, then from Makefrag.inc: CFLAGS, CPPFLAGS
|
||||
LOCAL_CFLAGS:= -DMKSH_DEFAULT_EXECSHELL=\"/system/bin/sh\" \
|
||||
-DMKSH_DEFAULT_TMPDIR=\"/sqlite_stmt_journals\" \
|
||||
-DMKSHRC_PATH=\"/system/etc/mkshrc\" \
|
||||
-fwrapv \
|
||||
-DMKSH_ASSUME_UTF8=0 -DMKSH_NOPWNAM \
|
||||
-D_GNU_SOURCE \
|
||||
-DHAVE_ATTRIBUTE_BOUNDED=0 -DHAVE_ATTRIBUTE_FORMAT=1 \
|
||||
-DHAVE_ATTRIBUTE_NONNULL=1 -DHAVE_ATTRIBUTE_NORETURN=1 \
|
||||
-DHAVE_ATTRIBUTE_UNUSED=1 -DHAVE_ATTRIBUTE_USED=1 \
|
||||
-DHAVE_SYS_PARAM_H=1 -DHAVE_SYS_MKDEV_H=0 \
|
||||
-DHAVE_SYS_MMAN_H=1 -DHAVE_SYS_SYSMACROS_H=1 \
|
||||
-DHAVE_GRP_H=1 -DHAVE_LIBGEN_H=1 -DHAVE_LIBUTIL_H=0 \
|
||||
-DHAVE_PATHS_H=1 -DHAVE_STDBOOL_H=1 -DHAVE_STDINT_H=1 \
|
||||
-DHAVE_STRINGS_H=1 -DHAVE_ULIMIT_H=0 -DHAVE_VALUES_H=0 \
|
||||
-DHAVE_CAN_INTTYPES=1 -DHAVE_CAN_UCBINTS=1 \
|
||||
-DHAVE_CAN_INT8TYPE=1 -DHAVE_CAN_UCBINT8=1 \
|
||||
-DHAVE_RLIM_T=1 -DHAVE_SIG_T=1 -DHAVE_SYS_SIGNAME=1 \
|
||||
-DHAVE_SYS_SIGLIST=1 -DHAVE_STRSIGNAL=0 \
|
||||
-DHAVE_GETRUSAGE=1 -DHAVE_KILLPG=1 -DHAVE_MKNOD=0 \
|
||||
-DHAVE_MKSTEMP=1 -DHAVE_NICE=1 -DHAVE_REVOKE=0 \
|
||||
-DHAVE_SETLOCALE_CTYPE=0 -DHAVE_LANGINFO_CODESET=0 \
|
||||
-DHAVE_SETMODE=1 -DHAVE_SETRESUGID=1 \
|
||||
-DHAVE_SETGROUPS=1 -DHAVE_STRCASESTR=1 \
|
||||
-DHAVE_STRLCPY=1 -DHAVE_FLOCK_DECL=1 \
|
||||
-DHAVE_REVOKE_DECL=1 -DHAVE_SYS_SIGLIST_DECL=1 \
|
||||
-DHAVE_PERSISTENT_HISTORY=0
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
0
MODULE_LICENSE_BSD_LIKE
Normal file
0
MODULE_LICENSE_BSD_LIKE
Normal file
21
NOTICE
Normal file
21
NOTICE
Normal file
|
@ -0,0 +1,21 @@
|
|||
mksh is covered by The MirOS Licence:
|
||||
|
||||
/*-
|
||||
* Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
* are retained or reproduced in an accompanying document, permission
|
||||
* is granted to deal in this work without restriction, including un‐
|
||||
* limited rights to use, publicly perform, distribute, sell, modify,
|
||||
* merge, give away, or sublicence.
|
||||
*
|
||||
* This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
|
||||
* the utmost extent permitted by applicable law, neither express nor
|
||||
* implied; without malicious intent or gross negligence. In no event
|
||||
* may a licensor, author or contributor be held liable for indirect,
|
||||
* direct, other damage, loss, or other issues arising in any way out
|
||||
* of dealing in the work, even if advised of the possibility of such
|
||||
* damage or existence of a defect, except proven that it results out
|
||||
* of said person’s immediate fault when using the work as intended.
|
||||
*/
|
124
mkmf.sh
Normal file
124
mkmf.sh
Normal file
|
@ -0,0 +1,124 @@
|
|||
# Copyright © 2010
|
||||
# Thorsten Glaser <t.glaser@tarent.de>
|
||||
# This file is provided under the same terms as mksh.
|
||||
#-
|
||||
# Helper script to let src/Build.sh generate Makefrag.inc
|
||||
# which we in turn use in the manual creation of Android.mk
|
||||
#
|
||||
# This script is supposed to be run from/inside AOSP by the
|
||||
# porter of mksh to Android (and only manually).
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
srcdir=$(pwd)
|
||||
rm -rf tmp
|
||||
mkdir tmp
|
||||
cd ../../..
|
||||
aospdir=$(pwd)
|
||||
cd $srcdir/tmp
|
||||
|
||||
addvar() {
|
||||
_vn=$1; shift
|
||||
|
||||
eval $_vn=\"\$$_vn '$*"'
|
||||
}
|
||||
|
||||
CFLAGS=
|
||||
CPPFLAGS=
|
||||
LDFLAGS=
|
||||
LIBS=
|
||||
|
||||
# The definitions below were generated by examining the
|
||||
# output of the following command:
|
||||
# make showcommands out/target/product/generic/system/bin/mksh 2>&1 | tee log
|
||||
#
|
||||
# They are only used to let Build.sh find the compiler, header
|
||||
# files, linker and libraries to generate Makefrag.inc (similar
|
||||
# to what GNU autotools’ configure scripts do), and never used
|
||||
# during the real build process. We need this to port mksh to
|
||||
# the Android platform and it is crucial these are as close as
|
||||
# possible to the values used later. (You also must example the
|
||||
# results gathered from Makefrag.inc to see they are the same
|
||||
# across all Android platforms, or add appropriate ifdefs.)
|
||||
# Since we no longer use the NDK, the AOSP has to have been
|
||||
# built before using this script (targetting generic/emulator).
|
||||
|
||||
CC=$aospdir/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-gcc
|
||||
addvar CPPFLAGS -I$aospdir/system/core/include \
|
||||
-I$aospdir/hardware/libhardware/include \
|
||||
-I$aospdir/system/core/include \
|
||||
-I$aospdir/hardware/libhardware/include \
|
||||
-I$aospdir/hardware/libhardware_legacy/include \
|
||||
-I$aospdir/hardware/ril/include \
|
||||
-I$aospdir/dalvik/libnativehelper/include \
|
||||
-I$aospdir/frameworks/base/include \
|
||||
-I$aospdir/frameworks/base/opengl/include \
|
||||
-I$aospdir/external/skia/include \
|
||||
-I$aospdir/out/target/product/generic/obj/include \
|
||||
-I$aospdir/bionic/libc/arch-arm/include \
|
||||
-I$aospdir/bionic/libc/include \
|
||||
-I$aospdir/bionic/libstdc++/include \
|
||||
-I$aospdir/bionic/libc/kernel/common \
|
||||
-I$aospdir/bionic/libc/kernel/arch-arm \
|
||||
-I$aospdir/bionic/libm/include \
|
||||
-I$aospdir/bionic/libm/include/arch/arm \
|
||||
-I$aospdir/bionic/libthread_db/include \
|
||||
-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ \
|
||||
-I$aospdir/system/core/include/arch/linux-arm/ \
|
||||
-include $aospdir/system/core/include/arch/linux-arm/AndroidConfig.h \
|
||||
-DANDROID -DNDEBUG -UDEBUG
|
||||
addvar CFLAGS -fno-exceptions -Wno-multichar -msoft-float -fpic \
|
||||
-ffunction-sections -funwind-tables -fstack-protector -fno-short-enums \
|
||||
-march=armv5te -mtune=xscale -mthumb-interwork -fmessage-length=0 \
|
||||
-W -Wall -Wno-unused -Winit-self -Wpointer-arith -Werror=return-type \
|
||||
-Werror=non-virtual-dtor -Werror=address -Werror=sequence-point \
|
||||
-Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once \
|
||||
-fgcse-after-reload -frerun-cse-after-loop -frename-registers -mthumb \
|
||||
-Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64
|
||||
addvar LDFLAGS -nostdlib -Bdynamic -Wl,-T,$aospdir/build/core/armelf.x \
|
||||
-Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections \
|
||||
-Wl,-z,nocopyreloc -Wl,--no-undefined \
|
||||
$aospdir/out/target/product/generic/obj/lib/crtbegin_dynamic.o
|
||||
addvar LIBS -L$aospdir/out/target/product/generic/obj/lib \
|
||||
-Wl,-rpath-link=$aospdir/out/target/product/generic/obj/lib -lc \
|
||||
$aospdir/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/interwork/libgcc.a \
|
||||
$aospdir/out/target/product/generic/obj/lib/crtend_android.o
|
||||
|
||||
|
||||
### Override flags
|
||||
# We don’t even *support* UTF-8 by default ☹
|
||||
addvar CPPFLAGS -DMKSH_ASSUME_UTF8=0
|
||||
# No getpwnam() calls (affects "cd ~username/" only)
|
||||
addvar CPPFLAGS -DMKSH_NOPWNAM
|
||||
# Compile an extra small mksh (optional)
|
||||
#addvar CPPFLAGS -DMKSH_SMALL
|
||||
# Leave out the ulimit builtin
|
||||
#addvar CPPFLAGS -DMKSH_NO_LIMITS
|
||||
|
||||
# Set target platform
|
||||
TARGET_OS=Linux
|
||||
# Building with -std=c99 or -std=gnu99 clashes with Bionic headers
|
||||
HAVE_CAN_STDG99=0
|
||||
HAVE_CAN_STDC99=0
|
||||
export HAVE_CAN_STDG99 HAVE_CAN_STDC99
|
||||
|
||||
# Android-x86 does not have helper functions for ProPolice SSP
|
||||
# and AOSP adds the flags by itself (same for warning flags)
|
||||
HAVE_CAN_FNOSTRICTALIASING=0
|
||||
HAVE_CAN_FSTACKPROTECTORALL=0
|
||||
HAVE_CAN_WALL=0
|
||||
export HAVE_CAN_FNOSTRICTALIASING HAVE_CAN_FSTACKPROTECTORALL HAVE_CAN_WALL
|
||||
|
||||
# disable the mknod(8) built-in to get rid of needing setmode.c
|
||||
HAVE_MKNOD=0; export HAVE_MKNOD
|
||||
|
||||
# even the idea of persistent history on a phone is funny
|
||||
HAVE_PERSISTENT_HISTORY=0; export HAVE_PERSISTENT_HISTORY
|
||||
|
||||
# ... and run it!
|
||||
export CC CPPFLAGS CFLAGS LDFLAGS LIBS TARGET_OS
|
||||
sh ../src/Build.sh -M
|
||||
rv=$?
|
||||
test x0 = x"$rv" && mv -f Makefrag.inc ../
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
exit $rv
|
29
mkshrc
Normal file
29
mkshrc
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2010
|
||||
# Thorsten Glaser <t.glaser@tarent.de>
|
||||
# This file is provided under the same terms as mksh.
|
||||
#-
|
||||
# Minimal /system/etc/mkshrc for Android
|
||||
|
||||
: ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${HOSTNAME:=android}
|
||||
: ${SHELL:=$MKSH} ${USER:=$(typeset x=$(id); x=${x#*\(}; print -r -- ${x%%\)*})}
|
||||
if (( USER_ID )); then PS1='$'; else PS1='#'; fi
|
||||
function precmd {
|
||||
typeset e=$?
|
||||
|
||||
(( e )) && print -n "$e|"
|
||||
}
|
||||
PS1='$(precmd)$USER@$HOSTNAME:${PWD:-?} '"$PS1 "
|
||||
export HOME HOSTNAME MKSH PS1 SHELL TERM USER
|
||||
alias l='ls'
|
||||
alias la='l -a'
|
||||
alias ll='l -l'
|
||||
alias lo='l -a -l'
|
||||
|
||||
for p in ~/.bin; do
|
||||
[[ -d $p/. ]] || continue
|
||||
[[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
|
||||
done
|
||||
|
||||
unset p
|
||||
|
||||
: place customisations above this line
|
22
src/00-NOTE.txt
Normal file
22
src/00-NOTE.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
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
|
1600
src/Build.sh
Normal file
1600
src/Build.sh
Normal file
File diff suppressed because it is too large
Load diff
1241
src/check.pl
Normal file
1241
src/check.pl
Normal file
File diff suppressed because it is too large
Load diff
7443
src/check.t
Normal file
7443
src/check.t
Normal file
File diff suppressed because it is too large
Load diff
5249
src/edit.c
Normal file
5249
src/edit.c
Normal file
File diff suppressed because it is too large
Load diff
89
src/emacsfn.h
Normal file
89
src/emacsfn.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#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
|
1580
src/eval.c
Normal file
1580
src/eval.c
Normal file
File diff suppressed because it is too large
Load diff
1518
src/exec.c
Normal file
1518
src/exec.c
Normal file
File diff suppressed because it is too large
Load diff
895
src/expr.c
Normal file
895
src/expr.c
Normal file
|
@ -0,0 +1,895 @@
|
|||
/* $OpenBSD: expr.c,v 1.21 2009/06/01 19:00:57 deraadt Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
* are retained or reproduced in an accompanying document, permission
|
||||
* is granted to deal in this work without restriction, including un-
|
||||
* limited rights to use, publicly perform, distribute, sell, modify,
|
||||
* merge, give away, or sublicence.
|
||||
*
|
||||
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
|
||||
* the utmost extent permitted by applicable law, neither express nor
|
||||
* implied; without malicious intent or gross negligence. In no event
|
||||
* may a licensor, author or contributor be held liable for indirect,
|
||||
* direct, other damage, loss, or other issues arising in any way out
|
||||
* of dealing in the work, even if advised of the possibility of such
|
||||
* damage or existence of a defect, except proven that it results out
|
||||
* of said person's immediate fault when using the work as intended.
|
||||
*/
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.44 2010/08/14 21:35:13 tg Exp $");
|
||||
|
||||
/* The order of these enums is constrained by the order of opinfo[] */
|
||||
enum token {
|
||||
/* some (long) unary operators */
|
||||
O_PLUSPLUS = 0, O_MINUSMINUS,
|
||||
/* binary operators */
|
||||
O_EQ, O_NE,
|
||||
/* assignments are assumed to be in range O_ASN .. O_BORASN */
|
||||
O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
|
||||
O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
|
||||
O_LSHIFT, O_RSHIFT,
|
||||
O_LE, O_GE, O_LT, O_GT,
|
||||
O_LAND,
|
||||
O_LOR,
|
||||
O_TIMES, O_DIV, O_MOD,
|
||||
O_PLUS, O_MINUS,
|
||||
O_BAND,
|
||||
O_BXOR,
|
||||
O_BOR,
|
||||
O_TERN,
|
||||
O_COMMA,
|
||||
/* things after this aren't used as binary operators */
|
||||
/* unary that are not also binaries */
|
||||
O_BNOT, O_LNOT,
|
||||
/* misc */
|
||||
OPEN_PAREN, CLOSE_PAREN, CTERN,
|
||||
/* things that don't appear in the opinfo[] table */
|
||||
VAR, LIT, END, BAD
|
||||
};
|
||||
#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
|
||||
#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
|
||||
|
||||
/* precisions; used to be enum prec but we do arithmetics on it */
|
||||
#define P_PRIMARY 0 /* VAR, LIT, (), ~ ! - + */
|
||||
#define P_MULT 1 /* * / % */
|
||||
#define P_ADD 2 /* + - */
|
||||
#define P_SHIFT 3 /* << >> */
|
||||
#define P_RELATION 4 /* < <= > >= */
|
||||
#define P_EQUALITY 5 /* == != */
|
||||
#define P_BAND 6 /* & */
|
||||
#define P_BXOR 7 /* ^ */
|
||||
#define P_BOR 8 /* | */
|
||||
#define P_LAND 9 /* && */
|
||||
#define P_LOR 10 /* || */
|
||||
#define P_TERN 11 /* ?: */
|
||||
#define P_ASSIGN 12 /* = *= /= %= += -= <<= >>= &= ^= |= */
|
||||
#define P_COMMA 13 /* , */
|
||||
#define MAX_PREC P_COMMA
|
||||
|
||||
struct opinfo {
|
||||
char name[4];
|
||||
int len; /* name length */
|
||||
int prec; /* precedence: lower is higher */
|
||||
};
|
||||
|
||||
/* Tokens in this table must be ordered so the longest are first
|
||||
* (eg, += before +). If you change something, change the order
|
||||
* of enum token too.
|
||||
*/
|
||||
static const struct opinfo opinfo[] = {
|
||||
{ "++", 2, P_PRIMARY }, /* before + */
|
||||
{ "--", 2, P_PRIMARY }, /* before - */
|
||||
{ "==", 2, P_EQUALITY }, /* before = */
|
||||
{ "!=", 2, P_EQUALITY }, /* before ! */
|
||||
{ "=", 1, P_ASSIGN }, /* keep assigns in a block */
|
||||
{ "*=", 2, P_ASSIGN },
|
||||
{ "/=", 2, P_ASSIGN },
|
||||
{ "%=", 2, P_ASSIGN },
|
||||
{ "+=", 2, P_ASSIGN },
|
||||
{ "-=", 2, P_ASSIGN },
|
||||
{ "<<=", 3, P_ASSIGN },
|
||||
{ ">>=", 3, P_ASSIGN },
|
||||
{ "&=", 2, P_ASSIGN },
|
||||
{ "^=", 2, P_ASSIGN },
|
||||
{ "|=", 2, P_ASSIGN },
|
||||
{ "<<", 2, P_SHIFT },
|
||||
{ ">>", 2, P_SHIFT },
|
||||
{ "<=", 2, P_RELATION },
|
||||
{ ">=", 2, P_RELATION },
|
||||
{ "<", 1, P_RELATION },
|
||||
{ ">", 1, P_RELATION },
|
||||
{ "&&", 2, P_LAND },
|
||||
{ "||", 2, P_LOR },
|
||||
{ "*", 1, P_MULT },
|
||||
{ "/", 1, P_MULT },
|
||||
{ "%", 1, P_MULT },
|
||||
{ "+", 1, P_ADD },
|
||||
{ "-", 1, P_ADD },
|
||||
{ "&", 1, P_BAND },
|
||||
{ "^", 1, P_BXOR },
|
||||
{ "|", 1, P_BOR },
|
||||
{ "?", 1, P_TERN },
|
||||
{ ",", 1, P_COMMA },
|
||||
{ "~", 1, P_PRIMARY },
|
||||
{ "!", 1, P_PRIMARY },
|
||||
{ "(", 1, P_PRIMARY },
|
||||
{ ")", 1, P_PRIMARY },
|
||||
{ ":", 1, P_PRIMARY },
|
||||
{ "", 0, P_PRIMARY }
|
||||
};
|
||||
|
||||
typedef struct expr_state Expr_state;
|
||||
struct expr_state {
|
||||
const char *expression; /* expression being evaluated */
|
||||
const char *tokp; /* lexical position */
|
||||
struct tbl *val; /* value from token() */
|
||||
struct tbl *evaling; /* variable that is being recursively
|
||||
* expanded (EXPRINEVAL flag set) */
|
||||
int noassign; /* don't do assigns (for ?:,&&,||) */
|
||||
enum token tok; /* token from token() */
|
||||
bool arith; /* evaluating an $(()) expression? */
|
||||
bool natural; /* unsigned arithmetic calculation */
|
||||
};
|
||||
|
||||
#define bivui(x, op, y) (es->natural ? \
|
||||
(mksh_ari_t)((x)->val.u op (y)->val.u) : \
|
||||
(mksh_ari_t)((x)->val.i op (y)->val.i) \
|
||||
)
|
||||
#define chvui(x, op) do { \
|
||||
if (es->natural) \
|
||||
(x)->val.u = op (x)->val.u; \
|
||||
else \
|
||||
(x)->val.i = op (x)->val.i; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#define stvui(x, n) do { \
|
||||
if (es->natural) \
|
||||
(x)->val.u = (n); \
|
||||
else \
|
||||
(x)->val.i = (n); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
enum error_type {
|
||||
ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
|
||||
ET_LVALUE, ET_RDONLY, ET_STR
|
||||
};
|
||||
|
||||
static void evalerr(Expr_state *, enum error_type, const char *)
|
||||
MKSH_A_NORETURN;
|
||||
static struct tbl *evalexpr(Expr_state *, int);
|
||||
static void exprtoken(Expr_state *);
|
||||
static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool);
|
||||
static void assign_check(Expr_state *, enum token, struct tbl *);
|
||||
static struct tbl *tempvar(void);
|
||||
static struct tbl *intvar(Expr_state *, struct tbl *);
|
||||
|
||||
/*
|
||||
* parse and evaluate expression
|
||||
*/
|
||||
int
|
||||
evaluate(const char *expr, mksh_ari_t *rval, int error_ok, bool arith)
|
||||
{
|
||||
struct tbl v;
|
||||
int ret;
|
||||
|
||||
v.flag = DEFINED|INTEGER;
|
||||
v.type = 0;
|
||||
ret = v_evaluate(&v, expr, error_ok, arith);
|
||||
*rval = v.val.i;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* parse and evaluate expression, storing result in vp.
|
||||
*/
|
||||
int
|
||||
v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
|
||||
bool arith)
|
||||
{
|
||||
struct tbl *v;
|
||||
Expr_state curstate;
|
||||
Expr_state * const es = &curstate;
|
||||
int i;
|
||||
|
||||
/* save state to allow recursive calls */
|
||||
curstate.expression = curstate.tokp = expr;
|
||||
curstate.noassign = 0;
|
||||
curstate.arith = arith;
|
||||
curstate.evaling = NULL;
|
||||
curstate.natural = false;
|
||||
|
||||
newenv(E_ERRH);
|
||||
i = sigsetjmp(e->jbuf, 0);
|
||||
if (i) {
|
||||
/* Clear EXPRINEVAL in of any variables we were playing with */
|
||||
if (curstate.evaling)
|
||||
curstate.evaling->flag &= ~EXPRINEVAL;
|
||||
quitenv(NULL);
|
||||
if (i == LAEXPR) {
|
||||
if (error_ok == KSH_RETURN_ERROR)
|
||||
return (0);
|
||||
errorfz();
|
||||
}
|
||||
unwind(i);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
exprtoken(es);
|
||||
if (es->tok == END) {
|
||||
es->tok = LIT;
|
||||
es->val = tempvar();
|
||||
}
|
||||
v = intvar(es, evalexpr(es, MAX_PREC));
|
||||
|
||||
if (es->tok != END)
|
||||
evalerr(es, ET_UNEXPECTED, NULL);
|
||||
|
||||
if (es->arith && es->natural)
|
||||
vp->flag |= INT_U;
|
||||
if (vp->flag & INTEGER)
|
||||
setint_v(vp, v, es->arith);
|
||||
else
|
||||
/* can fail if readonly */
|
||||
setstr(vp, str_val(v), error_ok);
|
||||
|
||||
quitenv(NULL);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
evalerr(Expr_state *es, enum error_type type, const char *str)
|
||||
{
|
||||
char tbuf[2];
|
||||
const char *s;
|
||||
|
||||
es->arith = false;
|
||||
switch (type) {
|
||||
case ET_UNEXPECTED:
|
||||
switch (es->tok) {
|
||||
case VAR:
|
||||
s = es->val->name;
|
||||
break;
|
||||
case LIT:
|
||||
s = str_val(es->val);
|
||||
break;
|
||||
case END:
|
||||
s = "end of expression";
|
||||
break;
|
||||
case BAD:
|
||||
tbuf[0] = *es->tokp;
|
||||
tbuf[1] = '\0';
|
||||
s = tbuf;
|
||||
break;
|
||||
default:
|
||||
s = opinfo[(int)es->tok].name;
|
||||
}
|
||||
warningf(true, "%s: unexpected '%s'", es->expression, s);
|
||||
break;
|
||||
|
||||
case ET_BADLIT:
|
||||
warningf(true, "%s: bad number '%s'", es->expression, str);
|
||||
break;
|
||||
|
||||
case ET_RECURSIVE:
|
||||
warningf(true, "%s: expression recurses on parameter '%s'",
|
||||
es->expression, str);
|
||||
break;
|
||||
|
||||
case ET_LVALUE:
|
||||
warningf(true, "%s: %s requires lvalue",
|
||||
es->expression, str);
|
||||
break;
|
||||
|
||||
case ET_RDONLY:
|
||||
warningf(true, "%s: %s applied to read only variable",
|
||||
es->expression, str);
|
||||
break;
|
||||
|
||||
default: /* keep gcc happy */
|
||||
case ET_STR:
|
||||
warningf(true, "%s: %s", es->expression, str);
|
||||
break;
|
||||
}
|
||||
unwind(LAEXPR);
|
||||
}
|
||||
|
||||
static struct tbl *
|
||||
evalexpr(Expr_state *es, int prec)
|
||||
{
|
||||
struct tbl *vl, *vr = NULL, *vasn;
|
||||
enum token op;
|
||||
mksh_ari_t res = 0;
|
||||
|
||||
if (prec == P_PRIMARY) {
|
||||
op = es->tok;
|
||||
if (op == O_BNOT || op == O_LNOT || op == O_MINUS ||
|
||||
op == O_PLUS) {
|
||||
exprtoken(es);
|
||||
vl = intvar(es, evalexpr(es, P_PRIMARY));
|
||||
if (op == O_BNOT)
|
||||
chvui(vl, ~);
|
||||
else if (op == O_LNOT)
|
||||
chvui(vl, !);
|
||||
else if (op == O_MINUS)
|
||||
chvui(vl, -);
|
||||
/* op == O_PLUS is a no-op */
|
||||
} else if (op == OPEN_PAREN) {
|
||||
exprtoken(es);
|
||||
vl = evalexpr(es, MAX_PREC);
|
||||
if (es->tok != CLOSE_PAREN)
|
||||
evalerr(es, ET_STR, "missing )");
|
||||
exprtoken(es);
|
||||
} else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
|
||||
exprtoken(es);
|
||||
vl = do_ppmm(es, op, es->val, true);
|
||||
exprtoken(es);
|
||||
} else if (op == VAR || op == LIT) {
|
||||
vl = es->val;
|
||||
exprtoken(es);
|
||||
} else {
|
||||
evalerr(es, ET_UNEXPECTED, NULL);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
|
||||
vl = do_ppmm(es, es->tok, vl, false);
|
||||
exprtoken(es);
|
||||
}
|
||||
return (vl);
|
||||
}
|
||||
vl = evalexpr(es, prec - 1);
|
||||
for (op = es->tok; IS_BINOP(op) && opinfo[(int)op].prec == prec;
|
||||
op = es->tok) {
|
||||
exprtoken(es);
|
||||
vasn = vl;
|
||||
if (op != O_ASN) /* vl may not have a value yet */
|
||||
vl = intvar(es, vl);
|
||||
if (IS_ASSIGNOP(op)) {
|
||||
assign_check(es, op, vasn);
|
||||
vr = intvar(es, evalexpr(es, P_ASSIGN));
|
||||
} else if (op != O_TERN && op != O_LAND && op != O_LOR)
|
||||
vr = intvar(es, evalexpr(es, prec - 1));
|
||||
if ((op == O_DIV || op == O_MOD || op == O_DIVASN ||
|
||||
op == O_MODASN) && vr->val.i == 0) {
|
||||
if (es->noassign)
|
||||
vr->val.i = 1;
|
||||
else
|
||||
evalerr(es, ET_STR, "zero divisor");
|
||||
}
|
||||
switch ((int)op) {
|
||||
case O_TIMES:
|
||||
case O_TIMESASN:
|
||||
res = bivui(vl, *, vr);
|
||||
break;
|
||||
case O_DIV:
|
||||
case O_DIVASN:
|
||||
res = bivui(vl, /, vr);
|
||||
break;
|
||||
case O_MOD:
|
||||
case O_MODASN:
|
||||
res = bivui(vl, %, vr);
|
||||
break;
|
||||
case O_PLUS:
|
||||
case O_PLUSASN:
|
||||
res = bivui(vl, +, vr);
|
||||
break;
|
||||
case O_MINUS:
|
||||
case O_MINUSASN:
|
||||
res = bivui(vl, -, vr);
|
||||
break;
|
||||
case O_LSHIFT:
|
||||
case O_LSHIFTASN:
|
||||
res = bivui(vl, <<, vr);
|
||||
break;
|
||||
case O_RSHIFT:
|
||||
case O_RSHIFTASN:
|
||||
res = bivui(vl, >>, vr);
|
||||
break;
|
||||
case O_LT:
|
||||
res = bivui(vl, <, vr);
|
||||
break;
|
||||
case O_LE:
|
||||
res = bivui(vl, <=, vr);
|
||||
break;
|
||||
case O_GT:
|
||||
res = bivui(vl, >, vr);
|
||||
break;
|
||||
case O_GE:
|
||||
res = bivui(vl, >=, vr);
|
||||
break;
|
||||
case O_EQ:
|
||||
res = bivui(vl, ==, vr);
|
||||
break;
|
||||
case O_NE:
|
||||
res = bivui(vl, !=, vr);
|
||||
break;
|
||||
case O_BAND:
|
||||
case O_BANDASN:
|
||||
res = bivui(vl, &, vr);
|
||||
break;
|
||||
case O_BXOR:
|
||||
case O_BXORASN:
|
||||
res = bivui(vl, ^, vr);
|
||||
break;
|
||||
case O_BOR:
|
||||
case O_BORASN:
|
||||
res = bivui(vl, |, vr);
|
||||
break;
|
||||
case O_LAND:
|
||||
if (!vl->val.i)
|
||||
es->noassign++;
|
||||
vr = intvar(es, evalexpr(es, prec - 1));
|
||||
res = bivui(vl, &&, vr);
|
||||
if (!vl->val.i)
|
||||
es->noassign--;
|
||||
break;
|
||||
case O_LOR:
|
||||
if (vl->val.i)
|
||||
es->noassign++;
|
||||
vr = intvar(es, evalexpr(es, prec - 1));
|
||||
res = bivui(vl, ||, vr);
|
||||
if (vl->val.i)
|
||||
es->noassign--;
|
||||
break;
|
||||
case O_TERN:
|
||||
{
|
||||
bool ev = vl->val.i != 0;
|
||||
|
||||
if (!ev)
|
||||
es->noassign++;
|
||||
vl = evalexpr(es, MAX_PREC);
|
||||
if (!ev)
|
||||
es->noassign--;
|
||||
if (es->tok != CTERN)
|
||||
evalerr(es, ET_STR, "missing :");
|
||||
exprtoken(es);
|
||||
if (ev)
|
||||
es->noassign++;
|
||||
vr = evalexpr(es, P_TERN);
|
||||
if (ev)
|
||||
es->noassign--;
|
||||
vl = ev ? vl : vr;
|
||||
}
|
||||
break;
|
||||
case O_ASN:
|
||||
res = vr->val.i;
|
||||
break;
|
||||
case O_COMMA:
|
||||
res = vr->val.i;
|
||||
break;
|
||||
}
|
||||
if (IS_ASSIGNOP(op)) {
|
||||
stvui(vr, res);
|
||||
if (!es->noassign) {
|
||||
if (vasn->flag & INTEGER)
|
||||
setint_v(vasn, vr, es->arith);
|
||||
else
|
||||
setint(vasn, res);
|
||||
}
|
||||
vl = vr;
|
||||
} else if (op != O_TERN)
|
||||
stvui(vl, res);
|
||||
}
|
||||
return (vl);
|
||||
}
|
||||
|
||||
static void
|
||||
exprtoken(Expr_state *es)
|
||||
{
|
||||
const char *cp = es->tokp;
|
||||
int c;
|
||||
char *tvar;
|
||||
|
||||
/* skip white space */
|
||||
skip_spaces:
|
||||
while ((c = *cp), ksh_isspace(c))
|
||||
++cp;
|
||||
if (es->tokp == es->expression && c == '#') {
|
||||
/* expression begins with # */
|
||||
es->natural = true; /* switch to unsigned */
|
||||
++cp;
|
||||
goto skip_spaces;
|
||||
}
|
||||
es->tokp = cp;
|
||||
|
||||
if (c == '\0')
|
||||
es->tok = END;
|
||||
else if (ksh_isalphx(c)) {
|
||||
for (; ksh_isalnux(c); c = *cp)
|
||||
cp++;
|
||||
if (c == '[') {
|
||||
int len;
|
||||
|
||||
len = array_ref_len(cp);
|
||||
if (len == 0)
|
||||
evalerr(es, ET_STR, "missing ]");
|
||||
cp += len;
|
||||
} else if (c == '(' /*)*/ ) {
|
||||
/* todo: add math functions (all take single argument):
|
||||
* abs acos asin atan cos cosh exp int log sin sinh sqrt
|
||||
* tan tanh
|
||||
*/
|
||||
;
|
||||
}
|
||||
if (es->noassign) {
|
||||
es->val = tempvar();
|
||||
es->val->flag |= EXPRLVALUE;
|
||||
} else {
|
||||
strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
|
||||
es->val = global(tvar);
|
||||
afree(tvar, ATEMP);
|
||||
}
|
||||
es->tok = VAR;
|
||||
} else if (c == '1' && cp[1] == '#') {
|
||||
cp += 2;
|
||||
cp += utf_ptradj(cp);
|
||||
strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
|
||||
goto process_tvar;
|
||||
#ifndef MKSH_SMALL
|
||||
} else if (c == '\'') {
|
||||
++cp;
|
||||
cp += utf_ptradj(cp);
|
||||
if (*cp++ != '\'')
|
||||
evalerr(es, ET_STR,
|
||||
"multi-character character constant");
|
||||
/* 'x' -> 1#x (x = one multibyte character) */
|
||||
c = cp - es->tokp;
|
||||
tvar = alloc(c + /* NUL */ 1, ATEMP);
|
||||
tvar[0] = '1';
|
||||
tvar[1] = '#';
|
||||
memcpy(tvar + 2, es->tokp + 1, c - 2);
|
||||
tvar[c] = '\0';
|
||||
goto process_tvar;
|
||||
#endif
|
||||
} else if (ksh_isdigit(c)) {
|
||||
while (c != '_' && (ksh_isalnux(c) || c == '#'))
|
||||
c = *cp++;
|
||||
strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP);
|
||||
process_tvar:
|
||||
es->val = tempvar();
|
||||
es->val->flag &= ~INTEGER;
|
||||
es->val->type = 0;
|
||||
es->val->val.s = tvar;
|
||||
if (setint_v(es->val, es->val, es->arith) == NULL)
|
||||
evalerr(es, ET_BADLIT, tvar);
|
||||
afree(tvar, ATEMP);
|
||||
es->tok = LIT;
|
||||
} else {
|
||||
int i, n0;
|
||||
|
||||
for (i = 0; (n0 = opinfo[i].name[0]); i++)
|
||||
if (c == n0 && strncmp(cp, opinfo[i].name,
|
||||
(size_t)opinfo[i].len) == 0) {
|
||||
es->tok = (enum token)i;
|
||||
cp += opinfo[i].len;
|
||||
break;
|
||||
}
|
||||
if (!n0)
|
||||
es->tok = BAD;
|
||||
}
|
||||
es->tokp = cp;
|
||||
}
|
||||
|
||||
/* Do a ++ or -- operation */
|
||||
static struct tbl *
|
||||
do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
|
||||
{
|
||||
struct tbl *vl;
|
||||
mksh_ari_t oval;
|
||||
|
||||
assign_check(es, op, vasn);
|
||||
|
||||
vl = intvar(es, vasn);
|
||||
oval = vl->val.i;
|
||||
if (op == O_PLUSPLUS) {
|
||||
if (es->natural)
|
||||
++vl->val.u;
|
||||
else
|
||||
++vl->val.i;
|
||||
} else {
|
||||
if (es->natural)
|
||||
--vl->val.u;
|
||||
else
|
||||
--vl->val.i;
|
||||
}
|
||||
if (vasn->flag & INTEGER)
|
||||
setint_v(vasn, vl, es->arith);
|
||||
else
|
||||
setint(vasn, vl->val.i);
|
||||
if (!is_prefix) /* undo the inc/dec */
|
||||
vl->val.i = oval;
|
||||
|
||||
return (vl);
|
||||
}
|
||||
|
||||
static void
|
||||
assign_check(Expr_state *es, enum token op, struct tbl *vasn)
|
||||
{
|
||||
if (es->tok == END ||
|
||||
(vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
|
||||
evalerr(es, ET_LVALUE, opinfo[(int)op].name);
|
||||
else if (vasn->flag & RDONLY)
|
||||
evalerr(es, ET_RDONLY, opinfo[(int)op].name);
|
||||
}
|
||||
|
||||
static struct tbl *
|
||||
tempvar(void)
|
||||
{
|
||||
struct tbl *vp;
|
||||
|
||||
vp = alloc(sizeof(struct tbl), ATEMP);
|
||||
vp->flag = ISSET|INTEGER;
|
||||
vp->type = 0;
|
||||
vp->areap = ATEMP;
|
||||
vp->ua.hval = 0;
|
||||
vp->val.i = 0;
|
||||
vp->name[0] = '\0';
|
||||
return (vp);
|
||||
}
|
||||
|
||||
/* cast (string) variable to temporary integer variable */
|
||||
static struct tbl *
|
||||
intvar(Expr_state *es, struct tbl *vp)
|
||||
{
|
||||
struct tbl *vq;
|
||||
|
||||
/* try to avoid replacing a temp var with another temp var */
|
||||
if (vp->name[0] == '\0' &&
|
||||
(vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
|
||||
return (vp);
|
||||
|
||||
vq = tempvar();
|
||||
if (setint_v(vq, vp, es->arith) == NULL) {
|
||||
if (vp->flag & EXPRINEVAL)
|
||||
evalerr(es, ET_RECURSIVE, vp->name);
|
||||
es->evaling = vp;
|
||||
vp->flag |= EXPRINEVAL;
|
||||
v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR, es->arith);
|
||||
vp->flag &= ~EXPRINEVAL;
|
||||
es->evaling = NULL;
|
||||
}
|
||||
return (vq);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UTF-8 support code: high-level functions
|
||||
*/
|
||||
|
||||
int
|
||||
utf_widthadj(const char *src, const char **dst)
|
||||
{
|
||||
size_t len;
|
||||
unsigned int wc;
|
||||
int width;
|
||||
|
||||
if (!UTFMODE || (len = utf_mbtowc(&wc, src)) == (size_t)-1 ||
|
||||
wc == 0)
|
||||
len = width = 1;
|
||||
else if ((width = utf_wcwidth(wc)) < 0)
|
||||
/* XXX use 2 for x_zotc3 here? */
|
||||
width = 1;
|
||||
|
||||
if (dst)
|
||||
*dst = src + len;
|
||||
return (width);
|
||||
}
|
||||
|
||||
int
|
||||
utf_mbswidth(const char *s)
|
||||
{
|
||||
size_t len;
|
||||
unsigned int wc;
|
||||
int width = 0, cw;
|
||||
|
||||
if (!UTFMODE)
|
||||
return (strlen(s));
|
||||
|
||||
while (*s)
|
||||
if (((len = utf_mbtowc(&wc, s)) == (size_t)-1) ||
|
||||
((cw = utf_wcwidth(wc)) == -1)) {
|
||||
s++;
|
||||
width += 1;
|
||||
} else {
|
||||
s += len;
|
||||
width += cw;
|
||||
}
|
||||
return (width);
|
||||
}
|
||||
|
||||
const char *
|
||||
utf_skipcols(const char *p, int cols)
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
while (c < cols) {
|
||||
if (!*p)
|
||||
return (p + cols - c);
|
||||
c += utf_widthadj(p, &p);
|
||||
}
|
||||
return (p);
|
||||
}
|
||||
|
||||
size_t
|
||||
utf_ptradj(const char *src)
|
||||
{
|
||||
register size_t n;
|
||||
|
||||
if (!UTFMODE ||
|
||||
*(const unsigned char *)(src) < 0xC2 ||
|
||||
(n = utf_mbtowc(NULL, src)) == (size_t)-1)
|
||||
n = 1;
|
||||
return (n);
|
||||
}
|
||||
|
||||
/*
|
||||
* UTF-8 support code: low-level functions
|
||||
*/
|
||||
|
||||
/* CESU-8 multibyte and wide character conversion crafted for mksh */
|
||||
|
||||
size_t
|
||||
utf_mbtowc(unsigned int *dst, const char *src)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char *)src;
|
||||
unsigned int c, wc;
|
||||
|
||||
if ((wc = *s++) < 0x80) {
|
||||
out:
|
||||
if (dst != NULL)
|
||||
*dst = wc;
|
||||
return (wc ? ((const char *)s - src) : 0);
|
||||
}
|
||||
if (wc < 0xC2 || wc >= 0xF0)
|
||||
/* < 0xC0: spurious second byte */
|
||||
/* < 0xC2: non-minimalistic mapping error in 2-byte seqs */
|
||||
/* > 0xEF: beyond BMP */
|
||||
goto ilseq;
|
||||
|
||||
if (wc < 0xE0) {
|
||||
wc = (wc & 0x1F) << 6;
|
||||
if (((c = *s++) & 0xC0) != 0x80)
|
||||
goto ilseq;
|
||||
wc |= c & 0x3F;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wc = (wc & 0x0F) << 12;
|
||||
|
||||
if (((c = *s++) & 0xC0) != 0x80)
|
||||
goto ilseq;
|
||||
wc |= (c & 0x3F) << 6;
|
||||
|
||||
if (((c = *s++) & 0xC0) != 0x80)
|
||||
goto ilseq;
|
||||
wc |= c & 0x3F;
|
||||
|
||||
/* Check for non-minimalistic mapping error in 3-byte seqs */
|
||||
if (wc >= 0x0800 && wc <= 0xFFFD)
|
||||
goto out;
|
||||
ilseq:
|
||||
return ((size_t)(-1));
|
||||
}
|
||||
|
||||
size_t
|
||||
utf_wctomb(char *dst, unsigned int wc)
|
||||
{
|
||||
unsigned char *d;
|
||||
|
||||
if (wc < 0x80) {
|
||||
*dst = wc;
|
||||
return (1);
|
||||
}
|
||||
|
||||
d = (unsigned char *)dst;
|
||||
if (wc < 0x0800)
|
||||
*d++ = (wc >> 6) | 0xC0;
|
||||
else {
|
||||
*d++ = ((wc = wc > 0xFFFD ? 0xFFFD : wc) >> 12) | 0xE0;
|
||||
*d++ = ((wc >> 6) & 0x3F) | 0x80;
|
||||
}
|
||||
*d++ = (wc & 0x3F) | 0x80;
|
||||
return ((char *)d - dst);
|
||||
}
|
||||
|
||||
|
||||
#ifndef MKSH_mirbsd_wcwidth
|
||||
/* --- begin of wcwidth.c excerpt --- */
|
||||
/*-
|
||||
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* for any purpose and without fee is hereby granted. The author
|
||||
* disclaims all warranties with regard to this software.
|
||||
*/
|
||||
|
||||
__RCSID("$miros: src/lib/libc/i18n/wcwidth.c,v 1.8 2008/09/20 12:01:18 tg Exp $");
|
||||
|
||||
int
|
||||
utf_wcwidth(unsigned int c)
|
||||
{
|
||||
static const struct cbset {
|
||||
unsigned short first;
|
||||
unsigned short last;
|
||||
} comb[] = {
|
||||
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
|
||||
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
|
||||
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
|
||||
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
|
||||
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
|
||||
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
|
||||
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
|
||||
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
|
||||
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
|
||||
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
|
||||
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
|
||||
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
|
||||
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
|
||||
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
|
||||
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
|
||||
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
|
||||
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
|
||||
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
|
||||
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
|
||||
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
|
||||
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
|
||||
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
|
||||
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
|
||||
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
|
||||
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
|
||||
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
|
||||
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
|
||||
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
|
||||
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
|
||||
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
|
||||
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
|
||||
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
|
||||
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
|
||||
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
|
||||
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
|
||||
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
|
||||
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
|
||||
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
|
||||
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
|
||||
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
|
||||
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }
|
||||
};
|
||||
size_t min = 0, mid, max = NELEM(comb) - 1;
|
||||
|
||||
/* test for 8-bit control characters */
|
||||
if (c < 32 || (c >= 0x7f && c < 0xa0))
|
||||
return (c ? -1 : 0);
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (c >= comb[0].first && c <= comb[max].last)
|
||||
while (max >= min) {
|
||||
mid = (min + max) / 2;
|
||||
if (c > comb[mid].last)
|
||||
min = mid + 1;
|
||||
else if (c < comb[mid].first)
|
||||
max = mid - 1;
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* if we arrive here, c is not a combining or C0/C1 control char */
|
||||
return ((c >= 0x1100 && (
|
||||
c <= 0x115f || /* Hangul Jamo init. consonants */
|
||||
c == 0x2329 || c == 0x232a ||
|
||||
(c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
|
||||
(c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
|
||||
(c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
|
||||
(c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
|
||||
(c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
|
||||
(c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
|
||||
(c >= 0xffe0 && c <= 0xffe6))) ? 2 : 1);
|
||||
}
|
||||
/* --- end of wcwidth.c excerpt --- */
|
||||
#endif
|
3429
src/funcs.c
Normal file
3429
src/funcs.c
Normal file
File diff suppressed because it is too large
Load diff
1483
src/histrap.c
Normal file
1483
src/histrap.c
Normal file
File diff suppressed because it is too large
Load diff
1648
src/jobs.c
Normal file
1648
src/jobs.c
Normal file
File diff suppressed because it is too large
Load diff
123
src/lalloc.c
Normal file
123
src/lalloc.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*-
|
||||
* Copyright © 2009
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
* are retained or reproduced in an accompanying document, permission
|
||||
* is granted to deal in this work without restriction, including un‐
|
||||
* limited rights to use, publicly perform, distribute, sell, modify,
|
||||
* merge, give away, or sublicence.
|
||||
*
|
||||
* This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
|
||||
* the utmost extent permitted by applicable law, neither express nor
|
||||
* implied; without malicious intent or gross negligence. In no event
|
||||
* may a licensor, author or contributor be held liable for indirect,
|
||||
* direct, other damage, loss, or other issues arising in any way out
|
||||
* of dealing in the work, even if advised of the possibility of such
|
||||
* damage or existence of a defect, except proven that it results out
|
||||
* of said person’s immediate fault when using the work as intended.
|
||||
*/
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.11 2009/08/08 13:08:51 tg Exp $");
|
||||
|
||||
/* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */
|
||||
#if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0)
|
||||
#define remalloc(p,n) ((p) == NULL ? malloc(n) : realloc((p), (n)))
|
||||
#else
|
||||
#define remalloc(p,n) realloc((p), (n))
|
||||
#endif
|
||||
|
||||
#define ALLOC_ISUNALIGNED(p) (((ptrdiff_t)(p)) % ALLOC_SIZE)
|
||||
|
||||
static ALLOC_ITEM *findptr(ALLOC_ITEM **, char *, Area *);
|
||||
|
||||
void
|
||||
ainit(Area *ap)
|
||||
{
|
||||
/* area pointer is an ALLOC_ITEM, just the head of the list */
|
||||
ap->next = NULL;
|
||||
}
|
||||
|
||||
static ALLOC_ITEM *
|
||||
findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap)
|
||||
{
|
||||
void *lp;
|
||||
|
||||
#ifndef MKSH_SMALL
|
||||
if (ALLOC_ISUNALIGNED(ptr))
|
||||
goto fail;
|
||||
#endif
|
||||
/* get address of ALLOC_ITEM from user item */
|
||||
/*
|
||||
* note: the alignment of "ptr" to ALLOC_SIZE is checked
|
||||
* above; the "void *" gets us rid of a gcc 2.95 warning
|
||||
*/
|
||||
*lpp = (lp = ptr - ALLOC_SIZE);
|
||||
/* search for allocation item in group list */
|
||||
while (ap->next != lp)
|
||||
if ((ap = ap->next) == NULL) {
|
||||
#ifndef MKSH_SMALL
|
||||
fail:
|
||||
#endif
|
||||
internal_errorf("rogue pointer %p", ptr);
|
||||
}
|
||||
return (ap);
|
||||
}
|
||||
|
||||
void *
|
||||
aresize(void *ptr, size_t numb, Area *ap)
|
||||
{
|
||||
ALLOC_ITEM *lp = NULL;
|
||||
|
||||
/* resizing (true) or newly allocating? */
|
||||
if (ptr != NULL) {
|
||||
ALLOC_ITEM *pp;
|
||||
|
||||
pp = findptr(&lp, ptr, ap);
|
||||
pp->next = lp->next;
|
||||
}
|
||||
|
||||
if ((numb >= SIZE_MAX - ALLOC_SIZE) ||
|
||||
(lp = remalloc(lp, numb + ALLOC_SIZE)) == NULL
|
||||
#ifndef MKSH_SMALL
|
||||
|| ALLOC_ISUNALIGNED(lp)
|
||||
#endif
|
||||
)
|
||||
internal_errorf("cannot allocate %lu data bytes",
|
||||
(unsigned long)numb);
|
||||
/* this only works because Area is an ALLOC_ITEM */
|
||||
lp->next = ap->next;
|
||||
ap->next = lp;
|
||||
/* return user item address */
|
||||
return ((char *)lp + ALLOC_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
afree(void *ptr, Area *ap)
|
||||
{
|
||||
if (ptr != NULL) {
|
||||
ALLOC_ITEM *lp, *pp;
|
||||
|
||||
pp = findptr(&lp, ptr, ap);
|
||||
/* unhook */
|
||||
pp->next = lp->next;
|
||||
/* now free ALLOC_ITEM */
|
||||
free(lp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
afreeall(Area *ap)
|
||||
{
|
||||
ALLOC_ITEM *lp;
|
||||
|
||||
/* traverse group (linked list) */
|
||||
while ((lp = ap->next) != NULL) {
|
||||
/* make next ALLOC_ITEM head of list */
|
||||
ap->next = lp->next;
|
||||
/* free old head */
|
||||
free(lp);
|
||||
}
|
||||
}
|
1479
src/main.c
Normal file
1479
src/main.c
Normal file
File diff suppressed because it is too large
Load diff
1579
src/misc.c
Normal file
1579
src/misc.c
Normal file
File diff suppressed because it is too large
Load diff
145
src/sh_flags.h
Normal file
145
src/sh_flags.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
#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
|
716
src/tree.c
Normal file
716
src/tree.c
Normal file
|
@ -0,0 +1,716 @@
|
|||
/* $OpenBSD: tree.c,v 1.19 2008/08/11 21:50:35 jaredy Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
* are retained or reproduced in an accompanying document, permission
|
||||
* is granted to deal in this work without restriction, including un-
|
||||
* limited rights to use, publicly perform, distribute, sell, modify,
|
||||
* merge, give away, or sublicence.
|
||||
*
|
||||
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
|
||||
* the utmost extent permitted by applicable law, neither express nor
|
||||
* implied; without malicious intent or gross negligence. In no event
|
||||
* may a licensor, author or contributor be held liable for indirect,
|
||||
* direct, other damage, loss, or other issues arising in any way out
|
||||
* of dealing in the work, even if advised of the possibility of such
|
||||
* damage or existence of a defect, except proven that it results out
|
||||
* of said person's immediate fault when using the work as intended.
|
||||
*/
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.30 2010/02/25 20:18:19 tg Exp $");
|
||||
|
||||
#define INDENT 4
|
||||
|
||||
#define tputc(c, shf) shf_putchar(c, shf);
|
||||
static void ptree(struct op *, int, struct shf *);
|
||||
static void pioact(struct shf *, int, struct ioword *);
|
||||
static void tputC(int, struct shf *);
|
||||
static void tputS(char *, struct shf *);
|
||||
static void vfptreef(struct shf *, int, const char *, va_list);
|
||||
static struct ioword **iocopy(struct ioword **, Area *);
|
||||
static void iofree(struct ioword **, Area *);
|
||||
|
||||
/*
|
||||
* print a command tree
|
||||
*/
|
||||
static void
|
||||
ptree(struct op *t, int indent, struct shf *shf)
|
||||
{
|
||||
const char **w;
|
||||
struct ioword **ioact;
|
||||
struct op *t1;
|
||||
|
||||
Chain:
|
||||
if (t == NULL)
|
||||
return;
|
||||
switch (t->type) {
|
||||
case TCOM:
|
||||
if (t->vars)
|
||||
for (w = (const char **)t->vars; *w != NULL; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
else
|
||||
shf_puts("#no-vars# ", shf);
|
||||
if (t->args)
|
||||
for (w = t->args; *w != NULL; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
else
|
||||
shf_puts("#no-args# ", shf);
|
||||
break;
|
||||
case TEXEC:
|
||||
t = t->left;
|
||||
goto Chain;
|
||||
case TPAREN:
|
||||
fptreef(shf, indent + 2, "( %T) ", t->left);
|
||||
break;
|
||||
case TPIPE:
|
||||
fptreef(shf, indent, "%T| ", t->left);
|
||||
t = t->right;
|
||||
goto Chain;
|
||||
case TLIST:
|
||||
fptreef(shf, indent, "%T%;", t->left);
|
||||
t = t->right;
|
||||
goto Chain;
|
||||
case TOR:
|
||||
case TAND:
|
||||
fptreef(shf, indent, "%T%s %T",
|
||||
t->left, (t->type==TOR) ? "||" : "&&", t->right);
|
||||
break;
|
||||
case TBANG:
|
||||
shf_puts("! ", shf);
|
||||
t = t->right;
|
||||
goto Chain;
|
||||
case TDBRACKET: {
|
||||
int i;
|
||||
|
||||
shf_puts("[[", shf);
|
||||
for (i = 0; t->args[i]; i++)
|
||||
fptreef(shf, indent, " %S", t->args[i]);
|
||||
shf_puts(" ]] ", shf);
|
||||
break;
|
||||
}
|
||||
case TSELECT:
|
||||
fptreef(shf, indent, "select %s ", t->str);
|
||||
/* FALLTHROUGH */
|
||||
case TFOR:
|
||||
if (t->type == TFOR)
|
||||
fptreef(shf, indent, "for %s ", t->str);
|
||||
if (t->vars != NULL) {
|
||||
shf_puts("in ", shf);
|
||||
for (w = (const char **)t->vars; *w; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
fptreef(shf, indent, "%;");
|
||||
}
|
||||
fptreef(shf, indent + INDENT, "do%N%T", t->left);
|
||||
fptreef(shf, indent, "%;done ");
|
||||
break;
|
||||
case TCASE:
|
||||
fptreef(shf, indent, "case %S in", t->str);
|
||||
for (t1 = t->left; t1 != NULL; t1 = t1->right) {
|
||||
fptreef(shf, indent, "%N(");
|
||||
for (w = (const char **)t1->vars; *w != NULL; w++)
|
||||
fptreef(shf, indent, "%S%c", *w,
|
||||
(w[1] != NULL) ? '|' : ')');
|
||||
fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
|
||||
}
|
||||
fptreef(shf, indent, "%Nesac ");
|
||||
break;
|
||||
case TIF:
|
||||
case TELIF:
|
||||
/* 3 == strlen("if ") */
|
||||
fptreef(shf, indent + 3, "if %T", t->left);
|
||||
for (;;) {
|
||||
t = t->right;
|
||||
if (t->left != NULL) {
|
||||
fptreef(shf, indent, "%;");
|
||||
fptreef(shf, indent + INDENT, "then%N%T",
|
||||
t->left);
|
||||
}
|
||||
if (t->right == NULL || t->right->type != TELIF)
|
||||
break;
|
||||
t = t->right;
|
||||
fptreef(shf, indent, "%;");
|
||||
/* 5 == strlen("elif ") */
|
||||
fptreef(shf, indent + 5, "elif %T", t->left);
|
||||
}
|
||||
if (t->right != NULL) {
|
||||
fptreef(shf, indent, "%;");
|
||||
fptreef(shf, indent + INDENT, "else%;%T", t->right);
|
||||
}
|
||||
fptreef(shf, indent, "%;fi ");
|
||||
break;
|
||||
case TWHILE:
|
||||
case TUNTIL:
|
||||
/* 6 == strlen("while"/"until") */
|
||||
fptreef(shf, indent + 6, "%s %T",
|
||||
(t->type==TWHILE) ? "while" : "until",
|
||||
t->left);
|
||||
fptreef(shf, indent, "%;do");
|
||||
fptreef(shf, indent + INDENT, "%;%T", t->right);
|
||||
fptreef(shf, indent, "%;done ");
|
||||
break;
|
||||
case TBRACE:
|
||||
fptreef(shf, indent + INDENT, "{%;%T", t->left);
|
||||
fptreef(shf, indent, "%;} ");
|
||||
break;
|
||||
case TCOPROC:
|
||||
fptreef(shf, indent, "%T|& ", t->left);
|
||||
break;
|
||||
case TASYNC:
|
||||
fptreef(shf, indent, "%T& ", t->left);
|
||||
break;
|
||||
case TFUNCT:
|
||||
fptreef(shf, indent,
|
||||
t->u.ksh_func ? "function %s %T" : "%s() %T",
|
||||
t->str, t->left);
|
||||
break;
|
||||
case TTIME:
|
||||
fptreef(shf, indent, "time %T", t->left);
|
||||
break;
|
||||
default:
|
||||
shf_puts("<botch>", shf);
|
||||
break;
|
||||
}
|
||||
if ((ioact = t->ioact) != NULL) {
|
||||
int need_nl = 0;
|
||||
|
||||
while (*ioact != NULL)
|
||||
pioact(shf, indent, *ioact++);
|
||||
/* Print here documents after everything else... */
|
||||
for (ioact = t->ioact; *ioact != NULL; ) {
|
||||
struct ioword *iop = *ioact++;
|
||||
|
||||
/* heredoc is 0 when tracing (set -x) */
|
||||
if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc &&
|
||||
/* iop->delim[1] == '<' means here string */
|
||||
(!iop->delim || iop->delim[1] != '<')) {
|
||||
tputc('\n', shf);
|
||||
shf_puts(iop->heredoc, shf);
|
||||
fptreef(shf, indent, "%s",
|
||||
evalstr(iop->delim, 0));
|
||||
need_nl = 1;
|
||||
}
|
||||
}
|
||||
/* Last delimiter must be followed by a newline (this often
|
||||
* leads to an extra blank line, but its not worth worrying
|
||||
* about)
|
||||
*/
|
||||
if (need_nl)
|
||||
tputc('\n', shf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pioact(struct shf *shf, int indent, struct ioword *iop)
|
||||
{
|
||||
int flag = iop->flag;
|
||||
int type = flag & IOTYPE;
|
||||
int expected;
|
||||
|
||||
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
|
||||
(type == IOCAT || type == IOWRITE) ? 1 :
|
||||
(type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
|
||||
iop->unit + 1;
|
||||
if (iop->unit != expected)
|
||||
shf_fprintf(shf, "%d", iop->unit);
|
||||
|
||||
switch (type) {
|
||||
case IOREAD:
|
||||
shf_puts("< ", shf);
|
||||
break;
|
||||
case IOHERE:
|
||||
shf_puts(flag & IOSKIP ? "<<-" : "<<", shf);
|
||||
break;
|
||||
case IOCAT:
|
||||
shf_puts(">> ", shf);
|
||||
break;
|
||||
case IOWRITE:
|
||||
shf_puts(flag & IOCLOB ? ">| " : "> ", shf);
|
||||
break;
|
||||
case IORDWR:
|
||||
shf_puts("<> ", shf);
|
||||
break;
|
||||
case IODUP:
|
||||
shf_puts(flag & IORDUP ? "<&" : ">&", shf);
|
||||
break;
|
||||
}
|
||||
/* name/delim are 0 when printing syntax errors */
|
||||
if (type == IOHERE) {
|
||||
if (iop->delim)
|
||||
fptreef(shf, indent, "%s%S ",
|
||||
/* here string */ iop->delim[1] == '<' ? "" : " ",
|
||||
iop->delim);
|
||||
else
|
||||
tputc(' ', shf);
|
||||
} else if (iop->name)
|
||||
fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
|
||||
iop->name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* variants of fputc, fputs for ptreef and snptreef
|
||||
*/
|
||||
static void
|
||||
tputC(int c, struct shf *shf)
|
||||
{
|
||||
if ((c&0x60) == 0) { /* C0|C1 */
|
||||
tputc((c&0x80) ? '$' : '^', shf);
|
||||
tputc(((c&0x7F)|0x40), shf);
|
||||
} else if ((c&0x7F) == 0x7F) { /* DEL */
|
||||
tputc((c&0x80) ? '$' : '^', shf);
|
||||
tputc('?', shf);
|
||||
} else
|
||||
tputc(c, shf);
|
||||
}
|
||||
|
||||
static void
|
||||
tputS(char *wp, struct shf *shf)
|
||||
{
|
||||
int c, quotelevel = 0;
|
||||
|
||||
/* problems:
|
||||
* `...` -> $(...)
|
||||
* 'foo' -> "foo"
|
||||
* could change encoding to:
|
||||
* OQUOTE ["'] ... CQUOTE ["']
|
||||
* COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
|
||||
*/
|
||||
while (1)
|
||||
switch (*wp++) {
|
||||
case EOS:
|
||||
return;
|
||||
case ADELIM:
|
||||
case CHAR:
|
||||
tputC(*wp++, shf);
|
||||
break;
|
||||
case QCHAR:
|
||||
c = *wp++;
|
||||
if (!quotelevel || (c == '"' || c == '`' || c == '$'))
|
||||
tputc('\\', shf);
|
||||
tputC(c, shf);
|
||||
break;
|
||||
case COMSUB:
|
||||
shf_puts("$(", shf);
|
||||
while (*wp != 0)
|
||||
tputC(*wp++, shf);
|
||||
tputc(')', shf);
|
||||
wp++;
|
||||
break;
|
||||
case EXPRSUB:
|
||||
shf_puts("$((", shf);
|
||||
while (*wp != 0)
|
||||
tputC(*wp++, shf);
|
||||
shf_puts("))", shf);
|
||||
wp++;
|
||||
break;
|
||||
case OQUOTE:
|
||||
quotelevel++;
|
||||
tputc('"', shf);
|
||||
break;
|
||||
case CQUOTE:
|
||||
if (quotelevel)
|
||||
quotelevel--;
|
||||
tputc('"', shf);
|
||||
break;
|
||||
case OSUBST:
|
||||
tputc('$', shf);
|
||||
if (*wp++ == '{')
|
||||
tputc('{', shf);
|
||||
while ((c = *wp++) != 0)
|
||||
tputC(c, shf);
|
||||
break;
|
||||
case CSUBST:
|
||||
if (*wp++ == '}')
|
||||
tputc('}', shf);
|
||||
break;
|
||||
case OPAT:
|
||||
tputc(*wp++, shf);
|
||||
tputc('(', shf);
|
||||
break;
|
||||
case SPAT:
|
||||
tputc('|', shf);
|
||||
break;
|
||||
case CPAT:
|
||||
tputc(')', shf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this is the _only_ way to reliably handle
|
||||
* variable args with an ANSI compiler
|
||||
*/
|
||||
/* VARARGS */
|
||||
int
|
||||
fptreef(struct shf *shf, int indent, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
|
||||
vfptreef(shf, indent, fmt, va);
|
||||
va_end(va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* VARARGS */
|
||||
char *
|
||||
snptreef(char *s, int n, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
struct shf shf;
|
||||
|
||||
shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
|
||||
|
||||
va_start(va, fmt);
|
||||
vfptreef(&shf, 0, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return (shf_sclose(&shf)); /* null terminates */
|
||||
}
|
||||
|
||||
static void
|
||||
vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = *fmt++)) {
|
||||
if (c == '%') {
|
||||
switch ((c = *fmt++)) {
|
||||
case 'c':
|
||||
tputc(va_arg(va, int), shf);
|
||||
break;
|
||||
case 's':
|
||||
shf_puts(va_arg(va, char *), shf);
|
||||
break;
|
||||
case 'S': /* word */
|
||||
tputS(va_arg(va, char *), shf);
|
||||
break;
|
||||
case 'd': /* decimal */
|
||||
shf_fprintf(shf, "%d", va_arg(va, int));
|
||||
break;
|
||||
case 'u': /* decimal */
|
||||
shf_fprintf(shf, "%u", va_arg(va, unsigned int));
|
||||
break;
|
||||
case 'T': /* format tree */
|
||||
ptree(va_arg(va, struct op *), indent, shf);
|
||||
break;
|
||||
case ';': /* newline or ; */
|
||||
case 'N': /* newline or space */
|
||||
if (shf->flags & SHF_STRING) {
|
||||
if (c == ';')
|
||||
tputc(';', shf);
|
||||
tputc(' ', shf);
|
||||
} else {
|
||||
int i;
|
||||
|
||||
tputc('\n', shf);
|
||||
for (i = indent; i >= 8; i -= 8)
|
||||
tputc('\t', shf);
|
||||
for (; i > 0; --i)
|
||||
tputc(' ', shf);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
pioact(shf, indent, va_arg(va, struct ioword *));
|
||||
break;
|
||||
default:
|
||||
tputc(c, shf);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
tputc(c, shf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* copy tree (for function definition)
|
||||
*/
|
||||
struct op *
|
||||
tcopy(struct op *t, Area *ap)
|
||||
{
|
||||
struct op *r;
|
||||
const char **tw;
|
||||
char **rw;
|
||||
|
||||
if (t == NULL)
|
||||
return (NULL);
|
||||
|
||||
r = alloc(sizeof(struct op), ap);
|
||||
|
||||
r->type = t->type;
|
||||
r->u.evalflags = t->u.evalflags;
|
||||
|
||||
if (t->type == TCASE)
|
||||
r->str = wdcopy(t->str, ap);
|
||||
else
|
||||
strdupx(r->str, t->str, ap);
|
||||
|
||||
if (t->vars == NULL)
|
||||
r->vars = NULL;
|
||||
else {
|
||||
for (tw = (const char **)t->vars; *tw++ != NULL; )
|
||||
;
|
||||
rw = r->vars = alloc((tw - (const char **)t->vars + 1) *
|
||||
sizeof(*tw), ap);
|
||||
for (tw = (const char **)t->vars; *tw != NULL; )
|
||||
*rw++ = wdcopy(*tw++, ap);
|
||||
*rw = NULL;
|
||||
}
|
||||
|
||||
if (t->args == NULL)
|
||||
r->args = NULL;
|
||||
else {
|
||||
for (tw = t->args; *tw++ != NULL; )
|
||||
;
|
||||
r->args = (const char **)(rw = alloc((tw - t->args + 1) *
|
||||
sizeof(*tw), ap));
|
||||
for (tw = t->args; *tw != NULL; )
|
||||
*rw++ = wdcopy(*tw++, ap);
|
||||
*rw = NULL;
|
||||
}
|
||||
|
||||
r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
|
||||
|
||||
r->left = tcopy(t->left, ap);
|
||||
r->right = tcopy(t->right, ap);
|
||||
r->lineno = t->lineno;
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
char *
|
||||
wdcopy(const char *wp, Area *ap)
|
||||
{
|
||||
size_t len = wdscan(wp, EOS) - wp;
|
||||
return (memcpy(alloc(len, ap), wp, len));
|
||||
}
|
||||
|
||||
/* return the position of prefix c in wp plus 1 */
|
||||
const char *
|
||||
wdscan(const char *wp, int c)
|
||||
{
|
||||
int nest = 0;
|
||||
|
||||
while (1)
|
||||
switch (*wp++) {
|
||||
case EOS:
|
||||
return (wp);
|
||||
case ADELIM:
|
||||
if (c == ADELIM)
|
||||
return (wp + 1);
|
||||
/* FALLTHROUGH */
|
||||
case CHAR:
|
||||
case QCHAR:
|
||||
wp++;
|
||||
break;
|
||||
case COMSUB:
|
||||
case EXPRSUB:
|
||||
while (*wp++ != 0)
|
||||
;
|
||||
break;
|
||||
case OQUOTE:
|
||||
case CQUOTE:
|
||||
break;
|
||||
case OSUBST:
|
||||
nest++;
|
||||
while (*wp++ != '\0')
|
||||
;
|
||||
break;
|
||||
case CSUBST:
|
||||
wp++;
|
||||
if (c == CSUBST && nest == 0)
|
||||
return (wp);
|
||||
nest--;
|
||||
break;
|
||||
case OPAT:
|
||||
nest++;
|
||||
wp++;
|
||||
break;
|
||||
case SPAT:
|
||||
case CPAT:
|
||||
if (c == wp[-1] && nest == 0)
|
||||
return (wp);
|
||||
if (wp[-1] == CPAT)
|
||||
nest--;
|
||||
break;
|
||||
default:
|
||||
internal_warningf(
|
||||
"wdscan: unknown char 0x%x (carrying on)",
|
||||
wp[-1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* return a copy of wp without any of the mark up characters and
|
||||
* with quote characters (" ' \) stripped.
|
||||
* (string is allocated from ATEMP)
|
||||
*/
|
||||
char *
|
||||
wdstrip(const char *wp, bool keepq, bool make_magic)
|
||||
{
|
||||
struct shf shf;
|
||||
int c;
|
||||
|
||||
shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
|
||||
|
||||
/* problems:
|
||||
* `...` -> $(...)
|
||||
* x${foo:-"hi"} -> x${foo:-hi}
|
||||
* x${foo:-'hi'} -> x${foo:-hi} unless keepq
|
||||
*/
|
||||
while (1)
|
||||
switch (*wp++) {
|
||||
case EOS:
|
||||
return (shf_sclose(&shf)); /* null terminates */
|
||||
case ADELIM:
|
||||
case CHAR:
|
||||
c = *wp++;
|
||||
if (make_magic && (ISMAGIC(c) || c == '[' || c == NOT ||
|
||||
c == '-' || c == ']' || c == '*' || c == '?'))
|
||||
shf_putchar(MAGIC, &shf);
|
||||
shf_putchar(c, &shf);
|
||||
break;
|
||||
case QCHAR:
|
||||
c = *wp++;
|
||||
if (keepq && (c == '"' || c == '`' || c == '$' || c == '\\'))
|
||||
shf_putchar('\\', &shf);
|
||||
shf_putchar(c, &shf);
|
||||
break;
|
||||
case COMSUB:
|
||||
shf_puts("$(", &shf);
|
||||
while (*wp != 0)
|
||||
shf_putchar(*wp++, &shf);
|
||||
shf_putchar(')', &shf);
|
||||
break;
|
||||
case EXPRSUB:
|
||||
shf_puts("$((", &shf);
|
||||
while (*wp != 0)
|
||||
shf_putchar(*wp++, &shf);
|
||||
shf_puts("))", &shf);
|
||||
break;
|
||||
case OQUOTE:
|
||||
break;
|
||||
case CQUOTE:
|
||||
break;
|
||||
case OSUBST:
|
||||
shf_putchar('$', &shf);
|
||||
if (*wp++ == '{')
|
||||
shf_putchar('{', &shf);
|
||||
while ((c = *wp++) != 0)
|
||||
shf_putchar(c, &shf);
|
||||
break;
|
||||
case CSUBST:
|
||||
if (*wp++ == '}')
|
||||
shf_putchar('}', &shf);
|
||||
break;
|
||||
case OPAT:
|
||||
if (make_magic) {
|
||||
shf_putchar(MAGIC, &shf);
|
||||
shf_putchar(*wp++ | 0x80, &shf);
|
||||
} else {
|
||||
shf_putchar(*wp++, &shf);
|
||||
shf_putchar('(', &shf);
|
||||
}
|
||||
break;
|
||||
case SPAT:
|
||||
if (make_magic)
|
||||
shf_putchar(MAGIC, &shf);
|
||||
shf_putchar('|', &shf);
|
||||
break;
|
||||
case CPAT:
|
||||
if (make_magic)
|
||||
shf_putchar(MAGIC, &shf);
|
||||
shf_putchar(')', &shf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct ioword **
|
||||
iocopy(struct ioword **iow, Area *ap)
|
||||
{
|
||||
struct ioword **ior;
|
||||
int i;
|
||||
|
||||
for (ior = iow; *ior++ != NULL; )
|
||||
;
|
||||
ior = alloc((ior - iow + 1) * sizeof(struct ioword *), ap);
|
||||
|
||||
for (i = 0; iow[i] != NULL; i++) {
|
||||
struct ioword *p, *q;
|
||||
|
||||
p = iow[i];
|
||||
q = alloc(sizeof(struct ioword), ap);
|
||||
ior[i] = q;
|
||||
*q = *p;
|
||||
if (p->name != NULL)
|
||||
q->name = wdcopy(p->name, ap);
|
||||
if (p->delim != NULL)
|
||||
q->delim = wdcopy(p->delim, ap);
|
||||
if (p->heredoc != NULL)
|
||||
strdupx(q->heredoc, p->heredoc, ap);
|
||||
}
|
||||
ior[i] = NULL;
|
||||
|
||||
return (ior);
|
||||
}
|
||||
|
||||
/*
|
||||
* free tree (for function definition)
|
||||
*/
|
||||
void
|
||||
tfree(struct op *t, Area *ap)
|
||||
{
|
||||
char **w;
|
||||
|
||||
if (t == NULL)
|
||||
return;
|
||||
|
||||
if (t->str != NULL)
|
||||
afree(t->str, ap);
|
||||
|
||||
if (t->vars != NULL) {
|
||||
for (w = t->vars; *w != NULL; w++)
|
||||
afree(*w, ap);
|
||||
afree(t->vars, ap);
|
||||
}
|
||||
|
||||
if (t->args != NULL) {
|
||||
union mksh_ccphack cw;
|
||||
/* XXX we assume the caller is right */
|
||||
cw.ro = t->args;
|
||||
for (w = cw.rw; *w != NULL; w++)
|
||||
afree(*w, ap);
|
||||
afree(t->args, ap);
|
||||
}
|
||||
|
||||
if (t->ioact != NULL)
|
||||
iofree(t->ioact, ap);
|
||||
|
||||
tfree(t->left, ap);
|
||||
tfree(t->right, ap);
|
||||
|
||||
afree(t, ap);
|
||||
}
|
||||
|
||||
static void
|
||||
iofree(struct ioword **iow, Area *ap)
|
||||
{
|
||||
struct ioword **iop;
|
||||
struct ioword *p;
|
||||
|
||||
for (iop = iow; (p = *iop++) != NULL; ) {
|
||||
if (p->name != NULL)
|
||||
afree(p->name, ap);
|
||||
if (p->delim != NULL)
|
||||
afree(p->delim, ap);
|
||||
if (p->heredoc != NULL)
|
||||
afree(p->heredoc, ap);
|
||||
afree(p, ap);
|
||||
}
|
||||
afree(iow, ap);
|
||||
}
|
39
src/var_spec.h
Normal file
39
src/var_spec.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#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
|
Loading…
Reference in a new issue