mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-28 07:59:50 +02:00
commit bash-20120727 snapshot
This commit is contained in:
@@ -14227,3 +14227,67 @@ bashline.c
|
||||
before adding the dummy history entry to make sure the dummy entry
|
||||
doesn't get added to previous incomplete command. Partial fix for
|
||||
problem reported by Peng Yu <pengyu.ut@gmail.com>
|
||||
|
||||
7/24
|
||||
----
|
||||
configure.in
|
||||
- interix: define RECYCLES_PIDS. Based on a report from Michael
|
||||
Haubenwallner <michael.haubenwallner@salomon.at>
|
||||
|
||||
7/26
|
||||
----
|
||||
jobs.c
|
||||
- make_child: call bgp_delete on the newly-created pid unconditionally.
|
||||
Some systems reuse pids before cycling through an entire set of
|
||||
CHILD_MAX/_SC_CHILD_MAX unique pids. This is no longer dependent
|
||||
on RECYCLES_PIDS. Based on a report from Michael Haubenwallner
|
||||
<michael.haubenwallner@salomon.at>
|
||||
|
||||
support/shobj-conf
|
||||
- Mac OS X: drop MACOSX_DEPLOYMENT_TARGET=10.3 from the LDFLAGS. We
|
||||
can finally kill Panther
|
||||
|
||||
7/28
|
||||
----
|
||||
subst.c
|
||||
- command_substitute: make sure last_made_pid gets reset if make_child
|
||||
fails
|
||||
|
||||
execute_cmd.c
|
||||
- execute_command_internal: case cm_simple: decide whether or not to
|
||||
wait_for a child if already_making_children is non-zero, indicates
|
||||
that there is an unwaited-for child. More of fix for bug report
|
||||
from Michael Haubenwallner <michael.haubenwallner@salomon.at>
|
||||
|
||||
jobs.c
|
||||
- make_child: call delete_old_job (new_pid) unconditionally, don't
|
||||
bother to check whether or not pid wrap occurred. Rest of fix for
|
||||
bug report from Michael Haubenwallner
|
||||
<michael.haubenwallner@salomon.at>
|
||||
|
||||
7/29
|
||||
----
|
||||
shell.c
|
||||
- subshell_exit: new function, exits the shell (via call to sh_exit())
|
||||
after calling any defined exit trap
|
||||
|
||||
externs.h
|
||||
- subshell_exit: new extern declaration
|
||||
|
||||
execute_cmd.c
|
||||
- execute_command_internal: make sure to call subshell_exit for
|
||||
{} group commands executed asynchronously (&). Part of fix for
|
||||
EXIT trap bug reported by Maarten Billemont <lhunath@lyndir.com>
|
||||
|
||||
sig.c
|
||||
- reset_terminating_signals: make sure to set termsigs_initialized back
|
||||
to 0, so a subsequent call to initialize_terminating_signals works
|
||||
right. Rest of fix for bug reported by Maarten Billemont
|
||||
<lhunath@lyndir.com>
|
||||
|
||||
{execute_cmd,general,jobs,mailcheck,mksyntax,test}.c
|
||||
builtins/{cd,fc,pushd,ulimit}.def
|
||||
lib/malloc/getpagesize.h
|
||||
lib/sh/{clktck,fpurge,inet_aton,mailstat,oslib,pathcanon,pathphys,spell,strerror}.c
|
||||
- make inclusion of <sys/param.h> dependent on HAVE_SYS_PARAM_H
|
||||
consistently
|
||||
|
||||
@@ -14219,3 +14219,74 @@ doc/{bash.1,bashref.texi}
|
||||
- add some text to the description of set -e clarifying its effect
|
||||
on shell functions and shell function execution. Suggested by
|
||||
Rainer Blome <rainer.blome@gmx.de>
|
||||
|
||||
bashline.c
|
||||
- edit_and_execute_command: increment current_command_line_count before
|
||||
adding partial line to command history (for command-oriented-history
|
||||
because of rl_newline at beginning of function), then reset it to 0
|
||||
before adding the dummy history entry to make sure the dummy entry
|
||||
doesn't get added to previous incomplete command. Partial fix for
|
||||
problem reported by Peng Yu <pengyu.ut@gmail.com>
|
||||
|
||||
7/24
|
||||
----
|
||||
configure.in
|
||||
- interix: define RECYCLES_PIDS. Based on a report from Michael
|
||||
Haubenwallner <michael.haubenwallner@salomon.at>
|
||||
|
||||
7/26
|
||||
----
|
||||
jobs.c
|
||||
- make_child: call bgp_delete on the newly-created pid unconditionally.
|
||||
Some systems reuse pids before cycling through an entire set of
|
||||
CHILD_MAX/_SC_CHILD_MAX unique pids. This is no longer dependent
|
||||
on RECYCLES_PIDS. Based on a report from Michael Haubenwallner
|
||||
<michael.haubenwallner@salomon.at>
|
||||
|
||||
support/shobj-conf
|
||||
- Mac OS X: drop MACOSX_DEPLOYMENT_TARGET=10.3 from the LDFLAGS. We
|
||||
can finally kill Panther
|
||||
|
||||
7/28
|
||||
----
|
||||
subst.c
|
||||
- command_substitute: make sure last_made_pid gets reset if make_child
|
||||
fails
|
||||
|
||||
execute_cmd.c
|
||||
- execute_command_internal: case cm_simple: decide whether or not to
|
||||
wait_for a child if already_making_children is non-zero, indicates
|
||||
that there is an unwaited-for child. More of fix for bug report
|
||||
from Michael Haubenwallner <michael.haubenwallner@salomon.at>
|
||||
|
||||
jobs.c
|
||||
- make_child: call delete_old_job (new_pid) unconditionally, don't
|
||||
bother to check whether or not pid wrap occurred. Rest of fix for
|
||||
bug report from Michael Haubenwallner
|
||||
<michael.haubenwallner@salomon.at>
|
||||
|
||||
7/29
|
||||
----
|
||||
shell.c
|
||||
- subshell_exit: new function, exits the shell (via call to sh_exit())
|
||||
after calling any defined exit trap
|
||||
|
||||
externs.h
|
||||
- subshell_exit: new extern declaration
|
||||
|
||||
execute_cmd.c
|
||||
- execute_command_internal: make sure to call subshell_exit for
|
||||
{} group commands executed asynchronously (&). Part of fix for
|
||||
EXIT trap bug reported by Maarten Billemont <lhunath@lyndir.com>
|
||||
|
||||
sig.c
|
||||
- reset_terminating_signals: make sure to set termsigs_initialized back
|
||||
to 0, so a subsequent call to initialize_terminating_signals works
|
||||
right. Rest of fix for bug reported by Maarten Billemont
|
||||
<lhunath@lyndir.com>
|
||||
|
||||
{execute_cmd,general,jobs,mailcheck,mksyntax,test}.c
|
||||
builtins/{cd,fc,pushd,ulimit}.def
|
||||
lib/malloc/getpagesize.h
|
||||
- make inclusion of <sys/param.h> dependent on HAVE_SYS_PARAM_H
|
||||
consistently
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.xcode.dsym.sigs</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>dSYM</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.xcode.dsym.sigstat</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>dSYM</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
@%:@! /bin/sh
|
||||
@%:@ From configure.in for Bash 4.2, version 4.048.
|
||||
@%:@ From configure.in for Bash 4.2, version 4.050.
|
||||
@%:@ Guess values for system-dependent variables and create Makefiles.
|
||||
@%:@ Generated by GNU Autoconf 2.68 for bash 4.2-maint.
|
||||
@%:@
|
||||
@@ -9160,7 +9160,7 @@ fi
|
||||
|
||||
done
|
||||
|
||||
for ac_header in sys/pte.h sys/stream.h sys/select.h sys/file.h \
|
||||
for ac_header in sys/pte.h sys/stream.h sys/select.h sys/file.h sys/param.h \
|
||||
sys/resource.h sys/param.h sys/socket.h sys/stat.h \
|
||||
sys/time.h sys/times.h sys/types.h sys/wait.h
|
||||
do :
|
||||
@@ -15760,7 +15760,7 @@ linux*) LOCAL_LDFLAGS=-rdynamic # allow dynamic loading
|
||||
*qnx*) LOCAL_CFLAGS="-Dqnx -F -3s" LOCAL_LDFLAGS="-3s" LOCAL_LIBS="-lunix -lncurses" ;;
|
||||
powerux*) LOCAL_LIBS="-lgen" ;;
|
||||
cygwin*) LOCAL_CFLAGS=-DRECYCLES_PIDS ;;
|
||||
opennt*|interix*) LOCAL_CFLAGS="-DNO_MAIN_ENV_ARG -DBROKEN_DIRENT_D_INO -D_POSIX_SOURCE -D_ALL_SOURCE" ;;
|
||||
opennt*|interix*) LOCAL_CFLAGS="-DNO_MAIN_ENV_ARG -DBROKEN_DIRENT_D_INO -D_POSIX_SOURCE -D_ALL_SOURCE -DRECYCLES_PIDS" ;;
|
||||
esac
|
||||
|
||||
case "${host_os}-${CC}" in
|
||||
|
||||
+53
-53
@@ -15,55 +15,55 @@
|
||||
'configure.in'
|
||||
],
|
||||
{
|
||||
'_LT_AC_TAGCONFIG' => 1,
|
||||
'AM_PROG_F77_C_O' => 1,
|
||||
'AC_INIT' => 1,
|
||||
'_LT_AC_TAGCONFIG' => 1,
|
||||
'm4_pattern_forbid' => 1,
|
||||
'_AM_COND_IF' => 1,
|
||||
'AC_INIT' => 1,
|
||||
'AC_CANONICAL_TARGET' => 1,
|
||||
'AC_SUBST' => 1,
|
||||
'_AM_COND_IF' => 1,
|
||||
'AC_CONFIG_LIBOBJ_DIR' => 1,
|
||||
'AC_FC_SRCEXT' => 1,
|
||||
'AC_SUBST' => 1,
|
||||
'AC_CANONICAL_HOST' => 1,
|
||||
'AC_FC_SRCEXT' => 1,
|
||||
'AC_PROG_LIBTOOL' => 1,
|
||||
'AM_INIT_AUTOMAKE' => 1,
|
||||
'AM_PATH_GUILE' => 1,
|
||||
'AC_CONFIG_SUBDIRS' => 1,
|
||||
'AM_PATH_GUILE' => 1,
|
||||
'AM_AUTOMAKE_VERSION' => 1,
|
||||
'LT_CONFIG_LTDL_DIR' => 1,
|
||||
'AC_REQUIRE_AUX_FILE' => 1,
|
||||
'AC_CONFIG_LINKS' => 1,
|
||||
'm4_sinclude' => 1,
|
||||
'AC_REQUIRE_AUX_FILE' => 1,
|
||||
'LT_SUPPORTED_TAG' => 1,
|
||||
'm4_sinclude' => 1,
|
||||
'AM_MAINTAINER_MODE' => 1,
|
||||
'AM_NLS' => 1,
|
||||
'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
|
||||
'AM_MAKEFILE_INCLUDE' => 1,
|
||||
'_m4_warn' => 1,
|
||||
'AM_MAKEFILE_INCLUDE' => 1,
|
||||
'AM_PROG_CXX_C_O' => 1,
|
||||
'_AM_COND_ENDIF' => 1,
|
||||
'_AM_MAKEFILE_INCLUDE' => 1,
|
||||
'_AM_COND_ENDIF' => 1,
|
||||
'AM_ENABLE_MULTILIB' => 1,
|
||||
'AM_SILENT_RULES' => 1,
|
||||
'AM_PROG_MOC' => 1,
|
||||
'AC_CONFIG_FILES' => 1,
|
||||
'include' => 1,
|
||||
'LT_INIT' => 1,
|
||||
'AM_PROG_AR' => 1,
|
||||
'include' => 1,
|
||||
'AM_GNU_GETTEXT' => 1,
|
||||
'AM_PROG_AR' => 1,
|
||||
'AC_LIBSOURCE' => 1,
|
||||
'AM_PROG_FC_C_O' => 1,
|
||||
'AC_CANONICAL_BUILD' => 1,
|
||||
'AM_PROG_FC_C_O' => 1,
|
||||
'AC_FC_FREEFORM' => 1,
|
||||
'AH_OUTPUT' => 1,
|
||||
'_AM_SUBST_NOTMAKE' => 1,
|
||||
'AC_CONFIG_AUX_DIR' => 1,
|
||||
'sinclude' => 1,
|
||||
'AM_PROG_CC_C_O' => 1,
|
||||
'_AM_SUBST_NOTMAKE' => 1,
|
||||
'm4_pattern_allow' => 1,
|
||||
'AM_XGETTEXT_OPTION' => 1,
|
||||
'AC_CANONICAL_SYSTEM' => 1,
|
||||
'AM_PROG_CC_C_O' => 1,
|
||||
'sinclude' => 1,
|
||||
'AM_CONDITIONAL' => 1,
|
||||
'AC_CANONICAL_SYSTEM' => 1,
|
||||
'AM_XGETTEXT_OPTION' => 1,
|
||||
'AC_CONFIG_HEADERS' => 1,
|
||||
'AC_DEFINE_TRACE_LITERAL' => 1,
|
||||
'AM_POT_TOOLS' => 1,
|
||||
@@ -84,55 +84,55 @@
|
||||
'configure.in'
|
||||
],
|
||||
{
|
||||
'AM_PROG_F77_C_O' => 1,
|
||||
'_LT_AC_TAGCONFIG' => 1,
|
||||
'm4_pattern_forbid' => 1,
|
||||
'AM_PROG_F77_C_O' => 1,
|
||||
'AC_INIT' => 1,
|
||||
'AC_CANONICAL_TARGET' => 1,
|
||||
'm4_pattern_forbid' => 1,
|
||||
'_AM_COND_IF' => 1,
|
||||
'AC_CONFIG_LIBOBJ_DIR' => 1,
|
||||
'AC_CANONICAL_TARGET' => 1,
|
||||
'AC_SUBST' => 1,
|
||||
'AC_CANONICAL_HOST' => 1,
|
||||
'AC_CONFIG_LIBOBJ_DIR' => 1,
|
||||
'AC_FC_SRCEXT' => 1,
|
||||
'AC_CANONICAL_HOST' => 1,
|
||||
'AC_PROG_LIBTOOL' => 1,
|
||||
'AM_INIT_AUTOMAKE' => 1,
|
||||
'AC_CONFIG_SUBDIRS' => 1,
|
||||
'AM_PATH_GUILE' => 1,
|
||||
'AC_CONFIG_SUBDIRS' => 1,
|
||||
'AM_AUTOMAKE_VERSION' => 1,
|
||||
'LT_CONFIG_LTDL_DIR' => 1,
|
||||
'AC_CONFIG_LINKS' => 1,
|
||||
'AC_REQUIRE_AUX_FILE' => 1,
|
||||
'LT_SUPPORTED_TAG' => 1,
|
||||
'AC_CONFIG_LINKS' => 1,
|
||||
'm4_sinclude' => 1,
|
||||
'LT_SUPPORTED_TAG' => 1,
|
||||
'AM_MAINTAINER_MODE' => 1,
|
||||
'AM_NLS' => 1,
|
||||
'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
|
||||
'_m4_warn' => 1,
|
||||
'AM_MAKEFILE_INCLUDE' => 1,
|
||||
'_m4_warn' => 1,
|
||||
'AM_PROG_CXX_C_O' => 1,
|
||||
'_AM_MAKEFILE_INCLUDE' => 1,
|
||||
'_AM_COND_ENDIF' => 1,
|
||||
'_AM_MAKEFILE_INCLUDE' => 1,
|
||||
'AM_ENABLE_MULTILIB' => 1,
|
||||
'AM_SILENT_RULES' => 1,
|
||||
'AM_PROG_MOC' => 1,
|
||||
'AC_CONFIG_FILES' => 1,
|
||||
'LT_INIT' => 1,
|
||||
'include' => 1,
|
||||
'AM_GNU_GETTEXT' => 1,
|
||||
'LT_INIT' => 1,
|
||||
'AM_PROG_AR' => 1,
|
||||
'AM_GNU_GETTEXT' => 1,
|
||||
'AC_LIBSOURCE' => 1,
|
||||
'AC_CANONICAL_BUILD' => 1,
|
||||
'AM_PROG_FC_C_O' => 1,
|
||||
'AC_CANONICAL_BUILD' => 1,
|
||||
'AC_FC_FREEFORM' => 1,
|
||||
'AH_OUTPUT' => 1,
|
||||
'AC_CONFIG_AUX_DIR' => 1,
|
||||
'_AM_SUBST_NOTMAKE' => 1,
|
||||
'm4_pattern_allow' => 1,
|
||||
'AM_PROG_CC_C_O' => 1,
|
||||
'AC_CONFIG_AUX_DIR' => 1,
|
||||
'sinclude' => 1,
|
||||
'AM_CONDITIONAL' => 1,
|
||||
'AC_CANONICAL_SYSTEM' => 1,
|
||||
'AM_PROG_CC_C_O' => 1,
|
||||
'm4_pattern_allow' => 1,
|
||||
'AM_XGETTEXT_OPTION' => 1,
|
||||
'AC_CANONICAL_SYSTEM' => 1,
|
||||
'AM_CONDITIONAL' => 1,
|
||||
'AC_CONFIG_HEADERS' => 1,
|
||||
'AC_DEFINE_TRACE_LITERAL' => 1,
|
||||
'AM_POT_TOOLS' => 1,
|
||||
@@ -155,19 +155,19 @@
|
||||
{
|
||||
'm4_pattern_forbid' => 1,
|
||||
'AC_CONFIG_LIBOBJ_DIR' => 1,
|
||||
'AC_C_VOLATILE' => 1,
|
||||
'AC_TYPE_OFF_T' => 1,
|
||||
'AC_C_VOLATILE' => 1,
|
||||
'AC_FUNC_CLOSEDIR_VOID' => 1,
|
||||
'AC_REPLACE_FNMATCH' => 1,
|
||||
'AC_PROG_LIBTOOL' => 1,
|
||||
'AC_FUNC_STAT' => 1,
|
||||
'AC_FUNC_WAIT3' => 1,
|
||||
'AC_HEADER_TIME' => 1,
|
||||
'AC_FUNC_LSTAT' => 1,
|
||||
'AC_STRUCT_TM' => 1,
|
||||
'AC_FUNC_WAIT3' => 1,
|
||||
'AM_AUTOMAKE_VERSION' => 1,
|
||||
'AC_FUNC_GETMNTENT' => 1,
|
||||
'AC_STRUCT_TM' => 1,
|
||||
'AC_FUNC_LSTAT' => 1,
|
||||
'AC_TYPE_MODE_T' => 1,
|
||||
'AC_FUNC_GETMNTENT' => 1,
|
||||
'AC_FUNC_STRTOD' => 1,
|
||||
'AC_CHECK_HEADERS' => 1,
|
||||
'AC_FUNC_STRNLEN' => 1,
|
||||
@@ -186,17 +186,17 @@
|
||||
'AC_STRUCT_ST_BLOCKS' => 1,
|
||||
'AC_TYPE_SIGNAL' => 1,
|
||||
'AC_TYPE_UID_T' => 1,
|
||||
'AC_PROG_MAKE_SET' => 1,
|
||||
'AC_CONFIG_AUX_DIR' => 1,
|
||||
'm4_pattern_allow' => 1,
|
||||
'AC_PROG_MAKE_SET' => 1,
|
||||
'sinclude' => 1,
|
||||
'm4_pattern_allow' => 1,
|
||||
'AC_DEFINE_TRACE_LITERAL' => 1,
|
||||
'AC_FUNC_STRERROR_R' => 1,
|
||||
'AC_PROG_CC' => 1,
|
||||
'AC_DECL_SYS_SIGLIST' => 1,
|
||||
'AC_FUNC_FORK' => 1,
|
||||
'AC_FUNC_STRCOLL' => 1,
|
||||
'AC_DECL_SYS_SIGLIST' => 1,
|
||||
'AC_FUNC_VPRINTF' => 1,
|
||||
'AC_FUNC_STRCOLL' => 1,
|
||||
'AC_PROG_YACC' => 1,
|
||||
'AC_INIT' => 1,
|
||||
'AC_STRUCT_TIMEZONE' => 1,
|
||||
@@ -218,33 +218,33 @@
|
||||
'AM_MAINTAINER_MODE' => 1,
|
||||
'AC_FUNC_UTIME_NULL' => 1,
|
||||
'AC_FUNC_SELECT_ARGTYPES' => 1,
|
||||
'AC_HEADER_STAT' => 1,
|
||||
'AC_FUNC_STRFTIME' => 1,
|
||||
'AC_PROG_CPP' => 1,
|
||||
'AC_HEADER_STAT' => 1,
|
||||
'AC_C_INLINE' => 1,
|
||||
'AC_PROG_LEX' => 1,
|
||||
'AC_C_CONST' => 1,
|
||||
'AC_PROG_CPP' => 1,
|
||||
'AC_TYPE_PID_T' => 1,
|
||||
'AC_C_CONST' => 1,
|
||||
'AC_PROG_LEX' => 1,
|
||||
'AC_CONFIG_FILES' => 1,
|
||||
'include' => 1,
|
||||
'AC_FUNC_SETVBUF_REVERSED' => 1,
|
||||
'AC_PROG_INSTALL' => 1,
|
||||
'AM_GNU_GETTEXT' => 1,
|
||||
'AC_CHECK_LIB' => 1,
|
||||
'AC_FUNC_OBSTACK' => 1,
|
||||
'AC_CHECK_LIB' => 1,
|
||||
'AC_FUNC_MALLOC' => 1,
|
||||
'AC_FUNC_GETGROUPS' => 1,
|
||||
'AC_FUNC_GETLOADAVG' => 1,
|
||||
'AH_OUTPUT' => 1,
|
||||
'AC_FUNC_FSEEKO' => 1,
|
||||
'AM_PROG_CC_C_O' => 1,
|
||||
'AC_FUNC_MKTIME' => 1,
|
||||
'AC_CANONICAL_SYSTEM' => 1,
|
||||
'AM_CONDITIONAL' => 1,
|
||||
'AC_CANONICAL_SYSTEM' => 1,
|
||||
'AC_FUNC_MKTIME' => 1,
|
||||
'AC_CONFIG_HEADERS' => 1,
|
||||
'AC_HEADER_SYS_WAIT' => 1,
|
||||
'AC_PROG_LN_S' => 1,
|
||||
'AC_FUNC_MEMCMP' => 1,
|
||||
'AC_PROG_LN_S' => 1,
|
||||
'm4_include' => 1,
|
||||
'AC_HEADER_DIRENT' => 1,
|
||||
'AC_CHECK_FUNCS' => 1
|
||||
|
||||
@@ -1228,6 +1228,8 @@ m4trace:configure.in:687: -1- AH_OUTPUT([HAVE_SYS_SELECT_H], [/* Define to 1 if
|
||||
@%:@undef HAVE_SYS_SELECT_H])
|
||||
m4trace:configure.in:687: -1- AH_OUTPUT([HAVE_SYS_FILE_H], [/* Define to 1 if you have the <sys/file.h> header file. */
|
||||
@%:@undef HAVE_SYS_FILE_H])
|
||||
m4trace:configure.in:687: -1- AH_OUTPUT([HAVE_SYS_PARAM_H], [/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
@%:@undef HAVE_SYS_PARAM_H])
|
||||
m4trace:configure.in:687: -1- AH_OUTPUT([HAVE_SYS_RESOURCE_H], [/* Define to 1 if you have the <sys/resource.h> header file. */
|
||||
@%:@undef HAVE_SYS_RESOURCE_H])
|
||||
m4trace:configure.in:687: -1- AH_OUTPUT([HAVE_SYS_PARAM_H], [/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ $PRODUCES cd.c
|
||||
#include "../bashtypes.h"
|
||||
#include "posixdir.h"
|
||||
#include "posixstat.h"
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
This file is cd.def, from which is created cd.c. It implements the
|
||||
builtins "cd" and "pwd" in Bash.
|
||||
|
||||
Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES cd.c
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../bashtypes.h"
|
||||
#include "posixdir.h"
|
||||
#include "posixstat.h"
|
||||
#ifndef _MINIX
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <tilde/tilde.h>
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../flags.h"
|
||||
#include "maxpath.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
extern int posixly_correct;
|
||||
extern int array_needs_making;
|
||||
extern const char * const bash_getcwd_errstr;
|
||||
|
||||
static int bindpwd __P((int));
|
||||
static int setpwd __P((char *));
|
||||
static char *resetpwd __P((char *));
|
||||
static int change_to_directory __P((char *, int));
|
||||
|
||||
/* Change this to 1 to get cd spelling correction by default. */
|
||||
int cdspelling = 0;
|
||||
|
||||
int cdable_vars;
|
||||
|
||||
static int eflag; /* file scope so bindpwd() can see it */
|
||||
|
||||
$BUILTIN cd
|
||||
$FUNCTION cd_builtin
|
||||
$SHORT_DOC cd [-L|[-P [-e]]] [dir]
|
||||
Change the shell working directory.
|
||||
|
||||
Change the current directory to DIR. The default DIR is the value of the
|
||||
HOME shell variable.
|
||||
|
||||
The variable CDPATH defines the search path for the directory containing
|
||||
DIR. Alternative directory names in CDPATH are separated by a colon (:).
|
||||
A null directory name is the same as the current directory. If DIR begins
|
||||
with a slash (/), then CDPATH is not used.
|
||||
|
||||
If the directory is not found, and the shell option `cdable_vars' is set,
|
||||
the word is assumed to be a variable name. If that variable has a value,
|
||||
its value is used for DIR.
|
||||
|
||||
Options:
|
||||
-L force symbolic links to be followed: resolve symbolic links in
|
||||
DIR after processing instances of `..'
|
||||
-P use the physical directory structure without following symbolic
|
||||
links: resolve symbolic links in DIR before processing instances
|
||||
of `..'
|
||||
-e if the -P option is supplied, and the current working directory
|
||||
cannot be determined successfully, exit with a non-zero status
|
||||
|
||||
The default is to follow symbolic links, as if `-L' were specified.
|
||||
`..' is processed by removing the immediately previous pathname component
|
||||
back to a slash or the beginning of DIR.
|
||||
|
||||
Exit Status:
|
||||
Returns 0 if the directory is changed, and if $PWD is set successfully when
|
||||
-P is used; non-zero otherwise.
|
||||
$END
|
||||
|
||||
/* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */
|
||||
static int
|
||||
setpwd (dirname)
|
||||
char *dirname;
|
||||
{
|
||||
int old_anm;
|
||||
SHELL_VAR *tvar;
|
||||
|
||||
old_anm = array_needs_making;
|
||||
tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
|
||||
if (tvar && readonly_p (tvar))
|
||||
return EXECUTION_FAILURE;
|
||||
if (tvar && old_anm == 0 && array_needs_making && exported_p (tvar))
|
||||
{
|
||||
update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
|
||||
array_needs_making = 0;
|
||||
}
|
||||
return EXECUTION_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
bindpwd (no_symlinks)
|
||||
int no_symlinks;
|
||||
{
|
||||
char *dirname, *pwdvar;
|
||||
int old_anm, r;
|
||||
SHELL_VAR *tvar;
|
||||
|
||||
r = sh_chkwrite (EXECUTION_SUCCESS);
|
||||
|
||||
#define tcwd the_current_working_directory
|
||||
dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
|
||||
: get_working_directory ("cd");
|
||||
#undef tcwd
|
||||
|
||||
old_anm = array_needs_making;
|
||||
pwdvar = get_string_value ("PWD");
|
||||
|
||||
tvar = bind_variable ("OLDPWD", pwdvar, 0);
|
||||
if (tvar && readonly_p (tvar))
|
||||
r = EXECUTION_FAILURE;
|
||||
|
||||
if (old_anm == 0 && array_needs_making && exported_p (tvar))
|
||||
{
|
||||
update_export_env_inplace ("OLDPWD=", 7, pwdvar);
|
||||
array_needs_making = 0;
|
||||
}
|
||||
|
||||
if (setpwd (dirname) == EXECUTION_FAILURE)
|
||||
r = EXECUTION_FAILURE;
|
||||
if (dirname == 0 && eflag)
|
||||
r = EXECUTION_FAILURE;
|
||||
|
||||
if (dirname && dirname != the_current_working_directory)
|
||||
free (dirname);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
/* Call get_working_directory to reset the value of
|
||||
the_current_working_directory () */
|
||||
static char *
|
||||
resetpwd (caller)
|
||||
char *caller;
|
||||
{
|
||||
char *tdir;
|
||||
|
||||
FREE (the_current_working_directory);
|
||||
the_current_working_directory = (char *)NULL;
|
||||
tdir = get_working_directory (caller);
|
||||
return (tdir);
|
||||
}
|
||||
|
||||
#define LCD_DOVARS 0x001
|
||||
#define LCD_DOSPELL 0x002
|
||||
#define LCD_PRINTPATH 0x004
|
||||
#define LCD_FREEDIRNAME 0x008
|
||||
|
||||
/* This builtin is ultimately the way that all user-visible commands should
|
||||
change the current working directory. It is called by cd_to_string (),
|
||||
so the programming interface is simple, and it handles errors and
|
||||
restrictions properly. */
|
||||
int
|
||||
cd_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
char *dirname, *cdpath, *path, *temp;
|
||||
int path_index, no_symlinks, opt, lflag;
|
||||
|
||||
#if defined (RESTRICTED_SHELL)
|
||||
if (restricted)
|
||||
{
|
||||
sh_restricted ((char *)NULL);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
#endif /* RESTRICTED_SHELL */
|
||||
|
||||
eflag = 0;
|
||||
no_symlinks = no_symbolic_links;
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "LP")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'P':
|
||||
no_symlinks = 1;
|
||||
break;
|
||||
case 'L':
|
||||
no_symlinks = 0;
|
||||
break;
|
||||
case 'e':
|
||||
eflag = 1;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
lflag = (cdable_vars ? LCD_DOVARS : 0) |
|
||||
((interactive && cdspelling) ? LCD_DOSPELL : 0);
|
||||
if (eflag && no_symlinks == 0)
|
||||
eflag = 0;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
/* `cd' without arguments is equivalent to `cd $HOME' */
|
||||
dirname = get_string_value ("HOME");
|
||||
|
||||
if (dirname == 0)
|
||||
{
|
||||
builtin_error (_("HOME not set"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
lflag = 0;
|
||||
}
|
||||
#if defined (CD_COMPLAINS)
|
||||
else if (list->next)
|
||||
{
|
||||
builtin_error (_("too many arguments"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
#endif
|
||||
else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
|
||||
{
|
||||
/* This is `cd -', equivalent to `cd $OLDPWD' */
|
||||
dirname = get_string_value ("OLDPWD");
|
||||
|
||||
if (dirname == 0)
|
||||
{
|
||||
builtin_error (_("OLDPWD not set"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
#if 0
|
||||
lflag = interactive ? LCD_PRINTPATH : 0;
|
||||
#else
|
||||
lflag = LCD_PRINTPATH; /* According to SUSv3 */
|
||||
#endif
|
||||
}
|
||||
else if (absolute_pathname (list->word->word))
|
||||
dirname = list->word->word;
|
||||
else if (privileged_mode == 0 && (cdpath = get_string_value ("CDPATH")))
|
||||
{
|
||||
dirname = list->word->word;
|
||||
|
||||
/* Find directory in $CDPATH. */
|
||||
path_index = 0;
|
||||
while (path = extract_colon_unit (cdpath, &path_index))
|
||||
{
|
||||
/* OPT is 1 if the path element is non-empty */
|
||||
opt = path[0] != '\0';
|
||||
temp = sh_makepath (path, dirname, MP_DOTILDE);
|
||||
free (path);
|
||||
|
||||
if (change_to_directory (temp, no_symlinks))
|
||||
{
|
||||
/* POSIX.2 says that if a nonempty directory from CDPATH
|
||||
is used to find the directory to change to, the new
|
||||
directory name is echoed to stdout, whether or not
|
||||
the shell is interactive. */
|
||||
if (opt && (path = no_symlinks ? temp : the_current_working_directory))
|
||||
printf ("%s\n", path);
|
||||
|
||||
free (temp);
|
||||
#if 0
|
||||
/* Posix.2 says that after using CDPATH, the resultant
|
||||
value of $PWD will not contain `.' or `..'. */
|
||||
return (bindpwd (posixly_correct || no_symlinks));
|
||||
#else
|
||||
return (bindpwd (no_symlinks));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
free (temp);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* changed for bash-4.2 Posix cd description steps 5-6 */
|
||||
/* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
|
||||
try the current directory, so we just punt now with an error
|
||||
message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
|
||||
is so we don't mistakenly treat a CDPATH value of "" as not
|
||||
specifying the current directory. */
|
||||
if (posixly_correct && cdpath[0])
|
||||
{
|
||||
builtin_error ("%s: %s", dirname, strerror (ENOENT));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
dirname = list->word->word;
|
||||
|
||||
/* When we get here, DIRNAME is the directory to change to. If we
|
||||
chdir successfully, just return. */
|
||||
if (change_to_directory (dirname, no_symlinks))
|
||||
{
|
||||
if (lflag & LCD_PRINTPATH)
|
||||
printf ("%s\n", dirname);
|
||||
return (bindpwd (no_symlinks));
|
||||
}
|
||||
|
||||
/* If the user requests it, then perhaps this is the name of
|
||||
a shell variable, whose value contains the directory to
|
||||
change to. */
|
||||
if (lflag & LCD_DOVARS)
|
||||
{
|
||||
temp = get_string_value (dirname);
|
||||
if (temp && change_to_directory (temp, no_symlinks))
|
||||
{
|
||||
printf ("%s\n", temp);
|
||||
return (bindpwd (no_symlinks));
|
||||
}
|
||||
}
|
||||
|
||||
/* If the user requests it, try to find a directory name similar in
|
||||
spelling to the one requested, in case the user made a simple
|
||||
typo. This is similar to the UNIX 8th and 9th Edition shells. */
|
||||
if (lflag & LCD_DOSPELL)
|
||||
{
|
||||
temp = dirspell (dirname);
|
||||
if (temp && change_to_directory (temp, no_symlinks))
|
||||
{
|
||||
printf ("%s\n", temp);
|
||||
free (temp);
|
||||
return (bindpwd (no_symlinks));
|
||||
}
|
||||
else
|
||||
FREE (temp);
|
||||
}
|
||||
|
||||
builtin_error ("%s: %s", dirname, strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
$BUILTIN pwd
|
||||
$FUNCTION pwd_builtin
|
||||
$SHORT_DOC pwd [-LP]
|
||||
Print the name of the current working directory.
|
||||
|
||||
Options:
|
||||
-L print the value of $PWD if it names the current working
|
||||
directory
|
||||
-P print the physical directory, without any symbolic links
|
||||
|
||||
By default, `pwd' behaves as if `-L' were specified.
|
||||
|
||||
Exit Status:
|
||||
Returns 0 unless an invalid option is given or the current directory
|
||||
cannot be read.
|
||||
$END
|
||||
|
||||
/* Non-zero means that pwd always prints the physical directory, without
|
||||
symbolic links. */
|
||||
static int verbatim_pwd;
|
||||
|
||||
/* Print the name of the current working directory. */
|
||||
int
|
||||
pwd_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
char *directory;
|
||||
int opt, pflag;
|
||||
|
||||
verbatim_pwd = no_symbolic_links;
|
||||
pflag = 0;
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "LP")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'P':
|
||||
verbatim_pwd = pflag = 1;
|
||||
break;
|
||||
case 'L':
|
||||
verbatim_pwd = 0;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
#define tcwd the_current_working_directory
|
||||
|
||||
directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
|
||||
: get_working_directory ("pwd");
|
||||
|
||||
/* Try again using getcwd() if canonicalization fails (for instance, if
|
||||
the file system has changed state underneath bash). */
|
||||
if ((tcwd && directory == 0) ||
|
||||
(posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
|
||||
{
|
||||
if (directory && directory != tcwd)
|
||||
free (directory);
|
||||
directory = resetpwd ("pwd");
|
||||
}
|
||||
|
||||
#undef tcwd
|
||||
|
||||
if (directory)
|
||||
{
|
||||
opt = EXECUTION_SUCCESS;
|
||||
printf ("%s\n", directory);
|
||||
/* This is dumb but posix-mandated. */
|
||||
if (posixly_correct && pflag)
|
||||
opt = setpwd (directory);
|
||||
if (directory != the_current_working_directory)
|
||||
free (directory);
|
||||
return (sh_chkwrite (opt));
|
||||
}
|
||||
else
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* Do the work of changing to the directory NEWDIR. Handle symbolic
|
||||
link following, etc. This function *must* return with
|
||||
the_current_working_directory either set to NULL (in which case
|
||||
getcwd() will eventually be called), or set to a string corresponding
|
||||
to the working directory. Return 1 on success, 0 on failure. */
|
||||
|
||||
static int
|
||||
change_to_directory (newdir, nolinks)
|
||||
char *newdir;
|
||||
int nolinks;
|
||||
{
|
||||
char *t, *tdir;
|
||||
int err, canon_failed, r, ndlen, dlen;
|
||||
|
||||
tdir = (char *)NULL;
|
||||
|
||||
if (the_current_working_directory == 0)
|
||||
{
|
||||
t = get_working_directory ("chdir");
|
||||
FREE (t);
|
||||
}
|
||||
|
||||
t = make_absolute (newdir, the_current_working_directory);
|
||||
|
||||
/* TDIR is either the canonicalized absolute pathname of NEWDIR
|
||||
(nolinks == 0) or the absolute physical pathname of NEWDIR
|
||||
(nolinks != 0). */
|
||||
tdir = nolinks ? sh_physpath (t, 0)
|
||||
: sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
|
||||
|
||||
ndlen = strlen (newdir);
|
||||
dlen = strlen (t);
|
||||
|
||||
/* Use the canonicalized version of NEWDIR, or, if canonicalization
|
||||
failed, use the non-canonical form. */
|
||||
canon_failed = 0;
|
||||
if (tdir && *tdir)
|
||||
free (t);
|
||||
else
|
||||
{
|
||||
FREE (tdir);
|
||||
tdir = t;
|
||||
canon_failed = 1;
|
||||
}
|
||||
|
||||
/* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
|
||||
returns NULL (because it checks the path, it will return NULL if the
|
||||
resolved path doesn't exist), fail immediately. */
|
||||
if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
|
||||
{
|
||||
#if defined ENAMETOOLONG
|
||||
if (errno != ENOENT && errno != ENAMETOOLONG)
|
||||
#else
|
||||
if (errno != ENOENT)
|
||||
#endif
|
||||
errno = ENOTDIR;
|
||||
free (tdir);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* If the chdir succeeds, update the_current_working_directory. */
|
||||
if (chdir (nolinks ? newdir : tdir) == 0)
|
||||
{
|
||||
/* If canonicalization failed, but the chdir succeeded, reset the
|
||||
shell's idea of the_current_working_directory. */
|
||||
if (canon_failed)
|
||||
{
|
||||
t = resetpwd ("cd");
|
||||
if (t == 0)
|
||||
set_working_directory (tdir);
|
||||
else
|
||||
free (t);
|
||||
}
|
||||
else
|
||||
set_working_directory (tdir);
|
||||
|
||||
free (tdir);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* We failed to change to the appropriate directory name. If we tried
|
||||
what the user passed (nolinks != 0), punt now. */
|
||||
if (nolinks)
|
||||
{
|
||||
free (tdir);
|
||||
return (0);
|
||||
}
|
||||
|
||||
err = errno;
|
||||
|
||||
/* We're not in physical mode (nolinks == 0), but we failed to change to
|
||||
the canonicalized directory name (TDIR). Try what the user passed
|
||||
verbatim. If we succeed, reinitialize the_current_working_directory. */
|
||||
if (chdir (newdir) == 0)
|
||||
{
|
||||
t = resetpwd ("cd");
|
||||
if (t == 0)
|
||||
set_working_directory (tdir);
|
||||
else
|
||||
free (t);
|
||||
|
||||
r = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = err;
|
||||
r = 0;
|
||||
}
|
||||
|
||||
free (tdir);
|
||||
return r;
|
||||
}
|
||||
+1
-1
@@ -52,7 +52,7 @@ $END
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HISTORY)
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include "../bashtypes.h"
|
||||
|
||||
@@ -0,0 +1,683 @@
|
||||
This file is fc.def, from which is created fc.c.
|
||||
It implements the builtin "fc" in Bash.
|
||||
|
||||
Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES fc.c
|
||||
|
||||
$BUILTIN fc
|
||||
$FUNCTION fc_builtin
|
||||
$DEPENDS_ON HISTORY
|
||||
$SHORT_DOC fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
|
||||
Display or execute commands from the history list.
|
||||
|
||||
fc is used to list or edit and re-execute commands from the history list.
|
||||
FIRST and LAST can be numbers specifying the range, or FIRST can be a
|
||||
string, which means the most recent command beginning with that
|
||||
string.
|
||||
|
||||
Options:
|
||||
-e ENAME select which editor to use. Default is FCEDIT, then EDITOR,
|
||||
then vi
|
||||
-l list lines instead of editing
|
||||
-n omit line numbers when listing
|
||||
-r reverse the order of the lines (newest listed first)
|
||||
|
||||
With the `fc -s [pat=rep ...] [command]' format, COMMAND is
|
||||
re-executed after the substitution OLD=NEW is performed.
|
||||
|
||||
A useful alias to use with this is r='fc -s', so that typing `r cc'
|
||||
runs the last command beginning with `cc' and typing `r' re-executes
|
||||
the last command.
|
||||
|
||||
Exit Status:
|
||||
Returns success or status of executed command; non-zero if an error occurs.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HISTORY)
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include "../bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <chartypes.h>
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../builtins.h"
|
||||
#include "../flags.h"
|
||||
#include "../bashhist.h"
|
||||
#include "maxpath.h"
|
||||
#include <readline/history.h>
|
||||
#include "bashgetopt.h"
|
||||
#include "common.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
extern int current_command_line_count;
|
||||
extern int literal_history;
|
||||
extern int posixly_correct;
|
||||
extern int subshell_environment, interactive_shell;
|
||||
|
||||
extern int unlink __P((const char *));
|
||||
|
||||
extern FILE *sh_mktmpfp __P((char *, int, char **));
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* The K*rn shell style fc command (Fix Command) */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* fc builtin command (fix command) for Bash for those who
|
||||
like K*rn-style history better than csh-style.
|
||||
|
||||
fc [-e ename] [-nlr] [first] [last]
|
||||
|
||||
FIRST and LAST can be numbers specifying the range, or FIRST can be
|
||||
a string, which means the most recent command beginning with that
|
||||
string.
|
||||
|
||||
-e ENAME selects which editor to use. Default is FCEDIT, then EDITOR,
|
||||
then the editor which corresponds to the current readline editing
|
||||
mode, then vi.
|
||||
|
||||
-l means list lines instead of editing.
|
||||
-n means no line numbers listed.
|
||||
-r means reverse the order of the lines (making it newest listed first).
|
||||
|
||||
fc -e - [pat=rep ...] [command]
|
||||
fc -s [pat=rep ...] [command]
|
||||
|
||||
Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
|
||||
*/
|
||||
|
||||
/* Data structure describing a list of global replacements to perform. */
|
||||
typedef struct repl {
|
||||
struct repl *next;
|
||||
char *pat;
|
||||
char *rep;
|
||||
} REPL;
|
||||
|
||||
/* Accessors for HIST_ENTRY lists that are called HLIST. */
|
||||
#define histline(i) (hlist[(i)]->line)
|
||||
#define histdata(i) (hlist[(i)]->data)
|
||||
|
||||
#define FREE_RLIST() \
|
||||
do { \
|
||||
for (rl = rlist; rl; ) { \
|
||||
REPL *r; \
|
||||
r = rl->next; \
|
||||
if (rl->pat) \
|
||||
free (rl->pat); \
|
||||
if (rl->rep) \
|
||||
free (rl->rep); \
|
||||
free (rl); \
|
||||
rl = r; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static char *fc_dosubs __P((char *, REPL *));
|
||||
static char *fc_gethist __P((char *, HIST_ENTRY **));
|
||||
static int fc_gethnum __P((char *, HIST_ENTRY **));
|
||||
static int fc_number __P((WORD_LIST *));
|
||||
static void fc_replhist __P((char *));
|
||||
#ifdef INCLUDE_UNUSED
|
||||
static char *fc_readline __P((FILE *));
|
||||
static void fc_addhist __P((char *));
|
||||
#endif
|
||||
|
||||
/* String to execute on a file that we want to edit. */
|
||||
#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
|
||||
#if defined (STRICT_POSIX)
|
||||
# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-ed}"
|
||||
#else
|
||||
# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}"
|
||||
#endif
|
||||
|
||||
int
|
||||
fc_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
register int i;
|
||||
register char *sep;
|
||||
int numbering, reverse, listing, execute;
|
||||
int histbeg, histend, last_hist, retval, opt, rh;
|
||||
FILE *stream;
|
||||
REPL *rlist, *rl;
|
||||
char *ename, *command, *newcom, *fcedit;
|
||||
HIST_ENTRY **hlist;
|
||||
char *fn;
|
||||
|
||||
numbering = 1;
|
||||
reverse = listing = execute = 0;
|
||||
ename = (char *)NULL;
|
||||
|
||||
/* Parse out the options and set which of the two forms we're in. */
|
||||
reset_internal_getopt ();
|
||||
lcurrent = list; /* XXX */
|
||||
while (fc_number (loptend = lcurrent) == 0 &&
|
||||
(opt = internal_getopt (list, ":e:lnrs")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'n':
|
||||
numbering = 0;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
listing = 1;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
reverse = 1;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
execute = 1;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
ename = list_optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
list = loptend;
|
||||
|
||||
if (ename && (*ename == '-') && (ename[1] == '\0'))
|
||||
execute = 1;
|
||||
|
||||
/* The "execute" form of the command (re-run, with possible string
|
||||
substitutions). */
|
||||
if (execute)
|
||||
{
|
||||
rlist = (REPL *)NULL;
|
||||
while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
|
||||
{
|
||||
*sep++ = '\0';
|
||||
rl = (REPL *)xmalloc (sizeof (REPL));
|
||||
rl->next = (REPL *)NULL;
|
||||
rl->pat = savestring (list->word->word);
|
||||
rl->rep = savestring (sep);
|
||||
|
||||
if (rlist == NULL)
|
||||
rlist = rl;
|
||||
else
|
||||
{
|
||||
rl->next = rlist;
|
||||
rlist = rl;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* If we have a list of substitutions to do, then reverse it
|
||||
to get the replacements in the proper order. */
|
||||
|
||||
rlist = REVERSE_LIST (rlist, REPL *);
|
||||
|
||||
hlist = history_list ();
|
||||
|
||||
/* If we still have something in list, it is a command spec.
|
||||
Otherwise, we use the most recent command in time. */
|
||||
command = fc_gethist (list ? list->word->word : (char *)NULL, hlist);
|
||||
|
||||
if (command == NULL)
|
||||
{
|
||||
builtin_error (_("no command found"));
|
||||
if (rlist)
|
||||
FREE_RLIST ();
|
||||
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (rlist)
|
||||
{
|
||||
newcom = fc_dosubs (command, rlist);
|
||||
free (command);
|
||||
FREE_RLIST ();
|
||||
command = newcom;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s\n", command);
|
||||
fc_replhist (command); /* replace `fc -s' with command */
|
||||
/* Posix says that the re-executed commands should be entered into the
|
||||
history. */
|
||||
return (parse_and_execute (command, "fc", SEVAL_NOHIST));
|
||||
}
|
||||
|
||||
/* This is the second form of the command (the list-or-edit-and-rerun
|
||||
form). */
|
||||
hlist = history_list ();
|
||||
if (hlist == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
for (i = 0; hlist[i]; i++);
|
||||
|
||||
/* With the Bash implementation of history, the current command line
|
||||
("fc blah..." and so on) is already part of the history list by
|
||||
the time we get to this point. This just skips over that command
|
||||
and makes the last command that this deals with be the last command
|
||||
the user entered before the fc. We need to check whether the
|
||||
line was actually added (HISTIGNORE may have caused it to not be),
|
||||
so we check hist_last_line_added. */
|
||||
|
||||
/* Even though command substitution through parse_and_execute turns off
|
||||
remember_on_history, command substitution in a shell when set -o history
|
||||
has been enabled (interactive or not) should use it in the last_hist
|
||||
calculation as if it were on. */
|
||||
rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list);
|
||||
last_hist = i - rh - hist_last_line_added;
|
||||
|
||||
/* XXX */
|
||||
if (i == last_hist && hlist[last_hist] == 0)
|
||||
while (last_hist >= 0 && hlist[last_hist] == 0)
|
||||
last_hist--;
|
||||
if (last_hist < 0)
|
||||
{
|
||||
sh_erange ((char *)NULL, _("history specification"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (list)
|
||||
{
|
||||
histbeg = fc_gethnum (list->word->word, hlist);
|
||||
list = list->next;
|
||||
|
||||
if (list)
|
||||
histend = fc_gethnum (list->word->word, hlist);
|
||||
else
|
||||
histend = listing ? last_hist : histbeg;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The default for listing is the last 16 history items. */
|
||||
if (listing)
|
||||
{
|
||||
histend = last_hist;
|
||||
histbeg = histend - 16 + 1; /* +1 because loop below uses >= */
|
||||
if (histbeg < 0)
|
||||
histbeg = 0;
|
||||
}
|
||||
else
|
||||
/* For editing, it is the last history command. */
|
||||
histbeg = histend = last_hist;
|
||||
}
|
||||
|
||||
/* "When not listing, the fc command that caused the editing shall not be
|
||||
entered into the history list." */
|
||||
if (listing == 0 && hist_last_line_added)
|
||||
{
|
||||
bash_delete_last_history ();
|
||||
/* If we're editing a single command -- the last command in the
|
||||
history -- and we just removed the dummy command added by
|
||||
edit_and_execute_command (), we need to check whether or not we
|
||||
just removed the last command in the history and need to back
|
||||
the pointer up. remember_on_history is off because we're running
|
||||
in parse_and_execute(). */
|
||||
if (histbeg == histend && histend == last_hist && hlist[last_hist] == 0)
|
||||
last_hist = histbeg = --histend;
|
||||
}
|
||||
|
||||
/* We print error messages for line specifications out of range. */
|
||||
if ((histbeg < 0) || (histend < 0))
|
||||
{
|
||||
sh_erange ((char *)NULL, _("history specification"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (histend < histbeg)
|
||||
{
|
||||
i = histend;
|
||||
histend = histbeg;
|
||||
histbeg = i;
|
||||
|
||||
reverse = 1;
|
||||
}
|
||||
|
||||
if (listing)
|
||||
stream = stdout;
|
||||
else
|
||||
{
|
||||
numbering = 0;
|
||||
stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
|
||||
if (stream == 0)
|
||||
{
|
||||
builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno));
|
||||
FREE (fn);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
|
||||
{
|
||||
QUIT;
|
||||
if (numbering)
|
||||
fprintf (stream, "%d", i + history_base);
|
||||
if (listing)
|
||||
{
|
||||
if (posixly_correct)
|
||||
fputs ("\t", stream);
|
||||
else
|
||||
fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
|
||||
}
|
||||
fprintf (stream, "%s\n", histline (i));
|
||||
}
|
||||
|
||||
if (listing)
|
||||
return (sh_chkwrite (EXECUTION_SUCCESS));
|
||||
|
||||
fflush (stream);
|
||||
if (ferror (stream))
|
||||
{
|
||||
sh_wrerror ();
|
||||
fclose (stream);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
fclose (stream);
|
||||
|
||||
/* Now edit the file of commands. */
|
||||
if (ename)
|
||||
{
|
||||
command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
|
||||
sprintf (command, "%s %s", ename, fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND;
|
||||
command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn));
|
||||
sprintf (command, "%s %s", fcedit, fn);
|
||||
}
|
||||
retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
|
||||
if (retval != EXECUTION_SUCCESS)
|
||||
{
|
||||
unlink (fn);
|
||||
free (fn);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* Make sure parse_and_execute doesn't turn this off, even though a
|
||||
call to parse_and_execute farther up the function call stack (e.g.,
|
||||
if this is called by vi_edit_and_execute_command) may have already
|
||||
called bash_history_disable. */
|
||||
remember_on_history = 1;
|
||||
|
||||
/* Turn on the `v' flag while fc_execute_file runs so the commands
|
||||
will be echoed as they are read by the parser. */
|
||||
begin_unwind_frame ("fc builtin");
|
||||
add_unwind_protect ((Function *)xfree, fn);
|
||||
add_unwind_protect (unlink, fn);
|
||||
unwind_protect_int (echo_input_at_read);
|
||||
echo_input_at_read = 1;
|
||||
|
||||
retval = fc_execute_file (fn);
|
||||
|
||||
run_unwind_frame ("fc builtin");
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/* Return 1 if LIST->word->word is a legal number for fc's use. */
|
||||
static int
|
||||
fc_number (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (list == 0)
|
||||
return 0;
|
||||
s = list->word->word;
|
||||
if (*s == '-')
|
||||
s++;
|
||||
return (legal_number (s, (intmax_t *)NULL));
|
||||
}
|
||||
|
||||
/* Return an absolute index into HLIST which corresponds to COMMAND. If
|
||||
COMMAND is a number, then it was specified in relative terms. If it
|
||||
is a string, then it is the start of a command line present in HLIST. */
|
||||
static int
|
||||
fc_gethnum (command, hlist)
|
||||
char *command;
|
||||
HIST_ENTRY **hlist;
|
||||
{
|
||||
int sign, n, clen, rh;
|
||||
register int i, j, last_hist;
|
||||
register char *s;
|
||||
|
||||
sign = 1;
|
||||
/* Count history elements. */
|
||||
for (i = 0; hlist[i]; i++);
|
||||
|
||||
/* With the Bash implementation of history, the current command line
|
||||
("fc blah..." and so on) is already part of the history list by
|
||||
the time we get to this point. This just skips over that command
|
||||
and makes the last command that this deals with be the last command
|
||||
the user entered before the fc. We need to check whether the
|
||||
line was actually added (HISTIGNORE may have caused it to not be),
|
||||
so we check hist_last_line_added. This needs to agree with the
|
||||
calculation of last_hist in fc_builtin above. */
|
||||
/* Even though command substitution through parse_and_execute turns off
|
||||
remember_on_history, command substitution in a shell when set -o history
|
||||
has been enabled (interactive or not) should use it in the last_hist
|
||||
calculation as if it were on. */
|
||||
rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list);
|
||||
last_hist = i - rh - hist_last_line_added;
|
||||
|
||||
if (i == last_hist && hlist[last_hist] == 0)
|
||||
while (last_hist >= 0 && hlist[last_hist] == 0)
|
||||
last_hist--;
|
||||
if (last_hist < 0)
|
||||
return (-1);
|
||||
|
||||
i = last_hist;
|
||||
|
||||
/* No specification defaults to most recent command. */
|
||||
if (command == NULL)
|
||||
return (i);
|
||||
|
||||
/* Otherwise, there is a specification. It can be a number relative to
|
||||
the current position, or an absolute history number. */
|
||||
s = command;
|
||||
|
||||
/* Handle possible leading minus sign. */
|
||||
if (s && (*s == '-'))
|
||||
{
|
||||
sign = -1;
|
||||
s++;
|
||||
}
|
||||
|
||||
if (s && DIGIT(*s))
|
||||
{
|
||||
n = atoi (s);
|
||||
n *= sign;
|
||||
|
||||
/* If the value is negative or zero, then it is an offset from
|
||||
the current history item. */
|
||||
if (n < 0)
|
||||
{
|
||||
n += i + 1;
|
||||
return (n < 0 ? 0 : n);
|
||||
}
|
||||
else if (n == 0)
|
||||
return (i);
|
||||
else
|
||||
{
|
||||
n -= history_base;
|
||||
return (i < n ? i : n);
|
||||
}
|
||||
}
|
||||
|
||||
clen = strlen (command);
|
||||
for (j = i; j >= 0; j--)
|
||||
{
|
||||
if (STREQN (command, histline (j), clen))
|
||||
return (j);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Locate the most recent history line which begins with
|
||||
COMMAND in HLIST, and return a malloc()'ed copy of it. */
|
||||
static char *
|
||||
fc_gethist (command, hlist)
|
||||
char *command;
|
||||
HIST_ENTRY **hlist;
|
||||
{
|
||||
int i;
|
||||
|
||||
if (hlist == 0)
|
||||
return ((char *)NULL);
|
||||
|
||||
i = fc_gethnum (command, hlist);
|
||||
|
||||
if (i >= 0)
|
||||
return (savestring (histline (i)));
|
||||
else
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
/* Read the edited history lines from STREAM and return them
|
||||
one at a time. This can read unlimited length lines. The
|
||||
caller should free the storage. */
|
||||
static char *
|
||||
fc_readline (stream)
|
||||
FILE *stream;
|
||||
{
|
||||
register int c;
|
||||
int line_len = 0, lindex = 0;
|
||||
char *line = (char *)NULL;
|
||||
|
||||
while ((c = getc (stream)) != EOF)
|
||||
{
|
||||
if ((lindex + 2) >= line_len)
|
||||
line = (char *)xrealloc (line, (line_len += 128));
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
line[lindex++] = '\n';
|
||||
line[lindex++] = '\0';
|
||||
return (line);
|
||||
}
|
||||
else
|
||||
line[lindex++] = c;
|
||||
}
|
||||
|
||||
if (!lindex)
|
||||
{
|
||||
if (line)
|
||||
free (line);
|
||||
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
if (lindex + 2 >= line_len)
|
||||
line = (char *)xrealloc (line, lindex + 3);
|
||||
|
||||
line[lindex++] = '\n'; /* Finish with newline if none in file */
|
||||
line[lindex++] = '\0';
|
||||
return (line);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Perform the SUBS on COMMAND.
|
||||
SUBS is a list of substitutions, and COMMAND is a simple string.
|
||||
Return a pointer to a malloc'ed string which contains the substituted
|
||||
command. */
|
||||
static char *
|
||||
fc_dosubs (command, subs)
|
||||
char *command;
|
||||
REPL *subs;
|
||||
{
|
||||
register char *new, *t;
|
||||
register REPL *r;
|
||||
|
||||
for (new = savestring (command), r = subs; r; r = r->next)
|
||||
{
|
||||
t = strsub (new, r->pat, r->rep, 1);
|
||||
free (new);
|
||||
new = t;
|
||||
}
|
||||
return (new);
|
||||
}
|
||||
|
||||
/* Use `command' to replace the last entry in the history list, which,
|
||||
by this time, is `fc blah...'. The intent is that the new command
|
||||
become the history entry, and that `fc' should never appear in the
|
||||
history list. This way you can do `r' to your heart's content. */
|
||||
static void
|
||||
fc_replhist (command)
|
||||
char *command;
|
||||
{
|
||||
int n;
|
||||
|
||||
if (command == 0 || *command == '\0')
|
||||
return;
|
||||
|
||||
n = strlen (command);
|
||||
if (command[n - 1] == '\n')
|
||||
command[n - 1] = '\0';
|
||||
|
||||
if (command && *command)
|
||||
{
|
||||
bash_delete_last_history ();
|
||||
maybe_add_history (command); /* Obeys HISTCONTROL setting. */
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
/* Add LINE to the history, after removing a single trailing newline. */
|
||||
static void
|
||||
fc_addhist (line)
|
||||
char *line;
|
||||
{
|
||||
register int n;
|
||||
|
||||
if (line == 0 || *line == 0)
|
||||
return;
|
||||
|
||||
n = strlen (line);
|
||||
|
||||
if (line[n - 1] == '\n')
|
||||
line[n - 1] = '\0';
|
||||
|
||||
if (line && *line)
|
||||
maybe_add_history (line); /* Obeys HISTCONTROL setting. */
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HISTORY */
|
||||
+1
-1
@@ -115,7 +115,7 @@ $END
|
||||
|
||||
#if defined (PUSHD_AND_POPD)
|
||||
#include <stdio.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,778 @@
|
||||
This file is pushd.def, from which is created pushd.c. It implements the
|
||||
builtins "pushd", "popd", and "dirs" in Bash.
|
||||
|
||||
Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES pushd.c
|
||||
|
||||
$BUILTIN pushd
|
||||
$FUNCTION pushd_builtin
|
||||
$DEPENDS_ON PUSHD_AND_POPD
|
||||
$SHORT_DOC pushd [-n] [+N | -N | dir]
|
||||
Add directories to stack.
|
||||
|
||||
Adds a directory to the top of the directory stack, or rotates
|
||||
the stack, making the new top of the stack the current working
|
||||
directory. With no arguments, exchanges the top two directories.
|
||||
|
||||
Options:
|
||||
-n Suppresses the normal change of directory when adding
|
||||
directories to the stack, so only the stack is manipulated.
|
||||
|
||||
Arguments:
|
||||
+N Rotates the stack so that the Nth directory (counting
|
||||
from the left of the list shown by `dirs', starting with
|
||||
zero) is at the top.
|
||||
|
||||
-N Rotates the stack so that the Nth directory (counting
|
||||
from the right of the list shown by `dirs', starting with
|
||||
zero) is at the top.
|
||||
|
||||
dir Adds DIR to the directory stack at the top, making it the
|
||||
new current working directory.
|
||||
|
||||
The `dirs' builtin displays the directory stack.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid argument is supplied or the directory
|
||||
change fails.
|
||||
$END
|
||||
|
||||
$BUILTIN popd
|
||||
$FUNCTION popd_builtin
|
||||
$DEPENDS_ON PUSHD_AND_POPD
|
||||
$SHORT_DOC popd [-n] [+N | -N]
|
||||
Remove directories from stack.
|
||||
|
||||
Removes entries from the directory stack. With no arguments, removes
|
||||
the top directory from the stack, and changes to the new top directory.
|
||||
|
||||
Options:
|
||||
-n Suppresses the normal change of directory when removing
|
||||
directories from the stack, so only the stack is manipulated.
|
||||
|
||||
Arguments:
|
||||
+N Removes the Nth entry counting from the left of the list
|
||||
shown by `dirs', starting with zero. For example: `popd +0'
|
||||
removes the first directory, `popd +1' the second.
|
||||
|
||||
-N Removes the Nth entry counting from the right of the list
|
||||
shown by `dirs', starting with zero. For example: `popd -0'
|
||||
removes the last directory, `popd -1' the next to last.
|
||||
|
||||
The `dirs' builtin displays the directory stack.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid argument is supplied or the directory
|
||||
change fails.
|
||||
$END
|
||||
|
||||
$BUILTIN dirs
|
||||
$FUNCTION dirs_builtin
|
||||
$DEPENDS_ON PUSHD_AND_POPD
|
||||
$SHORT_DOC dirs [-clpv] [+N] [-N]
|
||||
Display directory stack.
|
||||
|
||||
Display the list of currently remembered directories. Directories
|
||||
find their way onto the list with the `pushd' command; you can get
|
||||
back up through the list with the `popd' command.
|
||||
|
||||
Options:
|
||||
-c clear the directory stack by deleting all of the elements
|
||||
-l do not print tilde-prefixed versions of directories relative
|
||||
to your home directory
|
||||
-p print the directory stack with one entry per line
|
||||
-v print the directory stack with one entry per line prefixed
|
||||
with its position in the stack
|
||||
|
||||
Arguments:
|
||||
+N Displays the Nth entry counting from the left of the list shown by
|
||||
dirs when invoked without options, starting with zero.
|
||||
|
||||
-N Displays the Nth entry counting from the right of the list shown by
|
||||
dirs when invoked without options, starting with zero.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied or an error occurs.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (PUSHD_AND_POPD)
|
||||
#include <stdio.h>
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <tilde/tilde.h>
|
||||
|
||||
#include "../shell.h"
|
||||
#include "maxpath.h"
|
||||
#include "common.h"
|
||||
#include "builtext.h"
|
||||
|
||||
#ifdef LOADABLE_BUILTIN
|
||||
# include "builtins.h"
|
||||
#endif
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
/* The list of remembered directories. */
|
||||
static char **pushd_directory_list = (char **)NULL;
|
||||
|
||||
/* Number of existing slots in this list. */
|
||||
static int directory_list_size;
|
||||
|
||||
/* Offset to the end of the list. */
|
||||
static int directory_list_offset;
|
||||
|
||||
static void pushd_error __P((int, char *));
|
||||
static void clear_directory_stack __P((void));
|
||||
static int cd_to_string __P((char *));
|
||||
static int change_to_temp __P((char *));
|
||||
static void add_dirstack_element __P((char *));
|
||||
static int get_dirstack_index __P((intmax_t, int, int *));
|
||||
|
||||
#define NOCD 0x01
|
||||
#define ROTATE 0x02
|
||||
#define LONGFORM 0x04
|
||||
#define CLEARSTAK 0x08
|
||||
|
||||
int
|
||||
pushd_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
WORD_LIST *orig_list;
|
||||
char *temp, *current_directory, *top;
|
||||
int j, flags, skipopt;
|
||||
intmax_t num;
|
||||
char direction;
|
||||
|
||||
orig_list = list;
|
||||
if (list && list->word && ISOPTION (list->word->word, '-'))
|
||||
{
|
||||
list = list->next;
|
||||
skipopt = 1;
|
||||
}
|
||||
else
|
||||
skipopt = 0;
|
||||
|
||||
/* If there is no argument list then switch current and
|
||||
top of list. */
|
||||
if (list == 0)
|
||||
{
|
||||
if (directory_list_offset == 0)
|
||||
{
|
||||
builtin_error (_("no other directory"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
current_directory = get_working_directory ("pushd");
|
||||
if (current_directory == 0)
|
||||
return (EXECUTION_FAILURE);
|
||||
|
||||
j = directory_list_offset - 1;
|
||||
temp = pushd_directory_list[j];
|
||||
pushd_directory_list[j] = current_directory;
|
||||
j = change_to_temp (temp);
|
||||
free (temp);
|
||||
return j;
|
||||
}
|
||||
|
||||
for (flags = 0; skipopt == 0 && list; list = list->next)
|
||||
{
|
||||
if (ISOPTION (list->word->word, 'n'))
|
||||
{
|
||||
flags |= NOCD;
|
||||
}
|
||||
else if (ISOPTION (list->word->word, '-'))
|
||||
{
|
||||
list = list->next;
|
||||
break;
|
||||
}
|
||||
else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
|
||||
/* Let `pushd -' work like it used to. */
|
||||
break;
|
||||
else if (((direction = list->word->word[0]) == '+') || direction == '-')
|
||||
{
|
||||
if (legal_number (list->word->word + 1, &num) == 0)
|
||||
{
|
||||
sh_invalidnum (list->word->word);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (direction == '-')
|
||||
num = directory_list_offset - num;
|
||||
|
||||
if (num > directory_list_offset || num < 0)
|
||||
{
|
||||
pushd_error (directory_list_offset, list->word->word);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
flags |= ROTATE;
|
||||
}
|
||||
else if (*list->word->word == '-')
|
||||
{
|
||||
sh_invalidopt (list->word->word);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & ROTATE)
|
||||
{
|
||||
/* Rotate the stack num times. Remember, the current
|
||||
directory acts like it is part of the stack. */
|
||||
temp = get_working_directory ("pushd");
|
||||
|
||||
if (num == 0)
|
||||
{
|
||||
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
|
||||
free (temp);
|
||||
return j;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
top = pushd_directory_list[directory_list_offset - 1];
|
||||
|
||||
for (j = directory_list_offset - 2; j > -1; j--)
|
||||
pushd_directory_list[j + 1] = pushd_directory_list[j];
|
||||
|
||||
pushd_directory_list[j + 1] = temp;
|
||||
|
||||
temp = top;
|
||||
num--;
|
||||
}
|
||||
while (num);
|
||||
|
||||
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
|
||||
free (temp);
|
||||
return j;
|
||||
}
|
||||
|
||||
if (list == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
/* Change to the directory in list->word->word. Save the current
|
||||
directory on the top of the stack. */
|
||||
current_directory = get_working_directory ("pushd");
|
||||
if (current_directory == 0)
|
||||
return (EXECUTION_FAILURE);
|
||||
|
||||
j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
|
||||
if (j == EXECUTION_SUCCESS)
|
||||
{
|
||||
add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
|
||||
dirs_builtin ((WORD_LIST *)NULL);
|
||||
if (flags & NOCD)
|
||||
free (current_directory);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
free (current_directory);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop the directory stack, and then change to the new top of the stack.
|
||||
If LIST is non-null it should consist of a word +N or -N, which says
|
||||
what element to delete from the stack. The default is the top one. */
|
||||
int
|
||||
popd_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
register int i;
|
||||
intmax_t which;
|
||||
int flags;
|
||||
char direction;
|
||||
char *which_word;
|
||||
|
||||
which_word = (char *)NULL;
|
||||
for (flags = 0, which = 0, direction = '+'; list; list = list->next)
|
||||
{
|
||||
if (ISOPTION (list->word->word, 'n'))
|
||||
{
|
||||
flags |= NOCD;
|
||||
}
|
||||
else if (ISOPTION (list->word->word, '-'))
|
||||
{
|
||||
list = list->next;
|
||||
break;
|
||||
}
|
||||
else if (((direction = list->word->word[0]) == '+') || direction == '-')
|
||||
{
|
||||
if (legal_number (list->word->word + 1, &which) == 0)
|
||||
{
|
||||
sh_invalidnum (list->word->word);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
which_word = list->word->word;
|
||||
}
|
||||
else if (*list->word->word == '-')
|
||||
{
|
||||
sh_invalidopt (list->word->word);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
|
||||
{
|
||||
pushd_error (directory_list_offset, which_word ? which_word : "");
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* Handle case of no specification, or top of stack specification. */
|
||||
if ((direction == '+' && which == 0) ||
|
||||
(direction == '-' && which == directory_list_offset))
|
||||
{
|
||||
i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
|
||||
: EXECUTION_SUCCESS;
|
||||
if (i != EXECUTION_SUCCESS)
|
||||
return (i);
|
||||
free (pushd_directory_list[--directory_list_offset]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Since an offset other than the top directory was specified,
|
||||
remove that directory from the list and shift the remainder
|
||||
of the list into place. */
|
||||
i = (direction == '+') ? directory_list_offset - which : which;
|
||||
free (pushd_directory_list[i]);
|
||||
directory_list_offset--;
|
||||
|
||||
/* Shift the remainder of the list into place. */
|
||||
for (; i < directory_list_offset; i++)
|
||||
pushd_directory_list[i] = pushd_directory_list[i + 1];
|
||||
}
|
||||
|
||||
dirs_builtin ((WORD_LIST *)NULL);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
/* Print the current list of directories on the directory stack. */
|
||||
int
|
||||
dirs_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int flags, desired_index, index_flag, vflag;
|
||||
intmax_t i;
|
||||
char *temp, *w;
|
||||
|
||||
for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
|
||||
{
|
||||
if (ISOPTION (list->word->word, 'l'))
|
||||
{
|
||||
flags |= LONGFORM;
|
||||
}
|
||||
else if (ISOPTION (list->word->word, 'c'))
|
||||
{
|
||||
flags |= CLEARSTAK;
|
||||
}
|
||||
else if (ISOPTION (list->word->word, 'v'))
|
||||
{
|
||||
vflag |= 2;
|
||||
}
|
||||
else if (ISOPTION (list->word->word, 'p'))
|
||||
{
|
||||
vflag |= 1;
|
||||
}
|
||||
else if (ISOPTION (list->word->word, '-'))
|
||||
{
|
||||
list = list->next;
|
||||
break;
|
||||
}
|
||||
else if (*list->word->word == '+' || *list->word->word == '-')
|
||||
{
|
||||
int sign;
|
||||
if (legal_number (w = list->word->word + 1, &i) == 0)
|
||||
{
|
||||
sh_invalidnum (list->word->word);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
sign = (*list->word->word == '+') ? 1 : -1;
|
||||
desired_index = get_dirstack_index (i, sign, &index_flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_invalidopt (list->word->word);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & CLEARSTAK)
|
||||
{
|
||||
clear_directory_stack ();
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
|
||||
{
|
||||
pushd_error (directory_list_offset, w);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
#define DIRSTACK_FORMAT(temp) \
|
||||
(flags & LONGFORM) ? temp : polite_directory_format (temp)
|
||||
|
||||
/* The first directory printed is always the current working directory. */
|
||||
if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
|
||||
{
|
||||
temp = get_working_directory ("dirs");
|
||||
if (temp == 0)
|
||||
temp = savestring (_("<no current directory>"));
|
||||
if (vflag & 2)
|
||||
printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
|
||||
else
|
||||
printf ("%s", DIRSTACK_FORMAT (temp));
|
||||
free (temp);
|
||||
if (index_flag)
|
||||
{
|
||||
putchar ('\n');
|
||||
return (sh_chkwrite (EXECUTION_SUCCESS));
|
||||
}
|
||||
}
|
||||
|
||||
#define DIRSTACK_ENTRY(i) \
|
||||
(flags & LONGFORM) ? pushd_directory_list[i] \
|
||||
: polite_directory_format (pushd_directory_list[i])
|
||||
|
||||
/* Now print the requested directory stack entries. */
|
||||
if (index_flag)
|
||||
{
|
||||
if (vflag & 2)
|
||||
printf ("%2d %s", directory_list_offset - desired_index,
|
||||
DIRSTACK_ENTRY (desired_index));
|
||||
else
|
||||
printf ("%s", DIRSTACK_ENTRY (desired_index));
|
||||
}
|
||||
else
|
||||
for (i = directory_list_offset - 1; i >= 0; i--)
|
||||
if (vflag >= 2)
|
||||
printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
|
||||
else
|
||||
printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
|
||||
|
||||
putchar ('\n');
|
||||
|
||||
return (sh_chkwrite (EXECUTION_SUCCESS));
|
||||
}
|
||||
|
||||
static void
|
||||
pushd_error (offset, arg)
|
||||
int offset;
|
||||
char *arg;
|
||||
{
|
||||
if (offset == 0)
|
||||
builtin_error (_("directory stack empty"));
|
||||
else
|
||||
sh_erange (arg, _("directory stack index"));
|
||||
}
|
||||
|
||||
static void
|
||||
clear_directory_stack ()
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < directory_list_offset; i++)
|
||||
free (pushd_directory_list[i]);
|
||||
directory_list_offset = 0;
|
||||
}
|
||||
|
||||
/* Switch to the directory in NAME. This uses the cd_builtin to do the work,
|
||||
so if the result is EXECUTION_FAILURE then an error message has already
|
||||
been printed. */
|
||||
static int
|
||||
cd_to_string (name)
|
||||
char *name;
|
||||
{
|
||||
WORD_LIST *tlist;
|
||||
WORD_LIST *dir;
|
||||
int result;
|
||||
|
||||
dir = make_word_list (make_word (name), NULL);
|
||||
tlist = make_word_list (make_word ("--"), dir);
|
||||
result = cd_builtin (tlist);
|
||||
dispose_words (tlist);
|
||||
return (result);
|
||||
}
|
||||
|
||||
static int
|
||||
change_to_temp (temp)
|
||||
char *temp;
|
||||
{
|
||||
int tt;
|
||||
|
||||
tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
|
||||
|
||||
if (tt == EXECUTION_SUCCESS)
|
||||
dirs_builtin ((WORD_LIST *)NULL);
|
||||
|
||||
return (tt);
|
||||
}
|
||||
|
||||
static void
|
||||
add_dirstack_element (dir)
|
||||
char *dir;
|
||||
{
|
||||
if (directory_list_offset == directory_list_size)
|
||||
pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
|
||||
pushd_directory_list[directory_list_offset++] = dir;
|
||||
}
|
||||
|
||||
static int
|
||||
get_dirstack_index (ind, sign, indexp)
|
||||
intmax_t ind;
|
||||
int sign, *indexp;
|
||||
{
|
||||
if (indexp)
|
||||
*indexp = sign > 0 ? 1 : 2;
|
||||
|
||||
/* dirs +0 prints the current working directory. */
|
||||
/* dirs -0 prints last element in directory stack */
|
||||
if (ind == 0 && sign > 0)
|
||||
return 0;
|
||||
else if (ind == directory_list_offset)
|
||||
{
|
||||
if (indexp)
|
||||
*indexp = sign > 0 ? 2 : 1;
|
||||
return 0;
|
||||
}
|
||||
else if (ind >= 0 && ind <= directory_list_offset)
|
||||
return (sign > 0 ? directory_list_offset - ind : ind);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Used by the tilde expansion code. */
|
||||
char *
|
||||
get_dirstack_from_string (string)
|
||||
char *string;
|
||||
{
|
||||
int ind, sign, index_flag;
|
||||
intmax_t i;
|
||||
|
||||
sign = 1;
|
||||
if (*string == '-' || *string == '+')
|
||||
{
|
||||
sign = (*string == '-') ? -1 : 1;
|
||||
string++;
|
||||
}
|
||||
if (legal_number (string, &i) == 0)
|
||||
return ((char *)NULL);
|
||||
|
||||
index_flag = 0;
|
||||
ind = get_dirstack_index (i, sign, &index_flag);
|
||||
if (index_flag && (ind < 0 || ind > directory_list_offset))
|
||||
return ((char *)NULL);
|
||||
if (index_flag == 0 || (index_flag == 1 && ind == 0))
|
||||
return (get_string_value ("PWD"));
|
||||
else
|
||||
return (pushd_directory_list[ind]);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
char *
|
||||
get_dirstack_element (ind, sign)
|
||||
intmax_t ind;
|
||||
int sign;
|
||||
{
|
||||
int i;
|
||||
|
||||
i = get_dirstack_index (ind, sign, (int *)NULL);
|
||||
return (i < 0 || i > directory_list_offset) ? (char *)NULL
|
||||
: pushd_directory_list[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
set_dirstack_element (ind, sign, value)
|
||||
intmax_t ind;
|
||||
int sign;
|
||||
char *value;
|
||||
{
|
||||
int i;
|
||||
|
||||
i = get_dirstack_index (ind, sign, (int *)NULL);
|
||||
if (ind == 0 || i < 0 || i > directory_list_offset)
|
||||
return;
|
||||
free (pushd_directory_list[i]);
|
||||
pushd_directory_list[i] = savestring (value);
|
||||
}
|
||||
|
||||
WORD_LIST *
|
||||
get_directory_stack (flags)
|
||||
int flags;
|
||||
{
|
||||
register int i;
|
||||
WORD_LIST *ret;
|
||||
char *d, *t;
|
||||
|
||||
for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
|
||||
{
|
||||
d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
|
||||
: pushd_directory_list[i];
|
||||
ret = make_word_list (make_word (d), ret);
|
||||
}
|
||||
/* Now the current directory. */
|
||||
d = get_working_directory ("dirstack");
|
||||
i = 0; /* sentinel to decide whether or not to free d */
|
||||
if (d == 0)
|
||||
d = ".";
|
||||
else
|
||||
{
|
||||
t = polite_directory_format (d);
|
||||
/* polite_directory_format sometimes returns its argument unchanged.
|
||||
If it does not, we can free d right away. If it does, we need to
|
||||
mark d to be deleted later. */
|
||||
if (t != d)
|
||||
{
|
||||
free (d);
|
||||
d = t;
|
||||
}
|
||||
else /* t == d, so d is what we want */
|
||||
i = 1;
|
||||
}
|
||||
ret = make_word_list (make_word (d), ret);
|
||||
if (i)
|
||||
free (d);
|
||||
return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
|
||||
}
|
||||
|
||||
#ifdef LOADABLE_BUILTIN
|
||||
char * const dirs_doc[] = {
|
||||
N_("Display the list of currently remembered directories. Directories\n\
|
||||
find their way onto the list with the `pushd' command; you can get\n\
|
||||
back up through the list with the `popd' command.\n\
|
||||
\n\
|
||||
Options:\n\
|
||||
-c clear the directory stack by deleting all of the elements\n\
|
||||
-l do not print tilde-prefixed versions of directories relative\n\
|
||||
to your home directory\n\
|
||||
-p print the directory stack with one entry per line\n\
|
||||
-v print the directory stack with one entry per line prefixed\n\
|
||||
with its position in the stack\n\
|
||||
\n\
|
||||
Arguments:\n\
|
||||
+N Displays the Nth entry counting from the left of the list shown by\n\
|
||||
dirs when invoked without options, starting with zero.\n\
|
||||
\n\
|
||||
-N Displays the Nth entry counting from the right of the list shown by\n\
|
||||
dirs when invoked without options, starting with zero."),
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
char * const pushd_doc[] = {
|
||||
N_("Adds a directory to the top of the directory stack, or rotates\n\
|
||||
the stack, making the new top of the stack the current working\n\
|
||||
directory. With no arguments, exchanges the top two directories.\n\
|
||||
\n\
|
||||
Options:\n\
|
||||
-n Suppresses the normal change of directory when adding\n\
|
||||
directories to the stack, so only the stack is manipulated.\n\
|
||||
\n\
|
||||
Arguments:\n\
|
||||
+N Rotates the stack so that the Nth directory (counting\n\
|
||||
from the left of the list shown by `dirs', starting with\n\
|
||||
zero) is at the top.\n\
|
||||
\n\
|
||||
-N Rotates the stack so that the Nth directory (counting\n\
|
||||
from the right of the list shown by `dirs', starting with\n\
|
||||
zero) is at the top.\n\
|
||||
\n\
|
||||
dir Adds DIR to the directory stack at the top, making it the\n\
|
||||
new current working directory.\n\
|
||||
\n\
|
||||
The `dirs' builtin displays the directory stack."),
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
char * const popd_doc[] = {
|
||||
N_("Removes entries from the directory stack. With no arguments, removes\n\
|
||||
the top directory from the stack, and changes to the new top directory.\n\
|
||||
\n\
|
||||
Options:\n\
|
||||
-n Suppresses the normal change of directory when removing\n\
|
||||
directories from the stack, so only the stack is manipulated.\n\
|
||||
\n\
|
||||
Arguments:\n\
|
||||
+N Removes the Nth entry counting from the left of the list\n\
|
||||
shown by `dirs', starting with zero. For example: `popd +0'\n\
|
||||
removes the first directory, `popd +1' the second.\n\
|
||||
\n\
|
||||
-N Removes the Nth entry counting from the right of the list\n\
|
||||
shown by `dirs', starting with zero. For example: `popd -0'\n\
|
||||
removes the last directory, `popd -1' the next to last.\n\
|
||||
\n\
|
||||
The `dirs' builtin displays the directory stack."),
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin pushd_struct = {
|
||||
"pushd",
|
||||
pushd_builtin,
|
||||
BUILTIN_ENABLED,
|
||||
pushd_doc,
|
||||
"pushd [+N | -N] [-n] [dir]",
|
||||
0
|
||||
};
|
||||
|
||||
struct builtin popd_struct = {
|
||||
"popd",
|
||||
popd_builtin,
|
||||
BUILTIN_ENABLED,
|
||||
popd_doc,
|
||||
"popd [+N | -N] [-n]",
|
||||
0
|
||||
};
|
||||
|
||||
struct builtin dirs_struct = {
|
||||
"dirs",
|
||||
dirs_builtin,
|
||||
BUILTIN_ENABLED,
|
||||
dirs_doc,
|
||||
"dirs [-clpv] [+N] [-N]",
|
||||
0
|
||||
};
|
||||
#endif /* LOADABLE_BUILTIN */
|
||||
|
||||
#endif /* PUSHD_AND_POPD */
|
||||
+1
-1
@@ -73,7 +73,7 @@ $END
|
||||
#include <config.h>
|
||||
|
||||
#include "../bashtypes.h"
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,779 @@
|
||||
This file is ulimit.def, from which is created ulimit.c.
|
||||
It implements the builtin "ulimit" in Bash.
|
||||
|
||||
Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES ulimit.c
|
||||
|
||||
$BUILTIN ulimit
|
||||
$FUNCTION ulimit_builtin
|
||||
$DEPENDS_ON !_MINIX
|
||||
$SHORT_DOC ulimit [-SHabcdefilmnpqrstuvxT] [limit]
|
||||
Modify shell resource limits.
|
||||
|
||||
Provides control over the resources available to the shell and processes
|
||||
it creates, on systems that allow such control.
|
||||
|
||||
Options:
|
||||
-S use the `soft' resource limit
|
||||
-H use the `hard' resource limit
|
||||
-a all current limits are reported
|
||||
-b the socket buffer size
|
||||
-c the maximum size of core files created
|
||||
-d the maximum size of a process's data segment
|
||||
-e the maximum scheduling priority (`nice')
|
||||
-f the maximum size of files written by the shell and its children
|
||||
-i the maximum number of pending signals
|
||||
-l the maximum size a process may lock into memory
|
||||
-m the maximum resident set size
|
||||
-n the maximum number of open file descriptors
|
||||
-p the pipe buffer size
|
||||
-q the maximum number of bytes in POSIX message queues
|
||||
-r the maximum real-time scheduling priority
|
||||
-s the maximum stack size
|
||||
-t the maximum amount of cpu time in seconds
|
||||
-u the maximum number of user processes
|
||||
-v the size of virtual memory
|
||||
-x the maximum number of file locks
|
||||
-T the maximum number of threads
|
||||
|
||||
Not all options are available on all platforms.
|
||||
|
||||
If LIMIT is given, it is the new value of the specified resource; the
|
||||
special LIMIT values `soft', `hard', and `unlimited' stand for the
|
||||
current soft limit, the current hard limit, and no limit, respectively.
|
||||
Otherwise, the current value of the specified resource is printed. If
|
||||
no option is given, then -f is assumed.
|
||||
|
||||
Values are in 1024-byte increments, except for -t, which is in seconds,
|
||||
-p, which is in increments of 512 bytes, and -u, which is an unscaled
|
||||
number of processes.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied or an error occurs.
|
||||
$END
|
||||
|
||||
#if !defined (_MINIX)
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "../bashtypes.h"
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
#include "pipesize.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
/* For some reason, HPUX chose to make these definitions visible only if
|
||||
_KERNEL is defined, so we define _KERNEL before including <sys/resource.h>
|
||||
and #undef it afterward. */
|
||||
#if defined (HAVE_RESOURCE)
|
||||
# include <sys/time.h>
|
||||
# if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
|
||||
# define _KERNEL
|
||||
# endif
|
||||
# include <sys/resource.h>
|
||||
# if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
|
||||
# undef _KERNEL
|
||||
# endif
|
||||
#elif defined (HAVE_SYS_TIMES_H)
|
||||
# include <sys/times.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LIMITS_H)
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
/* Check for the most basic symbols. If they aren't present, this
|
||||
system's <sys/resource.h> isn't very useful to us. */
|
||||
#if !defined (RLIMIT_FSIZE) || !defined (HAVE_GETRLIMIT)
|
||||
# undef HAVE_RESOURCE
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_RESOURCE) && defined (HAVE_ULIMIT_H)
|
||||
# include <ulimit.h>
|
||||
#endif
|
||||
|
||||
#if !defined (RLIMTYPE)
|
||||
# define RLIMTYPE long
|
||||
# define string_to_rlimtype(s) strtol(s, (char **)NULL, 10)
|
||||
# define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "")
|
||||
#endif
|
||||
|
||||
/* Some systems use RLIMIT_NOFILE, others use RLIMIT_OFILE */
|
||||
#if defined (HAVE_RESOURCE) && defined (RLIMIT_OFILE) && !defined (RLIMIT_NOFILE)
|
||||
# define RLIMIT_NOFILE RLIMIT_OFILE
|
||||
#endif /* HAVE_RESOURCE && RLIMIT_OFILE && !RLIMIT_NOFILE */
|
||||
|
||||
/* Some systems have these, some do not. */
|
||||
#ifdef RLIMIT_FSIZE
|
||||
# define RLIMIT_FILESIZE RLIMIT_FSIZE
|
||||
#else
|
||||
# define RLIMIT_FILESIZE 256
|
||||
#endif
|
||||
|
||||
#define RLIMIT_PIPESIZE 257
|
||||
|
||||
#ifdef RLIMIT_NOFILE
|
||||
# define RLIMIT_OPENFILES RLIMIT_NOFILE
|
||||
#else
|
||||
# define RLIMIT_OPENFILES 258
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_VMEM
|
||||
# define RLIMIT_VIRTMEM RLIMIT_VMEM
|
||||
# define RLIMIT_VMBLKSZ 1024
|
||||
#else
|
||||
# ifdef RLIMIT_AS
|
||||
# define RLIMIT_VIRTMEM RLIMIT_AS
|
||||
# define RLIMIT_VMBLKSZ 1024
|
||||
# else
|
||||
# define RLIMIT_VIRTMEM 259
|
||||
# define RLIMIT_VMBLKSZ 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_NPROC
|
||||
# define RLIMIT_MAXUPROC RLIMIT_NPROC
|
||||
#else
|
||||
# define RLIMIT_MAXUPROC 260
|
||||
#endif
|
||||
|
||||
#if !defined (RLIM_INFINITY)
|
||||
# define RLIM_INFINITY 0x7fffffff
|
||||
#endif
|
||||
|
||||
#if !defined (RLIM_SAVED_CUR)
|
||||
# define RLIM_SAVED_CUR RLIM_INFINITY
|
||||
#endif
|
||||
|
||||
#if !defined (RLIM_SAVED_MAX)
|
||||
# define RLIM_SAVED_MAX RLIM_INFINITY
|
||||
#endif
|
||||
|
||||
#define LIMIT_HARD 0x01
|
||||
#define LIMIT_SOFT 0x02
|
||||
|
||||
/* "Blocks" are defined as 512 bytes when in Posix mode and 1024 bytes
|
||||
otherwise. */
|
||||
#define POSIXBLK -2
|
||||
|
||||
#define BLOCKSIZE(x) (((x) == POSIXBLK) ? (posixly_correct ? 512 : 1024) : (x))
|
||||
|
||||
extern int posixly_correct;
|
||||
|
||||
static int _findlim __P((int));
|
||||
|
||||
static int ulimit_internal __P((int, char *, int, int));
|
||||
|
||||
static int get_limit __P((int, RLIMTYPE *, RLIMTYPE *));
|
||||
static int set_limit __P((int, RLIMTYPE, int));
|
||||
|
||||
static void printone __P((int, RLIMTYPE, int));
|
||||
static void print_all_limits __P((int));
|
||||
|
||||
static int set_all_limits __P((int, RLIMTYPE));
|
||||
|
||||
static int filesize __P((RLIMTYPE *));
|
||||
static int pipesize __P((RLIMTYPE *));
|
||||
static int getmaxuprc __P((RLIMTYPE *));
|
||||
static int getmaxvm __P((RLIMTYPE *, RLIMTYPE *));
|
||||
|
||||
typedef struct {
|
||||
int option; /* The ulimit option for this limit. */
|
||||
int parameter; /* Parameter to pass to get_limit (). */
|
||||
int block_factor; /* Blocking factor for specific limit. */
|
||||
const char * const description; /* Descriptive string to output. */
|
||||
const char * const units; /* scale */
|
||||
} RESOURCE_LIMITS;
|
||||
|
||||
static RESOURCE_LIMITS limits[] = {
|
||||
#ifdef RLIMIT_PTHREAD
|
||||
{ 'T', RLIMIT_PTHREAD, 1, "number of threads", (char *)NULL },
|
||||
#endif
|
||||
#ifdef RLIMIT_SBSIZE
|
||||
{ 'b', RLIMIT_SBSIZE, 1, "socket buffer size", "bytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_CORE
|
||||
{ 'c', RLIMIT_CORE, POSIXBLK, "core file size", "blocks" },
|
||||
#endif
|
||||
#ifdef RLIMIT_DATA
|
||||
{ 'd', RLIMIT_DATA, 1024, "data seg size", "kbytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_NICE
|
||||
{ 'e', RLIMIT_NICE, 1, "scheduling priority", (char *)NULL },
|
||||
#endif
|
||||
{ 'f', RLIMIT_FILESIZE, POSIXBLK, "file size", "blocks" },
|
||||
#ifdef RLIMIT_SIGPENDING
|
||||
{ 'i', RLIMIT_SIGPENDING, 1, "pending signals", (char *)NULL },
|
||||
#endif
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
{ 'l', RLIMIT_MEMLOCK, 1024, "max locked memory", "kbytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_RSS
|
||||
{ 'm', RLIMIT_RSS, 1024, "max memory size", "kbytes" },
|
||||
#endif /* RLIMIT_RSS */
|
||||
{ 'n', RLIMIT_OPENFILES, 1, "open files", (char *)NULL},
|
||||
{ 'p', RLIMIT_PIPESIZE, 512, "pipe size", "512 bytes" },
|
||||
#ifdef RLIMIT_MSGQUEUE
|
||||
{ 'q', RLIMIT_MSGQUEUE, 1, "POSIX message queues", "bytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_RTPRIO
|
||||
{ 'r', RLIMIT_RTPRIO, 1, "real-time priority", (char *)NULL },
|
||||
#endif
|
||||
#ifdef RLIMIT_STACK
|
||||
{ 's', RLIMIT_STACK, 1024, "stack size", "kbytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_CPU
|
||||
{ 't', RLIMIT_CPU, 1, "cpu time", "seconds" },
|
||||
#endif /* RLIMIT_CPU */
|
||||
{ 'u', RLIMIT_MAXUPROC, 1, "max user processes", (char *)NULL },
|
||||
#if defined (HAVE_RESOURCE)
|
||||
{ 'v', RLIMIT_VIRTMEM, RLIMIT_VMBLKSZ, "virtual memory", "kbytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_SWAP
|
||||
{ 'w', RLIMIT_SWAP, 1024, "swap size", "kbytes" },
|
||||
#endif
|
||||
#ifdef RLIMIT_LOCKS
|
||||
{ 'x', RLIMIT_LOCKS, 1, "file locks", (char *)NULL },
|
||||
#endif
|
||||
{ -1, -1, -1, (char *)NULL, (char *)NULL }
|
||||
};
|
||||
#define NCMDS (sizeof(limits) / sizeof(limits[0]))
|
||||
|
||||
typedef struct _cmd {
|
||||
int cmd;
|
||||
char *arg;
|
||||
} ULCMD;
|
||||
|
||||
static ULCMD *cmdlist;
|
||||
static int ncmd;
|
||||
static int cmdlistsz;
|
||||
|
||||
#if !defined (HAVE_RESOURCE) && !defined (HAVE_ULIMIT)
|
||||
long
|
||||
ulimit (cmd, newlim)
|
||||
int cmd;
|
||||
long newlim;
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#endif /* !HAVE_RESOURCE && !HAVE_ULIMIT */
|
||||
|
||||
static int
|
||||
_findlim (opt)
|
||||
int opt;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; limits[i].option > 0; i++)
|
||||
if (limits[i].option == opt)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char optstring[4 + 2 * NCMDS];
|
||||
|
||||
/* Report or set limits associated with certain per-process resources.
|
||||
See the help documentation in builtins.c for a full description. */
|
||||
int
|
||||
ulimit_builtin (list)
|
||||
register WORD_LIST *list;
|
||||
{
|
||||
register char *s;
|
||||
int c, limind, mode, opt, all_limits;
|
||||
|
||||
mode = 0;
|
||||
|
||||
all_limits = 0;
|
||||
|
||||
/* Idea stolen from pdksh -- build option string the first time called. */
|
||||
if (optstring[0] == 0)
|
||||
{
|
||||
s = optstring;
|
||||
*s++ = 'a'; *s++ = 'S'; *s++ = 'H';
|
||||
for (c = 0; limits[c].option > 0; c++)
|
||||
{
|
||||
*s++ = limits[c].option;
|
||||
*s++ = ';';
|
||||
}
|
||||
*s = '\0';
|
||||
}
|
||||
|
||||
/* Initialize the command list. */
|
||||
if (cmdlistsz == 0)
|
||||
cmdlist = (ULCMD *)xmalloc ((cmdlistsz = 16) * sizeof (ULCMD));
|
||||
ncmd = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, optstring)) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'a':
|
||||
all_limits++;
|
||||
break;
|
||||
|
||||
/* -S and -H are modifiers, not real options. */
|
||||
case 'S':
|
||||
mode |= LIMIT_SOFT;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
mode |= LIMIT_HARD;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
default:
|
||||
if (ncmd >= cmdlistsz)
|
||||
cmdlist = (ULCMD *)xrealloc (cmdlist, (cmdlistsz *= 2) * sizeof (ULCMD));
|
||||
cmdlist[ncmd].cmd = opt;
|
||||
cmdlist[ncmd++].arg = list_optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (all_limits)
|
||||
{
|
||||
#ifdef NOTYET
|
||||
if (list) /* setting */
|
||||
{
|
||||
if (STREQ (list->word->word, "unlimited") == 0)
|
||||
{
|
||||
builtin_error (_("%s: invalid limit argument"), list->word->word);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
return (set_all_limits (mode == 0 ? LIMIT_SOFT|LIMIT_HARD : mode, RLIM_INFINITY));
|
||||
}
|
||||
#endif
|
||||
print_all_limits (mode == 0 ? LIMIT_SOFT : mode);
|
||||
return (sh_chkwrite (EXECUTION_SUCCESS));
|
||||
}
|
||||
|
||||
/* default is `ulimit -f' */
|
||||
if (ncmd == 0)
|
||||
{
|
||||
cmdlist[ncmd].cmd = 'f';
|
||||
/* `ulimit something' is same as `ulimit -f something' */
|
||||
cmdlist[ncmd++].arg = list ? list->word->word : (char *)NULL;
|
||||
if (list)
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* verify each command in the list. */
|
||||
for (c = 0; c < ncmd; c++)
|
||||
{
|
||||
limind = _findlim (cmdlist[c].cmd);
|
||||
if (limind == -1)
|
||||
{
|
||||
builtin_error (_("`%c': bad command"), cmdlist[c].cmd);
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
for (c = 0; c < ncmd; c++)
|
||||
if (ulimit_internal (cmdlist[c].cmd, cmdlist[c].arg, mode, ncmd > 1) == EXECUTION_FAILURE)
|
||||
return (EXECUTION_FAILURE);
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
static int
|
||||
ulimit_internal (cmd, cmdarg, mode, multiple)
|
||||
int cmd;
|
||||
char *cmdarg;
|
||||
int mode, multiple;
|
||||
{
|
||||
int opt, limind, setting;
|
||||
int block_factor;
|
||||
RLIMTYPE soft_limit, hard_limit, real_limit, limit;
|
||||
|
||||
setting = cmdarg != 0;
|
||||
limind = _findlim (cmd);
|
||||
if (mode == 0)
|
||||
mode = setting ? (LIMIT_HARD|LIMIT_SOFT) : LIMIT_SOFT;
|
||||
opt = get_limit (limind, &soft_limit, &hard_limit);
|
||||
if (opt < 0)
|
||||
{
|
||||
builtin_error (_("%s: cannot get limit: %s"), limits[limind].description,
|
||||
strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (setting == 0) /* print the value of the specified limit */
|
||||
{
|
||||
printone (limind, (mode & LIMIT_SOFT) ? soft_limit : hard_limit, multiple);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
/* Setting the limit. */
|
||||
if (STREQ (cmdarg, "hard"))
|
||||
real_limit = hard_limit;
|
||||
else if (STREQ (cmdarg, "soft"))
|
||||
real_limit = soft_limit;
|
||||
else if (STREQ (cmdarg, "unlimited"))
|
||||
real_limit = RLIM_INFINITY;
|
||||
else if (all_digits (cmdarg))
|
||||
{
|
||||
limit = string_to_rlimtype (cmdarg);
|
||||
block_factor = BLOCKSIZE(limits[limind].block_factor);
|
||||
real_limit = limit * block_factor;
|
||||
|
||||
if ((real_limit / block_factor) != limit)
|
||||
{
|
||||
sh_erange (cmdarg, _("limit"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_invalidnum (cmdarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (set_limit (limind, real_limit, mode) < 0)
|
||||
{
|
||||
builtin_error (_("%s: cannot modify limit: %s"), limits[limind].description,
|
||||
strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
static int
|
||||
get_limit (ind, softlim, hardlim)
|
||||
int ind;
|
||||
RLIMTYPE *softlim, *hardlim;
|
||||
{
|
||||
RLIMTYPE value;
|
||||
#if defined (HAVE_RESOURCE)
|
||||
struct rlimit limit;
|
||||
#endif
|
||||
|
||||
if (limits[ind].parameter >= 256)
|
||||
{
|
||||
switch (limits[ind].parameter)
|
||||
{
|
||||
case RLIMIT_FILESIZE:
|
||||
if (filesize (&value) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case RLIMIT_PIPESIZE:
|
||||
if (pipesize (&value) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case RLIMIT_OPENFILES:
|
||||
value = (RLIMTYPE)getdtablesize ();
|
||||
break;
|
||||
case RLIMIT_VIRTMEM:
|
||||
return (getmaxvm (softlim, hardlim));
|
||||
case RLIMIT_MAXUPROC:
|
||||
if (getmaxuprc (&value) < 0)
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*softlim = *hardlim = value;
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined (HAVE_RESOURCE)
|
||||
if (getrlimit (limits[ind].parameter, &limit) < 0)
|
||||
return -1;
|
||||
*softlim = limit.rlim_cur;
|
||||
*hardlim = limit.rlim_max;
|
||||
# if defined (HPUX9)
|
||||
if (limits[ind].parameter == RLIMIT_FILESIZE)
|
||||
{
|
||||
*softlim *= 512;
|
||||
*hardlim *= 512; /* Ugh. */
|
||||
}
|
||||
else
|
||||
# endif /* HPUX9 */
|
||||
return 0;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
set_limit (ind, newlim, mode)
|
||||
int ind;
|
||||
RLIMTYPE newlim;
|
||||
int mode;
|
||||
{
|
||||
#if defined (HAVE_RESOURCE)
|
||||
struct rlimit limit;
|
||||
RLIMTYPE val;
|
||||
#endif
|
||||
|
||||
if (limits[ind].parameter >= 256)
|
||||
switch (limits[ind].parameter)
|
||||
{
|
||||
case RLIMIT_FILESIZE:
|
||||
#if !defined (HAVE_RESOURCE)
|
||||
return (ulimit (2, newlim / 512L));
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
case RLIMIT_OPENFILES:
|
||||
#if defined (HAVE_SETDTABLESIZE)
|
||||
# if defined (__CYGWIN__)
|
||||
/* Grrr... Cygwin declares setdtablesize as void. */
|
||||
setdtablesize (newlim);
|
||||
return 0;
|
||||
# else
|
||||
return (setdtablesize (newlim));
|
||||
# endif
|
||||
#endif
|
||||
case RLIMIT_PIPESIZE:
|
||||
case RLIMIT_VIRTMEM:
|
||||
case RLIMIT_MAXUPROC:
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined (HAVE_RESOURCE)
|
||||
if (getrlimit (limits[ind].parameter, &limit) < 0)
|
||||
return -1;
|
||||
# if defined (HPUX9)
|
||||
if (limits[ind].parameter == RLIMIT_FILESIZE)
|
||||
newlim /= 512; /* Ugh. */
|
||||
# endif /* HPUX9 */
|
||||
val = (current_user.euid != 0 && newlim == RLIM_INFINITY &&
|
||||
(mode & LIMIT_HARD) == 0 && /* XXX -- test */
|
||||
(limit.rlim_cur <= limit.rlim_max))
|
||||
? limit.rlim_max : newlim;
|
||||
if (mode & LIMIT_SOFT)
|
||||
limit.rlim_cur = val;
|
||||
if (mode & LIMIT_HARD)
|
||||
limit.rlim_max = val;
|
||||
|
||||
return (setrlimit (limits[ind].parameter, &limit));
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
getmaxvm (softlim, hardlim)
|
||||
RLIMTYPE *softlim, *hardlim;
|
||||
{
|
||||
#if defined (HAVE_RESOURCE)
|
||||
struct rlimit datalim, stacklim;
|
||||
|
||||
if (getrlimit (RLIMIT_DATA, &datalim) < 0)
|
||||
return -1;
|
||||
|
||||
if (getrlimit (RLIMIT_STACK, &stacklim) < 0)
|
||||
return -1;
|
||||
|
||||
/* Protect against overflow. */
|
||||
*softlim = (datalim.rlim_cur / 1024L) + (stacklim.rlim_cur / 1024L);
|
||||
*hardlim = (datalim.rlim_max / 1024L) + (stacklim.rlim_max / 1024L);
|
||||
return 0;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif /* HAVE_RESOURCE */
|
||||
}
|
||||
|
||||
static int
|
||||
filesize(valuep)
|
||||
RLIMTYPE *valuep;
|
||||
{
|
||||
#if !defined (HAVE_RESOURCE)
|
||||
long result;
|
||||
if ((result = ulimit (1, 0L)) < 0)
|
||||
return -1;
|
||||
else
|
||||
*valuep = (RLIMTYPE) result * 512;
|
||||
return 0;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
pipesize (valuep)
|
||||
RLIMTYPE *valuep;
|
||||
{
|
||||
#if defined (PIPE_BUF)
|
||||
/* This is defined on Posix systems. */
|
||||
*valuep = (RLIMTYPE) PIPE_BUF;
|
||||
return 0;
|
||||
#else
|
||||
# if defined (_POSIX_PIPE_BUF)
|
||||
*valuep = (RLIMTYPE) _POSIX_PIPE_BUF;
|
||||
return 0;
|
||||
# else
|
||||
# if defined (PIPESIZE)
|
||||
/* This is defined by running a program from the Makefile. */
|
||||
*valuep = (RLIMTYPE) PIPESIZE;
|
||||
return 0;
|
||||
# else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
# endif /* PIPESIZE */
|
||||
# endif /* _POSIX_PIPE_BUF */
|
||||
#endif /* PIPE_BUF */
|
||||
}
|
||||
|
||||
static int
|
||||
getmaxuprc (valuep)
|
||||
RLIMTYPE *valuep;
|
||||
{
|
||||
long maxchild;
|
||||
|
||||
maxchild = getmaxchild ();
|
||||
if (maxchild < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*valuep = (RLIMTYPE) maxchild;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_all_limits (mode)
|
||||
int mode;
|
||||
{
|
||||
register int i;
|
||||
RLIMTYPE softlim, hardlim;
|
||||
|
||||
if (mode == 0)
|
||||
mode |= LIMIT_SOFT;
|
||||
|
||||
for (i = 0; limits[i].option > 0; i++)
|
||||
{
|
||||
if (get_limit (i, &softlim, &hardlim) == 0)
|
||||
printone (i, (mode & LIMIT_SOFT) ? softlim : hardlim, 1);
|
||||
else if (errno != EINVAL)
|
||||
builtin_error ("%s: cannot get limit: %s", limits[i].description,
|
||||
strerror (errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printone (limind, curlim, pdesc)
|
||||
int limind;
|
||||
RLIMTYPE curlim;
|
||||
int pdesc;
|
||||
{
|
||||
char unitstr[64];
|
||||
int factor;
|
||||
|
||||
factor = BLOCKSIZE(limits[limind].block_factor);
|
||||
if (pdesc)
|
||||
{
|
||||
if (limits[limind].units)
|
||||
sprintf (unitstr, "(%s, -%c) ", limits[limind].units, limits[limind].option);
|
||||
else
|
||||
sprintf (unitstr, "(-%c) ", limits[limind].option);
|
||||
|
||||
printf ("%-20s %16s", limits[limind].description, unitstr);
|
||||
}
|
||||
if (curlim == RLIM_INFINITY)
|
||||
puts ("unlimited");
|
||||
else if (curlim == RLIM_SAVED_MAX)
|
||||
puts ("hard");
|
||||
else if (curlim == RLIM_SAVED_CUR)
|
||||
puts ("soft");
|
||||
else
|
||||
print_rlimtype ((curlim / factor), 1);
|
||||
}
|
||||
|
||||
/* Set all limits to NEWLIM. NEWLIM currently must be RLIM_INFINITY, which
|
||||
causes all limits to be set as high as possible depending on mode (like
|
||||
csh `unlimit'). Returns -1 if NEWLIM is invalid, 0 if all limits
|
||||
were set successfully, and 1 if at least one limit could not be set.
|
||||
|
||||
To raise all soft limits to their corresponding hard limits, use
|
||||
ulimit -S -a unlimited
|
||||
To attempt to raise all hard limits to infinity (superuser-only), use
|
||||
ulimit -H -a unlimited
|
||||
To attempt to raise all soft and hard limits to infinity, use
|
||||
ulimit -a unlimited
|
||||
*/
|
||||
|
||||
static int
|
||||
set_all_limits (mode, newlim)
|
||||
int mode;
|
||||
RLIMTYPE newlim;
|
||||
{
|
||||
register int i;
|
||||
int retval = 0;
|
||||
|
||||
if (newlim != RLIM_INFINITY)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode == 0)
|
||||
mode = LIMIT_SOFT|LIMIT_HARD;
|
||||
|
||||
for (retval = i = 0; limits[i].option > 0; i++)
|
||||
if (set_limit (i, newlim, mode) < 0)
|
||||
{
|
||||
builtin_error (_("%s: cannot modify limit: %s"), limits[i].description,
|
||||
strerror (errno));
|
||||
retval = 1;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif /* !_MINIX */
|
||||
@@ -1,5 +1,5 @@
|
||||
#! /bin/sh
|
||||
# From configure.in for Bash 4.2, version 4.048.
|
||||
# From configure.in for Bash 4.2, version 4.050.
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.68 for bash 4.2-maint.
|
||||
#
|
||||
@@ -9160,7 +9160,7 @@ fi
|
||||
|
||||
done
|
||||
|
||||
for ac_header in sys/pte.h sys/stream.h sys/select.h sys/file.h \
|
||||
for ac_header in sys/pte.h sys/stream.h sys/select.h sys/file.h sys/param.h \
|
||||
sys/resource.h sys/param.h sys/socket.h sys/stat.h \
|
||||
sys/time.h sys/times.h sys/types.h sys/wait.h
|
||||
do :
|
||||
@@ -15760,7 +15760,7 @@ linux*) LOCAL_LDFLAGS=-rdynamic # allow dynamic loading
|
||||
*qnx*) LOCAL_CFLAGS="-Dqnx -F -3s" LOCAL_LDFLAGS="-3s" LOCAL_LIBS="-lunix -lncurses" ;;
|
||||
powerux*) LOCAL_LIBS="-lgen" ;;
|
||||
cygwin*) LOCAL_CFLAGS=-DRECYCLES_PIDS ;;
|
||||
opennt*|interix*) LOCAL_CFLAGS="-DNO_MAIN_ENV_ARG -DBROKEN_DIRENT_D_INO -D_POSIX_SOURCE -D_ALL_SOURCE" ;;
|
||||
opennt*|interix*) LOCAL_CFLAGS="-DNO_MAIN_ENV_ARG -DBROKEN_DIRENT_D_INO -D_POSIX_SOURCE -D_ALL_SOURCE -DRECYCLES_PIDS" ;;
|
||||
esac
|
||||
|
||||
case "${host_os}-${CC}" in
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ dnl Process this file with autoconf to produce a configure script.
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AC_REVISION([for Bash 4.2, version 4.048])dnl
|
||||
AC_REVISION([for Bash 4.2, version 4.049])dnl
|
||||
|
||||
define(bashvers, 4.2)
|
||||
define(relstatus, maint)
|
||||
@@ -1067,7 +1067,7 @@ linux*) LOCAL_LDFLAGS=-rdynamic # allow dynamic loading
|
||||
*qnx*) LOCAL_CFLAGS="-Dqnx -F -3s" LOCAL_LDFLAGS="-3s" LOCAL_LIBS="-lunix -lncurses" ;;
|
||||
powerux*) LOCAL_LIBS="-lgen" ;;
|
||||
cygwin*) LOCAL_CFLAGS=-DRECYCLES_PIDS ;;
|
||||
opennt*|interix*) LOCAL_CFLAGS="-DNO_MAIN_ENV_ARG -DBROKEN_DIRENT_D_INO -D_POSIX_SOURCE -D_ALL_SOURCE" ;;
|
||||
opennt*|interix*) LOCAL_CFLAGS="-DNO_MAIN_ENV_ARG -DBROKEN_DIRENT_D_INO -D_POSIX_SOURCE -D_ALL_SOURCE -DRECYCLES_PIDS" ;;
|
||||
esac
|
||||
|
||||
dnl Stanza for OS/compiler pair-specific flags
|
||||
|
||||
@@ -0,0 +1,979 @@
|
||||
*** ../bash-4.2-patched/execute_cmd.c 2012-05-02 12:02:27.000000000 -0400
|
||||
--- execute_cmd.c 2012-07-28 18:14:33.000000000 -0400
|
||||
***************
|
||||
*** 1,5 ****
|
||||
/* execute_cmd.c -- Execute a COMMAND structure. */
|
||||
|
||||
! /* Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
--- 1,5 ----
|
||||
/* execute_cmd.c -- Execute a COMMAND structure. */
|
||||
|
||||
! /* Copyright (C) 1987-2012 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
***************
|
||||
*** 119,122 ****
|
||||
--- 119,124 ----
|
||||
#endif
|
||||
|
||||
+ extern int job_control; /* XXX */
|
||||
+
|
||||
extern int close __P((int));
|
||||
|
||||
***************
|
||||
*** 196,200 ****
|
||||
static int execute_connection __P((COMMAND *, int, int, int, struct fd_bitmap *));
|
||||
|
||||
! static int execute_intern_function __P((WORD_DESC *, COMMAND *));
|
||||
|
||||
/* Set to 1 if fd 0 was the subject of redirection to a subshell. Global
|
||||
--- 198,202 ----
|
||||
static int execute_connection __P((COMMAND *, int, int, int, struct fd_bitmap *));
|
||||
|
||||
! static int execute_intern_function __P((WORD_DESC *, FUNCTION_DEF *));
|
||||
|
||||
/* Set to 1 if fd 0 was the subject of redirection to a subshell. Global
|
||||
***************
|
||||
*** 273,277 ****
|
||||
static int showing_function_line;
|
||||
|
||||
! static int line_number_for_err_trap;
|
||||
|
||||
/* A sort of function nesting level counter */
|
||||
--- 275,281 ----
|
||||
static int showing_function_line;
|
||||
|
||||
! /* $LINENO ($BASH_LINENO) for use by an ERR trap. Global so parse_and_execute
|
||||
! can save and restore it. */
|
||||
! int line_number_for_err_trap;
|
||||
|
||||
/* A sort of function nesting level counter */
|
||||
***************
|
||||
*** 531,534 ****
|
||||
--- 535,542 ----
|
||||
volatile int last_pid;
|
||||
volatile int save_line_number;
|
||||
+ #if defined (PROCESS_SUBSTITUTION)
|
||||
+ volatile int ofifo, nfifo, osize, saved_fifo;
|
||||
+ volatile char *ofifo_list;
|
||||
+ #endif
|
||||
|
||||
#if 0
|
||||
***************
|
||||
*** 593,597 ****
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
|
||||
! unlink_fifo_list ();
|
||||
#endif
|
||||
/* If we are part of a pipeline, and not the end of the pipeline,
|
||||
--- 601,606 ----
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
|
||||
! if (variable_context == 0) /* wait until shell function completes */
|
||||
! unlink_fifo_list ();
|
||||
#endif
|
||||
/* If we are part of a pipeline, and not the end of the pipeline,
|
||||
***************
|
||||
*** 670,673 ****
|
||||
--- 679,693 ----
|
||||
stdin_redir = stdin_redirects (command->redirects);
|
||||
|
||||
+ #if defined (PROCESS_SUBSTITUTION)
|
||||
+ if (variable_context != 0)
|
||||
+ {
|
||||
+ ofifo = num_fifos ();
|
||||
+ ofifo_list = copy_fifo_list (&osize);
|
||||
+ saved_fifo = 1;
|
||||
+ }
|
||||
+ else
|
||||
+ saved_fifo = 0;
|
||||
+ #endif
|
||||
+
|
||||
/* Handle WHILE FOR CASE etc. with redirections. (Also '&' input
|
||||
redirection.) */
|
||||
***************
|
||||
*** 677,680 ****
|
||||
--- 697,704 ----
|
||||
redirection_undo_list = (REDIRECT *)NULL;
|
||||
dispose_exec_redirects ();
|
||||
+ #if defined (PROCESS_SUBSTITUTION)
|
||||
+ if (saved_fifo)
|
||||
+ free (ofifo_list);
|
||||
+ #endif
|
||||
return (last_command_exit_value = EXECUTION_FAILURE);
|
||||
}
|
||||
***************
|
||||
*** 752,758 ****
|
||||
|
||||
/* XXX - this is something to watch out for if there are problems
|
||||
! when the shell is compiled without job control. */
|
||||
! if (already_making_children && pipe_out == NO_PIPE &&
|
||||
! last_made_pid != last_pid)
|
||||
{
|
||||
stop_pipeline (asynchronous, (COMMAND *)NULL);
|
||||
--- 776,784 ----
|
||||
|
||||
/* XXX - this is something to watch out for if there are problems
|
||||
! when the shell is compiled without job control. Don't worry about
|
||||
! whether or not last_made_pid == last_pid; already_making_children
|
||||
! tells us whether or not there are unwaited-for children to wait
|
||||
! for and reap. */
|
||||
! if (already_making_children && pipe_out == NO_PIPE)
|
||||
{
|
||||
stop_pipeline (asynchronous, (COMMAND *)NULL);
|
||||
***************
|
||||
*** 952,956 ****
|
||||
case cm_function_def:
|
||||
exec_result = execute_intern_function (command->value.Function_def->name,
|
||||
! command->value.Function_def->command);
|
||||
break;
|
||||
|
||||
--- 978,982 ----
|
||||
case cm_function_def:
|
||||
exec_result = execute_intern_function (command->value.Function_def->name,
|
||||
! command->value.Function_def);
|
||||
break;
|
||||
|
||||
***************
|
||||
*** 971,974 ****
|
||||
--- 997,1010 ----
|
||||
discard_unwind_frame ("loop_redirections");
|
||||
|
||||
+ #if defined (PROCESS_SUBSTITUTION)
|
||||
+ if (saved_fifo)
|
||||
+ {
|
||||
+ nfifo = num_fifos ();
|
||||
+ if (nfifo > ofifo)
|
||||
+ close_new_fifos (ofifo_list, osize);
|
||||
+ free (ofifo_list);
|
||||
+ }
|
||||
+ #endif
|
||||
+
|
||||
/* Invert the return value if we have to */
|
||||
if (invert)
|
||||
***************
|
||||
*** 1001,1004 ****
|
||||
--- 1037,1041 ----
|
||||
#endif
|
||||
currently_executing_command = (COMMAND *)NULL;
|
||||
+
|
||||
return (last_command_exit_value);
|
||||
}
|
||||
***************
|
||||
*** 1223,1227 ****
|
||||
#endif
|
||||
|
||||
! posix_time = (command->flags & CMD_TIME_POSIX);
|
||||
|
||||
nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
|
||||
--- 1260,1264 ----
|
||||
#endif
|
||||
|
||||
! posix_time = command && (command->flags & CMD_TIME_POSIX);
|
||||
|
||||
nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
|
||||
***************
|
||||
*** 1505,1509 ****
|
||||
return_code = return_catch_value;
|
||||
else
|
||||
! return_code = execute_command_internal (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
|
||||
|
||||
/* If we are asked to, invert the return value. */
|
||||
--- 1542,1546 ----
|
||||
return_code = return_catch_value;
|
||||
else
|
||||
! return_code = execute_command_internal ((COMMAND *)tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
|
||||
|
||||
/* If we are asked to, invert the return value. */
|
||||
***************
|
||||
*** 1549,1556 ****
|
||||
static void cpl_reap __P((void));
|
||||
static void cpl_flush __P((void));
|
||||
static struct cpelement *cpl_search __P((pid_t));
|
||||
! static struct cpelement *cpl_searchbyname __P((char *));
|
||||
static void cpl_prune __P((void));
|
||||
|
||||
Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0, 0, 0 };
|
||||
|
||||
--- 1586,1597 ----
|
||||
static void cpl_reap __P((void));
|
||||
static void cpl_flush __P((void));
|
||||
+ static void cpl_closeall __P((void));
|
||||
static struct cpelement *cpl_search __P((pid_t));
|
||||
! static struct cpelement *cpl_searchbyname __P((const char *));
|
||||
static void cpl_prune __P((void));
|
||||
|
||||
+ static void coproc_free __P((struct coproc *));
|
||||
+
|
||||
+ /* Will go away when there is fully-implemented support for multiple coprocs. */
|
||||
Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0, 0, 0 };
|
||||
|
||||
***************
|
||||
*** 1639,1668 ****
|
||||
cpl_reap ()
|
||||
{
|
||||
! struct cpelement *prev, *p;
|
||||
|
||||
! for (prev = p = coproc_list.head; p; prev = p, p = p->next)
|
||||
! if (p->coproc->c_flags & COPROC_DEAD)
|
||||
! {
|
||||
! prev->next = p->next; /* remove from list */
|
||||
!
|
||||
! /* Housekeeping in the border cases. */
|
||||
! if (p == coproc_list.head)
|
||||
! coproc_list.head = coproc_list.head->next;
|
||||
! else if (p == coproc_list.tail)
|
||||
! coproc_list.tail = prev;
|
||||
!
|
||||
! coproc_list.ncoproc--;
|
||||
! if (coproc_list.ncoproc == 0)
|
||||
! coproc_list.head = coproc_list.tail = 0;
|
||||
! else if (coproc_list.ncoproc == 1)
|
||||
! coproc_list.tail = coproc_list.head; /* just to make sure */
|
||||
|
||||
#if defined (DEBUG)
|
||||
! itrace("cpl_reap: deleting %d", p->coproc->c_pid);
|
||||
#endif
|
||||
|
||||
! coproc_dispose (p->coproc);
|
||||
! cpe_dispose (p);
|
||||
! }
|
||||
}
|
||||
|
||||
--- 1680,1722 ----
|
||||
cpl_reap ()
|
||||
{
|
||||
! struct cpelement *p, *next, *nh, *nt;
|
||||
|
||||
! /* Build a new list by removing dead coprocs and fix up the coproc_list
|
||||
! pointers when done. */
|
||||
! nh = nt = next = (struct cpelement *)0;
|
||||
! for (p = coproc_list.head; p; p = next)
|
||||
! {
|
||||
! next = p->next;
|
||||
! if (p->coproc->c_flags & COPROC_DEAD)
|
||||
! {
|
||||
! coproc_list.ncoproc--; /* keep running count, fix up pointers later */
|
||||
|
||||
#if defined (DEBUG)
|
||||
! itrace("cpl_reap: deleting %d", p->coproc->c_pid);
|
||||
#endif
|
||||
|
||||
! coproc_dispose (p->coproc);
|
||||
! cpe_dispose (p);
|
||||
! }
|
||||
! else if (nh == 0)
|
||||
! nh = nt = p;
|
||||
! else
|
||||
! {
|
||||
! nt->next = p;
|
||||
! nt = nt->next;
|
||||
! }
|
||||
! }
|
||||
!
|
||||
! if (coproc_list.ncoproc == 0)
|
||||
! coproc_list.head = coproc_list.tail = 0;
|
||||
! else
|
||||
! {
|
||||
! if (nt)
|
||||
! nt->next = 0;
|
||||
! coproc_list.head = nh;
|
||||
! coproc_list.tail = nt;
|
||||
! if (coproc_list.ncoproc == 1)
|
||||
! coproc_list.tail = coproc_list.head; /* just to make sure */
|
||||
! }
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1686,1689 ****
|
||||
--- 1740,1762 ----
|
||||
}
|
||||
|
||||
+ static void
|
||||
+ cpl_closeall ()
|
||||
+ {
|
||||
+ struct cpelement *cpe;
|
||||
+
|
||||
+ for (cpe = coproc_list.head; cpe; cpe = cpe->next)
|
||||
+ coproc_close (cpe->coproc);
|
||||
+ }
|
||||
+
|
||||
+ static void
|
||||
+ cpl_fdchk (fd)
|
||||
+ int fd;
|
||||
+ {
|
||||
+ struct cpelement *cpe;
|
||||
+
|
||||
+ for (cpe = coproc_list.head; cpe; cpe = cpe->next)
|
||||
+ coproc_checkfd (cpe->coproc, fd);
|
||||
+ }
|
||||
+
|
||||
/* Search for PID in the list of coprocs; return the cpelement struct if
|
||||
found. If not found, return NULL. */
|
||||
***************
|
||||
*** 1692,1700 ****
|
||||
pid_t pid;
|
||||
{
|
||||
! struct cpelement *cp;
|
||||
|
||||
! for (cp = coproc_list.head ; cp; cp = cp->next)
|
||||
! if (cp->coproc->c_pid == pid)
|
||||
! return cp;
|
||||
return (struct cpelement *)NULL;
|
||||
}
|
||||
--- 1765,1773 ----
|
||||
pid_t pid;
|
||||
{
|
||||
! struct cpelement *cpe;
|
||||
|
||||
! for (cpe = coproc_list.head ; cpe; cpe = cpe->next)
|
||||
! if (cpe->coproc->c_pid == pid)
|
||||
! return cpe;
|
||||
return (struct cpelement *)NULL;
|
||||
}
|
||||
***************
|
||||
*** 1704,1708 ****
|
||||
static struct cpelement *
|
||||
cpl_searchbyname (name)
|
||||
! char *name;
|
||||
{
|
||||
struct cpelement *cp;
|
||||
--- 1777,1781 ----
|
||||
static struct cpelement *
|
||||
cpl_searchbyname (name)
|
||||
! const char *name;
|
||||
{
|
||||
struct cpelement *cp;
|
||||
***************
|
||||
*** 1739,1743 ****
|
||||
--- 1812,1823 ----
|
||||
pid_t pid;
|
||||
{
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ struct cpelement *p;
|
||||
+
|
||||
+ p = cpl_search (pid);
|
||||
+ return (p ? p->coproc : 0);
|
||||
+ #else
|
||||
return (pid == sh_coproc.c_pid ? &sh_coproc : 0);
|
||||
+ #endif
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1746,1750 ****
|
||||
--- 1826,1837 ----
|
||||
const char *name;
|
||||
{
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ struct cpelement *p;
|
||||
+
|
||||
+ p = cpl_searchbyname (name);
|
||||
+ return (p ? p->coproc : 0);
|
||||
+ #else
|
||||
return ((sh_coproc.c_name && STREQ (sh_coproc.c_name, name)) ? &sh_coproc : 0);
|
||||
+ #endif
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1767,1771 ****
|
||||
struct coproc *cp;
|
||||
|
||||
! cp = &sh_coproc; /* XXX */
|
||||
coproc_init (cp);
|
||||
|
||||
--- 1854,1862 ----
|
||||
struct coproc *cp;
|
||||
|
||||
! #if MULTIPLE_COPROCS
|
||||
! cp = (struct coproc *)xmalloc (sizeof (struct coproc));
|
||||
! #else
|
||||
! cp = &sh_coproc;
|
||||
! #endif
|
||||
coproc_init (cp);
|
||||
|
||||
***************
|
||||
*** 1773,1779 ****
|
||||
--- 1864,1881 ----
|
||||
cp->c_pid = pid;
|
||||
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ cpl_add (cp);
|
||||
+ #endif
|
||||
+
|
||||
return (cp);
|
||||
}
|
||||
|
||||
+ static void
|
||||
+ coproc_free (cp)
|
||||
+ struct coproc *cp;
|
||||
+ {
|
||||
+ free (cp);
|
||||
+ }
|
||||
+
|
||||
void
|
||||
coproc_dispose (cp)
|
||||
***************
|
||||
*** 1786,1797 ****
|
||||
FREE (cp->c_name);
|
||||
coproc_close (cp);
|
||||
coproc_init (cp);
|
||||
}
|
||||
|
||||
! /* Placeholder for now. */
|
||||
void
|
||||
coproc_flush ()
|
||||
{
|
||||
coproc_dispose (&sh_coproc);
|
||||
}
|
||||
|
||||
--- 1888,1907 ----
|
||||
FREE (cp->c_name);
|
||||
coproc_close (cp);
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ coproc_free (cp);
|
||||
+ #else
|
||||
coproc_init (cp);
|
||||
+ #endif
|
||||
}
|
||||
|
||||
! /* Placeholder for now. Will require changes for multiple coprocs */
|
||||
void
|
||||
coproc_flush ()
|
||||
{
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ cpl_flush ();
|
||||
+ #else
|
||||
coproc_dispose (&sh_coproc);
|
||||
+ #endif
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1816,1820 ****
|
||||
coproc_closeall ()
|
||||
{
|
||||
! coproc_close (&sh_coproc);
|
||||
}
|
||||
|
||||
--- 1926,1934 ----
|
||||
coproc_closeall ()
|
||||
{
|
||||
! #if MULTIPLE_COPROCS
|
||||
! cpl_closeall ();
|
||||
! #else
|
||||
! coproc_close (&sh_coproc); /* XXX - will require changes for multiple coprocs */
|
||||
! #endif
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1822,1830 ****
|
||||
coproc_reap ()
|
||||
{
|
||||
struct coproc *cp;
|
||||
|
||||
! cp = &sh_coproc;
|
||||
if (cp && (cp->c_flags & COPROC_DEAD))
|
||||
coproc_dispose (cp);
|
||||
}
|
||||
|
||||
--- 1936,1948 ----
|
||||
coproc_reap ()
|
||||
{
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ cpl_reap ();
|
||||
+ #else
|
||||
struct coproc *cp;
|
||||
|
||||
! cp = &sh_coproc; /* XXX - will require changes for multiple coprocs */
|
||||
if (cp && (cp->c_flags & COPROC_DEAD))
|
||||
coproc_dispose (cp);
|
||||
+ #endif
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1873,1877 ****
|
||||
--- 1991,1999 ----
|
||||
int fd;
|
||||
{
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ cpl_fdchk (fd);
|
||||
+ #else
|
||||
coproc_checkfd (&sh_coproc, fd);
|
||||
+ #endif
|
||||
}
|
||||
|
||||
***************
|
||||
*** 1908,1923 ****
|
||||
struct coproc *cp;
|
||||
|
||||
cp = getcoprocbypid (pid);
|
||||
- #if 0
|
||||
- if (cp)
|
||||
- itrace("coproc_pidchk: pid %d has died", pid);
|
||||
#endif
|
||||
if (cp)
|
||||
{
|
||||
cp->c_status = status;
|
||||
cp->c_flags |= COPROC_DEAD;
|
||||
cp->c_flags &= ~COPROC_RUNNING;
|
||||
! #if 0
|
||||
coproc_dispose (cp);
|
||||
#endif
|
||||
}
|
||||
--- 2030,2053 ----
|
||||
struct coproc *cp;
|
||||
|
||||
+ #if MULTIPLE_COPROCS
|
||||
+ struct cpelement *cpe;
|
||||
+
|
||||
+ cpe = cpl_delete (pid);
|
||||
+ cp = cpe ? cpe->coproc : 0;
|
||||
+ #else
|
||||
cp = getcoprocbypid (pid);
|
||||
#endif
|
||||
if (cp)
|
||||
{
|
||||
+ #if 0
|
||||
+ itrace("coproc_pidchk: pid %d has died", pid);
|
||||
+ #endif
|
||||
cp->c_status = status;
|
||||
cp->c_flags |= COPROC_DEAD;
|
||||
cp->c_flags &= ~COPROC_RUNNING;
|
||||
! #if MULTIPLE_COPROCS
|
||||
coproc_dispose (cp);
|
||||
+ #else
|
||||
+ coproc_unsetvars (cp);
|
||||
#endif
|
||||
}
|
||||
***************
|
||||
*** 2015,2029 ****
|
||||
char *tcmd;
|
||||
|
||||
! /* XXX -- will require changes to handle multiple coprocs */
|
||||
if (sh_coproc.c_pid != NO_PID)
|
||||
! {
|
||||
! #if 0
|
||||
! internal_error ("execute_coproc: coproc [%d:%s] already exists", sh_coproc.c_pid, sh_coproc.c_name);
|
||||
! return (last_command_exit_value = EXECUTION_FAILURE);
|
||||
! #else
|
||||
! internal_warning ("execute_coproc: coproc [%d:%s] still exists", sh_coproc.c_pid, sh_coproc.c_name);
|
||||
! #endif
|
||||
! }
|
||||
coproc_init (&sh_coproc);
|
||||
|
||||
command_string_index = 0;
|
||||
--- 2145,2154 ----
|
||||
char *tcmd;
|
||||
|
||||
! /* XXX -- can be removed after changes to handle multiple coprocs */
|
||||
! #if !MULTIPLE_COPROCS
|
||||
if (sh_coproc.c_pid != NO_PID)
|
||||
! internal_warning ("execute_coproc: coproc [%d:%s] still exists", sh_coproc.c_pid, sh_coproc.c_name);
|
||||
coproc_init (&sh_coproc);
|
||||
+ #endif
|
||||
|
||||
command_string_index = 0;
|
||||
***************
|
||||
*** 2197,2202 ****
|
||||
cmd->flags |= CMD_IGNORE_RETURN;
|
||||
|
||||
- #if defined (JOB_CONTROL)
|
||||
lastpipe_flag = 0;
|
||||
begin_unwind_frame ("lastpipe-exec");
|
||||
lstdin = -1;
|
||||
--- 2322,2327 ----
|
||||
cmd->flags |= CMD_IGNORE_RETURN;
|
||||
|
||||
lastpipe_flag = 0;
|
||||
+
|
||||
begin_unwind_frame ("lastpipe-exec");
|
||||
lstdin = -1;
|
||||
***************
|
||||
*** 2222,2233 ****
|
||||
if (prev >= 0)
|
||||
add_unwind_protect (close, prev);
|
||||
- #endif
|
||||
|
||||
exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
|
||||
|
||||
- #if defined (JOB_CONTROL)
|
||||
if (lstdin > 0)
|
||||
restore_stdin (lstdin);
|
||||
- #endif
|
||||
|
||||
if (prev >= 0)
|
||||
--- 2347,2355 ----
|
||||
***************
|
||||
*** 2252,2258 ****
|
||||
}
|
||||
|
||||
- #if defined (JOB_CONTROL)
|
||||
discard_unwind_frame ("lastpipe-exec");
|
||||
- #endif
|
||||
|
||||
return (exec_result);
|
||||
--- 2374,2378 ----
|
||||
***************
|
||||
*** 2471,2479 ****
|
||||
/* Save this command unless it's a trap command and we're not running
|
||||
a debug trap. */
|
||||
- #if 0
|
||||
- if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
|
||||
- #else
|
||||
if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
|
||||
- #endif
|
||||
{
|
||||
FREE (the_printed_command_except_trap);
|
||||
--- 2591,2595 ----
|
||||
***************
|
||||
*** 2490,2494 ****
|
||||
|
||||
this_command_name = (char *)NULL;
|
||||
! v = bind_variable (identifier, list->word->word, 0);
|
||||
if (readonly_p (v) || noassign_p (v))
|
||||
{
|
||||
--- 2606,2617 ----
|
||||
|
||||
this_command_name = (char *)NULL;
|
||||
! /* XXX - special ksh93 for command index variable handling */
|
||||
! v = find_variable_last_nameref (identifier);
|
||||
! if (v && nameref_p (v))
|
||||
! {
|
||||
! v = bind_variable_value (v, list->word->word, 0);
|
||||
! }
|
||||
! else
|
||||
! v = bind_variable (identifier, list->word->word, 0);
|
||||
if (readonly_p (v) || noassign_p (v))
|
||||
{
|
||||
***************
|
||||
*** 2749,2752 ****
|
||||
--- 2872,2877 ----
|
||||
for (i = ind, l = list; l && --i; l = l->next)
|
||||
;
|
||||
+ if (l == 0) /* don't think this can happen */
|
||||
+ return (0);
|
||||
fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
|
||||
return (displen (l->word->word));
|
||||
***************
|
||||
*** 2889,2893 ****
|
||||
for (l = list; l && --reply; l = l->next)
|
||||
;
|
||||
! return (l->word->word);
|
||||
}
|
||||
}
|
||||
--- 3014,3018 ----
|
||||
for (l = list; l && --reply; l = l->next)
|
||||
;
|
||||
! return (l->word->word); /* XXX - can't be null? */
|
||||
}
|
||||
}
|
||||
***************
|
||||
*** 3330,3333 ****
|
||||
--- 3455,3459 ----
|
||||
static char * const nullstr = "";
|
||||
|
||||
+ /* XXX - can COND ever be NULL when this is called? */
|
||||
static int
|
||||
execute_cond_node (cond)
|
||||
***************
|
||||
*** 3379,3384 ****
|
||||
{
|
||||
rmatch = 0;
|
||||
! patmatch = ((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
|
||||
! (cond->op->word[0] == '!' || cond->op->word[0] == '=') ||
|
||||
(cond->op->word[0] == '=' && cond->op->word[1] == '\0'));
|
||||
#if defined (COND_REGEXP)
|
||||
--- 3505,3510 ----
|
||||
{
|
||||
rmatch = 0;
|
||||
! patmatch = (((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
|
||||
! (cond->op->word[0] == '!' || cond->op->word[0] == '=')) ||
|
||||
(cond->op->word[0] == '=' && cond->op->word[1] == '\0'));
|
||||
#if defined (COND_REGEXP)
|
||||
***************
|
||||
*** 3581,3585 ****
|
||||
WORD_LIST *words;
|
||||
{
|
||||
! WORD_LIST *w;
|
||||
struct builtin *b;
|
||||
int assoc, global;
|
||||
--- 3707,3711 ----
|
||||
WORD_LIST *words;
|
||||
{
|
||||
! WORD_LIST *w, *wcmd;
|
||||
struct builtin *b;
|
||||
int assoc, global;
|
||||
***************
|
||||
*** 3591,3594 ****
|
||||
--- 3717,3721 ----
|
||||
assoc = global = 0;
|
||||
|
||||
+ wcmd = words;
|
||||
for (w = words; w; w = w->next)
|
||||
if (w->word->flags & W_ASSIGNMENT)
|
||||
***************
|
||||
*** 3596,3604 ****
|
||||
if (b == 0)
|
||||
{
|
||||
! b = builtin_address_internal (words->word->word, 0);
|
||||
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
|
||||
return;
|
||||
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
|
||||
! words->word->flags |= W_ASSNBLTIN;
|
||||
}
|
||||
w->word->flags |= (W_NOSPLIT|W_NOGLOB|W_TILDEEXP|W_ASSIGNARG);
|
||||
--- 3723,3738 ----
|
||||
if (b == 0)
|
||||
{
|
||||
! /* Posix (post-2008) says that `command' doesn't change whether
|
||||
! or not the builtin it shadows is a `declaration command', even
|
||||
! though it removes other special builtin properties. In Posix
|
||||
! mode, we skip over one or more instances of `command' and
|
||||
! deal with the next word as the assignment builtin. */
|
||||
! while (posixly_correct && wcmd && wcmd->word && wcmd->word->word && STREQ (wcmd->word->word, "command"))
|
||||
! wcmd = wcmd->next;
|
||||
! b = builtin_address_internal (wcmd->word->word, 0);
|
||||
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
|
||||
return;
|
||||
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
|
||||
! wcmd->word->flags |= W_ASSNBLTIN;
|
||||
}
|
||||
w->word->flags |= (W_NOSPLIT|W_NOGLOB|W_TILDEEXP|W_ASSIGNARG);
|
||||
***************
|
||||
*** 3620,3632 ****
|
||||
if (b == 0)
|
||||
{
|
||||
! b = builtin_address_internal (words->word->word, 0);
|
||||
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
|
||||
return;
|
||||
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
|
||||
! words->word->flags |= W_ASSNBLTIN;
|
||||
}
|
||||
! if ((words->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
|
||||
assoc = 1;
|
||||
! if ((words->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
|
||||
global = 1;
|
||||
}
|
||||
--- 3754,3768 ----
|
||||
if (b == 0)
|
||||
{
|
||||
! while (posixly_correct && wcmd && wcmd->word && wcmd->word->word && STREQ (wcmd->word->word, "command"))
|
||||
! wcmd = wcmd->next;
|
||||
! b = builtin_address_internal (wcmd->word->word, 0);
|
||||
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
|
||||
return;
|
||||
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
|
||||
! wcmd->word->flags |= W_ASSNBLTIN;
|
||||
}
|
||||
! if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
|
||||
assoc = 1;
|
||||
! if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
|
||||
global = 1;
|
||||
}
|
||||
***************
|
||||
*** 3640,3645 ****
|
||||
{
|
||||
char *temp;
|
||||
! temp = search_for_command (pathname);
|
||||
! return (temp ? file_isdir (temp) : file_isdir (pathname));
|
||||
}
|
||||
|
||||
--- 3776,3785 ----
|
||||
{
|
||||
char *temp;
|
||||
! int ret;
|
||||
!
|
||||
! temp = search_for_command (pathname, 0);
|
||||
! ret = (temp ? file_isdir (temp) : file_isdir (pathname));
|
||||
! free (temp);
|
||||
! return ret;
|
||||
}
|
||||
|
||||
***************
|
||||
*** 3665,3668 ****
|
||||
--- 3805,3810 ----
|
||||
command_line = (char *)0;
|
||||
|
||||
+ QUIT;
|
||||
+
|
||||
/* If we're in a function, update the line number information. */
|
||||
if (variable_context && interactive_shell && sourcelevel == 0)
|
||||
***************
|
||||
*** 3981,3985 ****
|
||||
|
||||
if (command_line == 0)
|
||||
! command_line = savestring (the_printed_command_except_trap);
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
--- 4123,4127 ----
|
||||
|
||||
if (command_line == 0)
|
||||
! command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : "");
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
***************
|
||||
*** 4042,4050 ****
|
||||
char *error_trap;
|
||||
|
||||
- #if 0
|
||||
- /* XXX -- added 12/11 */
|
||||
- terminate_immediately++;
|
||||
- #endif
|
||||
-
|
||||
error_trap = 0;
|
||||
old_e_flag = exit_immediately_on_error;
|
||||
--- 4184,4187 ----
|
||||
***************
|
||||
*** 4128,4136 ****
|
||||
}
|
||||
|
||||
- #if 0
|
||||
- /* XXX -- added 12/11 */
|
||||
- terminate_immediately--;
|
||||
- #endif
|
||||
-
|
||||
return (result);
|
||||
}
|
||||
--- 4265,4268 ----
|
||||
***************
|
||||
*** 4160,4164 ****
|
||||
if (funcnest_max > 0 && funcnest >= funcnest_max)
|
||||
{
|
||||
! internal_error ("%s: maximum function nesting level exceeded (%d)", var->name, funcnest);
|
||||
funcnest = 0; /* XXX - should we reset it somewhere else? */
|
||||
jump_to_top_level (DISCARD);
|
||||
--- 4292,4296 ----
|
||||
if (funcnest_max > 0 && funcnest >= funcnest_max)
|
||||
{
|
||||
! internal_error (_("%s: maximum function nesting level exceeded (%d)"), var->name, funcnest);
|
||||
funcnest = 0; /* XXX - should we reset it somewhere else? */
|
||||
jump_to_top_level (DISCARD);
|
||||
***************
|
||||
*** 4252,4260 ****
|
||||
shell_fn = find_function_def (this_shell_function->name);
|
||||
sfile = shell_fn ? shell_fn->source_file : "";
|
||||
! array_push (funcname_a, this_shell_function->name);
|
||||
|
||||
! array_push (bash_source_a, sfile);
|
||||
t = itos (executing_line_number ());
|
||||
! array_push (bash_lineno_a, t);
|
||||
free (t);
|
||||
#endif
|
||||
--- 4384,4392 ----
|
||||
shell_fn = find_function_def (this_shell_function->name);
|
||||
sfile = shell_fn ? shell_fn->source_file : "";
|
||||
! array_push ((ARRAY *)funcname_a, this_shell_function->name);
|
||||
|
||||
! array_push ((ARRAY *)bash_source_a, sfile);
|
||||
t = itos (executing_line_number ());
|
||||
! array_push ((ARRAY *)bash_lineno_a, t);
|
||||
free (t);
|
||||
#endif
|
||||
***************
|
||||
*** 4331,4336 ****
|
||||
/* These two variables cannot be unset, and cannot be affected by the
|
||||
function. */
|
||||
! array_pop (bash_source_a);
|
||||
! array_pop (bash_lineno_a);
|
||||
|
||||
/* FUNCNAME can be unset, and so can potentially be changed by the
|
||||
--- 4463,4468 ----
|
||||
/* These two variables cannot be unset, and cannot be affected by the
|
||||
function. */
|
||||
! array_pop ((ARRAY *)bash_source_a);
|
||||
! array_pop ((ARRAY *)bash_lineno_a);
|
||||
|
||||
/* FUNCNAME can be unset, and so can potentially be changed by the
|
||||
***************
|
||||
*** 4666,4670 ****
|
||||
#endif /* RESTRICTED_SHELL */
|
||||
|
||||
! command = search_for_command (pathname);
|
||||
|
||||
if (command)
|
||||
--- 4798,4802 ----
|
||||
#endif /* RESTRICTED_SHELL */
|
||||
|
||||
! command = search_for_command (pathname, 1);
|
||||
|
||||
if (command)
|
||||
***************
|
||||
*** 4765,4768 ****
|
||||
--- 4897,4902 ----
|
||||
{
|
||||
parent_return:
|
||||
+ QUIT;
|
||||
+
|
||||
/* Make sure that the pipes are closed in the parent. */
|
||||
close_pipes (pipe_in, pipe_out);
|
||||
***************
|
||||
*** 4993,4996 ****
|
||||
--- 5127,5131 ----
|
||||
#if defined (HAVE_HASH_BANG_EXEC)
|
||||
READ_SAMPLE_BUF (command, sample, sample_len);
|
||||
+ sample[sample_len - 1] = '\0';
|
||||
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
|
||||
{
|
||||
***************
|
||||
*** 5045,5049 ****
|
||||
if (check_binary_file (sample, sample_len))
|
||||
{
|
||||
! internal_error (_("%s: cannot execute binary file"), command);
|
||||
return (EX_BINARY_FILE);
|
||||
}
|
||||
--- 5180,5184 ----
|
||||
if (check_binary_file (sample, sample_len))
|
||||
{
|
||||
! internal_error (_("%s: cannot execute binary file: %s"), command, strerror (i));
|
||||
return (EX_BINARY_FILE);
|
||||
}
|
||||
***************
|
||||
*** 5098,5104 ****
|
||||
|
||||
static int
|
||||
! execute_intern_function (name, function)
|
||||
WORD_DESC *name;
|
||||
! COMMAND *function;
|
||||
{
|
||||
SHELL_VAR *var;
|
||||
--- 5233,5239 ----
|
||||
|
||||
static int
|
||||
! execute_intern_function (name, funcdef)
|
||||
WORD_DESC *name;
|
||||
! FUNCTION_DEF *funcdef;
|
||||
{
|
||||
SHELL_VAR *var;
|
||||
***************
|
||||
*** 5114,5117 ****
|
||||
--- 5249,5260 ----
|
||||
}
|
||||
|
||||
+ /* Posix interpretation 383 */
|
||||
+ if (posixly_correct && find_special_builtin (name->word))
|
||||
+ {
|
||||
+ internal_error (_("`%s': is a special builtin"), name->word);
|
||||
+ last_command_exit_value = EX_BADUSAGE;
|
||||
+ jump_to_top_level (ERREXIT);
|
||||
+ }
|
||||
+
|
||||
var = find_function (name->word);
|
||||
if (var && (readonly_p (var) || noassign_p (var)))
|
||||
***************
|
||||
*** 5122,5126 ****
|
||||
}
|
||||
|
||||
! bind_function (name->word, function);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
--- 5265,5273 ----
|
||||
}
|
||||
|
||||
! #if defined (DEBUGGER)
|
||||
! bind_function_def (name->word, funcdef);
|
||||
! #endif
|
||||
!
|
||||
! bind_function (name->word, funcdef->command);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
+20
-11
@@ -33,7 +33,7 @@
|
||||
#include "filecntl.h"
|
||||
#include "posixstat.h"
|
||||
#include <signal.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
@@ -539,15 +539,10 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
volatile char *ofifo_list;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (command == 0 || breaking || continuing || read_but_dont_execute)
|
||||
return (EXECUTION_SUCCESS);
|
||||
#else
|
||||
if (breaking || continuing)
|
||||
return (last_command_exit_value);
|
||||
if (command == 0 || read_but_dont_execute)
|
||||
return (EXECUTION_SUCCESS);
|
||||
#endif
|
||||
|
||||
QUIT;
|
||||
run_pending_traps ();
|
||||
@@ -586,6 +581,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
(pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
|
||||
{
|
||||
pid_t paren_pid;
|
||||
int s;
|
||||
|
||||
/* Fork a subshell, turn off the subshell bit, turn off job
|
||||
control and call execute_command () on the command again. */
|
||||
@@ -593,8 +589,19 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
paren_pid = make_child (savestring (make_command_string (command)),
|
||||
asynchronous);
|
||||
if (paren_pid == 0)
|
||||
exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
|
||||
/* NOTREACHED */
|
||||
{
|
||||
/* We want to run the exit trap for forced {} subshells, and we
|
||||
want to note this before execute_in_subshell modifies the
|
||||
COMMAND struct. Need to keep in mind that execute_in_subshell
|
||||
runs the exit trap for () subshells itself. */
|
||||
s = user_subshell == 0 && command->type == cm_group && pipe_in == NO_PIPE && pipe_out == NO_PIPE && asynchronous;
|
||||
last_command_exit_value = execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close);
|
||||
if (s)
|
||||
subshell_exit (last_command_exit_value);
|
||||
else
|
||||
exit (last_command_exit_value);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
else
|
||||
{
|
||||
close_pipes (pipe_in, pipe_out);
|
||||
@@ -775,9 +782,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
the child. */
|
||||
|
||||
/* XXX - this is something to watch out for if there are problems
|
||||
when the shell is compiled without job control. */
|
||||
if (already_making_children && pipe_out == NO_PIPE &&
|
||||
last_made_pid != last_pid)
|
||||
when the shell is compiled without job control. Don't worry about
|
||||
whether or not last_made_pid == last_pid; already_making_children
|
||||
tells us whether or not there are unwaited-for children to wait
|
||||
for and reap. */
|
||||
if (already_making_children && pipe_out == NO_PIPE)
|
||||
{
|
||||
stop_pipeline (asynchronous, (COMMAND *)NULL);
|
||||
|
||||
|
||||
+5354
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,7 @@ extern void xtrace_print_cond_term __P((int, int, WORD_DESC *, char *, char *));
|
||||
/* Functions from shell.c. */
|
||||
extern void exit_shell __P((int)) __attribute__((__noreturn__));
|
||||
extern void sh_exit __P((int)) __attribute__((__noreturn__));
|
||||
extern void subshell_exit __P((int)) __attribute__((__noreturn__));
|
||||
extern void disable_priv_mode __P((void));
|
||||
extern void unbind_args __P((void));
|
||||
|
||||
|
||||
+502
@@ -0,0 +1,502 @@
|
||||
/* externs.h -- extern function declarations which do not appear in their
|
||||
own header file. */
|
||||
|
||||
/* Copyright (C) 1993-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Make sure that this is included *after* config.h! */
|
||||
|
||||
#if !defined (_EXTERNS_H_)
|
||||
# define _EXTERNS_H_
|
||||
|
||||
#include "stdc.h"
|
||||
|
||||
/* Functions from expr.c. */
|
||||
extern intmax_t evalexp __P((char *, int *));
|
||||
|
||||
/* Functions from print_cmd.c. */
|
||||
#define FUNC_MULTILINE 0x01
|
||||
#define FUNC_EXTERNAL 0x02
|
||||
|
||||
extern char *make_command_string __P((COMMAND *));
|
||||
extern char *named_function_string __P((char *, COMMAND *, int));
|
||||
|
||||
extern void print_command __P((COMMAND *));
|
||||
extern void print_simple_command __P((SIMPLE_COM *));
|
||||
extern void print_word_list __P((WORD_LIST *, char *));
|
||||
|
||||
/* debugger support */
|
||||
extern void print_for_command_head __P((FOR_COM *));
|
||||
#if defined (SELECT_COMMAND)
|
||||
extern void print_select_command_head __P((SELECT_COM *));
|
||||
#endif
|
||||
extern void print_case_command_head __P((CASE_COM *));
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
extern void print_arith_command __P((WORD_LIST *));
|
||||
#endif
|
||||
#if defined (COND_COMMAND)
|
||||
extern void print_cond_command __P((COND_COM *));
|
||||
#endif
|
||||
|
||||
/* set -x support */
|
||||
extern void xtrace_init __P((void));
|
||||
#ifdef NEED_XTRACE_SET_DECL
|
||||
extern void xtrace_set __P((int, FILE *));
|
||||
#endif
|
||||
extern void xtrace_fdchk __P((int));
|
||||
extern void xtrace_reset __P((void));
|
||||
extern char *indirection_level_string __P((void));
|
||||
extern void xtrace_print_assignment __P((char *, char *, int, int));
|
||||
extern void xtrace_print_word_list __P((WORD_LIST *, int));
|
||||
extern void xtrace_print_for_command_head __P((FOR_COM *));
|
||||
#if defined (SELECT_COMMAND)
|
||||
extern void xtrace_print_select_command_head __P((SELECT_COM *));
|
||||
#endif
|
||||
extern void xtrace_print_case_command_head __P((CASE_COM *));
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
extern void xtrace_print_arith_cmd __P((WORD_LIST *));
|
||||
#endif
|
||||
#if defined (COND_COMMAND)
|
||||
extern void xtrace_print_cond_term __P((int, int, WORD_DESC *, char *, char *));
|
||||
#endif
|
||||
|
||||
/* Functions from shell.c. */
|
||||
extern void exit_shell __P((int)) __attribute__((__noreturn__));
|
||||
extern void sh_exit __P((int)) __attribute__((__noreturn__));
|
||||
extern void disable_priv_mode __P((void));
|
||||
extern void unbind_args __P((void));
|
||||
|
||||
#if defined (RESTRICTED_SHELL)
|
||||
extern int shell_is_restricted __P((char *));
|
||||
extern int maybe_make_restricted __P((char *));
|
||||
#endif
|
||||
|
||||
extern void unset_bash_input __P((int));
|
||||
extern void get_current_user_info __P((void));
|
||||
|
||||
/* Functions from eval.c. */
|
||||
extern int reader_loop __P((void));
|
||||
extern int parse_command __P((void));
|
||||
extern int read_command __P((void));
|
||||
|
||||
/* Functions from braces.c. */
|
||||
#if defined (BRACE_EXPANSION)
|
||||
extern char **brace_expand __P((char *));
|
||||
#endif
|
||||
|
||||
/* Miscellaneous functions from parse.y */
|
||||
extern int yyparse __P((void));
|
||||
extern int return_EOF __P((void));
|
||||
extern char *xparse_dolparen __P((char *, char *, int *, int));
|
||||
extern void reset_parser __P((void));
|
||||
extern WORD_LIST *parse_string_to_word_list __P((char *, int, const char *));
|
||||
|
||||
extern int parser_in_command_position __P((void));
|
||||
|
||||
extern void free_pushed_string_input __P((void));
|
||||
|
||||
extern char *decode_prompt_string __P((char *));
|
||||
|
||||
extern int get_current_prompt_level __P((void));
|
||||
extern void set_current_prompt_level __P((int));
|
||||
|
||||
#if defined (HISTORY)
|
||||
extern char *history_delimiting_chars __P((const char *));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in locale.c */
|
||||
extern void set_default_locale __P((void));
|
||||
extern void set_default_locale_vars __P((void));
|
||||
extern int set_locale_var __P((char *, char *));
|
||||
extern int set_lang __P((char *, char *));
|
||||
extern void set_default_lang __P((void));
|
||||
extern char *get_locale_var __P((char *));
|
||||
extern char *localetrans __P((char *, int, int *));
|
||||
extern char *mk_msgstr __P((char *, int *));
|
||||
extern char *localeexpand __P((char *, int, int, int, int *));
|
||||
|
||||
/* Declarations for functions defined in list.c. */
|
||||
extern void list_walk __P((GENERIC_LIST *, sh_glist_func_t *));
|
||||
extern void wlist_walk __P((WORD_LIST *, sh_icpfunc_t *));
|
||||
extern GENERIC_LIST *list_reverse ();
|
||||
extern int list_length ();
|
||||
extern GENERIC_LIST *list_append ();
|
||||
extern GENERIC_LIST *list_remove ();
|
||||
|
||||
/* Declarations for functions defined in stringlib.c */
|
||||
extern int find_string_in_alist __P((char *, STRING_INT_ALIST *, int));
|
||||
extern char *find_token_in_alist __P((int, STRING_INT_ALIST *, int));
|
||||
extern int find_index_in_alist __P((char *, STRING_INT_ALIST *, int));
|
||||
|
||||
extern char *substring __P((const char *, int, int));
|
||||
extern char *strsub __P((char *, char *, char *, int));
|
||||
extern char *strcreplace __P((char *, int, char *, int));
|
||||
extern void strip_leading __P((char *));
|
||||
extern void strip_trailing __P((char *, int, int));
|
||||
extern void xbcopy __P((char *, char *, int));
|
||||
|
||||
/* Functions from version.c. */
|
||||
extern char *shell_version_string __P((void));
|
||||
extern void show_shell_version __P((int));
|
||||
|
||||
/* Functions from the bash library, lib/sh/libsh.a. These should really
|
||||
go into a separate include file. */
|
||||
|
||||
/* declarations for functions defined in lib/sh/casemod.c */
|
||||
extern char *sh_modcase __P((const char *, char *, int));
|
||||
|
||||
/* Defines for flags argument to sh_modcase. These need to agree with what's
|
||||
in lib/sh/casemode.c */
|
||||
#define CASE_LOWER 0x0001
|
||||
#define CASE_UPPER 0x0002
|
||||
#define CASE_CAPITALIZE 0x0004
|
||||
#define CASE_UNCAP 0x0008
|
||||
#define CASE_TOGGLE 0x0010
|
||||
#define CASE_TOGGLEALL 0x0020
|
||||
#define CASE_UPFIRST 0x0040
|
||||
#define CASE_LOWFIRST 0x0080
|
||||
|
||||
#define CASE_USEWORDS 0x1000
|
||||
|
||||
/* declarations for functions defined in lib/sh/clktck.c */
|
||||
extern long get_clk_tck __P((void));
|
||||
|
||||
/* declarations for functions defined in lib/sh/clock.c */
|
||||
extern void clock_t_to_secs ();
|
||||
extern void print_clock_t ();
|
||||
|
||||
/* Declarations for functions defined in lib/sh/dprintf.c */
|
||||
#if !defined (HAVE_DPRINTF)
|
||||
extern void dprintf __P((int, const char *, ...)) __attribute__((__format__ (printf, 2, 3)));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fmtulong.c */
|
||||
#define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */
|
||||
#define FL_ADDBASE 0x02 /* add base# prefix to converted value */
|
||||
#define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */
|
||||
#define FL_UNSIGNED 0x08 /* don't add any sign */
|
||||
|
||||
extern char *fmtulong __P((unsigned long int, int, char *, size_t, int));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fmtulong.c */
|
||||
#if defined (HAVE_LONG_LONG)
|
||||
extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fmtumax.c */
|
||||
extern char *fmtumax __P((uintmax_t, int, char *, size_t, int));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fnxform.c */
|
||||
extern char *fnx_fromfs __P((char *, size_t));
|
||||
extern char *fnx_tofs __P((char *, size_t));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fpurge.c */
|
||||
|
||||
#if defined NEED_FPURGE_DECL
|
||||
#if !HAVE_DECL_FPURGE
|
||||
|
||||
#if HAVE_FPURGE
|
||||
# define fpurge _bash_fpurge
|
||||
#endif
|
||||
extern int fpurge __P((FILE *stream));
|
||||
|
||||
#endif /* HAVE_DECL_FPURGE */
|
||||
#endif /* NEED_FPURGE_DECL */
|
||||
|
||||
/* Declarations for functions defined in lib/sh/getcwd.c */
|
||||
#if !defined (HAVE_GETCWD)
|
||||
extern char *getcwd __P((char *, size_t));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in lib/sh/input_avail.c */
|
||||
extern int input_avail __P((int));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/itos.c */
|
||||
extern char *inttostr __P((intmax_t, char *, size_t));
|
||||
extern char *itos __P((intmax_t));
|
||||
extern char *uinttostr __P((uintmax_t, char *, size_t));
|
||||
extern char *uitos __P((uintmax_t));
|
||||
|
||||
/* declarations for functions defined in lib/sh/makepath.c */
|
||||
#define MP_DOTILDE 0x01
|
||||
#define MP_DOCWD 0x02
|
||||
#define MP_RMDOT 0x04
|
||||
#define MP_IGNDOT 0x08
|
||||
|
||||
extern char *sh_makepath __P((const char *, const char *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/mbscasecmp.c */
|
||||
#if !defined (HAVE_MBSCASECMP)
|
||||
extern char *mbscasecmp __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/mbschr.c */
|
||||
#if !defined (HAVE_MBSCHR)
|
||||
extern char *mbschr __P((const char *, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/mbscmp.c */
|
||||
#if !defined (HAVE_MBSCMP)
|
||||
extern char *mbscmp __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/netconn.c */
|
||||
extern int isnetconn __P((int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/netopen.c */
|
||||
extern int netopen __P((char *));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/oslib.c */
|
||||
|
||||
#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN)
|
||||
extern int dup2 __P((int, int));
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_GETDTABLESIZE)
|
||||
extern int getdtablesize __P((void));
|
||||
#endif /* !HAVE_GETDTABLESIZE */
|
||||
|
||||
#if !defined (HAVE_GETHOSTNAME)
|
||||
extern int gethostname __P((char *, int));
|
||||
#endif /* !HAVE_GETHOSTNAME */
|
||||
|
||||
extern int getmaxgroups __P((void));
|
||||
extern long getmaxchild __P((void));
|
||||
|
||||
/* declarations for functions defined in lib/sh/pathcanon.c */
|
||||
#define PATH_CHECKDOTDOT 0x0001
|
||||
#define PATH_CHECKEXISTS 0x0002
|
||||
#define PATH_HARDPATH 0x0004
|
||||
#define PATH_NOALLOC 0x0008
|
||||
|
||||
extern char *sh_canonpath __P((char *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/pathphys.c */
|
||||
extern char *sh_physpath __P((char *, int));
|
||||
extern char *sh_realpath __P((const char *, char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/setlinebuf.c */
|
||||
#ifdef NEED_SH_SETLINEBUF_DECL
|
||||
extern int sh_setlinebuf __P((FILE *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/shaccess.c */
|
||||
extern int sh_eaccess __P((char *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/shmatch.c */
|
||||
extern int sh_regmatch __P((const char *, const char *, int));
|
||||
|
||||
/* defines for flags argument to sh_regmatch. */
|
||||
#define SHMAT_SUBEXP 0x001 /* save subexpressions in SH_REMATCH */
|
||||
#define SHMAT_PWARN 0x002 /* print a warning message on invalid regexp */
|
||||
|
||||
/* declarations for functions defined in lib/sh/shmbchar.c */
|
||||
extern size_t mbstrlen __P((const char *));
|
||||
extern char *mbsmbchar __P((const char *));
|
||||
extern int sh_mbsnlen __P((const char *, size_t, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/shquote.c */
|
||||
extern char *sh_single_quote __P((const char *));
|
||||
extern char *sh_double_quote __P((const char *));
|
||||
extern char *sh_mkdoublequoted __P((const char *, int, int));
|
||||
extern char *sh_un_double_quote __P((char *));
|
||||
extern char *sh_backslash_quote __P((char *, const char *, int));
|
||||
extern char *sh_backslash_quote_for_double_quotes __P((char *));
|
||||
extern int sh_contains_shell_metas __P((char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/spell.c */
|
||||
extern int spname __P((char *, char *));
|
||||
extern char *dirspell __P((char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/strcasecmp.c */
|
||||
#if !defined (HAVE_STRCASECMP)
|
||||
extern int strncasecmp __P((const char *, const char *, int));
|
||||
extern int strcasecmp __P((const char *, const char *));
|
||||
#endif /* HAVE_STRCASECMP */
|
||||
|
||||
/* declarations for functions defined in lib/sh/strcasestr.c */
|
||||
#if ! HAVE_STRCASESTR
|
||||
extern char *strcasestr __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strchrnul.c */
|
||||
#if ! HAVE_STRCHRNUL
|
||||
extern char *strchrnul __P((const char *, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strerror.c */
|
||||
#if !defined (HAVE_STRERROR) && !defined (strerror)
|
||||
extern char *strerror __P((int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strftime.c */
|
||||
#if !defined (HAVE_STRFTIME) && defined (NEED_STRFTIME_DECL)
|
||||
extern size_t strftime __P((char *, size_t, const char *, const struct tm *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions and structures defined in lib/sh/stringlist.c */
|
||||
|
||||
/* This is a general-purpose argv-style array struct. */
|
||||
typedef struct _list_of_strings {
|
||||
char **list;
|
||||
int list_size;
|
||||
int list_len;
|
||||
} STRINGLIST;
|
||||
|
||||
typedef int sh_strlist_map_func_t __P((char *));
|
||||
|
||||
extern STRINGLIST *strlist_create __P((int));
|
||||
extern STRINGLIST *strlist_resize __P((STRINGLIST *, int));
|
||||
extern void strlist_flush __P((STRINGLIST *));
|
||||
extern void strlist_dispose __P((STRINGLIST *));
|
||||
extern int strlist_remove __P((STRINGLIST *, char *));
|
||||
extern STRINGLIST *strlist_copy __P((STRINGLIST *));
|
||||
extern STRINGLIST *strlist_merge __P((STRINGLIST *, STRINGLIST *));
|
||||
extern STRINGLIST *strlist_append __P((STRINGLIST *, STRINGLIST *));
|
||||
extern STRINGLIST *strlist_prefix_suffix __P((STRINGLIST *, char *, char *));
|
||||
extern void strlist_print __P((STRINGLIST *, char *));
|
||||
extern void strlist_walk __P((STRINGLIST *, sh_strlist_map_func_t *));
|
||||
extern void strlist_sort __P((STRINGLIST *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/stringvec.c */
|
||||
|
||||
extern char **strvec_create __P((int));
|
||||
extern char **strvec_resize __P((char **, int));
|
||||
extern void strvec_flush __P((char **));
|
||||
extern void strvec_dispose __P((char **));
|
||||
extern int strvec_remove __P((char **, char *));
|
||||
extern int strvec_len __P((char **));
|
||||
extern int strvec_search __P((char **, char *));
|
||||
extern char **strvec_copy __P((char **));
|
||||
extern int strvec_strcmp __P((char **, char **));
|
||||
extern void strvec_sort __P((char **));
|
||||
|
||||
extern char **strvec_from_word_list __P((WORD_LIST *, int, int, int *));
|
||||
extern WORD_LIST *strvec_to_word_list __P((char **, int, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/strnlen.c */
|
||||
#if !defined (HAVE_STRNLEN)
|
||||
extern size_t strnlen __P((const char *, size_t));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strpbrk.c */
|
||||
#if !defined (HAVE_STRPBRK)
|
||||
extern char *strpbrk __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtod.c */
|
||||
#if !defined (HAVE_STRTOD)
|
||||
extern double strtod __P((const char *, char **));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtol.c */
|
||||
#if !HAVE_DECL_STRTOL
|
||||
extern long strtol __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtoll.c */
|
||||
#if defined (HAVE_LONG_LONG) && !HAVE_DECL_STRTOLL
|
||||
extern long long strtoll __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtoul.c */
|
||||
#if !HAVE_DECL_STRTOUL
|
||||
extern unsigned long strtoul __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtoull.c */
|
||||
#if defined (HAVE_LONG_LONG) && !HAVE_DECL_STRTOULL
|
||||
extern unsigned long long strtoull __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strimax.c */
|
||||
#if !HAVE_DECL_STRTOIMAX
|
||||
extern intmax_t strtoimax __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strumax.c */
|
||||
#if !HAVE_DECL_STRTOUMAX
|
||||
extern uintmax_t strtoumax __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtrans.c */
|
||||
extern char *ansicstr __P((char *, int, int, int *, int *));
|
||||
extern char *ansic_quote __P((char *, int, int *));
|
||||
extern int ansic_shouldquote __P((const char *));
|
||||
extern char *ansiexpand __P((char *, int, int, int *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/timeval.c. No prototypes
|
||||
so we don't have to count on having a definition of struct timeval in
|
||||
scope when this file is included. */
|
||||
extern void timeval_to_secs ();
|
||||
extern void print_timeval ();
|
||||
|
||||
/* declarations for functions defined in lib/sh/tmpfile.c */
|
||||
#define MT_USETMPDIR 0x0001
|
||||
#define MT_READWRITE 0x0002
|
||||
#define MT_USERANDOM 0x0004
|
||||
|
||||
extern char *sh_mktmpname __P((char *, int));
|
||||
extern int sh_mktmpfd __P((char *, int, char **));
|
||||
/* extern FILE *sh_mktmpfp __P((char *, int, char **)); */
|
||||
|
||||
/* declarations for functions defined in lib/sh/uconvert.c */
|
||||
extern int uconvert __P((char *, long *, long *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/ufuncs.c */
|
||||
extern unsigned int falarm __P((unsigned int, unsigned int));
|
||||
extern unsigned int fsleep __P((unsigned int, unsigned int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/unicode.c */
|
||||
extern int u32cconv __P((unsigned long, char *));
|
||||
extern void u32reset __P((void));
|
||||
|
||||
/* declarations for functions defined in lib/sh/winsize.c */
|
||||
extern void get_new_window_size __P((int, int *, int *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zcatfd.c */
|
||||
extern int zcatfd __P((int, int, char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zgetline.c */
|
||||
extern ssize_t zgetline __P((int, char **, size_t *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zmapfd.c */
|
||||
extern int zmapfd __P((int, char **, char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zread.c */
|
||||
extern ssize_t zread __P((int, char *, size_t));
|
||||
extern ssize_t zreadretry __P((int, char *, size_t));
|
||||
extern ssize_t zreadintr __P((int, char *, size_t));
|
||||
extern ssize_t zreadc __P((int, char *));
|
||||
extern ssize_t zreadcintr __P((int, char *));
|
||||
extern void zreset __P((void));
|
||||
extern void zsyncfd __P((int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zwrite.c */
|
||||
extern int zwrite __P((int, char *, size_t));
|
||||
|
||||
/* declarations for functions defined in lib/glob/gmisc.c */
|
||||
extern int match_pattern_char __P((char *, char *));
|
||||
extern int umatchlen __P((char *, size_t));
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
extern int match_pattern_wchar __P((wchar_t *, wchar_t *));
|
||||
extern int wmatchlen __P((wchar_t *, size_t));
|
||||
#endif
|
||||
|
||||
#endif /* _EXTERNS_H_ */
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "bashtypes.h"
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include "posixstat.h"
|
||||
|
||||
+1166
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,7 @@
|
||||
|
||||
#include "filecntl.h"
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
@@ -1898,15 +1898,13 @@ make_child (command, async_p)
|
||||
last_asynchronous_pid = 1;
|
||||
#endif
|
||||
|
||||
if (pid_wrap > 0)
|
||||
delete_old_job (pid);
|
||||
/* Delete the saved status for any job containing this PID in case it's
|
||||
been reused. */
|
||||
delete_old_job (pid);
|
||||
|
||||
#if !defined (RECYCLES_PIDS)
|
||||
/* Only check for saved status if we've saved more than CHILD_MAX
|
||||
statuses, unless the system recycles pids. */
|
||||
if ((js.c_reaped + bgpids.npid) >= js.c_childmax)
|
||||
#endif
|
||||
bgp_delete (pid); /* new process, discard any saved status */
|
||||
/* Perform the check for pid reuse unconditionally. Some systems reuse
|
||||
PIDs before giving a process CHILD_MAX/_SC_CHILD_MAX unique ones. */
|
||||
bgp_delete (pid); /* new process, discard any saved status */
|
||||
|
||||
last_made_pid = pid;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#endif
|
||||
|
||||
#if !defined (getpagesize)
|
||||
# ifndef _MINIX
|
||||
# if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
# endif
|
||||
# if defined (PAGESIZE)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Emulation of getpagesize() for systems that need it.
|
||||
Copyright (C) 1991-2003 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne-Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
# if defined (_SC_PAGESIZE)
|
||||
# define getpagesize() sysconf(_SC_PAGESIZE)
|
||||
# else
|
||||
# if defined (_SC_PAGE_SIZE)
|
||||
# define getpagesize() sysconf(_SC_PAGE_SIZE)
|
||||
# endif /* _SC_PAGE_SIZE */
|
||||
# endif /* _SC_PAGESIZE */
|
||||
#endif
|
||||
|
||||
#if !defined (getpagesize)
|
||||
# ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
# endif
|
||||
# if defined (PAGESIZE)
|
||||
# define getpagesize() PAGESIZE
|
||||
# else /* !PAGESIZE */
|
||||
# if defined (EXEC_PAGESIZE)
|
||||
# define getpagesize() EXEC_PAGESIZE
|
||||
# else /* !EXEC_PAGESIZE */
|
||||
# if defined (NBPG)
|
||||
# if !defined (CLSIZE)
|
||||
# define CLSIZE 1
|
||||
# endif /* !CLSIZE */
|
||||
# define getpagesize() (NBPG * CLSIZE)
|
||||
# else /* !NBPG */
|
||||
# if defined (NBPC)
|
||||
# define getpagesize() NBPC
|
||||
# endif /* NBPC */
|
||||
# endif /* !NBPG */
|
||||
# endif /* !EXEC_PAGESIZE */
|
||||
# endif /* !PAGESIZE */
|
||||
#endif /* !getpagesize */
|
||||
|
||||
#if !defined (getpagesize)
|
||||
# define getpagesize() 4096 /* Just punt and use reasonable value */
|
||||
#endif
|
||||
+3
-1
@@ -21,7 +21,9 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#include <sys/param.h>
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/* clktck.c - get the value of CLK_TCK. */
|
||||
|
||||
/* Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LIMITS_H)
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
|
||||
# if !defined (CLK_TCK)
|
||||
# if defined (HZ)
|
||||
# define CLK_TCK HZ
|
||||
# else
|
||||
# define CLK_TCK 60
|
||||
# endif
|
||||
# endif /* !CLK_TCK */
|
||||
#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */
|
||||
|
||||
long
|
||||
get_clk_tck ()
|
||||
{
|
||||
static long retval = 0;
|
||||
|
||||
if (retval != 0)
|
||||
return (retval);
|
||||
|
||||
#if defined (HAVE_SYSCONF) && defined (_SC_CLK_TCK)
|
||||
retval = sysconf (_SC_CLK_TCK);
|
||||
#else /* !SYSCONF || !_SC_CLK_TCK */
|
||||
retval = CLK_TCK;
|
||||
#endif /* !SYSCONF || !_SC_CLK_TCK */
|
||||
|
||||
return (retval);
|
||||
}
|
||||
@@ -67,7 +67,9 @@ static char rcsid[] = "$Id: inet_addr.c,v 1.5 1996/08/14 03:48:37 drepper Exp $"
|
||||
#if !defined (HAVE_INET_ATON) && defined (HAVE_NETWORK) && defined (HAVE_NETINET_IN_H) && defined (HAVE_ARPA_INET_H)
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/* inet_aton - convert string to numeric IP address */
|
||||
|
||||
/* Snagged from GNU C library, version 2.0.3. */
|
||||
|
||||
/*
|
||||
* ++Copyright++ 1983, 1990, 1993
|
||||
* -
|
||||
* Copyright (c) 1983, 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* -
|
||||
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies, and that
|
||||
* the name of Digital Equipment Corporation not be used in advertising or
|
||||
* publicity pertaining to distribution of the document or software without
|
||||
* specific, written prior permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
|
||||
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
* SOFTWARE.
|
||||
* -
|
||||
* --Copyright--
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93";
|
||||
static char rcsid[] = "$Id: inet_addr.c,v 1.5 1996/08/14 03:48:37 drepper Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if !defined (HAVE_INET_ATON) && defined (HAVE_NETWORK) && defined (HAVE_NETINET_IN_H) && defined (HAVE_ARPA_INET_H)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <bashansi.h>
|
||||
#include <ctype.h>
|
||||
#include <stdc.h>
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
# define INADDR_NONE 0xffffffff
|
||||
#endif
|
||||
|
||||
/* these are compatibility routines, not needed on recent BSD releases */
|
||||
|
||||
#if 0
|
||||
/* Not used, not needed. */
|
||||
/*
|
||||
* Ascii internet address interpretation routine.
|
||||
* The value returned is in network order.
|
||||
*/
|
||||
u_long
|
||||
inet_addr(cp)
|
||||
register const char *cp;
|
||||
{
|
||||
struct in_addr val;
|
||||
|
||||
if (inet_aton(cp, &val))
|
||||
return (val.s_addr);
|
||||
return (INADDR_NONE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check whether "cp" is a valid ascii representation
|
||||
* of an Internet address and convert to a binary address.
|
||||
* Returns 1 if the address is valid, 0 if not.
|
||||
* This replaces inet_addr, the return value from which
|
||||
* cannot distinguish between failure and a local broadcast address.
|
||||
*/
|
||||
int
|
||||
inet_aton(cp, addr)
|
||||
register const char *cp;
|
||||
struct in_addr *addr;
|
||||
{
|
||||
register u_bits32_t val;
|
||||
register int base, n;
|
||||
register unsigned char c;
|
||||
u_int parts[4];
|
||||
register u_int *pp = parts;
|
||||
|
||||
c = *cp;
|
||||
for (;;) {
|
||||
/*
|
||||
* Collect number up to ``.''.
|
||||
* Values are specified as for C:
|
||||
* 0x=hex, 0=octal, isdigit=decimal.
|
||||
*/
|
||||
#if 0
|
||||
if (!isdigit(c))
|
||||
#else
|
||||
if (c != '0' && c != '1' && c != '2' && c != '3' && c != '4' &&
|
||||
c != '5' && c != '6' && c != '7' && c != '8' && c != '9')
|
||||
#endif
|
||||
return (0);
|
||||
val = 0; base = 10;
|
||||
if (c == '0') {
|
||||
c = *++cp;
|
||||
if (c == 'x' || c == 'X')
|
||||
base = 16, c = *++cp;
|
||||
else
|
||||
base = 8;
|
||||
}
|
||||
for (;;) {
|
||||
if (isascii(c) && isdigit(c)) {
|
||||
val = (val * base) + (c - '0');
|
||||
c = *++cp;
|
||||
} else if (base == 16 && isascii(c) && isxdigit(c)) {
|
||||
val = (val << 4) |
|
||||
(c + 10 - (islower(c) ? 'a' : 'A'));
|
||||
c = *++cp;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (c == '.') {
|
||||
/*
|
||||
* Internet format:
|
||||
* a.b.c.d
|
||||
* a.b.c (with c treated as 16 bits)
|
||||
* a.b (with b treated as 24 bits)
|
||||
*/
|
||||
if (pp >= parts + 3)
|
||||
return (0);
|
||||
*pp++ = val;
|
||||
c = *++cp;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Check for trailing characters.
|
||||
*/
|
||||
if (c != '\0' && (!isascii(c) || !isspace(c)))
|
||||
return (0);
|
||||
/*
|
||||
* Concoct the address according to
|
||||
* the number of parts specified.
|
||||
*/
|
||||
n = pp - parts + 1;
|
||||
switch (n) {
|
||||
|
||||
case 0:
|
||||
return (0); /* initial nondigit */
|
||||
|
||||
case 1: /* a -- 32 bits */
|
||||
break;
|
||||
|
||||
case 2: /* a.b -- 8.24 bits */
|
||||
if (val > 0xffffff)
|
||||
return (0);
|
||||
val |= parts[0] << 24;
|
||||
break;
|
||||
|
||||
case 3: /* a.b.c -- 8.8.16 bits */
|
||||
if (val > 0xffff)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16);
|
||||
break;
|
||||
|
||||
case 4: /* a.b.c.d -- 8.8.8.8 bits */
|
||||
if (val > 0xff)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
||||
break;
|
||||
}
|
||||
if (addr)
|
||||
addr->s_addr = htonl(val);
|
||||
return (1);
|
||||
}
|
||||
|
||||
#endif /* !HAVE_INET_ATON */
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
#include <posixdir.h>
|
||||
#include <bashansi.h>
|
||||
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/* mailstat.c -- stat a mailbox file, handling maildir-type mail directories */
|
||||
|
||||
/* Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#include <posixstat.h>
|
||||
#include <posixdir.h>
|
||||
#include <bashansi.h>
|
||||
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#include <maxpath.h>
|
||||
|
||||
/*
|
||||
* Stat a file. If it's a maildir, check all messages
|
||||
* in the maildir and present the grand total as a file.
|
||||
* The fields in the 'struct stat' are from the mail directory.
|
||||
* The following fields are emulated:
|
||||
*
|
||||
* st_nlink always 1, unless st_blocks is not present, in which case it's
|
||||
* the total number of messages
|
||||
* st_size total number of bytes in all files
|
||||
* st_blocks total number of messages, if present in struct stat
|
||||
* st_atime access time of newest file in maildir
|
||||
* st_mtime modify time of newest file in maildir
|
||||
* st_mode S_IFDIR changed to S_IFREG
|
||||
*
|
||||
* This is good enough for most mail-checking applications.
|
||||
*/
|
||||
|
||||
int
|
||||
mailstat(path, st)
|
||||
const char *path;
|
||||
struct stat *st;
|
||||
{
|
||||
static struct stat st_new_last, st_ret_last;
|
||||
struct stat st_ret, st_tmp;
|
||||
DIR *dd;
|
||||
struct dirent *fn;
|
||||
char dir[PATH_MAX * 2], file[PATH_MAX * 2];
|
||||
int i, l;
|
||||
time_t atime, mtime;
|
||||
|
||||
atime = mtime = 0;
|
||||
|
||||
/* First see if it's a directory. */
|
||||
if ((i = stat(path, st)) != 0 || S_ISDIR(st->st_mode) == 0)
|
||||
return i;
|
||||
|
||||
if (strlen(path) > sizeof(dir) - 5)
|
||||
{
|
||||
#ifdef ENAMETOOLONG
|
||||
errno = ENAMETOOLONG;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
st_ret = *st;
|
||||
st_ret.st_nlink = 1;
|
||||
st_ret.st_size = 0;
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
st_ret.st_blocks = 0;
|
||||
#else
|
||||
st_ret.st_nlink = 0;
|
||||
#endif
|
||||
st_ret.st_mode &= ~S_IFDIR;
|
||||
st_ret.st_mode |= S_IFREG;
|
||||
|
||||
/* See if cur/ is present */
|
||||
sprintf(dir, "%s/cur", path);
|
||||
if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
|
||||
return 0;
|
||||
st_ret.st_atime = st_tmp.st_atime;
|
||||
|
||||
/* See if tmp/ is present */
|
||||
sprintf(dir, "%s/tmp", path);
|
||||
if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
|
||||
return 0;
|
||||
st_ret.st_mtime = st_tmp.st_mtime;
|
||||
|
||||
/* And new/ */
|
||||
sprintf(dir, "%s/new", path);
|
||||
if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
|
||||
return 0;
|
||||
st_ret.st_mtime = st_tmp.st_mtime;
|
||||
|
||||
/* Optimization - if new/ didn't change, nothing else did. */
|
||||
if (st_tmp.st_dev == st_new_last.st_dev &&
|
||||
st_tmp.st_ino == st_new_last.st_ino &&
|
||||
st_tmp.st_atime == st_new_last.st_atime &&
|
||||
st_tmp.st_mtime == st_new_last.st_mtime)
|
||||
{
|
||||
*st = st_ret_last;
|
||||
return 0;
|
||||
}
|
||||
st_new_last = st_tmp;
|
||||
|
||||
/* Loop over new/ and cur/ */
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
sprintf(dir, "%s/%s", path, i ? "cur" : "new");
|
||||
sprintf(file, "%s/", dir);
|
||||
l = strlen(file);
|
||||
if ((dd = opendir(dir)) == NULL)
|
||||
return 0;
|
||||
while ((fn = readdir(dd)) != NULL)
|
||||
{
|
||||
if (fn->d_name[0] == '.' || strlen(fn->d_name) + l >= sizeof(file))
|
||||
continue;
|
||||
strcpy(file + l, fn->d_name);
|
||||
if (stat(file, &st_tmp) != 0)
|
||||
continue;
|
||||
st_ret.st_size += st_tmp.st_size;
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
st_ret.st_blocks++;
|
||||
#else
|
||||
st_ret.st_nlink++;
|
||||
#endif
|
||||
if (st_tmp.st_atime != st_tmp.st_mtime && st_tmp.st_atime > atime)
|
||||
atime = st_tmp.st_atime;
|
||||
if (st_tmp.st_mtime > mtime)
|
||||
mtime = st_tmp.st_mtime;
|
||||
}
|
||||
closedir(dd);
|
||||
}
|
||||
|
||||
/* if (atime) */ /* Set atime even if cur/ is empty */
|
||||
st_ret.st_atime = atime;
|
||||
if (mtime)
|
||||
st_ret.st_mtime = mtime;
|
||||
|
||||
*st = st_ret_last = st_ret;
|
||||
return 0;
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
/* oslib.c - functions present only in some unix versions. */
|
||||
|
||||
/* Copyright (C) 1995,2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LIMITS_H)
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
#include <posixstat.h>
|
||||
#include <filecntl.h>
|
||||
#include <bashansi.h>
|
||||
|
||||
#if !defined (HAVE_KILLPG)
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <chartypes.h>
|
||||
|
||||
#include <shell.h>
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
/* Make the functions strchr and strrchr if they do not exist. */
|
||||
#if !defined (HAVE_STRCHR)
|
||||
char *
|
||||
strchr (string, c)
|
||||
char *string;
|
||||
int c;
|
||||
{
|
||||
register char *s;
|
||||
|
||||
for (s = string; s && *s; s++)
|
||||
if (*s == c)
|
||||
return (s);
|
||||
|
||||
return ((char *) NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
strrchr (string, c)
|
||||
char *string;
|
||||
int c;
|
||||
{
|
||||
register char *s, *t;
|
||||
|
||||
for (s = string, t = (char *)NULL; s && *s; s++)
|
||||
if (*s == c)
|
||||
t = s;
|
||||
return (t);
|
||||
}
|
||||
#endif /* !HAVE_STRCHR */
|
||||
|
||||
#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN)
|
||||
/* Replacement for dup2 (), for those systems which either don't have it,
|
||||
or supply one with broken behaviour. */
|
||||
int
|
||||
dup2 (fd1, fd2)
|
||||
int fd1, fd2;
|
||||
{
|
||||
int saved_errno, r;
|
||||
|
||||
/* If FD1 is not a valid file descriptor, then return immediately with
|
||||
an error. */
|
||||
if (fcntl (fd1, F_GETFL, 0) == -1)
|
||||
return (-1);
|
||||
|
||||
if (fd2 < 0 || fd2 >= getdtablesize ())
|
||||
{
|
||||
errno = EBADF;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fd1 == fd2)
|
||||
return (0);
|
||||
|
||||
saved_errno = errno;
|
||||
|
||||
(void) close (fd2);
|
||||
r = fcntl (fd1, F_DUPFD, fd2);
|
||||
|
||||
if (r >= 0)
|
||||
errno = saved_errno;
|
||||
else
|
||||
if (errno == EINVAL)
|
||||
errno = EBADF;
|
||||
|
||||
/* Force the new file descriptor to remain open across exec () calls. */
|
||||
SET_OPEN_ON_EXEC (fd2);
|
||||
return (r);
|
||||
}
|
||||
#endif /* !HAVE_DUP2 */
|
||||
|
||||
/*
|
||||
* Return the total number of available file descriptors.
|
||||
*
|
||||
* On some systems, like 4.2BSD and its descendents, there is a system call
|
||||
* that returns the size of the descriptor table: getdtablesize(). There are
|
||||
* lots of ways to emulate this on non-BSD systems.
|
||||
*
|
||||
* On System V.3, this can be obtained via a call to ulimit:
|
||||
* return (ulimit(4, 0L));
|
||||
*
|
||||
* On other System V systems, NOFILE is defined in /usr/include/sys/param.h
|
||||
* (this is what we assume below), so we can simply use it:
|
||||
* return (NOFILE);
|
||||
*
|
||||
* On POSIX systems, there are specific functions for retrieving various
|
||||
* configuration parameters:
|
||||
* return (sysconf(_SC_OPEN_MAX));
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined (HAVE_GETDTABLESIZE)
|
||||
int
|
||||
getdtablesize ()
|
||||
{
|
||||
# if defined (_POSIX_VERSION) && defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
|
||||
return (sysconf(_SC_OPEN_MAX)); /* Posix systems use sysconf */
|
||||
# else /* ! (_POSIX_VERSION && HAVE_SYSCONF && _SC_OPEN_MAX) */
|
||||
# if defined (ULIMIT_MAXFDS)
|
||||
return (ulimit (4, 0L)); /* System V.3 systems use ulimit(4, 0L) */
|
||||
# else /* !ULIMIT_MAXFDS */
|
||||
# if defined (NOFILE) /* Other systems use NOFILE */
|
||||
return (NOFILE);
|
||||
# else /* !NOFILE */
|
||||
return (20); /* XXX - traditional value is 20 */
|
||||
# endif /* !NOFILE */
|
||||
# endif /* !ULIMIT_MAXFDS */
|
||||
# endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
|
||||
}
|
||||
#endif /* !HAVE_GETDTABLESIZE */
|
||||
|
||||
#if !defined (HAVE_BCOPY)
|
||||
# if defined (bcopy)
|
||||
# undef bcopy
|
||||
# endif
|
||||
void
|
||||
bcopy (s,d,n)
|
||||
char *d, *s;
|
||||
int n;
|
||||
{
|
||||
FASTCOPY (s, d, n);
|
||||
}
|
||||
#endif /* !HAVE_BCOPY */
|
||||
|
||||
#if !defined (HAVE_BZERO)
|
||||
# if defined (bzero)
|
||||
# undef bzero
|
||||
# endif
|
||||
void
|
||||
bzero (s, n)
|
||||
char *s;
|
||||
int n;
|
||||
{
|
||||
register int i;
|
||||
register char *r;
|
||||
|
||||
for (i = 0, r = s; i < n; i++)
|
||||
*r++ = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_GETHOSTNAME)
|
||||
# if defined (HAVE_UNAME)
|
||||
# include <sys/utsname.h>
|
||||
int
|
||||
gethostname (name, namelen)
|
||||
char *name;
|
||||
int namelen;
|
||||
{
|
||||
int i;
|
||||
struct utsname ut;
|
||||
|
||||
--namelen;
|
||||
|
||||
uname (&ut);
|
||||
i = strlen (ut.nodename) + 1;
|
||||
strncpy (name, ut.nodename, i < namelen ? i : namelen);
|
||||
name[namelen] = '\0';
|
||||
return (0);
|
||||
}
|
||||
# else /* !HAVE_UNAME */
|
||||
int
|
||||
gethostname (name, namelen)
|
||||
char *name;
|
||||
int namelen;
|
||||
{
|
||||
strncpy (name, "unknown", namelen);
|
||||
name[namelen] = '\0';
|
||||
return 0;
|
||||
}
|
||||
# endif /* !HAVE_UNAME */
|
||||
#endif /* !HAVE_GETHOSTNAME */
|
||||
|
||||
#if !defined (HAVE_KILLPG)
|
||||
int
|
||||
killpg (pgrp, sig)
|
||||
pid_t pgrp;
|
||||
int sig;
|
||||
{
|
||||
return (kill (-pgrp, sig));
|
||||
}
|
||||
#endif /* !HAVE_KILLPG */
|
||||
|
||||
#if !defined (HAVE_MKFIFO) && defined (PROCESS_SUBSTITUTION)
|
||||
int
|
||||
mkfifo (path, mode)
|
||||
char *path;
|
||||
int mode;
|
||||
{
|
||||
#if defined (S_IFIFO)
|
||||
return (mknod (path, (mode | S_IFIFO), 0));
|
||||
#else /* !S_IFIFO */
|
||||
return (-1);
|
||||
#endif /* !S_IFIFO */
|
||||
}
|
||||
#endif /* !HAVE_MKFIFO && PROCESS_SUBSTITUTION */
|
||||
|
||||
#define DEFAULT_MAXGROUPS 64
|
||||
|
||||
int
|
||||
getmaxgroups ()
|
||||
{
|
||||
static int maxgroups = -1;
|
||||
|
||||
if (maxgroups > 0)
|
||||
return maxgroups;
|
||||
|
||||
#if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
|
||||
maxgroups = sysconf (_SC_NGROUPS_MAX);
|
||||
#else
|
||||
# if defined (NGROUPS_MAX)
|
||||
maxgroups = NGROUPS_MAX;
|
||||
# else /* !NGROUPS_MAX */
|
||||
# if defined (NGROUPS)
|
||||
maxgroups = NGROUPS;
|
||||
# else /* !NGROUPS */
|
||||
maxgroups = DEFAULT_MAXGROUPS;
|
||||
# endif /* !NGROUPS */
|
||||
# endif /* !NGROUPS_MAX */
|
||||
#endif /* !HAVE_SYSCONF || !SC_NGROUPS_MAX */
|
||||
|
||||
if (maxgroups <= 0)
|
||||
maxgroups = DEFAULT_MAXGROUPS;
|
||||
|
||||
return maxgroups;
|
||||
}
|
||||
|
||||
long
|
||||
getmaxchild ()
|
||||
{
|
||||
static long maxchild = -1L;
|
||||
|
||||
if (maxchild > 0)
|
||||
return maxchild;
|
||||
|
||||
#if defined (HAVE_SYSCONF) && defined (_SC_CHILD_MAX)
|
||||
maxchild = sysconf (_SC_CHILD_MAX);
|
||||
#else
|
||||
# if defined (CHILD_MAX)
|
||||
maxchild = CHILD_MAX;
|
||||
# else
|
||||
# if defined (MAXUPRC)
|
||||
maxchild = MAXUPRC;
|
||||
# endif /* MAXUPRC */
|
||||
# endif /* CHILD_MAX */
|
||||
#endif /* !HAVE_SYSCONF || !_SC_CHILD_MAX */
|
||||
|
||||
return (maxchild);
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include <posixstat.h>
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
/* pathcanon.c -- canonicalize and manipulate pathnames. */
|
||||
|
||||
/* Copyright (C) 2000 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include <posixstat.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <filecntl.h>
|
||||
#include <bashansi.h>
|
||||
#include <stdio.h>
|
||||
#include <chartypes.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#if defined (__CYGWIN__)
|
||||
#include <sys/cygwin.h>
|
||||
|
||||
static int
|
||||
_is_cygdrive (path)
|
||||
char *path;
|
||||
{
|
||||
static char user[MAXPATHLEN];
|
||||
static char system[MAXPATHLEN];
|
||||
static int first_time = 1;
|
||||
|
||||
/* If the path is the first part of a network path, treat it as
|
||||
existing. */
|
||||
if (path[0] == '/' && path[1] == '/' && !strchr (path + 2, '/'))
|
||||
return 1;
|
||||
/* Otherwise check for /cygdrive prefix. */
|
||||
if (first_time)
|
||||
{
|
||||
char user_flags[MAXPATHLEN];
|
||||
char system_flags[MAXPATHLEN];
|
||||
/* Get the cygdrive info */
|
||||
cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags);
|
||||
first_time = 0;
|
||||
}
|
||||
return !strcasecmp (path, user) || !strcasecmp (path, system);
|
||||
}
|
||||
#endif /* __CYGWIN__ */
|
||||
|
||||
/* Return 1 if PATH corresponds to a directory. A function for debugging. */
|
||||
static int
|
||||
_path_isdir (path)
|
||||
char *path;
|
||||
{
|
||||
int l;
|
||||
struct stat sb;
|
||||
|
||||
/* This should leave errno set to the correct value. */
|
||||
errno = 0;
|
||||
l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode);
|
||||
#if defined (__CYGWIN__)
|
||||
if (l == 0)
|
||||
l = _is_cygdrive (path);
|
||||
#endif
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Canonicalize PATH, and return a new path. The new path differs from PATH
|
||||
in that:
|
||||
Multple `/'s are collapsed to a single `/'.
|
||||
Leading `./'s and trailing `/.'s are removed.
|
||||
Trailing `/'s are removed.
|
||||
Non-leading `../'s and trailing `..'s are handled by removing
|
||||
portions of the path. */
|
||||
|
||||
/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
|
||||
|
||||
#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
|
||||
|
||||
char *
|
||||
sh_canonpath (path, flags)
|
||||
char *path;
|
||||
int flags;
|
||||
{
|
||||
char stub_char;
|
||||
char *result, *p, *q, *base, *dotdot;
|
||||
int rooted, double_slash_path;
|
||||
|
||||
/* The result cannot be larger than the input PATH. */
|
||||
result = (flags & PATH_NOALLOC) ? path : savestring (path);
|
||||
|
||||
/* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any
|
||||
leading `x:' (dos drive name). */
|
||||
if (rooted = ROOTEDPATH(path))
|
||||
{
|
||||
stub_char = DIRSEP;
|
||||
#if defined (__CYGWIN__)
|
||||
base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 3 : result + 1;
|
||||
#else
|
||||
base = result + 1;
|
||||
#endif
|
||||
double_slash_path = DOUBLE_SLASH (path);
|
||||
base += double_slash_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
stub_char = '.';
|
||||
#if defined (__CYGWIN__)
|
||||
base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 2 : result;
|
||||
#else
|
||||
base = result;
|
||||
#endif
|
||||
double_slash_path = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* invariants:
|
||||
* base points to the portion of the path we want to modify
|
||||
* p points at beginning of path element we're considering.
|
||||
* q points just past the last path element we wrote (no slash).
|
||||
* dotdot points just past the point where .. cannot backtrack
|
||||
* any further (no slash).
|
||||
*/
|
||||
p = q = dotdot = base;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (ISDIRSEP(p[0])) /* null element */
|
||||
p++;
|
||||
else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
|
||||
p += 1; /* don't count the separator in case it is nul */
|
||||
else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
|
||||
{
|
||||
p += 2; /* skip `..' */
|
||||
if (q > dotdot) /* can backtrack */
|
||||
{
|
||||
if (flags & PATH_CHECKDOTDOT)
|
||||
{
|
||||
char c;
|
||||
|
||||
/* Make sure what we have so far corresponds to a valid
|
||||
path before we chop some of it off. */
|
||||
c = *q;
|
||||
*q = '\0';
|
||||
if (_path_isdir (result) == 0)
|
||||
{
|
||||
if ((flags & PATH_NOALLOC) == 0)
|
||||
free (result);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
*q = c;
|
||||
}
|
||||
|
||||
while (--q > dotdot && ISDIRSEP(*q) == 0)
|
||||
;
|
||||
}
|
||||
else if (rooted == 0)
|
||||
{
|
||||
/* /.. is / but ./../ is .. */
|
||||
if (q != base)
|
||||
*q++ = DIRSEP;
|
||||
*q++ = '.';
|
||||
*q++ = '.';
|
||||
dotdot = q;
|
||||
}
|
||||
}
|
||||
else /* real path element */
|
||||
{
|
||||
/* add separator if not at start of work portion of result */
|
||||
if (q != base)
|
||||
*q++ = DIRSEP;
|
||||
while (*p && (ISDIRSEP(*p) == 0))
|
||||
*q++ = *p++;
|
||||
/* Check here for a valid directory with _path_isdir. */
|
||||
if (flags & PATH_CHECKEXISTS)
|
||||
{
|
||||
char c;
|
||||
|
||||
/* Make sure what we have so far corresponds to a valid
|
||||
path before we chop some of it off. */
|
||||
c = *q;
|
||||
*q = '\0';
|
||||
if (_path_isdir (result) == 0)
|
||||
{
|
||||
if ((flags & PATH_NOALLOC) == 0)
|
||||
free (result);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
*q = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Empty string is really ``.'' or `/', depending on what we started with. */
|
||||
if (q == result)
|
||||
*q++ = stub_char;
|
||||
*q = '\0';
|
||||
|
||||
/* If the result starts with `//', but the original path does not, we
|
||||
can turn the // into /. Because of how we set `base', this should never
|
||||
be true, but it's a sanity check. */
|
||||
if (DOUBLE_SLASH(result) && double_slash_path == 0)
|
||||
{
|
||||
if (result[2] == '\0') /* short-circuit for bare `//' */
|
||||
result[1] = '\0';
|
||||
else
|
||||
strcpy (result, result + 1);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include <posixstat.h>
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
/* pathphys.c -- return pathname with all symlinks expanded. */
|
||||
|
||||
/* Copyright (C) 2000 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#include <posixstat.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <filecntl.h>
|
||||
#include <bashansi.h>
|
||||
#include <stdio.h>
|
||||
#include <chartypes.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
#if !defined (MAXSYMLINKS)
|
||||
# define MAXSYMLINKS 32
|
||||
#endif
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
extern char *get_working_directory __P((char *));
|
||||
|
||||
static int
|
||||
_path_readlink (path, buf, bufsiz)
|
||||
char *path;
|
||||
char *buf;
|
||||
int bufsiz;
|
||||
{
|
||||
#ifdef HAVE_READLINK
|
||||
return readlink (path, buf, bufsiz);
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
|
||||
|
||||
#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
|
||||
|
||||
/*
|
||||
* Return PATH with all symlinks expanded in newly-allocated memory.
|
||||
* This always gets an absolute pathname.
|
||||
*/
|
||||
|
||||
char *
|
||||
sh_physpath (path, flags)
|
||||
char *path;
|
||||
int flags;
|
||||
{
|
||||
char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1];
|
||||
char *result, *p, *q, *qsave, *qbase, *workpath;
|
||||
int double_slash_path, linklen, nlink;
|
||||
|
||||
linklen = strlen (path);
|
||||
|
||||
#if 0
|
||||
/* First sanity check -- punt immediately if the name is too long. */
|
||||
if (linklen >= PATH_MAX)
|
||||
return (savestring (path));
|
||||
#endif
|
||||
|
||||
nlink = 0;
|
||||
q = result = (char *)xmalloc (PATH_MAX + 1);
|
||||
|
||||
/* Even if we get something longer than PATH_MAX, we might be able to
|
||||
shorten it, so we try. */
|
||||
if (linklen >= PATH_MAX)
|
||||
workpath = savestring (path);
|
||||
else
|
||||
{
|
||||
workpath = (char *)xmalloc (PATH_MAX + 1);
|
||||
strcpy (workpath, path);
|
||||
}
|
||||
|
||||
/* This always gets an absolute pathname. */
|
||||
|
||||
/* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any
|
||||
leading `x:' (dos drive name). */
|
||||
#if defined (__CYGWIN__)
|
||||
qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
|
||||
#else
|
||||
qbase = workpath + 1;
|
||||
#endif
|
||||
double_slash_path = DOUBLE_SLASH (workpath);
|
||||
qbase += double_slash_path;
|
||||
|
||||
for (p = workpath; p < qbase; )
|
||||
*q++ = *p++;
|
||||
qbase = q;
|
||||
|
||||
/*
|
||||
* invariants:
|
||||
* qbase points to the portion of the result path we want to modify
|
||||
* p points at beginning of path element we're considering.
|
||||
* q points just past the last path element we wrote (no slash).
|
||||
*
|
||||
* XXX -- need to fix error checking for too-long pathnames
|
||||
*/
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (ISDIRSEP(p[0])) /* null element */
|
||||
p++;
|
||||
else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
|
||||
p += 1; /* don't count the separator in case it is nul */
|
||||
else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
|
||||
{
|
||||
p += 2; /* skip `..' */
|
||||
if (q > qbase)
|
||||
{
|
||||
while (--q > qbase && ISDIRSEP(*q) == 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
else /* real path element */
|
||||
{
|
||||
/* add separator if not at start of work portion of result */
|
||||
qsave = q;
|
||||
if (q != qbase)
|
||||
*q++ = DIRSEP;
|
||||
while (*p && (ISDIRSEP(*p) == 0))
|
||||
{
|
||||
if (q - result >= PATH_MAX)
|
||||
{
|
||||
#ifdef ENAMETOOLONG
|
||||
errno = ENAMETOOLONG;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
goto error;
|
||||
}
|
||||
|
||||
*q++ = *p++;
|
||||
}
|
||||
|
||||
*q = '\0';
|
||||
|
||||
linklen = _path_readlink (result, linkbuf, PATH_MAX);
|
||||
if (linklen < 0) /* if errno == EINVAL, it's not a symlink */
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
goto error;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* It's a symlink, and the value is in LINKBUF. */
|
||||
nlink++;
|
||||
if (nlink > MAXSYMLINKS)
|
||||
{
|
||||
#ifdef ELOOP
|
||||
errno = ELOOP;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
error:
|
||||
free (result);
|
||||
free (workpath);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
linkbuf[linklen] = '\0';
|
||||
|
||||
/* If the new path length would overrun PATH_MAX, punt now. */
|
||||
if ((strlen (p) + linklen + 2) >= PATH_MAX)
|
||||
{
|
||||
#ifdef ENAMETOOLONG
|
||||
errno = ENAMETOOLONG;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Form the new pathname by copying the link value to a temporary
|
||||
buffer and appending the rest of `workpath'. Reset p to point
|
||||
to the start of the rest of the path. If the link value is an
|
||||
absolute pathname, reset p, q, and qbase. If not, reset p
|
||||
and q. */
|
||||
strcpy (tbuf, linkbuf);
|
||||
tbuf[linklen] = '/';
|
||||
strcpy (tbuf + linklen, p);
|
||||
strcpy (workpath, tbuf);
|
||||
|
||||
if (ABSPATH(linkbuf))
|
||||
{
|
||||
q = result;
|
||||
/* Duplicating some code here... */
|
||||
#if defined (__CYGWIN__)
|
||||
qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
|
||||
#else
|
||||
qbase = workpath + 1;
|
||||
#endif
|
||||
double_slash_path = DOUBLE_SLASH (workpath);
|
||||
qbase += double_slash_path;
|
||||
|
||||
for (p = workpath; p < qbase; )
|
||||
*q++ = *p++;
|
||||
qbase = q;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = workpath;
|
||||
q = qsave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*q = '\0';
|
||||
free (workpath);
|
||||
|
||||
/* If the result starts with `//', but the original path does not, we
|
||||
can turn the // into /. Because of how we set `qbase', this should never
|
||||
be true, but it's a sanity check. */
|
||||
if (DOUBLE_SLASH(result) && double_slash_path == 0)
|
||||
{
|
||||
if (result[2] == '\0') /* short-circuit for bare `//' */
|
||||
result[1] = '\0';
|
||||
else
|
||||
strcpy (result, result + 1);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
char *
|
||||
sh_realpath (pathname, resolved)
|
||||
const char *pathname;
|
||||
char *resolved;
|
||||
{
|
||||
char *tdir, *wd;
|
||||
|
||||
if (pathname == 0 || *pathname == '\0')
|
||||
{
|
||||
errno = (pathname == 0) ? EINVAL : ENOENT;
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
if (ABSPATH (pathname) == 0)
|
||||
{
|
||||
wd = get_working_directory ("sh_realpath");
|
||||
if (wd == 0)
|
||||
return ((char *)NULL);
|
||||
tdir = sh_makepath ((char *)pathname, wd, 0);
|
||||
free (wd);
|
||||
}
|
||||
else
|
||||
tdir = savestring (pathname);
|
||||
|
||||
wd = sh_physpath (tdir, 0);
|
||||
free (tdir);
|
||||
|
||||
if (resolved == 0)
|
||||
return (wd);
|
||||
|
||||
if (wd)
|
||||
{
|
||||
strncpy (resolved, wd, PATH_MAX - 1);
|
||||
resolved[PATH_MAX - 1] = '\0';
|
||||
free (wd);
|
||||
return resolved;
|
||||
}
|
||||
else
|
||||
{
|
||||
resolved[0] = '\0';
|
||||
return wd;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
#include <bashtypes.h>
|
||||
#include <posixdir.h>
|
||||
#include <posixstat.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/* spell.c -- spelling correction for pathnames. */
|
||||
|
||||
/* Copyright (C) 2000 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <bashtypes.h>
|
||||
#include <posixdir.h>
|
||||
#include <posixstat.h>
|
||||
#ifndef _MINIX
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <bashansi.h>
|
||||
#include <maxpath.h>
|
||||
#include <stdc.h>
|
||||
|
||||
static int mindist __P((char *, char *, char *));
|
||||
static int spdist __P((char *, char *));
|
||||
|
||||
/*
|
||||
* `spname' and its helpers are inspired by the code in "The UNIX
|
||||
* Programming Environment", Kernighan & Pike, Prentice-Hall 1984,
|
||||
* pages 209 - 213.
|
||||
*/
|
||||
|
||||
/*
|
||||
* `spname' -- return a correctly spelled filename
|
||||
*
|
||||
* int spname(char * oldname, char * newname)
|
||||
* Returns: -1 if no reasonable match found
|
||||
* 0 if exact match found
|
||||
* 1 if corrected
|
||||
* Stores corrected name in `newname'.
|
||||
*/
|
||||
int
|
||||
spname(oldname, newname)
|
||||
char *oldname;
|
||||
char *newname;
|
||||
{
|
||||
char *op, *np, *p;
|
||||
char guess[PATH_MAX + 1], best[PATH_MAX + 1];
|
||||
|
||||
op = oldname;
|
||||
np = newname;
|
||||
for (;;)
|
||||
{
|
||||
while (*op == '/') /* Skip slashes */
|
||||
*np++ = *op++;
|
||||
*np = '\0';
|
||||
|
||||
if (*op == '\0') /* Exact or corrected */
|
||||
{
|
||||
/* `.' is rarely the right thing. */
|
||||
if (oldname[1] == '\0' && newname[1] == '\0' &&
|
||||
oldname[0] != '.' && newname[0] == '.')
|
||||
return -1;
|
||||
return strcmp(oldname, newname) != 0;
|
||||
}
|
||||
|
||||
/* Copy next component into guess */
|
||||
for (p = guess; *op != '/' && *op != '\0'; op++)
|
||||
if (p < guess + PATH_MAX)
|
||||
*p++ = *op;
|
||||
*p = '\0';
|
||||
|
||||
if (mindist(newname, guess, best) >= 3)
|
||||
return -1; /* Hopeless */
|
||||
|
||||
/*
|
||||
* Add to end of newname
|
||||
*/
|
||||
for (p = best; *np = *p++; np++)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Search directory for a guess
|
||||
*/
|
||||
static int
|
||||
mindist(dir, guess, best)
|
||||
char *dir;
|
||||
char *guess;
|
||||
char *best;
|
||||
{
|
||||
DIR *fd;
|
||||
struct dirent *dp;
|
||||
int dist, x;
|
||||
|
||||
dist = 3; /* Worst distance */
|
||||
if (*dir == '\0')
|
||||
dir = ".";
|
||||
|
||||
if ((fd = opendir(dir)) == NULL)
|
||||
return dist;
|
||||
|
||||
while ((dp = readdir(fd)) != NULL)
|
||||
{
|
||||
/*
|
||||
* Look for a better guess. If the new guess is as
|
||||
* good as the current one, we take it. This way,
|
||||
* any single character match will be a better match
|
||||
* than ".".
|
||||
*/
|
||||
x = spdist(dp->d_name, guess);
|
||||
if (x <= dist && x != 3)
|
||||
{
|
||||
strcpy(best, dp->d_name);
|
||||
dist = x;
|
||||
if (dist == 0) /* Exact match */
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void)closedir(fd);
|
||||
|
||||
/* Don't return `.' */
|
||||
if (best[0] == '.' && best[1] == '\0')
|
||||
dist = 3;
|
||||
return dist;
|
||||
}
|
||||
|
||||
/*
|
||||
* `spdist' -- return the "distance" between two names.
|
||||
*
|
||||
* int spname(char * oldname, char * newname)
|
||||
* Returns: 0 if strings are identical
|
||||
* 1 if two characters are transposed
|
||||
* 2 if one character is wrong, added or deleted
|
||||
* 3 otherwise
|
||||
*/
|
||||
static int
|
||||
spdist(cur, new)
|
||||
char *cur, *new;
|
||||
{
|
||||
while (*cur == *new)
|
||||
{
|
||||
if (*cur == '\0')
|
||||
return 0; /* Exact match */
|
||||
cur++;
|
||||
new++;
|
||||
}
|
||||
|
||||
if (*cur)
|
||||
{
|
||||
if (*new)
|
||||
{
|
||||
if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
|
||||
return 1; /* Transposition */
|
||||
|
||||
if (strcmp (cur + 1, new + 1) == 0)
|
||||
return 2; /* One character mismatch */
|
||||
}
|
||||
|
||||
if (strcmp(&cur[1], &new[0]) == 0)
|
||||
return 2; /* Extra character */
|
||||
}
|
||||
|
||||
if (*new && strcmp(cur, new + 1) == 0)
|
||||
return 2; /* Missing character */
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
char *
|
||||
dirspell (dirname)
|
||||
char *dirname;
|
||||
{
|
||||
int n;
|
||||
char *guess;
|
||||
|
||||
n = (strlen (dirname) * 3 + 1) / 2 + 1;
|
||||
guess = (char *)malloc (n);
|
||||
if (guess == 0)
|
||||
return 0;
|
||||
|
||||
switch (spname (dirname, guess))
|
||||
{
|
||||
case -1:
|
||||
default:
|
||||
free (guess);
|
||||
return (char *)NULL;
|
||||
case 0:
|
||||
case 1:
|
||||
return guess;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
#if !defined (HAVE_STRERROR)
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* strerror.c - string corresponding to a particular value of errno. */
|
||||
|
||||
/* Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if !defined (HAVE_STRERROR)
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <shell.h>
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
/* Return a string corresponding to the error number E. From
|
||||
the ANSI C spec. */
|
||||
#if defined (strerror)
|
||||
# undef strerror
|
||||
#endif
|
||||
|
||||
static char *errbase = "Unknown system error ";
|
||||
|
||||
char *
|
||||
strerror (e)
|
||||
int e;
|
||||
{
|
||||
static char emsg[40];
|
||||
#if defined (HAVE_SYS_ERRLIST)
|
||||
extern int sys_nerr;
|
||||
extern char *sys_errlist[];
|
||||
|
||||
if (e > 0 && e < sys_nerr)
|
||||
return (sys_errlist[e]);
|
||||
else
|
||||
#endif /* HAVE_SYS_ERRLIST */
|
||||
{
|
||||
char *z;
|
||||
|
||||
z = itos (e);
|
||||
strcpy (emsg, errbase);
|
||||
strcat (emsg, z);
|
||||
free (z);
|
||||
return (&emsg[0]);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_STRERROR */
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
#include <stdio.h>
|
||||
#include "bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
|
||||
+491
@@ -0,0 +1,491 @@
|
||||
/* mailcheck.c -- The check is in the mail... */
|
||||
|
||||
/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include "posixtime.h"
|
||||
#include "bashansi.h"
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "execute_cmd.h"
|
||||
#include "mailcheck.h"
|
||||
#include <tilde/tilde.h>
|
||||
|
||||
/* Values for flags word in struct _fileinfo */
|
||||
#define MBOX_INITIALIZED 0x01
|
||||
|
||||
extern time_t shell_start_time;
|
||||
|
||||
extern int mailstat __P((const char *, struct stat *));
|
||||
|
||||
typedef struct _fileinfo {
|
||||
char *name;
|
||||
char *msg;
|
||||
time_t access_time;
|
||||
time_t mod_time;
|
||||
off_t file_size;
|
||||
int flags;
|
||||
} FILEINFO;
|
||||
|
||||
/* The list of remembered mail files. */
|
||||
static FILEINFO **mailfiles = (FILEINFO **)NULL;
|
||||
|
||||
/* Number of mail files that we have. */
|
||||
static int mailfiles_count;
|
||||
|
||||
/* The last known time that mail was checked. */
|
||||
static time_t last_time_mail_checked = 0;
|
||||
|
||||
/* Non-zero means warn if a mail file has been read since last checked. */
|
||||
int mail_warning;
|
||||
|
||||
static int find_mail_file __P((char *));
|
||||
static void init_mail_file __P((int));
|
||||
static void update_mail_file __P((int));
|
||||
static int add_mail_file __P((char *, char *));
|
||||
|
||||
static FILEINFO *alloc_mail_file __P((char *, char *));
|
||||
static void dispose_mail_file __P((FILEINFO *));
|
||||
|
||||
static int file_mod_date_changed __P((int));
|
||||
static int file_access_date_changed __P((int));
|
||||
static int file_has_grown __P((int));
|
||||
|
||||
static char *parse_mailpath_spec __P((char *));
|
||||
|
||||
/* Returns non-zero if it is time to check mail. */
|
||||
int
|
||||
time_to_check_mail ()
|
||||
{
|
||||
char *temp;
|
||||
time_t now;
|
||||
intmax_t seconds;
|
||||
|
||||
temp = get_string_value ("MAILCHECK");
|
||||
|
||||
/* Negative number, or non-numbers (such as empty string) cause no
|
||||
checking to take place. */
|
||||
if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
|
||||
return (0);
|
||||
|
||||
now = NOW;
|
||||
/* Time to check if MAILCHECK is explicitly set to zero, or if enough
|
||||
time has passed since the last check. */
|
||||
return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
|
||||
}
|
||||
|
||||
/* Okay, we have checked the mail. Perhaps I should make this function
|
||||
go away. */
|
||||
void
|
||||
reset_mail_timer ()
|
||||
{
|
||||
last_time_mail_checked = NOW;
|
||||
}
|
||||
|
||||
/* Locate a file in the list. Return index of
|
||||
entry, or -1 if not found. */
|
||||
static int
|
||||
find_mail_file (file)
|
||||
char *file;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < mailfiles_count; i++)
|
||||
if (STREQ (mailfiles[i]->name, file))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define RESET_MAIL_FILE(i) \
|
||||
do \
|
||||
{ \
|
||||
mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
|
||||
mailfiles[i]->file_size = 0; \
|
||||
mailfiles[i]->flags = 0; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define UPDATE_MAIL_FILE(i, finfo) \
|
||||
do \
|
||||
{ \
|
||||
mailfiles[i]->access_time = finfo.st_atime; \
|
||||
mailfiles[i]->mod_time = finfo.st_mtime; \
|
||||
mailfiles[i]->file_size = finfo.st_size; \
|
||||
mailfiles[i]->flags |= MBOX_INITIALIZED; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
static void
|
||||
init_mail_file (i)
|
||||
int i;
|
||||
{
|
||||
mailfiles[i]->access_time = mailfiles[i]->mod_time = last_time_mail_checked ? last_time_mail_checked : shell_start_time;
|
||||
mailfiles[i]->file_size = 0;
|
||||
mailfiles[i]->flags = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
update_mail_file (i)
|
||||
int i;
|
||||
{
|
||||
char *file;
|
||||
struct stat finfo;
|
||||
|
||||
file = mailfiles[i]->name;
|
||||
if (mailstat (file, &finfo) == 0)
|
||||
UPDATE_MAIL_FILE (i, finfo);
|
||||
else
|
||||
RESET_MAIL_FILE (i);
|
||||
}
|
||||
|
||||
/* Add this file to the list of remembered files and return its index
|
||||
in the list of mail files. */
|
||||
static int
|
||||
add_mail_file (file, msg)
|
||||
char *file, *msg;
|
||||
{
|
||||
struct stat finfo;
|
||||
char *filename;
|
||||
int i;
|
||||
|
||||
filename = full_pathname (file);
|
||||
i = find_mail_file (filename);
|
||||
if (i >= 0)
|
||||
{
|
||||
if (mailstat (filename, &finfo) == 0)
|
||||
UPDATE_MAIL_FILE (i, finfo);
|
||||
|
||||
free (filename);
|
||||
return i;
|
||||
}
|
||||
|
||||
i = mailfiles_count++;
|
||||
mailfiles = (FILEINFO **)xrealloc
|
||||
(mailfiles, mailfiles_count * sizeof (FILEINFO *));
|
||||
|
||||
mailfiles[i] = alloc_mail_file (filename, msg);
|
||||
init_mail_file (i);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Reset the existing mail files access and modification times to zero. */
|
||||
void
|
||||
reset_mail_files ()
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < mailfiles_count; i++)
|
||||
RESET_MAIL_FILE (i);
|
||||
}
|
||||
|
||||
static FILEINFO *
|
||||
alloc_mail_file (filename, msg)
|
||||
char *filename, *msg;
|
||||
{
|
||||
FILEINFO *mf;
|
||||
|
||||
mf = (FILEINFO *)xmalloc (sizeof (FILEINFO));
|
||||
mf->name = filename;
|
||||
mf->msg = msg ? savestring (msg) : (char *)NULL;
|
||||
mf->flags = 0;
|
||||
|
||||
return mf;
|
||||
}
|
||||
|
||||
static void
|
||||
dispose_mail_file (mf)
|
||||
FILEINFO *mf;
|
||||
{
|
||||
free (mf->name);
|
||||
FREE (mf->msg);
|
||||
free (mf);
|
||||
}
|
||||
|
||||
/* Free the information that we have about the remembered mail files. */
|
||||
void
|
||||
free_mail_files ()
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < mailfiles_count; i++)
|
||||
dispose_mail_file (mailfiles[i]);
|
||||
|
||||
if (mailfiles)
|
||||
free (mailfiles);
|
||||
|
||||
mailfiles_count = 0;
|
||||
mailfiles = (FILEINFO **)NULL;
|
||||
}
|
||||
|
||||
void
|
||||
init_mail_dates ()
|
||||
{
|
||||
if (mailfiles == 0)
|
||||
remember_mail_dates ();
|
||||
}
|
||||
|
||||
/* Return non-zero if FILE's mod date has changed and it has not been
|
||||
accessed since modified. If the size has dropped to zero, reset
|
||||
the cached mail file info. */
|
||||
static int
|
||||
file_mod_date_changed (i)
|
||||
int i;
|
||||
{
|
||||
time_t mtime;
|
||||
struct stat finfo;
|
||||
char *file;
|
||||
|
||||
file = mailfiles[i]->name;
|
||||
mtime = mailfiles[i]->mod_time;
|
||||
|
||||
if (mailstat (file, &finfo) != 0)
|
||||
return (0);
|
||||
|
||||
if (finfo.st_size > 0)
|
||||
return (mtime < finfo.st_mtime);
|
||||
|
||||
if (finfo.st_size == 0 && mailfiles[i]->file_size > 0)
|
||||
UPDATE_MAIL_FILE (i, finfo);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Return non-zero if FILE's access date has changed. */
|
||||
static int
|
||||
file_access_date_changed (i)
|
||||
int i;
|
||||
{
|
||||
time_t atime;
|
||||
struct stat finfo;
|
||||
char *file;
|
||||
|
||||
file = mailfiles[i]->name;
|
||||
atime = mailfiles[i]->access_time;
|
||||
|
||||
if (mailstat (file, &finfo) != 0)
|
||||
return (0);
|
||||
|
||||
if (finfo.st_size > 0)
|
||||
return (atime < finfo.st_atime);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Return non-zero if FILE's size has increased. */
|
||||
static int
|
||||
file_has_grown (i)
|
||||
int i;
|
||||
{
|
||||
off_t size;
|
||||
struct stat finfo;
|
||||
char *file;
|
||||
|
||||
file = mailfiles[i]->name;
|
||||
size = mailfiles[i]->file_size;
|
||||
|
||||
return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
|
||||
}
|
||||
|
||||
/* Take an element from $MAILPATH and return the portion from
|
||||
the first unquoted `?' or `%' to the end of the string. This is the
|
||||
message to be printed when the file contents change. */
|
||||
static char *
|
||||
parse_mailpath_spec (str)
|
||||
char *str;
|
||||
{
|
||||
char *s;
|
||||
int pass_next;
|
||||
|
||||
for (s = str, pass_next = 0; s && *s; s++)
|
||||
{
|
||||
if (pass_next)
|
||||
{
|
||||
pass_next = 0;
|
||||
continue;
|
||||
}
|
||||
if (*s == '\\')
|
||||
{
|
||||
pass_next++;
|
||||
continue;
|
||||
}
|
||||
if (*s == '?' || *s == '%')
|
||||
return s;
|
||||
}
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
make_default_mailpath ()
|
||||
{
|
||||
#if defined (DEFAULT_MAIL_DIRECTORY)
|
||||
char *mp;
|
||||
|
||||
get_current_user_info ();
|
||||
mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
|
||||
strcpy (mp, DEFAULT_MAIL_DIRECTORY);
|
||||
mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
|
||||
strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
|
||||
return (mp);
|
||||
#else
|
||||
return ((char *)NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Remember the dates of the files specified by MAILPATH, or if there is
|
||||
no MAILPATH, by the file specified in MAIL. If neither exists, use a
|
||||
default value, which we randomly concoct from using Unix. */
|
||||
|
||||
void
|
||||
remember_mail_dates ()
|
||||
{
|
||||
char *mailpaths;
|
||||
char *mailfile, *mp;
|
||||
int i = 0;
|
||||
|
||||
mailpaths = get_string_value ("MAILPATH");
|
||||
|
||||
/* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
|
||||
if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
|
||||
{
|
||||
add_mail_file (mailpaths, (char *)NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mailpaths == 0)
|
||||
{
|
||||
mailpaths = make_default_mailpath ();
|
||||
if (mailpaths)
|
||||
{
|
||||
add_mail_file (mailpaths, (char *)NULL);
|
||||
free (mailpaths);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (mailfile = extract_colon_unit (mailpaths, &i))
|
||||
{
|
||||
mp = parse_mailpath_spec (mailfile);
|
||||
if (mp && *mp)
|
||||
*mp++ = '\0';
|
||||
add_mail_file (mailfile, mp);
|
||||
free (mailfile);
|
||||
}
|
||||
}
|
||||
|
||||
/* check_mail () is useful for more than just checking mail. Since it has
|
||||
the paranoids dream ability of telling you when someone has read your
|
||||
mail, it can just as easily be used to tell you when someones .profile
|
||||
file has been read, thus letting one know when someone else has logged
|
||||
in. Pretty good, huh? */
|
||||
|
||||
/* Check for mail in some files. If the modification date of any
|
||||
of the files in MAILPATH has changed since we last did a
|
||||
remember_mail_dates () then mention that the user has mail.
|
||||
Special hack: If the variable MAIL_WARNING is non-zero and the
|
||||
mail file has been accessed since the last time we remembered, then
|
||||
the message "The mail in <mailfile> has been read" is printed. */
|
||||
void
|
||||
check_mail ()
|
||||
{
|
||||
char *current_mail_file, *message;
|
||||
int i, use_user_notification;
|
||||
char *dollar_underscore, *temp;
|
||||
|
||||
dollar_underscore = get_string_value ("_");
|
||||
if (dollar_underscore)
|
||||
dollar_underscore = savestring (dollar_underscore);
|
||||
|
||||
for (i = 0; i < mailfiles_count; i++)
|
||||
{
|
||||
current_mail_file = mailfiles[i]->name;
|
||||
|
||||
if (*current_mail_file == '\0')
|
||||
continue;
|
||||
|
||||
if (file_mod_date_changed (i))
|
||||
{
|
||||
int file_is_bigger;
|
||||
|
||||
use_user_notification = mailfiles[i]->msg != (char *)NULL;
|
||||
message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_");
|
||||
|
||||
bind_variable ("_", current_mail_file, 0);
|
||||
|
||||
#define atime mailfiles[i]->access_time
|
||||
#define mtime mailfiles[i]->mod_time
|
||||
|
||||
/* Have to compute this before the call to update_mail_file, which
|
||||
resets all the information. */
|
||||
file_is_bigger = file_has_grown (i);
|
||||
|
||||
update_mail_file (i);
|
||||
|
||||
/* If the user has just run a program which manipulates the
|
||||
mail file, then don't bother explaining that the mail
|
||||
file has been manipulated. Since some systems don't change
|
||||
the access time to be equal to the modification time when
|
||||
the mail in the file is manipulated, check the size also. If
|
||||
the file has not grown, continue. */
|
||||
if ((atime >= mtime) && !file_is_bigger)
|
||||
continue;
|
||||
|
||||
/* If the mod time is later than the access time and the file
|
||||
has grown, note the fact that this is *new* mail. */
|
||||
if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
|
||||
message = _("You have new mail in $_");
|
||||
#undef atime
|
||||
#undef mtime
|
||||
|
||||
if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
|
||||
{
|
||||
puts (temp);
|
||||
free (temp);
|
||||
}
|
||||
else
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
if (mail_warning && file_access_date_changed (i))
|
||||
{
|
||||
update_mail_file (i);
|
||||
printf (_("The mail in %s has been read\n"), current_mail_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (dollar_underscore)
|
||||
{
|
||||
bind_variable ("_", dollar_underscore, 0);
|
||||
free (dollar_underscore);
|
||||
}
|
||||
else
|
||||
unbind_variable ("_");
|
||||
}
|
||||
+1
-1
@@ -380,7 +380,7 @@ main(argc, argv)
|
||||
#if !defined (HAVE_STRERROR)
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
#if defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* mksyntax.c - construct shell syntax table for fast char attribute lookup.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2000-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bashansi.h"
|
||||
#include "chartypes.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "syntax.h"
|
||||
|
||||
extern int optind;
|
||||
extern char *optarg;
|
||||
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRERROR
|
||||
extern char *strerror();
|
||||
#endif
|
||||
|
||||
struct wordflag {
|
||||
int flag;
|
||||
char *fstr;
|
||||
} wordflags[] = {
|
||||
{ CWORD, "CWORD" },
|
||||
{ CSHMETA, "CSHMETA" },
|
||||
{ CSHBRK, "CSHBRK" },
|
||||
{ CBACKQ, "CBACKQ" },
|
||||
{ CQUOTE, "CQUOTE" },
|
||||
{ CSPECL, "CSPECL" },
|
||||
{ CEXP, "CEXP" },
|
||||
{ CBSDQUOTE, "CBSDQUOTE" },
|
||||
{ CBSHDOC, "CBSHDOC" },
|
||||
{ CGLOB, "CGLOB" },
|
||||
{ CXGLOB, "CXGLOB" },
|
||||
{ CXQUOTE, "CXQUOTE" },
|
||||
{ CSPECVAR, "CSPECVAR" },
|
||||
{ CSUBSTOP, "CSUBSTOP" },
|
||||
{ CBLANK, "CBLANK" },
|
||||
};
|
||||
|
||||
#define N_WFLAGS (sizeof (wordflags) / sizeof (wordflags[0]))
|
||||
#define SYNSIZE 256
|
||||
|
||||
int lsyntax[SYNSIZE];
|
||||
int debug;
|
||||
char *progname;
|
||||
|
||||
char preamble[] = "\
|
||||
/*\n\
|
||||
* This file was generated by mksyntax. DO NOT EDIT.\n\
|
||||
*/\n\
|
||||
\n";
|
||||
|
||||
char includes[] = "\
|
||||
#include \"config.h\"\n\
|
||||
#include \"stdc.h\"\n\
|
||||
#include \"syntax.h\"\n\n";
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf (stderr, "%s: usage: %s [-d] [-o filename]\n", progname, progname);
|
||||
exit (2);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
static int
|
||||
getcflag (s)
|
||||
char *s;
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < N_WFLAGS; i++)
|
||||
if (strcmp (s, wordflags[i].fstr) == 0)
|
||||
return wordflags[i].flag;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *
|
||||
cdesc (i)
|
||||
int i;
|
||||
{
|
||||
static char xbuf[16];
|
||||
|
||||
if (i == ' ')
|
||||
return "SPC";
|
||||
else if (ISPRINT (i))
|
||||
{
|
||||
xbuf[0] = i;
|
||||
xbuf[1] = '\0';
|
||||
return (xbuf);
|
||||
}
|
||||
else if (i == CTLESC)
|
||||
return "CTLESC";
|
||||
else if (i == CTLNUL)
|
||||
return "CTLNUL";
|
||||
else if (i == '\033') /* ASCII */
|
||||
return "ESC";
|
||||
|
||||
xbuf[0] = '\\';
|
||||
xbuf[2] = '\0';
|
||||
|
||||
switch (i)
|
||||
{
|
||||
#ifdef __STDC__
|
||||
case '\a': xbuf[1] = 'a'; break;
|
||||
case '\v': xbuf[1] = 'v'; break;
|
||||
#else
|
||||
case '\007': xbuf[1] = 'a'; break;
|
||||
case 0x0B: xbuf[1] = 'v'; break;
|
||||
#endif
|
||||
case '\b': xbuf[1] = 'b'; break;
|
||||
case '\f': xbuf[1] = 'f'; break;
|
||||
case '\n': xbuf[1] = 'n'; break;
|
||||
case '\r': xbuf[1] = 'r'; break;
|
||||
case '\t': xbuf[1] = 't'; break;
|
||||
default: sprintf (xbuf, "%d", i); break;
|
||||
}
|
||||
|
||||
return xbuf;
|
||||
}
|
||||
|
||||
static char *
|
||||
getcstr (f)
|
||||
int f;
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < N_WFLAGS; i++)
|
||||
if (f == wordflags[i].flag)
|
||||
return (wordflags[i].fstr);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
addcstr (str, flag)
|
||||
char *str;
|
||||
int flag;
|
||||
{
|
||||
char *s, *fstr;
|
||||
unsigned char uc;
|
||||
|
||||
for (s = str; s && *s; s++)
|
||||
{
|
||||
uc = *s;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fstr = getcstr (flag);
|
||||
fprintf(stderr, "added %s for character %s\n", fstr, cdesc(uc));
|
||||
}
|
||||
|
||||
lsyntax[uc] |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
addcchar (c, flag)
|
||||
unsigned char c;
|
||||
int flag;
|
||||
{
|
||||
char *fstr;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fstr = getcstr (flag);
|
||||
fprintf (stderr, "added %s for character %s\n", fstr, cdesc(c));
|
||||
}
|
||||
lsyntax[c] |= flag;
|
||||
}
|
||||
|
||||
static void
|
||||
addblanks ()
|
||||
{
|
||||
register int i;
|
||||
unsigned char uc;
|
||||
|
||||
for (i = 0; i < SYNSIZE; i++)
|
||||
{
|
||||
uc = i;
|
||||
/* Since we don't call setlocale(), this defaults to the "C" locale, and
|
||||
the default blank characters will be space and tab. */
|
||||
if (isblank (uc))
|
||||
lsyntax[uc] |= CBLANK;
|
||||
}
|
||||
}
|
||||
|
||||
/* load up the correct flag values in lsyntax */
|
||||
static void
|
||||
load_lsyntax ()
|
||||
{
|
||||
/* shell metacharacters */
|
||||
addcstr (shell_meta_chars, CSHMETA);
|
||||
|
||||
/* shell word break characters */
|
||||
addcstr (shell_break_chars, CSHBRK);
|
||||
|
||||
addcchar ('`', CBACKQ);
|
||||
|
||||
addcstr (shell_quote_chars, CQUOTE);
|
||||
|
||||
addcchar (CTLESC, CSPECL);
|
||||
addcchar (CTLNUL, CSPECL);
|
||||
|
||||
addcstr (shell_exp_chars, CEXP);
|
||||
|
||||
addcstr (slashify_in_quotes, CBSDQUOTE);
|
||||
addcstr (slashify_in_here_document, CBSHDOC);
|
||||
|
||||
addcstr (shell_glob_chars, CGLOB);
|
||||
|
||||
#if defined (EXTENDED_GLOB)
|
||||
addcstr (ext_glob_chars, CXGLOB);
|
||||
#endif
|
||||
|
||||
addcstr (shell_quote_chars, CXQUOTE);
|
||||
addcchar ('\\', CXQUOTE);
|
||||
|
||||
addcstr ("@*#?-$!", CSPECVAR); /* omits $0...$9 and $_ */
|
||||
|
||||
addcstr ("-=?+", CSUBSTOP); /* OP in ${paramOPword} */
|
||||
|
||||
addblanks ();
|
||||
}
|
||||
|
||||
static void
|
||||
dump_lflags (fp, ind)
|
||||
FILE *fp;
|
||||
int ind;
|
||||
{
|
||||
int xflags, first, i;
|
||||
|
||||
xflags = lsyntax[ind];
|
||||
first = 1;
|
||||
|
||||
if (xflags == 0)
|
||||
fputs (wordflags[0].fstr, fp);
|
||||
else
|
||||
{
|
||||
for (i = 1; i < N_WFLAGS; i++)
|
||||
if (xflags & wordflags[i].flag)
|
||||
{
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
putc ('|', fp);
|
||||
fputs (wordflags[i].fstr, fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wcomment (fp, i)
|
||||
FILE *fp;
|
||||
int i;
|
||||
{
|
||||
fputs ("\t\t/* ", fp);
|
||||
|
||||
fprintf (fp, "%s", cdesc(i));
|
||||
|
||||
fputs (" */", fp);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_lsyntax (fp)
|
||||
FILE *fp;
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf (fp, "int sh_syntabsiz = %d;\n", SYNSIZE);
|
||||
fprintf (fp, "int sh_syntaxtab[%d] = {\n", SYNSIZE);
|
||||
|
||||
for (i = 0; i < SYNSIZE; i++)
|
||||
{
|
||||
putc ('\t', fp);
|
||||
dump_lflags (fp, i);
|
||||
putc (',', fp);
|
||||
wcomment (fp, i);
|
||||
putc ('\n', fp);
|
||||
}
|
||||
|
||||
fprintf (fp, "};\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
int opt, i;
|
||||
char *filename;
|
||||
FILE *fp;
|
||||
|
||||
if ((progname = strrchr (argv[0], '/')) == 0)
|
||||
progname = argv[0];
|
||||
else
|
||||
progname++;
|
||||
|
||||
filename = (char *)NULL;
|
||||
debug = 0;
|
||||
|
||||
while ((opt = getopt (argc, argv, "do:")) != EOF)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'o':
|
||||
filename = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (filename)
|
||||
{
|
||||
fp = fopen (filename, "w");
|
||||
if (fp == 0)
|
||||
{
|
||||
fprintf (stderr, "%s: %s: cannot open: %s\n", progname, filename, strerror(errno));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = "stdout";
|
||||
fp = stdout;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < SYNSIZE; i++)
|
||||
lsyntax[i] = CWORD;
|
||||
|
||||
load_lsyntax ();
|
||||
|
||||
fprintf (fp, "%s\n", preamble);
|
||||
fprintf (fp, "%s\n", includes);
|
||||
|
||||
dump_lsyntax (fp);
|
||||
|
||||
if (fp != stdout)
|
||||
fclose (fp);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
|
||||
#if !defined (HAVE_STRERROR)
|
||||
|
||||
#include <bashtypes.h>
|
||||
#ifndef _MINIX
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* Return a string corresponding to the error number E. From
|
||||
the ANSI C spec. */
|
||||
#if defined (strerror)
|
||||
# undef strerror
|
||||
#endif
|
||||
|
||||
char *
|
||||
strerror (e)
|
||||
int e;
|
||||
{
|
||||
static char emsg[40];
|
||||
#if defined (HAVE_SYS_ERRLIST)
|
||||
extern int sys_nerr;
|
||||
extern char *sys_errlist[];
|
||||
|
||||
if (e > 0 && e < sys_nerr)
|
||||
return (sys_errlist[e]);
|
||||
else
|
||||
#endif /* HAVE_SYS_ERRLIST */
|
||||
{
|
||||
sprintf (emsg, "Unknown system error %d", e);
|
||||
return (&emsg[0]);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_STRERROR */
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* patchlevel.h -- current bash patch level */
|
||||
|
||||
/* Copyright (C) 2001-2011 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 2001-2012 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
|
||||
looks for to find the patch level (for the sccs version string). */
|
||||
|
||||
#define PATCHLEVEL 36
|
||||
#define PATCHLEVEL 37
|
||||
|
||||
#endif /* _PATCHLEVEL_H_ */
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* patchlevel.h -- current bash patch level */
|
||||
|
||||
/* Copyright (C) 2001-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined (_PATCHLEVEL_H_)
|
||||
#define _PATCHLEVEL_H_
|
||||
|
||||
/* It's important that there be no other strings in this file that match the
|
||||
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
|
||||
looks for to find the patch level (for the sccs version string). */
|
||||
|
||||
#define PATCHLEVEL 36
|
||||
|
||||
#endif /* _PATCHLEVEL_H_ */
|
||||
@@ -957,6 +957,24 @@ sh_exit (s)
|
||||
exit (s);
|
||||
}
|
||||
|
||||
/* Exit a subshell, which includes calling the exit trap. We don't want to
|
||||
do any more cleanup, since a subshell is created as an exact copy of its
|
||||
parent. */
|
||||
void
|
||||
subshell_exit (s)
|
||||
int s;
|
||||
{
|
||||
fflush (stdout);
|
||||
fflush (stderr);
|
||||
|
||||
/* Do trap[0] if defined. Allow it to override the exit status
|
||||
passed to us. */
|
||||
if (signal_is_trapped (0))
|
||||
s = run_exit_trap ();
|
||||
|
||||
sh_exit (s);
|
||||
}
|
||||
|
||||
/* Source the bash startup files. If POSIXLY_CORRECT is non-zero, we obey
|
||||
the Posix.2 startup file rules: $ENV is expanded, and if the file it
|
||||
names exists, that file is sourced. The Posix.2 rules are in effect
|
||||
|
||||
@@ -358,6 +358,8 @@ reset_terminating_signals ()
|
||||
signal (XSIG (i), XHANDLER (i));
|
||||
}
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
termsigs_initialized = 0;
|
||||
}
|
||||
#undef XSIG
|
||||
#undef XHANDLER
|
||||
|
||||
@@ -0,0 +1,692 @@
|
||||
/* sig.c - interface for shell signal handlers and signal initialization. */
|
||||
|
||||
/* Copyright (C) 1994-2012 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#if defined (JOB_CONTROL)
|
||||
#include "jobs.h"
|
||||
#endif /* JOB_CONTROL */
|
||||
#include "siglist.h"
|
||||
#include "sig.h"
|
||||
#include "trap.h"
|
||||
|
||||
#include "builtins/common.h"
|
||||
|
||||
#if defined (READLINE)
|
||||
# include "bashline.h"
|
||||
# include <readline/readline.h>
|
||||
#endif
|
||||
|
||||
#if defined (HISTORY)
|
||||
# include "bashhist.h"
|
||||
#endif
|
||||
|
||||
extern int last_command_exit_value;
|
||||
extern int last_command_exit_signal;
|
||||
extern int return_catch_flag;
|
||||
extern int loop_level, continuing, breaking, funcnest;
|
||||
extern int executing_list;
|
||||
extern int comsub_ignore_return;
|
||||
extern int parse_and_execute_level, shell_initialized;
|
||||
#if defined (HISTORY)
|
||||
extern int history_lines_this_session;
|
||||
#endif
|
||||
extern int no_line_editing;
|
||||
|
||||
extern void initialize_siglist ();
|
||||
|
||||
/* Non-zero after SIGINT. */
|
||||
volatile int interrupt_state = 0;
|
||||
|
||||
/* Non-zero after SIGWINCH */
|
||||
volatile int sigwinch_received = 0;
|
||||
|
||||
/* Set to the value of any terminating signal received. */
|
||||
volatile int terminating_signal = 0;
|
||||
|
||||
/* The environment at the top-level R-E loop. We use this in
|
||||
the case of error return. */
|
||||
procenv_t top_level;
|
||||
|
||||
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
||||
/* The signal masks that this shell runs with. */
|
||||
sigset_t top_level_mask;
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
/* When non-zero, we throw_to_top_level (). */
|
||||
int interrupt_immediately = 0;
|
||||
|
||||
/* When non-zero, we call the terminating signal handler immediately. */
|
||||
int terminate_immediately = 0;
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
static SigHandler *old_winch = (SigHandler *)SIG_DFL;
|
||||
#endif
|
||||
|
||||
static void initialize_shell_signals __P((void));
|
||||
|
||||
void
|
||||
initialize_signals (reinit)
|
||||
int reinit;
|
||||
{
|
||||
initialize_shell_signals ();
|
||||
initialize_job_signals ();
|
||||
#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
|
||||
if (reinit == 0)
|
||||
initialize_siglist ();
|
||||
#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
|
||||
}
|
||||
|
||||
/* A structure describing a signal that terminates the shell if not
|
||||
caught. The orig_handler member is present so children can reset
|
||||
these signals back to their original handlers. */
|
||||
struct termsig {
|
||||
int signum;
|
||||
SigHandler *orig_handler;
|
||||
int orig_flags;
|
||||
};
|
||||
|
||||
#define NULL_HANDLER (SigHandler *)SIG_DFL
|
||||
|
||||
/* The list of signals that would terminate the shell if not caught.
|
||||
We catch them, but just so that we can write the history file,
|
||||
and so forth. */
|
||||
static struct termsig terminating_signals[] = {
|
||||
#ifdef SIGHUP
|
||||
{ SIGHUP, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGINT
|
||||
{ SIGINT, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGILL
|
||||
{ SIGILL, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGTRAP
|
||||
{ SIGTRAP, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGIOT
|
||||
{ SIGIOT, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGDANGER
|
||||
{ SIGDANGER, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGEMT
|
||||
{ SIGEMT, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGFPE
|
||||
{ SIGFPE, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGBUS
|
||||
{ SIGBUS, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGSEGV
|
||||
{ SIGSEGV, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGSYS
|
||||
{ SIGSYS, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGPIPE
|
||||
{ SIGPIPE, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGALRM
|
||||
{ SIGALRM, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGTERM
|
||||
{ SIGTERM, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGXCPU
|
||||
{ SIGXCPU, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGXFSZ
|
||||
{ SIGXFSZ, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGVTALRM
|
||||
{ SIGVTALRM, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef SIGPROF
|
||||
{ SIGPROF, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SIGLOST
|
||||
{ SIGLOST, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGUSR1
|
||||
{ SIGUSR1, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGUSR2
|
||||
{ SIGUSR2, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
};
|
||||
|
||||
#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
|
||||
|
||||
#define XSIG(x) (terminating_signals[x].signum)
|
||||
#define XHANDLER(x) (terminating_signals[x].orig_handler)
|
||||
#define XSAFLAGS(x) (terminating_signals[x].orig_flags)
|
||||
|
||||
static int termsigs_initialized = 0;
|
||||
|
||||
/* Initialize signals that will terminate the shell to do some
|
||||
unwind protection. For non-interactive shells, we only call
|
||||
this when a trap is defined for EXIT (0) or when trap is run
|
||||
to display signal dispositions. */
|
||||
void
|
||||
initialize_terminating_signals ()
|
||||
{
|
||||
register int i;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
struct sigaction act, oact;
|
||||
#endif
|
||||
|
||||
itrace("initialize_terminating_signals: termsigs_initialized = %d", termsigs_initialized);
|
||||
if (termsigs_initialized)
|
||||
return;
|
||||
|
||||
/* The following code is to avoid an expensive call to
|
||||
set_signal_handler () for each terminating_signals. Fortunately,
|
||||
this is possible in Posix. Unfortunately, we have to call signal ()
|
||||
on non-Posix systems for each signal in terminating_signals. */
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
act.sa_handler = termsig_sighandler;
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigemptyset (&oact.sa_mask);
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
sigaddset (&act.sa_mask, XSIG (i));
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
/* If we've already trapped it, don't do anything. */
|
||||
if (signal_is_trapped (XSIG (i)))
|
||||
continue;
|
||||
|
||||
sigaction (XSIG (i), &act, &oact);
|
||||
XHANDLER(i) = oact.sa_handler;
|
||||
XSAFLAGS(i) = oact.sa_flags;
|
||||
/* Don't do anything with signals that are ignored at shell entry
|
||||
if the shell is not interactive. */
|
||||
/* XXX - should we do this for interactive shells, too? */
|
||||
if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
|
||||
{
|
||||
sigaction (XSIG (i), &oact, &act);
|
||||
set_signal_ignored (XSIG (i));
|
||||
}
|
||||
#if defined (SIGPROF) && !defined (_MINIX)
|
||||
if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
|
||||
sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
|
||||
#endif /* SIGPROF && !_MINIX */
|
||||
}
|
||||
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
/* If we've already trapped it, don't do anything. */
|
||||
if (signal_is_trapped (XSIG (i)))
|
||||
continue;
|
||||
|
||||
XHANDLER(i) = signal (XSIG (i), termsig_sighandler);
|
||||
XSAFLAGS(i) = 0;
|
||||
/* Don't do anything with signals that are ignored at shell entry
|
||||
if the shell is not interactive. */
|
||||
/* XXX - should we do this for interactive shells, too? */
|
||||
if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
|
||||
{
|
||||
signal (XSIG (i), SIG_IGN);
|
||||
set_signal_ignored (XSIG (i));
|
||||
}
|
||||
#ifdef SIGPROF
|
||||
if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
|
||||
signal (XSIG (i), XHANDLER (i));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
termsigs_initialized = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
initialize_shell_signals ()
|
||||
{
|
||||
if (interactive)
|
||||
initialize_terminating_signals ();
|
||||
|
||||
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
||||
/* All shells use the signal mask they inherit, and pass it along
|
||||
to child processes. Children will never block SIGCHLD, though. */
|
||||
sigemptyset (&top_level_mask);
|
||||
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
|
||||
# if defined (SIGCHLD)
|
||||
sigdelset (&top_level_mask, SIGCHLD);
|
||||
# endif
|
||||
#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */
|
||||
|
||||
/* And, some signals that are specifically ignored by the shell. */
|
||||
set_signal_handler (SIGQUIT, SIG_IGN);
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
set_signal_handler (SIGINT, sigint_sighandler);
|
||||
set_signal_handler (SIGTERM, SIG_IGN);
|
||||
set_sigwinch_handler ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reset_terminating_signals ()
|
||||
{
|
||||
register int i;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
struct sigaction act;
|
||||
#endif
|
||||
|
||||
if (termsigs_initialized == 0)
|
||||
return;
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
/* Skip a signal if it's trapped or handled specially, because the
|
||||
trap code will restore the correct value. */
|
||||
if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
|
||||
continue;
|
||||
|
||||
act.sa_handler = XHANDLER (i);
|
||||
act.sa_flags = XSAFLAGS (i);
|
||||
sigaction (XSIG (i), &act, (struct sigaction *) NULL);
|
||||
}
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
|
||||
continue;
|
||||
|
||||
signal (XSIG (i), XHANDLER (i));
|
||||
}
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
termsigs_initialized = 0;
|
||||
}
|
||||
#undef XSIG
|
||||
#undef XHANDLER
|
||||
|
||||
/* Run some of the cleanups that should be performed when we run
|
||||
jump_to_top_level from a builtin command context. XXX - might want to
|
||||
also call reset_parser here. */
|
||||
void
|
||||
top_level_cleanup ()
|
||||
{
|
||||
/* Clean up string parser environment. */
|
||||
while (parse_and_execute_level)
|
||||
parse_and_execute_cleanup ();
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
run_unwind_protects ();
|
||||
loop_level = continuing = breaking = funcnest = 0;
|
||||
executing_list = comsub_ignore_return = return_catch_flag = 0;
|
||||
}
|
||||
|
||||
/* What to do when we've been interrupted, and it is safe to handle it. */
|
||||
void
|
||||
throw_to_top_level ()
|
||||
{
|
||||
int print_newline = 0;
|
||||
|
||||
if (interrupt_state)
|
||||
{
|
||||
print_newline = 1;
|
||||
DELINTERRUPT;
|
||||
}
|
||||
|
||||
if (interrupt_state)
|
||||
return;
|
||||
|
||||
last_command_exit_signal = (last_command_exit_value > 128) ?
|
||||
(last_command_exit_value - 128) : 0;
|
||||
last_command_exit_value |= 128;
|
||||
|
||||
/* Run any traps set on SIGINT. */
|
||||
run_interrupt_trap ();
|
||||
|
||||
/* Clean up string parser environment. */
|
||||
while (parse_and_execute_level)
|
||||
parse_and_execute_cleanup ();
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
give_terminal_to (shell_pgrp, 0);
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
||||
/* This should not be necessary on systems using sigsetjmp/siglongjmp. */
|
||||
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
|
||||
#endif
|
||||
|
||||
reset_parser ();
|
||||
|
||||
#if defined (READLINE)
|
||||
if (interactive)
|
||||
bashline_reset ();
|
||||
#endif /* READLINE */
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
run_unwind_protects ();
|
||||
loop_level = continuing = breaking = funcnest = 0;
|
||||
executing_list = comsub_ignore_return = return_catch_flag = 0;
|
||||
|
||||
if (interactive && print_newline)
|
||||
{
|
||||
fflush (stdout);
|
||||
fprintf (stderr, "\n");
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
/* An interrupted `wait' command in a script does not exit the script. */
|
||||
if (interactive || (interactive_shell && !shell_initialized) ||
|
||||
(print_newline && signal_is_trapped (SIGINT)))
|
||||
jump_to_top_level (DISCARD);
|
||||
else
|
||||
jump_to_top_level (EXITPROG);
|
||||
}
|
||||
|
||||
/* This is just here to isolate the longjmp calls. */
|
||||
void
|
||||
jump_to_top_level (value)
|
||||
int value;
|
||||
{
|
||||
longjmp (top_level, value);
|
||||
}
|
||||
|
||||
sighandler
|
||||
termsig_sighandler (sig)
|
||||
int sig;
|
||||
{
|
||||
/* If we get called twice with the same signal before handling it,
|
||||
terminate right away. */
|
||||
if (
|
||||
#ifdef SIGHUP
|
||||
sig != SIGHUP &&
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
sig != SIGINT &&
|
||||
#endif
|
||||
#ifdef SIGDANGER
|
||||
sig != SIGDANGER &&
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
sig != SIGPIPE &&
|
||||
#endif
|
||||
#ifdef SIGALRM
|
||||
sig != SIGALRM &&
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
sig != SIGTERM &&
|
||||
#endif
|
||||
#ifdef SIGXCPU
|
||||
sig != SIGXCPU &&
|
||||
#endif
|
||||
#ifdef SIGXFSZ
|
||||
sig != SIGXFSZ &&
|
||||
#endif
|
||||
#ifdef SIGVTALRM
|
||||
sig != SIGVTALRM &&
|
||||
#endif
|
||||
#ifdef SIGLOST
|
||||
sig != SIGLOST &&
|
||||
#endif
|
||||
#ifdef SIGUSR1
|
||||
sig != SIGUSR1 &&
|
||||
#endif
|
||||
#ifdef SIGUSR2
|
||||
sig != SIGUSR2 &&
|
||||
#endif
|
||||
sig == terminating_signal)
|
||||
terminate_immediately = 1;
|
||||
|
||||
terminating_signal = sig;
|
||||
|
||||
/* XXX - should this also trigger when interrupt_immediately is set? */
|
||||
if (terminate_immediately)
|
||||
{
|
||||
#if defined (HISTORY)
|
||||
/* XXX - will inhibit history file being written */
|
||||
# if defined (READLINE)
|
||||
if (interactive_shell == 0 || interactive == 0 || (sig != SIGHUP && sig != SIGTERM) || no_line_editing || (RL_ISSTATE (RL_STATE_READCMD) == 0))
|
||||
# endif
|
||||
history_lines_this_session = 0;
|
||||
#endif
|
||||
terminate_immediately = 0;
|
||||
termsig_handler (sig);
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
if (interactive_shell && interactive && no_line_editing == 0)
|
||||
bashline_set_event_hook ();
|
||||
#endif
|
||||
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
void
|
||||
termsig_handler (sig)
|
||||
int sig;
|
||||
{
|
||||
static int handling_termsig = 0;
|
||||
|
||||
/* Simple semaphore to keep this function from being executed multiple
|
||||
times. Since we no longer are running as a signal handler, we don't
|
||||
block multiple occurrences of the terminating signals while running. */
|
||||
if (handling_termsig)
|
||||
return;
|
||||
handling_termsig = 1;
|
||||
terminating_signal = 0; /* keep macro from re-testing true. */
|
||||
|
||||
/* I don't believe this condition ever tests true. */
|
||||
if (sig == SIGINT && signal_is_trapped (SIGINT))
|
||||
run_interrupt_trap ();
|
||||
|
||||
#if 0
|
||||
#if defined (HISTORY)
|
||||
if (interactive_shell && (sig != SIGABRT && sig != SIGINT && sig != SIGHUP && sig != SIGTERM))
|
||||
maybe_save_shell_history ();
|
||||
#endif /* HISTORY */
|
||||
#endif
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB))))
|
||||
hangup_all_jobs ();
|
||||
end_job_control ();
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
/* Reset execution context */
|
||||
loop_level = continuing = breaking = funcnest = 0;
|
||||
executing_list = comsub_ignore_return = return_catch_flag = 0;
|
||||
|
||||
run_exit_trap ();
|
||||
set_signal_handler (sig, SIG_DFL);
|
||||
kill (getpid (), sig);
|
||||
}
|
||||
|
||||
/* What we really do when SIGINT occurs. */
|
||||
sighandler
|
||||
sigint_sighandler (sig)
|
||||
int sig;
|
||||
{
|
||||
#if defined (MUST_REINSTALL_SIGHANDLERS)
|
||||
signal (sig, sigint_sighandler);
|
||||
#endif
|
||||
|
||||
/* interrupt_state needs to be set for the stack of interrupts to work
|
||||
right. Should it be set unconditionally? */
|
||||
if (interrupt_state == 0)
|
||||
ADDINTERRUPT;
|
||||
|
||||
if (interrupt_immediately)
|
||||
{
|
||||
interrupt_immediately = 0;
|
||||
last_command_exit_value = 128 + sig;
|
||||
throw_to_top_level ();
|
||||
}
|
||||
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
sighandler
|
||||
sigwinch_sighandler (sig)
|
||||
int sig;
|
||||
{
|
||||
#if defined (MUST_REINSTALL_SIGHANDLERS)
|
||||
set_signal_handler (SIGWINCH, sigwinch_sighandler);
|
||||
#endif /* MUST_REINSTALL_SIGHANDLERS */
|
||||
sigwinch_received = 1;
|
||||
SIGRETURN (0);
|
||||
}
|
||||
#endif /* SIGWINCH */
|
||||
|
||||
void
|
||||
set_sigwinch_handler ()
|
||||
{
|
||||
#if defined (SIGWINCH)
|
||||
old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
unset_sigwinch_handler ()
|
||||
{
|
||||
#if defined (SIGWINCH)
|
||||
set_signal_handler (SIGWINCH, old_winch);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Signal functions used by the rest of the code. */
|
||||
#if !defined (HAVE_POSIX_SIGNALS)
|
||||
|
||||
/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
|
||||
sigprocmask (operation, newset, oldset)
|
||||
int operation, *newset, *oldset;
|
||||
{
|
||||
int old, new;
|
||||
|
||||
if (newset)
|
||||
new = *newset;
|
||||
else
|
||||
new = 0;
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
case SIG_BLOCK:
|
||||
old = sigblock (new);
|
||||
break;
|
||||
|
||||
case SIG_SETMASK:
|
||||
old = sigsetmask (new);
|
||||
break;
|
||||
|
||||
default:
|
||||
internal_error (_("sigprocmask: %d: invalid operation"), operation);
|
||||
}
|
||||
|
||||
if (oldset)
|
||||
*oldset = old;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if !defined (SA_INTERRUPT)
|
||||
# define SA_INTERRUPT 0
|
||||
#endif
|
||||
|
||||
#if !defined (SA_RESTART)
|
||||
# define SA_RESTART 0
|
||||
#endif
|
||||
|
||||
SigHandler *
|
||||
set_signal_handler (sig, handler)
|
||||
int sig;
|
||||
SigHandler *handler;
|
||||
{
|
||||
struct sigaction act, oact;
|
||||
|
||||
act.sa_handler = handler;
|
||||
act.sa_flags = 0;
|
||||
|
||||
/* XXX - bash-4.2 */
|
||||
/* We don't want a child death to interrupt interruptible system calls, even
|
||||
if we take the time to reap children */
|
||||
#if defined (SIGCHLD)
|
||||
if (sig == SIGCHLD)
|
||||
act.sa_flags |= SA_RESTART; /* XXX */
|
||||
#endif
|
||||
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigemptyset (&oact.sa_mask);
|
||||
sigaction (sig, &act, &oact);
|
||||
return (oact.sa_handler);
|
||||
}
|
||||
#endif /* HAVE_POSIX_SIGNALS */
|
||||
@@ -5332,6 +5332,8 @@ command_substitute (string, quoted)
|
||||
sys_error (_("cannot make child for command substitution"));
|
||||
error_exit:
|
||||
|
||||
last_made_pid = old_pid;
|
||||
|
||||
FREE (istring);
|
||||
close (fildes[0]);
|
||||
close (fildes[1]);
|
||||
|
||||
+3
-1
@@ -177,7 +177,9 @@ darwin[89]*|darwin1[012]*)
|
||||
|
||||
SHOBJ_CFLAGS='-fno-common'
|
||||
|
||||
SHOBJ_LD='MACOSX_DEPLOYMENT_TARGET=10.3 ${CC}'
|
||||
# SHOBJ_LD='MACOSX_DEPLOYMENT_TARGET=10.3 ${CC}'
|
||||
# we can finally kill Mac OS X 10.3
|
||||
SHOBJ_LD='${CC}'
|
||||
|
||||
SHLIB_LIBVERSION='$(SHLIB_MAJOR)$(SHLIB_MINOR).$(SHLIB_LIBSUFF)'
|
||||
SHLIB_LIBSUFF='dylib'
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if !defined (HAVE_LIMITS_H)
|
||||
#if !defined (HAVE_LIMITS_H) && defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,852 @@
|
||||
/* test.c - GNU test program (ksb and mjb) */
|
||||
|
||||
/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
|
||||
|
||||
/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
|
||||
binary operators. */
|
||||
/* #define PATTERN_MATCHING */
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if !defined (HAVE_LIMITS_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif /* !_POSIX_VERSION */
|
||||
#include "posixstat.h"
|
||||
#include "filecntl.h"
|
||||
#include "stat-time.h"
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "pathexp.h"
|
||||
#include "test.h"
|
||||
#include "builtins/common.h"
|
||||
|
||||
#include <glob/strmatch.h>
|
||||
|
||||
#if !defined (STRLEN)
|
||||
# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
|
||||
#endif
|
||||
|
||||
#if !defined (STREQ)
|
||||
# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
|
||||
#endif /* !STREQ */
|
||||
#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)
|
||||
|
||||
#if !defined (R_OK)
|
||||
#define R_OK 4
|
||||
#define W_OK 2
|
||||
#define X_OK 1
|
||||
#define F_OK 0
|
||||
#endif /* R_OK */
|
||||
|
||||
#define EQ 0
|
||||
#define NE 1
|
||||
#define LT 2
|
||||
#define GT 3
|
||||
#define LE 4
|
||||
#define GE 5
|
||||
|
||||
#define NT 0
|
||||
#define OT 1
|
||||
#define EF 2
|
||||
|
||||
/* The following few defines control the truth and false output of each stage.
|
||||
TRUE and FALSE are what we use to compute the final output value.
|
||||
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
|
||||
Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define SHELL_BOOLEAN(value) (!(value))
|
||||
|
||||
#define TEST_ERREXIT_STATUS 2
|
||||
|
||||
static procenv_t test_exit_buf;
|
||||
static int test_error_return;
|
||||
#define test_exit(val) \
|
||||
do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
|
||||
|
||||
extern int sh_stat __P((const char *, struct stat *));
|
||||
|
||||
static int pos; /* The offset of the current argument in ARGV. */
|
||||
static int argc; /* The number of arguments present in ARGV. */
|
||||
static char **argv; /* The argument list. */
|
||||
static int noeval;
|
||||
|
||||
static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
|
||||
static void beyond __P((void)) __attribute__((__noreturn__));
|
||||
static void integer_expected_error __P((char *)) __attribute__((__noreturn__));
|
||||
|
||||
static int unary_operator __P((void));
|
||||
static int binary_operator __P((void));
|
||||
static int two_arguments __P((void));
|
||||
static int three_arguments __P((void));
|
||||
static int posixtest __P((void));
|
||||
|
||||
static int expr __P((void));
|
||||
static int term __P((void));
|
||||
static int and __P((void));
|
||||
static int or __P((void));
|
||||
|
||||
static int filecomp __P((char *, char *, int));
|
||||
static int arithcomp __P((char *, char *, int, int));
|
||||
static int patcomp __P((char *, char *, int));
|
||||
|
||||
static void
|
||||
test_syntax_error (format, arg)
|
||||
char *format, *arg;
|
||||
{
|
||||
builtin_error (format, arg);
|
||||
test_exit (TEST_ERREXIT_STATUS);
|
||||
}
|
||||
|
||||
/*
|
||||
* beyond - call when we're beyond the end of the argument list (an
|
||||
* error condition)
|
||||
*/
|
||||
static void
|
||||
beyond ()
|
||||
{
|
||||
test_syntax_error (_("argument expected"), (char *)NULL);
|
||||
}
|
||||
|
||||
/* Syntax error for when an integer argument was expected, but
|
||||
something else was found. */
|
||||
static void
|
||||
integer_expected_error (pch)
|
||||
char *pch;
|
||||
{
|
||||
test_syntax_error (_("%s: integer expression expected"), pch);
|
||||
}
|
||||
|
||||
/* Increment our position in the argument list. Check that we're not
|
||||
past the end of the argument list. This check is supressed if the
|
||||
argument is FALSE. Made a macro for efficiency. */
|
||||
#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
|
||||
#define unary_advance() do { advance (1); ++pos; } while (0)
|
||||
|
||||
/*
|
||||
* expr:
|
||||
* or
|
||||
*/
|
||||
static int
|
||||
expr ()
|
||||
{
|
||||
if (pos >= argc)
|
||||
beyond ();
|
||||
|
||||
return (FALSE ^ or ()); /* Same with this. */
|
||||
}
|
||||
|
||||
/*
|
||||
* or:
|
||||
* and
|
||||
* and '-o' or
|
||||
*/
|
||||
static int
|
||||
or ()
|
||||
{
|
||||
int value, v2;
|
||||
|
||||
value = and ();
|
||||
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
|
||||
{
|
||||
advance (0);
|
||||
v2 = or ();
|
||||
return (value || v2);
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* and:
|
||||
* term
|
||||
* term '-a' and
|
||||
*/
|
||||
static int
|
||||
and ()
|
||||
{
|
||||
int value, v2;
|
||||
|
||||
value = term ();
|
||||
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
|
||||
{
|
||||
advance (0);
|
||||
v2 = and ();
|
||||
return (value && v2);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* term - parse a term and return 1 or 0 depending on whether the term
|
||||
* evaluates to true or false, respectively.
|
||||
*
|
||||
* term ::=
|
||||
* '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
|
||||
* '-'('G'|'L'|'O'|'S'|'N') filename
|
||||
* '-t' [int]
|
||||
* '-'('z'|'n') string
|
||||
* '-o' option
|
||||
* string
|
||||
* string ('!='|'='|'==') string
|
||||
* <int> '-'(eq|ne|le|lt|ge|gt) <int>
|
||||
* file '-'(nt|ot|ef) file
|
||||
* '(' <expr> ')'
|
||||
* int ::=
|
||||
* positive and negative integers
|
||||
*/
|
||||
static int
|
||||
term ()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (pos >= argc)
|
||||
beyond ();
|
||||
|
||||
/* Deal with leading `not's. */
|
||||
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
value = 0;
|
||||
while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
advance (1);
|
||||
value = 1 - value;
|
||||
}
|
||||
|
||||
return (value ? !term() : term());
|
||||
}
|
||||
|
||||
/* A paren-bracketed argument. */
|
||||
if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
|
||||
{
|
||||
advance (1);
|
||||
value = expr ();
|
||||
if (argv[pos] == 0) /* ( */
|
||||
test_syntax_error (_("`)' expected"), (char *)NULL);
|
||||
else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
|
||||
test_syntax_error (_("`)' expected, found %s"), argv[pos]);
|
||||
advance (0);
|
||||
return (value);
|
||||
}
|
||||
|
||||
/* are there enough arguments left that this could be dyadic? */
|
||||
if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
|
||||
value = binary_operator ();
|
||||
|
||||
/* Might be a switch type argument */
|
||||
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
|
||||
{
|
||||
if (test_unop (argv[pos]))
|
||||
value = unary_operator ();
|
||||
else
|
||||
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = argv[pos][0] != '\0';
|
||||
advance (0);
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
static int
|
||||
stat_mtime (fn, st, ts)
|
||||
char *fn;
|
||||
struct stat *st;
|
||||
struct timespec *ts;
|
||||
{
|
||||
int r;
|
||||
|
||||
r = sh_stat (fn, st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ts = get_stat_mtime (st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
filecomp (s, t, op)
|
||||
char *s, *t;
|
||||
int op;
|
||||
{
|
||||
struct stat st1, st2;
|
||||
struct timespec ts1, ts2;
|
||||
int r1, r2;
|
||||
|
||||
if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
|
||||
{
|
||||
if (op == EF)
|
||||
return (FALSE);
|
||||
}
|
||||
if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
|
||||
{
|
||||
if (op == EF)
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
|
||||
case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
|
||||
case EF: return (same_file (s, t, &st1, &st2));
|
||||
}
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
arithcomp (s, t, op, flags)
|
||||
char *s, *t;
|
||||
int op, flags;
|
||||
{
|
||||
intmax_t l, r;
|
||||
int expok;
|
||||
|
||||
if (flags & TEST_ARITHEXP)
|
||||
{
|
||||
l = evalexp (s, &expok);
|
||||
if (expok == 0)
|
||||
return (FALSE); /* should probably longjmp here */
|
||||
r = evalexp (t, &expok);
|
||||
if (expok == 0)
|
||||
return (FALSE); /* ditto */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (legal_number (s, &l) == 0)
|
||||
integer_expected_error (s);
|
||||
if (legal_number (t, &r) == 0)
|
||||
integer_expected_error (t);
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case EQ: return (l == r);
|
||||
case NE: return (l != r);
|
||||
case LT: return (l < r);
|
||||
case GT: return (l > r);
|
||||
case LE: return (l <= r);
|
||||
case GE: return (l >= r);
|
||||
}
|
||||
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
patcomp (string, pat, op)
|
||||
char *string, *pat;
|
||||
int op;
|
||||
{
|
||||
int m;
|
||||
|
||||
m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
|
||||
return ((op == EQ) ? (m == 0) : (m != 0));
|
||||
}
|
||||
|
||||
int
|
||||
binary_test (op, arg1, arg2, flags)
|
||||
char *op, *arg1, *arg2;
|
||||
int flags;
|
||||
{
|
||||
int patmatch;
|
||||
|
||||
patmatch = (flags & TEST_PATMATCH);
|
||||
|
||||
if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
|
||||
return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
|
||||
else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
|
||||
{
|
||||
if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
|
||||
return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
|
||||
else
|
||||
return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
|
||||
}
|
||||
else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
|
||||
return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
|
||||
|
||||
|
||||
else if (op[2] == 't')
|
||||
{
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */
|
||||
case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */
|
||||
case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */
|
||||
case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */
|
||||
}
|
||||
}
|
||||
else if (op[1] == 'e')
|
||||
{
|
||||
switch (op[2])
|
||||
{
|
||||
case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */
|
||||
case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */
|
||||
}
|
||||
}
|
||||
else if (op[2] == 'e')
|
||||
{
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */
|
||||
case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */
|
||||
case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */
|
||||
}
|
||||
}
|
||||
|
||||
return (FALSE); /* should never get here */
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
binary_operator ()
|
||||
{
|
||||
int value;
|
||||
char *w;
|
||||
|
||||
w = argv[pos + 1];
|
||||
if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
|
||||
((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */
|
||||
(w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */
|
||||
{
|
||||
value = binary_test (w, argv[pos], argv[pos + 2], 0);
|
||||
pos += 3;
|
||||
return (value);
|
||||
}
|
||||
|
||||
#if defined (PATTERN_MATCHING)
|
||||
if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
|
||||
{
|
||||
value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
|
||||
pos += 3;
|
||||
return (value);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
|
||||
{
|
||||
test_syntax_error (_("%s: binary operator expected"), w);
|
||||
/* NOTREACHED */
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
value = binary_test (w, argv[pos], argv[pos + 2], 0);
|
||||
pos += 3;
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
unary_operator ()
|
||||
{
|
||||
char *op;
|
||||
intmax_t r;
|
||||
|
||||
op = argv[pos];
|
||||
if (test_unop (op) == 0)
|
||||
return (FALSE);
|
||||
|
||||
/* the only tricky case is `-t', which may or may not take an argument. */
|
||||
if (op[1] == 't')
|
||||
{
|
||||
advance (0);
|
||||
if (pos < argc)
|
||||
{
|
||||
if (legal_number (argv[pos], &r))
|
||||
{
|
||||
advance (0);
|
||||
return (unary_test (op, argv[pos - 1]));
|
||||
}
|
||||
else
|
||||
return (FALSE);
|
||||
}
|
||||
else
|
||||
return (unary_test (op, "1"));
|
||||
}
|
||||
|
||||
/* All of the unary operators take an argument, so we first call
|
||||
unary_advance (), which checks to make sure that there is an
|
||||
argument, and then advances pos right past it. This means that
|
||||
pos - 1 is the location of the argument. */
|
||||
unary_advance ();
|
||||
return (unary_test (op, argv[pos - 1]));
|
||||
}
|
||||
|
||||
int
|
||||
unary_test (op, arg)
|
||||
char *op, *arg;
|
||||
{
|
||||
intmax_t r;
|
||||
struct stat stat_buf;
|
||||
SHELL_VAR *v;
|
||||
|
||||
switch (op[1])
|
||||
{
|
||||
case 'a': /* file exists in the file system? */
|
||||
case 'e':
|
||||
return (sh_stat (arg, &stat_buf) == 0);
|
||||
|
||||
case 'r': /* file is readable? */
|
||||
return (sh_eaccess (arg, R_OK) == 0);
|
||||
|
||||
case 'w': /* File is writeable? */
|
||||
return (sh_eaccess (arg, W_OK) == 0);
|
||||
|
||||
case 'x': /* File is executable? */
|
||||
return (sh_eaccess (arg, X_OK) == 0);
|
||||
|
||||
case 'O': /* File is owned by you? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 &&
|
||||
(uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
|
||||
|
||||
case 'G': /* File is owned by your group? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 &&
|
||||
(gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
|
||||
|
||||
case 'N':
|
||||
return (sh_stat (arg, &stat_buf) == 0 &&
|
||||
stat_buf.st_atime <= stat_buf.st_mtime);
|
||||
|
||||
case 'f': /* File is a file? */
|
||||
if (sh_stat (arg, &stat_buf) < 0)
|
||||
return (FALSE);
|
||||
|
||||
/* -f is true if the given file exists and is a regular file. */
|
||||
#if defined (S_IFMT)
|
||||
return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
|
||||
#else
|
||||
return (S_ISREG (stat_buf.st_mode));
|
||||
#endif /* !S_IFMT */
|
||||
|
||||
case 'd': /* File is a directory? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
|
||||
|
||||
case 's': /* File has something in it? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
|
||||
|
||||
case 'S': /* File is a socket? */
|
||||
#if !defined (S_ISSOCK)
|
||||
return (FALSE);
|
||||
#else
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
|
||||
#endif /* S_ISSOCK */
|
||||
|
||||
case 'c': /* File is character special? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
|
||||
|
||||
case 'b': /* File is block special? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
|
||||
|
||||
case 'p': /* File is a named pipe? */
|
||||
#ifndef S_ISFIFO
|
||||
return (FALSE);
|
||||
#else
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
|
||||
#endif /* S_ISFIFO */
|
||||
|
||||
case 'L': /* Same as -h */
|
||||
case 'h': /* File is a symbolic link? */
|
||||
#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
|
||||
return (FALSE);
|
||||
#else
|
||||
return ((arg[0] != '\0') &&
|
||||
(lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
|
||||
#endif /* S_IFLNK && HAVE_LSTAT */
|
||||
|
||||
case 'u': /* File is setuid? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
|
||||
|
||||
case 'g': /* File is setgid? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
|
||||
|
||||
case 'k': /* File has sticky bit set? */
|
||||
#if !defined (S_ISVTX)
|
||||
/* This is not Posix, and is not defined on some Posix systems. */
|
||||
return (FALSE);
|
||||
#else
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
|
||||
#endif
|
||||
|
||||
case 't': /* File fd is a terminal? */
|
||||
if (legal_number (arg, &r) == 0)
|
||||
return (FALSE);
|
||||
return ((r == (int)r) && isatty ((int)r));
|
||||
|
||||
case 'n': /* True if arg has some length. */
|
||||
return (arg[0] != '\0');
|
||||
|
||||
case 'z': /* True if arg has no length. */
|
||||
return (arg[0] == '\0');
|
||||
|
||||
case 'o': /* True if option `arg' is set. */
|
||||
return (minus_o_option_value (arg) == 1);
|
||||
|
||||
case 'v':
|
||||
v = find_variable (arg);
|
||||
return (v && var_isset (v) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
/* We can't actually get here, but this shuts up gcc. */
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Return TRUE if OP is one of the test command's binary operators. */
|
||||
int
|
||||
test_binop (op)
|
||||
char *op;
|
||||
{
|
||||
if (op[0] == '=' && op[1] == '\0')
|
||||
return (1); /* '=' */
|
||||
else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
|
||||
return (1);
|
||||
else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
|
||||
return (1); /* `==' and `!=' */
|
||||
#if defined (PATTERN_MATCHING)
|
||||
else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
|
||||
return (1);
|
||||
#endif
|
||||
else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
|
||||
return (0);
|
||||
else
|
||||
{
|
||||
if (op[2] == 't')
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': /* -nt */
|
||||
case 'o': /* -ot */
|
||||
case 'l': /* -lt */
|
||||
case 'g': /* -gt */
|
||||
return (1);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
else if (op[1] == 'e')
|
||||
switch (op[2])
|
||||
{
|
||||
case 'q': /* -eq */
|
||||
case 'f': /* -ef */
|
||||
return (1);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
else if (op[2] == 'e')
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': /* -ne */
|
||||
case 'g': /* -ge */
|
||||
case 'l': /* -le */
|
||||
return (1);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return non-zero if OP is one of the test command's unary operators. */
|
||||
int
|
||||
test_unop (op)
|
||||
char *op;
|
||||
{
|
||||
if (op[0] != '-' || op[2] != 0)
|
||||
return (0);
|
||||
|
||||
switch (op[1])
|
||||
{
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'k': case 'n':
|
||||
case 'o': case 'p': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'z':
|
||||
case 'G': case 'L': case 'O': case 'S': case 'N':
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
two_arguments ()
|
||||
{
|
||||
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
return (argv[pos + 1][0] == '\0');
|
||||
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
|
||||
{
|
||||
if (test_unop (argv[pos]))
|
||||
return (unary_operator ());
|
||||
else
|
||||
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
|
||||
}
|
||||
else
|
||||
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
|
||||
|
||||
/* This could be augmented to handle `-t' as equivalent to `-t 1', but
|
||||
POSIX requires that `-t' be given an argument. */
|
||||
#define ONE_ARG_TEST(s) ((s)[0] != '\0')
|
||||
|
||||
static int
|
||||
three_arguments ()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (test_binop (argv[pos+1]))
|
||||
{
|
||||
value = binary_operator ();
|
||||
pos = argc;
|
||||
}
|
||||
else if (ANDOR (argv[pos+1]))
|
||||
{
|
||||
if (argv[pos+1][1] == 'a')
|
||||
value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
|
||||
else
|
||||
value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
|
||||
pos = argc;
|
||||
}
|
||||
else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
advance (1);
|
||||
value = !two_arguments ();
|
||||
}
|
||||
else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
|
||||
{
|
||||
value = ONE_ARG_TEST(argv[pos+1]);
|
||||
pos = argc;
|
||||
}
|
||||
else
|
||||
test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/* This is an implementation of a Posix.2 proposal by David Korn. */
|
||||
static int
|
||||
posixtest ()
|
||||
{
|
||||
int value;
|
||||
|
||||
switch (argc - 1) /* one extra passed in */
|
||||
{
|
||||
case 0:
|
||||
value = FALSE;
|
||||
pos = argc;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = ONE_ARG_TEST(argv[1]);
|
||||
pos = argc;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = two_arguments ();
|
||||
pos = argc;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = three_arguments ();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
advance (1);
|
||||
value = !three_arguments ();
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
value = expr ();
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* [:
|
||||
* '[' expr ']'
|
||||
* test:
|
||||
* test expr
|
||||
*/
|
||||
int
|
||||
test_command (margc, margv)
|
||||
int margc;
|
||||
char **margv;
|
||||
{
|
||||
int value;
|
||||
int code;
|
||||
|
||||
USE_VAR(margc);
|
||||
|
||||
code = setjmp (test_exit_buf);
|
||||
|
||||
if (code)
|
||||
return (test_error_return);
|
||||
|
||||
argv = margv;
|
||||
|
||||
if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
|
||||
{
|
||||
--margc;
|
||||
|
||||
if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
|
||||
test_syntax_error (_("missing `]'"), (char *)NULL);
|
||||
|
||||
if (margc < 2)
|
||||
test_exit (SHELL_BOOLEAN (FALSE));
|
||||
}
|
||||
|
||||
argc = margc;
|
||||
pos = 1;
|
||||
|
||||
if (pos >= argc)
|
||||
test_exit (SHELL_BOOLEAN (FALSE));
|
||||
|
||||
noeval = 0;
|
||||
value = posixtest ();
|
||||
|
||||
if (pos != argc)
|
||||
test_syntax_error (_("too many arguments"), (char *)NULL);
|
||||
|
||||
test_exit (SHELL_BOOLEAN (value));
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
BUILD_DIR=/usr/local/build/chet/bash/bash-current
|
||||
BUILD_DIR=/usr/local/build/bash/bash-current
|
||||
THIS_SH=$BUILD_DIR/bash
|
||||
PATH=$PATH:$BUILD_DIR
|
||||
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
BUILD_DIR=/usr/local/build/chet/bash/bash-current
|
||||
THIS_SH=$BUILD_DIR/bash
|
||||
PATH=$PATH:$BUILD_DIR
|
||||
|
||||
export THIS_SH PATH
|
||||
|
||||
rm -f /tmp/xx
|
||||
|
||||
/bin/sh "$@"
|
||||
+1
-1
@@ -8,7 +8,7 @@ unalias: usage: unalias [-a] name [name ...]
|
||||
declare -fr func
|
||||
./errors.tests: line 36: func: readonly function
|
||||
./errors.tests: line 39: unset: -x: invalid option
|
||||
unset: usage: unset [-f] [-v] [name ...]
|
||||
unset: usage: unset [-f] [-v] [-n] [name ...]
|
||||
./errors.tests: line 42: unset: func: cannot unset: readonly function
|
||||
./errors.tests: line 45: declare: func: readonly function
|
||||
./errors.tests: line 49: unset: XPATH: cannot unset: readonly variable
|
||||
|
||||
Reference in New Issue
Block a user