summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChet Ramey <chet.ramey@case.edu>2011-12-12 21:56:29 -0500
committerChet Ramey <chet.ramey@case.edu>2011-12-12 21:56:29 -0500
commit9e51a74d57554525639a5ce59f7011386223c5c0 (patch)
tree2b1cb7afbcfb284fdbb2f4730e919a9dc91d3f83
parent1b13a2904ae75ef7bf1c7550c99d41a11a070321 (diff)
downloadbash-9e51a74d57554525639a5ce59f7011386223c5c0.tar.gz
commit bash-20100325 snapshot
-rw-r--r--CWRU/CWRU.chlog96
-rw-r--r--CWRU/CWRU.chlog~94
-rw-r--r--CWRU/misc/sigs.c2
-rw-r--r--CWRU/misc/sigs.c~47
-rw-r--r--Makefile.in4
-rw-r--r--Makefile.in~6
-rw-r--r--autom4te.cache/output.05
-rw-r--r--autom4te.cache/requests22
-rw-r--r--autom4te.cache/traces.02
-rw-r--r--bashhist.c11
-rw-r--r--bashhist.c.save898
-rw-r--r--bashhist.c~13
-rw-r--r--bashline.c3
-rw-r--r--bashline.c~4
-rw-r--r--builtins/printf.def2
-rw-r--r--builtins/trap.def16
-rw-r--r--builtins/trap.def~31
-rw-r--r--config.h.in3
-rw-r--r--config.h.in~3
-rwxr-xr-xconfigure5
-rw-r--r--configure.in4
-rw-r--r--configure.in~5
-rw-r--r--execute_cmd.c1
-rw-r--r--execute_cmd.c~4
-rw-r--r--externs.h2
-rw-r--r--externs.h~5
-rw-r--r--lib/readline/complete.c6
-rw-r--r--lib/readline/complete.c~16
-rw-r--r--lib/readline/signals.c~671
-rw-r--r--lib/sh/eaccess.c13
-rw-r--r--lib/sh/eaccess.c~226
-rw-r--r--parse.y91
-rw-r--r--parse.y.save5915
-rw-r--r--parse.y~86
-rw-r--r--parser.h8
-rw-r--r--parser.h~64
-rw-r--r--po/ja.po1267
-rw-r--r--sig.c20
-rw-r--r--sig.c~11
-rw-r--r--subst.c64
-rw-r--r--subst.c~66
-rw-r--r--subst.h17
-rw-r--r--subst.h~4
-rwxr-xr-xtests/RUN-ONE-TEST2
-rw-r--r--trap.c24
-rw-r--r--trap.c~27
-rw-r--r--trap.h3
-rw-r--r--trap.h~3
48 files changed, 9655 insertions, 237 deletions
diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog
index 9d4c7215..220af121 100644
--- a/CWRU/CWRU.chlog
+++ b/CWRU/CWRU.chlog
@@ -9524,7 +9524,7 @@ subst.c
- in parameter_brace_expand, before calling parameter_brace_expand_rhs,
add Q_DOLBRACE to `quoted' if we're within double quotes.
- in expand_word_internal, if the Q_DOLBRACE flag is set, remove a
- backslash escaping a { or }. Result of a Posix discussion on the
+ backslash escaping a }. Result of a Posix discussion on the
austin-group list
2/27
@@ -9595,3 +9595,97 @@ lib/sh/xmbsrtowcs.c
mbsnrtowcs is available and the indices are not required. Called
from xdupmbstowcs as required. Initial patch from
<0xe2.0x9a.0x9b@gmail.com>
+
+ 3/22
+ ----
+print_cmd.c
+ - call print_deferred_heredocs virtually every time a recursive call
+ to make_command_string_internal is made so here documents get
+ printed correctly when they are attached to commands inside compound
+ commands such as for and while. Fixes bug reported by Mike
+ Frysinger <vapier@gentoo.org>
+
+ 3/25
+ ----
+builtins/printf.def
+ - fix have_precision case in PF macro to call printf with precision
+ instead of fieldwidth argument. Fixes bug reported by Rob Robason
+ <rob@robason.net>
+
+ 3/26
+ ----
+trap.[ch]
+ - new function, signal_is_hard_ignored, returns true if the shell
+ inherited SIG_IGN as a signal's disposition
+ - new function, set_original_signal (sig, handler), provides interface
+ for rest of shell to set original_signals[sig] = handler
+
+execute_cmd.c
+ - execute_disk_command needs to call reset_terminating_signals in the
+ child process before resetting traps with restore_original_signals
+
+builtins/trap.def
+ - call initialize_terminating_signals before calling display_traps for
+ trap -p or trap without any other arguments. Possible future use
+
+lib/readline/complete.c
+ - rl_filename_completion_function needs to call
+ rl_filename_dequoting_function with `dirname' (which has already
+ been tilde-expanded) instead of `users_dirname', because it calls
+ opendir with `dirname'. Fixes bug reported by Stefan H. Holek
+ <stefan@jarn.com>
+
+ 3/27
+ ----
+sig.c
+ - experimental change to set_signal_handler: when setting the SIGCHLD
+ handler, set the SA_RESTART flag so that interruptible system calls
+ get restarted after a child dies. Fixes bug reported by Tomas
+ Trnka <tomastrnka@gmx.com>, but needs further evaluation
+
+lib/sh/eaccess.c
+ - eaccess(2) apparently does only half the job: it does not check that
+ the permission bits on a file actually allow, for instance, execution.
+ Need to augment with a call to sh_stataccess if eaccess returns
+ success on FreeBSD. Fixes FreeBSD problem reported by Jonan Hattne
+ <johan.hattne@utsouthwestern.edu>
+
+ 3/28
+ ----
+parse.y,bashline.c,externs.h
+ - history_delimiting_chars now takes a const char * as an argument:
+ the line being added to the history. Changed callers
+
+parse.y
+ - bash_add_history should not add a semicolon separator if the current
+ history entry already ends in a newline. It can introduce syntax
+ errors (e.g., when it results in a null command before a close brace).
+ Fixes bug reported by Andreas Schwab <schwab@linux-m68k.org>
+
+parse.y
+ - history_delimiting_chars needs to return a newline instead of a
+ semicolon if it thinks the current line starts a here document
+ (if it contains `<<'). Also keeps track of the fact with a new
+ static variable, LAST_WAS_HEREDOC, so it can return the right
+ sequence of newlines later for the here-document body. Fixes bug
+ reported by Andreas Schwab <schwab@linux-m68k.org>
+
+ 3/29
+ ----
+lib/sh/eaccess.c
+ - if the system has faccessat, sh_eaccess will now use it in
+ preference to all other options
+
+ 3/30
+ ----
+subst.h
+ - new string_extract and extract_dollar_brace_string flag value:
+ SX_POSIXEXP, set if the shell is expanding one of the new Posix
+ pattern removal word expansions
+
+parser.h
+ - new definitions for "word expansion state", shared between parse.y
+ and subst.c
+
+subst.c
+ - include parser.h
diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~
index 9e94652e..02e11f7c 100644
--- a/CWRU/CWRU.chlog~
+++ b/CWRU/CWRU.chlog~
@@ -9524,7 +9524,7 @@ subst.c
- in parameter_brace_expand, before calling parameter_brace_expand_rhs,
add Q_DOLBRACE to `quoted' if we're within double quotes.
- in expand_word_internal, if the Q_DOLBRACE flag is set, remove a
- backslash escaping a { or }. Result of a Posix discussion on the
+ backslash escaping a }. Result of a Posix discussion on the
austin-group list
2/27
@@ -9592,5 +9592,93 @@ configure.in,config.h.in
lib/sh/xmbsrtowcs.c
- new function, xdupmbstowcs2, fast version of xdupmbstowcs used when
- mbsnrtowcs is available and the indices are not required. Initial
- patch from <0xe2.0x9a.0x9b@gmail.com>
+ mbsnrtowcs is available and the indices are not required. Called
+ from xdupmbstowcs as required. Initial patch from
+ <0xe2.0x9a.0x9b@gmail.com>
+
+ 3/22
+ ----
+print_cmd.c
+ - call print_deferred_heredocs virtually every time a recursive call
+ to make_command_string_internal is made so here documents get
+ printed correctly when they are attached to commands inside compound
+ commands such as for and while. Fixes bug reported by Mike
+ Frysinger <vapier@gentoo.org>
+
+ 3/25
+ ----
+builtins/printf.def
+ - fix have_precision case in PF macro to call printf with precision
+ instead of fieldwidth argument. Fixes bug reported by Rob Robason
+ <rob@robason.net>
+
+ 3/26
+ ----
+trap.[ch]
+ - new function, signal_is_hard_ignored, returns true if the shell
+ inherited SIG_IGN as a signal's disposition
+ - new function, set_original_signal (sig, handler), provides interface
+ for rest of shell to set original_signals[sig] = handler
+
+execute_cmd.c
+ - execute_disk_command needs to call reset_terminating_signals in the
+ child process before resetting traps with restore_original_signals
+
+builtins/trap.def
+ - call initialize_terminating_signals before calling display_traps for
+ trap -p or trap without any other arguments. Possible future use
+
+lib/readline/complete.c
+ - rl_filename_completion_function needs to call
+ rl_filename_dequoting_function with `dirname' (which has already
+ been tilde-expanded) instead of `users_dirname', because it calls
+ opendir with `dirname'. Fixes bug reported by Stefan H. Holek
+ <stefan@jarn.com>
+
+ 3/27
+ ----
+sig.c
+ - experimental change to set_signal_handler: when setting the SIGCHLD
+ handler, set the SA_RESTART flag so that interruptible system calls
+ get restarted after a child dies. Fixes bug reported by Tomas
+ Trnka <tomastrnka@gmx.com>, but needs further evaluation
+
+lib/sh/eaccess.c
+ - eaccess(2) apparently does only half the job: it does not check that
+ the permission bits on a file actually allow, for instance, execution.
+ Need to augment with a call to sh_stataccess if eaccess returns
+ success on FreeBSD. Fixes FreeBSD problem reported by Jonan Hattne
+ <johan.hattne@utsouthwestern.edu>
+
+ 3/28
+ ----
+parse.y,bashline.c,externs.h
+ - history_delimiting_chars now takes a const char * as an argument:
+ the line being added to the history. Changed callers
+
+parse.y
+ - bash_add_history should not add a semicolon separator if the current
+ history entry already ends in a newline. It can introduce syntax
+ errors (e.g., when it results in a null command before a close brace).
+ Fixes bug reported by Andreas Schwab <schwab@linux-m68k.org>
+
+parse.y
+ - history_delimiting_chars needs to return a newline instead of a
+ semicolon if it thinks the current line starts a here document
+ (if it contains `<<'). Also keeps track of the fact with a new
+ static variable, LAST_WAS_HEREDOC, so it can return the right
+ sequence of newlines later for the here-document body. Fixes bug
+ reported by Andreas Schwab <schwab@linux-m68k.org>
+
+ 3/29
+ ----
+lib/sh/eaccess.c
+ - if the system has faccessat, sh_eaccess will now use it in
+ preference to all other options
+
+ 3/30
+ ----
+subst.h
+ - new string_extract and extract_dollar_brace_string flag value:
+ SX_POSIXEXP, set if the shell is expanding one of the new Posix
+ pattern removal word expansions
diff --git a/CWRU/misc/sigs.c b/CWRU/misc/sigs.c
index 097ab317..6389ff2c 100644
--- a/CWRU/misc/sigs.c
+++ b/CWRU/misc/sigs.c
@@ -21,7 +21,7 @@
#include <signal.h>
#include <stdio.h>
-extern char *sys_siglist[];
+extern const char * const sys_siglist[];
typedef void sighandler();
diff --git a/CWRU/misc/sigs.c~ b/CWRU/misc/sigs.c~
new file mode 100644
index 00000000..097ab317
--- /dev/null
+++ b/CWRU/misc/sigs.c~
@@ -0,0 +1,47 @@
+/* sigs - print signal dispositions for a process */
+
+/* Copyright (C) 1990-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 <signal.h>
+#include <stdio.h>
+
+extern char *sys_siglist[];
+
+typedef void sighandler();
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ register int i;
+ sighandler *h;
+
+ for (i = 1; i < NSIG; i++) {
+ h = signal(i, SIG_DFL);
+ if (h != SIG_DFL) {
+ if (h == SIG_IGN)
+ fprintf(stderr, "%d: ignored (%s)\n", i, sys_siglist[i]);
+ else
+ fprintf(stderr, "%d: caught (%s)\n", i, sys_siglist[i]);
+ }
+ }
+ exit(0);
+}
+
+
diff --git a/Makefile.in b/Makefile.in
index 66c27e59..54e0f39e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -969,7 +969,7 @@ mailcheck.o: execute_cmd.h mailcheck.h
make_cmd.o: config.h bashtypes.h ${BASHINCDIR}/filecntl.h bashansi.h
make_cmd.o: command.h ${BASHINCDIR}/stdc.h general.h xmalloc.h error.h flags.h make_cmd.h
make_cmd.o: variables.h arrayfunc.h conftypes.h array.h hashlib.h subst.h input.h externs.h
-make_cmd.o: jobs.h quit.h siglist.h syntax.h dispose_cmd.h
+make_cmd.o: jobs.h quit.h siglist.h syntax.h dispose_cmd.h parser.h
make_cmd.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/ocache.h
y.tab.o: config.h bashtypes.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/memalloc.h
y.tab.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h
@@ -1022,7 +1022,7 @@ subst.o: config.h bashtypes.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDI
subst.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h
subst.o: general.h xmalloc.h bashtypes.h variables.h arrayfunc.h conftypes.h array.h hashlib.h
subst.o: quit.h ${BASHINCDIR}/maxpath.h unwind_prot.h dispose_cmd.h
-subst.o: make_cmd.h subst.h sig.h pathnames.h externs.h
+subst.o: make_cmd.h subst.h sig.h pathnames.h externs.h parser.h
subst.o: flags.h jobs.h siglist.h execute_cmd.h ${BASHINCDIR}/filecntl.h trap.h pathexp.h
subst.o: mailcheck.h input.h $(DEFSRC)/getopt.h $(DEFSRC)/common.h
subst.o: bashline.h bashhist.h ${GLOB_LIBSRC}/strmatch.h
diff --git a/Makefile.in~ b/Makefile.in~
index 20abf6f4..656de92f 100644
--- a/Makefile.in~
+++ b/Makefile.in~
@@ -1,6 +1,6 @@
-# Makefile for bash-4.0, version 3.5
+# Makefile for bash-4.1, version 4.1
#
-# Copyright (C) 1996-2009 Free Software Foundation, Inc.
+# Copyright (C) 1996-2010 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -969,7 +969,7 @@ mailcheck.o: execute_cmd.h mailcheck.h
make_cmd.o: config.h bashtypes.h ${BASHINCDIR}/filecntl.h bashansi.h
make_cmd.o: command.h ${BASHINCDIR}/stdc.h general.h xmalloc.h error.h flags.h make_cmd.h
make_cmd.o: variables.h arrayfunc.h conftypes.h array.h hashlib.h subst.h input.h externs.h
-make_cmd.o: jobs.h quit.h siglist.h syntax.h dispose_cmd.h
+make_cmd.o: jobs.h quit.h siglist.h syntax.h dispose_cmd.h parser.h
make_cmd.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/ocache.h
y.tab.o: config.h bashtypes.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/memalloc.h
y.tab.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h
diff --git a/autom4te.cache/output.0 b/autom4te.cache/output.0
index 922ef661..a9a53c8d 100644
--- a/autom4te.cache/output.0
+++ b/autom4te.cache/output.0
@@ -1,5 +1,5 @@
@%:@! /bin/sh
-@%:@ From configure.in for Bash 4.1, version 4.022.
+@%:@ From configure.in for Bash 4.1, version 4.023.
@%:@ Guess values for system-dependent variables and create Makefiles.
@%:@ Generated by GNU Autoconf 2.63 for bash 4.1-maint.
@%:@
@@ -13577,7 +13577,8 @@ done
-for ac_func in bcopy bzero confstr fnmatch \
+
+for ac_func in bcopy bzero confstr faccessat fnmatch \
getaddrinfo gethostbyname getservbyname getservent inet_aton \
memmove pathconf putenv raise regcomp regexec \
setenv setlinebuf setlocale setvbuf siginterrupt strchr \
diff --git a/autom4te.cache/requests b/autom4te.cache/requests
index 014c1526..4a5e08c9 100644
--- a/autom4te.cache/requests
+++ b/autom4te.cache/requests
@@ -15,25 +15,25 @@
'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_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_GNU_GETTEXT_INTL_SUBDIR' => 1,
'_m4_warn' => 1,
@@ -49,13 +49,13 @@
'AC_CANONICAL_BUILD' => 1,
'AC_FC_FREEFORM' => 1,
'AH_OUTPUT' => 1,
- 'AC_CONFIG_AUX_DIR' => 1,
'_AM_SUBST_NOTMAKE' => 1,
- 'AM_PROG_CC_C_O' => 1,
- 'm4_pattern_allow' => 1,
+ 'AC_CONFIG_AUX_DIR' => 1,
'sinclude' => 1,
- 'AM_CONDITIONAL' => 1,
+ 'm4_pattern_allow' => 1,
+ 'AM_PROG_CC_C_O' => 1,
'AC_CANONICAL_SYSTEM' => 1,
+ 'AM_CONDITIONAL' => 1,
'AC_CONFIG_HEADERS' => 1,
'AC_DEFINE_TRACE_LITERAL' => 1,
'm4_include' => 1,
diff --git a/autom4te.cache/traces.0 b/autom4te.cache/traces.0
index e4d852a9..0ac783ac 100644
--- a/autom4te.cache/traces.0
+++ b/autom4te.cache/traces.0
@@ -1352,6 +1352,8 @@ m4trace:configure.in:730: -1- AH_OUTPUT([HAVE_BZERO], [/* Define to 1 if you hav
#undef HAVE_BZERO])
m4trace:configure.in:730: -1- AH_OUTPUT([HAVE_CONFSTR], [/* Define to 1 if you have the `confstr\' function. */
#undef HAVE_CONFSTR])
+m4trace:configure.in:730: -1- AH_OUTPUT([HAVE_FACCESSAT], [/* Define to 1 if you have the `faccessat\' function. */
+#undef HAVE_FACCESSAT])
m4trace:configure.in:730: -1- AH_OUTPUT([HAVE_FNMATCH], [/* Define to 1 if you have the `fnmatch\' function. */
#undef HAVE_FNMATCH])
m4trace:configure.in:730: -1- AH_OUTPUT([HAVE_GETADDRINFO], [/* Define to 1 if you have the `getaddrinfo\' function. */
diff --git a/bashhist.c b/bashhist.c
index 5a8b9c56..aad8e23b 100644
--- a/bashhist.c
+++ b/bashhist.c
@@ -184,6 +184,7 @@ int dont_save_function_defs;
extern int current_command_line_count;
extern struct dstack dstack;
+extern int parser_state;
static int bash_history_inhibit_expansion __P((char *, int));
#if defined (READLINE)
@@ -731,7 +732,7 @@ bash_add_history (line)
add_it = 1;
if (command_oriented_history && current_command_line_count > 1)
{
- chars_to_add = literal_history ? "\n" : history_delimiting_chars ();
+ chars_to_add = literal_history ? "\n" : history_delimiting_chars (line);
using_history ();
current = previous_history ();
@@ -751,8 +752,12 @@ bash_add_history (line)
chars_to_add = "";
}
-if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';')
- chars_to_add++;
+ /* If we're not in some kind of quoted construct, the current history
+ entry ends with a newline, and we're going to add a semicolon,
+ don't. In some cases, it results in a syntax error (e.g., before
+ a close brace), and it should not be needed. */
+ if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';')
+ chars_to_add++;
new_line = (char *)xmalloc (1
+ curlen
diff --git a/bashhist.c.save b/bashhist.c.save
new file mode 100644
index 00000000..a80968fd
--- /dev/null
+++ b/bashhist.c.save
@@ -0,0 +1,898 @@
+/* bashhist.c -- bash interface to the GNU history library. */
+
+/* Copyright (C) 1993-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"
+
+#if defined (HISTORY)
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "bashtypes.h"
+#include <stdio.h>
+#include <errno.h>
+#include "bashansi.h"
+#include "posixstat.h"
+#include "filecntl.h"
+
+#include "bashintl.h"
+
+#if defined (SYSLOG_HISTORY)
+# include <syslog.h>
+#endif
+
+#include "shell.h"
+#include "flags.h"
+#include "input.h"
+#include "parser.h" /* for the struct dstack stuff. */
+#include "pathexp.h" /* for the struct ignorevar stuff */
+#include "bashhist.h" /* matching prototypes and declarations */
+#include "builtins/common.h"
+
+#include <readline/history.h>
+#include <glob/glob.h>
+#include <glob/strmatch.h>
+
+#if defined (READLINE)
+# include "bashline.h"
+extern int rl_done, rl_dispatching; /* should really include readline.h */
+#endif
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+static int histignore_item_func __P((struct ign *));
+static int check_history_control __P((char *));
+static void hc_erasedups __P((char *));
+static void really_add_history __P((char *));
+
+static struct ignorevar histignore =
+{
+ "HISTIGNORE",
+ (struct ign *)0,
+ 0,
+ (char *)0,
+ (sh_iv_item_func_t *)histignore_item_func,
+};
+
+#define HIGN_EXPAND 0x01
+
+/* Declarations of bash history variables. */
+/* Non-zero means to remember lines typed to the shell on the history
+ list. This is different than the user-controlled behaviour; this
+ becomes zero when we read lines from a file, for example. */
+int remember_on_history = 1;
+int enable_history_list = 1; /* value for `set -o history' */
+
+/* The number of lines that Bash has added to this history session. The
+ difference between the number of the top element in the history list
+ (offset from history_base) and the number of lines in the history file.
+ Appending this session's history to the history file resets this to 0. */
+int history_lines_this_session;
+
+/* The number of lines that Bash has read from the history file. */
+int history_lines_in_file;
+
+#if defined (BANG_HISTORY)
+/* Non-zero means do no history expansion on this line, regardless
+ of what history_expansion says. */
+int history_expansion_inhibited;
+#endif
+
+/* With the old default, every line was saved in the history individually.
+ I.e., if the user enters:
+ bash$ for i in a b c
+ > do
+ > echo $i
+ > done
+ Each line will be individually saved in the history.
+ bash$ history
+ 10 for i in a b c
+ 11 do
+ 12 echo $i
+ 13 done
+ 14 history
+ If the variable command_oriented_history is set, multiple lines
+ which form one command will be saved as one history entry.
+ bash$ for i in a b c
+ > do
+ > echo $i
+ > done
+ bash$ history
+ 10 for i in a b c
+ do
+ echo $i
+ done
+ 11 history
+ The user can then recall the whole command all at once instead
+ of just being able to recall one line at a time.
+
+ This is now enabled by default.
+ */
+int command_oriented_history = 1;
+
+/* Set to 1 if the first line of a possibly-multi-line command was saved
+ in the history list. Managed by maybe_add_history(), but global so
+ the history-manipluating builtins can see it. */
+int current_command_first_line_saved = 0;
+
+/* Non-zero means to store newlines in the history list when using
+ command_oriented_history rather than trying to use semicolons. */
+int literal_history;
+
+/* Non-zero means to append the history to the history file at shell
+ exit, even if the history has been stifled. */
+int force_append_history;
+
+/* A nit for picking at history saving. Flags have the following values:
+
+ Value == 0 means save all lines parsed by the shell on the history.
+ Value & HC_IGNSPACE means save all lines that do not start with a space.
+ Value & HC_IGNDUPS means save all lines that do not match the last
+ line saved.
+ Value & HC_ERASEDUPS means to remove all other matching lines from the
+ history list before saving the latest line. */
+int history_control;
+
+/* Set to 1 if the last command was added to the history list successfully
+ as a separate history entry; set to 0 if the line was ignored or added
+ to a previous entry as part of command-oriented-history processing. */
+int hist_last_line_added;
+
+/* Set to 1 if builtins/history.def:push_history added the last history
+ entry. */
+int hist_last_line_pushed;
+
+#if defined (READLINE)
+/* If non-zero, and readline is being used, the user is offered the
+ chance to re-edit a failed history expansion. */
+int history_reediting;
+
+/* If non-zero, and readline is being used, don't directly execute a
+ line with history substitution. Reload it into the editing buffer
+ instead and let the user further edit and confirm with a newline. */
+int hist_verify;
+
+#endif /* READLINE */
+
+/* Non-zero means to not save function definitions in the history list. */
+int dont_save_function_defs;
+
+/* Variables declared in other files used here. */
+extern int current_command_line_count;
+
+extern struct dstack dstack;
+
+static int bash_history_inhibit_expansion __P((char *, int));
+#if defined (READLINE)
+static void re_edit __P((char *));
+#endif
+static int history_expansion_p __P((char *));
+static int shell_comment __P((char *));
+static int should_expand __P((char *));
+static HIST_ENTRY *last_history_entry __P((void));
+static char *expand_histignore_pattern __P((char *));
+static int history_should_ignore __P((char *));
+
+/* Is the history expansion starting at string[i] one that should not
+ be expanded? */
+static int
+bash_history_inhibit_expansion (string, i)
+ char *string;
+ int i;
+{
+ /* The shell uses ! as a pattern negation character in globbing [...]
+ expressions, so let those pass without expansion. */
+ if (i > 0 && (string[i - 1] == '[') && member (']', string + i + 1))
+ return (1);
+ /* The shell uses ! as the indirect expansion character, so let those
+ expansions pass as well. */
+ else if (i > 1 && string[i - 1] == '{' && string[i - 2] == '$' &&
+ member ('}', string + i + 1))
+ return (1);
+#if defined (EXTENDED_GLOB)
+ else if (extended_glob && i > 1 && string[i+1] == '(' && member (')', string + i + 2))
+ return (1);
+#endif
+ else
+ return (0);
+}
+
+void
+bash_initialize_history ()
+{
+ history_quotes_inhibit_expansion = 1;
+ history_search_delimiter_chars = ";&()|<>";
+ history_inhibit_expansion_function = bash_history_inhibit_expansion;
+#if defined (BANG_HISTORY)
+ sv_histchars ("histchars");
+#endif
+}
+
+void
+bash_history_reinit (interact)
+ int interact;
+{
+#if defined (BANG_HISTORY)
+ history_expansion = interact != 0;
+ history_expansion_inhibited = 1;
+#endif
+ remember_on_history = enable_history_list = interact != 0;
+ history_inhibit_expansion_function = bash_history_inhibit_expansion;
+}
+
+void
+bash_history_disable ()
+{
+ remember_on_history = 0;
+#if defined (BANG_HISTORY)
+ history_expansion_inhibited = 1;
+#endif
+}
+
+void
+bash_history_enable ()
+{
+ remember_on_history = 1;
+#if defined (BANG_HISTORY)
+ history_expansion_inhibited = 0;
+#endif
+ history_inhibit_expansion_function = bash_history_inhibit_expansion;
+ sv_history_control ("HISTCONTROL");
+ sv_histignore ("HISTIGNORE");
+}
+
+/* Load the history list from the history file. */
+void
+load_history ()
+{
+ char *hf;
+
+ /* Truncate history file for interactive shells which desire it.
+ Note that the history file is automatically truncated to the
+ size of HISTSIZE if the user does not explicitly set the size
+ differently. */
+ set_if_not ("HISTSIZE", "500");
+ sv_histsize ("HISTSIZE");
+
+ set_if_not ("HISTFILESIZE", get_string_value ("HISTSIZE"));
+ sv_histsize ("HISTFILESIZE");
+
+ /* Read the history in HISTFILE into the history list. */
+ hf = get_string_value ("HISTFILE");
+
+ if (hf && *hf && file_exists (hf))
+ {
+ read_history (hf);
+ using_history ();
+ history_lines_in_file = where_history ();
+ }
+}
+
+void
+bash_clear_history ()
+{
+ clear_history ();
+ history_lines_this_session = 0;
+}
+
+/* Delete and free the history list entry at offset I. */
+int
+bash_delete_histent (i)
+ int i;
+{
+ HIST_ENTRY *discard;
+
+ discard = remove_history (i);
+ if (discard)
+ free_history_entry (discard);
+ history_lines_this_session--;
+
+ return 1;
+}
+
+int
+bash_delete_last_history ()
+{
+ register int i;
+ HIST_ENTRY **hlist, *histent;
+ int r;
+
+ hlist = history_list ();
+ if (hlist == NULL)
+ return 0;
+
+ for (i = 0; hlist[i]; i++)
+ ;
+ i--;
+
+ /* History_get () takes a parameter that must be offset by history_base. */
+ histent = history_get (history_base + i); /* Don't free this */
+ if (histent == NULL)
+ return 0;
+
+ r = bash_delete_histent (i);
+
+ if (where_history () > history_length)
+ history_set_pos (history_length);
+
+ return r;
+}
+
+#ifdef INCLUDE_UNUSED
+/* Write the existing history out to the history file. */
+void
+save_history ()
+{
+ char *hf;
+
+ hf = get_string_value ("HISTFILE");
+ if (hf && *hf && file_exists (hf))
+ {
+ /* Append only the lines that occurred this session to
+ the history file. */
+ using_history ();
+
+ if (history_lines_this_session < where_history () || force_append_history)
+ append_history (history_lines_this_session, hf);
+ else
+ write_history (hf);
+ sv_histsize ("HISTFILESIZE");
+ }
+}
+#endif
+
+int
+maybe_append_history (filename)
+ char *filename;
+{
+ int fd, result;
+ struct stat buf;
+
+ result = EXECUTION_SUCCESS;
+ if (history_lines_this_session && (history_lines_this_session < where_history ()))
+ {
+ /* If the filename was supplied, then create it if necessary. */
+ if (stat (filename, &buf) == -1 && errno == ENOENT)
+ {
+ fd = open (filename, O_WRONLY|O_CREAT, 0600);
+ if (fd < 0)
+ {
+ builtin_error (_("%s: cannot create: %s"), filename, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ close (fd);
+ }
+ result = append_history (history_lines_this_session, filename);
+ history_lines_in_file += history_lines_this_session;
+ history_lines_this_session = 0;
+ }
+ return (result);
+}
+
+/* If this is an interactive shell, then append the lines executed
+ this session to the history file. */
+int
+maybe_save_shell_history ()
+{
+ int result;
+ char *hf;
+
+ result = 0;
+ if (history_lines_this_session)
+ {
+ hf = get_string_value ("HISTFILE");
+
+ if (hf && *hf)
+ {
+ /* If the file doesn't exist, then create it. */
+ if (file_exists (hf) == 0)
+ {
+ int file;
+ file = open (hf, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ if (file != -1)
+ close (file);
+ }
+
+ /* Now actually append the lines if the history hasn't been
+ stifled. If the history has been stifled, rewrite the
+ history file. */
+ using_history ();
+ if (history_lines_this_session <= where_history () || force_append_history)
+ {
+ result = append_history (history_lines_this_session, hf);
+ history_lines_in_file += history_lines_this_session;
+ }
+ else
+ {
+ result = write_history (hf);
+ history_lines_in_file = history_lines_this_session;
+ }
+ history_lines_this_session = 0;
+
+ sv_histsize ("HISTFILESIZE");
+ }
+ }
+ return (result);
+}
+
+#if defined (READLINE)
+/* Tell readline () that we have some text for it to edit. */
+static void
+re_edit (text)
+ char *text;
+{
+ if (bash_input.type == st_stdin)
+ bash_re_edit (text);
+}
+#endif /* READLINE */
+
+/* Return 1 if this line needs history expansion. */
+static int
+history_expansion_p (line)
+ char *line;
+{
+ register char *s;
+
+ for (s = line; *s; s++)
+ if (*s == history_expansion_char || *s == history_subst_char)
+ return 1;
+ return 0;
+}
+
+/* Do pre-processing on LINE. If PRINT_CHANGES is non-zero, then
+ print the results of expanding the line if there were any changes.
+ If there is an error, return NULL, otherwise the expanded line is
+ returned. If ADDIT is non-zero the line is added to the history
+ list after history expansion. ADDIT is just a suggestion;
+ REMEMBER_ON_HISTORY can veto, and does.
+ Right now this does history expansion. */
+char *
+pre_process_line (line, print_changes, addit)
+ char *line;
+ int print_changes, addit;
+{
+ char *history_value;
+ char *return_value;
+ int expanded;
+
+ return_value = line;
+ expanded = 0;
+
+# if defined (BANG_HISTORY)
+ /* History expand the line. If this results in no errors, then
+ add that line to the history if ADDIT is non-zero. */
+ if (!history_expansion_inhibited && history_expansion && history_expansion_p (line))
+ {
+ expanded = history_expand (line, &history_value);
+
+ if (expanded)
+ {
+ if (print_changes)
+ {
+ if (expanded < 0)
+ internal_error ("%s", history_value);
+#if defined (READLINE)
+ else if (hist_verify == 0 || expanded == 2)
+#else
+ else
+#endif
+ fprintf (stderr, "%s\n", history_value);
+ }
+
+ /* If there was an error, return NULL. */
+ if (expanded < 0 || expanded == 2) /* 2 == print only */
+ {
+# if defined (READLINE)
+ if (expanded == 2 && rl_dispatching == 0 && *history_value)
+# else
+ if (expanded == 2 && *history_value)
+# endif /* !READLINE */
+ maybe_add_history (history_value);
+
+ free (history_value);
+
+# if defined (READLINE)
+ /* New hack. We can allow the user to edit the
+ failed history expansion. */
+ if (history_reediting && expanded < 0 && rl_done)
+ re_edit (line);
+# endif /* READLINE */
+ return ((char *)NULL);
+ }
+
+# if defined (READLINE)
+ if (hist_verify && expanded == 1)
+ {
+ re_edit (history_value);
+ return ((char *)NULL);
+ }
+# endif
+ }
+
+ /* Let other expansions know that return_value can be free'ed,
+ and that a line has been added to the history list. Note
+ that we only add lines that have something in them. */
+ expanded = 1;
+ return_value = history_value;
+ }
+# endif /* BANG_HISTORY */
+
+ if (addit && remember_on_history && *return_value)
+ maybe_add_history (return_value);
+
+#if 0
+ if (expanded == 0)
+ return_value = savestring (line);
+#endif
+
+ return (return_value);
+}
+
+/* Return 1 if the first non-whitespace character in LINE is a `#', indicating
+ * that the line is a shell comment. */
+static int
+shell_comment (line)
+ char *line;
+{
+ char *p;
+
+ for (p = line; p && *p && whitespace (*p); p++)
+ ;
+ return (p && *p == '#');
+}
+
+#ifdef INCLUDE_UNUSED
+/* Remove shell comments from LINE. A `#' and anything after it is a comment.
+ This isn't really useful yet, since it doesn't handle quoting. */
+static char *
+filter_comments (line)
+ char *line;
+{
+ char *p;
+
+ for (p = line; p && *p && *p != '#'; p++)
+ ;
+ if (p && *p == '#')
+ *p = '\0';
+ return (line);
+}
+#endif
+
+/* Check LINE against what HISTCONTROL says to do. Returns 1 if the line
+ should be saved; 0 if it should be discarded. */
+static int
+check_history_control (line)
+ char *line;
+{
+ HIST_ENTRY *temp;
+ int r;
+
+ if (history_control == 0)
+ return 1;
+
+ /* ignorespace or ignoreboth */
+ if ((history_control & HC_IGNSPACE) && *line == ' ')
+ return 0;
+
+ /* ignoredups or ignoreboth */
+ if (history_control & HC_IGNDUPS)
+ {
+ using_history ();
+ temp = previous_history ();
+
+ r = (temp == 0 || STREQ (temp->line, line) == 0);
+
+ using_history ();
+
+ if (r == 0)
+ return r;
+ }
+
+ return 1;
+}
+
+/* Remove all entries matching LINE from the history list. Triggered when
+ HISTCONTROL includes `erasedups'. */
+static void
+hc_erasedups (line)
+ char *line;
+{
+ HIST_ENTRY *temp;
+ int r;
+
+ using_history ();
+ while (temp = previous_history ())
+ {
+ if (STREQ (temp->line, line))
+ {
+ r = where_history ();
+ remove_history (r);
+ }
+ }
+ using_history ();
+}
+
+/* Add LINE to the history list, handling possibly multi-line compound
+ commands. We note whether or not we save the first line of each command
+ (which is usually the entire command and history entry), and don't add
+ the second and subsequent lines of a multi-line compound command if we
+ didn't save the first line. We don't usually save shell comment lines in
+ compound commands in the history, because they could have the effect of
+ commenting out the rest of the command when the entire command is saved as
+ a single history entry (when COMMAND_ORIENTED_HISTORY is enabled). If
+ LITERAL_HISTORY is set, we're saving lines in the history with embedded
+ newlines, so it's OK to save comment lines. We also make sure to save
+ multiple-line quoted strings or other constructs. */
+void
+maybe_add_history (line)
+ char *line;
+{
+ hist_last_line_added = 0;
+
+ /* Don't use the value of history_control to affect the second
+ and subsequent lines of a multi-line command (old code did
+ this only when command_oriented_history is enabled). */
+ if (current_command_line_count > 1)
+ {
+ if (current_command_first_line_saved &&
+ (literal_history || dstack.delimiter_depth != 0 || shell_comment (line) == 0))
+ bash_add_history (line);
+ return;
+ }
+
+ /* This is the first line of a (possible multi-line) command. Note whether
+ or not we should save the first line and remember it. */
+ current_command_first_line_saved = check_add_history (line, 0);
+}
+
+/* Just check LINE against HISTCONTROL and HISTIGNORE and add it to the
+ history if it's OK. Used by `history -s' as well as maybe_add_history().
+ Returns 1 if the line was saved in the history, 0 otherwise. */
+int
+check_add_history (line, force)
+ char *line;
+ int force;
+{
+ if (check_history_control (line) && history_should_ignore (line) == 0)
+ {
+ /* We're committed to saving the line. If the user has requested it,
+ remove other matching lines from the history. */
+ if (history_control & HC_ERASEDUPS)
+ hc_erasedups (line);
+
+ if (force)
+ {
+ really_add_history (line);
+ using_history ();
+ }
+ else
+ bash_add_history (line);
+ return 1;
+ }
+ return 0;
+}
+
+#if defined (SYSLOG_HISTORY)
+#define SYSLOG_MAXLEN 600
+
+void
+bash_syslog_history (line)
+ const char *line;
+{
+ char trunc[SYSLOG_MAXLEN];
+
+ if (strlen(line) < SYSLOG_MAXLEN)
+ syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line);
+ else
+ {
+ strncpy (trunc, line, SYSLOG_MAXLEN);
+ trunc[SYSLOG_MAXLEN - 1] = '\0';
+ syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc);
+ }
+}
+#endif
+
+/* Add a line to the history list.
+ The variable COMMAND_ORIENTED_HISTORY controls the style of history
+ remembering; when non-zero, and LINE is not the first line of a
+ complete parser construct, append LINE to the last history line instead
+ of adding it as a new line. */
+void
+bash_add_history (line)
+ char *line;
+{
+ int add_it, offset, curlen;
+ HIST_ENTRY *current, *old;
+ char *chars_to_add, *new_line;
+
+ add_it = 1;
+ if (command_oriented_history && current_command_line_count > 1)
+ {
+ chars_to_add = literal_history ? "\n" : history_delimiting_chars ();
+
+ using_history ();
+ current = previous_history ();
+
+ if (current)
+ {
+ /* If the previous line ended with an escaped newline (escaped
+ with backslash, but otherwise unquoted), then remove the quoted
+ newline, since that is what happens when the line is parsed. */
+ curlen = strlen (current->line);
+
+ if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\\' &&
+ current->line[curlen - 2] != '\\')
+ {
+ current->line[curlen - 1] = '\0';
+ curlen--;
+ chars_to_add = "";
+ }
+
+ new_line = (char *)xmalloc (1
+ + curlen
+ + strlen (line)
+ + strlen (chars_to_add));
+ sprintf (new_line, "%s%s%s", current->line, chars_to_add, line);
+ offset = where_history ();
+ old = replace_history_entry (offset, new_line, current->data);
+ free (new_line);
+
+ if (old)
+ free_history_entry (old);
+
+ add_it = 0;
+ }
+ }
+
+ if (add_it)
+ really_add_history (line);
+
+#if defined (SYSLOG_HISTORY)
+ bash_syslog_history (line);
+#endif
+
+ using_history ();
+}
+
+static void
+really_add_history (line)
+ char *line;
+{
+ hist_last_line_added = 1;
+ hist_last_line_pushed = 0;
+ add_history (line);
+ history_lines_this_session++;
+}
+
+int
+history_number ()
+{
+ using_history ();
+ return (remember_on_history ? history_base + where_history () : 1);
+}
+
+static int
+should_expand (s)
+ char *s;
+{
+ char *p;
+
+ for (p = s; p && *p; p++)
+ {
+ if (*p == '\\')
+ p++;
+ else if (*p == '&')
+ return 1;
+ }
+ return 0;
+}
+
+static int
+histignore_item_func (ign)
+ struct ign *ign;
+{
+ if (should_expand (ign->val))
+ ign->flags |= HIGN_EXPAND;
+ return (0);
+}
+
+void
+setup_history_ignore (varname)
+ char *varname;
+{
+ setup_ignore_patterns (&histignore);
+}
+
+static HIST_ENTRY *
+last_history_entry ()
+{
+ HIST_ENTRY *he;
+
+ using_history ();
+ he = previous_history ();
+ using_history ();
+ return he;
+}
+
+char *
+last_history_line ()
+{
+ HIST_ENTRY *he;
+
+ he = last_history_entry ();
+ if (he == 0)
+ return ((char *)NULL);
+ return he->line;
+}
+
+static char *
+expand_histignore_pattern (pat)
+ char *pat;
+{
+ HIST_ENTRY *phe;
+ char *ret;
+
+ phe = last_history_entry ();
+
+ if (phe == (HIST_ENTRY *)0)
+ return (savestring (pat));
+
+ ret = strcreplace (pat, '&', phe->line, 1);
+
+ return ret;
+}
+
+/* Return 1 if we should not put LINE into the history according to the
+ patterns in HISTIGNORE. */
+static int
+history_should_ignore (line)
+ char *line;
+{
+ register int i, match;
+ char *npat;
+
+ if (histignore.num_ignores == 0)
+ return 0;
+
+ for (i = match = 0; i < histignore.num_ignores; i++)
+ {
+ if (histignore.ignores[i].flags & HIGN_EXPAND)
+ npat = expand_histignore_pattern (histignore.ignores[i].val);
+ else
+ npat = histignore.ignores[i].val;
+
+ match = strmatch (npat, line, FNMATCH_EXTFLAG) != FNM_NOMATCH;
+
+ if (histignore.ignores[i].flags & HIGN_EXPAND)
+ free (npat);
+
+ if (match)
+ break;
+ }
+
+ return match;
+}
+#endif /* HISTORY */
diff --git a/bashhist.c~ b/bashhist.c~
index a80968fd..f12fdac5 100644
--- a/bashhist.c~
+++ b/bashhist.c~
@@ -184,6 +184,7 @@ int dont_save_function_defs;
extern int current_command_line_count;
extern struct dstack dstack;
+extern int parser_state;
static int bash_history_inhibit_expansion __P((char *, int));
#if defined (READLINE)
@@ -731,7 +732,8 @@ bash_add_history (line)
add_it = 1;
if (command_oriented_history && current_command_line_count > 1)
{
- chars_to_add = literal_history ? "\n" : history_delimiting_chars ();
+ chars_to_add = literal_history ? "\n" : history_delimiting_chars (line);
+itrace("bash_add_history: `%s' PST_HEREDOC = %d chars_to_add = `%s'", line, (parser_state&PST_HEREDOC), chars_to_add);
using_history ();
current = previous_history ();
@@ -751,6 +753,15 @@ bash_add_history (line)
chars_to_add = "";
}
+#if 1 /* XXX - bash-4.2 */
+ /* If we're not in some kind of quoted construct, the current history
+ entry ends with a newline, and we're going to add a semicolon,
+ don't. In some cases, it results in a syntax error (e.g., before
+ a close brace), and it should not be needed. */
+ if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';')
+ chars_to_add++;
+#endif
+
new_line = (char *)xmalloc (1
+ curlen
+ strlen (line)
diff --git a/bashline.c b/bashline.c
index 6164bc6f..d4d00637 100644
--- a/bashline.c
+++ b/bashline.c
@@ -863,6 +863,7 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
{
char *command, *metaval;
int r, cclc, rrs, metaflag;
+ sh_parser_state_t ps;
rrs = rl_readline_state;
cclc = current_command_line_count;
@@ -897,7 +898,9 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
yet. */
if (rl_deprep_term_function)
(*rl_deprep_term_function) ();
+ save_parser_state (&ps);
r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
+ restore_parser_state (&ps);
if (rl_prep_term_function)
(*rl_prep_term_function) (metaflag);
diff --git a/bashline.c~ b/bashline.c~
index 3654cee8..6164bc6f 100644
--- a/bashline.c~
+++ b/bashline.c~
@@ -1680,11 +1680,7 @@ globword:
a single match (multiple matches that end up reducing the number of
characters in the common prefix are bad) will ever be returned on
regular completion. */
-#if 1
if (globpat)
-#else
- if (glob_pattern_p (hint))
-#endif
{
if (state == 0)
{
diff --git a/builtins/printf.def b/builtins/printf.def
index 28f8dd8f..277566f8 100644
--- a/builtins/printf.def
+++ b/builtins/printf.def
@@ -117,7 +117,7 @@ extern int errno;
else if (have_fieldwidth) \
nw = vflag ? vbprintf (f, fieldwidth, func) : printf (f, fieldwidth, func); \
else if (have_precision) \
- nw = vflag ? vbprintf (f, precision, func) : printf (f, fieldwidth, func); \
+ nw = vflag ? vbprintf (f, precision, func) : printf (f, precision, func); \
else \
nw = vflag ? vbprintf (f, func) : printf (f, func); \
tw += nw; \
diff --git a/builtins/trap.def b/builtins/trap.def
index c880ba18..e6f15ed3 100644
--- a/builtins/trap.def
+++ b/builtins/trap.def
@@ -127,7 +127,10 @@ trap_builtin (list)
if (list_signal_names)
return (sh_chkwrite (display_signal_list ((WORD_LIST *)NULL, 1)));
else if (display || list == 0)
- return (sh_chkwrite (display_traps (list)));
+ {
+ initialize_terminating_signals ();
+ return (sh_chkwrite (display_traps (list)));
+ }
else
{
char *first_arg;
@@ -199,6 +202,8 @@ trap_builtin (list)
switch (sig)
{
case SIGINT:
+ /* XXX - should we do this if original disposition
+ was SIG_IGN? */
if (interactive)
set_signal_handler (SIGINT, sigint_sighandler);
else
@@ -240,10 +245,19 @@ showtrap (i)
char *t, *p, *sn;
p = trap_list[i];
+#if 1
if (p == (char *)DEFAULT_SIG)
return;
t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p);
+#else
+ if (p == (char *)DEFAULT_SIG && signal_is_hard_ignored (i) == 0)
+ return;
+ else if (signal_is_hard_ignored (i))
+ t = (char *)NULL;
+ else
+ t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p);
+#endif
sn = signal_name (i);
/* Make sure that signals whose names are unknown (for whatever reason)
are printed as signal numbers. */
diff --git a/builtins/trap.def~ b/builtins/trap.def~
index a8da71dd..a2d475e6 100644
--- a/builtins/trap.def~
+++ b/builtins/trap.def~
@@ -93,7 +93,7 @@ static int display_traps __P((WORD_LIST *));
#define REVERT 1 /* Revert to this signals original value. */
#define IGNORE 2 /* Ignore this signal. */
-extern int posixly_correct;
+extern int posixly_correct, subshell_environment;
int
trap_builtin (list)
@@ -103,6 +103,7 @@ trap_builtin (list)
list_signal_names = display = 0;
result = EXECUTION_SUCCESS;
+
reset_internal_getopt ();
while ((opt = internal_getopt (list, "lp")) != -1)
{
@@ -126,7 +127,10 @@ trap_builtin (list)
if (list_signal_names)
return (sh_chkwrite (display_signal_list ((WORD_LIST *)NULL, 1)));
else if (display || list == 0)
- return (sh_chkwrite (display_traps (list)));
+ {
+ initialize_terminating_signals ();
+ return (sh_chkwrite (display_traps (list)));
+ }
else
{
char *first_arg;
@@ -163,6 +167,16 @@ trap_builtin (list)
operation = REVERT;
}
+ /* If we're in a command substitution, we haven't freed the trap strings
+ (though we reset the signal handlers). If we're setting a trap to
+ handle a signal here, free the rest of the trap strings since they
+ don't apply any more. */
+ if (subshell_environment & SUBSHELL_RESETTRAP)
+ {
+ free_trap_strings ();
+ subshell_environment &= ~SUBSHELL_RESETTRAP;
+ }
+
while (list)
{
sig = decode_signal (list->word->word, opt);
@@ -188,6 +202,8 @@ trap_builtin (list)
switch (sig)
{
case SIGINT:
+ /* XXX - should we do this if original disposition
+ was SIG_IGN? */
if (interactive)
set_signal_handler (SIGINT, sigint_sighandler);
else
@@ -229,10 +245,17 @@ showtrap (i)
char *t, *p, *sn;
p = trap_list[i];
+#if 1
if (p == (char *)DEFAULT_SIG)
return;
-
- t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p);
+#else
+ if (p == (char *)DEFAULT_SIG && signal_is_hard_ignored (i) == 0)
+ return;
+ else if (signal_is_hard_ignored (i))
+ t = (char *)NULL;
+ else
+#endif
+ t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p);
sn = signal_name (i);
/* Make sure that signals whose names are unknown (for whatever reason)
are printed as signal numbers. */
diff --git a/config.h.in b/config.h.in
index afc1c84d..3e9f7665 100644
--- a/config.h.in
+++ b/config.h.in
@@ -555,6 +555,9 @@
/* Define if you have the eaccess function. */
#undef HAVE_EACCESS
+/* Define if you have the faccessat function. */
+#undef HAVE_FACCESSAT
+
/* Define if you have the fcntl function. */
#undef HAVE_FCNTL
diff --git a/config.h.in~ b/config.h.in~
index 4cb74a61..afc1c84d 100644
--- a/config.h.in~
+++ b/config.h.in~
@@ -680,6 +680,9 @@
/* Define if you have the mbscmp function. */
#undef HAVE_MBSCMP
+/* Define if you have the mbsnrtowcs function. */
+#undef HAVE_MBSNRTOWCS
+
/* Define if you have the mbsrtowcs function. */
#undef HAVE_MBSRTOWCS
diff --git a/configure b/configure
index b2bef583..3ac81c7b 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
#! /bin/sh
-# From configure.in for Bash 4.1, version 4.022.
+# From configure.in for Bash 4.1, version 4.023.
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.63 for bash 4.1-maint.
#
@@ -13577,7 +13577,8 @@ done
-for ac_func in bcopy bzero confstr fnmatch \
+
+for ac_func in bcopy bzero confstr faccessat fnmatch \
getaddrinfo gethostbyname getservbyname getservent inet_aton \
memmove pathconf putenv raise regcomp regexec \
setenv setlinebuf setlocale setvbuf siginterrupt strchr \
diff --git a/configure.in b/configure.in
index 2696f854..6cecad51 100644
--- a/configure.in
+++ b/configure.in
@@ -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.1, version 4.022])dnl
+AC_REVISION([for Bash 4.1, version 4.023])dnl
define(bashvers, 4.1)
define(relstatus, maint)
@@ -727,7 +727,7 @@ AC_CHECK_FUNCS(dup2 eaccess fcntl getdtablesize getgroups gethostname \
AC_REPLACE_FUNCS(rename)
dnl checks for c library functions
-AC_CHECK_FUNCS(bcopy bzero confstr fnmatch \
+AC_CHECK_FUNCS(bcopy bzero confstr faccessat fnmatch \
getaddrinfo gethostbyname getservbyname getservent inet_aton \
memmove pathconf putenv raise regcomp regexec \
setenv setlinebuf setlocale setvbuf siginterrupt strchr \
diff --git a/configure.in~ b/configure.in~
index b8810242..55de15f4 100644
--- a/configure.in~
+++ b/configure.in~
@@ -5,7 +5,7 @@ dnl report bugs to chet@po.cwru.edu
dnl
dnl Process this file with autoconf to produce a configure script.
-# Copyright (C) 1987-2009 Free Software Foundation, Inc.
+# Copyright (C) 1987-2010 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
@@ -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.1, version 4.021])dnl
+AC_REVISION([for Bash 4.1, version 4.023])dnl
define(bashvers, 4.1)
define(relstatus, maint)
@@ -740,6 +740,7 @@ AC_REPLACE_FUNCS(getcwd memset)
AC_REPLACE_FUNCS(strcasecmp strcasestr strerror strftime strnlen strpbrk strstr)
AC_REPLACE_FUNCS(strtod strtol strtoul strtoll strtoull strtoimax strtoumax)
AC_REPLACE_FUNCS(dprintf)
+AC_REPLACE_FUNCS(strchrnul)
AC_CHECK_DECLS([confstr])
AC_CHECK_DECLS([printf])
diff --git a/execute_cmd.c b/execute_cmd.c
index 0773fbd2..7a429e8e 100644
--- a/execute_cmd.c
+++ b/execute_cmd.c
@@ -4542,6 +4542,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
#endif
#endif
+ reset_terminating_signals (); /* XXX */
/* Cancel traps, in trap.c. */
restore_original_signals ();
diff --git a/execute_cmd.c~ b/execute_cmd.c~
index 4dc08ce0..0773fbd2 100644
--- a/execute_cmd.c~
+++ b/execute_cmd.c~
@@ -1219,6 +1219,7 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
posix_time = (command->flags & CMD_TIME_POSIX);
+#if 0 /* XXX - bash-4.2 */
nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
if (posixly_correct && nullcmd)
{
@@ -1232,6 +1233,7 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
tbefore = shell_start_time;
#endif
}
+#endif
old_flags = command->flags;
command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX);
@@ -1287,9 +1289,11 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
time_format = POSIX_TIMEFORMAT;
else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
{
+#if 0 /* XXX - bash-4.2 */
if (posixly_correct && nullcmd)
time_format = "user\t%2lU\nsys\t%2lS";
else
+#endif
time_format = BASH_TIMEFORMAT;
}
if (time_format && *time_format)
diff --git a/externs.h b/externs.h
index 41316c5b..f0f68d76 100644
--- a/externs.h
+++ b/externs.h
@@ -114,7 +114,7 @@ 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((void));
+extern char *history_delimiting_chars __P((const char *));
#endif
/* Declarations for functions defined in locale.c */
diff --git a/externs.h~ b/externs.h~
index 1f5b03b8..41316c5b 100644
--- a/externs.h~
+++ b/externs.h~
@@ -327,6 +327,11 @@ extern int strcasecmp __P((const char *, const char *));
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));
diff --git a/lib/readline/complete.c b/lib/readline/complete.c
index 453b2e67..806356cf 100644
--- a/lib/readline/complete.c
+++ b/lib/readline/complete.c
@@ -2094,9 +2094,9 @@ rl_filename_completion_function (text, state)
else if (rl_completion_found_quote && rl_filename_dequoting_function)
{
/* delete single and double quotes */
- temp = (*rl_filename_dequoting_function) (users_dirname, rl_completion_quote_character);
- free (users_dirname);
- users_dirname = temp;
+ temp = (*rl_filename_dequoting_function) (dirname, rl_completion_quote_character);
+ free (dirname);
+ dirname = temp;
}
directory = opendir (dirname);
diff --git a/lib/readline/complete.c~ b/lib/readline/complete.c~
index a551dd50..453b2e67 100644
--- a/lib/readline/complete.c~
+++ b/lib/readline/complete.c~
@@ -2304,14 +2304,14 @@ rl_old_menu_complete (count, invoking_key)
if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
{
- rl_ding ();
+ rl_ding ();
FREE (matches);
matches = (char **)0;
FREE (orig_text);
orig_text = (char *)0;
- completion_changed_buffer = 0;
- RL_UNSETSTATE(RL_STATE_COMPLETING);
- return (0);
+ completion_changed_buffer = 0;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ return (0);
}
RL_UNSETSTATE(RL_STATE_COMPLETING);
@@ -2428,14 +2428,14 @@ rl_menu_complete (count, ignore)
if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
{
- rl_ding ();
+ rl_ding ();
FREE (matches);
matches = (char **)0;
FREE (orig_text);
orig_text = (char *)0;
- completion_changed_buffer = 0;
- RL_UNSETSTATE(RL_STATE_COMPLETING);
- return (0);
+ completion_changed_buffer = 0;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ return (0);
}
RL_UNSETSTATE(RL_STATE_COMPLETING);
diff --git a/lib/readline/signals.c~ b/lib/readline/signals.c~
new file mode 100644
index 00000000..5257996d
--- /dev/null
+++ b/lib/readline/signals.c~
@@ -0,0 +1,671 @@
+/* signals.c -- signal handling support for readline. */
+
+/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
+
+ This file is part of the GNU Readline Library (Readline), a library
+ for reading lines of text with interactive input and history editing.
+
+ Readline 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.
+
+ Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h> /* Just for NULL. Yuck. */
+#include <sys/types.h>
+#include <signal.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+
+#if defined (GWINSZ_IN_SYS_IOCTL)
+# include <sys/ioctl.h>
+#endif /* GWINSZ_IN_SYS_IOCTL */
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#include "rlprivate.h"
+
+#if defined (HANDLE_SIGNALS)
+
+#if !defined (RETSIGTYPE)
+# if defined (VOID_SIGHANDLER)
+# define RETSIGTYPE void
+# else
+# define RETSIGTYPE int
+# endif /* !VOID_SIGHANDLER */
+#endif /* !RETSIGTYPE */
+
+#if defined (VOID_SIGHANDLER)
+# define SIGHANDLER_RETURN return
+#else
+# define SIGHANDLER_RETURN return (0)
+#endif
+
+/* This typedef is equivalent to the one for Function; it allows us
+ to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */
+typedef RETSIGTYPE SigHandler ();
+
+#if defined (HAVE_POSIX_SIGNALS)
+typedef struct sigaction sighandler_cxt;
+# define rl_sigaction(s, nh, oh) sigaction(s, nh, oh)
+#else
+typedef struct { SigHandler *sa_handler; int sa_mask, sa_flags; } sighandler_cxt;
+# define sigemptyset(m)
+#endif /* !HAVE_POSIX_SIGNALS */
+
+#ifndef SA_RESTART
+# define SA_RESTART 0
+#endif
+
+static SigHandler *rl_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *));
+static void rl_maybe_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *));
+
+static RETSIGTYPE rl_signal_handler PARAMS((int));
+static RETSIGTYPE _rl_handle_signal PARAMS((int));
+
+/* Exported variables for use by applications. */
+
+/* If non-zero, readline will install its own signal handlers for
+ SIGINT, SIGTERM, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */
+int rl_catch_signals = 1;
+
+/* If non-zero, readline will install a signal handler for SIGWINCH. */
+#ifdef SIGWINCH
+int rl_catch_sigwinch = 1;
+#else
+int rl_catch_sigwinch = 0; /* for the readline state struct in readline.c */
+#endif
+
+/* Private variables. */
+int _rl_interrupt_immediately = 0;
+int volatile _rl_caught_signal = 0; /* should be sig_atomic_t, but that requires including <signal.h> everywhere */
+
+/* If non-zero, print characters corresponding to received signals as long as
+ the user has indicated his desire to do so (_rl_echo_control_chars). */
+int _rl_echoctl = 0;
+
+int _rl_intr_char = 0;
+int _rl_quit_char = 0;
+int _rl_susp_char = 0;
+
+static int signals_set_flag;
+static int sigwinch_set_flag;
+
+/* **************************************************************** */
+/* */
+/* Signal Handling */
+/* */
+/* **************************************************************** */
+
+static sighandler_cxt old_int, old_term, old_alrm, old_quit;
+#if defined (SIGTSTP)
+static sighandler_cxt old_tstp, old_ttou, old_ttin;
+#endif
+#if defined (SIGWINCH)
+static sighandler_cxt old_winch;
+#endif
+
+/* Readline signal handler functions. */
+
+/* Called from RL_CHECK_SIGNALS() macro */
+RETSIGTYPE
+_rl_signal_handler (sig)
+{
+ _rl_caught_signal = 0; /* XXX */
+
+ _rl_handle_signal (sig);
+ SIGHANDLER_RETURN;
+}
+
+static RETSIGTYPE
+rl_signal_handler (sig)
+ int sig;
+{
+fprintf(stderr, "rl_signal_handler: caught %d", sig);
+ if (_rl_interrupt_immediately || RL_ISSTATE(RL_STATE_CALLBACK))
+ {
+ _rl_interrupt_immediately = 0;
+ _rl_handle_signal (sig);
+ }
+ else
+ _rl_caught_signal = sig;
+
+ SIGHANDLER_RETURN;
+}
+
+static RETSIGTYPE
+_rl_handle_signal (sig)
+ int sig;
+{
+#if defined (HAVE_POSIX_SIGNALS)
+ sigset_t set;
+#else /* !HAVE_POSIX_SIGNALS */
+# if defined (HAVE_BSD_SIGNALS)
+ long omask;
+# else /* !HAVE_BSD_SIGNALS */
+ sighandler_cxt dummy_cxt; /* needed for rl_set_sighandler call */
+# endif /* !HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ RL_SETSTATE(RL_STATE_SIGHANDLER);
+
+#if !defined (HAVE_BSD_SIGNALS) && !defined (HAVE_POSIX_SIGNALS)
+ /* Since the signal will not be blocked while we are in the signal
+ handler, ignore it until rl_clear_signals resets the catcher. */
+# if defined (SIGALRM)
+ if (sig == SIGINT || sig == SIGALRM)
+# else
+ if (sig == SIGINT)
+# endif
+ rl_set_sighandler (sig, SIG_IGN, &dummy_cxt);
+#endif /* !HAVE_BSD_SIGNALS && !HAVE_POSIX_SIGNALS */
+
+ switch (sig)
+ {
+ case SIGINT:
+ _rl_reset_completion_state ();
+ rl_free_line_state ();
+ /* FALLTHROUGH */
+
+ case SIGTERM:
+#if defined (SIGTSTP)
+ case SIGTSTP:
+ case SIGTTOU:
+ case SIGTTIN:
+#endif /* SIGTSTP */
+#if defined (SIGALRM)
+ case SIGALRM:
+#endif
+#if defined (SIGQUIT)
+ case SIGQUIT:
+#endif
+ rl_echo_signal_char (sig);
+ rl_cleanup_after_signal ();
+
+#if defined (HAVE_POSIX_SIGNALS)
+ sigemptyset (&set);
+ sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &set);
+ sigdelset (&set, sig);
+#else /* !HAVE_POSIX_SIGNALS */
+# if defined (HAVE_BSD_SIGNALS)
+ omask = sigblock (0);
+# endif /* HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+#if defined (__EMX__)
+ signal (sig, SIG_ACK);
+#endif
+
+#if defined (HAVE_KILL)
+ kill (getpid (), sig);
+#else
+ raise (sig); /* assume we have raise */
+#endif
+
+ /* Let the signal that we just sent through. */
+#if defined (HAVE_POSIX_SIGNALS)
+ sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL);
+#else /* !HAVE_POSIX_SIGNALS */
+# if defined (HAVE_BSD_SIGNALS)
+ sigsetmask (omask & ~(sigmask (sig)));
+# endif /* HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ rl_reset_after_signal ();
+ }
+
+ RL_UNSETSTATE(RL_STATE_SIGHANDLER);
+ SIGHANDLER_RETURN;
+}
+
+#if defined (SIGWINCH)
+static RETSIGTYPE
+rl_sigwinch_handler (sig)
+ int sig;
+{
+ SigHandler *oh;
+
+#if defined (MUST_REINSTALL_SIGHANDLERS)
+ sighandler_cxt dummy_winch;
+
+ /* We don't want to change old_winch -- it holds the state of SIGWINCH
+ disposition set by the calling application. We need this state
+ because we call the application's SIGWINCH handler after updating
+ our own idea of the screen size. */
+ rl_set_sighandler (SIGWINCH, rl_sigwinch_handler, &dummy_winch);
+#endif
+
+ RL_SETSTATE(RL_STATE_SIGHANDLER);
+ rl_resize_terminal ();
+
+ /* If another sigwinch handler has been installed, call it. */
+ oh = (SigHandler *)old_winch.sa_handler;
+ if (oh && oh != (SigHandler *)SIG_IGN && oh != (SigHandler *)SIG_DFL)
+ (*oh) (sig);
+
+ RL_UNSETSTATE(RL_STATE_SIGHANDLER);
+ SIGHANDLER_RETURN;
+}
+#endif /* SIGWINCH */
+
+/* Functions to manage signal handling. */
+
+#if !defined (HAVE_POSIX_SIGNALS)
+static int
+rl_sigaction (sig, nh, oh)
+ int sig;
+ sighandler_cxt *nh, *oh;
+{
+ oh->sa_handler = signal (sig, nh->sa_handler);
+ return 0;
+}
+#endif /* !HAVE_POSIX_SIGNALS */
+
+/* Set up a readline-specific signal handler, saving the old signal
+ information in OHANDLER. Return the old signal handler, like
+ signal(). */
+static SigHandler *
+rl_set_sighandler (sig, handler, ohandler)
+ int sig;
+ SigHandler *handler;
+ sighandler_cxt *ohandler;
+{
+ sighandler_cxt old_handler;
+#if defined (HAVE_POSIX_SIGNALS)
+ struct sigaction act;
+
+ act.sa_handler = handler;
+# if defined (SIGWINCH)
+ act.sa_flags = (sig == SIGWINCH) ? SA_RESTART : 0;
+# else
+ act.sa_flags = 0;
+# endif /* SIGWINCH */
+ sigemptyset (&act.sa_mask);
+ sigemptyset (&ohandler->sa_mask);
+ sigaction (sig, &act, &old_handler);
+#else
+ old_handler.sa_handler = (SigHandler *)signal (sig, handler);
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ /* XXX -- assume we have memcpy */
+ /* If rl_set_signals is called twice in a row, don't set the old handler to
+ rl_signal_handler, because that would cause infinite recursion. */
+ if (handler != rl_signal_handler || old_handler.sa_handler != rl_signal_handler)
+ memcpy (ohandler, &old_handler, sizeof (sighandler_cxt));
+
+ return (ohandler->sa_handler);
+}
+
+static void
+rl_maybe_set_sighandler (sig, handler, ohandler)
+ int sig;
+ SigHandler *handler;
+ sighandler_cxt *ohandler;
+{
+ sighandler_cxt dummy;
+ SigHandler *oh;
+
+ sigemptyset (&dummy.sa_mask);
+ oh = rl_set_sighandler (sig, handler, ohandler);
+ if (oh == (SigHandler *)SIG_IGN)
+ rl_sigaction (sig, ohandler, &dummy);
+}
+
+int
+rl_set_signals ()
+{
+ sighandler_cxt dummy;
+ SigHandler *oh;
+#if defined (HAVE_POSIX_SIGNALS)
+ static int sigmask_set = 0;
+ static sigset_t bset, oset;
+#endif
+
+#if defined (HAVE_POSIX_SIGNALS)
+ if (rl_catch_signals && sigmask_set == 0)
+ {
+ sigemptyset (&bset);
+
+ sigaddset (&bset, SIGINT);
+ sigaddset (&bset, SIGTERM);
+#if defined (SIGQUIT)
+ sigaddset (&bset, SIGQUIT);
+#endif
+#if defined (SIGALRM)
+ sigaddset (&bset, SIGALRM);
+#endif
+#if defined (SIGTSTP)
+ sigaddset (&bset, SIGTSTP);
+#endif
+#if defined (SIGTTIN)
+ sigaddset (&bset, SIGTTIN);
+#endif
+#if defined (SIGTTOU)
+ sigaddset (&bset, SIGTTOU);
+#endif
+ sigmask_set = 1;
+ }
+#endif /* HAVE_POSIX_SIGNALS */
+
+ if (rl_catch_signals && signals_set_flag == 0)
+ {
+#if defined (HAVE_POSIX_SIGNALS)
+ sigemptyset (&oset);
+ sigprocmask (SIG_BLOCK, &bset, &oset);
+#endif
+
+ rl_maybe_set_sighandler (SIGINT, rl_signal_handler, &old_int);
+ rl_maybe_set_sighandler (SIGTERM, rl_signal_handler, &old_term);
+#if defined (SIGQUIT)
+ rl_maybe_set_sighandler (SIGQUIT, rl_signal_handler, &old_quit);
+#endif
+
+#if defined (SIGALRM)
+ oh = rl_set_sighandler (SIGALRM, rl_signal_handler, &old_alrm);
+ if (oh == (SigHandler *)SIG_IGN)
+ rl_sigaction (SIGALRM, &old_alrm, &dummy);
+#if defined (HAVE_POSIX_SIGNALS) && defined (SA_RESTART)
+ /* If the application using readline has already installed a signal
+ handler with SA_RESTART, SIGALRM will cause reads to be restarted
+ automatically, so readline should just get out of the way. Since
+ we tested for SIG_IGN above, we can just test for SIG_DFL here. */
+ if (oh != (SigHandler *)SIG_DFL && (old_alrm.sa_flags & SA_RESTART))
+ rl_sigaction (SIGALRM, &old_alrm, &dummy);
+#endif /* HAVE_POSIX_SIGNALS */
+#endif /* SIGALRM */
+
+#if defined (SIGTSTP)
+ rl_maybe_set_sighandler (SIGTSTP, rl_signal_handler, &old_tstp);
+#endif /* SIGTSTP */
+
+#if defined (SIGTTOU)
+ rl_maybe_set_sighandler (SIGTTOU, rl_signal_handler, &old_ttou);
+#endif /* SIGTTOU */
+
+#if defined (SIGTTIN)
+ rl_maybe_set_sighandler (SIGTTIN, rl_signal_handler, &old_ttin);
+#endif /* SIGTTIN */
+
+ signals_set_flag = 1;
+
+#if defined (HAVE_POSIX_SIGNALS)
+ sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
+#endif
+ }
+
+#if defined (SIGWINCH)
+ if (rl_catch_sigwinch && sigwinch_set_flag == 0)
+ {
+ rl_maybe_set_sighandler (SIGWINCH, rl_sigwinch_handler, &old_winch);
+ sigwinch_set_flag = 1;
+ }
+#endif /* SIGWINCH */
+
+ return 0;
+}
+
+int
+rl_clear_signals ()
+{
+ sighandler_cxt dummy;
+
+ if (rl_catch_signals && signals_set_flag == 1)
+ {
+ sigemptyset (&dummy.sa_mask);
+
+ rl_sigaction (SIGINT, &old_int, &dummy);
+ rl_sigaction (SIGTERM, &old_term, &dummy);
+#if defined (SIGQUIT)
+ rl_sigaction (SIGQUIT, &old_quit, &dummy);
+#endif
+#if defined (SIGALRM)
+ rl_sigaction (SIGALRM, &old_alrm, &dummy);
+#endif
+
+#if defined (SIGTSTP)
+ rl_sigaction (SIGTSTP, &old_tstp, &dummy);
+#endif /* SIGTSTP */
+
+#if defined (SIGTTOU)
+ rl_sigaction (SIGTTOU, &old_ttou, &dummy);
+#endif /* SIGTTOU */
+
+#if defined (SIGTTIN)
+ rl_sigaction (SIGTTIN, &old_ttin, &dummy);
+#endif /* SIGTTIN */
+
+ signals_set_flag = 0;
+ }
+
+#if defined (SIGWINCH)
+ if (rl_catch_sigwinch && sigwinch_set_flag == 1)
+ {
+ sigemptyset (&dummy.sa_mask);
+ rl_sigaction (SIGWINCH, &old_winch, &dummy);
+ sigwinch_set_flag = 0;
+ }
+#endif
+
+ return 0;
+}
+
+/* Clean up the terminal and readline state after catching a signal, before
+ resending it to the calling application. */
+void
+rl_cleanup_after_signal ()
+{
+ _rl_clean_up_for_exit ();
+ if (rl_deprep_term_function)
+ (*rl_deprep_term_function) ();
+ rl_clear_pending_input ();
+ rl_clear_signals ();
+}
+
+/* Reset the terminal and readline state after a signal handler returns. */
+void
+rl_reset_after_signal ()
+{
+ if (rl_prep_term_function)
+ (*rl_prep_term_function) (_rl_meta_flag);
+ rl_set_signals ();
+}
+
+/* Free up the readline variable line state for the current line (undo list,
+ any partial history entry, any keyboard macros in progress, and any
+ numeric arguments in process) after catching a signal, before calling
+ rl_cleanup_after_signal(). */
+void
+rl_free_line_state ()
+{
+ register HIST_ENTRY *entry;
+
+ rl_free_undo_list ();
+
+ entry = current_history ();
+ if (entry)
+ entry->data = (char *)NULL;
+
+ _rl_kill_kbd_macro ();
+ rl_clear_message ();
+ _rl_reset_argument ();
+}
+
+#endif /* HANDLE_SIGNALS */
+
+/* **************************************************************** */
+/* */
+/* SIGINT Management */
+/* */
+/* **************************************************************** */
+
+#if defined (HAVE_POSIX_SIGNALS)
+static sigset_t sigint_set, sigint_oset;
+static sigset_t sigwinch_set, sigwinch_oset;
+#else /* !HAVE_POSIX_SIGNALS */
+# if defined (HAVE_BSD_SIGNALS)
+static int sigint_oldmask;
+static int sigwinch_oldmask;
+# endif /* HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+static int sigint_blocked;
+static int sigwinch_blocked;
+
+/* Cause SIGINT to not be delivered until the corresponding call to
+ release_sigint(). */
+void
+_rl_block_sigint ()
+{
+ if (sigint_blocked)
+ return;
+
+#if defined (HAVE_POSIX_SIGNALS)
+ sigemptyset (&sigint_set);
+ sigemptyset (&sigint_oset);
+ sigaddset (&sigint_set, SIGINT);
+ sigprocmask (SIG_BLOCK, &sigint_set, &sigint_oset);
+#else /* !HAVE_POSIX_SIGNALS */
+# if defined (HAVE_BSD_SIGNALS)
+ sigint_oldmask = sigblock (sigmask (SIGINT));
+# else /* !HAVE_BSD_SIGNALS */
+# if defined (HAVE_USG_SIGHOLD)
+ sighold (SIGINT);
+# endif /* HAVE_USG_SIGHOLD */
+# endif /* !HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ sigint_blocked = 1;
+}
+
+/* Allow SIGINT to be delivered. */
+void
+_rl_release_sigint ()
+{
+ if (sigint_blocked == 0)
+ return;
+
+#if defined (HAVE_POSIX_SIGNALS)
+ sigprocmask (SIG_SETMASK, &sigint_oset, (sigset_t *)NULL);
+#else
+# if defined (HAVE_BSD_SIGNALS)
+ sigsetmask (sigint_oldmask);
+# else /* !HAVE_BSD_SIGNALS */
+# if defined (HAVE_USG_SIGHOLD)
+ sigrelse (SIGINT);
+# endif /* HAVE_USG_SIGHOLD */
+# endif /* !HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ sigint_blocked = 0;
+}
+
+/* Cause SIGWINCH to not be delivered until the corresponding call to
+ release_sigwinch(). */
+void
+_rl_block_sigwinch ()
+{
+ if (sigwinch_blocked)
+ return;
+
+#if defined (HAVE_POSIX_SIGNALS)
+ sigemptyset (&sigwinch_set);
+ sigemptyset (&sigwinch_oset);
+ sigaddset (&sigwinch_set, SIGWINCH);
+ sigprocmask (SIG_BLOCK, &sigwinch_set, &sigwinch_oset);
+#else /* !HAVE_POSIX_SIGNALS */
+# if defined (HAVE_BSD_SIGNALS)
+ sigwinch_oldmask = sigblock (sigmask (SIGWINCH));
+# else /* !HAVE_BSD_SIGNALS */
+# if defined (HAVE_USG_SIGHOLD)
+ sighold (SIGWINCH);
+# endif /* HAVE_USG_SIGHOLD */
+# endif /* !HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ sigwinch_blocked = 1;
+}
+
+/* Allow SIGWINCH to be delivered. */
+void
+_rl_release_sigwinch ()
+{
+ if (sigwinch_blocked == 0)
+ return;
+
+#if defined (HAVE_POSIX_SIGNALS)
+ sigprocmask (SIG_SETMASK, &sigwinch_oset, (sigset_t *)NULL);
+#else
+# if defined (HAVE_BSD_SIGNALS)
+ sigsetmask (sigwinch_oldmask);
+# else /* !HAVE_BSD_SIGNALS */
+# if defined (HAVE_USG_SIGHOLD)
+ sigrelse (SIGWINCH);
+# endif /* HAVE_USG_SIGHOLD */
+# endif /* !HAVE_BSD_SIGNALS */
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ sigwinch_blocked = 0;
+}
+
+/* **************************************************************** */
+/* */
+/* Echoing special control characters */
+/* */
+/* **************************************************************** */
+void
+rl_echo_signal_char (sig)
+ int sig;
+{
+ char cstr[3];
+ int cslen, c;
+
+ if (_rl_echoctl == 0 || _rl_echo_control_chars == 0)
+ return;
+
+ switch (sig)
+ {
+ case SIGINT: c = _rl_intr_char; break;
+#if defined (SIGQUIT)
+ case SIGQUIT: c = _rl_quit_char; break;
+#endif
+#if defined (SIGTSTP)
+ case SIGTSTP: c = _rl_susp_char; break;
+#endif
+ default: return;
+ }
+
+ if (CTRL_CHAR (c) || c == RUBOUT)
+ {
+ cstr[0] = '^';
+ cstr[1] = CTRL_CHAR (c) ? UNCTRL (c) : '?';
+ cstr[cslen = 2] = '\0';
+ }
+ else
+ {
+ cstr[0] = c;
+ cstr[cslen = 1] = '\0';
+ }
+
+ _rl_output_some_chars (cstr, cslen);
+}
diff --git a/lib/sh/eaccess.c b/lib/sh/eaccess.c
index 989bc225..ff1bb994 100644
--- a/lib/sh/eaccess.c
+++ b/lib/sh/eaccess.c
@@ -198,11 +198,20 @@ sh_eaccess (path, mode)
char *path;
int mode;
{
+ int ret;
+
if (path_is_devfd (path))
return (sh_stataccess (path, mode));
-#if defined (HAVE_EACCESS) /* FreeBSD */
- return (eaccess (path, mode));
+#if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
+ return (faccessat (AT_FDCWD, path, mode, AT_EACCESS));
+#elif defined (HAVE_EACCESS) /* FreeBSD */
+ ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */
+# if defined (__FreeBSD__)
+ if (ret == 0 && current_user.euid == 0 && mode == X_OK)
+ return (sh_stataccess (path, mode));
+# endif
+ return ret;
#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
return access (path, mode|EFF_ONLY_OK);
#else
diff --git a/lib/sh/eaccess.c~ b/lib/sh/eaccess.c~
new file mode 100644
index 00000000..efd9363d
--- /dev/null
+++ b/lib/sh/eaccess.c~
@@ -0,0 +1,226 @@
+/* eaccess.c - eaccess replacement for the shell, plus other access functions. */
+
+/* Copyright (C) 2006 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_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+
+#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 "shell.h"
+
+#if !defined (R_OK)
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+#endif /* R_OK */
+
+static int path_is_devfd __P((const char *));
+static int sh_stataccess __P((char *, int));
+#if HAVE_DECL_SETREGID
+static int sh_euidaccess __P((char *, int));
+#endif
+
+static int
+path_is_devfd (path)
+ const char *path;
+{
+ if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
+ return 1;
+ else if (STREQN (path, "/dev/std", 8))
+ {
+ if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err"))
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return 0;
+}
+
+/* A wrapper for stat () which disallows pathnames that are empty strings
+ and handles /dev/fd emulation on systems that don't have it. */
+int
+sh_stat (path, finfo)
+ const char *path;
+ struct stat *finfo;
+{
+ if (*path == '\0')
+ {
+ errno = ENOENT;
+ return (-1);
+ }
+ if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
+ {
+#if !defined (HAVE_DEV_FD)
+ intmax_t fd;
+ int r;
+
+ if (legal_number (path + 8, &fd) && fd == (int)fd)
+ {
+ r = fstat ((int)fd, finfo);
+ if (r == 0 || errno != EBADF)
+ return (r);
+ }
+ errno = ENOENT;
+ return (-1);
+#else
+ /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
+ trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
+ On most systems, with the notable exception of linux, this is
+ effectively a no-op. */
+ char pbuf[32];
+ strcpy (pbuf, DEV_FD_PREFIX);
+ strcat (pbuf, path + 8);
+ return (stat (pbuf, finfo));
+#endif /* !HAVE_DEV_FD */
+ }
+#if !defined (HAVE_DEV_STDIN)
+ else if (STREQN (path, "/dev/std", 8))
+ {
+ if (STREQ (path+8, "in"))
+ return (fstat (0, finfo));
+ else if (STREQ (path+8, "out"))
+ return (fstat (1, finfo));
+ else if (STREQ (path+8, "err"))
+ return (fstat (2, finfo));
+ else
+ return (stat (path, finfo));
+ }
+#endif /* !HAVE_DEV_STDIN */
+ return (stat (path, finfo));
+}
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+ and don't make the mistake of telling root that any file is
+ executable. This version uses stat(2). */
+static int
+sh_stataccess (path, mode)
+ char *path;
+ int mode;
+{
+ struct stat st;
+
+ if (sh_stat (path, &st) < 0)
+ return (-1);
+
+ if (current_user.euid == 0)
+ {
+ /* Root can read or write any file. */
+ if ((mode & X_OK) == 0)
+ return (0);
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return (0);
+ }
+
+ if (st.st_uid == current_user.euid) /* owner */
+ mode <<= 6;
+ else if (group_member (st.st_gid))
+ mode <<= 3;
+
+ if (st.st_mode & mode)
+ return (0);
+
+ errno = EACCES;
+ return (-1);
+}
+
+#if HAVE_DECL_SETREGID
+/* Version to call when uid != euid or gid != egid. We temporarily swap
+ the effective and real uid and gid as appropriate. */
+static int
+sh_euidaccess (path, mode)
+ char *path;
+ int mode;
+{
+ int r, e;
+
+ if (current_user.uid != current_user.euid)
+ setreuid (current_user.euid, current_user.uid);
+ if (current_user.gid != current_user.egid)
+ setregid (current_user.egid, current_user.gid);
+
+ r = access (path, mode);
+ e = errno;
+
+ if (current_user.uid != current_user.euid)
+ setreuid (current_user.uid, current_user.euid);
+ if (current_user.gid != current_user.egid)
+ setregid (current_user.gid, current_user.egid);
+
+ errno = e;
+ return r;
+}
+#endif
+
+int
+sh_eaccess (path, mode)
+ char *path;
+ int mode;
+{
+ int ret;
+
+ if (path_is_devfd (path))
+ return (sh_stataccess (path, mode));
+
+#if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
+ return (faccessat (AT_FDCWD, path, mode, AT_EACCESS));
+#elif defined (HAVE_EACCESS) /* FreeBSD */
+ return (eaccess (path, mode)); /* XXX -- not always correct for X_OK */
+#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
+ return access (path, mode|EFF_ONLY_OK);
+#else
+ if (mode == F_OK)
+ return (sh_stataccess (path, mode));
+
+# if HAVE_DECL_SETREGID
+ if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
+ return (sh_euidaccess (path, mode));
+# endif
+
+ if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
+ return (access (path, mode));
+
+ return (sh_stataccess (path, mode));
+#endif
+}
diff --git a/parse.y b/parse.y
index 92b65aab..dd311875 100644
--- a/parse.y
+++ b/parse.y
@@ -2305,7 +2305,7 @@ shell_getc (remove_quoted_newline)
else
{
char *hdcs;
- hdcs = history_delimiting_chars ();
+ hdcs = history_delimiting_chars (shell_input_line);
if (hdcs && hdcs[0] == ';')
maybe_add_history (shell_input_line);
}
@@ -3062,12 +3062,13 @@ tokword:
* reprompting the user, if necessary, after reading a newline, and returning
* correct error values if it reads EOF.
*/
-#define P_FIRSTCLOSE 0x01
-#define P_ALLOWESC 0x02
-#define P_DQUOTE 0x04
-#define P_COMMAND 0x08 /* parsing a command, so look for comments */
-#define P_BACKQUOTE 0x10 /* parsing a backquoted command substitution */
-#define P_ARRAYSUB 0x20 /* parsing a [...] array subscript for assignment */
+#define P_FIRSTCLOSE 0x0001
+#define P_ALLOWESC 0x0002
+#define P_DQUOTE 0x0004
+#define P_COMMAND 0x0008 /* parsing a command, so look for comments */
+#define P_BACKQUOTE 0x0010 /* parsing a backquoted command substitution */
+#define P_ARRAYSUB 0x0020 /* parsing a [...] array subscript for assignment */
+#define P_DOLBRACE 0x0040 /* parsing a ${...} construct */
/* Lexical state while parsing a grouping construct or $(...). */
#define LEX_WASDOL 0x001
@@ -3115,6 +3116,9 @@ parse_matched_pair (qc, open, close, lenp, flags)
int nestlen, ttranslen, start_lineno;
char *ret, *nestret, *ttrans;
int retind, retsize, rflags;
+ int dolbrace_state;
+
+ dolbrace_state = (flags & P_DOLBRACE) ? DOLBRACE_PARAM : 0;
/*itrace("parse_matched_pair[%d]: open = %c close = %c flags = %d", line_number, open, close, flags);*/
count = 1;
@@ -3225,12 +3229,33 @@ parse_matched_pair (qc, open, close, lenp, flags)
if MBTEST(ch == '\\') /* backslashes */
tflags |= LEX_PASSNEXT;
-#if 0
+#if 0 /* XXX - bash-4.2 */
+ /* Based on which dolstate is currently in (param, op, or word),
+ decide what the op is. We're really only concerned if it's % or
+ #, so we can turn on a flag that says whether or not we should
+ treat single quotes as special when inside a double-quoted
+ ${...}. This logic must agree with subst.c:extract_dollar_brace_string
+ since they share the same defines. */
+ if (flags & P_DOLBRACE)
+ {
+ if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '%' && retind > 0)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '#' && retind > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if MBTEST(dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", ch) != 0)
+ dolbrace_state = DOLBRACE_OP;
+ else if MBTEST(dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", ch) == 0)
+ dolbrace_state = DOLBRACE_WORD;
+ }
+#endif
+
+#if 0 /* XXX - bash-4.2 */
/* The big hammer. Single quotes aren't special in double quotes. The
- problem is that Posix says the single quotes are semi-special:
+ problem is that Posix used to say the single quotes are semi-special:
within a double-quoted ${...} construct "an even number of
unescaped double-quotes or single-quotes, if any, shall occur." */
- if MBTEST(open == '{' && (flags & P_DQUOTE) && ch == '\'') /* } */
+ /* This was changed in Interp 221 */
+ if MBTEST(/*posixly_correct && shell_compatibility_level > 41 &&*/ dolbrace_state == DOLBRACE_QUOTE && (flags & P_DQUOTE) && (flags & P_DOLBRACE) && ch == '\'')
continue;
#endif
@@ -3307,7 +3332,7 @@ parse_dollar_word:
if (ch == '(') /* ) */
nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
else if (ch == '{') /* } */
- nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
+ nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
else if (ch == '[') /* ] */
nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
@@ -3750,7 +3775,7 @@ eof_error:
if (ch == '(') /* ) */
nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
else if (ch == '{') /* } */
- nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
+ nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
else if (ch == '[') /* ] */
nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
@@ -4398,7 +4423,7 @@ read_token_word (character)
((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */
{
if (peek_char == '{') /* } */
- ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE);
+ ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE|P_DOLBRACE);
else if (peek_char == '(') /* ) */
{
/* XXX - push and pop the `(' as a delimiter for use by
@@ -4803,28 +4828,35 @@ static const int no_semi_successors[] = {
/* If we are not within a delimited expression, try to be smart
about which separators can be semi-colons and which must be
newlines. Returns the string that should be added into the
- history entry. */
+ history entry. LINE is the line we're about to add; it helps
+ make some more intelligent decisions in certain cases. */
char *
-history_delimiting_chars ()
+history_delimiting_chars (line)
+ const char *line;
{
+ static int last_was_heredoc = 0; /* was the last entry the start of a here document? */
register int i;
+ if ((parser_state & PST_HEREDOC) == 0)
+ last_was_heredoc = 0;
+
if (dstack.delimiter_depth != 0)
return ("\n");
/* We look for current_command_line_count == 2 because we are looking to
add the first line of the body of the here document (the second line
- of the command). */
+ of the command). We also keep LAST_WAS_HEREDOC as a private sentinel
+ variable to note when we think we added the first line of a here doc
+ (the one with a "<<" somewhere in it) */
if (parser_state & PST_HEREDOC)
-#if 0
-{
- if (last_read_token == WORD && (token_before_that == LESS_LESS || token_before_that == LESS_LESS_MINUS))
- return ("\n"); /* XXX -- need to clean up in bash_add_history */
-#endif
- return (current_command_line_count == 2 ? "\n" : "");
-#if 0
-}
-#endif
+ {
+ if (last_was_heredoc)
+ {
+ last_was_heredoc = 0;
+ return "\n";
+ }
+ return (current_command_line_count == 2 ? "\n" : "");
+ }
/* First, handle some special cases. */
/*(*/
@@ -4847,6 +4879,15 @@ history_delimiting_chars ()
else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
return " "; /* function def using `function name' without `()' */
+ /* If we're not in a here document, but we think we're about to parse one,
+ and we would otherwise return a `;', return a newline to delimit the
+ line with the here-doc delimiter */
+ else if ((parser_state & PST_HEREDOC) == 0 && current_command_line_count > 1 && last_read_token == '\n' && strstr (line, "<<"))
+ {
+ last_was_heredoc = 1;
+ return "\n";
+ }
+
else if (token_before_that == WORD && two_tokens_ago == FOR)
{
/* Tricky. `for i\nin ...' should not have a semicolon, but
diff --git a/parse.y.save b/parse.y.save
new file mode 100644
index 00000000..b786fa7d
--- /dev/null
+++ b/parse.y.save
@@ -0,0 +1,5915 @@
+/* parse.y - Yacc grammar for bash. */
+
+/* Copyright (C) 1989-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"
+#include "bashansi.h"
+
+#include "filecntl.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (HAVE_LOCALE_H)
+# include <locale.h>
+#endif
+
+#include <stdio.h>
+#include "chartypes.h"
+#include <signal.h>
+
+#include "memalloc.h"
+
+#include "bashintl.h"
+
+#define NEED_STRFTIME_DECL /* used in externs.h */
+
+#include "shell.h"
+#include "trap.h"
+#include "flags.h"
+#include "parser.h"
+#include "mailcheck.h"
+#include "test.h"
+#include "builtins.h"
+#include "builtins/common.h"
+#include "builtins/builtext.h"
+
+#include "shmbutil.h"
+
+#if defined (READLINE)
+# include "bashline.h"
+# include <readline/readline.h>
+#endif /* READLINE */
+
+#if defined (HISTORY)
+# include "bashhist.h"
+# include <readline/history.h>
+#endif /* HISTORY */
+
+#if defined (JOB_CONTROL)
+# include "jobs.h"
+#endif /* JOB_CONTROL */
+
+#if defined (ALIAS)
+# include "alias.h"
+#else
+typedef void *alias_t;
+#endif /* ALIAS */
+
+#if defined (PROMPT_STRING_DECODE)
+# ifndef _MINIX
+# include <sys/param.h>
+# endif
+# include <time.h>
+# if defined (TM_IN_SYS_TIME)
+# include <sys/types.h>
+# include <sys/time.h>
+# endif /* TM_IN_SYS_TIME */
+# include "maxpath.h"
+#endif /* PROMPT_STRING_DECODE */
+
+#define RE_READ_TOKEN -99
+#define NO_EXPANSION -100
+
+#ifdef DEBUG
+# define YYDEBUG 1
+#else
+# define YYDEBUG 0
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+# define last_shell_getc_is_singlebyte \
+ ((shell_input_line_index > 1) \
+ ? shell_input_line_property[shell_input_line_index - 1] \
+ : 1)
+# define MBTEST(x) ((x) && last_shell_getc_is_singlebyte)
+#else
+# define last_shell_getc_is_singlebyte 1
+# define MBTEST(x) ((x))
+#endif
+
+#if defined (EXTENDED_GLOB)
+extern int extended_glob;
+#endif
+
+extern int eof_encountered;
+extern int no_line_editing, running_under_emacs;
+extern int current_command_number;
+extern int sourcelevel, parse_and_execute_level;
+extern int posixly_correct;
+extern int last_command_exit_value;
+extern pid_t last_command_subst_pid;
+extern char *shell_name, *current_host_name;
+extern char *dist_version;
+extern int patch_level;
+extern int dump_translatable_strings, dump_po_strings;
+extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
+#if defined (BUFFERED_INPUT)
+extern int bash_input_fd_changed;
+#endif
+
+extern int errno;
+/* **************************************************************** */
+/* */
+/* "Forward" declarations */
+/* */
+/* **************************************************************** */
+
+#ifdef DEBUG
+static void debug_parser __P((int));
+#endif
+
+static int yy_getc __P((void));
+static int yy_ungetc __P((int));
+
+#if defined (READLINE)
+static int yy_readline_get __P((void));
+static int yy_readline_unget __P((int));
+#endif
+
+static int yy_string_get __P((void));
+static int yy_string_unget __P((int));
+static void rewind_input_string __P((void));
+static int yy_stream_get __P((void));
+static int yy_stream_unget __P((int));
+
+static int shell_getc __P((int));
+static void shell_ungetc __P((int));
+static void discard_until __P((int));
+
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+static void push_string __P((char *, int, alias_t *));
+static void pop_string __P((void));
+static void free_string_list __P((void));
+#endif
+
+static char *read_a_line __P((int));
+
+static int reserved_word_acceptable __P((int));
+static int yylex __P((void));
+static int alias_expand_token __P((char *));
+static int time_command_acceptable __P((void));
+static int special_case_tokens __P((char *));
+static int read_token __P((int));
+static char *parse_matched_pair __P((int, int, int, int *, int));
+static char *parse_comsub __P((int, int, int, int *, int));
+#if defined (ARRAY_VARS)
+static char *parse_compound_assignment __P((int *));
+#endif
+#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
+static int parse_dparen __P((int));
+static int parse_arith_cmd __P((char **, int));
+#endif
+#if defined (COND_COMMAND)
+static void cond_error __P((void));
+static COND_COM *cond_expr __P((void));
+static COND_COM *cond_or __P((void));
+static COND_COM *cond_and __P((void));
+static COND_COM *cond_term __P((void));
+static int cond_skip_newlines __P((void));
+static COMMAND *parse_cond_command __P((void));
+#endif
+#if defined (ARRAY_VARS)
+static int token_is_assignment __P((char *, int));
+static int token_is_ident __P((char *, int));
+#endif
+static int read_token_word __P((int));
+static void discard_parser_constructs __P((int));
+
+static char *error_token_from_token __P((int));
+static char *error_token_from_text __P((void));
+static void print_offending_line __P((void));
+static void report_syntax_error __P((char *));
+
+static void handle_eof_input_unit __P((void));
+static void prompt_again __P((void));
+#if 0
+static void reset_readline_prompt __P((void));
+#endif
+static void print_prompt __P((void));
+
+#if defined (HANDLE_MULTIBYTE)
+static void set_line_mbstate __P((void));
+static char *shell_input_line_property = NULL;
+#else
+# define set_line_mbstate()
+#endif
+
+extern int yyerror __P((const char *));
+
+#ifdef DEBUG
+extern int yydebug;
+#endif
+
+/* Default prompt strings */
+char *primary_prompt = PPROMPT;
+char *secondary_prompt = SPROMPT;
+
+/* PROMPT_STRING_POINTER points to one of these, never to an actual string. */
+char *ps1_prompt, *ps2_prompt;
+
+/* Handle on the current prompt string. Indirectly points through
+ ps1_ or ps2_prompt. */
+char **prompt_string_pointer = (char **)NULL;
+char *current_prompt_string;
+
+/* Non-zero means we expand aliases in commands. */
+int expand_aliases = 0;
+
+/* If non-zero, the decoded prompt string undergoes parameter and
+ variable substitution, command substitution, arithmetic substitution,
+ string expansion, process substitution, and quote removal in
+ decode_prompt_string. */
+int promptvars = 1;
+
+/* If non-zero, $'...' and $"..." are expanded when they appear within
+ a ${...} expansion, even when the expansion appears within double
+ quotes. */
+int extended_quote = 1;
+
+/* The number of lines read from input while creating the current command. */
+int current_command_line_count;
+
+/* The token that currently denotes the end of parse. */
+int shell_eof_token;
+
+/* The token currently being read. */
+int current_token;
+
+/* The current parser state. */
+int parser_state;
+
+/* Variables to manage the task of reading here documents, because we need to
+ defer the reading until after a complete command has been collected. */
+static REDIRECT *redir_stack[10];
+int need_here_doc;
+
+/* Where shell input comes from. History expansion is performed on each
+ line when the shell is interactive. */
+static char *shell_input_line = (char *)NULL;
+static int shell_input_line_index;
+static int shell_input_line_size; /* Amount allocated for shell_input_line. */
+static int shell_input_line_len; /* strlen (shell_input_line) */
+
+/* Either zero or EOF. */
+static int shell_input_line_terminator;
+
+/* The line number in a script on which a function definition starts. */
+static int function_dstart;
+
+/* The line number in a script on which a function body starts. */
+static int function_bstart;
+
+/* The line number in a script at which an arithmetic for command starts. */
+static int arith_for_lineno;
+
+/* The decoded prompt string. Used if READLINE is not defined or if
+ editing is turned off. Analogous to current_readline_prompt. */
+static char *current_decoded_prompt;
+
+/* The last read token, or NULL. read_token () uses this for context
+ checking. */
+static int last_read_token;
+
+/* The token read prior to last_read_token. */
+static int token_before_that;
+
+/* The token read prior to token_before_that. */
+static int two_tokens_ago;
+
+static int global_extglob;
+
+/* The line number in a script where the word in a `case WORD', `select WORD'
+ or `for WORD' begins. This is a nested command maximum, since the array
+ index is decremented after a case, select, or for command is parsed. */
+#define MAX_CASE_NEST 128
+static int word_lineno[MAX_CASE_NEST];
+static int word_top = -1;
+
+/* If non-zero, it is the token that we want read_token to return
+ regardless of what text is (or isn't) present to be read. This
+ is reset by read_token. If token_to_read == WORD or
+ ASSIGNMENT_WORD, yylval.word should be set to word_desc_to_read. */
+static int token_to_read;
+static WORD_DESC *word_desc_to_read;
+
+static REDIRECTEE source;
+static REDIRECTEE redir;
+%}
+
+%union {
+ WORD_DESC *word; /* the word that we read. */
+ int number; /* the number that we read. */
+ WORD_LIST *word_list;
+ COMMAND *command;
+ REDIRECT *redirect;
+ ELEMENT element;
+ PATTERN_LIST *pattern;
+}
+
+/* Reserved words. Members of the first group are only recognized
+ in the case that they are preceded by a list_terminator. Members
+ of the second group are for [[...]] commands. Members of the
+ third group are recognized only under special circumstances. */
+%token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION COPROC
+%token COND_START COND_END COND_ERROR
+%token IN BANG TIME TIMEOPT
+
+/* More general tokens. yylex () knows how to make these. */
+%token <word> WORD ASSIGNMENT_WORD REDIR_WORD
+%token <number> NUMBER
+%token <word_list> ARITH_CMD ARITH_FOR_EXPRS
+%token <command> COND_CMD
+%token AND_AND OR_OR GREATER_GREATER LESS_LESS LESS_AND LESS_LESS_LESS
+%token GREATER_AND SEMI_SEMI SEMI_AND SEMI_SEMI_AND
+%token LESS_LESS_MINUS AND_GREATER AND_GREATER_GREATER LESS_GREATER
+%token GREATER_BAR BAR_AND
+
+/* The types that the various syntactical units return. */
+
+%type <command> inputunit command pipeline pipeline_command
+%type <command> list list0 list1 compound_list simple_list simple_list1
+%type <command> simple_command shell_command
+%type <command> for_command select_command case_command group_command
+%type <command> arith_command
+%type <command> cond_command
+%type <command> arith_for_command
+%type <command> coproc
+%type <command> function_def function_body if_command elif_clause subshell
+%type <redirect> redirection redirection_list
+%type <element> simple_command_element
+%type <word_list> word_list pattern
+%type <pattern> pattern_list case_clause_sequence case_clause
+%type <number> timespec
+%type <number> list_terminator
+
+%start inputunit
+
+%left '&' ';' '\n' yacc_EOF
+%left AND_AND OR_OR
+%right '|' BAR_AND
+%%
+
+inputunit: simple_list simple_list_terminator
+ {
+ /* Case of regular command. Discard the error
+ safety net,and return the command just parsed. */
+ global_command = $1;
+ eof_encountered = 0;
+ /* discard_parser_constructs (0); */
+ if (parser_state & PST_CMDSUBST)
+ parser_state |= PST_EOFTOKEN;
+ YYACCEPT;
+ }
+ | '\n'
+ {
+ /* Case of regular command, but not a very
+ interesting one. Return a NULL command. */
+ global_command = (COMMAND *)NULL;
+ if (parser_state & PST_CMDSUBST)
+ parser_state |= PST_EOFTOKEN;
+ YYACCEPT;
+ }
+ | error '\n'
+ {
+ /* Error during parsing. Return NULL command. */
+ global_command = (COMMAND *)NULL;
+ eof_encountered = 0;
+ /* discard_parser_constructs (1); */
+ if (interactive && parse_and_execute_level == 0)
+ {
+ YYACCEPT;
+ }
+ else
+ {
+ YYABORT;
+ }
+ }
+ | yacc_EOF
+ {
+ /* Case of EOF seen by itself. Do ignoreeof or
+ not. */
+ global_command = (COMMAND *)NULL;
+ handle_eof_input_unit ();
+ YYACCEPT;
+ }
+ ;
+
+word_list: WORD
+ { $$ = make_word_list ($1, (WORD_LIST *)NULL); }
+ | word_list WORD
+ { $$ = make_word_list ($2, $1); }
+ ;
+
+redirection: '>' WORD
+ {
+ source.dest = 1;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_output_direction, redir, 0);
+ }
+ | '<' WORD
+ {
+ source.dest = 0;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_input_direction, redir, 0);
+ }
+ | NUMBER '>' WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_output_direction, redir, 0);
+ }
+ | NUMBER '<' WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_input_direction, redir, 0);
+ }
+ | REDIR_WORD '>' WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_output_direction, redir, REDIR_VARASSIGN);
+ }
+ | REDIR_WORD '<' WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_input_direction, redir, REDIR_VARASSIGN);
+ }
+ | GREATER_GREATER WORD
+ {
+ source.dest = 1;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_appending_to, redir, 0);
+ }
+ | NUMBER GREATER_GREATER WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_appending_to, redir, 0);
+ }
+ | REDIR_WORD GREATER_GREATER WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_appending_to, redir, REDIR_VARASSIGN);
+ }
+ | GREATER_BAR WORD
+ {
+ source.dest = 1;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_output_force, redir, 0);
+ }
+ | NUMBER GREATER_BAR WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_output_force, redir, 0);
+ }
+ | REDIR_WORD GREATER_BAR WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_output_force, redir, REDIR_VARASSIGN);
+ }
+ | LESS_GREATER WORD
+ {
+ source.dest = 0;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_input_output, redir, 0);
+ }
+ | NUMBER LESS_GREATER WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_input_output, redir, 0);
+ }
+ | REDIR_WORD LESS_GREATER WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_input_output, redir, REDIR_VARASSIGN);
+ }
+ | LESS_LESS WORD
+ {
+ source.dest = 0;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_reading_until, redir, 0);
+ redir_stack[need_here_doc++] = $$;
+ }
+ | NUMBER LESS_LESS WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_reading_until, redir, 0);
+ redir_stack[need_here_doc++] = $$;
+ }
+ | REDIR_WORD LESS_LESS WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_reading_until, redir, REDIR_VARASSIGN);
+ redir_stack[need_here_doc++] = $$;
+ }
+ | LESS_LESS_MINUS WORD
+ {
+ source.dest = 0;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_deblank_reading_until, redir, 0);
+ redir_stack[need_here_doc++] = $$;
+ }
+ | NUMBER LESS_LESS_MINUS WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_deblank_reading_until, redir, 0);
+ redir_stack[need_here_doc++] = $$;
+ }
+ | REDIR_WORD LESS_LESS_MINUS WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_deblank_reading_until, redir, REDIR_VARASSIGN);
+ redir_stack[need_here_doc++] = $$;
+ }
+ | LESS_LESS_LESS WORD
+ {
+ source.dest = 0;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_reading_string, redir, 0);
+ }
+ | NUMBER LESS_LESS_LESS WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_reading_string, redir, 0);
+ }
+ | REDIR_WORD LESS_LESS_LESS WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_reading_string, redir, REDIR_VARASSIGN);
+ }
+ | LESS_AND NUMBER
+ {
+ source.dest = 0;
+ redir.dest = $2;
+ $$ = make_redirection (source, r_duplicating_input, redir, 0);
+ }
+ | NUMBER LESS_AND NUMBER
+ {
+ source.dest = $1;
+ redir.dest = $3;
+ $$ = make_redirection (source, r_duplicating_input, redir, 0);
+ }
+ | REDIR_WORD LESS_AND NUMBER
+ {
+ source.filename = $1;
+ redir.dest = $3;
+ $$ = make_redirection (source, r_duplicating_input, redir, REDIR_VARASSIGN);
+ }
+ | GREATER_AND NUMBER
+ {
+ source.dest = 1;
+ redir.dest = $2;
+ $$ = make_redirection (source, r_duplicating_output, redir, 0);
+ }
+ | NUMBER GREATER_AND NUMBER
+ {
+ source.dest = $1;
+ redir.dest = $3;
+ $$ = make_redirection (source, r_duplicating_output, redir, 0);
+ }
+ | REDIR_WORD GREATER_AND NUMBER
+ {
+ source.filename = $1;
+ redir.dest = $3;
+ $$ = make_redirection (source, r_duplicating_output, redir, REDIR_VARASSIGN);
+ }
+ | LESS_AND WORD
+ {
+ source.dest = 0;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_duplicating_input_word, redir, 0);
+ }
+ | NUMBER LESS_AND WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_duplicating_input_word, redir, 0);
+ }
+ | REDIR_WORD LESS_AND WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_duplicating_input_word, redir, REDIR_VARASSIGN);
+ }
+ | GREATER_AND WORD
+ {
+ source.dest = 1;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_duplicating_output_word, redir, 0);
+ }
+ | NUMBER GREATER_AND WORD
+ {
+ source.dest = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_duplicating_output_word, redir, 0);
+ }
+ | REDIR_WORD GREATER_AND WORD
+ {
+ source.filename = $1;
+ redir.filename = $3;
+ $$ = make_redirection (source, r_duplicating_output_word, redir, REDIR_VARASSIGN);
+ }
+ | GREATER_AND '-'
+ {
+ source.dest = 1;
+ redir.dest = 0;
+ $$ = make_redirection (source, r_close_this, redir, 0);
+ }
+ | NUMBER GREATER_AND '-'
+ {
+ source.dest = $1;
+ redir.dest = 0;
+ $$ = make_redirection (source, r_close_this, redir, 0);
+ }
+ | REDIR_WORD GREATER_AND '-'
+ {
+ source.filename = $1;
+ redir.dest = 0;
+ $$ = make_redirection (source, r_close_this, redir, REDIR_VARASSIGN);
+ }
+ | LESS_AND '-'
+ {
+ source.dest = 0;
+ redir.dest = 0;
+ $$ = make_redirection (source, r_close_this, redir, 0);
+ }
+ | NUMBER LESS_AND '-'
+ {
+ source.dest = $1;
+ redir.dest = 0;
+ $$ = make_redirection (source, r_close_this, redir, 0);
+ }
+ | REDIR_WORD LESS_AND '-'
+ {
+ source.filename = $1;
+ redir.dest = 0;
+ $$ = make_redirection (source, r_close_this, redir, REDIR_VARASSIGN);
+ }
+ | AND_GREATER WORD
+ {
+ source.dest = 1;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_err_and_out, redir, 0);
+ }
+ | AND_GREATER_GREATER WORD
+ {
+ source.dest = 1;
+ redir.filename = $2;
+ $$ = make_redirection (source, r_append_err_and_out, redir, 0);
+ }
+ ;
+
+simple_command_element: WORD
+ { $$.word = $1; $$.redirect = 0; }
+ | ASSIGNMENT_WORD
+ { $$.word = $1; $$.redirect = 0; }
+ | redirection
+ { $$.redirect = $1; $$.word = 0; }
+ ;
+
+redirection_list: redirection
+ {
+ $$ = $1;
+ }
+ | redirection_list redirection
+ {
+ register REDIRECT *t;
+
+ for (t = $1; t->next; t = t->next)
+ ;
+ t->next = $2;
+ $$ = $1;
+ }
+ ;
+
+simple_command: simple_command_element
+ { $$ = make_simple_command ($1, (COMMAND *)NULL); }
+ | simple_command simple_command_element
+ { $$ = make_simple_command ($2, $1); }
+ ;
+
+command: simple_command
+ { $$ = clean_simple_command ($1); }
+ | shell_command
+ { $$ = $1; }
+ | shell_command redirection_list
+ {
+ COMMAND *tc;
+
+ tc = $1;
+ if (tc->redirects)
+ {
+ register REDIRECT *t;
+ for (t = tc->redirects; t->next; t = t->next)
+ ;
+ t->next = $2;
+ }
+ else
+ tc->redirects = $2;
+ $$ = $1;
+ }
+ | function_def
+ { $$ = $1; }
+ | coproc
+ { $$ = $1; }
+ ;
+
+shell_command: for_command
+ { $$ = $1; }
+ | case_command
+ { $$ = $1; }
+ | WHILE compound_list DO compound_list DONE
+ { $$ = make_while_command ($2, $4); }
+ | UNTIL compound_list DO compound_list DONE
+ { $$ = make_until_command ($2, $4); }
+ | select_command
+ { $$ = $1; }
+ | if_command
+ { $$ = $1; }
+ | subshell
+ { $$ = $1; }
+ | group_command
+ { $$ = $1; }
+ | arith_command
+ { $$ = $1; }
+ | cond_command
+ { $$ = $1; }
+ | arith_for_command
+ { $$ = $1; }
+ ;
+
+for_command: FOR WORD newline_list DO compound_list DONE
+ {
+ $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD newline_list '{' compound_list '}'
+ {
+ $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD ';' newline_list DO compound_list DONE
+ {
+ $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD ';' newline_list '{' compound_list '}'
+ {
+ $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE
+ {
+ $$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD newline_list IN word_list list_terminator newline_list '{' compound_list '}'
+ {
+ $$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD newline_list IN list_terminator newline_list DO compound_list DONE
+ {
+ $$ = make_for_command ($2, (WORD_LIST *)NULL, $8, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | FOR WORD newline_list IN list_terminator newline_list '{' compound_list '}'
+ {
+ $$ = make_for_command ($2, (WORD_LIST *)NULL, $8, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ ;
+
+arith_for_command: FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_list DONE
+ {
+ $$ = make_arith_for_command ($2, $6, arith_for_lineno);
+ if (word_top > 0) word_top--;
+ }
+ | FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}'
+ {
+ $$ = make_arith_for_command ($2, $6, arith_for_lineno);
+ if (word_top > 0) word_top--;
+ }
+ | FOR ARITH_FOR_EXPRS DO compound_list DONE
+ {
+ $$ = make_arith_for_command ($2, $4, arith_for_lineno);
+ if (word_top > 0) word_top--;
+ }
+ | FOR ARITH_FOR_EXPRS '{' compound_list '}'
+ {
+ $$ = make_arith_for_command ($2, $4, arith_for_lineno);
+ if (word_top > 0) word_top--;
+ }
+ ;
+
+select_command: SELECT WORD newline_list DO list DONE
+ {
+ $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | SELECT WORD newline_list '{' list '}'
+ {
+ $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | SELECT WORD ';' newline_list DO list DONE
+ {
+ $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | SELECT WORD ';' newline_list '{' list '}'
+ {
+ $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | SELECT WORD newline_list IN word_list list_terminator newline_list DO list DONE
+ {
+ $$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | SELECT WORD newline_list IN word_list list_terminator newline_list '{' list '}'
+ {
+ $$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ ;
+
+case_command: CASE WORD newline_list IN newline_list ESAC
+ {
+ $$ = make_case_command ($2, (PATTERN_LIST *)NULL, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | CASE WORD newline_list IN case_clause_sequence newline_list ESAC
+ {
+ $$ = make_case_command ($2, $5, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ | CASE WORD newline_list IN case_clause ESAC
+ {
+ $$ = make_case_command ($2, $5, word_lineno[word_top]);
+ if (word_top > 0) word_top--;
+ }
+ ;
+
+function_def: WORD '(' ')' newline_list function_body
+ { $$ = make_function_def ($1, $5, function_dstart, function_bstart); }
+
+ | FUNCTION WORD '(' ')' newline_list function_body
+ { $$ = make_function_def ($2, $6, function_dstart, function_bstart); }
+
+ | FUNCTION WORD newline_list function_body
+ { $$ = make_function_def ($2, $4, function_dstart, function_bstart); }
+ ;
+
+function_body: shell_command
+ { $$ = $1; }
+ | shell_command redirection_list
+ {
+ COMMAND *tc;
+
+ tc = $1;
+ /* According to Posix.2 3.9.5, redirections
+ specified after the body of a function should
+ be attached to the function and performed when
+ the function is executed, not as part of the
+ function definition command. */
+ /* XXX - I don't think it matters, but we might
+ want to change this in the future to avoid
+ problems differentiating between a function
+ definition with a redirection and a function
+ definition containing a single command with a
+ redirection. The two are semantically equivalent,
+ though -- the only difference is in how the
+ command printing code displays the redirections. */
+ if (tc->redirects)
+ {
+ register REDIRECT *t;
+ for (t = tc->redirects; t->next; t = t->next)
+ ;
+ t->next = $2;
+ }
+ else
+ tc->redirects = $2;
+ $$ = $1;
+ }
+ ;
+
+subshell: '(' compound_list ')'
+ {
+ $$ = make_subshell_command ($2);
+ $$->flags |= CMD_WANT_SUBSHELL;
+ }
+ ;
+
+coproc: COPROC shell_command
+ {
+ $$ = make_coproc_command ("COPROC", $2);
+ $$->flags |= CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL;
+ }
+ | COPROC shell_command redirection_list
+ {
+ COMMAND *tc;
+
+ tc = $2;
+ if (tc->redirects)
+ {
+ register REDIRECT *t;
+ for (t = tc->redirects; t->next; t = t->next)
+ ;
+ t->next = $3;
+ }
+ else
+ tc->redirects = $3;
+ $$ = make_coproc_command ("COPROC", $2);
+ $$->flags |= CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL;
+ }
+ | COPROC WORD shell_command
+ {
+ $$ = make_coproc_command ($2->word, $3);
+ $$->flags |= CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL;
+ }
+ | COPROC WORD shell_command redirection_list
+ {
+ COMMAND *tc;
+
+ tc = $3;
+ if (tc->redirects)
+ {
+ register REDIRECT *t;
+ for (t = tc->redirects; t->next; t = t->next)
+ ;
+ t->next = $4;
+ }
+ else
+ tc->redirects = $4;
+ $$ = make_coproc_command ($2->word, $3);
+ $$->flags |= CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL;
+ }
+ | COPROC simple_command
+ {
+ $$ = make_coproc_command ("COPROC", clean_simple_command ($2));
+ $$->flags |= CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL;
+ }
+ ;
+
+if_command: IF compound_list THEN compound_list FI
+ { $$ = make_if_command ($2, $4, (COMMAND *)NULL); }
+ | IF compound_list THEN compound_list ELSE compound_list FI
+ { $$ = make_if_command ($2, $4, $6); }
+ | IF compound_list THEN compound_list elif_clause FI
+ { $$ = make_if_command ($2, $4, $5); }
+ ;
+
+
+group_command: '{' compound_list '}'
+ { $$ = make_group_command ($2); }
+ ;
+
+arith_command: ARITH_CMD
+ { $$ = make_arith_command ($1); }
+ ;
+
+cond_command: COND_START COND_CMD COND_END
+ { $$ = $2; }
+ ;
+
+elif_clause: ELIF compound_list THEN compound_list
+ { $$ = make_if_command ($2, $4, (COMMAND *)NULL); }
+ | ELIF compound_list THEN compound_list ELSE compound_list
+ { $$ = make_if_command ($2, $4, $6); }
+ | ELIF compound_list THEN compound_list elif_clause
+ { $$ = make_if_command ($2, $4, $5); }
+ ;
+
+case_clause: pattern_list
+ | case_clause_sequence pattern_list
+ { $2->next = $1; $$ = $2; }
+ ;
+
+pattern_list: newline_list pattern ')' compound_list
+ { $$ = make_pattern_list ($2, $4); }
+ | newline_list pattern ')' newline_list
+ { $$ = make_pattern_list ($2, (COMMAND *)NULL); }
+ | newline_list '(' pattern ')' compound_list
+ { $$ = make_pattern_list ($3, $5); }
+ | newline_list '(' pattern ')' newline_list
+ { $$ = make_pattern_list ($3, (COMMAND *)NULL); }
+ ;
+
+case_clause_sequence: pattern_list SEMI_SEMI
+ { $$ = $1; }
+ | case_clause_sequence pattern_list SEMI_SEMI
+ { $2->next = $1; $$ = $2; }
+ | pattern_list SEMI_AND
+ { $1->flags |= CASEPAT_FALLTHROUGH; $$ = $1; }
+ | case_clause_sequence pattern_list SEMI_AND
+ { $2->flags |= CASEPAT_FALLTHROUGH; $2->next = $1; $$ = $2; }
+ | pattern_list SEMI_SEMI_AND
+ { $1->flags |= CASEPAT_TESTNEXT; $$ = $1; }
+ | case_clause_sequence pattern_list SEMI_SEMI_AND
+ { $2->flags |= CASEPAT_TESTNEXT; $2->next = $1; $$ = $2; }
+ ;
+
+pattern: WORD
+ { $$ = make_word_list ($1, (WORD_LIST *)NULL); }
+ | pattern '|' WORD
+ { $$ = make_word_list ($3, $1); }
+ ;
+
+/* A list allows leading or trailing newlines and
+ newlines as operators (equivalent to semicolons).
+ It must end with a newline or semicolon.
+ Lists are used within commands such as if, for, while. */
+
+list: newline_list list0
+ {
+ $$ = $2;
+ if (need_here_doc)
+ gather_here_documents ();
+ }
+ ;
+
+compound_list: list
+ | newline_list list1
+ {
+ $$ = $2;
+ }
+ ;
+
+list0: list1 '\n' newline_list
+ | list1 '&' newline_list
+ {
+ if ($1->type == cm_connection)
+ $$ = connect_async_list ($1, (COMMAND *)NULL, '&');
+ else
+ $$ = command_connect ($1, (COMMAND *)NULL, '&');
+ }
+ | list1 ';' newline_list
+
+ ;
+
+list1: list1 AND_AND newline_list list1
+ { $$ = command_connect ($1, $4, AND_AND); }
+ | list1 OR_OR newline_list list1
+ { $$ = command_connect ($1, $4, OR_OR); }
+ | list1 '&' newline_list list1
+ {
+ if ($1->type == cm_connection)
+ $$ = connect_async_list ($1, $4, '&');
+ else
+ $$ = command_connect ($1, $4, '&');
+ }
+ | list1 ';' newline_list list1
+ { $$ = command_connect ($1, $4, ';'); }
+ | list1 '\n' newline_list list1
+ { $$ = command_connect ($1, $4, ';'); }
+ | pipeline_command
+ { $$ = $1; }
+ ;
+
+simple_list_terminator: '\n'
+ | yacc_EOF
+ ;
+
+list_terminator:'\n'
+ { $$ = '\n'; }
+ | ';'
+ { $$ = ';'; }
+ | yacc_EOF
+ { $$ = yacc_EOF; }
+ ;
+
+newline_list:
+ | newline_list '\n'
+ ;
+
+/* A simple_list is a list that contains no significant newlines
+ and no leading or trailing newlines. Newlines are allowed
+ only following operators, where they are not significant.
+
+ This is what an inputunit consists of. */
+
+simple_list: simple_list1
+ {
+ $$ = $1;
+ if (need_here_doc)
+ gather_here_documents ();
+ if ((parser_state & PST_CMDSUBST) && current_token == shell_eof_token)
+ {
+ global_command = $1;
+ eof_encountered = 0;
+ rewind_input_string ();
+ YYACCEPT;
+ }
+ }
+ | simple_list1 '&'
+ {
+ if ($1->type == cm_connection)
+ $$ = connect_async_list ($1, (COMMAND *)NULL, '&');
+ else
+ $$ = command_connect ($1, (COMMAND *)NULL, '&');
+ if (need_here_doc)
+ gather_here_documents ();
+ if ((parser_state & PST_CMDSUBST) && current_token == shell_eof_token)
+ {
+ global_command = $1;
+ eof_encountered = 0;
+ rewind_input_string ();
+ YYACCEPT;
+ }
+ }
+ | simple_list1 ';'
+ {
+ $$ = $1;
+ if (need_here_doc)
+ gather_here_documents ();
+ if ((parser_state & PST_CMDSUBST) && current_token == shell_eof_token)
+ {
+ global_command = $1;
+ eof_encountered = 0;
+ rewind_input_string ();
+ YYACCEPT;
+ }
+ }
+ ;
+
+simple_list1: simple_list1 AND_AND newline_list simple_list1
+ { $$ = command_connect ($1, $4, AND_AND); }
+ | simple_list1 OR_OR newline_list simple_list1
+ { $$ = command_connect ($1, $4, OR_OR); }
+ | simple_list1 '&' simple_list1
+ {
+ if ($1->type == cm_connection)
+ $$ = connect_async_list ($1, $3, '&');
+ else
+ $$ = command_connect ($1, $3, '&');
+ }
+ | simple_list1 ';' simple_list1
+ { $$ = command_connect ($1, $3, ';'); }
+
+ | pipeline_command
+ { $$ = $1; }
+ ;
+
+pipeline_command: pipeline
+ { $$ = $1; }
+ | BANG pipeline
+ {
+ if ($2)
+ $2->flags |= CMD_INVERT_RETURN;
+ $$ = $2;
+ }
+ | timespec pipeline
+ {
+ if ($2)
+ $2->flags |= $1;
+ $$ = $2;
+ }
+ | timespec BANG pipeline
+ {
+ if ($3)
+ $3->flags |= $1|CMD_INVERT_RETURN;
+ $$ = $3;
+ }
+ | BANG timespec pipeline
+ {
+ if ($3)
+ $3->flags |= $2|CMD_INVERT_RETURN;
+ $$ = $3;
+ }
+ | timespec list_terminator
+ {
+ ELEMENT x;
+
+ /* Boy, this is unclean. `time' by itself can
+ time a null command. We cheat and push a
+ newline back if the list_terminator was a newline
+ to avoid the double-newline problem (one to
+ terminate this, one to terminate the command) */
+ x.word = 0;
+ x.redirect = 0;
+ $$ = make_simple_command (x, (COMMAND *)NULL);
+ $$->flags |= $1;
+ /* XXX - let's cheat and push a newline back */
+ if ($2 == '\n')
+ token_to_read = '\n';
+ }
+
+ ;
+
+pipeline: pipeline '|' newline_list pipeline
+ { $$ = command_connect ($1, $4, '|'); }
+ | pipeline BAR_AND newline_list pipeline
+ {
+ /* Make cmd1 |& cmd2 equivalent to cmd1 2>&1 | cmd2 */
+ COMMAND *tc;
+ REDIRECTEE rd, sd;
+ REDIRECT *r;
+
+ tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple : $1;
+ sd.dest = 2;
+ rd.dest = 1;
+ r = make_redirection (sd, r_duplicating_output, rd, 0);
+ if (tc->redirects)
+ {
+ register REDIRECT *t;
+ for (t = tc->redirects; t->next; t = t->next)
+ ;
+ t->next = r;
+ }
+ else
+ tc->redirects = r;
+
+ $$ = command_connect ($1, $4, '|');
+ }
+ | command
+ { $$ = $1; }
+ ;
+
+timespec: TIME
+ { $$ = CMD_TIME_PIPELINE; }
+ | TIME TIMEOPT
+ { $$ = CMD_TIME_PIPELINE|CMD_TIME_POSIX; }
+ ;
+%%
+
+/* Initial size to allocate for tokens, and the
+ amount to grow them by. */
+#define TOKEN_DEFAULT_INITIAL_SIZE 496
+#define TOKEN_DEFAULT_GROW_SIZE 512
+
+/* Should we call prompt_again? */
+#define SHOULD_PROMPT() \
+ (interactive && (bash_input.type == st_stdin || bash_input.type == st_stream))
+
+#if defined (ALIAS)
+# define expanding_alias() (pushed_string_list && pushed_string_list->expander)
+#else
+# define expanding_alias() 0
+#endif
+
+/* Global var is non-zero when end of file has been reached. */
+int EOF_Reached = 0;
+
+#ifdef DEBUG
+static void
+debug_parser (i)
+ int i;
+{
+#if YYDEBUG != 0
+ yydebug = i;
+#endif
+}
+#endif
+
+/* yy_getc () returns the next available character from input or EOF.
+ yy_ungetc (c) makes `c' the next character to read.
+ init_yy_io (get, unget, type, location) makes the function GET the
+ installed function for getting the next character, makes UNGET the
+ installed function for un-getting a character, sets the type of stream
+ (either string or file) from TYPE, and makes LOCATION point to where
+ the input is coming from. */
+
+/* Unconditionally returns end-of-file. */
+int
+return_EOF ()
+{
+ return (EOF);
+}
+
+/* Variable containing the current get and unget functions.
+ See ./input.h for a clearer description. */
+BASH_INPUT bash_input;
+
+/* Set all of the fields in BASH_INPUT to NULL. Free bash_input.name if it
+ is non-null, avoiding a memory leak. */
+void
+initialize_bash_input ()
+{
+ bash_input.type = st_none;
+ FREE (bash_input.name);
+ bash_input.name = (char *)NULL;
+ bash_input.location.file = (FILE *)NULL;
+ bash_input.location.string = (char *)NULL;
+ bash_input.getter = (sh_cget_func_t *)NULL;
+ bash_input.ungetter = (sh_cunget_func_t *)NULL;
+}
+
+/* Set the contents of the current bash input stream from
+ GET, UNGET, TYPE, NAME, and LOCATION. */
+void
+init_yy_io (get, unget, type, name, location)
+ sh_cget_func_t *get;
+ sh_cunget_func_t *unget;
+ enum stream_type type;
+ const char *name;
+ INPUT_STREAM location;
+{
+ bash_input.type = type;
+ FREE (bash_input.name);
+ bash_input.name = name ? savestring (name) : (char *)NULL;
+
+ /* XXX */
+#if defined (CRAY)
+ memcpy((char *)&bash_input.location.string, (char *)&location.string, sizeof(location));
+#else
+ bash_input.location = location;
+#endif
+ bash_input.getter = get;
+ bash_input.ungetter = unget;
+}
+
+char *
+yy_input_name ()
+{
+ return (bash_input.name ? bash_input.name : "stdin");
+}
+
+/* Call this to get the next character of input. */
+static int
+yy_getc ()
+{
+ return (*(bash_input.getter)) ();
+}
+
+/* Call this to unget C. That is, to make C the next character
+ to be read. */
+static int
+yy_ungetc (c)
+ int c;
+{
+ return (*(bash_input.ungetter)) (c);
+}
+
+#if defined (BUFFERED_INPUT)
+#ifdef INCLUDE_UNUSED
+int
+input_file_descriptor ()
+{
+ switch (bash_input.type)
+ {
+ case st_stream:
+ return (fileno (bash_input.location.file));
+ case st_bstream:
+ return (bash_input.location.buffered_fd);
+ case st_stdin:
+ default:
+ return (fileno (stdin));
+ }
+}
+#endif
+#endif /* BUFFERED_INPUT */
+
+/* **************************************************************** */
+/* */
+/* Let input be read from readline (). */
+/* */
+/* **************************************************************** */
+
+#if defined (READLINE)
+char *current_readline_prompt = (char *)NULL;
+char *current_readline_line = (char *)NULL;
+int current_readline_line_index = 0;
+
+static int
+yy_readline_get ()
+{
+ SigHandler *old_sigint;
+ int line_len;
+ unsigned char c;
+
+ if (!current_readline_line)
+ {
+ if (!bash_readline_initialized)
+ initialize_readline ();
+
+#if defined (JOB_CONTROL)
+ if (job_control)
+ give_terminal_to (shell_pgrp, 0);
+#endif /* JOB_CONTROL */
+
+ old_sigint = (SigHandler *)IMPOSSIBLE_TRAP_HANDLER;
+ if (signal_is_ignored (SIGINT) == 0)
+ {
+ interrupt_immediately++;
+ old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler);
+ }
+ terminate_immediately = 1;
+
+ current_readline_line = readline (current_readline_prompt ?
+ current_readline_prompt : "");
+
+ terminate_immediately = 0;
+ if (signal_is_ignored (SIGINT) == 0)
+ {
+ interrupt_immediately--;
+ if (old_sigint != IMPOSSIBLE_TRAP_HANDLER)
+ set_signal_handler (SIGINT, old_sigint);
+ }
+
+#if 0
+ /* Reset the prompt to the decoded value of prompt_string_pointer. */
+ reset_readline_prompt ();
+#endif
+
+ if (current_readline_line == 0)
+ return (EOF);
+
+ current_readline_line_index = 0;
+ line_len = strlen (current_readline_line);
+
+ current_readline_line = (char *)xrealloc (current_readline_line, 2 + line_len);
+ current_readline_line[line_len++] = '\n';
+ current_readline_line[line_len] = '\0';
+ }
+
+ if (current_readline_line[current_readline_line_index] == 0)
+ {
+ free (current_readline_line);
+ current_readline_line = (char *)NULL;
+ return (yy_readline_get ());
+ }
+ else
+ {
+ c = current_readline_line[current_readline_line_index++];
+ return (c);
+ }
+}
+
+static int
+yy_readline_unget (c)
+ int c;
+{
+ if (current_readline_line_index && current_readline_line)
+ current_readline_line[--current_readline_line_index] = c;
+ return (c);
+}
+
+void
+with_input_from_stdin ()
+{
+ INPUT_STREAM location;
+
+ if (bash_input.type != st_stdin && stream_on_stack (st_stdin) == 0)
+ {
+ location.string = current_readline_line;
+ init_yy_io (yy_readline_get, yy_readline_unget,
+ st_stdin, "readline stdin", location);
+ }
+}
+
+#else /* !READLINE */
+
+void
+with_input_from_stdin ()
+{
+ with_input_from_stream (stdin, "stdin");
+}
+#endif /* !READLINE */
+
+/* **************************************************************** */
+/* */
+/* Let input come from STRING. STRING is zero terminated. */
+/* */
+/* **************************************************************** */
+
+static int
+yy_string_get ()
+{
+ register char *string;
+ register unsigned char c;
+
+ string = bash_input.location.string;
+
+ /* If the string doesn't exist, or is empty, EOF found. */
+ if (string && *string)
+ {
+ c = *string++;
+ bash_input.location.string = string;
+ return (c);
+ }
+ else
+ return (EOF);
+}
+
+static int
+yy_string_unget (c)
+ int c;
+{
+ *(--bash_input.location.string) = c;
+ return (c);
+}
+
+void
+with_input_from_string (string, name)
+ char *string;
+ const char *name;
+{
+ INPUT_STREAM location;
+
+ location.string = string;
+ init_yy_io (yy_string_get, yy_string_unget, st_string, name, location);
+}
+
+/* Count the number of characters we've consumed from bash_input.location.string
+ and read into shell_input_line, but have not returned from shell_getc.
+ That is the true input location. Rewind bash_input.location.string by
+ that number of characters, so it points to the last character actually
+ consumed by the parser. */
+static void
+rewind_input_string ()
+{
+ int xchars;
+
+ /* number of unconsumed characters in the input -- XXX need to take newlines
+ into account, e.g., $(...\n) */
+ xchars = shell_input_line_len - shell_input_line_index;
+ if (bash_input.location.string[-1] == '\n')
+ xchars++;
+
+ /* XXX - how to reflect bash_input.location.string back to string passed to
+ parse_and_execute or xparse_dolparen? xparse_dolparen needs to know how
+ far into the string we parsed. parse_and_execute knows where bash_input.
+ location.string is, and how far from orig_string that is -- that's the
+ number of characters the command consumed. */
+
+ /* bash_input.location.string - xchars should be where we parsed to */
+ /* need to do more validation on xchars value for sanity -- test cases. */
+ bash_input.location.string -= xchars;
+}
+
+/* **************************************************************** */
+/* */
+/* Let input come from STREAM. */
+/* */
+/* **************************************************************** */
+
+/* These two functions used to test the value of the HAVE_RESTARTABLE_SYSCALLS
+ define, and just use getc/ungetc if it was defined, but since bash
+ installs its signal handlers without the SA_RESTART flag, some signals
+ (like SIGCHLD, SIGWINCH, etc.) received during a read(2) will not cause
+ the read to be restarted. We need to restart it ourselves. */
+
+static int
+yy_stream_get ()
+{
+ int result;
+
+ result = EOF;
+ if (bash_input.location.file)
+ {
+ if (interactive)
+ {
+ interrupt_immediately++;
+ terminate_immediately++;
+ }
+ result = getc_with_restart (bash_input.location.file);
+ if (interactive)
+ {
+ interrupt_immediately--;
+ terminate_immediately--;
+ }
+ }
+ return (result);
+}
+
+static int
+yy_stream_unget (c)
+ int c;
+{
+ return (ungetc_with_restart (c, bash_input.location.file));
+}
+
+void
+with_input_from_stream (stream, name)
+ FILE *stream;
+ const char *name;
+{
+ INPUT_STREAM location;
+
+ location.file = stream;
+ init_yy_io (yy_stream_get, yy_stream_unget, st_stream, name, location);
+}
+
+typedef struct stream_saver {
+ struct stream_saver *next;
+ BASH_INPUT bash_input;
+ int line;
+#if defined (BUFFERED_INPUT)
+ BUFFERED_STREAM *bstream;
+#endif /* BUFFERED_INPUT */
+} STREAM_SAVER;
+
+/* The globally known line number. */
+int line_number = 0;
+
+#if defined (COND_COMMAND)
+static int cond_lineno;
+static int cond_token;
+#endif
+
+STREAM_SAVER *stream_list = (STREAM_SAVER *)NULL;
+
+void
+push_stream (reset_lineno)
+ int reset_lineno;
+{
+ STREAM_SAVER *saver = (STREAM_SAVER *)xmalloc (sizeof (STREAM_SAVER));
+
+ xbcopy ((char *)&bash_input, (char *)&(saver->bash_input), sizeof (BASH_INPUT));
+
+#if defined (BUFFERED_INPUT)
+ saver->bstream = (BUFFERED_STREAM *)NULL;
+ /* If we have a buffered stream, clear out buffers[fd]. */
+ if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0)
+ saver->bstream = set_buffered_stream (bash_input.location.buffered_fd,
+ (BUFFERED_STREAM *)NULL);
+#endif /* BUFFERED_INPUT */
+
+ saver->line = line_number;
+ bash_input.name = (char *)NULL;
+ saver->next = stream_list;
+ stream_list = saver;
+ EOF_Reached = 0;
+ if (reset_lineno)
+ line_number = 0;
+}
+
+void
+pop_stream ()
+{
+ if (!stream_list)
+ EOF_Reached = 1;
+ else
+ {
+ STREAM_SAVER *saver = stream_list;
+
+ EOF_Reached = 0;
+ stream_list = stream_list->next;
+
+ init_yy_io (saver->bash_input.getter,
+ saver->bash_input.ungetter,
+ saver->bash_input.type,
+ saver->bash_input.name,
+ saver->bash_input.location);
+
+#if defined (BUFFERED_INPUT)
+ /* If we have a buffered stream, restore buffers[fd]. */
+ /* If the input file descriptor was changed while this was on the
+ save stack, update the buffered fd to the new file descriptor and
+ re-establish the buffer <-> bash_input fd correspondence. */
+ if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0)
+ {
+ if (bash_input_fd_changed)
+ {
+ bash_input_fd_changed = 0;
+ if (default_buffered_input >= 0)
+ {
+ bash_input.location.buffered_fd = default_buffered_input;
+ saver->bstream->b_fd = default_buffered_input;
+ SET_CLOSE_ON_EXEC (default_buffered_input);
+ }
+ }
+ /* XXX could free buffered stream returned as result here. */
+ set_buffered_stream (bash_input.location.buffered_fd, saver->bstream);
+ }
+#endif /* BUFFERED_INPUT */
+
+ line_number = saver->line;
+
+ FREE (saver->bash_input.name);
+ free (saver);
+ }
+}
+
+/* Return 1 if a stream of type TYPE is saved on the stack. */
+int
+stream_on_stack (type)
+ enum stream_type type;
+{
+ register STREAM_SAVER *s;
+
+ for (s = stream_list; s; s = s->next)
+ if (s->bash_input.type == type)
+ return 1;
+ return 0;
+}
+
+/* Save the current token state and return it in a malloced array. */
+int *
+save_token_state ()
+{
+ int *ret;
+
+ ret = (int *)xmalloc (4 * sizeof (int));
+ ret[0] = last_read_token;
+ ret[1] = token_before_that;
+ ret[2] = two_tokens_ago;
+ ret[3] = current_token;
+ return ret;
+}
+
+void
+restore_token_state (ts)
+ int *ts;
+{
+ if (ts == 0)
+ return;
+ last_read_token = ts[0];
+ token_before_that = ts[1];
+ two_tokens_ago = ts[2];
+ current_token = ts[3];
+}
+
+/*
+ * This is used to inhibit alias expansion and reserved word recognition
+ * inside case statement pattern lists. A `case statement pattern list' is:
+ *
+ * everything between the `in' in a `case word in' and the next ')'
+ * or `esac'
+ * everything between a `;;' and the next `)' or `esac'
+ */
+
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+
+#define END_OF_ALIAS 0
+
+/*
+ * Pseudo-global variables used in implementing token-wise alias expansion.
+ */
+
+/*
+ * Pushing and popping strings. This works together with shell_getc to
+ * implement alias expansion on a per-token basis.
+ */
+
+typedef struct string_saver {
+ struct string_saver *next;
+ int expand_alias; /* Value to set expand_alias to when string is popped. */
+ char *saved_line;
+#if defined (ALIAS)
+ alias_t *expander; /* alias that caused this line to be pushed. */
+#endif
+ int saved_line_size, saved_line_index, saved_line_terminator;
+} STRING_SAVER;
+
+STRING_SAVER *pushed_string_list = (STRING_SAVER *)NULL;
+
+/*
+ * Push the current shell_input_line onto a stack of such lines and make S
+ * the current input. Used when expanding aliases. EXPAND is used to set
+ * the value of expand_next_token when the string is popped, so that the
+ * word after the alias in the original line is handled correctly when the
+ * alias expands to multiple words. TOKEN is the token that was expanded
+ * into S; it is saved and used to prevent infinite recursive expansion.
+ */
+static void
+push_string (s, expand, ap)
+ char *s;
+ int expand;
+ alias_t *ap;
+{
+ STRING_SAVER *temp = (STRING_SAVER *)xmalloc (sizeof (STRING_SAVER));
+
+ temp->expand_alias = expand;
+ temp->saved_line = shell_input_line;
+ temp->saved_line_size = shell_input_line_size;
+ temp->saved_line_index = shell_input_line_index;
+ temp->saved_line_terminator = shell_input_line_terminator;
+#if defined (ALIAS)
+ temp->expander = ap;
+#endif
+ temp->next = pushed_string_list;
+ pushed_string_list = temp;
+
+#if defined (ALIAS)
+ if (ap)
+ ap->flags |= AL_BEINGEXPANDED;
+#endif
+
+ shell_input_line = s;
+ shell_input_line_size = strlen (s);
+ shell_input_line_index = 0;
+ shell_input_line_terminator = '\0';
+#if 0
+ parser_state &= ~PST_ALEXPNEXT; /* XXX */
+#endif
+
+ set_line_mbstate ();
+}
+
+/*
+ * Make the top of the pushed_string stack be the current shell input.
+ * Only called when there is something on the stack. Called from shell_getc
+ * when it thinks it has consumed the string generated by an alias expansion
+ * and needs to return to the original input line.
+ */
+static void
+pop_string ()
+{
+ STRING_SAVER *t;
+
+ FREE (shell_input_line);
+ shell_input_line = pushed_string_list->saved_line;
+ shell_input_line_index = pushed_string_list->saved_line_index;
+ shell_input_line_size = pushed_string_list->saved_line_size;
+ shell_input_line_terminator = pushed_string_list->saved_line_terminator;
+
+ if (pushed_string_list->expand_alias)
+ parser_state |= PST_ALEXPNEXT;
+ else
+ parser_state &= ~PST_ALEXPNEXT;
+
+ t = pushed_string_list;
+ pushed_string_list = pushed_string_list->next;
+
+#if defined (ALIAS)
+ if (t->expander)
+ t->expander->flags &= ~AL_BEINGEXPANDED;
+#endif
+
+ free ((char *)t);
+
+ set_line_mbstate ();
+}
+
+static void
+free_string_list ()
+{
+ register STRING_SAVER *t, *t1;
+
+ for (t = pushed_string_list; t; )
+ {
+ t1 = t->next;
+ FREE (t->saved_line);
+#if defined (ALIAS)
+ if (t->expander)
+ t->expander->flags &= ~AL_BEINGEXPANDED;
+#endif
+ free ((char *)t);
+ t = t1;
+ }
+ pushed_string_list = (STRING_SAVER *)NULL;
+}
+
+#endif /* ALIAS || DPAREN_ARITHMETIC */
+
+void
+free_pushed_string_input ()
+{
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ free_string_list ();
+#endif
+}
+
+/* Return a line of text, taken from wherever yylex () reads input.
+ If there is no more input, then we return NULL. If REMOVE_QUOTED_NEWLINE
+ is non-zero, we remove unquoted \<newline> pairs. This is used by
+ read_secondary_line to read here documents. */
+static char *
+read_a_line (remove_quoted_newline)
+ int remove_quoted_newline;
+{
+ static char *line_buffer = (char *)NULL;
+ static int buffer_size = 0;
+ int indx, c, peekc, pass_next;
+
+#if defined (READLINE)
+ if (no_line_editing && SHOULD_PROMPT ())
+#else
+ if (SHOULD_PROMPT ())
+#endif
+ print_prompt ();
+
+ pass_next = indx = 0;
+ while (1)
+ {
+ /* Allow immediate exit if interrupted during input. */
+ QUIT;
+
+ c = yy_getc ();
+
+ /* Ignore null bytes in input. */
+ if (c == 0)
+ {
+#if 0
+ internal_warning ("read_a_line: ignored null byte in input");
+#endif
+ continue;
+ }
+
+ /* If there is no more input, then we return NULL. */
+ if (c == EOF)
+ {
+ if (interactive && bash_input.type == st_stream)
+ clearerr (stdin);
+ if (indx == 0)
+ return ((char *)NULL);
+ c = '\n';
+ }
+
+ /* `+2' in case the final character in the buffer is a newline. */
+ RESIZE_MALLOCED_BUFFER (line_buffer, indx, 2, buffer_size, 128);
+
+ /* IF REMOVE_QUOTED_NEWLINES is non-zero, we are reading a
+ here document with an unquoted delimiter. In this case,
+ the line will be expanded as if it were in double quotes.
+ We allow a backslash to escape the next character, but we
+ need to treat the backslash specially only if a backslash
+ quoting a backslash-newline pair appears in the line. */
+ if (pass_next)
+ {
+ line_buffer[indx++] = c;
+ pass_next = 0;
+ }
+ else if (c == '\\' && remove_quoted_newline)
+ {
+ QUIT;
+ peekc = yy_getc ();
+ if (peekc == '\n')
+ {
+ line_number++;
+ continue; /* Make the unquoted \<newline> pair disappear. */
+ }
+ else
+ {
+ yy_ungetc (peekc);
+ pass_next = 1;
+ line_buffer[indx++] = c; /* Preserve the backslash. */
+ }
+ }
+ else
+ line_buffer[indx++] = c;
+
+ if (c == '\n')
+ {
+ line_buffer[indx] = '\0';
+ return (line_buffer);
+ }
+ }
+}
+
+/* Return a line as in read_a_line (), but insure that the prompt is
+ the secondary prompt. This is used to read the lines of a here
+ document. REMOVE_QUOTED_NEWLINE is non-zero if we should remove
+ newlines quoted with backslashes while reading the line. It is
+ non-zero unless the delimiter of the here document was quoted. */
+char *
+read_secondary_line (remove_quoted_newline)
+ int remove_quoted_newline;
+{
+ char *ret;
+ int n, c;
+
+ prompt_string_pointer = &ps2_prompt;
+ if (SHOULD_PROMPT())
+ prompt_again ();
+ ret = read_a_line (remove_quoted_newline);
+#if defined (HISTORY)
+ if (ret && remember_on_history && (parser_state & PST_HEREDOC))
+ {
+ /* To make adding the the here-document body right, we need to rely
+ on history_delimiting_chars() returning \n for the first line of
+ the here-document body and the null string for the second and
+ subsequent lines, so we avoid double newlines.
+ current_command_line_count == 2 for the first line of the body. */
+
+ current_command_line_count++;
+ maybe_add_history (ret);
+ }
+#endif /* HISTORY */
+ return ret;
+}
+
+/* **************************************************************** */
+/* */
+/* YYLEX () */
+/* */
+/* **************************************************************** */
+
+/* Reserved words. These are only recognized as the first word of a
+ command. */
+STRING_INT_ALIST word_token_alist[] = {
+ { "if", IF },
+ { "then", THEN },
+ { "else", ELSE },
+ { "elif", ELIF },
+ { "fi", FI },
+ { "case", CASE },
+ { "esac", ESAC },
+ { "for", FOR },
+#if defined (SELECT_COMMAND)
+ { "select", SELECT },
+#endif
+ { "while", WHILE },
+ { "until", UNTIL },
+ { "do", DO },
+ { "done", DONE },
+ { "in", IN },
+ { "function", FUNCTION },
+#if defined (COMMAND_TIMING)
+ { "time", TIME },
+#endif
+ { "{", '{' },
+ { "}", '}' },
+ { "!", BANG },
+#if defined (COND_COMMAND)
+ { "[[", COND_START },
+ { "]]", COND_END },
+#endif
+#if defined (COPROCESS_SUPPORT)
+ { "coproc", COPROC },
+#endif
+ { (char *)NULL, 0}
+};
+
+/* other tokens that can be returned by read_token() */
+STRING_INT_ALIST other_token_alist[] = {
+ /* Multiple-character tokens with special values */
+ { "-p", TIMEOPT },
+ { "&&", AND_AND },
+ { "||", OR_OR },
+ { ">>", GREATER_GREATER },
+ { "<<", LESS_LESS },
+ { "<&", LESS_AND },
+ { ">&", GREATER_AND },
+ { ";;", SEMI_SEMI },
+ { ";&", SEMI_AND },
+ { ";;&", SEMI_SEMI_AND },
+ { "<<-", LESS_LESS_MINUS },
+ { "<<<", LESS_LESS_LESS },
+ { "&>", AND_GREATER },
+ { "&>>", AND_GREATER_GREATER },
+ { "<>", LESS_GREATER },
+ { ">|", GREATER_BAR },
+ { "|&", BAR_AND },
+ { "EOF", yacc_EOF },
+ /* Tokens whose value is the character itself */
+ { ">", '>' },
+ { "<", '<' },
+ { "-", '-' },
+ { "{", '{' },
+ { "}", '}' },
+ { ";", ';' },
+ { "(", '(' },
+ { ")", ')' },
+ { "|", '|' },
+ { "&", '&' },
+ { "newline", '\n' },
+ { (char *)NULL, 0}
+};
+
+/* others not listed here:
+ WORD look at yylval.word
+ ASSIGNMENT_WORD look at yylval.word
+ NUMBER look at yylval.number
+ ARITH_CMD look at yylval.word_list
+ ARITH_FOR_EXPRS look at yylval.word_list
+ COND_CMD look at yylval.command
+*/
+
+/* These are used by read_token_word, but appear up here so that shell_getc
+ can use them to decide when to add otherwise blank lines to the history. */
+
+/* The primary delimiter stack. */
+struct dstack dstack = { (char *)NULL, 0, 0 };
+
+/* A temporary delimiter stack to be used when decoding prompt strings.
+ This is needed because command substitutions in prompt strings (e.g., PS2)
+ can screw up the parser's quoting state. */
+static struct dstack temp_dstack = { (char *)NULL, 0, 0 };
+
+/* Macro for accessing the top delimiter on the stack. Returns the
+ delimiter or zero if none. */
+#define current_delimiter(ds) \
+ (ds.delimiter_depth ? ds.delimiters[ds.delimiter_depth - 1] : 0)
+
+#define push_delimiter(ds, character) \
+ do \
+ { \
+ if (ds.delimiter_depth + 2 > ds.delimiter_space) \
+ ds.delimiters = (char *)xrealloc \
+ (ds.delimiters, (ds.delimiter_space += 10) * sizeof (char)); \
+ ds.delimiters[ds.delimiter_depth] = character; \
+ ds.delimiter_depth++; \
+ } \
+ while (0)
+
+#define pop_delimiter(ds) ds.delimiter_depth--
+
+/* Return the next shell input character. This always reads characters
+ from shell_input_line; when that line is exhausted, it is time to
+ read the next line. This is called by read_token when the shell is
+ processing normal command input. */
+
+/* This implements one-character lookahead/lookbehind across physical input
+ lines, to avoid something being lost because it's pushed back with
+ shell_ungetc when we're at the start of a line. */
+static int eol_ungetc_lookahead = 0;
+
+static int
+shell_getc (remove_quoted_newline)
+ int remove_quoted_newline;
+{
+ register int i;
+ int c;
+ unsigned char uc;
+
+ QUIT;
+
+ if (sigwinch_received)
+ {
+ sigwinch_received = 0;
+ get_new_window_size (0, (int *)0, (int *)0);
+ }
+
+ if (eol_ungetc_lookahead)
+ {
+ c = eol_ungetc_lookahead;
+ eol_ungetc_lookahead = 0;
+ return (c);
+ }
+
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ /* If shell_input_line[shell_input_line_index] == 0, but there is
+ something on the pushed list of strings, then we don't want to go
+ off and get another line. We let the code down below handle it. */
+
+ if (!shell_input_line || ((!shell_input_line[shell_input_line_index]) &&
+ (pushed_string_list == (STRING_SAVER *)NULL)))
+#else /* !ALIAS && !DPAREN_ARITHMETIC */
+ if (!shell_input_line || !shell_input_line[shell_input_line_index])
+#endif /* !ALIAS && !DPAREN_ARITHMETIC */
+ {
+ line_number++;
+
+ restart_read:
+
+ /* Allow immediate exit if interrupted during input. */
+ QUIT;
+
+ i = 0;
+ shell_input_line_terminator = 0;
+
+ /* If the shell is interatctive, but not currently printing a prompt
+ (interactive_shell && interactive == 0), we don't want to print
+ notifies or cleanup the jobs -- we want to defer it until we do
+ print the next prompt. */
+ if (interactive_shell == 0 || SHOULD_PROMPT())
+ {
+#if defined (JOB_CONTROL)
+ /* This can cause a problem when reading a command as the result
+ of a trap, when the trap is called from flush_child. This call
+ had better not cause jobs to disappear from the job table in
+ that case, or we will have big trouble. */
+ notify_and_cleanup ();
+#else /* !JOB_CONTROL */
+ cleanup_dead_jobs ();
+#endif /* !JOB_CONTROL */
+ }
+
+#if defined (READLINE)
+ if (no_line_editing && SHOULD_PROMPT())
+#else
+ if (SHOULD_PROMPT())
+#endif
+ print_prompt ();
+
+ if (bash_input.type == st_stream)
+ clearerr (stdin);
+
+ while (1)
+ {
+ c = yy_getc ();
+
+ /* Allow immediate exit if interrupted during input. */
+ QUIT;
+
+ if (c == '\0')
+ {
+#if 0
+ internal_warning ("shell_getc: ignored null byte in input");
+#endif
+ continue;
+ }
+
+ RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);
+
+ if (c == EOF)
+ {
+ if (bash_input.type == st_stream)
+ clearerr (stdin);
+
+ if (i == 0)
+ shell_input_line_terminator = EOF;
+
+ shell_input_line[i] = '\0';
+ break;
+ }
+
+ shell_input_line[i++] = c;
+
+ if (c == '\n')
+ {
+ shell_input_line[--i] = '\0';
+ current_command_line_count++;
+ break;
+ }
+ }
+
+ shell_input_line_index = 0;
+ shell_input_line_len = i; /* == strlen (shell_input_line) */
+
+ set_line_mbstate ();
+
+#if defined (HISTORY)
+ if (remember_on_history && shell_input_line && shell_input_line[0])
+ {
+ char *expansions;
+# if defined (BANG_HISTORY)
+ int old_hist;
+
+ /* If the current delimiter is a single quote, we should not be
+ performing history expansion, even if we're on a different
+ line from the original single quote. */
+ old_hist = history_expansion_inhibited;
+ if (current_delimiter (dstack) == '\'')
+ history_expansion_inhibited = 1;
+# endif
+ expansions = pre_process_line (shell_input_line, 1, 1);
+# if defined (BANG_HISTORY)
+ history_expansion_inhibited = old_hist;
+# endif
+ if (expansions != shell_input_line)
+ {
+ free (shell_input_line);
+ shell_input_line = expansions;
+ shell_input_line_len = shell_input_line ?
+ strlen (shell_input_line) : 0;
+ if (shell_input_line_len == 0)
+ current_command_line_count--;
+
+ /* We have to force the xrealloc below because we don't know
+ the true allocated size of shell_input_line anymore. */
+ shell_input_line_size = shell_input_line_len;
+
+ set_line_mbstate ();
+ }
+ }
+ /* Try to do something intelligent with blank lines encountered while
+ entering multi-line commands. XXX - this is grotesque */
+ else if (remember_on_history && shell_input_line &&
+ shell_input_line[0] == '\0' &&
+ current_command_line_count > 1)
+ {
+ if (current_delimiter (dstack))
+ /* We know shell_input_line[0] == 0 and we're reading some sort of
+ quoted string. This means we've got a line consisting of only
+ a newline in a quoted string. We want to make sure this line
+ gets added to the history. */
+ maybe_add_history (shell_input_line);
+ else
+ {
+ char *hdcs;
+ hdcs = history_delimiting_chars ();
+ if (hdcs && hdcs[0] == ';')
+ maybe_add_history (shell_input_line);
+ }
+ }
+
+#endif /* HISTORY */
+
+ if (shell_input_line)
+ {
+ /* Lines that signify the end of the shell's input should not be
+ echoed. */
+ if (echo_input_at_read && (shell_input_line[0] ||
+ shell_input_line_terminator != EOF))
+ fprintf (stderr, "%s\n", shell_input_line);
+ }
+ else
+ {
+ shell_input_line_size = 0;
+ prompt_string_pointer = &current_prompt_string;
+ if (SHOULD_PROMPT ())
+ prompt_again ();
+ goto restart_read;
+ }
+
+ /* Add the newline to the end of this string, iff the string does
+ not already end in an EOF character. */
+ if (shell_input_line_terminator != EOF)
+ {
+ if (shell_input_line_len + 3 > shell_input_line_size)
+ shell_input_line = (char *)xrealloc (shell_input_line,
+ 1 + (shell_input_line_size += 2));
+
+ shell_input_line[shell_input_line_len] = '\n';
+ shell_input_line[shell_input_line_len + 1] = '\0';
+
+ set_line_mbstate ();
+ }
+ }
+
+next_alias_char:
+ uc = shell_input_line[shell_input_line_index];
+
+ if (uc)
+ shell_input_line_index++;
+
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ /* If UC is NULL, we have reached the end of the current input string. If
+ pushed_string_list is non-empty, it's time to pop to the previous string
+ because we have fully consumed the result of the last alias expansion.
+ Do it transparently; just return the next character of the string popped
+ to. */
+pop_alias:
+ if (uc == 0 && (pushed_string_list != (STRING_SAVER *)NULL))
+ {
+ pop_string ();
+ uc = shell_input_line[shell_input_line_index];
+ if (uc)
+ shell_input_line_index++;
+ }
+#endif /* ALIAS || DPAREN_ARITHMETIC */
+
+ if MBTEST(uc == '\\' && remove_quoted_newline && shell_input_line[shell_input_line_index] == '\n')
+ {
+ if (SHOULD_PROMPT ())
+ prompt_again ();
+ line_number++;
+ /* What do we do here if we're expanding an alias whose definition
+ includes an escaped newline? If that's the last character in the
+ alias expansion, we just pop the pushed string list (recall that
+ we inhibit the appending of a space in mk_alexpansion() if newline
+ is the last character). If it's not the last character, we need
+ to consume the quoted newline and move to the next character in
+ the expansion. */
+ if (expanding_alias () && shell_input_line[shell_input_line_index+1] == '\0')
+ {
+ uc = 0;
+ goto pop_alias;
+ }
+ else if (expanding_alias () && shell_input_line[shell_input_line_index+1] != '\0')
+ {
+ shell_input_line_index++; /* skip newline */
+ goto next_alias_char; /* and get next character */
+ }
+ else
+ goto restart_read;
+ }
+
+ if (uc == 0 && shell_input_line_terminator == EOF)
+ return ((shell_input_line_index != 0) ? '\n' : EOF);
+
+ return (uc);
+}
+
+/* Put C back into the input for the shell. This might need changes for
+ HANDLE_MULTIBYTE around EOLs. Since we (currently) never push back a
+ character different than we read, shell_input_line_property doesn't need
+ to change when manipulating shell_input_line. The define for
+ last_shell_getc_is_singlebyte should take care of it, though. */
+static void
+shell_ungetc (c)
+ int c;
+{
+ if (shell_input_line && shell_input_line_index)
+ shell_input_line[--shell_input_line_index] = c;
+ else
+ eol_ungetc_lookahead = c;
+}
+
+#ifdef INCLUDE_UNUSED
+/* Back the input pointer up by one, effectively `ungetting' a character. */
+static void
+shell_ungetchar ()
+{
+ if (shell_input_line && shell_input_line_index)
+ shell_input_line_index--;
+}
+#endif
+
+/* Discard input until CHARACTER is seen, then push that character back
+ onto the input stream. */
+static void
+discard_until (character)
+ int character;
+{
+ int c;
+
+ while ((c = shell_getc (0)) != EOF && c != character)
+ ;
+
+ if (c != EOF)
+ shell_ungetc (c);
+}
+
+void
+execute_variable_command (command, vname)
+ char *command, *vname;
+{
+ char *last_lastarg;
+ sh_parser_state_t ps;
+
+ save_parser_state (&ps);
+ last_lastarg = get_string_value ("_");
+ if (last_lastarg)
+ last_lastarg = savestring (last_lastarg);
+
+ parse_and_execute (savestring (command), vname, SEVAL_NONINT|SEVAL_NOHIST);
+
+ restore_parser_state (&ps);
+ bind_variable ("_", last_lastarg, 0);
+ FREE (last_lastarg);
+
+ if (token_to_read == '\n') /* reset_parser was called */
+ token_to_read = 0;
+}
+
+/* Place to remember the token. We try to keep the buffer
+ at a reasonable size, but it can grow. */
+static char *token = (char *)NULL;
+
+/* Current size of the token buffer. */
+static int token_buffer_size;
+
+/* Command to read_token () explaining what we want it to do. */
+#define READ 0
+#define RESET 1
+#define prompt_is_ps1 \
+ (!prompt_string_pointer || prompt_string_pointer == &ps1_prompt)
+
+/* Function for yyparse to call. yylex keeps track of
+ the last two tokens read, and calls read_token. */
+static int
+yylex ()
+{
+ if (interactive && (current_token == 0 || current_token == '\n'))
+ {
+ /* Before we print a prompt, we might have to check mailboxes.
+ We do this only if it is time to do so. Notice that only here
+ is the mail alarm reset; nothing takes place in check_mail ()
+ except the checking of mail. Please don't change this. */
+ if (prompt_is_ps1 && time_to_check_mail ())
+ {
+ check_mail ();
+ reset_mail_timer ();
+ }
+
+ /* Avoid printing a prompt if we're not going to read anything, e.g.
+ after resetting the parser with read_token (RESET). */
+ if (token_to_read == 0 && SHOULD_PROMPT ())
+ prompt_again ();
+ }
+
+ two_tokens_ago = token_before_that;
+ token_before_that = last_read_token;
+ last_read_token = current_token;
+ current_token = read_token (READ);
+
+ if ((parser_state & PST_EOFTOKEN) && current_token == shell_eof_token)
+ {
+ current_token = yacc_EOF;
+ if (bash_input.type == st_string)
+ rewind_input_string ();
+ }
+ parser_state &= ~PST_EOFTOKEN;
+
+ return (current_token);
+}
+
+/* When non-zero, we have read the required tokens
+ which allow ESAC to be the next one read. */
+static int esacs_needed_count;
+
+void
+gather_here_documents ()
+{
+ int r;
+
+ r = 0;
+ while (need_here_doc)
+ {
+ parser_state |= PST_HEREDOC;
+ make_here_document (redir_stack[r++], line_number);
+ parser_state &= ~PST_HEREDOC;
+ need_here_doc--;
+ }
+}
+
+/* When non-zero, an open-brace used to create a group is awaiting a close
+ brace partner. */
+static int open_brace_count;
+
+#define command_token_position(token) \
+ (((token) == ASSIGNMENT_WORD) || (parser_state&PST_REDIRLIST) || \
+ ((token) != SEMI_SEMI && (token) != SEMI_AND && (token) != SEMI_SEMI_AND && reserved_word_acceptable(token)))
+
+#define assignment_acceptable(token) \
+ (command_token_position(token) && ((parser_state & PST_CASEPAT) == 0))
+
+/* Check to see if TOKEN is a reserved word and return the token
+ value if it is. */
+#define CHECK_FOR_RESERVED_WORD(tok) \
+ do { \
+ if (!dollar_present && !quoted && \
+ reserved_word_acceptable (last_read_token)) \
+ { \
+ int i; \
+ for (i = 0; word_token_alist[i].word != (char *)NULL; i++) \
+ if (STREQ (tok, word_token_alist[i].word)) \
+ { \
+ if ((parser_state & PST_CASEPAT) && (word_token_alist[i].token != ESAC)) \
+ break; \
+ if (word_token_alist[i].token == TIME && time_command_acceptable () == 0) \
+ break; \
+ if (word_token_alist[i].token == ESAC) \
+ parser_state &= ~(PST_CASEPAT|PST_CASESTMT); \
+ else if (word_token_alist[i].token == CASE) \
+ parser_state |= PST_CASESTMT; \
+ else if (word_token_alist[i].token == COND_END) \
+ parser_state &= ~(PST_CONDCMD|PST_CONDEXPR); \
+ else if (word_token_alist[i].token == COND_START) \
+ parser_state |= PST_CONDCMD; \
+ else if (word_token_alist[i].token == '{') \
+ open_brace_count++; \
+ else if (word_token_alist[i].token == '}' && open_brace_count) \
+ open_brace_count--; \
+ return (word_token_alist[i].token); \
+ } \
+ } \
+ } while (0)
+
+#if defined (ALIAS)
+
+ /* OK, we have a token. Let's try to alias expand it, if (and only if)
+ it's eligible.
+
+ It is eligible for expansion if EXPAND_ALIASES is set, and
+ the token is unquoted and the last token read was a command
+ separator (or expand_next_token is set), and we are currently
+ processing an alias (pushed_string_list is non-empty) and this
+ token is not the same as the current or any previously
+ processed alias.
+
+ Special cases that disqualify:
+ In a pattern list in a case statement (parser_state & PST_CASEPAT). */
+
+static char *
+mk_alexpansion (s)
+ char *s;
+{
+ int l;
+ char *r;
+
+ l = strlen (s);
+ r = xmalloc (l + 2);
+ strcpy (r, s);
+ /* If the last character in the alias is a newline, don't add a trailing
+ space to the expansion. Works with shell_getc above. */
+ if (r[l - 1] != ' ' && r[l - 1] != '\n')
+ r[l++] = ' ';
+ r[l] = '\0';
+ return r;
+}
+
+static int
+alias_expand_token (tokstr)
+ char *tokstr;
+{
+ char *expanded;
+ alias_t *ap;
+
+ if (((parser_state & PST_ALEXPNEXT) || command_token_position (last_read_token)) &&
+ (parser_state & PST_CASEPAT) == 0)
+ {
+ ap = find_alias (tokstr);
+
+ /* Currently expanding this token. */
+ if (ap && (ap->flags & AL_BEINGEXPANDED))
+ return (NO_EXPANSION);
+
+ /* mk_alexpansion puts an extra space on the end of the alias expansion,
+ so the lookahead by the parser works right. If this gets changed,
+ make sure the code in shell_getc that deals with reaching the end of
+ an expanded alias is changed with it. */
+ expanded = ap ? mk_alexpansion (ap->value) : (char *)NULL;
+
+ if (expanded)
+ {
+ push_string (expanded, ap->flags & AL_EXPANDNEXT, ap);
+ return (RE_READ_TOKEN);
+ }
+ else
+ /* This is an eligible token that does not have an expansion. */
+ return (NO_EXPANSION);
+ }
+ return (NO_EXPANSION);
+}
+#endif /* ALIAS */
+
+static int
+time_command_acceptable ()
+{
+#if defined (COMMAND_TIMING)
+ switch (last_read_token)
+ {
+ case 0:
+ case ';':
+ case '\n':
+ case AND_AND:
+ case OR_OR:
+ case '&':
+ case DO:
+ case THEN:
+ case ELSE:
+ case '{': /* } */
+ case '(': /* ) */
+ return 1;
+ default:
+ return 0;
+ }
+#else
+ return 0;
+#endif /* COMMAND_TIMING */
+}
+
+/* Handle special cases of token recognition:
+ IN is recognized if the last token was WORD and the token
+ before that was FOR or CASE or SELECT.
+
+ DO is recognized if the last token was WORD and the token
+ before that was FOR or SELECT.
+
+ ESAC is recognized if the last token caused `esacs_needed_count'
+ to be set
+
+ `{' is recognized if the last token as WORD and the token
+ before that was FUNCTION, or if we just parsed an arithmetic
+ `for' command.
+
+ `}' is recognized if there is an unclosed `{' present.
+
+ `-p' is returned as TIMEOPT if the last read token was TIME.
+
+ ']]' is returned as COND_END if the parser is currently parsing
+ a conditional expression ((parser_state & PST_CONDEXPR) != 0)
+
+ `time' is returned as TIME if and only if it is immediately
+ preceded by one of `;', `\n', `||', `&&', or `&'.
+*/
+
+static int
+special_case_tokens (tokstr)
+ char *tokstr;
+{
+ if ((last_read_token == WORD) &&
+#if defined (SELECT_COMMAND)
+ ((token_before_that == FOR) || (token_before_that == CASE) || (token_before_that == SELECT)) &&
+#else
+ ((token_before_that == FOR) || (token_before_that == CASE)) &&
+#endif
+ (tokstr[0] == 'i' && tokstr[1] == 'n' && tokstr[2] == 0))
+ {
+ if (token_before_that == CASE)
+ {
+ parser_state |= PST_CASEPAT;
+ esacs_needed_count++;
+ }
+ return (IN);
+ }
+
+ if (last_read_token == WORD &&
+#if defined (SELECT_COMMAND)
+ (token_before_that == FOR || token_before_that == SELECT) &&
+#else
+ (token_before_that == FOR) &&
+#endif
+ (tokstr[0] == 'd' && tokstr[1] == 'o' && tokstr[2] == '\0'))
+ return (DO);
+
+ /* Ditto for ESAC in the CASE case.
+ Specifically, this handles "case word in esac", which is a legal
+ construct, certainly because someone will pass an empty arg to the
+ case construct, and we don't want it to barf. Of course, we should
+ insist that the case construct has at least one pattern in it, but
+ the designers disagree. */
+ if (esacs_needed_count)
+ {
+ esacs_needed_count--;
+ if (STREQ (tokstr, "esac"))
+ {
+ parser_state &= ~PST_CASEPAT;
+ return (ESAC);
+ }
+ }
+
+ /* The start of a shell function definition. */
+ if (parser_state & PST_ALLOWOPNBRC)
+ {
+ parser_state &= ~PST_ALLOWOPNBRC;
+ if (tokstr[0] == '{' && tokstr[1] == '\0') /* } */
+ {
+ open_brace_count++;
+ function_bstart = line_number;
+ return ('{'); /* } */
+ }
+ }
+
+ /* We allow a `do' after a for ((...)) without an intervening
+ list_terminator */
+ if (last_read_token == ARITH_FOR_EXPRS && tokstr[0] == 'd' && tokstr[1] == 'o' && !tokstr[2])
+ return (DO);
+ if (last_read_token == ARITH_FOR_EXPRS && tokstr[0] == '{' && tokstr[1] == '\0') /* } */
+ {
+ open_brace_count++;
+ return ('{'); /* } */
+ }
+
+ if (open_brace_count && reserved_word_acceptable (last_read_token) && tokstr[0] == '}' && !tokstr[1])
+ {
+ open_brace_count--; /* { */
+ return ('}');
+ }
+
+#if defined (COMMAND_TIMING)
+ /* Handle -p after `time'. */
+ if (last_read_token == TIME && tokstr[0] == '-' && tokstr[1] == 'p' && !tokstr[2])
+ return (TIMEOPT);
+#endif
+
+#if 0
+#if defined (COMMAND_TIMING)
+ if (STREQ (token, "time") && ((parser_state & PST_CASEPAT) == 0) && time_command_acceptable ())
+ return (TIME);
+#endif /* COMMAND_TIMING */
+#endif
+
+#if defined (COND_COMMAND) /* [[ */
+ if ((parser_state & PST_CONDEXPR) && tokstr[0] == ']' && tokstr[1] == ']' && tokstr[2] == '\0')
+ return (COND_END);
+#endif
+
+ return (-1);
+}
+
+/* Called from shell.c when Control-C is typed at top level. Or
+ by the error rule at top level. */
+void
+reset_parser ()
+{
+ dstack.delimiter_depth = 0; /* No delimiters found so far. */
+ open_brace_count = 0;
+
+#if defined (EXTENDED_GLOB)
+ /* Reset to global value of extended glob */
+ if (parser_state & PST_EXTPAT)
+ extended_glob = global_extglob;
+#endif
+
+ parser_state = 0;
+
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ if (pushed_string_list)
+ free_string_list ();
+#endif /* ALIAS || DPAREN_ARITHMETIC */
+
+ if (shell_input_line)
+ {
+ free (shell_input_line);
+ shell_input_line = (char *)NULL;
+ shell_input_line_size = shell_input_line_index = 0;
+ }
+
+ FREE (word_desc_to_read);
+ word_desc_to_read = (WORD_DESC *)NULL;
+
+ current_token = '\n'; /* XXX */
+ last_read_token = '\n';
+ token_to_read = '\n';
+}
+
+/* Read the next token. Command can be READ (normal operation) or
+ RESET (to normalize state). */
+static int
+read_token (command)
+ int command;
+{
+ int character; /* Current character. */
+ int peek_char; /* Temporary look-ahead character. */
+ int result; /* The thing to return. */
+
+ if (command == RESET)
+ {
+ reset_parser ();
+ return ('\n');
+ }
+
+ if (token_to_read)
+ {
+ result = token_to_read;
+ if (token_to_read == WORD || token_to_read == ASSIGNMENT_WORD)
+ {
+ yylval.word = word_desc_to_read;
+ word_desc_to_read = (WORD_DESC *)NULL;
+ }
+ token_to_read = 0;
+ return (result);
+ }
+
+#if defined (COND_COMMAND)
+ if ((parser_state & (PST_CONDCMD|PST_CONDEXPR)) == PST_CONDCMD)
+ {
+ cond_lineno = line_number;
+ parser_state |= PST_CONDEXPR;
+ yylval.command = parse_cond_command ();
+ if (cond_token != COND_END)
+ {
+ cond_error ();
+ return (-1);
+ }
+ token_to_read = COND_END;
+ parser_state &= ~(PST_CONDEXPR|PST_CONDCMD);
+ return (COND_CMD);
+ }
+#endif
+
+#if defined (ALIAS)
+ /* This is a place to jump back to once we have successfully expanded a
+ token with an alias and pushed the string with push_string () */
+ re_read_token:
+#endif /* ALIAS */
+
+ /* Read a single word from input. Start by skipping blanks. */
+ while ((character = shell_getc (1)) != EOF && shellblank (character))
+ ;
+
+ if (character == EOF)
+ {
+ EOF_Reached = 1;
+ return (yacc_EOF);
+ }
+
+ if MBTEST(character == '#' && (!interactive || interactive_comments))
+ {
+ /* A comment. Discard until EOL or EOF, and then return a newline. */
+ discard_until ('\n');
+ shell_getc (0);
+ character = '\n'; /* this will take the next if statement and return. */
+ }
+
+ if (character == '\n')
+ {
+ /* If we're about to return an unquoted newline, we can go and collect
+ the text of any pending here document. */
+ if (need_here_doc)
+ gather_here_documents ();
+
+#if defined (ALIAS)
+ parser_state &= ~PST_ALEXPNEXT;
+#endif /* ALIAS */
+
+ parser_state &= ~PST_ASSIGNOK;
+
+ return (character);
+ }
+
+ if (parser_state & PST_REGEXP)
+ goto tokword;
+
+ /* Shell meta-characters. */
+ if MBTEST(shellmeta (character) && ((parser_state & PST_DBLPAREN) == 0))
+ {
+#if defined (ALIAS)
+ /* Turn off alias tokenization iff this character sequence would
+ not leave us ready to read a command. */
+ if (character == '<' || character == '>')
+ parser_state &= ~PST_ALEXPNEXT;
+#endif /* ALIAS */
+
+ parser_state &= ~PST_ASSIGNOK;
+
+ peek_char = shell_getc (1);
+ if (character == peek_char)
+ {
+ switch (character)
+ {
+ case '<':
+ /* If '<' then we could be at "<<" or at "<<-". We have to
+ look ahead one more character. */
+ peek_char = shell_getc (1);
+ if MBTEST(peek_char == '-')
+ return (LESS_LESS_MINUS);
+ else if MBTEST(peek_char == '<')
+ return (LESS_LESS_LESS);
+ else
+ {
+ shell_ungetc (peek_char);
+ return (LESS_LESS);
+ }
+
+ case '>':
+ return (GREATER_GREATER);
+
+ case ';':
+ parser_state |= PST_CASEPAT;
+#if defined (ALIAS)
+ parser_state &= ~PST_ALEXPNEXT;
+#endif /* ALIAS */
+
+ peek_char = shell_getc (1);
+ if MBTEST(peek_char == '&')
+ return (SEMI_SEMI_AND);
+ else
+ {
+ shell_ungetc (peek_char);
+ return (SEMI_SEMI);
+ }
+
+ case '&':
+ return (AND_AND);
+
+ case '|':
+ return (OR_OR);
+
+#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
+ case '(': /* ) */
+ result = parse_dparen (character);
+ if (result == -2)
+ break;
+ else
+ return result;
+#endif
+ }
+ }
+ else if MBTEST(character == '<' && peek_char == '&')
+ return (LESS_AND);
+ else if MBTEST(character == '>' && peek_char == '&')
+ return (GREATER_AND);
+ else if MBTEST(character == '<' && peek_char == '>')
+ return (LESS_GREATER);
+ else if MBTEST(character == '>' && peek_char == '|')
+ return (GREATER_BAR);
+ else if MBTEST(character == '&' && peek_char == '>')
+ {
+ peek_char = shell_getc (1);
+ if MBTEST(peek_char == '>')
+ return (AND_GREATER_GREATER);
+ else
+ {
+ shell_ungetc (peek_char);
+ return (AND_GREATER);
+ }
+ }
+ else if MBTEST(character == '|' && peek_char == '&')
+ return (BAR_AND);
+ else if MBTEST(character == ';' && peek_char == '&')
+ {
+ parser_state |= PST_CASEPAT;
+#if defined (ALIAS)
+ parser_state &= ~PST_ALEXPNEXT;
+#endif /* ALIAS */
+ return (SEMI_AND);
+ }
+
+ shell_ungetc (peek_char);
+
+ /* If we look like we are reading the start of a function
+ definition, then let the reader know about it so that
+ we will do the right thing with `{'. */
+ if MBTEST(character == ')' && last_read_token == '(' && token_before_that == WORD)
+ {
+ parser_state |= PST_ALLOWOPNBRC;
+#if defined (ALIAS)
+ parser_state &= ~PST_ALEXPNEXT;
+#endif /* ALIAS */
+ function_dstart = line_number;
+ }
+
+ /* case pattern lists may be preceded by an optional left paren. If
+ we're not trying to parse a case pattern list, the left paren
+ indicates a subshell. */
+ if MBTEST(character == '(' && (parser_state & PST_CASEPAT) == 0) /* ) */
+ parser_state |= PST_SUBSHELL;
+ /*(*/
+ else if MBTEST((parser_state & PST_CASEPAT) && character == ')')
+ parser_state &= ~PST_CASEPAT;
+ /*(*/
+ else if MBTEST((parser_state & PST_SUBSHELL) && character == ')')
+ parser_state &= ~PST_SUBSHELL;
+
+#if defined (PROCESS_SUBSTITUTION)
+ /* Check for the constructs which introduce process substitution.
+ Shells running in `posix mode' don't do process substitution. */
+ if MBTEST(posixly_correct || ((character != '>' && character != '<') || peek_char != '(')) /*)*/
+#endif /* PROCESS_SUBSTITUTION */
+ return (character);
+ }
+
+ /* Hack <&- (close stdin) case. Also <&N- (dup and close). */
+ if MBTEST(character == '-' && (last_read_token == LESS_AND || last_read_token == GREATER_AND))
+ return (character);
+
+tokword:
+ /* Okay, if we got this far, we have to read a word. Read one,
+ and then check it against the known ones. */
+ result = read_token_word (character);
+#if defined (ALIAS)
+ if (result == RE_READ_TOKEN)
+ goto re_read_token;
+#endif
+ return result;
+}
+
+/*
+ * Match a $(...) or other grouping construct. This has to handle embedded
+ * quoted strings ('', ``, "") and nested constructs. It also must handle
+ * reprompting the user, if necessary, after reading a newline, and returning
+ * correct error values if it reads EOF.
+ */
+#define P_FIRSTCLOSE 0x01
+#define P_ALLOWESC 0x02
+#define P_DQUOTE 0x04
+#define P_COMMAND 0x08 /* parsing a command, so look for comments */
+#define P_BACKQUOTE 0x10 /* parsing a backquoted command substitution */
+#define P_ARRAYSUB 0x20 /* parsing a [...] array subscript for assignment */
+
+/* Lexical state while parsing a grouping construct or $(...). */
+#define LEX_WASDOL 0x001
+#define LEX_CKCOMMENT 0x002
+#define LEX_INCOMMENT 0x004
+#define LEX_PASSNEXT 0x008
+#define LEX_RESWDOK 0x010
+#define LEX_CKCASE 0x020
+#define LEX_INCASE 0x040
+#define LEX_INHEREDOC 0x080
+#define LEX_HEREDELIM 0x100 /* reading here-doc delimiter */
+#define LEX_STRIPDOC 0x200 /* <<- strip tabs from here doc delim */
+#define LEX_INWORD 0x400
+
+#define COMSUB_META(ch) ((ch) == ';' || (ch) == '&' || (ch) == '|')
+
+#define CHECK_NESTRET_ERROR() \
+ do { \
+ if (nestret == &matched_pair_error) \
+ { \
+ free (ret); \
+ return &matched_pair_error; \
+ } \
+ } while (0)
+
+#define APPEND_NESTRET() \
+ do { \
+ if (nestlen) \
+ { \
+ RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64); \
+ strcpy (ret + retind, nestret); \
+ retind += nestlen; \
+ } \
+ } while (0)
+
+static char matched_pair_error;
+
+static char *
+parse_matched_pair (qc, open, close, lenp, flags)
+ int qc; /* `"' if this construct is within double quotes */
+ int open, close;
+ int *lenp, flags;
+{
+ int count, ch, tflags;
+ int nestlen, ttranslen, start_lineno;
+ char *ret, *nestret, *ttrans;
+ int retind, retsize, rflags;
+
+/*itrace("parse_matched_pair[%d]: open = %c close = %c flags = %d", line_number, open, close, flags);*/
+ count = 1;
+ tflags = 0;
+
+ if ((flags & P_COMMAND) && qc != '`' && qc != '\'' && qc != '"' && (flags & P_DQUOTE) == 0)
+ tflags |= LEX_CKCOMMENT;
+
+ /* RFLAGS is the set of flags we want to pass to recursive calls. */
+ rflags = (qc == '"') ? P_DQUOTE : (flags & P_DQUOTE);
+
+ ret = (char *)xmalloc (retsize = 64);
+ retind = 0;
+
+ start_lineno = line_number;
+ while (count)
+ {
+ ch = shell_getc (qc != '\'' && (tflags & LEX_PASSNEXT) == 0);
+
+ if (ch == EOF)
+ {
+ free (ret);
+ parser_error (start_lineno, _("unexpected EOF while looking for matching `%c'"), close);
+ EOF_Reached = 1; /* XXX */
+ return (&matched_pair_error);
+ }
+
+ /* Possible reprompting. */
+ if (ch == '\n' && SHOULD_PROMPT ())
+ prompt_again ();
+
+ /* Don't bother counting parens or doing anything else if in a comment
+ or part of a case statement */
+ if (tflags & LEX_INCOMMENT)
+ {
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+
+ if (ch == '\n')
+ tflags &= ~LEX_INCOMMENT;
+
+ continue;
+ }
+
+ /* Not exactly right yet, should handle shell metacharacters, too. If
+ any changes are made to this test, make analogous changes to subst.c:
+ extract_delimited_string(). */
+ else if MBTEST((tflags & LEX_CKCOMMENT) && (tflags & LEX_INCOMMENT) == 0 && ch == '#' && (retind == 0 || ret[retind-1] == '\n' || shellblank (ret[retind - 1])))
+ tflags |= LEX_INCOMMENT;
+
+ if (tflags & LEX_PASSNEXT) /* last char was backslash */
+ {
+ tflags &= ~LEX_PASSNEXT;
+ if (qc != '\'' && ch == '\n') /* double-quoted \<newline> disappears. */
+ {
+ if (retind > 0)
+ retind--; /* swallow previously-added backslash */
+ continue;
+ }
+
+ RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
+ if MBTEST(ch == CTLESC || ch == CTLNUL)
+ ret[retind++] = CTLESC;
+ ret[retind++] = ch;
+ continue;
+ }
+ /* If we're reparsing the input (e.g., from parse_string_to_word_list),
+ we've already prepended CTLESC to single-quoted results of $'...'.
+ We may want to do this for other CTLESC-quoted characters in
+ reparse, too. */
+ else if MBTEST((parser_state & PST_REPARSE) && open == '\'' && (ch == CTLESC || ch == CTLNUL))
+ {
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+ continue;
+ }
+ else if MBTEST(ch == CTLESC || ch == CTLNUL) /* special shell escapes */
+ {
+ RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
+ ret[retind++] = CTLESC;
+ ret[retind++] = ch;
+ continue;
+ }
+ else if MBTEST(ch == close) /* ending delimiter */
+ count--;
+ /* handle nested ${...} specially. */
+ else if MBTEST(open != close && (tflags & LEX_WASDOL) && open == '{' && ch == open) /* } */
+ count++;
+ else if MBTEST(((flags & P_FIRSTCLOSE) == 0) && ch == open) /* nested begin */
+ count++;
+
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+
+ /* If we just read the ending character, don't bother continuing. */
+ if (count == 0)
+ break;
+
+ if (open == '\'') /* '' inside grouping construct */
+ {
+ if MBTEST((flags & P_ALLOWESC) && ch == '\\')
+ tflags |= LEX_PASSNEXT;
+ continue;
+ }
+
+ if MBTEST(ch == '\\') /* backslashes */
+ tflags |= LEX_PASSNEXT;
+
+#if 0
+ /* The big hammer. Single quotes aren't special in double quotes. The
+ problem is that Posix says the single quotes are semi-special:
+ within a double-quoted ${...} construct "an even number of
+ unescaped double-quotes or single-quotes, if any, shall occur." */
+ if MBTEST(open == '{' && (flags & P_DQUOTE) && ch == '\'') /* } */
+ continue;
+#endif
+
+ /* Could also check open == '`' if we want to parse grouping constructs
+ inside old-style command substitution. */
+ if (open != close) /* a grouping construct */
+ {
+ if MBTEST(shellquote (ch))
+ {
+ /* '', ``, or "" inside $(...) or other grouping construct. */
+ push_delimiter (dstack, ch);
+ if MBTEST((tflags & LEX_WASDOL) && ch == '\'') /* $'...' inside group */
+ nestret = parse_matched_pair (ch, ch, ch, &nestlen, P_ALLOWESC|rflags);
+ else
+ nestret = parse_matched_pair (ch, ch, ch, &nestlen, rflags);
+ pop_delimiter (dstack);
+ CHECK_NESTRET_ERROR ();
+
+ if MBTEST((tflags & LEX_WASDOL) && ch == '\'' && (extended_quote || (rflags & P_DQUOTE) == 0))
+ {
+ /* Translate $'...' here. */
+ ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
+ xfree (nestret);
+
+ if ((rflags & P_DQUOTE) == 0)
+ {
+ nestret = sh_single_quote (ttrans);
+ free (ttrans);
+ nestlen = strlen (nestret);
+ }
+ else
+ {
+ nestret = ttrans;
+ nestlen = ttranslen;
+ }
+ retind -= 2; /* back up before the $' */
+ }
+ else if MBTEST((tflags & LEX_WASDOL) && ch == '"' && (extended_quote || (rflags & P_DQUOTE) == 0))
+ {
+ /* Locale expand $"..." here. */
+ ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen);
+ xfree (nestret);
+
+ nestret = sh_mkdoublequoted (ttrans, ttranslen, 0);
+ free (ttrans);
+ nestlen = ttranslen + 2;
+ retind -= 2; /* back up before the $" */
+ }
+
+ APPEND_NESTRET ();
+ FREE (nestret);
+ }
+ else if ((flags & P_ARRAYSUB) && (tflags & LEX_WASDOL) && (ch == '(' || ch == '{' || ch == '[')) /* ) } ] */
+ goto parse_dollar_word;
+ }
+ /* Parse an old-style command substitution within double quotes as a
+ single word. */
+ /* XXX - sh and ksh93 don't do this - XXX */
+ else if MBTEST(open == '"' && ch == '`')
+ {
+ nestret = parse_matched_pair (0, '`', '`', &nestlen, rflags);
+
+ CHECK_NESTRET_ERROR ();
+ APPEND_NESTRET ();
+
+ FREE (nestret);
+ }
+ else if MBTEST(open != '`' && (tflags & LEX_WASDOL) && (ch == '(' || ch == '{' || ch == '[')) /* ) } ] */
+ /* check for $(), $[], or ${} inside quoted string. */
+ {
+parse_dollar_word:
+ if (open == ch) /* undo previous increment */
+ count--;
+ if (ch == '(') /* ) */
+ nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
+ else if (ch == '{') /* } */
+ nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
+ else if (ch == '[') /* ] */
+ nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
+
+ CHECK_NESTRET_ERROR ();
+ APPEND_NESTRET ();
+
+ FREE (nestret);
+ }
+ if MBTEST(ch == '$')
+ tflags |= LEX_WASDOL;
+ else
+ tflags &= ~LEX_WASDOL;
+ }
+
+ ret[retind] = '\0';
+ if (lenp)
+ *lenp = retind;
+/*itrace("parse_matched_pair[%d]: returning %s", line_number, ret);*/
+ return ret;
+}
+
+/* Parse a $(...) command substitution. This is messier than I'd like, and
+ reproduces a lot more of the token-reading code than I'd like. */
+static char *
+parse_comsub (qc, open, close, lenp, flags)
+ int qc; /* `"' if this construct is within double quotes */
+ int open, close;
+ int *lenp, flags;
+{
+ int count, ch, peekc, tflags, lex_rwlen, lex_wlen, lex_firstind;
+ int nestlen, ttranslen, start_lineno;
+ char *ret, *nestret, *ttrans, *heredelim;
+ int retind, retsize, rflags, hdlen;
+
+#if 0 /* XXX - bash-4.2 -- jwm@horde.net */
+ /* Assume $(( introduces arithmetic command and parse accordingly. */
+ peekc = shell_getc (0);
+ shell_ungetc (peekc);
+ if (peekc == '(')
+ return (parse_matched_pair (qc, open, close, lenp, 0));
+#endif
+
+/*itrace("parse_comsub: qc = `%c' open = %c close = %c", qc, open, close);*/
+ count = 1;
+ tflags = LEX_RESWDOK;
+
+ if ((flags & P_COMMAND) && qc != '\'' && qc != '"' && (flags & P_DQUOTE) == 0)
+ tflags |= LEX_CKCASE;
+ if ((tflags & LEX_CKCASE) && (interactive == 0 || interactive_comments))
+ tflags |= LEX_CKCOMMENT;
+
+ /* RFLAGS is the set of flags we want to pass to recursive calls. */
+ rflags = (flags & P_DQUOTE);
+
+ ret = (char *)xmalloc (retsize = 64);
+ retind = 0;
+
+ start_lineno = line_number;
+ lex_rwlen = lex_wlen = 0;
+
+ heredelim = 0;
+ lex_firstind = -1;
+
+ while (count)
+ {
+comsub_readchar:
+ ch = shell_getc (qc != '\'' && (tflags & LEX_PASSNEXT) == 0);
+
+ if (ch == EOF)
+ {
+eof_error:
+ free (ret);
+ FREE (heredelim);
+ parser_error (start_lineno, _("unexpected EOF while looking for matching `%c'"), close);
+ EOF_Reached = 1; /* XXX */
+ return (&matched_pair_error);
+ }
+
+ /* If we hit the end of a line and are reading the contents of a here
+ document, and it's not the same line that the document starts on,
+ check for this line being the here doc delimiter. Otherwise, if
+ we're in a here document, mark the next character as the beginning
+ of a line. */
+ if (ch == '\n')
+ {
+ if ((tflags & LEX_HEREDELIM) && heredelim)
+ {
+ tflags &= ~LEX_HEREDELIM;
+ tflags |= LEX_INHEREDOC;
+ lex_firstind = retind + 1;
+ }
+ else if (tflags & LEX_INHEREDOC)
+ {
+ int tind;
+ tind = lex_firstind;
+ while ((tflags & LEX_STRIPDOC) && ret[tind] == '\t')
+ tind++;
+ if (STREQN (ret + tind, heredelim, hdlen))
+ {
+ tflags &= ~(LEX_STRIPDOC|LEX_INHEREDOC);
+/*itrace("parse_comsub:%d: found here doc end `%s'", line_number, ret + tind);*/
+ free (heredelim);
+ heredelim = 0;
+ lex_firstind = -1;
+ }
+ else
+ lex_firstind = retind + 1;
+ }
+ }
+
+ /* Possible reprompting. */
+ if (ch == '\n' && SHOULD_PROMPT ())
+ prompt_again ();
+
+ /* XXX -- possibly allow here doc to be delimited by ending right
+ paren. */
+ if ((tflags & LEX_INHEREDOC) && ch == close && count == 1)
+ {
+ int tind;
+/*itrace("parse_comsub: in here doc, ch == close, retind - firstind = %d hdlen = %d retind = %d", retind-lex_firstind, hdlen, retind);*/
+ tind = lex_firstind;
+ while ((tflags & LEX_STRIPDOC) && ret[tind] == '\t')
+ tind++;
+ if (retind-tind == hdlen && STREQN (ret + tind, heredelim, hdlen))
+ {
+ tflags &= ~(LEX_STRIPDOC|LEX_INHEREDOC);
+/*itrace("parse_comsub:%d: found here doc end `%s'", line_number, ret + tind);*/
+ free (heredelim);
+ heredelim = 0;
+ lex_firstind = -1;
+ }
+ }
+
+ /* Don't bother counting parens or doing anything else if in a comment */
+ if (tflags & (LEX_INCOMMENT|LEX_INHEREDOC))
+ {
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+
+ if ((tflags & LEX_INCOMMENT) && ch == '\n')
+{
+/*itrace("parse_comsub:%d: lex_incomment -> 0 ch = `%c'", line_number, ch);*/
+ tflags &= ~LEX_INCOMMENT;
+}
+
+ continue;
+ }
+
+ if (tflags & LEX_PASSNEXT) /* last char was backslash */
+ {
+/*itrace("parse_comsub:%d: lex_passnext -> 0 ch = `%c' (%d)", line_number, ch, __LINE__);*/
+ tflags &= ~LEX_PASSNEXT;
+ if (qc != '\'' && ch == '\n') /* double-quoted \<newline> disappears. */
+ {
+ if (retind > 0)
+ retind--; /* swallow previously-added backslash */
+ continue;
+ }
+
+ RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
+ if MBTEST(ch == CTLESC || ch == CTLNUL)
+ ret[retind++] = CTLESC;
+ ret[retind++] = ch;
+ continue;
+ }
+
+ /* If this is a shell break character, we are not in a word. If not,
+ we either start or continue a word. */
+ if MBTEST(shellbreak (ch))
+ {
+ tflags &= ~LEX_INWORD;
+/*itrace("parse_comsub:%d: lex_inword -> 0 ch = `%c' (%d)", line_number, ch, __LINE__);*/
+ }
+ else
+ {
+ if (tflags & LEX_INWORD)
+ {
+ lex_wlen++;
+/*itrace("parse_comsub:%d: lex_inword == 1 ch = `%c' lex_wlen = %d (%d)", line_number, ch, lex_wlen, __LINE__);*/
+ }
+ else
+ {
+/*itrace("parse_comsub:%d: lex_inword -> 1 ch = `%c' (%d)", line_number, ch, __LINE__);*/
+ tflags |= LEX_INWORD;
+ lex_wlen = 0;
+ }
+ }
+
+ /* Skip whitespace */
+ if MBTEST(shellblank (ch) && lex_rwlen == 0)
+ {
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+ continue;
+ }
+
+ /* Either we are looking for the start of the here-doc delimiter
+ (lex_firstind == -1) or we are reading one (lex_firstind >= 0).
+ If this character is a shell break character and we are reading
+ the delimiter, save it and note that we are now reading a here
+ document. If we've found the start of the delimiter, note it by
+ setting lex_firstind. Backslashes can quote shell metacharacters
+ in here-doc delimiters. */
+ if (tflags & LEX_HEREDELIM)
+ {
+ if (lex_firstind == -1 && shellbreak (ch) == 0)
+ lex_firstind = retind;
+#if 0
+ else if (heredelim && (tflags & LEX_PASSNEXT) == 0 && ch == '\n')
+ {
+ tflags |= LEX_INHEREDOC;
+ tflags &= ~LEX_HEREDELIM;
+ lex_firstind = retind + 1;
+ }
+#endif
+ else if (lex_firstind >= 0 && (tflags & LEX_PASSNEXT) == 0 && shellbreak (ch))
+ {
+ if (heredelim == 0)
+ {
+ nestret = substring (ret, lex_firstind, retind);
+ heredelim = string_quote_removal (nestret, 0);
+ free (nestret);
+ hdlen = STRLEN(heredelim);
+/*itrace("parse_comsub:%d: found here doc delimiter `%s' (%d)", line_number, heredelim, hdlen);*/
+ }
+ if (ch == '\n')
+ {
+ tflags |= LEX_INHEREDOC;
+ tflags &= ~LEX_HEREDELIM;
+ lex_firstind = retind + 1;
+ }
+ else
+ lex_firstind = -1;
+ }
+ }
+
+ /* Meta-characters that can introduce a reserved word. Not perfect yet. */
+ if MBTEST((tflags & LEX_RESWDOK) == 0 && (tflags & LEX_CKCASE) && (tflags & LEX_INCOMMENT) == 0 && (shellmeta(ch) || ch == '\n'))
+ {
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+ peekc = shell_getc (1);
+ if (ch == peekc && (ch == '&' || ch == '|' || ch == ';')) /* two-character tokens */
+ {
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = peekc;
+/*itrace("parse_comsub:%d: set lex_reswordok = 1, ch = `%c'", line_number, ch);*/
+ tflags |= LEX_RESWDOK;
+ lex_rwlen = 0;
+ continue;
+ }
+ else if (ch == '\n' || COMSUB_META(ch))
+ {
+ shell_ungetc (peekc);
+/*itrace("parse_comsub:%d: set lex_reswordok = 1, ch = `%c'", line_number, ch);*/
+ tflags |= LEX_RESWDOK;
+ lex_rwlen = 0;
+ continue;
+ }
+ else if (ch == EOF)
+ goto eof_error;
+ else
+ {
+ /* `unget' the character we just added and fall through */
+ retind--;
+ shell_ungetc (peekc);
+ }
+ }
+
+ /* If we can read a reserved word, try to read one. */
+ if (tflags & LEX_RESWDOK)
+ {
+ if MBTEST(islower (ch))
+ {
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+ lex_rwlen++;
+ continue;
+ }
+ else if MBTEST(lex_rwlen == 4 && shellbreak (ch))
+ {
+ if (STREQN (ret + retind - 4, "case", 4))
+{
+ tflags |= LEX_INCASE;
+/*itrace("parse_comsub:%d: found `case', lex_incase -> 1 lex_reswdok -> 0", line_number);*/
+}
+ else if (STREQN (ret + retind - 4, "esac", 4))
+{
+ tflags &= ~LEX_INCASE;
+/*itrace("parse_comsub:%d: found `esac', lex_incase -> 0 lex_reswdok -> 0", line_number);*/
+}
+ tflags &= ~LEX_RESWDOK;
+ }
+ else if MBTEST((tflags & LEX_CKCOMMENT) && ch == '#' && (lex_rwlen == 0 || ((tflags & LEX_INWORD) && lex_wlen == 0)))
+ ; /* don't modify LEX_RESWDOK if we're starting a comment */
+ else if MBTEST((tflags & LEX_INCASE) && ch != '\n')
+ /* If we can read a reserved word and we're in case, we're at the
+ point where we can read a new pattern list or an esac. We
+ handle the esac case above. If we read a newline, we want to
+ leave LEX_RESWDOK alone. If we read anything else, we want to
+ turn off LEX_RESWDOK, since we're going to read a pattern list. */
+{
+ tflags &= ~LEX_RESWDOK;
+/*itrace("parse_comsub:%d: lex_incase == 1 found `%c', lex_reswordok -> 0", line_number, ch);*/
+}
+ else if MBTEST(shellbreak (ch) == 0)
+{
+ tflags &= ~LEX_RESWDOK;
+/*itrace("parse_comsub:%d: found `%c', lex_reswordok -> 0", line_number, ch);*/
+}
+ }
+
+ /* Might be the start of a here-doc delimiter */
+ if MBTEST((tflags & LEX_INCOMMENT) == 0 && (tflags & LEX_CKCASE) && ch == '<')
+ {
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+ peekc = shell_getc (1);
+ if (peekc == EOF)
+ goto eof_error;
+ if (peekc == ch)
+ {
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = peekc;
+ peekc = shell_getc (1);
+ if (peekc == EOF)
+ goto eof_error;
+ if (peekc == '-')
+ {
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = peekc;
+ tflags |= LEX_STRIPDOC;
+ }
+ else
+ shell_ungetc (peekc);
+ if (peekc != '<')
+ {
+ tflags |= LEX_HEREDELIM;
+ lex_firstind = -1;
+ }
+ continue;
+ }
+ else
+ ch = peekc; /* fall through and continue XXX */
+ }
+ else if MBTEST((tflags & LEX_CKCOMMENT) && (tflags & LEX_INCOMMENT) == 0 && ch == '#' && (((tflags & LEX_RESWDOK) && lex_rwlen == 0) || ((tflags & LEX_INWORD) && lex_wlen == 0)))
+{
+/*itrace("parse_comsub:%d: lex_incomment -> 1 (%d)", line_number, __LINE__);*/
+ tflags |= LEX_INCOMMENT;
+}
+
+ if MBTEST(ch == CTLESC || ch == CTLNUL) /* special shell escapes */
+ {
+ RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
+ ret[retind++] = CTLESC;
+ ret[retind++] = ch;
+ continue;
+ }
+#if 0
+ else if MBTEST((tflags & LEX_INCASE) && ch == close && close == ')')
+ tflags &= ~LEX_INCASE; /* XXX */
+#endif
+ else if MBTEST(ch == close && (tflags & LEX_INCASE) == 0) /* ending delimiter */
+{
+ count--;
+/*itrace("parse_comsub:%d: found close: count = %d", line_number, count);*/
+}
+ else if MBTEST(((flags & P_FIRSTCLOSE) == 0) && (tflags & LEX_INCASE) == 0 && ch == open) /* nested begin */
+{
+ count++;
+/*itrace("parse_comsub:%d: found open: count = %d", line_number, count);*/
+}
+
+ /* Add this character. */
+ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+ ret[retind++] = ch;
+
+ /* If we just read the ending character, don't bother continuing. */
+ if (count == 0)
+ break;
+
+ if MBTEST(ch == '\\') /* backslashes */
+ tflags |= LEX_PASSNEXT;
+
+ if MBTEST(shellquote (ch))
+ {
+ /* '', ``, or "" inside $(...). */
+ push_delimiter (dstack, ch);
+ if MBTEST((tflags & LEX_WASDOL) && ch == '\'') /* $'...' inside group */
+ nestret = parse_matched_pair (ch, ch, ch, &nestlen, P_ALLOWESC|rflags);
+ else
+ nestret = parse_matched_pair (ch, ch, ch, &nestlen, rflags);
+ pop_delimiter (dstack);
+ CHECK_NESTRET_ERROR ();
+
+ if MBTEST((tflags & LEX_WASDOL) && ch == '\'' && (extended_quote || (rflags & P_DQUOTE) == 0))
+ {
+ /* Translate $'...' here. */
+ ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
+ xfree (nestret);
+
+ if ((rflags & P_DQUOTE) == 0)
+ {
+ nestret = sh_single_quote (ttrans);
+ free (ttrans);
+ nestlen = strlen (nestret);
+ }
+ else
+ {
+ nestret = ttrans;
+ nestlen = ttranslen;
+ }
+ retind -= 2; /* back up before the $' */
+ }
+ else if MBTEST((tflags & LEX_WASDOL) && ch == '"' && (extended_quote || (rflags & P_DQUOTE) == 0))
+ {
+ /* Locale expand $"..." here. */
+ ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen);
+ xfree (nestret);
+
+ nestret = sh_mkdoublequoted (ttrans, ttranslen, 0);
+ free (ttrans);
+ nestlen = ttranslen + 2;
+ retind -= 2; /* back up before the $" */
+ }
+
+ APPEND_NESTRET ();
+ FREE (nestret);
+ }
+ else if MBTEST((tflags & LEX_WASDOL) && (ch == '(' || ch == '{' || ch == '[')) /* ) } ] */
+ /* check for $(), $[], or ${} inside command substitution. */
+ {
+ if ((tflags & LEX_INCASE) == 0 && open == ch) /* undo previous increment */
+ count--;
+ if (ch == '(') /* ) */
+ nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
+ else if (ch == '{') /* } */
+ nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
+ else if (ch == '[') /* ] */
+ nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
+
+ CHECK_NESTRET_ERROR ();
+ APPEND_NESTRET ();
+
+ FREE (nestret);
+ }
+ if MBTEST(ch == '$')
+ tflags |= LEX_WASDOL;
+ else
+ tflags &= ~LEX_WASDOL;
+ }
+
+ FREE (heredelim);
+ ret[retind] = '\0';
+ if (lenp)
+ *lenp = retind;
+/*itrace("parse_comsub:%d: returning `%s'", line_number, ret);*/
+ return ret;
+}
+
+/* XXX - this needs to handle functionality like subst.c:no_longjmp_on_fatal_error;
+ maybe extract_command_subst should handle it. */
+char *
+xparse_dolparen (base, string, indp, flags)
+ char *base;
+ char *string;
+ int *indp;
+ int flags;
+{
+ sh_parser_state_t ps;
+ int orig_ind, nc, sflags;
+ char *ret, *s, *ep, *ostring;
+
+ /*yydebug = 1;*/
+ orig_ind = *indp;
+ ostring = string;
+
+ sflags = SEVAL_NONINT|SEVAL_NOHIST|SEVAL_NOFREE;
+ if (flags & SX_NOLONGJMP)
+ sflags |= SEVAL_NOLONGJMP;
+ save_parser_state (&ps);
+
+ /*(*/
+ parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*(*/
+ shell_eof_token = ')';
+ parse_string (string, "command substitution", sflags, &ep);
+
+ restore_parser_state (&ps);
+ reset_parser ();
+ if (interactive)
+ token_to_read = 0;
+
+ /* Need to find how many characters parse_and_execute consumed, update
+ *indp, if flags != 0, copy the portion of the string parsed into RET
+ and return it. If flags & 1 (EX_NOALLOC) we can return NULL. */
+
+ /*(*/
+ if (ep[-1] != ')')
+ {
+#if DEBUG
+ if (ep[-1] != '\n')
+ itrace("xparse_dolparen:%d: ep[-1] != RPAREN (%d), ep = `%s'", line_number, ep[-1], ep);
+#endif
+ while (ep > ostring && ep[-1] == '\n') ep--;
+ }
+
+ nc = ep - ostring;
+ *indp = ep - base - 1;
+
+ /*(*/
+#if DEBUG
+ if (base[*indp] != ')')
+ itrace("xparse_dolparen:%d: base[%d] != RPAREN (%d), base = `%s'", line_number, *indp, base[*indp], base);
+#endif
+
+ if (flags & SX_NOALLOC)
+ return (char *)NULL;
+
+ if (nc == 0)
+ {
+ ret = xmalloc (1);
+ ret[0] = '\0';
+ }
+ else
+ ret = substring (ostring, 0, nc - 1);
+
+ return ret;
+}
+
+#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
+/* Parse a double-paren construct. It can be either an arithmetic
+ command, an arithmetic `for' command, or a nested subshell. Returns
+ the parsed token, -1 on error, or -2 if we didn't do anything and
+ should just go on. */
+static int
+parse_dparen (c)
+ int c;
+{
+ int cmdtyp, sline;
+ char *wval;
+ WORD_DESC *wd;
+
+#if defined (ARITH_FOR_COMMAND)
+ if (last_read_token == FOR)
+ {
+ arith_for_lineno = line_number;
+ cmdtyp = parse_arith_cmd (&wval, 0);
+ if (cmdtyp == 1)
+ {
+ wd = alloc_word_desc ();
+ wd->word = wval;
+ yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
+ return (ARITH_FOR_EXPRS);
+ }
+ else
+ return -1; /* ERROR */
+ }
+#endif
+
+#if defined (DPAREN_ARITHMETIC)
+ if (reserved_word_acceptable (last_read_token))
+ {
+ sline = line_number;
+
+ cmdtyp = parse_arith_cmd (&wval, 0);
+ if (cmdtyp == 1) /* arithmetic command */
+ {
+ wd = alloc_word_desc ();
+ wd->word = wval;
+ wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB|W_DQUOTE;
+ yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
+ return (ARITH_CMD);
+ }
+ else if (cmdtyp == 0) /* nested subshell */
+ {
+ push_string (wval, 0, (alias_t *)NULL);
+ if ((parser_state & PST_CASEPAT) == 0)
+ parser_state |= PST_SUBSHELL;
+ return (c);
+ }
+ else /* ERROR */
+ return -1;
+ }
+#endif
+
+ return -2; /* XXX */
+}
+
+/* We've seen a `(('. Look for the matching `))'. If we get it, return 1.
+ If not, assume it's a nested subshell for backwards compatibility and
+ return 0. In any case, put the characters we've consumed into a locally-
+ allocated buffer and make *ep point to that buffer. Return -1 on an
+ error, for example EOF. */
+static int
+parse_arith_cmd (ep, adddq)
+ char **ep;
+ int adddq;
+{
+ int exp_lineno, rval, c;
+ char *ttok, *tokstr;
+ int ttoklen;
+
+ exp_lineno = line_number;
+ ttok = parse_matched_pair (0, '(', ')', &ttoklen, 0);
+ rval = 1;
+ if (ttok == &matched_pair_error)
+ return -1;
+ /* Check that the next character is the closing right paren. If
+ not, this is a syntax error. ( */
+ c = shell_getc (0);
+ if MBTEST(c != ')')
+ rval = 0;
+
+ tokstr = (char *)xmalloc (ttoklen + 4);
+
+ /* if ADDDQ != 0 then (( ... )) -> "..." */
+ if (rval == 1 && adddq) /* arith cmd, add double quotes */
+ {
+ tokstr[0] = '"';
+ strncpy (tokstr + 1, ttok, ttoklen - 1);
+ tokstr[ttoklen] = '"';
+ tokstr[ttoklen+1] = '\0';
+ }
+ else if (rval == 1) /* arith cmd, don't add double quotes */
+ {
+ strncpy (tokstr, ttok, ttoklen - 1);
+ tokstr[ttoklen-1] = '\0';
+ }
+ else /* nested subshell */
+ {
+ tokstr[0] = '(';
+ strncpy (tokstr + 1, ttok, ttoklen - 1);
+ tokstr[ttoklen] = ')';
+ tokstr[ttoklen+1] = c;
+ tokstr[ttoklen+2] = '\0';
+ }
+
+ *ep = tokstr;
+ FREE (ttok);
+ return rval;
+}
+#endif /* DPAREN_ARITHMETIC || ARITH_FOR_COMMAND */
+
+#if defined (COND_COMMAND)
+static void
+cond_error ()
+{
+ char *etext;
+
+ if (EOF_Reached && cond_token != COND_ERROR) /* [[ */
+ parser_error (cond_lineno, _("unexpected EOF while looking for `]]'"));
+ else if (cond_token != COND_ERROR)
+ {
+ if (etext = error_token_from_token (cond_token))
+ {
+ parser_error (cond_lineno, _("syntax error in conditional expression: unexpected token `%s'"), etext);
+ free (etext);
+ }
+ else
+ parser_error (cond_lineno, _("syntax error in conditional expression"));
+ }
+}
+
+static COND_COM *
+cond_expr ()
+{
+ return (cond_or ());
+}
+
+static COND_COM *
+cond_or ()
+{
+ COND_COM *l, *r;
+
+ l = cond_and ();
+ if (cond_token == OR_OR)
+ {
+ r = cond_or ();
+ l = make_cond_node (COND_OR, (WORD_DESC *)NULL, l, r);
+ }
+ return l;
+}
+
+static COND_COM *
+cond_and ()
+{
+ COND_COM *l, *r;
+
+ l = cond_term ();
+ if (cond_token == AND_AND)
+ {
+ r = cond_and ();
+ l = make_cond_node (COND_AND, (WORD_DESC *)NULL, l, r);
+ }
+ return l;
+}
+
+static int
+cond_skip_newlines ()
+{
+ while ((cond_token = read_token (READ)) == '\n')
+ {
+ if (SHOULD_PROMPT ())
+ prompt_again ();
+ }
+ return (cond_token);
+}
+
+#define COND_RETURN_ERROR() \
+ do { cond_token = COND_ERROR; return ((COND_COM *)NULL); } while (0)
+
+static COND_COM *
+cond_term ()
+{
+ WORD_DESC *op;
+ COND_COM *term, *tleft, *tright;
+ int tok, lineno;
+ char *etext;
+
+ /* Read a token. It can be a left paren, a `!', a unary operator, or a
+ word that should be the first argument of a binary operator. Start by
+ skipping newlines, since this is a compound command. */
+ tok = cond_skip_newlines ();
+ lineno = line_number;
+ if (tok == COND_END)
+ {
+ COND_RETURN_ERROR ();
+ }
+ else if (tok == '(')
+ {
+ term = cond_expr ();
+ if (cond_token != ')')
+ {
+ if (term)
+ dispose_cond_node (term); /* ( */
+ if (etext = error_token_from_token (cond_token))
+ {
+ parser_error (lineno, _("unexpected token `%s', expected `)'"), etext);
+ free (etext);
+ }
+ else
+ parser_error (lineno, _("expected `)'"));
+ COND_RETURN_ERROR ();
+ }
+ term = make_cond_node (COND_EXPR, (WORD_DESC *)NULL, term, (COND_COM *)NULL);
+ (void)cond_skip_newlines ();
+ }
+ else if (tok == BANG || (tok == WORD && (yylval.word->word[0] == '!' && yylval.word->word[1] == '\0')))
+ {
+ if (tok == WORD)
+ dispose_word (yylval.word); /* not needed */
+ term = cond_term ();
+ if (term)
+ term->flags |= CMD_INVERT_RETURN;
+ }
+ else if (tok == WORD && yylval.word->word[0] == '-' && yylval.word->word[2] == 0 && test_unop (yylval.word->word))
+ {
+ op = yylval.word;
+ tok = read_token (READ);
+ if (tok == WORD)
+ {
+ tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
+ term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
+ }
+ else
+ {
+ dispose_word (op);
+ if (etext = error_token_from_token (tok))
+ {
+ parser_error (line_number, _("unexpected argument `%s' to conditional unary operator"), etext);
+ free (etext);
+ }
+ else
+ parser_error (line_number, _("unexpected argument to conditional unary operator"));
+ COND_RETURN_ERROR ();
+ }
+
+ (void)cond_skip_newlines ();
+ }
+ else if (tok == WORD) /* left argument to binary operator */
+ {
+ /* lhs */
+ tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
+
+ /* binop */
+ tok = read_token (READ);
+ if (tok == WORD && test_binop (yylval.word->word))
+ {
+ op = yylval.word;
+ if (op->word[0] == '=' && (op->word[1] == '\0' || (op->word[1] == '=' && op->word[2] == '\0')))
+ parser_state |= PST_EXTPAT;
+ else if (op->word[0] == '!' && op->word[1] == '=' && op->word[2] == '\0')
+ parser_state |= PST_EXTPAT;
+ }
+#if defined (COND_REGEXP)
+ else if (tok == WORD && STREQ (yylval.word->word, "=~"))
+ {
+ op = yylval.word;
+ parser_state |= PST_REGEXP;
+ }
+#endif
+ else if (tok == '<' || tok == '>')
+ op = make_word_from_token (tok); /* ( */
+ /* There should be a check before blindly accepting the `)' that we have
+ seen the opening `('. */
+ else if (tok == COND_END || tok == AND_AND || tok == OR_OR || tok == ')')
+ {
+ /* Special case. [[ x ]] is equivalent to [[ -n x ]], just like
+ the test command. Similarly for [[ x && expr ]] or
+ [[ x || expr ]] or [[ (x) ]]. */
+ op = make_word ("-n");
+ term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
+ cond_token = tok;
+ return (term);
+ }
+ else
+ {
+ if (etext = error_token_from_token (tok))
+ {
+ parser_error (line_number, _("unexpected token `%s', conditional binary operator expected"), etext);
+ free (etext);
+ }
+ else
+ parser_error (line_number, _("conditional binary operator expected"));
+ dispose_cond_node (tleft);
+ COND_RETURN_ERROR ();
+ }
+
+ /* rhs */
+ if (parser_state & PST_EXTPAT)
+ extended_glob = 1;
+ tok = read_token (READ);
+ if (parser_state & PST_EXTPAT)
+ extended_glob = global_extglob;
+ parser_state &= ~(PST_REGEXP|PST_EXTPAT);
+
+ if (tok == WORD)
+ {
+ tright = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
+ term = make_cond_node (COND_BINARY, op, tleft, tright);
+ }
+ else
+ {
+ if (etext = error_token_from_token (tok))
+ {
+ parser_error (line_number, _("unexpected argument `%s' to conditional binary operator"), etext);
+ free (etext);
+ }
+ else
+ parser_error (line_number, _("unexpected argument to conditional binary operator"));
+ dispose_cond_node (tleft);
+ dispose_word (op);
+ COND_RETURN_ERROR ();
+ }
+
+ (void)cond_skip_newlines ();
+ }
+ else
+ {
+ if (tok < 256)
+ parser_error (line_number, _("unexpected token `%c' in conditional command"), tok);
+ else if (etext = error_token_from_token (tok))
+ {
+ parser_error (line_number, _("unexpected token `%s' in conditional command"), etext);
+ free (etext);
+ }
+ else
+ parser_error (line_number, _("unexpected token %d in conditional command"), tok);
+ COND_RETURN_ERROR ();
+ }
+ return (term);
+}
+
+/* This is kind of bogus -- we slip a mini recursive-descent parser in
+ here to handle the conditional statement syntax. */
+static COMMAND *
+parse_cond_command ()
+{
+ COND_COM *cexp;
+
+ global_extglob = extended_glob;
+ cexp = cond_expr ();
+ return (make_cond_command (cexp));
+}
+#endif
+
+#if defined (ARRAY_VARS)
+/* When this is called, it's guaranteed that we don't care about anything
+ in t beyond i. We do save and restore the chars, though. */
+static int
+token_is_assignment (t, i)
+ char *t;
+ int i;
+{
+ unsigned char c, c1;
+ int r;
+
+ c = t[i]; c1 = t[i+1];
+ t[i] = '='; t[i+1] = '\0';
+ r = assignment (t, (parser_state & PST_COMPASSIGN) != 0);
+ t[i] = c; t[i+1] = c1;
+ return r;
+}
+
+/* XXX - possible changes here for `+=' */
+static int
+token_is_ident (t, i)
+ char *t;
+ int i;
+{
+ unsigned char c;
+ int r;
+
+ c = t[i];
+ t[i] = '\0';
+ r = legal_identifier (t);
+ t[i] = c;
+ return r;
+}
+#endif
+
+static int
+read_token_word (character)
+ int character;
+{
+ /* The value for YYLVAL when a WORD is read. */
+ WORD_DESC *the_word;
+
+ /* Index into the token that we are building. */
+ int token_index;
+
+ /* ALL_DIGITS becomes zero when we see a non-digit. */
+ int all_digit_token;
+
+ /* DOLLAR_PRESENT becomes non-zero if we see a `$'. */
+ int dollar_present;
+
+ /* COMPOUND_ASSIGNMENT becomes non-zero if we are parsing a compound
+ assignment. */
+ int compound_assignment;
+
+ /* QUOTED becomes non-zero if we see one of ("), ('), (`), or (\). */
+ int quoted;
+
+ /* Non-zero means to ignore the value of the next character, and just
+ to add it no matter what. */
+ int pass_next_character;
+
+ /* The current delimiting character. */
+ int cd;
+ int result, peek_char;
+ char *ttok, *ttrans;
+ int ttoklen, ttranslen;
+ intmax_t lvalue;
+
+ if (token_buffer_size < TOKEN_DEFAULT_INITIAL_SIZE)
+ token = (char *)xrealloc (token, token_buffer_size = TOKEN_DEFAULT_INITIAL_SIZE);
+
+ token_index = 0;
+ all_digit_token = DIGIT (character);
+ dollar_present = quoted = pass_next_character = compound_assignment = 0;
+
+ for (;;)
+ {
+ if (character == EOF)
+ goto got_token;
+
+ if (pass_next_character)
+ {
+ pass_next_character = 0;
+ goto got_escaped_character;
+ }
+
+ cd = current_delimiter (dstack);
+
+ /* Handle backslashes. Quote lots of things when not inside of
+ double-quotes, quote some things inside of double-quotes. */
+ if MBTEST(character == '\\')
+ {
+ peek_char = shell_getc (0);
+
+ /* Backslash-newline is ignored in all cases except
+ when quoted with single quotes. */
+ if (peek_char == '\n')
+ {
+ character = '\n';
+ goto next_character;
+ }
+ else
+ {
+ shell_ungetc (peek_char);
+
+ /* If the next character is to be quoted, note it now. */
+ if (cd == 0 || cd == '`' ||
+ (cd == '"' && peek_char >= 0 && (sh_syntaxtab[peek_char] & CBSDQUOTE)))
+ pass_next_character++;
+
+ quoted = 1;
+ goto got_character;
+ }
+ }
+
+ /* Parse a matched pair of quote characters. */
+ if MBTEST(shellquote (character))
+ {
+ push_delimiter (dstack, character);
+ ttok = parse_matched_pair (character, character, character, &ttoklen, (character == '`') ? P_COMMAND : 0);
+ pop_delimiter (dstack);
+ if (ttok == &matched_pair_error)
+ return -1; /* Bail immediately. */
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ token_buffer_size, TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = character;
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ all_digit_token = 0;
+ quoted = 1;
+ dollar_present |= (character == '"' && strchr (ttok, '$') != 0);
+ FREE (ttok);
+ goto next_character;
+ }
+
+#ifdef COND_REGEXP
+ /* When parsing a regexp as a single word inside a conditional command,
+ we need to special-case characters special to both the shell and
+ regular expressions. Right now, that is only '(' and '|'. */ /*)*/
+ if MBTEST((parser_state & PST_REGEXP) && (character == '(' || character == '|')) /*)*/
+ {
+ if (character == '|')
+ goto got_character;
+
+ push_delimiter (dstack, character);
+ ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0);
+ pop_delimiter (dstack);
+ if (ttok == &matched_pair_error)
+ return -1; /* Bail immediately. */
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ token_buffer_size, TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = character;
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ FREE (ttok);
+ dollar_present = all_digit_token = 0;
+ goto next_character;
+ }
+#endif /* COND_REGEXP */
+
+#ifdef EXTENDED_GLOB
+ /* Parse a ksh-style extended pattern matching specification. */
+ if MBTEST(extended_glob && PATTERN_CHAR (character))
+ {
+ peek_char = shell_getc (1);
+ if MBTEST(peek_char == '(') /* ) */
+ {
+ push_delimiter (dstack, peek_char);
+ ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0);
+ pop_delimiter (dstack);
+ if (ttok == &matched_pair_error)
+ return -1; /* Bail immediately. */
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = character;
+ token[token_index++] = peek_char;
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ FREE (ttok);
+ dollar_present = all_digit_token = 0;
+ goto next_character;
+ }
+ else
+ shell_ungetc (peek_char);
+ }
+#endif /* EXTENDED_GLOB */
+
+ /* If the delimiter character is not single quote, parse some of
+ the shell expansions that must be read as a single word. */
+ if (shellexp (character))
+ {
+ peek_char = shell_getc (1);
+ /* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */
+ if MBTEST(peek_char == '(' || \
+ ((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */
+ {
+ if (peek_char == '{') /* } */
+ ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE);
+ else if (peek_char == '(') /* ) */
+ {
+ /* XXX - push and pop the `(' as a delimiter for use by
+ the command-oriented-history code. This way newlines
+ appearing in the $(...) string get added to the
+ history literally rather than causing a possibly-
+ incorrect `;' to be added. ) */
+ push_delimiter (dstack, peek_char);
+ ttok = parse_comsub (cd, '(', ')', &ttoklen, P_COMMAND);
+ pop_delimiter (dstack);
+ }
+ else
+ ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0);
+ if (ttok == &matched_pair_error)
+ return -1; /* Bail immediately. */
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = character;
+ token[token_index++] = peek_char;
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ FREE (ttok);
+ dollar_present = 1;
+ all_digit_token = 0;
+ goto next_character;
+ }
+ /* This handles $'...' and $"..." new-style quoted strings. */
+ else if MBTEST(character == '$' && (peek_char == '\'' || peek_char == '"'))
+ {
+ int first_line;
+
+ first_line = line_number;
+ push_delimiter (dstack, peek_char);
+ ttok = parse_matched_pair (peek_char, peek_char, peek_char,
+ &ttoklen,
+ (peek_char == '\'') ? P_ALLOWESC : 0);
+ pop_delimiter (dstack);
+ if (ttok == &matched_pair_error)
+ return -1;
+ if (peek_char == '\'')
+ {
+ ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen);
+ free (ttok);
+
+ /* Insert the single quotes and correctly quote any
+ embedded single quotes (allowed because P_ALLOWESC was
+ passed to parse_matched_pair). */
+ ttok = sh_single_quote (ttrans);
+ free (ttrans);
+ ttranslen = strlen (ttok);
+ ttrans = ttok;
+ }
+ else
+ {
+ /* Try to locale-expand the converted string. */
+ ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen);
+ free (ttok);
+
+ /* Add the double quotes back */
+ ttok = sh_mkdoublequoted (ttrans, ttranslen, 0);
+ free (ttrans);
+ ttranslen += 2;
+ ttrans = ttok;
+ }
+
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ strcpy (token + token_index, ttrans);
+ token_index += ttranslen;
+ FREE (ttrans);
+ quoted = 1;
+ all_digit_token = 0;
+ goto next_character;
+ }
+ /* This could eventually be extended to recognize all of the
+ shell's single-character parameter expansions, and set flags.*/
+ else if MBTEST(character == '$' && peek_char == '$')
+ {
+ ttok = (char *)xmalloc (3);
+ ttok[0] = ttok[1] = '$';
+ ttok[2] = '\0';
+ RESIZE_MALLOCED_BUFFER (token, token_index, 3,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ strcpy (token + token_index, ttok);
+ token_index += 2;
+ dollar_present = 1;
+ all_digit_token = 0;
+ FREE (ttok);
+ goto next_character;
+ }
+ else
+ shell_ungetc (peek_char);
+ }
+
+#if defined (ARRAY_VARS)
+ /* Identify possible array subscript assignment; match [...]. If
+ parser_state&PST_COMPASSIGN, we need to parse [sub]=words treating
+ `sub' as if it were enclosed in double quotes. */
+ else if MBTEST(character == '[' && /* ] */
+ ((token_index > 0 && assignment_acceptable (last_read_token) && token_is_ident (token, token_index)) ||
+ (token_index == 0 && (parser_state&PST_COMPASSIGN))))
+ {
+ ttok = parse_matched_pair (cd, '[', ']', &ttoklen, P_ARRAYSUB);
+ if (ttok == &matched_pair_error)
+ return -1; /* Bail immediately. */
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = character;
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ FREE (ttok);
+ all_digit_token = 0;
+ goto next_character;
+ }
+ /* Identify possible compound array variable assignment. */
+ else if MBTEST(character == '=' && token_index > 0 && (assignment_acceptable (last_read_token) || (parser_state & PST_ASSIGNOK)) && token_is_assignment (token, token_index))
+ {
+ peek_char = shell_getc (1);
+ if MBTEST(peek_char == '(') /* ) */
+ {
+ ttok = parse_compound_assignment (&ttoklen);
+
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 4,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+
+ token[token_index++] = '=';
+ token[token_index++] = '(';
+ if (ttok)
+ {
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ }
+ token[token_index++] = ')';
+ FREE (ttok);
+ all_digit_token = 0;
+ compound_assignment = 1;
+#if 1
+ goto next_character;
+#else
+ goto got_token; /* ksh93 seems to do this */
+#endif
+ }
+ else
+ shell_ungetc (peek_char);
+ }
+#endif
+
+ /* When not parsing a multi-character word construct, shell meta-
+ characters break words. */
+ if MBTEST(shellbreak (character))
+ {
+ shell_ungetc (character);
+ goto got_token;
+ }
+
+ got_character:
+
+ if (character == CTLESC || character == CTLNUL)
+ token[token_index++] = CTLESC;
+
+ got_escaped_character:
+
+ all_digit_token &= DIGIT (character);
+ dollar_present |= character == '$';
+
+ token[token_index++] = character;
+
+ RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+
+ next_character:
+ if (character == '\n' && SHOULD_PROMPT ())
+ prompt_again ();
+
+ /* We want to remove quoted newlines (that is, a \<newline> pair)
+ unless we are within single quotes or pass_next_character is
+ set (the shell equivalent of literal-next). */
+ cd = current_delimiter (dstack);
+ character = shell_getc (cd != '\'' && pass_next_character == 0);
+ } /* end for (;;) */
+
+got_token:
+
+ token[token_index] = '\0';
+
+ /* Check to see what thing we should return. If the last_read_token
+ is a `<', or a `&', or the character which ended this token is
+ a '>' or '<', then, and ONLY then, is this input token a NUMBER.
+ Otherwise, it is just a word, and should be returned as such. */
+ if MBTEST(all_digit_token && (character == '<' || character == '>' || \
+ last_read_token == LESS_AND || \
+ last_read_token == GREATER_AND))
+ {
+ if (legal_number (token, &lvalue) && (int)lvalue == lvalue)
+ yylval.number = lvalue;
+ else
+ yylval.number = -1;
+ return (NUMBER);
+ }
+
+ /* Check for special case tokens. */
+ result = (last_shell_getc_is_singlebyte) ? special_case_tokens (token) : -1;
+ if (result >= 0)
+ return result;
+
+#if defined (ALIAS)
+ /* Posix.2 does not allow reserved words to be aliased, so check for all
+ of them, including special cases, before expanding the current token
+ as an alias. */
+ if MBTEST(posixly_correct)
+ CHECK_FOR_RESERVED_WORD (token);
+
+ /* Aliases are expanded iff EXPAND_ALIASES is non-zero, and quoting
+ inhibits alias expansion. */
+ if (expand_aliases && quoted == 0)
+ {
+ result = alias_expand_token (token);
+ if (result == RE_READ_TOKEN)
+ return (RE_READ_TOKEN);
+ else if (result == NO_EXPANSION)
+ parser_state &= ~PST_ALEXPNEXT;
+ }
+
+ /* If not in Posix.2 mode, check for reserved words after alias
+ expansion. */
+ if MBTEST(posixly_correct == 0)
+#endif
+ CHECK_FOR_RESERVED_WORD (token);
+
+ the_word = (WORD_DESC *)xmalloc (sizeof (WORD_DESC));
+ the_word->word = (char *)xmalloc (1 + token_index);
+ the_word->flags = 0;
+ strcpy (the_word->word, token);
+ if (dollar_present)
+ the_word->flags |= W_HASDOLLAR;
+ if (quoted)
+ the_word->flags |= W_QUOTED; /*(*/
+ if (compound_assignment && token[token_index-1] == ')')
+ the_word->flags |= W_COMPASSIGN;
+ /* A word is an assignment if it appears at the beginning of a
+ simple command, or after another assignment word. This is
+ context-dependent, so it cannot be handled in the grammar. */
+ if (assignment (token, (parser_state & PST_COMPASSIGN) != 0))
+ {
+ the_word->flags |= W_ASSIGNMENT;
+ /* Don't perform word splitting on assignment statements. */
+ if (assignment_acceptable (last_read_token) || (parser_state & PST_COMPASSIGN) != 0)
+ the_word->flags |= W_NOSPLIT;
+ }
+
+ if (command_token_position (last_read_token))
+ {
+ struct builtin *b;
+ b = builtin_address_internal (token, 0);
+ if (b && (b->flags & ASSIGNMENT_BUILTIN))
+ parser_state |= PST_ASSIGNOK;
+ else if (STREQ (token, "eval") || STREQ (token, "let"))
+ parser_state |= PST_ASSIGNOK;
+ }
+
+ yylval.word = the_word;
+
+ if (token[0] == '{' && token[token_index-1] == '}' &&
+ (character == '<' || character == '>'))
+ {
+ /* can use token; already copied to the_word */
+ token[token_index-1] = '\0';
+ if (legal_identifier (token+1))
+ {
+ strcpy (the_word->word, token+1);
+/*itrace("read_token_word: returning REDIR_WORD for %s", the_word->word);*/
+ return (REDIR_WORD);
+ }
+ }
+
+ result = ((the_word->flags & (W_ASSIGNMENT|W_NOSPLIT)) == (W_ASSIGNMENT|W_NOSPLIT))
+ ? ASSIGNMENT_WORD : WORD;
+
+ switch (last_read_token)
+ {
+ case FUNCTION:
+ parser_state |= PST_ALLOWOPNBRC;
+ function_dstart = line_number;
+ break;
+ case CASE:
+ case SELECT:
+ case FOR:
+ if (word_top < MAX_CASE_NEST)
+ word_top++;
+ word_lineno[word_top] = line_number;
+ break;
+ }
+
+ return (result);
+}
+
+/* Return 1 if TOKSYM is a token that after being read would allow
+ a reserved word to be seen, else 0. */
+static int
+reserved_word_acceptable (toksym)
+ int toksym;
+{
+ switch (toksym)
+ {
+ case '\n':
+ case ';':
+ case '(':
+ case ')':
+ case '|':
+ case '&':
+ case '{':
+ case '}': /* XXX */
+ case AND_AND:
+ case BANG:
+ case BAR_AND:
+ case DO:
+ case DONE:
+ case ELIF:
+ case ELSE:
+ case ESAC:
+ case FI:
+ case IF:
+ case OR_OR:
+ case SEMI_SEMI:
+ case SEMI_AND:
+ case SEMI_SEMI_AND:
+ case THEN:
+ case TIME:
+ case TIMEOPT:
+ case COPROC:
+ case UNTIL:
+ case WHILE:
+ case 0:
+ return 1;
+ default:
+#if defined (COPROCESS_SUPPORT)
+ if (last_read_token == WORD && token_before_that == COPROC)
+ return 1;
+#endif
+ return 0;
+ }
+}
+
+/* Return the index of TOKEN in the alist of reserved words, or -1 if
+ TOKEN is not a shell reserved word. */
+int
+find_reserved_word (tokstr)
+ char *tokstr;
+{
+ int i;
+ for (i = 0; word_token_alist[i].word; i++)
+ if (STREQ (tokstr, word_token_alist[i].word))
+ return i;
+ return -1;
+}
+
+#if 0
+#if defined (READLINE)
+/* Called after each time readline is called. This insures that whatever
+ the new prompt string is gets propagated to readline's local prompt
+ variable. */
+static void
+reset_readline_prompt ()
+{
+ char *temp_prompt;
+
+ if (prompt_string_pointer)
+ {
+ temp_prompt = (*prompt_string_pointer)
+ ? decode_prompt_string (*prompt_string_pointer)
+ : (char *)NULL;
+
+ if (temp_prompt == 0)
+ {
+ temp_prompt = (char *)xmalloc (1);
+ temp_prompt[0] = '\0';
+ }
+
+ FREE (current_readline_prompt);
+ current_readline_prompt = temp_prompt;
+ }
+}
+#endif /* READLINE */
+#endif /* 0 */
+
+#if defined (HISTORY)
+/* A list of tokens which can be followed by newlines, but not by
+ semi-colons. When concatenating multiple lines of history, the
+ newline separator for such tokens is replaced with a space. */
+static const int no_semi_successors[] = {
+ '\n', '{', '(', ')', ';', '&', '|',
+ CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
+ WHILE, AND_AND, OR_OR, IN,
+ 0
+};
+
+/* If we are not within a delimited expression, try to be smart
+ about which separators can be semi-colons and which must be
+ newlines. Returns the string that should be added into the
+ history entry. */
+char *
+history_delimiting_chars ()
+{
+ register int i;
+
+ if (dstack.delimiter_depth != 0)
+ return ("\n");
+
+ /* We look for current_command_line_count == 2 because we are looking to
+ add the first line of the body of the here document (the second line
+ of the command). */
+ if (parser_state & PST_HEREDOC)
+ return (current_command_line_count == 2 ? "\n" : "");
+
+ /* First, handle some special cases. */
+ /*(*/
+ /* If we just read `()', assume it's a function definition, and don't
+ add a semicolon. If the token before the `)' was not `(', and we're
+ not in the midst of parsing a case statement, assume it's a
+ parenthesized command and add the semicolon. */
+ /*)(*/
+ if (token_before_that == ')')
+ {
+ if (two_tokens_ago == '(') /*)*/ /* function def */
+ return " ";
+ /* This does not work for subshells inside case statement
+ command lists. It's a suboptimal solution. */
+ else if (parser_state & PST_CASESTMT) /* case statement pattern */
+ return " ";
+ else
+ return "; "; /* (...) subshell */
+ }
+ else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
+ return " "; /* function def using `function name' without `()' */
+
+ else if (token_before_that == WORD && two_tokens_ago == FOR)
+ {
+ /* Tricky. `for i\nin ...' should not have a semicolon, but
+ `for i\ndo ...' should. We do what we can. */
+ for (i = shell_input_line_index; whitespace (shell_input_line[i]); i++)
+ ;
+ if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n')
+ return " ";
+ return ";";
+ }
+ else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))
+ return " ";
+
+ for (i = 0; no_semi_successors[i]; i++)
+ {
+ if (token_before_that == no_semi_successors[i])
+ return (" ");
+ }
+
+ return ("; ");
+}
+#endif /* HISTORY */
+
+/* Issue a prompt, or prepare to issue a prompt when the next character
+ is read. */
+static void
+prompt_again ()
+{
+ char *temp_prompt;
+
+ if (interactive == 0 || expanding_alias ()) /* XXX */
+ return;
+
+ ps1_prompt = get_string_value ("PS1");
+ ps2_prompt = get_string_value ("PS2");
+
+ if (!prompt_string_pointer)
+ prompt_string_pointer = &ps1_prompt;
+
+ temp_prompt = *prompt_string_pointer
+ ? decode_prompt_string (*prompt_string_pointer)
+ : (char *)NULL;
+
+ if (temp_prompt == 0)
+ {
+ temp_prompt = (char *)xmalloc (1);
+ temp_prompt[0] = '\0';
+ }
+
+ current_prompt_string = *prompt_string_pointer;
+ prompt_string_pointer = &ps2_prompt;
+
+#if defined (READLINE)
+ if (!no_line_editing)
+ {
+ FREE (current_readline_prompt);
+ current_readline_prompt = temp_prompt;
+ }
+ else
+#endif /* READLINE */
+ {
+ FREE (current_decoded_prompt);
+ current_decoded_prompt = temp_prompt;
+ }
+}
+
+int
+get_current_prompt_level ()
+{
+ return ((current_prompt_string && current_prompt_string == ps2_prompt) ? 2 : 1);
+}
+
+void
+set_current_prompt_level (x)
+ int x;
+{
+ prompt_string_pointer = (x == 2) ? &ps2_prompt : &ps1_prompt;
+ current_prompt_string = *prompt_string_pointer;
+}
+
+static void
+print_prompt ()
+{
+ fprintf (stderr, "%s", current_decoded_prompt);
+ fflush (stderr);
+}
+
+/* Return a string which will be printed as a prompt. The string
+ may contain special characters which are decoded as follows:
+
+ \a bell (ascii 07)
+ \d the date in Day Mon Date format
+ \e escape (ascii 033)
+ \h the hostname up to the first `.'
+ \H the hostname
+ \j the number of active jobs
+ \l the basename of the shell's tty device name
+ \n CRLF
+ \r CR
+ \s the name of the shell
+ \t the time in 24-hour hh:mm:ss format
+ \T the time in 12-hour hh:mm:ss format
+ \@ the time in 12-hour hh:mm am/pm format
+ \A the time in 24-hour hh:mm format
+ \D{fmt} the result of passing FMT to strftime(3)
+ \u your username
+ \v the version of bash (e.g., 2.00)
+ \V the release of bash, version + patchlevel (e.g., 2.00.0)
+ \w the current working directory
+ \W the last element of $PWD
+ \! the history number of this command
+ \# the command number of this command
+ \$ a $ or a # if you are root
+ \nnn character code nnn in octal
+ \\ a backslash
+ \[ begin a sequence of non-printing chars
+ \] end a sequence of non-printing chars
+*/
+#define PROMPT_GROWTH 48
+char *
+decode_prompt_string (string)
+ char *string;
+{
+ WORD_LIST *list;
+ char *result, *t;
+ struct dstack save_dstack;
+ int last_exit_value, last_comsub_pid;
+#if defined (PROMPT_STRING_DECODE)
+ int result_size, result_index;
+ int c, n, i;
+ char *temp, octal_string[4];
+ struct tm *tm;
+ time_t the_time;
+ char timebuf[128];
+ char *timefmt;
+
+ result = (char *)xmalloc (result_size = PROMPT_GROWTH);
+ result[result_index = 0] = 0;
+ temp = (char *)NULL;
+
+ while (c = *string++)
+ {
+ if (posixly_correct && c == '!')
+ {
+ if (*string == '!')
+ {
+ temp = savestring ("!");
+ goto add_string;
+ }
+ else
+ {
+#if !defined (HISTORY)
+ temp = savestring ("1");
+#else /* HISTORY */
+ temp = itos (history_number ());
+#endif /* HISTORY */
+ string--; /* add_string increments string again. */
+ goto add_string;
+ }
+ }
+ if (c == '\\')
+ {
+ c = *string;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ strncpy (octal_string, string, 3);
+ octal_string[3] = '\0';
+
+ n = read_octal (octal_string);
+ temp = (char *)xmalloc (3);
+
+ if (n == CTLESC || n == CTLNUL)
+ {
+ temp[0] = CTLESC;
+ temp[1] = n;
+ temp[2] = '\0';
+ }
+ else if (n == -1)
+ {
+ temp[0] = '\\';
+ temp[1] = '\0';
+ }
+ else
+ {
+ temp[0] = n;
+ temp[1] = '\0';
+ }
+
+ for (c = 0; n != -1 && c < 3 && ISOCTAL (*string); c++)
+ string++;
+
+ c = 0; /* tested at add_string: */
+ goto add_string;
+
+ case 'd':
+ case 't':
+ case 'T':
+ case '@':
+ case 'A':
+ /* Make the current time/date into a string. */
+ (void) time (&the_time);
+ tm = localtime (&the_time);
+
+ if (c == 'd')
+ n = strftime (timebuf, sizeof (timebuf), "%a %b %d", tm);
+ else if (c == 't')
+ n = strftime (timebuf, sizeof (timebuf), "%H:%M:%S", tm);
+ else if (c == 'T')
+ n = strftime (timebuf, sizeof (timebuf), "%I:%M:%S", tm);
+ else if (c == '@')
+ n = strftime (timebuf, sizeof (timebuf), "%I:%M %p", tm);
+ else if (c == 'A')
+ n = strftime (timebuf, sizeof (timebuf), "%H:%M", tm);
+
+ if (n == 0)
+ timebuf[0] = '\0';
+ else
+ timebuf[sizeof(timebuf) - 1] = '\0';
+
+ temp = savestring (timebuf);
+ goto add_string;
+
+ case 'D': /* strftime format */
+ if (string[1] != '{') /* } */
+ goto not_escape;
+
+ (void) time (&the_time);
+ tm = localtime (&the_time);
+ string += 2; /* skip { */
+ timefmt = xmalloc (strlen (string) + 3);
+ for (t = timefmt; *string && *string != '}'; )
+ *t++ = *string++;
+ *t = '\0';
+ c = *string; /* tested at add_string */
+ if (timefmt[0] == '\0')
+ {
+ timefmt[0] = '%';
+ timefmt[1] = 'X'; /* locale-specific current time */
+ timefmt[2] = '\0';
+ }
+ n = strftime (timebuf, sizeof (timebuf), timefmt, tm);
+ free (timefmt);
+
+ if (n == 0)
+ timebuf[0] = '\0';
+ else
+ timebuf[sizeof(timebuf) - 1] = '\0';
+
+ if (promptvars || posixly_correct)
+ /* Make sure that expand_prompt_string is called with a
+ second argument of Q_DOUBLE_QUOTES if we use this
+ function here. */
+ temp = sh_backslash_quote_for_double_quotes (timebuf);
+ else
+ temp = savestring (timebuf);
+ goto add_string;
+
+ case 'n':
+ temp = (char *)xmalloc (3);
+ temp[0] = no_line_editing ? '\n' : '\r';
+ temp[1] = no_line_editing ? '\0' : '\n';
+ temp[2] = '\0';
+ goto add_string;
+
+ case 's':
+ temp = base_pathname (shell_name);
+ temp = savestring (temp);
+ goto add_string;
+
+ case 'v':
+ case 'V':
+ temp = (char *)xmalloc (16);
+ if (c == 'v')
+ strcpy (temp, dist_version);
+ else
+ sprintf (temp, "%s.%d", dist_version, patch_level);
+ goto add_string;
+
+ case 'w':
+ case 'W':
+ {
+ /* Use the value of PWD because it is much more efficient. */
+ char t_string[PATH_MAX];
+ int tlen;
+
+ temp = get_string_value ("PWD");
+
+ if (temp == 0)
+ {
+ if (getcwd (t_string, sizeof(t_string)) == 0)
+ {
+ t_string[0] = '.';
+ tlen = 1;
+ }
+ else
+ tlen = strlen (t_string);
+ }
+ else
+ {
+ tlen = sizeof (t_string) - 1;
+ strncpy (t_string, temp, tlen);
+ }
+ t_string[tlen] = '\0';
+
+#if defined (MACOSX)
+ /* Convert from "fs" format to "input" format */
+ temp = fnx_fromfs (t_string, strlen (t_string));
+ if (temp != t_string)
+ strcpy (t_string, temp);
+#endif
+
+#define ROOT_PATH(x) ((x)[0] == '/' && (x)[1] == 0)
+#define DOUBLE_SLASH_ROOT(x) ((x)[0] == '/' && (x)[1] == '/' && (x)[2] == 0)
+ /* Abbreviate \W as ~ if $PWD == $HOME */
+ if (c == 'W' && (((t = get_string_value ("HOME")) == 0) || STREQ (t, t_string) == 0))
+ {
+ if (ROOT_PATH (t_string) == 0 && DOUBLE_SLASH_ROOT (t_string) == 0)
+ {
+ t = strrchr (t_string, '/');
+ if (t)
+ strcpy (t_string, t + 1);
+ }
+ }
+#undef ROOT_PATH
+#undef DOUBLE_SLASH_ROOT
+ else
+ /* polite_directory_format is guaranteed to return a string
+ no longer than PATH_MAX - 1 characters. */
+ strcpy (t_string, polite_directory_format (t_string));
+
+ temp = trim_pathname (t_string, PATH_MAX - 1);
+ /* If we're going to be expanding the prompt string later,
+ quote the directory name. */
+ if (promptvars || posixly_correct)
+ /* Make sure that expand_prompt_string is called with a
+ second argument of Q_DOUBLE_QUOTES if we use this
+ function here. */
+ temp = sh_backslash_quote_for_double_quotes (t_string);
+ else
+ temp = savestring (t_string);
+
+ goto add_string;
+ }
+
+ case 'u':
+ if (current_user.user_name == 0)
+ get_current_user_info ();
+ temp = savestring (current_user.user_name);
+ goto add_string;
+
+ case 'h':
+ case 'H':
+ temp = savestring (current_host_name);
+ if (c == 'h' && (t = (char *)strchr (temp, '.')))
+ *t = '\0';
+ goto add_string;
+
+ case '#':
+ temp = itos (current_command_number);
+ goto add_string;
+
+ case '!':
+#if !defined (HISTORY)
+ temp = savestring ("1");
+#else /* HISTORY */
+ temp = itos (history_number ());
+#endif /* HISTORY */
+ goto add_string;
+
+ case '$':
+ t = temp = (char *)xmalloc (3);
+ if ((promptvars || posixly_correct) && (current_user.euid != 0))
+ *t++ = '\\';
+ *t++ = current_user.euid == 0 ? '#' : '$';
+ *t = '\0';
+ goto add_string;
+
+ case 'j':
+ temp = itos (count_all_jobs ());
+ goto add_string;
+
+ case 'l':
+#if defined (HAVE_TTYNAME)
+ temp = (char *)ttyname (fileno (stdin));
+ t = temp ? base_pathname (temp) : "tty";
+ temp = savestring (t);
+#else
+ temp = savestring ("tty");
+#endif /* !HAVE_TTYNAME */
+ goto add_string;
+
+#if defined (READLINE)
+ case '[':
+ case ']':
+ if (no_line_editing)
+ {
+ string++;
+ break;
+ }
+ temp = (char *)xmalloc (3);
+ n = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
+ i = 0;
+ if (n == CTLESC || n == CTLNUL)
+ temp[i++] = CTLESC;
+ temp[i++] = n;
+ temp[i] = '\0';
+ goto add_string;
+#endif /* READLINE */
+
+ case '\\':
+ case 'a':
+ case 'e':
+ case 'r':
+ temp = (char *)xmalloc (2);
+ if (c == 'a')
+ temp[0] = '\07';
+ else if (c == 'e')
+ temp[0] = '\033';
+ else if (c == 'r')
+ temp[0] = '\r';
+ else /* (c == '\\') */
+ temp[0] = c;
+ temp[1] = '\0';
+ goto add_string;
+
+ default:
+not_escape:
+ temp = (char *)xmalloc (3);
+ temp[0] = '\\';
+ temp[1] = c;
+ temp[2] = '\0';
+
+ add_string:
+ if (c)
+ string++;
+ result =
+ sub_append_string (temp, result, &result_index, &result_size);
+ temp = (char *)NULL; /* Freed in sub_append_string (). */
+ result[result_index] = '\0';
+ break;
+ }
+ }
+ else
+ {
+ RESIZE_MALLOCED_BUFFER (result, result_index, 3, result_size, PROMPT_GROWTH);
+ result[result_index++] = c;
+ result[result_index] = '\0';
+ }
+ }
+#else /* !PROMPT_STRING_DECODE */
+ result = savestring (string);
+#endif /* !PROMPT_STRING_DECODE */
+
+ /* Save the delimiter stack and point `dstack' to temp space so any
+ command substitutions in the prompt string won't result in screwing
+ up the parser's quoting state. */
+ save_dstack = dstack;
+ dstack = temp_dstack;
+ dstack.delimiter_depth = 0;
+
+ /* Perform variable and parameter expansion and command substitution on
+ the prompt string. */
+ if (promptvars || posixly_correct)
+ {
+ last_exit_value = last_command_exit_value;
+ last_comsub_pid = last_command_subst_pid;
+ list = expand_prompt_string (result, Q_DOUBLE_QUOTES, 0);
+ free (result);
+ result = string_list (list);
+ dispose_words (list);
+ last_command_exit_value = last_exit_value;
+ last_command_subst_pid = last_comsub_pid;
+ }
+ else
+ {
+ t = dequote_string (result);
+ free (result);
+ result = t;
+ }
+
+ dstack = save_dstack;
+
+ return (result);
+}
+
+/************************************************
+ * *
+ * ERROR HANDLING *
+ * *
+ ************************************************/
+
+/* Report a syntax error, and restart the parser. Call here for fatal
+ errors. */
+int
+yyerror (msg)
+ const char *msg;
+{
+ report_syntax_error ((char *)NULL);
+ reset_parser ();
+ return (0);
+}
+
+static char *
+error_token_from_token (tok)
+ int tok;
+{
+ char *t;
+
+ if (t = find_token_in_alist (tok, word_token_alist, 0))
+ return t;
+
+ if (t = find_token_in_alist (tok, other_token_alist, 0))
+ return t;
+
+ t = (char *)NULL;
+ /* This stuff is dicy and needs closer inspection */
+ switch (current_token)
+ {
+ case WORD:
+ case ASSIGNMENT_WORD:
+ if (yylval.word)
+ t = savestring (yylval.word->word);
+ break;
+ case NUMBER:
+ t = itos (yylval.number);
+ break;
+ case ARITH_CMD:
+ if (yylval.word_list)
+ t = string_list (yylval.word_list);
+ break;
+ case ARITH_FOR_EXPRS:
+ if (yylval.word_list)
+ t = string_list_internal (yylval.word_list, " ; ");
+ break;
+ case COND_CMD:
+ t = (char *)NULL; /* punt */
+ break;
+ }
+
+ return t;
+}
+
+static char *
+error_token_from_text ()
+{
+ char *msg, *t;
+ int token_end, i;
+
+ t = shell_input_line;
+ i = shell_input_line_index;
+ token_end = 0;
+ msg = (char *)NULL;
+
+ if (i && t[i] == '\0')
+ i--;
+
+ while (i && (whitespace (t[i]) || t[i] == '\n'))
+ i--;
+
+ if (i)
+ token_end = i + 1;
+
+ while (i && (member (t[i], " \n\t;|&") == 0))
+ i--;
+
+ while (i != token_end && (whitespace (t[i]) || t[i] == '\n'))
+ i++;
+
+ /* Return our idea of the offending token. */
+ if (token_end || (i == 0 && token_end == 0))
+ {
+ if (token_end)
+ msg = substring (t, i, token_end);
+ else /* one-character token */
+ {
+ msg = (char *)xmalloc (2);
+ msg[0] = t[i];
+ msg[1] = '\0';
+ }
+ }
+
+ return (msg);
+}
+
+static void
+print_offending_line ()
+{
+ char *msg;
+ int token_end;
+
+ msg = savestring (shell_input_line);
+ token_end = strlen (msg);
+ while (token_end && msg[token_end - 1] == '\n')
+ msg[--token_end] = '\0';
+
+ parser_error (line_number, "`%s'", msg);
+ free (msg);
+}
+
+/* Report a syntax error with line numbers, etc.
+ Call here for recoverable errors. If you have a message to print,
+ then place it in MESSAGE, otherwise pass NULL and this will figure
+ out an appropriate message for you. */
+static void
+report_syntax_error (message)
+ char *message;
+{
+ char *msg;
+
+ if (message)
+ {
+ parser_error (line_number, "%s", message);
+ if (interactive && EOF_Reached)
+ EOF_Reached = 0;
+ last_command_exit_value = parse_and_execute_level ? EX_BADSYNTAX : EX_BADUSAGE;
+ return;
+ }
+
+ /* If the line of input we're reading is not null, try to find the
+ objectionable token. First, try to figure out what token the
+ parser's complaining about by looking at current_token. */
+ if (current_token != 0 && EOF_Reached == 0 && (msg = error_token_from_token (current_token)))
+ {
+ parser_error (line_number, _("syntax error near unexpected token `%s'"), msg);
+ free (msg);
+
+ if (interactive == 0)
+ print_offending_line ();
+
+ last_command_exit_value = parse_and_execute_level ? EX_BADSYNTAX : EX_BADUSAGE;
+ return;
+ }
+
+ /* If looking at the current token doesn't prove fruitful, try to find the
+ offending token by analyzing the text of the input line near the current
+ input line index and report what we find. */
+ if (shell_input_line && *shell_input_line)
+ {
+ msg = error_token_from_text ();
+ if (msg)
+ {
+ parser_error (line_number, _("syntax error near `%s'"), msg);
+ free (msg);
+ }
+
+ /* If not interactive, print the line containing the error. */
+ if (interactive == 0)
+ print_offending_line ();
+ }
+ else
+ {
+ msg = EOF_Reached ? _("syntax error: unexpected end of file") : _("syntax error");
+ parser_error (line_number, "%s", msg);
+ /* When the shell is interactive, this file uses EOF_Reached
+ only for error reporting. Other mechanisms are used to
+ decide whether or not to exit. */
+ if (interactive && EOF_Reached)
+ EOF_Reached = 0;
+ }
+
+ last_command_exit_value = parse_and_execute_level ? EX_BADSYNTAX : EX_BADUSAGE;
+}
+
+/* ??? Needed function. ??? We have to be able to discard the constructs
+ created during parsing. In the case of error, we want to return
+ allocated objects to the memory pool. In the case of no error, we want
+ to throw away the information about where the allocated objects live.
+ (dispose_command () will actually free the command.) */
+static void
+discard_parser_constructs (error_p)
+ int error_p;
+{
+}
+
+/************************************************
+ * *
+ * EOF HANDLING *
+ * *
+ ************************************************/
+
+/* Do that silly `type "bye" to exit' stuff. You know, "ignoreeof". */
+
+/* A flag denoting whether or not ignoreeof is set. */
+int ignoreeof = 0;
+
+/* The number of times that we have encountered an EOF character without
+ another character intervening. When this gets above the limit, the
+ shell terminates. */
+int eof_encountered = 0;
+
+/* The limit for eof_encountered. */
+int eof_encountered_limit = 10;
+
+/* If we have EOF as the only input unit, this user wants to leave
+ the shell. If the shell is not interactive, then just leave.
+ Otherwise, if ignoreeof is set, and we haven't done this the
+ required number of times in a row, print a message. */
+static void
+handle_eof_input_unit ()
+{
+ if (interactive)
+ {
+ /* shell.c may use this to decide whether or not to write out the
+ history, among other things. We use it only for error reporting
+ in this file. */
+ if (EOF_Reached)
+ EOF_Reached = 0;
+
+ /* If the user wants to "ignore" eof, then let her do so, kind of. */
+ if (ignoreeof)
+ {
+ if (eof_encountered < eof_encountered_limit)
+ {
+ fprintf (stderr, _("Use \"%s\" to leave the shell.\n"),
+ login_shell ? "logout" : "exit");
+ eof_encountered++;
+ /* Reset the parsing state. */
+ last_read_token = current_token = '\n';
+ /* Reset the prompt string to be $PS1. */
+ prompt_string_pointer = (char **)NULL;
+ prompt_again ();
+ return;
+ }
+ }
+
+ /* In this case EOF should exit the shell. Do it now. */
+ reset_parser ();
+ exit_builtin ((WORD_LIST *)NULL);
+ }
+ else
+ {
+ /* We don't write history files, etc., for non-interactive shells. */
+ EOF_Reached = 1;
+ }
+}
+
+/************************************************
+ * *
+ * STRING PARSING FUNCTIONS *
+ * *
+ ************************************************/
+
+/* It's very important that these two functions treat the characters
+ between ( and ) identically. */
+
+static WORD_LIST parse_string_error;
+
+/* Take a string and run it through the shell parser, returning the
+ resultant word list. Used by compound array assignment. */
+WORD_LIST *
+parse_string_to_word_list (s, flags, whom)
+ char *s;
+ int flags;
+ const char *whom;
+{
+ WORD_LIST *wl;
+ int tok, orig_current_token, orig_line_number, orig_input_terminator;
+ int orig_line_count;
+ int old_echo_input, old_expand_aliases;
+#if defined (HISTORY)
+ int old_remember_on_history, old_history_expansion_inhibited;
+#endif
+
+#if defined (HISTORY)
+ old_remember_on_history = remember_on_history;
+# if defined (BANG_HISTORY)
+ old_history_expansion_inhibited = history_expansion_inhibited;
+# endif
+ bash_history_disable ();
+#endif
+
+ orig_line_number = line_number;
+ orig_line_count = current_command_line_count;
+ orig_input_terminator = shell_input_line_terminator;
+ old_echo_input = echo_input_at_read;
+ old_expand_aliases = expand_aliases;
+
+ push_stream (1);
+ last_read_token = WORD; /* WORD to allow reserved words here */
+ current_command_line_count = 0;
+ echo_input_at_read = expand_aliases = 0;
+
+ with_input_from_string (s, whom);
+ wl = (WORD_LIST *)NULL;
+
+ if (flags & 1)
+ parser_state |= PST_COMPASSIGN|PST_REPARSE;
+
+ while ((tok = read_token (READ)) != yacc_EOF)
+ {
+ if (tok == '\n' && *bash_input.location.string == '\0')
+ break;
+ if (tok == '\n') /* Allow newlines in compound assignments */
+ continue;
+ if (tok != WORD && tok != ASSIGNMENT_WORD)
+ {
+ line_number = orig_line_number + line_number - 1;
+ orig_current_token = current_token;
+ current_token = tok;
+ yyerror (NULL); /* does the right thing */
+ current_token = orig_current_token;
+ if (wl)
+ dispose_words (wl);
+ wl = &parse_string_error;
+ break;
+ }
+ wl = make_word_list (yylval.word, wl);
+ }
+
+ last_read_token = '\n';
+ pop_stream ();
+
+#if defined (HISTORY)
+ remember_on_history = old_remember_on_history;
+# if defined (BANG_HISTORY)
+ history_expansion_inhibited = old_history_expansion_inhibited;
+# endif /* BANG_HISTORY */
+#endif /* HISTORY */
+
+ echo_input_at_read = old_echo_input;
+ expand_aliases = old_expand_aliases;
+
+ current_command_line_count = orig_line_count;
+ shell_input_line_terminator = orig_input_terminator;
+
+ if (flags & 1)
+ parser_state &= ~(PST_COMPASSIGN|PST_REPARSE);
+
+ if (wl == &parse_string_error)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ if (interactive_shell == 0 && posixly_correct)
+ jump_to_top_level (FORCE_EOF);
+ else
+ jump_to_top_level (DISCARD);
+ }
+
+ return (REVERSE_LIST (wl, WORD_LIST *));
+}
+
+static char *
+parse_compound_assignment (retlenp)
+ int *retlenp;
+{
+ WORD_LIST *wl, *rl;
+ int tok, orig_line_number, orig_token_size, orig_last_token, assignok;
+ char *saved_token, *ret;
+
+ saved_token = token;
+ orig_token_size = token_buffer_size;
+ orig_line_number = line_number;
+ orig_last_token = last_read_token;
+
+ last_read_token = WORD; /* WORD to allow reserved words here */
+
+ token = (char *)NULL;
+ token_buffer_size = 0;
+
+ assignok = parser_state&PST_ASSIGNOK; /* XXX */
+
+ wl = (WORD_LIST *)NULL; /* ( */
+ parser_state |= PST_COMPASSIGN;
+
+ while ((tok = read_token (READ)) != ')')
+ {
+ if (tok == '\n') /* Allow newlines in compound assignments */
+ {
+ if (SHOULD_PROMPT ())
+ prompt_again ();
+ continue;
+ }
+ if (tok != WORD && tok != ASSIGNMENT_WORD)
+ {
+ current_token = tok; /* for error reporting */
+ if (tok == yacc_EOF) /* ( */
+ parser_error (orig_line_number, _("unexpected EOF while looking for matching `)'"));
+ else
+ yyerror(NULL); /* does the right thing */
+ if (wl)
+ dispose_words (wl);
+ wl = &parse_string_error;
+ break;
+ }
+ wl = make_word_list (yylval.word, wl);
+ }
+
+ FREE (token);
+ token = saved_token;
+ token_buffer_size = orig_token_size;
+
+ parser_state &= ~PST_COMPASSIGN;
+
+ if (wl == &parse_string_error)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ last_read_token = '\n'; /* XXX */
+ if (interactive_shell == 0 && posixly_correct)
+ jump_to_top_level (FORCE_EOF);
+ else
+ jump_to_top_level (DISCARD);
+ }
+
+ last_read_token = orig_last_token; /* XXX - was WORD? */
+
+ if (wl)
+ {
+ rl = REVERSE_LIST (wl, WORD_LIST *);
+ ret = string_list (rl);
+ dispose_words (rl);
+ }
+ else
+ ret = (char *)NULL;
+
+ if (retlenp)
+ *retlenp = (ret && *ret) ? strlen (ret) : 0;
+
+ if (assignok)
+ parser_state |= PST_ASSIGNOK;
+
+ return ret;
+}
+
+/************************************************
+ * *
+ * SAVING AND RESTORING PARTIAL PARSE STATE *
+ * *
+ ************************************************/
+
+sh_parser_state_t *
+save_parser_state (ps)
+ sh_parser_state_t *ps;
+{
+ if (ps == 0)
+ ps = (sh_parser_state_t *)xmalloc (sizeof (sh_parser_state_t));
+ if (ps == 0)
+ return ((sh_parser_state_t *)NULL);
+
+ ps->parser_state = parser_state;
+ ps->token_state = save_token_state ();
+
+ ps->input_line_terminator = shell_input_line_terminator;
+ ps->eof_encountered = eof_encountered;
+
+ ps->current_command_line_count = current_command_line_count;
+
+#if defined (HISTORY)
+ ps->remember_on_history = remember_on_history;
+# if defined (BANG_HISTORY)
+ ps->history_expansion_inhibited = history_expansion_inhibited;
+# endif
+#endif
+
+ ps->last_command_exit_value = last_command_exit_value;
+#if defined (ARRAY_VARS)
+ ps->pipestatus = save_pipestatus_array ();
+#endif
+
+ ps->last_shell_builtin = last_shell_builtin;
+ ps->this_shell_builtin = this_shell_builtin;
+
+ ps->expand_aliases = expand_aliases;
+ ps->echo_input_at_read = echo_input_at_read;
+
+ return (ps);
+}
+
+void
+restore_parser_state (ps)
+ sh_parser_state_t *ps;
+{
+ if (ps == 0)
+ return;
+
+ parser_state = ps->parser_state;
+ if (ps->token_state)
+ {
+ restore_token_state (ps->token_state);
+ free (ps->token_state);
+ }
+
+ shell_input_line_terminator = ps->input_line_terminator;
+ eof_encountered = ps->eof_encountered;
+
+ current_command_line_count = ps->current_command_line_count;
+
+#if defined (HISTORY)
+ remember_on_history = ps->remember_on_history;
+# if defined (BANG_HISTORY)
+ history_expansion_inhibited = ps->history_expansion_inhibited;
+# endif
+#endif
+
+ last_command_exit_value = ps->last_command_exit_value;
+#if defined (ARRAY_VARS)
+ restore_pipestatus_array (ps->pipestatus);
+#endif
+
+ last_shell_builtin = ps->last_shell_builtin;
+ this_shell_builtin = ps->this_shell_builtin;
+
+ expand_aliases = ps->expand_aliases;
+ echo_input_at_read = ps->echo_input_at_read;
+}
+
+/************************************************
+ * *
+ * MULTIBYTE CHARACTER HANDLING *
+ * *
+ ************************************************/
+
+#if defined (HANDLE_MULTIBYTE)
+static void
+set_line_mbstate ()
+{
+ int i, previ, len, c;
+ mbstate_t mbs, prevs;
+ size_t mbclen;
+
+ if (shell_input_line == NULL)
+ return;
+ len = strlen (shell_input_line); /* XXX - shell_input_line_len ? */
+ FREE (shell_input_line_property);
+ shell_input_line_property = (char *)xmalloc (len + 1);
+
+ memset (&prevs, '\0', sizeof (mbstate_t));
+ for (i = previ = 0; i < len; i++)
+ {
+ mbs = prevs;
+
+ c = shell_input_line[i];
+ if (c == EOF)
+ {
+ int j;
+ for (j = i; j < len; j++)
+ shell_input_line_property[j] = 1;
+ break;
+ }
+
+ mbclen = mbrlen (shell_input_line + previ, i - previ + 1, &mbs);
+ if (mbclen == 1 || mbclen == (size_t)-1)
+ {
+ mbclen = 1;
+ previ = i + 1;
+ }
+ else if (mbclen == (size_t)-2)
+ mbclen = 0;
+ else if (mbclen > 1)
+ {
+ mbclen = 0;
+ previ = i + 1;
+ prevs = mbs;
+ }
+ else
+ {
+ /* XXX - what to do if mbrlen returns 0? (null wide character) */
+ int j;
+ for (j = i; j < len; j++)
+ shell_input_line_property[j] = 1;
+ break;
+ }
+
+ shell_input_line_property[i] = mbclen;
+ }
+}
+#endif /* HANDLE_MULTIBYTE */
diff --git a/parse.y~ b/parse.y~
index 00dd12a4..eea5d1fb 100644
--- a/parse.y~
+++ b/parse.y~
@@ -2305,7 +2305,7 @@ shell_getc (remove_quoted_newline)
else
{
char *hdcs;
- hdcs = history_delimiting_chars ();
+ hdcs = history_delimiting_chars (shell_input_line);
if (hdcs && hdcs[0] == ';')
maybe_add_history (shell_input_line);
}
@@ -3062,12 +3062,13 @@ tokword:
* reprompting the user, if necessary, after reading a newline, and returning
* correct error values if it reads EOF.
*/
-#define P_FIRSTCLOSE 0x01
-#define P_ALLOWESC 0x02
-#define P_DQUOTE 0x04
-#define P_COMMAND 0x08 /* parsing a command, so look for comments */
-#define P_BACKQUOTE 0x10 /* parsing a backquoted command substitution */
-#define P_ARRAYSUB 0x20 /* parsing a [...] array subscript for assignment */
+#define P_FIRSTCLOSE 0x0001
+#define P_ALLOWESC 0x0002
+#define P_DQUOTE 0x0004
+#define P_COMMAND 0x0008 /* parsing a command, so look for comments */
+#define P_BACKQUOTE 0x0010 /* parsing a backquoted command substitution */
+#define P_ARRAYSUB 0x0020 /* parsing a [...] array subscript for assignment */
+#define P_DOLBRACE 0x0040 /* parsing a ${...} construct */
/* Lexical state while parsing a grouping construct or $(...). */
#define LEX_WASDOL 0x001
@@ -3115,6 +3116,9 @@ parse_matched_pair (qc, open, close, lenp, flags)
int nestlen, ttranslen, start_lineno;
char *ret, *nestret, *ttrans;
int retind, retsize, rflags;
+ int dolbrace_state;
+
+ dolbrace_state = (flags & P_DOLBRACE) ? DOLBRACE_PARAM : 0;
/*itrace("parse_matched_pair[%d]: open = %c close = %c flags = %d", line_number, open, close, flags);*/
count = 1;
@@ -3225,12 +3229,32 @@ parse_matched_pair (qc, open, close, lenp, flags)
if MBTEST(ch == '\\') /* backslashes */
tflags |= LEX_PASSNEXT;
-#if 0
+#if 0 /* XXX - bash-4.2 */
+ /* Based on which dolstate is currently in (param, op, or word),
+ decide what the op is. We're really only concerned if it's % or
+ #, so we can turn on a flag that says whether or not we should
+ treat single quotes as special when inside a double-quoted
+ ${...} */
+ if (flags & P_DOLBRACE)
+ {
+ if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '%' && retind > 0)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '#' && retind > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if MBTEST(dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", ch) != 0)
+ dolbrace_state = DOLBRACE_OP;
+ else if MBTEST(dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", ch) == 0)
+ dolbrace_state = DOLBRACE_WORD;
+ }
+#endif
+
+#if 0 /* XXX - bash-4.2 */
/* The big hammer. Single quotes aren't special in double quotes. The
- problem is that Posix says the single quotes are semi-special:
+ problem is that Posix used to say the single quotes are semi-special:
within a double-quoted ${...} construct "an even number of
unescaped double-quotes or single-quotes, if any, shall occur." */
- if MBTEST(open == '{' && (flags & P_DQUOTE) && ch == '\'') /* } */
+ /* This was changed in Interp 221 */
+ if MBTEST(/*posixly_correct && shell_compatibility_level > 41 &&*/ dolbrace_state == DOLBRACE_QUOTE && (flags & P_DQUOTE) && (flags & P_DOLBRACE) && ch == '\'')
continue;
#endif
@@ -3307,7 +3331,7 @@ parse_dollar_word:
if (ch == '(') /* ) */
nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
else if (ch == '{') /* } */
- nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
+ nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
else if (ch == '[') /* ] */
nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
@@ -3750,7 +3774,7 @@ eof_error:
if (ch == '(') /* ) */
nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
else if (ch == '{') /* } */
- nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
+ nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
else if (ch == '[') /* ] */
nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
@@ -4398,7 +4422,7 @@ read_token_word (character)
((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */
{
if (peek_char == '{') /* } */
- ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE);
+ ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE|P_DOLBRACE);
else if (peek_char == '(') /* ) */
{
/* XXX - push and pop the `(' as a delimiter for use by
@@ -4803,24 +4827,35 @@ static const int no_semi_successors[] = {
/* If we are not within a delimited expression, try to be smart
about which separators can be semi-colons and which must be
newlines. Returns the string that should be added into the
- history entry. */
+ history entry. LINE is the line we're about to add; it helps
+ make some more intelligent decisions in certain cases. */
char *
-history_delimiting_chars ()
+history_delimiting_chars (line)
+ const char *line;
{
+ static int last_was_heredoc = 0; /* was the last entry the start of a here document? */
register int i;
+ if ((parser_state & PST_HEREDOC) == 0)
+ last_was_heredoc = 0;
+
if (dstack.delimiter_depth != 0)
return ("\n");
/* We look for current_command_line_count == 2 because we are looking to
add the first line of the body of the here document (the second line
- of the command). */
+ of the command). We also keep LAST_WAS_HEREDOC as a private sentinel
+ variable to note when we think we added the first line of a here doc
+ (the one with a "<<" somewhere in it) */
if (parser_state & PST_HEREDOC)
-{
- if (last_read_token == WORD && (token_before_that == LESS_LESS || token_before_that == LESS_LESS_MINUS))
- return ("\n"); /* XXX -- need to clean up in bash_add_history */
- return (current_command_line_count == 2 ? "\n" : "");
-}
+ {
+ if (last_was_heredoc)
+ {
+ last_was_heredoc = 0;
+ return "\n";
+ }
+ return (current_command_line_count == 2 ? "\n" : "");
+ }
/* First, handle some special cases. */
/*(*/
@@ -4843,6 +4878,15 @@ history_delimiting_chars ()
else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
return " "; /* function def using `function name' without `()' */
+ /* If we're not in a here document, but we think we're about to parse one,
+ and we would otherwise return a `;', return a newline to delimit the
+ line with the here-doc delimiter */
+ else if ((parser_state & PST_HEREDOC) == 0 && current_command_line_count > 1 && last_read_token == '\n' && strstr (line, "<<"))
+ {
+ last_was_heredoc = 1;
+ return "\n";
+ }
+
else if (token_before_that == WORD && two_tokens_ago == FOR)
{
/* Tricky. `for i\nin ...' should not have a semicolon, but
diff --git a/parser.h b/parser.h
index 9ff65769..5b971839 100644
--- a/parser.h
+++ b/parser.h
@@ -61,4 +61,12 @@ struct dstack {
int delimiter_space;
};
+/* States we can be in while scanning a ${...} expansion. Shared between
+ parse.y and subst.c */
+#define DOLBRACE_PARAM 0x01
+#define DOLBRACE_OP 0x02
+#define DOLBRACE_WORD 0x04
+
+#define DOLBRACE_QUOTE 0x40
+
#endif /* _PARSER_H_ */
diff --git a/parser.h~ b/parser.h~
new file mode 100644
index 00000000..9ff65769
--- /dev/null
+++ b/parser.h~
@@ -0,0 +1,64 @@
+/* parser.h -- Everything you wanted to know about the parser, but were
+ afraid to ask. */
+
+/* Copyright (C) 1995, 2008,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/>.
+*/
+
+#if !defined (_PARSER_H_)
+# define _PARSER_H_
+
+# include "command.h"
+# include "input.h"
+
+/* Possible states for the parser that require it to do special things. */
+#define PST_CASEPAT 0x000001 /* in a case pattern list */
+#define PST_ALEXPNEXT 0x000002 /* expand next word for aliases */
+#define PST_ALLOWOPNBRC 0x000004 /* allow open brace for function def */
+#define PST_NEEDCLOSBRC 0x000008 /* need close brace */
+#define PST_DBLPAREN 0x000010 /* double-paren parsing */
+#define PST_SUBSHELL 0x000020 /* ( ... ) subshell */
+#define PST_CMDSUBST 0x000040 /* $( ... ) command substitution */
+#define PST_CASESTMT 0x000080 /* parsing a case statement */
+#define PST_CONDCMD 0x000100 /* parsing a [[...]] command */
+#define PST_CONDEXPR 0x000200 /* parsing the guts of [[...]] */
+#define PST_ARITHFOR 0x000400 /* parsing an arithmetic for command */
+#define PST_ALEXPAND 0x000800 /* OK to expand aliases - unused */
+#define PST_EXTPAT 0x001000 /* parsing an extended shell pattern */
+#define PST_COMPASSIGN 0x002000 /* parsing x=(...) compound assignment */
+#define PST_ASSIGNOK 0x004000 /* assignment statement ok in this context */
+#define PST_EOFTOKEN 0x008000 /* yylex checks against shell_eof_token */
+#define PST_REGEXP 0x010000 /* parsing an ERE/BRE as a single word */
+#define PST_HEREDOC 0x020000 /* reading body of here-document */
+#define PST_REPARSE 0x040000 /* re-parsing in parse_string_to_word_list */
+#define PST_REDIRLIST 0x080000 /* parsing a list of redirctions preceding a simple command name */
+
+
+/* Definition of the delimiter stack. Needed by parse.y and bashhist.c. */
+struct dstack {
+/* DELIMITERS is a stack of the nested delimiters that we have
+ encountered so far. */
+ char *delimiters;
+
+/* Offset into the stack of delimiters. */
+ int delimiter_depth;
+
+/* How many slots are allocated to DELIMITERS. */
+ int delimiter_space;
+};
+
+#endif /* _PARSER_H_ */
diff --git a/po/ja.po b/po/ja.po
index 1021ae16..5056fe8d 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: GNU bash 4.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-30 08:25-0500\n"
-"PO-Revision-Date: 2010-02-14 10:35+0900\n"
+"PO-Revision-Date: 2010-04-01 01:59+0900\n"
"Last-Translator: Yasuaki Taniguchi <yasuakit@gmail.com>\n"
"Language-Team: Japanese <translation-team-ja@lists.sourceforge.net>\n"
"MIME-Version: 1.0\n"
@@ -84,9 +84,9 @@ msgid "%s: cannot read: %s"
msgstr "%s: %s を読み出しすることができません"
#: builtins/bind.def:260
-#, fuzzy, c-format
+#, c-format
msgid "`%s': cannot unbind"
-msgstr "%s: コマンドが見つかりません"
+msgstr "`%s': 割り当て解除できません"
#: builtins/bind.def:295 builtins/bind.def:325
#, c-format
@@ -96,7 +96,7 @@ msgstr "`%s': 不明な関数名です"
#: builtins/bind.def:303
#, c-format
msgid "%s is not bound to any keys.\n"
-msgstr ""
+msgstr "%s はどのキーにも割り当てられていません。\n"
#: builtins/bind.def:307
#, c-format
@@ -611,6 +611,24 @@ msgid ""
" -N\tDisplays the Nth entry counting from the right of the list shown by\n"
"\tdirs when invoked without options, starting with zero."
msgstr ""
+"現在記憶されているディレクトリスタックを表示します。ディレクトリは `pushd'\n"
+" コマンドによってスタックの一番上に保存されます。`popd'コマンドによって\n"
+" スタックから取り戻すことができます。\n"
+" \n"
+" オプション:\n"
+" -c\tディレクトリスタックからすべての要素を取り除き空にする\n"
+" -l\tホームディレクトリからの相対パスを ~ を利用した形式で\n"
+" \t表示しない\n"
+" -p\tディレクトリスタックの要素を1行毎に表示する\n"
+" -v\tディレクトリスタックの要素を1行ごとに番号をつけて\n"
+" \t表示する\n"
+" \n"
+" 引数:\n"
+" +N\tオプションなしで起動された場合にリストの先頭から数えて\n"
+" \tN番目の要素を表示します。開始番号は0です。\n"
+" \n"
+" -N\tオプションなしで起動された場合にリストの末尾から数えて\n"
+"\tN番目の要素を表示します。開始番号は0です。"
#: builtins/pushd.def:705
msgid ""
@@ -636,6 +654,25 @@ msgid ""
" \n"
" The `dirs' builtin displays the directory stack."
msgstr ""
+"ディレクトリスタックの先頭にディレクトリを追加します。またはディレクトリ\n"
+" スタックを回転します。新しいスタックの先頭がカレントディレクトリにな\n"
+" ります。引数なしで起動された場合、先頭の2つのディレクトリを交換します。\n"
+" \n"
+" オプション:\n"
+" -n\tスタックにディレクトリを追加した時に通常のディレクトリ変更\n"
+" \tを抑止します。よって、スタックのみ操作されます。\n"
+" \n"
+" 引数:\n"
+" +N\t先頭がN番目のディレクトリになるように回転します(`dirs'で\n"
+" \t表示されるスタックの先頭から数えた数です。開始番号は0です)。\n"
+" \n"
+" -N\t先頭がN番目のディレクトリになるように回転します(`dirs'で\n"
+" \t表示されるスタックの末尾から数えた数です。開始番号は0です)。\n"
+" \n"
+" ディレクトリ\tディレクトリをスタックの先頭に加え、そのディレク\n"
+" \tトリを新しいカレントディレクトリにします。\n"
+" \n"
+" `dirs' ビルトインコマンドでディレクトリスタックを表示します。"
#: builtins/pushd.def:730
msgid ""
@@ -657,6 +694,23 @@ msgid ""
" \n"
" The `dirs' builtin displays the directory stack."
msgstr ""
+"ディレクトリスタックから要素を削除します。引数が無い場合スタックの先頭から\n"
+" 削除し、新しいスタックの先頭ディレクトリに移動します。\n"
+" \n"
+" オプション:\n"
+" -n\tスタックからディレクトリを削除した時に通常のディレクトリ変更\n"
+" \tを抑止します。よって、スタックのみ操作されます。\n"
+" \n"
+" 引数:\n"
+" +N\tディレクトリスタック(`dirs' で表示される)の先頭から数えて\n"
+" \t N 番目の要素を削除します。開始番号は 0 です。例: `popd +0'\n"
+" \tは最初のディレクトリを削除します。`popd +1' は2番目です。\n"
+" \n"
+" -N\tディレクトリスタック(`dirs' で表示される)の最後から数えて\n"
+" \t N 番目の要素を削除します。開始番号は 0 です。例: `popd +0'\n"
+" \tは最後のディレクトリを削除します。`popd +1' は最後から2番目です。\n"
+" \n"
+" `dirs' ビルトインコマンドでディレクトリスタックを表示します。"
#: builtins/read.def:252
#, c-format
@@ -670,7 +724,7 @@ msgstr "読み込みエラー: %d: %s"
#: builtins/return.def:73
msgid "can only `return' from a function or sourced script"
-msgstr ""
+msgstr "`return' は関数または source されたスクリプト内のみで利用できます"
#: builtins/set.def:768
msgid "cannot simultaneously unset a function and a variable"
@@ -702,7 +756,7 @@ msgstr "シフト回数"
#: builtins/shopt.def:260
msgid "cannot set and unset shell options simultaneously"
-msgstr ""
+msgstr "シェルオプションを同時に有効かつ無効にできません"
#: builtins/shopt.def:325
#, c-format
@@ -754,7 +808,7 @@ msgstr "%s は %s です\n"
#: builtins/type.def:337
#, c-format
msgid "%s is hashed (%s)\n"
-msgstr ""
+msgstr "%s はハッシュされている (%s)\n"
#: builtins/ulimit.def:372
#, c-format
@@ -902,7 +956,7 @@ msgstr "0より小さい指数部です"
#: expr.c:826
msgid "identifier expected after pre-increment or pre-decrement"
-msgstr ""
+msgstr "識別子は前置インクリメントまたは前置デクリメントが予期されます"
#: expr.c:854
msgid "missing `)'"
@@ -946,7 +1000,7 @@ msgstr "ファイル記述子(fd) %d を無遅延モードに再設定できま
#: input.c:258
#, c-format
msgid "cannot allocate new file descriptor for bash input from fd %d"
-msgstr ""
+msgstr "新規ファイル記述子(fd) %d を bash の入力として割り当てられません"
#: input.c:266
#, c-format
@@ -975,7 +1029,7 @@ msgstr ""
#: jobs.c:1113
#, c-format
msgid "add_process: pid %5ld (%s) marked as still alive"
-msgstr ""
+msgstr "add_process: pid %5ld (%s) はまだ存在しています"
#: jobs.c:1401
#, c-format
@@ -1031,7 +1085,7 @@ msgstr " (wd: %s)"
#: jobs.c:1776
#, c-format
msgid "child setpgid (%ld to %ld)"
-msgstr ""
+msgstr "子プロセス setpgid (%ld から %ld)"
#: jobs.c:2104 nojobs.c:585
#, c-format
@@ -1060,7 +1114,7 @@ msgstr "%s: ジョブ %d はすでにバックグラウンドで動作してい
#: jobs.c:3059
msgid "waitchld: turning on WNOHANG to avoid indefinite block"
-msgstr ""
+msgstr "waitchld: 不定のブロックを避けるために WNOHANG をオンにしました。"
#: jobs.c:3508
#, c-format
@@ -1082,7 +1136,6 @@ msgid "initialize_job_control: getpgrp failed"
msgstr "initialize_job_control: getpgrp が失敗しました"
#: jobs.c:3639
-#, fuzzy
msgid "initialize_job_control: line discipline"
msgstr "initialize_job_control: line discipline"
@@ -1102,7 +1155,7 @@ msgstr "このシェルにはジョブ制御がありません"
#: lib/malloc/malloc.c:296
#, c-format
msgid "malloc: failed assertion: %s\n"
-msgstr ""
+msgstr "malloc: 失敗したアサーション: %s\n"
#: lib/malloc/malloc.c:312
#, c-format
@@ -1110,6 +1163,8 @@ msgid ""
"\r\n"
"malloc: %s:%d: assertion botched\r\n"
msgstr ""
+"\\r\n"
+"malloc: %s:%d: アサーション失敗\\r\n"
#: lib/malloc/malloc.c:313
msgid "unknown"
@@ -1239,7 +1294,7 @@ msgstr "make_here_document: 誤った指定の種類 %d"
#: make_cmd.c:659
#, c-format
msgid "here-document at line %d delimited by end-of-file (wanted `%s')"
-msgstr ""
+msgstr "ヒアドキュメントの %d 行目でファイル終了 (EOF) に達しました (`%s' が必要)"
#: make_cmd.c:756
#, c-format
@@ -1350,7 +1405,7 @@ msgstr "completion: 関数 `%s' が見つかりません"
#: pcomplib.c:179
#, c-format
msgid "progcomp_insert: %s: NULL COMPSPEC"
-msgstr ""
+msgstr "progcomp_insert: %s: NULL COMPSPEC"
#: print_cmd.c:290
#, c-format
@@ -1453,14 +1508,13 @@ msgid "Shell options:\n"
msgstr "シェル オプション:\n"
#: shell.c:1801
-#, fuzzy
msgid "\t-irsD or -c command or -O shopt_option\t\t(invocation only)\n"
-msgstr "\t-irsD 又は コマンド\t\t(訴えのみ)\n"
+msgstr "\t-irsD or -c command or -O shopt_option\t\t(invocation only)\n"
#: shell.c:1816
#, c-format
msgid "\t-%s or -o option\n"
-msgstr "\t-%s 又は -o オプション\n"
+msgstr "\t-%s または -o オプション\n"
#: shell.c:1822
#, c-format
@@ -1543,9 +1597,8 @@ msgid "Alarm clock"
msgstr "アラーム時計"
#: siglist.c:111
-#, fuzzy
msgid "Terminated"
-msgstr "終了しました"
+msgstr "Terminated"
#: siglist.c:115
msgid "Urgent IO condition"
@@ -1637,11 +1690,11 @@ msgstr "HFT monitorモードが奪われました"
#: siglist.c:211
msgid "HFT sound sequence has completed"
-msgstr ""
+msgstr "HFT サウンドシーケンスが完了しました"
#: siglist.c:215
msgid "Information request"
-msgstr ""
+msgstr "情報要求"
#: siglist.c:223
msgid "Unknown Signal #"
@@ -1706,7 +1759,7 @@ msgstr "%s: パラメータが null または設定されていません"
#: subst.c:5907
#, c-format
msgid "%s: substring expression < 0"
-msgstr ""
+msgstr "%s: substring expression < 0"
#: subst.c:6965
#, c-format
@@ -1776,7 +1829,7 @@ msgstr "run_pending_traps: trap_list[%d] に誤った値があります: %p"
#: trap.c:331
#, c-format
msgid "run_pending_traps: signal handler is SIG_DFL, resending %d (%s) to myself"
-msgstr ""
+msgstr "run_pending_traps: シグナルハンドラーは SIG_DFLです。, %d (%s) を自身に再送します。"
#: trap.c:380
#, c-format
@@ -1854,12 +1907,12 @@ msgstr "GNU bash, バージョン %s (%s)\n"
#: version.c:91 version2.c:88
#, c-format
msgid "This is free software; you are free to change and redistribute it.\n"
-msgstr ""
+msgstr "This is free software; you are free to change and redistribute it.\n"
#: version.c:92 version2.c:89
#, c-format
msgid "There is NO WARRANTY, to the extent permitted by law.\n"
-msgstr ""
+msgstr "There is NO WARRANTY, to the extent permitted by law.\n"
#: version2.c:86
#, c-format
@@ -1893,15 +1946,15 @@ msgstr "%s: %s:%d: %lu バイトを割当できません"
#: builtins.c:43
msgid "alias [-p] [name[=value] ... ]"
-msgstr "alias [-p] [名前[=値] ... ]"
+msgstr "alias [-p] [name[=value] ... ]"
#: builtins.c:47
msgid "unalias [-a] name [name ...]"
-msgstr "unalias [-a] 名前 [名前 ...]"
+msgstr "unalias [-a] name [name ...]"
#: builtins.c:51
msgid "bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-x keyseq:shell-command] [keyseq:readline-function or readline-command]"
-msgstr ""
+msgstr "bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-x keyseq:shell-command] [keyseq:readline-function or readline-command]"
#: builtins.c:54
msgid "break [n]"
@@ -1913,15 +1966,15 @@ msgstr "continue [n]"
#: builtins.c:58
msgid "builtin [shell-builtin [arg ...]]"
-msgstr "builtin [shell-builtin [引数 ...]]"
+msgstr "builtin [shell-builtin [arg ...]]"
#: builtins.c:61
msgid "caller [expr]"
-msgstr "caller [式]"
+msgstr "caller [expr]"
#: builtins.c:64
msgid "cd [-L|-P] [dir]"
-msgstr "cd [-L|-P] [ディレクトリ]"
+msgstr "cd [-L|-P] [dir]"
#: builtins.c:66
msgid "pwd [-LP]"
@@ -1941,87 +1994,87 @@ msgstr "false"
#: builtins.c:74
msgid "command [-pVv] command [arg ...]"
-msgstr "command [-pVv] コマンド [引数 ...]"
+msgstr "command [-pVv] command [arg ...]"
#: builtins.c:76
msgid "declare [-aAfFilrtux] [-p] [name[=value] ...]"
-msgstr "declare [-aAfFilrtux] [-p] [名前[=値] ...]"
+msgstr "declare [-aAfFilrtux] [-p] [name[=value] ...]"
#: builtins.c:78
msgid "typeset [-aAfFilrtux] [-p] name[=value] ..."
-msgstr "typeset [-aAfFilrtux] [-p] 名前[=値] ..."
+msgstr "typeset [-aAfFilrtux] [-p] name[=value] ..."
#: builtins.c:80
msgid "local [option] name[=value] ..."
-msgstr "local [オプション] 名前[=値] ..."
+msgstr "local [option] name[=value] ..."
#: builtins.c:83
msgid "echo [-neE] [arg ...]"
-msgstr "echo [-neE] [引数 ...]"
+msgstr "echo [-neE] [arg ...]"
#: builtins.c:87
msgid "echo [-n] [arg ...]"
-msgstr "echo [-n] [引数 ...]"
+msgstr "echo [-n] [arg ...]"
#: builtins.c:90
msgid "enable [-a] [-dnps] [-f filename] [name ...]"
-msgstr "enable [-a] [-dnps] [-f ファイル名] [名前 ...]"
+msgstr "enable [-a] [-dnps] [-f filename] [name ...]"
#: builtins.c:92
msgid "eval [arg ...]"
-msgstr "eval [引数 ...]"
+msgstr "eval [arg ...]"
#: builtins.c:94
msgid "getopts optstring name [arg]"
-msgstr "getopts optstring 名前 [引数]"
+msgstr "getopts optstring name [arg]"
#: builtins.c:96
msgid "exec [-cl] [-a name] [command [arguments ...]] [redirection ...]"
-msgstr "exec [-cl] [-a 名前] [コマンド [引数 ...]] [リダイレクト ...]"
+msgstr "exec [-cl] [-a name] [command [arguments ...]] [redirection ...]"
#: builtins.c:98
msgid "exit [n]"
-msgstr "終了 [n]"
+msgstr "exit [n]"
#: builtins.c:100
msgid "logout [n]"
-msgstr "ログアウト [n]"
+msgstr "logout [n]"
#: builtins.c:103
msgid "fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]"
-msgstr ""
+msgstr "fc [-e ename] [-lnr] [first] [last] または fc -s [pat=rep] [command]"
#: builtins.c:107
msgid "fg [job_spec]"
-msgstr "fg [ジョブ指定]"
+msgstr "fg [job_spec]"
#: builtins.c:111
msgid "bg [job_spec ...]"
-msgstr "bg [ジョブ指定 ...]"
+msgstr "bg [job_spec ...]"
#: builtins.c:114
msgid "hash [-lr] [-p pathname] [-dt] [name ...]"
-msgstr "hash [-lr] [-p パス名] [-dt] [名前 ...]"
+msgstr "hash [-lr] [-p pathname] [-dt] [name ...]"
#: builtins.c:117
msgid "help [-dms] [pattern ...]"
-msgstr "help [-dms] [パターン ...]"
+msgstr "help [-dms] [pattern ...]"
#: builtins.c:121
msgid "history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...]"
-msgstr "history [-c] [-d オフセット] [n] または history -anrw [ファイル名] または history -ps 引数 [引数...]"
+msgstr "history [-c] [-d offset] [n] または history -anrw [filename] または history -ps arg [arg...]"
#: builtins.c:125
msgid "jobs [-lnprs] [jobspec ...] or jobs -x command [args]"
-msgstr "jobs [-lnprs] [ジョブ指定 ...] または jobs -x コマンド [引数]"
+msgstr "jobs [-lnprs] [jobspec ...] または jobs -x command [args]"
#: builtins.c:129
msgid "disown [-h] [-ar] [jobspec ...]"
-msgstr "disown [-h] [-ar] [ジョブ指定 ...]"
+msgstr "disown [-h] [-ar] [jobspec ...]"
#: builtins.c:132
msgid "kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]"
-msgstr "kill [-s シグナル指定 | -n シグナル番号 | -シグナル指定] pid | ジョブ指定 ... または kill -l [シグナル指定]"
+msgstr "kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... または kill -l [sigspec]"
#: builtins.c:134
msgid "let arg [arg ...]"
@@ -2029,7 +2082,7 @@ msgstr "let 引数 [引数 ...]"
#: builtins.c:136
msgid "read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]"
-msgstr "read [-ers] [-a 配列] [-d 区切り文字] [-i テキスト] [-n 文字数] [-N 文字数] [-p プロンプト] [-t タイムアウト] [-u ファイル記述子] [名前 ...]"
+msgstr "read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]"
#: builtins.c:138
msgid "return [n]"
@@ -2037,19 +2090,19 @@ msgstr "return [n]"
#: builtins.c:140
msgid "set [--abefhkmnptuvxBCHP] [-o option-name] [arg ...]"
-msgstr "set [--abefhkmnptuvxBCHP] [-o オプション名] [引数 ...]"
+msgstr "set [--abefhkmnptuvxBCHP] [-o option-name] [arg ...]"
#: builtins.c:142
msgid "unset [-f] [-v] [name ...]"
-msgstr "unset [-f] [-v] [名前 ...]"
+msgstr "unset [-f] [-v] [name ...]"
#: builtins.c:144
msgid "export [-fn] [name[=value] ...] or export -p"
-msgstr "export [-fn] [名前[=値] ...] または export -p"
+msgstr "export [-fn] [name[=value] ...] or export -p"
#: builtins.c:146
msgid "readonly [-af] [name[=value] ...] or readonly -p"
-msgstr "readonly [-af] [名前[=値] ...] または readonly -p"
+msgstr "readonly [-af] [name[=value] ...] or readonly -p"
#: builtins.c:148
msgid "shift [n]"
@@ -2057,11 +2110,11 @@ msgstr "shift [n]"
#: builtins.c:150
msgid "source filename [arguments]"
-msgstr "source ファイル名 [引数]"
+msgstr "source filename [arguments]"
#: builtins.c:152
msgid ". filename [arguments]"
-msgstr ". ファイル名 [引数]"
+msgstr ". filename [arguments]"
#: builtins.c:155
msgid "suspend [-f]"
@@ -2069,11 +2122,11 @@ msgstr "suspend [-f]"
#: builtins.c:158
msgid "test [expr]"
-msgstr "test [式]"
+msgstr "test [expr]"
#: builtins.c:160
msgid "[ arg... ]"
-msgstr "[ 引数... ]"
+msgstr "[ arg... ]"
#: builtins.c:162
msgid "times"
@@ -2081,15 +2134,15 @@ msgstr "times"
#: builtins.c:164
msgid "trap [-lp] [[arg] signal_spec ...]"
-msgstr "trap [-lp] [[引数] シグナル指定 ...]"
+msgstr "trap [-lp] [[arg] signal_spec ...]"
#: builtins.c:166
msgid "type [-afptP] name [name ...]"
-msgstr "type [-afptP] 名前 [名前 ...]"
+msgstr "type [-afptP] name [name ...]"
#: builtins.c:169
msgid "ulimit [-SHacdefilmnpqrstuvx] [limit]"
-msgstr "ulimit [-SHacdefilmnpqrstuvx] [上限]"
+msgstr "ulimit [-SHacdefilmnpqrstuvx] [limit]"
#: builtins.c:172
msgid "umask [-p] [-S] [mode]"
@@ -2105,67 +2158,67 @@ msgstr "wait [pid]"
#: builtins.c:182
msgid "for NAME [in WORDS ... ] ; do COMMANDS; done"
-msgstr "for 変数名 [in 語 ... ] ; do コマンド群; done"
+msgstr "for NAME [in WORDS ... ] ; do COMMANDS; done"
#: builtins.c:184
msgid "for (( exp1; exp2; exp3 )); do COMMANDS; done"
-msgstr "for (( 式1; 式2; 式3 )); do コマンド群; done"
+msgstr "for (( exp1; exp2; exp3 )); do COMMANDS; done"
#: builtins.c:186
msgid "select NAME [in WORDS ... ;] do COMMANDS; done"
-msgstr "select 変数名 [in 語 ... ;] do コマンド群; done"
+msgstr "select NAME [in WORDS ... ;] do COMMANDS; done"
#: builtins.c:188
msgid "time [-p] pipeline"
-msgstr ""
+msgstr "time [-p] pipeline"
#: builtins.c:190
msgid "case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac"
-msgstr "case 語 in [パターン [| パターン]...) コマンド群 ;;]... esac"
+msgstr "case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac"
#: builtins.c:192
msgid "if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi"
-msgstr "if コマンド群; then コマンド群; [ elif コマンド群; then コマンド群; ]... [ else コマンド群; ] fi"
+msgstr "if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi"
#: builtins.c:194
msgid "while COMMANDS; do COMMANDS; done"
-msgstr "while コマンド群; do コマンド群; done"
+msgstr "while COMMANDS; do COMMANDS; done"
#: builtins.c:196
msgid "until COMMANDS; do COMMANDS; done"
-msgstr "until コマンド群; do コマンド群; done"
+msgstr "until COMMANDS; do COMMANDS; done"
#: builtins.c:198
msgid "coproc [NAME] command [redirections]"
-msgstr "coproc [名前] コマンド [リダイレクト]"
+msgstr "coproc [NAME] command [redirections]"
#: builtins.c:200
msgid "function name { COMMANDS ; } or name () { COMMANDS ; }"
-msgstr "function 関数名 { コマンド群 ; } または 関数名() { コマンド群 ; } "
+msgstr "function name { COMMANDS ; } または name () { COMMANDS ; }"
#: builtins.c:202
msgid "{ COMMANDS ; }"
-msgstr "{ コマンド群 ; }"
+msgstr "{ COMMANDS ; }"
#: builtins.c:204
msgid "job_spec [&]"
-msgstr "ジョブ指定 [&]"
+msgstr "job_spec [&]"
#: builtins.c:206
msgid "(( expression ))"
-msgstr "(( 式 ))"
+msgstr "(( expression ))"
#: builtins.c:208
msgid "[[ expression ]]"
-msgstr "[[ 式 ]]"
+msgstr "[[ expression ]]"
#: builtins.c:210
msgid "variables - Names and meanings of some shell variables"
-msgstr ""
+msgstr "変数 - 変数の名前とその意味"
#: builtins.c:213
msgid "pushd [-n] [+N | -N | dir]"
-msgstr "pushd [-n] [+N | -N | ディレクトリ]"
+msgstr "pushd [-n] [+N | -N | dir]"
#: builtins.c:217
msgid "popd [-n] [+N | -N]"
@@ -2177,31 +2230,31 @@ msgstr "dirs [-clpv] [+N] [-N]"
#: builtins.c:224
msgid "shopt [-pqsu] [-o] [optname ...]"
-msgstr "shopt [-pqsu] [-o] [オプション名 ...]"
+msgstr "shopt [-pqsu] [-o] [optname ...]"
#: builtins.c:226
msgid "printf [-v var] format [arguments]"
-msgstr "printf [-v var] 書式文字列 [引数]"
+msgstr "printf [-v var] format [arguments]"
#: builtins.c:229
msgid "complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...]"
-msgstr ""
+msgstr "complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...]"
#: builtins.c:233
msgid "compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word]"
-msgstr ""
+msgstr "compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word]"
#: builtins.c:237
msgid "compopt [-o|+o option] [-DE] [name ...]"
-msgstr "compopt [-o|+o オプション] [-DE] [名前 ...]"
+msgstr "compopt [-o|+o option] [-DE] [name ...]"
#: builtins.c:240
msgid "mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]"
-msgstr ""
+msgstr "mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]"
#: builtins.c:242
msgid "readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]"
-msgstr ""
+msgstr "readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]"
#: builtins.c:254
msgid ""
@@ -2285,6 +2338,38 @@ msgid ""
" Exit Status:\n"
" bind returns 0 unless an unrecognized option is given or an error occurs."
msgstr ""
+"Readline のキーバインディングと変数を設定します。\n"
+" \n"
+" キーシーケンスを Readline 関数またはマクロに関連付けを行うか、Readline\n"
+" 変数を設定します。オプションが無い引数の構文は ~/.inputrc の構文と\n"
+" 同様です。しかし、単一の引数として与えなければなりません。\n"
+" 例: bind '\"\\C-x\\C-r\": re-read-init-file'\n"
+" \n"
+" オプション:\n"
+" -m keymap Use KEYMAP as the keymap for the duration of this\n"
+" command. Acceptable keymap names are emacs,\n"
+" emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move,\n"
+" vi-command, and vi-insert.\n"
+" -l 関数名一覧を表示します。\n"
+" -P 関数名およびキーバインディング一覧を表示します。\n"
+" -p 関数名とキーバインディングを入力として再利用可能な\n"
+" 形式で一覧表示します。\n"
+" -S List key sequences that invoke macros and their values\n"
+" -s List key sequences that invoke macros and their values\n"
+" in a form that can be reused as input.\n"
+" -V 変数名と値の一覧を表示します。\n"
+" -v 変数名と値を入力として再利用可能な形式で一覧\n"
+" 表示します。\n"
+" -q function-name 関数名がどのキーによって起動されるか探索します\n"
+" -u function-name 関数名にバインドされたすべてのキーを解除します。\n"
+" -r keyseq KEYSEQ に対するバインドを解除します。\n"
+" -f filename FILENAME からキーバインドを読み込みます。\n"
+" -x keyseq:shell-command\tKEYSEQ が入力された時に SHELL-COMMAND \n"
+" \t\t\t\tが実行されるようにします。\n"
+" \n"
+" 終了ステータス:\n"
+" bind は解釈できないオプションが渡された場合およびエラーが発生した場合\n"
+" を除き 0 を返します。"
#: builtins.c:326
msgid ""
@@ -2302,7 +2387,7 @@ msgstr ""
" N階層のループを終了します。\n"
" \n"
" 終了ステータス:\n"
-" N が1以上の場合を除き、終了ステータスは 0 です。"
+" N が1未満の場合を除き、終了ステータスは 0 です。"
#: builtins.c:338
msgid ""
@@ -2314,6 +2399,13 @@ msgid ""
" Exit Status:\n"
" The exit status is 0 unless N is not greater than or equal to 1."
msgstr ""
+"for, while, または until ループを再開します。\n"
+" \n"
+" FOR, WHILE または UNTIL ループの次の繰り返しを再開します。\n"
+" もし N が指定された場合、N 階層のループを再開します。\n"
+" \n"
+" 終了ステータス:\n"
+" N が1未満の場合を除き、終了ステータスは 0 です。"
#: builtins.c:350
msgid ""
@@ -2327,6 +2419,15 @@ msgid ""
" Returns the exit status of SHELL-BUILTIN, or false if SHELL-BUILTIN is\n"
" not a shell builtin.."
msgstr ""
+"シェル組み込みコマンドを実行します。\n"
+" \n"
+" コマンドを検索しないでシェル組み込みコマンドを ARG をつけて実行し\n"
+" ます。これはシェル組み込みコマンドをシェル関数として実装するが、関数\n"
+" 内で組み込みコマンドを実行する必要がある場合に役に立ちます。\n"
+" \n"
+" 終了ステータス:\n"
+" シェル組み込みコマンドの終了ステータスを返します。シェル組み込みコマ\n"
+" ンドが無い場合は false を返します。"
#: builtins.c:365
msgid ""
@@ -2343,6 +2444,18 @@ msgid ""
" Returns 0 unless the shell is not executing a shell function or EXPR\n"
" is invalid."
msgstr ""
+"現在のサブルーチン呼び出しのコンテキストを返します。\n"
+" \n"
+" EXPR が無い場合 \"$line $filename\" を返します。 EXPR がある場合、\n"
+" \"$line $subroutine $filename\" を返します。この追加の情報はスタックトレース\n"
+" を提供する時に利用します。\n"
+" \n"
+" EXPR の値は現在のフレームに戻るまでに何回フレームが呼び出されているかを\n"
+" 意味します。最上位のフレームは 0 です。\n"
+" \n"
+" 終了ステータス:\n"
+" シェルが関数を実行できないか式 EXPR が無効な場合を除き 0 を返します。\n"
+" is invalid."
#: builtins.c:383
msgid ""
@@ -2370,6 +2483,28 @@ msgid ""
" Exit Status:\n"
" Returns 0 if the directory is changed; non-zero otherwise."
msgstr ""
+"シェルのカレントディレクトリを変更します。\n"
+" \n"
+" カレントディレクトリを`ディレクトリ'に変更します。`ディレクトリ'の\n"
+" デフォルトは HOME シェル変数です。\n"
+" \n"
+" CDPATH はディレクトリが含まれる検索パスを定義します。CDPATH にはコロン(:)\n"
+" で区切られた代替ディレクトリを指定します。\n"
+" NULL のディレクトリ名はカレントディレクトリと同義です。ディレクトリが\n"
+" スラッシュ (/) から始まる場合は CDPATH は利用されません。\n"
+" \n"
+" ディレクトリが見つからなく、かつ `cdabl_vars' シェルオプションが設定されて\n"
+" いる場合、引数は変数名として扱われます。その変数に値がある場合、その値が\n"
+" DIR として扱われます。\n"
+" \n"
+" オプション:\n"
+" -L\tシンボリックリンクを強制的にたどります\n"
+" -P\tシンボリックリンクをたどらず物理構造を利用します\n"
+" \n"
+" デフォルトでは `-L' が指定された場合と同様シンボリックリンクをたどります\n"
+" \n"
+" 終了ステータス:\n"
+" ディレクトリを変更した場合は 0、そうでなければ 0 以外です。"
#: builtins.c:411
msgid ""
@@ -2386,6 +2521,18 @@ msgid ""
" Returns 0 unless an invalid option is given or the current directory\n"
" cannot be read."
msgstr ""
+"カレントディレクトリの名前を表示します。\n"
+" \n"
+" オプション:\n"
+" -L\t$PWD がカレントディレクトリの名前を指している場合は $PWD\n"
+" \tを表示します。\n"
+" -P\tシンボリックリンクを辿った物理的なディレクトリを表示します。\n"
+" \n"
+" デフォルトでは `pwd' は `-L' が指定されたように動作します。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションまたはカレントディレクトリを読み込めない場合を除き\n"
+" 0を返します。"
#: builtins.c:428
msgid ""
@@ -2444,6 +2591,20 @@ msgid ""
" Exit Status:\n"
" Returns exit status of COMMAND, or failure if COMMAND is not found."
msgstr ""
+"単純なコマンドを実行するかコマンドに関する情報を表示します。\n"
+" \n"
+" シェル関数の探索を抑止して COMMAND を引数 ARGS で実行します。または\n"
+" 指定した COMMANDs の情報を表示します。シェル関数と同じ名前のコマンド\n"
+" がディスク上に存在する時に使用します。\n"
+" \n"
+" オプション:\n"
+" -p\tuse a default value for PATH that is guaranteed to find all of\n"
+" \tthe standard utilities\n"
+" -v\t`type'組み込み関数と同様に COMMAND の説明を表示します。\n"
+" -V\tCOMMAND に対してより冗長な説明を表示します。\n"
+" \n"
+" 終了ステータス:\n"
+" COMMAND の終了ステータスを返します。または COMMAND が見つからない時に失敗を返します。"
#: builtins.c:476
msgid ""
@@ -2479,6 +2640,37 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is supplied or an error occurs."
msgstr ""
+"変数の値および属性を設定します。\n"
+" \n"
+" 変数を宣言し、それに属性を与えます。NAME を指定しない場合\n"
+" すべての変数に対する属性と値を表示します。\n"
+" \n"
+" オプション:\n"
+" -f\trestrict action or display to function names and definitions\n"
+" -F\trestrict display to function names only (plus line number and\n"
+" \tsource file when debugging)\n"
+" -p\tdisplay the attributes and value of each NAME\n"
+" \n"
+" 属性を設定するオプション:\n"
+" -a\tNAME をインデックス型配列にします(サポートされている場合)\n"
+" -A\tNAME を連想配列にします(サポートされている場合)\n"
+" -i\tNAME に`整数型'の属性を設定します。\n"
+" -l\tNAME の設定時に値を小文字に変換します\n"
+" -r\tNAME を読み取り専用にします\n"
+" -t\tNAME に `trace' 属性を設定します\n"
+" -u\tNAME 設定時に値を大文字に設定します\n"
+" -x\tNAME をエクスポートします\n"
+" \n"
+" `-' の代わりに `+' を使用すると与えられた属性を無効にします。\n"
+" \n"
+" 整数属性を与えられた変数は値を割り当てられた時に、数値として評価され\n"
+" ます。(`let' コマンド参照してください。)\n"
+" \n"
+" 関数内で使用された場合は `local' コマンドを使用した時と同様に `declare' \n"
+" は NAME をローカル変数にします。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられたかエラーが発生しない限り成功を返します。"
#: builtins.c:512
msgid ""
@@ -2486,6 +2678,9 @@ msgid ""
" \n"
" Obsolete. See `help declare'."
msgstr ""
+"変数の値および属性を設定します。\n"
+" \n"
+" 旧式です。`help declare'を参照してください。"
#: builtins.c:520
msgid ""
@@ -2501,6 +2696,17 @@ msgid ""
" Returns success unless an invalid option is supplied, an error occurs,\n"
" or the shell is not executing a function."
msgstr ""
+"ローカル変数を定義します。\n"
+" \n"
+" NAME という名前のローカル変数を定義し値を VALUE に設定します。`declare'と\n"
+" 同じオプションを受け付けます。\n"
+" \n"
+" ローカル変数はシェル関数の中でのみ使用できます。宣言された関数の中およびそこ\n"
+" から呼び出された関数のみで参照できます。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられる、エラーが発生する、またはシェルが関数を実行できない\n"
+" 場合を除き成功を返します。"
#: builtins.c:537
msgid ""
@@ -2532,6 +2738,31 @@ msgid ""
" Exit Status:\n"
" Returns success unless a write error occurs."
msgstr ""
+"引数を標準出力に書き出します。\n"
+" \n"
+" 引数 ARG を最後に改行を加えて標準出力に表示します。\n"
+" \n"
+" オプション:\n"
+" -n\t最後に改行を加えない\n"
+" -e\t下記に示すバックスラッシュエスケープの解釈を有効にする\n"
+" -E\tバックスラッシュエスケープの解釈を明示的に無効にする\n"
+" \n"
+" `echo' では下記のバックスラッシュエスケープ文字を解釈します:\n"
+" \\a\tアラート (bell)\n"
+" \\b\tバックスペース\n"
+" \\c\t以降の出力を抑止\n"
+" \\e\tエスケープ文字\n"
+" \\f\tフォームフィード\n"
+" \\n\t改行\n"
+" \\r\tキャリッジリターン\n"
+" \\t\t水平タブ\n"
+" \\v\t垂直タブ\n"
+" \\\\\tバックスラッシュ\n"
+" \\0nnn\tASCII コードが NNN (8進数) の文字。 NNN は0から3個の8進数字\n"
+" \\xHH\t値が HH (16進数) の8ビット文字。HH は 1 または2個の16進数字\n"
+" \n"
+" 終了ステータス:\n"
+" 書き込みエラーが発生しない限り成功を返します。"
#: builtins.c:571
msgid ""
@@ -2545,6 +2776,15 @@ msgid ""
" Exit Status:\n"
" Returns success unless a write error occurs."
msgstr ""
+"引数を標準出力に書き出します。\n"
+" \n"
+" 引数 ARG を最後に改行を加えて標準出力に表示します。\n"
+" \n"
+" オプション:\n"
+" -n\t最後に改行を加えない\n"
+" \n"
+" 終了ステータス:\n"
+" 書き込みエラーが発生しない限り成功を返します。"
#: builtins.c:586
msgid ""
@@ -2572,6 +2812,29 @@ msgid ""
" Exit Status:\n"
" Returns success unless NAME is not a shell builtin or an error occurs."
msgstr ""
+"シェル組み込み関数を有効または無効にします。\n"
+" \n"
+" シェル組み込み関数を有効または無効にします。シェル組み込み関数を無効にすると\n"
+" ディスク上に存在するシェル組み込み関数と同じ名前のコマンドをフルパスを指定す\n"
+" ることなく実行することが出来ます。\n"
+" \n"
+" オプション:\n"
+" -a\t組み込み関数の一覧をそれぞれが有効か否かを含めて表示します\n"
+" -n\tNAME を無効にするか、または無効な組み込み関数一覧を表示します\n"
+" -p\t組み込み関数一覧を再利用可能な形式で表示します\n"
+" -s\tPosix で定義されている組み込み関数のみ表示します\n"
+" \n"
+" 動的ロードを制御するオプション:\n"
+" -f\t共有オブジェクト FILENAME から組み込み関数 NAME を読み込みます\n"
+" -d\t-f で読み込まれた組み込み関数を削除します\n"
+" \n"
+" オプション無しで起動された場合、各 NAME は有効になります。\n"
+" \n"
+" $PATH 上に存在する `test' をシェル組み込み関数の代わりに利用する場合は\n"
+" `enable -n test'と入力してください。\n"
+" \n"
+" 終了ステータス:\n"
+" NAME が組み込み関数ではないかエラーが発生しない限り成功を返します。"
#: builtins.c:614
msgid ""
@@ -2583,8 +2846,17 @@ msgid ""
" Exit Status:\n"
" Returns exit status of command or success if command is null."
msgstr ""
+"引数をシェルコマンドとして実行します。\n"
+" \n"
+" 引数を一つの文字列に連結し、その結果をシェルへの入力として使用し、\n"
+" その結果をコマンドとして実行します。\n"
+" \n"
+" 終了ステータス:\n"
+" コマンドの終了ステータスを返します。コマンドが null の場合は成功を\n"
+" 返します。"
#: builtins.c:626
+#, fuzzy
msgid ""
"Parse option arguments.\n"
" \n"
@@ -2624,6 +2896,43 @@ msgid ""
" Returns success if an option is found; fails if the end of options is\n"
" encountered or an error occurs."
msgstr ""
+"オプション引数を解析します。\n"
+" \n"
+" Getopts は位置パラメーターをオプションとして解析する際にシェルによっ\n"
+" て使用される手続です。\n"
+" \n"
+" OPTSTRING には認識されるオプションの文字が含まれます。文字の後に\n"
+" コロン(:)が続く場合は、オプションは空白文字でオプションから区切\n"
+" られた引数を持つと予期されます。\n"
+" \n"
+" Each time it is invoked, getopts will place the next option in the\n"
+" shell variable $name, initializing name if it does not exist, and\n"
+" the index of the next argument to be processed into the shell\n"
+" variable OPTIND. OPTIND is initialized to 1 each time the shell or\n"
+" a shell script is invoked. When an option requires an argument,\n"
+" getopts places that argument into the shell variable OPTARG.\n"
+" \n"
+" getopts reports errors in one of two ways. If the first character\n"
+" of OPTSTRING is a colon, getopts uses silent error reporting. In\n"
+" this mode, no error messages are printed. If an invalid option is\n"
+" seen, getopts places the option character found into OPTARG. If a\n"
+" required argument is not found, getopts places a ':' into NAME and\n"
+" sets OPTARG to the option character found. If getopts is not in\n"
+" silent mode, and an invalid option is seen, getopts places '?' into\n"
+" NAME and unsets OPTARG. If a required argument is not found, a '?'\n"
+" is placed in NAME, OPTARG is unset, and a diagnostic message is\n"
+" printed.\n"
+" \n"
+" If the shell variable OPTERR has the value 0, getopts disables the\n"
+" printing of error messages, even if the first character of\n"
+" OPTSTRING is not a colon. OPTERR has the value 1 by default.\n"
+" \n"
+" Getopts normally parses the positional parameters ($0 - $9), but if\n"
+" more arguments are given, they are parsed instead.\n"
+" \n"
+" Exit Status:\n"
+" Returns success if an option is found; fails if the end of options is\n"
+" encountered or an error occurs."
#: builtins.c:668
msgid ""
@@ -2644,6 +2953,22 @@ msgid ""
" Exit Status:\n"
" Returns success unless COMMAND is not found or a redirection error occurs."
msgstr ""
+"シェルを与えられたコマンドで置換します。\n"
+" \n"
+" 指定したプログラムでシェルを置換して COMMAND を実行します。ARGUMENTS は\n"
+" COMMAND の引数となります。もし COMMAND が指定されない場合は、現在のシェル\n"
+" に対する全てのリダイレクトが行われます。\n"
+" \n"
+" オプション:\n"
+" -a name\tNAME を COMMAND の 0 番目の引数として与えます\n"
+" -c\t\tCOMMAND を環境変数なしで実行します\n"
+" -l\t\tdash(-) を COMMAND の 0 番目の引数とします\n"
+" \n"
+" もしコマンドが実行できない場合、非対話的なシェルは終了し、対話的なシェルは\n"
+" オプション `execfail' が設定されます。\n"
+" \n"
+" 終了ステータス:\n"
+" COMMAND が見つからないかリダイレクトエラーが発生しない限り成功を返します。"
#: builtins.c:689
msgid ""
@@ -2652,6 +2977,10 @@ msgid ""
" Exits the shell with a status of N. If N is omitted, the exit status\n"
" is that of the last command executed."
msgstr ""
+"シェルを終了します。\n"
+" \n"
+" 終了ステータス N でシェルを終了します。 N を指定しない場合は\n"
+" 最後に実行したコマンドの終了ステータスになります。"
#: builtins.c:698
msgid ""
@@ -2660,6 +2989,10 @@ msgid ""
" Exits a login shell with exit status N. Returns an error if not executed\n"
" in a login shell."
msgstr ""
+"ログインシェルを終了します。\n"
+" \n"
+" 終了ステータス N でログインシェルを終了します。実行したのがログインシェル\n"
+" 内で無い場合はエラーを返します。"
#: builtins.c:708
msgid ""
@@ -2687,6 +3020,29 @@ msgid ""
" Exit Status:\n"
" Returns success or status of executed command; non-zero if an error occurs."
msgstr ""
+"ヒストリ一覧からコマンドを表示または実行します。\n"
+" \n"
+" fc はヒストリ一覧を表示または編集してコマンドを再実行するために使用します。\n"
+" FIRST および LAST は範囲を指定する数値です。FIRST は文字列を指定することも\n"
+" できます。その場合はその文字列で始まる直近に実行したコマンドを表します。\n"
+" \n"
+" オプション:\n"
+" -e ENAME\t使用するエディタを選択します。デフォルトは FCEDIT で、次は EDITOR、\n"
+" \t\tそして vi の順です。\n"
+" -l \t編集ではなく行を一覧表示します\n"
+" -n\t一覧表示時に行番号を表示しません\n"
+" -r\t行を逆順にします (最新のコマンドを最初にする)\n"
+" \n"
+" `fc -s [pat=rep ...] [command]' 形式を使用すると、COMMAND は\n"
+" OLD=NEW の置換が行われた後に再実行されます。\n"
+" \n"
+" これを使った使いやすいエイリアスは r='fc -s' です。これで `r cc' を実行する\n"
+" と最後に実行した cc で始まるコマンドが実行されます。`r' で直前のコマンドが\n"
+" 実行されます。\n"
+" \n"
+" 終了ステータス:\n"
+" 実行したコマンドのステータスまたは成功が帰ります。エラーが発生した場合は 0 \n"
+" 以外の値になります。"
#: builtins.c:738
msgid ""
@@ -2699,6 +3055,16 @@ msgid ""
" Exit Status:\n"
" Status of command placed in foreground, or failure if an error occurs."
msgstr ""
+"ジョブをフォアグランドにします。\n"
+" \n"
+" JOB_SPEC で識別されたジョブをフォアグランドにして、現在のジョブにします。\n"
+" もし JOB_SPEC が存在しない場合、シェルが現在のレントジョブとして考えている\n"
+" ものが利用されます。\n"
+" \n"
+" \n"
+" 終了ステータス:\n"
+" フォアグラウンドになったコマンドのステータスを返します。または、エラーが\n"
+" 発生した時に失敗を返します。"
#: builtins.c:753
msgid ""
@@ -2711,6 +3077,14 @@ msgid ""
" Exit Status:\n"
" Returns success unless job control is not enabled or an error occurs."
msgstr ""
+"ジョブをバックグラウンドにします。\n"
+" \n"
+" JOB_SPEC で識別されるジョブを `&' と共に始めた時のようにバックグラウンドに\n"
+" します。もし JOB_SPEC が存在しない場合、シェルが現在のジョブとして考えてい\n"
+" るものが利用されます。\n"
+" \n"
+" 終了ステータス:\n"
+" ジョブ制御が有効になっていないかエラーが発生しない限り成功を返します。"
#: builtins.c:767
msgid ""
@@ -2734,6 +3108,24 @@ msgid ""
" Exit Status:\n"
" Returns success unless NAME is not found or an invalid option is given."
msgstr ""
+"プログラムの位置を記憶または表示します。\n"
+" \n"
+" 各コマンド NAME のフルパスを決定し記憶します。もし引数が与えられなかった場合、\n"
+" 記憶しているコマンドの情報が表示されます。\n"
+" \n"
+" オプション:\n"
+" -d\t\t各 NAME に対して記憶している情報を消去します\n"
+" -l\t\t入力として再可能な形式で表示します\n"
+" -p pathname\tNAME のフルパス名として PATHNAME を使用します\n"
+" -r\t\t全ての記憶している位置情報を消去します\n"
+" -t\t\t各 NAME に対して記憶している位置を表示します。複数の NAME\n"
+" \t\tが与えられた場合、位置を表示する前に対象となる NAME を表示します。\n"
+" 引数:\n"
+" NAME\t\t各 NAME は $PATH の中を探索され、記録されたコマンド一覧に\n"
+" \t\t追加されます。\n"
+" \n"
+" 終了ステータス:\n"
+" NAME が見つからないか、無効なオプションが与えられない限り成功を返します。"
#: builtins.c:792
msgid ""
@@ -2755,6 +3147,23 @@ msgid ""
" Exit Status:\n"
" Returns success unless PATTERN is not found or an invalid option is given."
msgstr ""
+"組み込みコマンドの情報を表示します。\n"
+" \n"
+" 組み込みコマンドに関する簡潔な要約を表示します。もし PATTERN が\n"
+" 指定された場合は、PATTERN に一致する全てのコマンドに対する詳細な\n"
+" ヘルプを表示します。それ以外はヘルプトピックを表示します。\n"
+" \n"
+" オプション:\n"
+" -d\t各ヘルプトピックに対して短い説明を出力します\n"
+" -m\t使用法を擬似的な man ページ形式で表示します\n"
+" -s\t一致した各トピックに対して簡単な使用法のみを表示します\n"
+" \tPATTERN\n"
+" \n"
+" 引数:\n"
+" PATTERN\tヘルプトピックを指定するパターン\n"
+" \n"
+" 終了ステータス:\n"
+" PATTERN が見つからないか無効なオプションが与えられない限り成功を返します。"
#: builtins.c:816
msgid ""
@@ -2788,6 +3197,35 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is given or an error occurs."
msgstr ""
+"ヒストリ一覧を表示または操作します。\n"
+" \n"
+" 行番号をつけてヒストリを表示します。操作した各項目には前に`*'が付きます。\n"
+" 引数 N がある場合は最後の N 個の項目のみを表示します。\n"
+" \n"
+" オプション:\n"
+" -c\tヒストリ一覧から全ての項目を削除します。\n"
+" -d offset\tOFFSET 番目のヒストリ項目を削除します。\n"
+" \n"
+" -a\tこのセッションからヒストリファイルに行を追加します\n"
+" -n\tヒストリファイルからまだ読み込まれていない行を全て読み込みます\n"
+" -r\tヒストリファイルを読み込み、内容をヒストリ一覧に追加します\n"
+" -w\t現在のヒストリをヒストリファイルに書き込みます。そしてそれらを\n"
+" \tヒストリ一覧に追加します\n"
+" \n"
+" -p\t各 ARG に対してヒストリ展開を実行し、結果をヒストリ一覧に追加し\n"
+" \tしないで表示します\n"
+" -s\tARG を単一の項目としてヒストリ一覧に追加します\n"
+" \n"
+" FILENAME を与えた場合、FILENAME がヒストリファイルをして使用されます。それが\n"
+" 無く、$HISTFILE に値がある場合その値が使用されます。そうでなければ \n"
+" ~/.bash_history が使用されます。\n"
+"\n"
+" もし $HISTTIMEFORMAT 変数が設定され、NULL で無ければ、strftime(3) の書式\n"
+" 文字列として各ヒストリ項目の時刻を表示する際に使用されます。それ以外は\n"
+" 時刻は表示されません。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるかエラーが発生しない限り成功を返します。"
#: builtins.c:852
msgid ""
@@ -2812,6 +3250,26 @@ msgid ""
" Returns success unless an invalid option is given or an error occurs.\n"
" If -x is used, returns the exit status of COMMAND."
msgstr ""
+"ジョブのステータスを表示します。\n"
+" \n"
+" アクティブなジョブを一覧表示します。JOBSPEC はジョブの出力を制限します。\n"
+" オプションがない場合全てのアクティブなジョブのステータスが表示されます。\n"
+" \n"
+" オプション:\n"
+" -l\t通常の情報に加えてプロセスIDを一覧表示します\n"
+" -n\t最後の通知からステータスが変更になったプロセスのみ一覧表示\n"
+" \tします\n"
+" -p\tプロセスIDのみを一覧表示します\n"
+" -r\t実行中のジョブの出力を制限します\n"
+" -s\t停止中のジョブの出力を制限します\n"
+" \n"
+" If -x is supplied, COMMAND is run after all job specifications that\n"
+" appear in ARGS have been replaced with the process ID of that job's\n"
+" process group leader.\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるかエラーが発生しない限り成功を返します。\n"
+" もし -x が使用された場合、COMMAND の終了ステータスを返します。"
#: builtins.c:879
msgid ""
@@ -2829,6 +3287,19 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option or JOBSPEC is given."
msgstr ""
+"現在のシェルからジョブを削除します。\n"
+" \n"
+" アクティブなジョブのテーブルから各引数の JOBSPEC を削除します。JOBSPEC が指定\n"
+" されない場合、シェルが現在のジョブと考えている物が使用されます。\n"
+" \n"
+" オプション:\n"
+" -a\tJOBSPEC が与えられない時に全てのジョブを削除します\n"
+" -h\tシェルが SIGHUP を受け取った時に各 JOBSPEC のジョブに対して SIGHUP \n"
+" \tが送られないようにマークします\n"
+" -r\t実行中のジョブのみ削除します\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションか JOBSPEC が与えられない限り成功を返します。"
#: builtins.c:898
msgid ""
@@ -2851,6 +3322,24 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is given or an error occurs."
msgstr ""
+"ジョブにシグナルを送ります。\n"
+" \n"
+" PID または JOBSPEC で識別されるプロセスに SIGSPEC または SIGNUM で名付けら\n"
+" れるシグナルを送ります。もし SIGSPEC も SIGNUM も指定されない場合、SIGTERM\n"
+" と見なされます。\n"
+" \n"
+" オプション:\n"
+" -s sig\tSIG はシグナル名です\n"
+" -n sig\tSIG はシグナル番号です\n"
+" -l\tシグナル名を一覧表示します。-l の後に引数が続いた場合、\n"
+" \tそれらは一覧表示されるべきシグナル番号であると見なされます\n"
+" \n"
+" Kill は次の2つの理由からシェル組み込み関数です。一つはプロセスIDの代わりに\n"
+" ジョブIDを使用できるようにするためです。もう一つは作成したプロセスが制限に\n"
+" 達した時にプロセスを kill することができるようにするためです。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるかエラーが発生しない限り成功を返します。"
#: builtins.c:921
msgid ""
@@ -2895,6 +3384,43 @@ msgid ""
" Exit Status:\n"
" If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.."
msgstr ""
+"数式表現を評価します。\n"
+" \n"
+" ARG を数式表現として評価します。評価は固定長の整数として評価され、桁溢れは\n"
+" 確認されません。しかし、0 で割り算をした場合は捕捉されエラーとしてフラグが\n"
+" 立ちます。下記の演算子一覧は同一優先順位の演算子毎にグループ化されています。\n"
+" 優先順位は降順になっています。\n"
+" \n"
+" \tid++, id--\t変数の後置インクリメント、デクリメント\n"
+" \t++id, --id\t変数の前置インクリメント、デクリメント\n"
+" \t-, +\t\t単項マイナス、プラス\n"
+" \t!, ~\t\t論理およびビット否定\n"
+" \t**\t\t指数演算\n"
+" \t*, /, %\t\t乗算、除算、剰余演算\n"
+" \t+, -\t\t加算、減算\n"
+" \t<<, >>\t\t左および右ビットシフト\n"
+" \t<=, >=, <, >\t比較\n"
+" \t==, !=\t\t等価、不等価\n"
+" \t&\t\tビット論理積\n"
+" \t^\t\tビット排他的論理和\n"
+" \t|\t\tビット論理和\n"
+" \t&&\t\t論理積\n"
+" \t||\t\t論理和\n"
+" \texpr ? expr : expr\n"
+" \t\t\t条件演算子\n"
+" \t=, *=, /=, %=,\n"
+" \t+=, -=, <<=, >>=,\n"
+" \t&=, ^=, |=\t値の割当\n"
+" \n"
+" シェル変数はオペランドとして使用できます。変数名は数式内で (強制的に\n"
+" 固定長整数の) 値に置き換えられます。変数は数式内で使用する時には必ずしも\n"
+" 整数属性を持っている必要はありません。\n"
+" \n"
+" 演算子は優先順位の順に評価されます。小括弧でくくられた数式は先に評価され、\n"
+" 上記の優先順位を上書きするかもしれません。\n"
+" \n"
+" 終了ステータス:\n"
+" ARG の評価値が 0 の場合 let は 1 を返します。それ以外の場合は let は 0 を返します。"
#: builtins.c:966
msgid ""
@@ -2937,6 +3463,42 @@ msgid ""
" The return code is zero, unless end-of-file is encountered, read times out,\n"
" or an invalid file descriptor is supplied as the argument to -u."
msgstr ""
+"標準入力から一行読み込みフィールド毎に分割します。\n"
+" \n"
+" 標準入力から一行読み込みます。または -u が指定されている場合はファイル記述子\n"
+" FD から読み込みます。行は単語分割によってフィールドに分割され、最初の単語が\n"
+" 最初の NAME に、2番目の単語が2番目に NAME にという順で割り当てられます。残っ\n"
+" た単語は全て最後の NAME に割り当てられます。変数 $IFS に設定された文字のみが\n"
+" 単語の区切りとして認識されます。\n"
+" \n"
+" もし NAME を指定しなかった場合、行は REPLY 変数に保存されます。\n"
+" \n"
+" オプション:\n"
+" -a array\t読み込んだ単語をインデックス型配列 ARRAY に順番に割り当てます。\n"
+" \t\t開始番号は 0 です。\n"
+" -d delim\t改行ではなく文字 DELIM が最初に現れるまで読み込みを続けます\n"
+" -e\t\t対話的シェルで行を得るのに Readline を使用します\n"
+" -i text\tReadline の初期テキストとして TEXT を使用します\n"
+" -n nchars\t改行が無くても文字数 NCHARS を読み込んだら復帰します。NCHARS\n"
+" \t\tより前に区切り文字 (DELIMITER) が現れた場合は区切り文字が\n"
+" \t\t優先されます\n"
+" -N nchars\t厳密に文字数 NCHARS を読み込み復帰します。ただし、ファイル終\n"
+" \t\t了(EOF) になるか読み込みタイムアウトが発生した場合は除きます。\n"
+" \t\t区切り文字 (DELIMITER) は無視されます\n"
+" -p prompt\t読み込み前に文字列 PROMPT を後ろに改行を付けないで表示します\n"
+" -r\t\tバックスペースで文字をエスケープすることを禁止します\n"
+" -s\t\t端末から読み込まれる文字をエコーバックしません\n"
+" -t timeout\tTIMEOUT 秒以内に入力行が完全に読み込まれなかった場合\n"
+" \t\tタイムアウトを発生させ失敗として復帰します。TMOUT 変数の値が\n"
+" \t\tデフォルトのタイムアウトです。TIMEOUT を小数にすることもでき\n"
+" \t\tます。もし TIMEOUT が 0 の場合、指定したファイル記述子からの\n"
+" \t\t入力が使用可能な場合にのみ読み込み、成功返します。タイムアウ\n"
+" \t\tト発生時の終了ステータスは128より大きくなります\n"
+" -u fd\t\t読み込みに標準入力ではなくファイル記述子 FD を使用します\n"
+" \n"
+" 終了ステータス:\n"
+" ファイル終了(EOF)、読み込みタイムアウト、-u に無効なファイル記述子が与え\n"
+" られた場合を除き0を返します。"
#: builtins.c:1009
msgid ""
@@ -2949,8 +3511,18 @@ msgid ""
" Exit Status:\n"
" Returns N, or failure if the shell is not executing a function or script."
msgstr ""
+"シェル関数から復帰します。\n"
+" \n"
+" N で指定した値を戻り値として関数または source されたスクリプトを終了します。\n"
+" N が指定されない場合、関数またはスクリプトで最後に実行したコマンドの戻り値\n"
+" が使用されます。\n"
+" \n"
+" 終了ステータス:\n"
+" 戻り値 N、またはシェルが関数またはスクリプトを実行していない場合は失敗を\n"
+" 返します。"
#: builtins.c:1022
+#, fuzzy
msgid ""
"Set or unset values of shell options and positional parameters.\n"
" \n"
@@ -3030,6 +3602,83 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is given."
msgstr ""
+"シェルオプションや位置パラメーターの値を設定・消去します\n"
+" \n"
+" シェル属性や位置パラメーターの値を変更、またはシェル変数の名前と値\n"
+" を表示します。\n"
+" \n"
+" オプション:\n"
+" -a Mark variables which are modified or created for export.\n"
+" -b Notify of job termination immediately.\n"
+" -e コマンドの終了ステータスが 0 以外の場合は即時終了します。 \n"
+" -f Disable file name generation (globbing).\n"
+" -h Remember the location of commands as they are looked up.\n"
+" -k All assignment arguments are placed in the environment for a\n"
+" command, not just those that precede the command name.\n"
+" -m ジョブ制御を有効にします。\n"
+" -n コマンドを読み込みますが実行しません。\n"
+" -o option-name\n"
+" Set the variable corresponding to option-name:\n"
+" allexport same as -a\n"
+" braceexpand same as -B\n"
+" emacs use an emacs-style line editing interface\n"
+" errexit same as -e\n"
+" errtrace same as -E\n"
+" functrace same as -T\n"
+" hashall same as -h\n"
+" histexpand same as -H\n"
+" history enable command history\n"
+" ignoreeof the shell will not exit upon reading EOF\n"
+" interactive-comments\n"
+" allow comments to appear in interactive commands\n"
+" keyword same as -k\n"
+" monitor same as -m\n"
+" noclobber same as -C\n"
+" noexec same as -n\n"
+" noglob same as -f\n"
+" nolog currently accepted but ignored\n"
+" notify same as -b\n"
+" nounset same as -u\n"
+" onecmd same as -t\n"
+" physical same as -P\n"
+" pipefail the return value of a pipeline is the status of\n"
+" the last command to exit with a non-zero status,\n"
+" or zero if no command exited with a non-zero status\n"
+" posix change the behavior of bash where the default\n"
+" operation differs from the Posix standard to\n"
+" match the standard\n"
+" privileged same as -p\n"
+" verbose same as -v\n"
+" vi use a vi-style line editing interface\n"
+" xtrace same as -x\n"
+" -p Turned on whenever the real and effective user ids do not match.\n"
+" Disables processing of the $ENV file and importing of shell\n"
+" functions. Turning this option off causes the effective uid and\n"
+" gid to be set to the real uid and gid.\n"
+" -t Exit after reading and executing one command.\n"
+" -u Treat unset variables as an error when substituting.\n"
+" -v Print shell input lines as they are read.\n"
+" -x Print commands and their arguments as they are executed.\n"
+" -B the shell will perform brace expansion\n"
+" -C If set, disallow existing regular files to be overwritten\n"
+" by redirection of output.\n"
+" -E If set, the ERR trap is inherited by shell functions.\n"
+" -H Enable ! style history substitution. This flag is on\n"
+" by default when the shell is interactive.\n"
+" -P If set, do not follow symbolic links when executing commands\n"
+" such as cd which change the current directory.\n"
+" -T If set, the DEBUG trap is inherited by shell functions.\n"
+" - Assign any remaining arguments to the positional parameters.\n"
+" The -x and -v options are turned off.\n"
+" \n"
+" Using + rather than - causes these flags to be turned off. The\n"
+" flags can also be used upon invocation of the shell. The current\n"
+" set of flags may be found in $-. The remaining n ARGs are positional\n"
+" parameters and are assigned, in order, to $1, $2, .. $n. If no\n"
+" ARGs are given, all shell variables are printed.\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられない限り成功を返します。"
#: builtins.c:1104
msgid ""
@@ -3049,6 +3698,21 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is given or a NAME is read-only."
msgstr ""
+"シェル変数および関数の値・属性を消去します。\n"
+" \n"
+" 各 NAMEに対して関連する値または関数を削除します。\n"
+" \n"
+" オプション:\n"
+" -f\t各 NAME をシェル関数として扱います\n"
+" -v\t各 NAME をシェル変数として扱います\n"
+" \n"
+" オプションが無い場合、最初に変数を消去しようと試みます。失敗した場合には\n"
+" 関数を消去しようと試みます。\n"
+" \n"
+" いくつかの変数は消去できません。`readonly' も参照してください。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるか NAME が読み取り専用の場合を除き成功を返します。"
#: builtins.c:1124
msgid ""
@@ -3067,6 +3731,22 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is given or NAME is invalid."
msgstr ""
+"シェル変数に export 属性を設定します。\n"
+" \n"
+" 各 NAME に対して後に続けて実行されるコマンドの環境変数として自動的に\n"
+" エクスポートされるようにマークします。VALUE が与えられた場合はエクス\n"
+" ポートする前に値を設定します。\n"
+"\n"
+" オプション:\n"
+" -f\tシェル関数を参照します\n"
+" -n\t各 NAME に対してエクスポート属性を削除します\n"
+" -p\t全てのエクスポートされた変数・関数一覧を表示します\n"
+" \n"
+" 引数 `--' 以降はオプションとして処理されません。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるか、無効な NAME が与えられない限り成功\n"
+" を返します。"
#: builtins.c:1143
msgid ""
@@ -3087,6 +3767,23 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is given or NAME is invalid."
msgstr ""
+"シェル変数を変更不可にします。\n"
+" \n"
+" 各 NAME を読み取り専用にします。これらの NAME の値はこれ以降の代入で\n"
+" 変更ができなくなります。VALUE が与えられた場合、読み取り専用にする前\n"
+" に値が設定されます。\n"
+" \n"
+" オプション:\n"
+" -a\tインデックス型配列変数を示します\n"
+" -A\t連想配列変数を示します\n"
+" -f\tシェル関数を示します\n"
+" -p\t全ての読み取り専用変数・関数の一覧を表示します\n"
+" \n"
+" 引数 `--' は以降のオプション処理を無効にします\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるか、与えられた NAME が無効な場合を除き成功\n"
+" を返します。"
#: builtins.c:1164
msgid ""
@@ -3098,6 +3795,13 @@ msgid ""
" Exit Status:\n"
" Returns success unless N is negative or greater than $#."
msgstr ""
+"位置パラメーターをシフトします。\n"
+" \n"
+" 位置パラメーター名 $N+1,$N+2 ... を $1,$2 ... に変更します。 \n"
+" N が与えられなかった場合、1 と見なされます。\n"
+" \n"
+" 終了ステータス:\n"
+" Nが負の値または $# より大きい場合を除き成功を返します。"
#: builtins.c:1176 builtins.c:1191
msgid ""
@@ -3112,6 +3816,16 @@ msgid ""
" Returns the status of the last command executed in FILENAME; fails if\n"
" FILENAME cannot be read."
msgstr ""
+"ファイルを読み込み現在のシェルでコマンドを実行します。\n"
+" \n"
+" FILENAME を読み込み現在のシェルでコマンドを実行します。$PATH の項目が\n"
+" FILENAME が含まれるディレクトリを見つけるために使用されます。引数\n"
+" ARGUMENTS が与えられた場合 FILENAME を実行する際の位置パラメーターと\n"
+" して使用されます。\n"
+" \n"
+" 終了ステータス:\n"
+" FILENAME で最後に実行したコマンドのステータスを返します。FILENAME が\n"
+" 読み込めなかった場合は失敗を返します。"
#: builtins.c:1207
msgid ""
@@ -3126,6 +3840,16 @@ msgid ""
" Exit Status:\n"
" Returns success unless job control is not enabled or an error occurs."
msgstr ""
+"シェルの実行を一時停止します。\n"
+" \n"
+" SIGCONT シグナルを受け取るまでこのシェルの実行を一時停止します。強制\n"
+" オプションが無い限りログインシェルは一時停止できません。\n"
+" \n"
+" オプション:\n"
+" -f\tシェルがログインシェルだとしても強制的に一時停止します\n"
+" \n"
+" 終了ステータス:\n"
+" ジョブ制御が有効でないかエラーが発生しない限り成功を返します。"
#: builtins.c:1223
msgid ""
@@ -3201,6 +3925,77 @@ msgid ""
" Returns success if EXPR evaluates to true; fails if EXPR evaluates to\n"
" false or an invalid argument is given."
msgstr ""
+"条件式を評価します。\n"
+" \n"
+" 式 EXPR の結果に応じて 0 (真、true) または 1 (偽、false) を返します。\n"
+" 式は単項演算子または二項演算子です。単項演算子はファイルのステータス\n"
+" を評価するために使用されます。文字列演算子に加えて数値比較演算子も\n"
+" 存在します。\n"
+" \n"
+" ファイル演算子:\n"
+" \n"
+" -a FILE ファイルが存在する時に真(true)。\n"
+" -b FILE ファイルがブロック特殊デバイスの時に真(true)。\n"
+" -c FILE ファイルがキャラクタ特殊デバイスの時に真(true)。\n"
+" -d FILE ファイルがディレクトリの時に真(true)。\n"
+" -e FILE ファイルが存在する時に真(true)。\n"
+" -f FILE ファイルが存在し、通常ファイルの時に真(true)。\n"
+" -g FILE ファイルに setgid が設定されている時に真(true)。\n"
+" -h FILE ファイルがシンボリックリンクの時に真(true)。\n"
+" -L FILE ファイルがシンボリックリンクの時に真(true)。\n"
+" -k FILE ファイルに sticky ビットが設定されている時に真(true)。\n"
+" -p FILE ファイルが名前付きパイプの時に真(true)。\n"
+" -r FILE ファイルがユーザに対して読み込み可能な時に真(true)。\n"
+" -s FILE ファイルが存在し、かつ空ファイルでない場合に真(true)。\n"
+" -S FILE ファイルがソケットの場合に真(true)。\n"
+" -t FD FD(ファイル識別子) が端末に開いている時に真(true)。\n"
+" -u FILE ファイルに setuid が設定されている時に真(true)。\n"
+" -w FILE ファイルがユーザに対して書き込み可能な時に真(true)。\n"
+" -x FILE ファイルがユーザに対して実行可能な時に真(true)。\n"
+" -O FILE ファイルをユーザが実効的に所有されている時に真(true)。\n"
+" -G FILE ファイルのグループにユーザが実効的に所属している時に真(true)。\n"
+" -N FILE ファイルを最後に読み込んだ以降に変更されている時に真(true)。\n"
+" \n"
+" FILE1 -nt FILE2 file1 が file2 より新しい時(更新時間に基づく)に真(true)。\n"
+" \n"
+" \n"
+" FILE1 -ot FILE2 file1 が file2 より古い時に真(true)。\n"
+" \n"
+" FILE1 -ef FILE2 file1 が file2 に対するハードリンクの時に真(true)。\n"
+" \n"
+" 文字列演算子:\n"
+" \n"
+" -z STRING 文字列が空の時に真(true)。\n"
+" \n"
+" -n STRING\n"
+" STRING 文字列が空でない時に真(true)。\n"
+" \n"
+" STRING1 = STRING2\n"
+" 文字列が同一の時に真(true)。\n"
+" STRING1 != STRING2\n"
+" 文字列が同一でない時に真(true)。\n"
+" STRING1 < STRING2\n"
+" 辞書順で STRING1 が STRING2 より前の時に真(true)。\n"
+" STRING1 > STRING2\n"
+" 辞書順で STRING1 が STRING2 より後の時に真(true)。\n"
+" \n"
+" その他演算子:\n"
+" \n"
+" -o OPTION シェルオプション OPTION が有効な時に真(true)。\n"
+" ! EXPR 式 expr が偽(fales)の時に真(true)。\n"
+" EXPR1 -a EXPR2 式 expr1 および expr2 の両方とも真(true)の時に真(true)。\n"
+" EXPR1 -o EXPR2 式 expr1 または expr2 のいずれかが真(true)の時に真(true)。\n"
+" \n"
+" arg1 OP arg2 数値比較演算を行います。OP は -eq, -ne, -lt, -le, -gt,\n"
+" または -ge のいずれかです。\n"
+" \n"
+" 数値演算二項演算子は ARG1 と ARG2 の関係がそれぞれ、等しい(-eq)、\n"
+" 等しくない(-ne)、より小さい(-lt)、以下(-le)、より大きい(-gt)、または\n"
+" 以上(-ge)の時に真(true)を返します。\n"
+" \n"
+" 終了ステータス:\n"
+" 式 EXPR の評価値が真(true)の時に成功を返します。EXPR の評価値が偽(false) または\n"
+" 引数が無効な場合に失敗を返します。"
#: builtins.c:1299
msgid ""
@@ -3209,6 +4004,10 @@ msgid ""
" This is a synonym for the \"test\" builtin, but the last argument must\n"
" be a literal `]', to match the opening `['."
msgstr ""
+"条件式を評価します。\n"
+" \n"
+" これは test 組み込み関数と同義語です。ただし、最後の引数に開始の`['と一致\n"
+" するように文字`]'を与えなければいけません。"
#: builtins.c:1308
msgid ""
@@ -3220,6 +4019,13 @@ msgid ""
" Exit Status:\n"
" Always succeeds."
msgstr ""
+"プロセスの時間を表示します。\n"
+" \n"
+" シェルとその子プロセスが使用したユーザー時間とシステム時間それぞれの累積を\n"
+" 表示します。\n"
+" \n"
+" 終了ステータス:\n"
+" 常に成功を返します。"
#: builtins.c:1320
msgid ""
@@ -3251,6 +4057,33 @@ msgid ""
" Exit Status:\n"
" Returns success unless a SIGSPEC is invalid or an invalid option is given."
msgstr ""
+"シグナルまたは他のイベントをトラップします。\n"
+" \n"
+" シェルがシグナルを受け取るか他の条件が発生した時に実行されるハンドラーを\n"
+" 定義および有効化します。\n"
+" \n"
+" ARG はシグナル SIGNAL_SPEC を受け取った時に読み込まれ実行されるコマンド\n"
+" です。もし ARG が無い (かつシグナル SIGNAL_SPEC が与えられた場合) または\n"
+" `-' の場合、各指定したシグナルはオリジナルの値にリセットされます。\n"
+" ARG が NULL 文字列の場合、各シグナル SIGNAL_SPEC はシェルにおよび起動さ\n"
+" れたコマンドによって無視されます。\n"
+" \n"
+" もし SIGNAL_SPEC が EXIT (0) の場合、ARG がシェルの終了時に実行されます。\n"
+" もし SIGNAL_SPEC が DEBUGの場合 ARG は単に毎回コマンドの前に実行されます。\n"
+" \n"
+" もし引数が与えられない場合、 trap は各シグナルに割り当てられたコマンドの\n"
+" 一覧を表示します。\n"
+" \n"
+" オプション:\n"
+" -l\tシグナル名とシグナル番号の対応一覧を表示します\n"
+" -p\t各 SIGNAL_SPEC に関連づけられた trap コマンドを表示します\n"
+" \n"
+" 各 SIGNAL_SPEC は <signal.h> にあるシグナル名かシグナル番号です。シグ\n"
+" ナル名は大文字小文字を区別しません。また SIG 接頭辞はオプションです。\n"
+" シグナルはシェルに対して \"kill -signal $$\" で送ることができます。\n"
+" \n"
+" 終了ステータス:\n"
+" SIGSPEC が無効か、無効なオプションを与えられない限り成功を返します。"
#: builtins.c:1352
msgid ""
@@ -3280,6 +4113,32 @@ msgid ""
" Exit Status:\n"
" Returns success if all of the NAMEs are found; fails if any are not found."
msgstr ""
+"コマンドの種類に関する情報を表示します。\n"
+" \n"
+" 各 NAME に対してコマンド名として使われた時にどのように解釈されるかを\n"
+" 示します。\n"
+" \n"
+" オプション:\n"
+" -a\tNAME という名前になっている実行可能なものの全ての位置を表示し\n"
+" \tます。これには `-p' が同時に指定されていない場合に限ってエイリアス、\n"
+" \t組み込み関数、シェル関数も含みます\n"
+" -f\tシェル関数の検索を抑止します\n"
+" -P\t各 NAME に対して PATH 探索を強制します。エイリアス、組み込み\n"
+" \t関数、シェル関数があったとしても実行されるディスク上のファイル名を\n"
+" \t返します\n"
+" -p\t実行されるディスク上のファイル名を返します。`type -t NAME'\n"
+" \tが `file' を返さない場合、何も返しません。\n"
+" -t\t次のいずれかの単語を返します。`alias', `keyword', `function',\n"
+" \t `builtin', `file' or `'。それぞれ NAME がエイリアス、シェル予約語、\n"
+" \tシェル関数、シェル組み込み関数、ディスク上のファイル、何も見つからない\n"
+" \tに対応します。\n"
+" \n"
+" 引数:\n"
+" NAME\t解釈するコマンドの名前です。\n"
+" \n"
+" 終了ステータス:\n"
+" 全ての NAME が見つかった場合に成功を返します。どれかが見つからなかった場合\n"
+" は失敗を返します。"
#: builtins.c:1383
msgid ""
@@ -3323,6 +4182,44 @@ msgid ""
" Exit Status:\n"
" Returns success unless an invalid option is supplied or an error occurs."
msgstr ""
+"シェルの資源制限を変更します。\n"
+" \n"
+" シェルおよびシェルが作成するプロセスが使用可能な資源に対する制御を提供します。\n"
+" ただし、システムがそのような制御を許可している場合です。\n"
+" \n"
+" オプション:\n"
+" -S\t`soft' 資源制限を使用します\n"
+" -H\t`hard' 資源制限を使用します\n"
+" -a\t現在の全ての資源制限を表示します\n"
+" -b\tソケットバッファサイズ\n"
+" -c\t作成されるコアファイルの最大サイズ\n"
+" -d\tプロセスデータセグメントの最大サイズ\n"
+" -e\tスケジュール優先度の最大値 (`nice')\n"
+" -f\tシェル及び子プロセスが書き込み可能なファイルサイズの最大値\n"
+" -i\t保留するシグナルの最大数\n"
+" -l\tメモリにロックできるプロセスの最大サイズ\n"
+" -m\t最大のメモリサイズ\n"
+" -n\tファイル記述子を開くことができる最大数\n"
+" -p\tパイプのバッファサイズ\n"
+" -q\tPOSIX message queues の最大バイト数\n"
+" -r\tリアルタイムスケジュール優先度の最大値\n"
+" -s\tスタックサイズの最大値\n"
+" -t\tCPU時間総量の最大値 (秒単位)\n"
+" -u\tユーザープロセスの最大数\n"
+" -v\t仮想メモリのサイズ\n"
+" -x\tファイルロックの最大数\n"
+" \n"
+" LIMIT が与えられた場合、指定した資源に対する新しい値になります。特別な\n"
+" LIMIT の値である `soft'、`hard'、および `unlimited' は現在の `soft' 制限\n"
+" 現在の`hard' 制限および制限なしをそれぞれ意味します。\n"
+" それ以外の場合、指定した資源の現在の値が表示されます。オプションが与え\n"
+" られなかった場合 -f と見なされます。\n"
+" \n"
+" 値は 1024 バイト単位で増加します。例外は -t が秒単位、-p が 512バイト\n"
+" 単位、および -u は1プロセス単位です。\n"
+" \n"
+" 終了ステータス:\n"
+" 無効なオプションが与えられるかエラーが発生しない限り成功を返します。"
#: builtins.c:1428
msgid ""
@@ -3341,6 +4238,20 @@ msgid ""
" Exit Status:\n"
" Returns success unless MODE is invalid or an invalid option is given."
msgstr ""
+"ファイルのモードマスクを表示または設定します。\n"
+" \n"
+" ユーザーがファイル作成時のマスクを MODE に設定します。MODE が指定されない場合\n"
+" 現在のマスクの値を表示します。\n"
+" \n"
+" MODE が数値で開始した場合8進数として解釈されます。それ以外は chmod(1) で受け\n"
+" 入れられるシンボルモードの文字列として扱われます。\n"
+" \n"
+" オプション:\n"
+" -p\tMODE が指定されない場合、入力として再利用可能な形式で表示します\n"
+" -S\tシンボルモードで出力します。それ以外は8進数で出力します\n"
+" \n"
+" 終了ステータス:\n"
+" MODE が無効か、無効なオプションが与えられない限り成功を返します。"
#: builtins.c:1448
msgid ""
@@ -3356,6 +4267,16 @@ msgid ""
" Returns the status of ID; fails if ID is invalid or an invalid option is\n"
" given."
msgstr ""
+"ジョブの実行完了を待ち、終了ステータスを返します。\n"
+" \n"
+" ID で識別されるプロセス (プロセスID または ジョブ指定) を待ち、その終了\n"
+" ステータスを返します。ID が与えられない場合、現在アクティブな全ての子プ\n"
+" ロセスを待ち 0 を返します。ID がジョブ指定の場合ジョブのパイプラインに\n"
+" ある全てのプロセスを待ちます。\n"
+" \n"
+" 終了ステータス:\n"
+" ID の終了ステータスを返します。IDが無効であるか、無効なオプションが\n"
+" 与えられた場合には失敗を返します。"
#: builtins.c:1466
msgid ""
@@ -3369,6 +4290,15 @@ msgid ""
" Returns the status of ID; fails if ID is invalid or an invalid option is\n"
" given."
msgstr ""
+"プロセスの実行完了を待ち、終了ステータスを返します。\n"
+" \n"
+" 指定されたプロセスを待ち、その終了ステータスを返します。PID が与えられ\n"
+" ない場合、現在アクティブな全ての子プロセスを待ち、0 を返します。PID は\n"
+" プロセスIDでなければなりません。\n"
+" \n"
+" 終了ステータス:\n"
+" IDの終了ステータスを返します。IDが無効か、無効なオプションが与えられた\n"
+" 場合はエラーを返します。"
#: builtins.c:1481
msgid ""
@@ -3382,6 +4312,14 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"リストの各要素に対してコマンドを実行します。\n"
+" \n"
+" `for' ループではリストの各要素に対して一連のコマンドを実行します。\n"
+" `in WORDS ...;' が存在しない場合、`in \"$@\"' であると見なされます。\n"
+" WORDS の要素が NAME の値として代入され COMMANDS が実行されます。\n"
+" \n"
+" 終了ステータス:\n"
+" 最後に実行したコマンドのステータスを返します。"
#: builtins.c:1495
msgid ""
@@ -3399,6 +4337,19 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"算術 for ループ\n"
+" \n"
+" 以下と等価です。\n"
+" \t(( EXP1 ))\n"
+" \twhile (( EXP2 )); do\n"
+" \t\tCOMMANDS\n"
+" \t\t(( EXP3 ))\n"
+" \tdone\n"
+" EXP1、EXP2、および EXP3 は数式です。いずれかの数式を省略した場合、\n"
+" 値が 1 であるとして評価されます。\n"
+" \n"
+" 終了ステータス:\n"
+" 最後に実行したコマンドのステータスを返します。"
#: builtins.c:1513
msgid ""
@@ -3419,6 +4370,22 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"Select words from a list and execute commands.\n"
+" \n"
+" The WORDS are expanded, generating a list of words. The\n"
+" set of expanded words is printed on the standard error, each\n"
+" preceded by a number. If `in WORDS' is not present, `in \"$@\"'\n"
+" is assumed. The PS3 prompt is then displayed and a line read\n"
+" from the standard input. If the line consists of the number\n"
+" corresponding to one of the displayed words, then NAME is set\n"
+" to that word. If the line is empty, WORDS and the prompt are\n"
+" redisplayed. If EOF is read, the command completes. Any other\n"
+" value read causes NAME to be set to null. The line read is saved\n"
+" in the variable REPLY. COMMANDS are executed after each selection\n"
+" until a break command is executed.\n"
+" \n"
+" 終了ステータス:\n"
+" 最後に実行したコマンドのステータスを返します。"
#: builtins.c:1534
msgid ""
@@ -3435,6 +4402,18 @@ msgid ""
" Exit Status:\n"
" The return status is the return status of PIPELINE."
msgstr ""
+"パイプラインを実行する時に消費された時間を報告します。\n"
+" \n"
+" PIPELINE を実行し、終了時に PIPELINE を実行するために費やされた実\n"
+" 時間、ユーザー CPU 時間、およびシステム CPU 時間の要約を表示します。\n"
+" \n"
+" オプション:\n"
+" -p\t移植性のある Posix 形式で時間の要約を表示します\n"
+" \n"
+" 変数 TIMEFORMAT の値が出力の形式として使用されます。\n"
+" \n"
+" 終了ステータス:\n"
+" PIPELINE の戻り値が終了ステータスとなります。"
#: builtins.c:1551
msgid ""
@@ -3448,6 +4427,7 @@ msgid ""
msgstr ""
#: builtins.c:1563
+#, fuzzy
msgid ""
"Execute commands based on conditional.\n"
" \n"
@@ -3462,6 +4442,18 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"条件に従ってコマンドを実行します。\n"
+" \n"
+" The `if COMMANDS' list is executed. If its exit status is zero, then the\n"
+" `then COMMANDS' list is executed. Otherwise, each `elif COMMANDS' list is\n"
+" executed in turn, and if its exit status is zero, the corresponding\n"
+" `then COMMANDS' list is executed and the if command completes. Otherwise,\n"
+" the `else COMMANDS' list is executed, if present. The exit status of the\n"
+" entire construct is the exit status of the last command executed, or zero\n"
+" if no condition tested true.\n"
+" \n"
+" Exit Status:\n"
+" Returns the status of the last command executed."
#: builtins.c:1580
msgid ""
@@ -3473,6 +4465,13 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"テストが成功する限りコマンドを実行します。\n"
+" \n"
+" `while' COMMANDS にある最後のコマンドの終了ステータスが 0 である間 \n"
+" COMMANDS を展開して実行します。\n"
+" \n"
+" 終了ステータス:\n"
+" 最後に実行したコマンドのステータスを返します。"
#: builtins.c:1592
msgid ""
@@ -3484,6 +4483,13 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"テストが失敗する限りコマンドを実行します。\n"
+" \n"
+" `until' COMMANDS にある最後のコマンドの終了ステータスが 0 でない間\n"
+" COMMANDS を展開して実行します。\n"
+" \n"
+" 終了ステータス:\n"
+" 最後に実行したコマンドのステータスを返します。"
#: builtins.c:1604
msgid ""
@@ -3497,6 +4503,15 @@ msgid ""
" Exit Status:\n"
" Returns the exit status of COMMAND."
msgstr ""
+"NAME という名前の非同期プロセスを作成します。\n"
+" \n"
+" COMMAND を非同期で実行します。コマンドの標準出力および標準入力は実行\n"
+" しているシェルのインデックス型配列変数 NAME の要素添字 0、1 に設定\n"
+" されるファイル記述子へのパイプとして接続されます。\n"
+" デフォルトの NAME は \"COPROC\" です。\n"
+" \n"
+" 終了ステータス:\n"
+" COMMAND の終了ステータスを返します。"
#: builtins.c:1618
msgid ""
@@ -3510,6 +4525,15 @@ msgid ""
" Exit Status:\n"
" Returns success unless NAME is readonly."
msgstr ""
+"シェル関数を定義します。\n"
+" \n"
+" NAME という名前のシェル関数を作成します。単にコマンドとして起動された時は\n"
+" NAME は COMMANDs をシェルのコンテキスト内で呼び出します。NAME を起動した\n"
+" 時に引数は関数に $1...$n という位置パラメーターで、関数名は $FUNCNAME\n"
+" 変数として渡されます。\n"
+" \n"
+" 終了ステータス:\n"
+" NAME が読み取り専用でない限り成功を返します。"
#: builtins.c:1632
msgid ""
@@ -3521,6 +4545,13 @@ msgid ""
" Exit Status:\n"
" Returns the status of the last command executed."
msgstr ""
+"複数のコマンドを一つにグループ化します。\n"
+" \n"
+" 複数のコマンドをグループにして1セットとして実行します。これはコマンド\n"
+" のセット全体をリダイレクトする一つの方法です。\n"
+" \n"
+" 終了ステータス:\n"
+" 最後に実行したコマンドのステータスを返します。"
#: builtins.c:1644
msgid ""
@@ -3546,6 +4577,13 @@ msgid ""
" Exit Status:\n"
" Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise."
msgstr ""
+"算術式を評価します。\n"
+" \n"
+" 算術式の規定に基づいて EXPRESSION を評価します。\"let EXPRESSION\"\n"
+" と等価です。\n"
+" \n"
+" 終了ステータス:\n"
+" EXPRESSION の評価値が 0 の場合は 1、それ以外は 0 を返します。"
#: builtins.c:1671
msgid ""
@@ -3625,6 +4663,55 @@ msgid ""
" HISTIGNORE\tA colon-separated list of patterns used to decide which\n"
" \t\tcommands should be saved on the history list.\n"
msgstr ""
+"通常の変数名とその使用法。\n"
+" \n"
+" BASH_VERSION\tBashのバージョン情報。\n"
+" CDPATH\t`cd`の引数として与えられたディレクトリを検索する際に\n"
+" \t\t使用されるコロン (:) で区切られたディレクトリの一覧。\n"
+" GLOBIGNORE\tパス名を展開する時に無視されるコロン (:) で区切られた\n"
+" \t\tファイル名パターンの一覧。\n"
+" HISTFILE\tコマンドヒストリが保存されるファイル名。\n"
+" HISTFILESIZE\tヒストリファイルに保存することができる最大行数。\n"
+" HISTSIZE\t実行中のシェルがアクセスできる最大ヒストリ行数。\n"
+" HOME\tログインディレクトリの完全パス名。\n"
+" HOSTNAME\t現在のホスト名。\n"
+" HOSTTYPE\tこのバージョンの Bash を実行している CPU の種類。\n"
+" IGNOREEOF\tControls the action of the shell on receipt of an EOF\n"
+" \t\tcharacter as the sole input. If set, then the value\n"
+" \t\tof it is the number of EOF characters that can be seen\n"
+" \t\tin a row on an empty line before the shell will exit\n"
+" \t\t(default 10). When unset, EOF signifies the end of input.\n"
+" MACHTYPE\tA string describing the current system Bash is running on.\n"
+" MAILCHECK\tBash がメールを確認する頻度 (秒単位)。\n"
+" MAILPATH\tBash が新規メールを確認するコロン (:) で区切られた\n"
+" \t\tファイル名の一覧。\n"
+" OSTYPE\tこのバージョンの Bash を実行している OS のバージョン。\n"
+" PATH\tコマンドを検索する際に使用されるコロン (:) で区切ら\n"
+" \t\tれたディレクトリの一覧。\n"
+" PROMPT_COMMAND\tプライマリプロンプトが表示される前に毎回実行\n"
+" \t\tされるコマンド。\n"
+" PS1\t\tプライマリプロンプト文字列。\n"
+" PS2\t\tセカンダリプロンプト文字列。\n"
+" PWD\t\t現在のディレクトリの完全パス名。\n"
+" SHELLOPTS\tコロン (:) で区切られた有効なシェルオプション一覧。\n"
+" TERM\t現在の端末種類名。\n"
+" TIMEFORMAT\tThe output format for timing statistics displayed by the\n"
+" \t\t`time' reserved word.\n"
+" auto_resume\tNon-null means a command word appearing on a line by\n"
+" \t\titself is first looked for in the list of currently\n"
+" \t\tstopped jobs. If found there, that job is foregrounded.\n"
+" \t\tA value of `exact' means that the command word must\n"
+" \t\texactly match a command in the list of stopped jobs. A\n"
+" \t\tvalue of `substring' means that the command word must\n"
+" \t\tmatch a substring of the job. Any other value means that\n"
+" \t\tthe command must be a prefix of a stopped job.\n"
+" histchars\tCharacters controlling history expansion and quick\n"
+" \t\tsubstitution. The first character is the history\n"
+" \t\tsubstitution character, usually `!'. The second is\n"
+" \t\tthe `quick substitution' character, usually `^'. The\n"
+" \t\tthird is the `history comment' character, usually `#'.\n"
+" HISTIGNORE\tA colon-separated list of patterns used to decide which\n"
+" \t\tcommands should be saved on the history list.\n"
#: builtins.c:1754
msgid ""
diff --git a/sig.c b/sig.c
index dd9bd3dd..be16ce5b 100644
--- a/sig.c
+++ b/sig.c
@@ -216,7 +216,8 @@ 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). */
+ this when a trap is defined for EXIT (0) or when trap is run
+ to display signal dispositions. */
void
initialize_terminating_signals ()
{
@@ -250,7 +251,8 @@ initialize_terminating_signals ()
XSAFLAGS(i) = oact.sa_flags;
/* Don't do anything with signals that are ignored at shell entry
if the shell is not interactive. */
- if (!interactive_shell && XHANDLER (i) == SIG_IGN)
+ /* 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));
@@ -273,7 +275,8 @@ initialize_terminating_signals ()
XSAFLAGS(i) = 0;
/* Don't do anything with signals that are ignored at shell entry
if the shell is not interactive. */
- if (!interactive_shell && XHANDLER (i) == SIG_IGN)
+ /* 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));
@@ -652,12 +655,13 @@ set_signal_handler (sig, handler)
act.sa_handler = handler;
act.sa_flags = 0;
-#if 0
- if (sig == SIGALRM)
- act.sa_flags |= SA_INTERRUPT; /* XXX */
- else
+
+ /* 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 (sig == SIGCHLD)
act.sa_flags |= SA_RESTART; /* XXX */
-#endif
+
sigemptyset (&act.sa_mask);
sigemptyset (&oact.sa_mask);
sigaction (sig, &act, &oact);
diff --git a/sig.c~ b/sig.c~
index a217b894..60cdf6cb 100644
--- a/sig.c~
+++ b/sig.c~
@@ -60,6 +60,8 @@ extern int executing_list;
extern int comsub_ignore_return;
extern int parse_and_execute_level, shell_initialized;
+extern void intialize_siglist ();
+
/* Non-zero after SIGINT. */
volatile int interrupt_state = 0;
@@ -214,7 +216,8 @@ 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). */
+ this when a trap is defined for EXIT (0) or when trap is run
+ to display signal dispositions. */
void
initialize_terminating_signals ()
{
@@ -248,7 +251,8 @@ initialize_terminating_signals ()
XSAFLAGS(i) = oact.sa_flags;
/* Don't do anything with signals that are ignored at shell entry
if the shell is not interactive. */
- if (!interactive_shell && XHANDLER (i) == SIG_IGN)
+ /* 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));
@@ -271,7 +275,8 @@ initialize_terminating_signals ()
XSAFLAGS(i) = 0;
/* Don't do anything with signals that are ignored at shell entry
if the shell is not interactive. */
- if (!interactive_shell && XHANDLER (i) == SIG_IGN)
+ /* 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));
diff --git a/subst.c b/subst.c
index 7abf3424..e3dac4dc 100644
--- a/subst.c
+++ b/subst.c
@@ -42,6 +42,7 @@
#include "bashintl.h"
#include "shell.h"
+#include "parser.h"
#include "flags.h"
#include "jobs.h"
#include "execute_cmd.h"
@@ -785,6 +786,7 @@ string_extract_double_quoted (string, sindex, stripdq)
/* Process a character that was quoted by a backslash. */
if (pass_next)
{
+ /* XXX - take another look at this in light of Interp 221 */
/* Posix.2 sez:
``The backslash shall retain its special meaning as an escape
@@ -960,7 +962,7 @@ skip_double_quoted (string, slen, sind)
if (string[i + 1] == LPAREN)
ret = extract_command_subst (string, &si, SX_NOALLOC);
else
- ret = extract_dollar_brace_string (string, &si, 1, SX_NOALLOC);
+ ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
i = si + 1;
continue;
@@ -1266,7 +1268,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
#if 0
/* Process a nested command substitution, but only if we're parsing a
- command substitution. XXX - for bash-4.2 */
+ command substitution. XXX - bash-4.2 */
if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN)
{
si = i + 2;
@@ -1370,7 +1372,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
{
register int i, c;
size_t slen;
- int pass_character, nesting_level, si;
+ int pass_character, nesting_level, si, dolbrace_state;
char *result, *t;
DECLARE_MBSTATE;
@@ -1378,6 +1380,12 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
nesting_level = 1;
slen = strlen (string + *sindex) + *sindex;
+ /* The handling of dolbrace_state needs to agree with the code in parse.y:
+ parse_matched_pair() */
+ dolbrace_state = 0;
+ if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+ dolbrace_state = (flags & SX_POSIXEXP) ? DOLBRACE_QUOTE : DOLBRACE_PARAM;
+
i = *sindex;
while (c = string[i])
{
@@ -1432,6 +1440,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
continue;
}
+#if 1
/* Pass the contents of single-quoted and double-quoted strings
through verbatim. */
if (c == '\'' || c == '"')
@@ -1442,9 +1451,44 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
/* skip_XXX_quoted leaves index one past close quote */
continue;
}
+#else /* XXX - bash-4.2 */
+ /* Pass the contents of double-quoted strings through verbatim. */
+ if (c == '"')
+ {
+ si = i + 1;
+ i = skip_double_quoted (string, slen, si);
+ /* skip_XXX_quoted leaves index one past close quote */
+ continue;
+ }
+
+ if (c == '\'')
+ {
+/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/
+ if (posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE)
+ ADVANCE_CHAR (string, slen, i);
+ else
+ {
+ si = i + 1;
+ i = skip_single_quoted (string, slen, si);
+ }
+
+ continue;
+ }
+#endif
/* move past this character, which was not special. */
ADVANCE_CHAR (string, slen, i);
+
+ /* This logic must agree with parse.y:parse_matched_pair, since they
+ share the same defines. */
+ if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 0)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0)
+ dolbrace_state = DOLBRACE_OP;
+ else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0)
+ dolbrace_state = DOLBRACE_WORD;
}
if (c == 0 && nesting_level)
@@ -5596,7 +5640,7 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
w->word = temp;
#else /* XXX - bash-4.2 */
- /* From Posix group discussion Feb-March 2010 */
+ /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */
free (temp);
w->word = t1;
@@ -6895,7 +6939,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
{
/* Extract the contents of the ${ ... } expansion
according to the Posix.2 rules. */
- value = extract_dollar_brace_string (string, &sindex, quoted, 0);
+ value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#') ? SX_POSIXEXP : 0);
if (string[sindex] == RBRACE)
sindex++;
else
@@ -7015,7 +7059,6 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
{
/* If the operator is `+', we don't want the value of the named
variable for anything, just the value of the right hand side. */
-
if (c == '+')
{
/* XXX -- if we're double-quoted and the named variable is "$@",
@@ -7030,7 +7073,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
if (value)
{
/* XXX - bash-4.2 */
- /* From Posix discussion on austin-group list */
+ /* From Posix discussion on austin-group list. Issue 221 */
if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
quoted |= Q_DOLBRACE;
ret = parameter_brace_expand_rhs (name, value, c,
@@ -7076,6 +7119,8 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
if (contains_dollar_at)
*contains_dollar_at = 0;
+ /* XXX - bash-4.2 */
+ /* From Posix discussion on austin-group list. Issue 221 */
if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
quoted |= Q_DOLBRACE;
ret = parameter_brace_expand_rhs (name, value, c, quoted,
@@ -7894,9 +7939,10 @@ add_string:
else
tflag = 0;
+ /* XXX - bash-4.2 */
/* From Posix discussion on austin-group list: Backslash escaping
- { or } in ${...} is removed. */
- if ((quoted & Q_DOLBRACE) && (c == '{' || c == '}'))
+ a } in ${...} is removed. Issue 0000221 */
+ if ((quoted & Q_DOLBRACE) && c == RBRACE)
{
SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
}
diff --git a/subst.c~ b/subst.c~
index 7c5f2b3f..88c2077a 100644
--- a/subst.c~
+++ b/subst.c~
@@ -42,6 +42,7 @@
#include "bashintl.h"
#include "shell.h"
+#include "parser.h"
#include "flags.h"
#include "jobs.h"
#include "execute_cmd.h"
@@ -785,6 +786,7 @@ string_extract_double_quoted (string, sindex, stripdq)
/* Process a character that was quoted by a backslash. */
if (pass_next)
{
+ /* XXX - take another look at this in light of Interp 221 */
/* Posix.2 sez:
``The backslash shall retain its special meaning as an escape
@@ -960,7 +962,7 @@ skip_double_quoted (string, slen, sind)
if (string[i + 1] == LPAREN)
ret = extract_command_subst (string, &si, SX_NOALLOC);
else
- ret = extract_dollar_brace_string (string, &si, 1, SX_NOALLOC);
+ ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
i = si + 1;
continue;
@@ -1266,7 +1268,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
#if 0
/* Process a nested command substitution, but only if we're parsing a
- command substitution. XXX - for bash-4.2 */
+ command substitution. XXX - bash-4.2 */
if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN)
{
si = i + 2;
@@ -1370,7 +1372,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
{
register int i, c;
size_t slen;
- int pass_character, nesting_level, si;
+ int pass_character, nesting_level, si, dolbrace_state;
char *result, *t;
DECLARE_MBSTATE;
@@ -1378,6 +1380,12 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
nesting_level = 1;
slen = strlen (string + *sindex) + *sindex;
+ /* The handling of dolbrace_state needs to agree with the code in parse.y:
+ parse_matched_pair() */
+ dolbrace_state = 0;
+ if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
+ dolbrace_state = (flags & SX_POSIXEXP) ? DOLBRACE_QUOTE : DOLBRACE_PARAM;
+
i = *sindex;
while (c = string[i])
{
@@ -1432,6 +1440,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
continue;
}
+#if 1
/* Pass the contents of single-quoted and double-quoted strings
through verbatim. */
if (c == '\'' || c == '"')
@@ -1442,9 +1451,44 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
/* skip_XXX_quoted leaves index one past close quote */
continue;
}
+#else /* XXX - bash-4.2 */
+ /* Pass the contents of double-quoted strings through verbatim. */
+ if (c == '"')
+ {
+ si = i + 1;
+ i = skip_double_quoted (string, slen, si);
+ /* skip_XXX_quoted leaves index one past close quote */
+ continue;
+ }
+
+ if (c == '\'')
+ {
+/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/
+ if (posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE)
+ ADVANCE_CHAR (string, slen, i);
+ else
+ {
+ si = i + 1;
+ i = skip_single_quoted (string, slen, si);
+ }
+
+ continue;
+ }
+#endif
/* move past this character, which was not special. */
ADVANCE_CHAR (string, slen, i);
+
+ /* This logic must agree with parse.y:parse_matched_pair, since they
+ share the same defines. */
+ if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 0)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1)
+ dolbrace_state = DOLBRACE_QUOTE;
+ else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0)
+ dolbrace_state = DOLBRACE_OP;
+ else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0)
+ dolbrace_state = DOLBRACE_WORD;
}
if (c == 0 && nesting_level)
@@ -5591,11 +5635,12 @@ parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
else
#endif /* ARRAY_VARS */
bind_variable (name, t1, 0);
-#if 0
+#if 1
free (t1);
w->word = temp;
-#else /* XXX - bash-4.2 -- depends on Posix group interpretation */
+#else /* XXX - bash-4.2 */
+ /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */
free (temp);
w->word = t1;
@@ -6894,7 +6939,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
{
/* Extract the contents of the ${ ... } expansion
according to the Posix.2 rules. */
- value = extract_dollar_brace_string (string, &sindex, quoted, 0);
+ value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#') ? SX_POSIXEXP : 0);
if (string[sindex] == RBRACE)
sindex++;
else
@@ -7029,7 +7074,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
if (value)
{
/* XXX - bash-4.2 */
- /* From Posix discussion on austin-group list */
+ /* From Posix discussion on austin-group list. Issue 221 */
if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
quoted |= Q_DOLBRACE;
ret = parameter_brace_expand_rhs (name, value, c,
@@ -7075,6 +7120,8 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
if (contains_dollar_at)
*contains_dollar_at = 0;
+ /* XXX - bash-4.2 */
+ /* From Posix discussion on austin-group list. Issue 221 */
if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
quoted |= Q_DOLBRACE;
ret = parameter_brace_expand_rhs (name, value, c, quoted,
@@ -7893,9 +7940,10 @@ add_string:
else
tflag = 0;
+ /* XXX - bash-4.2 */
/* From Posix discussion on austin-group list: Backslash escaping
- { or } in ${...} is removed. */
- if ((quoted & Q_DOLBRACE) && (c == '{' || c == '}'))
+ a } in ${...} is removed. Issue 0000221 */
+ if ((quoted & Q_DOLBRACE) && c == RBRACE)
{
SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
}
diff --git a/subst.h b/subst.h
index 5d2717e0..8896a117 100644
--- a/subst.h
+++ b/subst.h
@@ -47,14 +47,15 @@
#define ASS_MKASSOC 0x04
/* Flags for the string extraction functions. */
-#define SX_NOALLOC 0x01 /* just skip; don't return substring */
-#define SX_VARNAME 0x02 /* variable name; for string_extract () */
-#define SX_REQMATCH 0x04 /* closing/matching delimiter required */
-#define SX_COMMAND 0x08 /* extracting a shell script/command */
-#define SX_NOCTLESC 0x10 /* don't honor CTLESC quoting */
-#define SX_NOESCCTLNUL 0x20 /* don't let CTLESC quote CTLNUL */
-#define SX_NOLONGJMP 0x40 /* don't longjmp on fatal error */
-#define SX_ARITHSUB 0x80 /* extracting $(( ... )) (currently unused) */
+#define SX_NOALLOC 0x0001 /* just skip; don't return substring */
+#define SX_VARNAME 0x0002 /* variable name; for string_extract () */
+#define SX_REQMATCH 0x0004 /* closing/matching delimiter required */
+#define SX_COMMAND 0x0008 /* extracting a shell script/command */
+#define SX_NOCTLESC 0x0010 /* don't honor CTLESC quoting */
+#define SX_NOESCCTLNUL 0x0020 /* don't let CTLESC quote CTLNUL */
+#define SX_NOLONGJMP 0x0040 /* don't longjmp on fatal error */
+#define SX_ARITHSUB 0x0080 /* extracting $(( ... )) (currently unused) */
+#define SX_POSIXEXP 0x0100 /* extracting new Posix pattern removal expansions in extract_dollar_brace_string */
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
diff --git a/subst.h~ b/subst.h~
index 7628939a..5d2717e0 100644
--- a/subst.h~
+++ b/subst.h~
@@ -30,7 +30,8 @@
decide whether to retain the backslash. Q_KEEP_BACKSLASH means
to unconditionally retain the backslash. Q_PATQUOTE means that we're
expanding a pattern ${var%#[#%]pattern} in an expansion surrounded
- by double quotes. */
+ by double quotes. Q_DOLBRACE means we are expanding a ${...} word, so
+ backslashes should also escape { and } and be removed. */
#define Q_DOUBLE_QUOTES 0x01
#define Q_HERE_DOCUMENT 0x02
#define Q_KEEP_BACKSLASH 0x04
@@ -38,6 +39,7 @@
#define Q_QUOTED 0x10
#define Q_ADDEDQUOTES 0x20
#define Q_QUOTEDNULL 0x40
+#define Q_DOLBRACE 0x80
/* Flag values controlling how assignment statements are treated. */
#define ASS_APPEND 0x01
diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST
index 72ec06a2..3efcf32d 100755
--- a/tests/RUN-ONE-TEST
+++ b/tests/RUN-ONE-TEST
@@ -1,4 +1,4 @@
-BUILD_DIR=/usr/local/build/bash/bash-current
+BUILD_DIR=/usr/local/build/chet/bash/bash-current
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
diff --git a/trap.c b/trap.c
index ab059bbe..d0c932fc 100644
--- a/trap.c
+++ b/trap.c
@@ -121,6 +121,13 @@ int wait_signal_received;
sigmodes[sig] |= SIG_HARD_IGNORE; \
} while (0)
+#define SETORIGSIG(sig,handler) \
+ do { \
+ original_signals[sig] = handler; \
+ if (original_signals[sig] == SIG_IGN) \
+ sigmodes[sig] |= SIG_HARD_IGNORE; \
+ } while (0)
+
#define GET_ORIGINAL_SIGNAL(sig) \
if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \
GETORIGSIG(sig)
@@ -140,7 +147,7 @@ initialize_traps ()
{
pending_traps[i] = 0;
trap_list[i] = (char *)DEFAULT_SIG;
- sigmodes[i] = SIG_INHERITED;
+ sigmodes[i] = SIG_INHERITED; /* XXX - only set, not used */
original_signals[i] = IMPOSSIBLE_TRAP_HANDLER;
}
@@ -600,6 +607,15 @@ get_original_signal (sig)
GETORIGSIG (sig);
}
+void
+set_original_signal (sig, handler)
+ int sig;
+ SigHandler *handler;
+{
+ if (sig > 0 && sig < NSIG && original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER)
+ SETORIGSIG (sig, handler);
+}
+
/* Restore the default action for SIG; i.e., the action the shell
would have taken before you used the trap command. This is called
from trap_builtin (), which takes care to restore the handlers for
@@ -1071,6 +1087,12 @@ signal_is_ignored (sig)
return (sigmodes[sig] & SIG_IGNORED);
}
+int
+signal_is_hard_ignored (sig)
+{
+ return (sigmodes[sig] & SIG_HARD_IGNORE);
+}
+
void
set_signal_ignored (sig)
int sig;
diff --git a/trap.c~ b/trap.c~
index 5e5b3a21..309bc622 100644
--- a/trap.c~
+++ b/trap.c~
@@ -121,6 +121,13 @@ int wait_signal_received;
sigmodes[sig] |= SIG_HARD_IGNORE; \
} while (0)
+#define SETORIGSIG(sig,handler) \
+ do { \
+ original_signals[sig] = handler; \
+ if (original_signals[sig] == SIG_IGN) \
+ sigmodes[sig] |= SIG_HARD_IGNORE; \
+ } while (0)
+
#define GET_ORIGINAL_SIGNAL(sig) \
if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \
GETORIGSIG(sig)
@@ -140,7 +147,7 @@ initialize_traps ()
{
pending_traps[i] = 0;
trap_list[i] = (char *)DEFAULT_SIG;
- sigmodes[i] = SIG_INHERITED;
+ sigmodes[i] = SIG_INHERITED; /* XXX - only set, not used */
original_signals[i] = IMPOSSIBLE_TRAP_HANDLER;
}
@@ -600,6 +607,15 @@ get_original_signal (sig)
GETORIGSIG (sig);
}
+void
+set_original_signal (sig, handler)
+ int sig;
+ SigHandler *handler;
+{
+ if (sig > 0 && sig < NSIG && original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER)
+ SETORIGSIG (sig, handler);
+}
+
/* Restore the default action for SIG; i.e., the action the shell
would have taken before you used the trap command. This is called
from trap_builtin (), which takes care to restore the handlers for
@@ -912,7 +928,6 @@ run_interrupt_trap ()
_run_trap_internal (SIGINT, "interrupt trap");
}
-#ifdef INCLUDE_UNUSED
/* Free all the allocated strings in the list of traps and reset the trap
values to the default. Intended to be called from subshells that want
to complete work done by reset_signal_handlers upon execution of a
@@ -936,7 +951,6 @@ free_trap_string (sig)
change_signal (sig, (char *)DEFAULT_SIG);
sigmodes[sig] &= ~SIG_TRAPPED;
}
-#endif
/* Reset the handler for SIG to the original value. */
static void
@@ -1073,10 +1087,17 @@ signal_is_ignored (sig)
return (sigmodes[sig] & SIG_IGNORED);
}
+int
+signal_is_hard_ignored (sig)
+{
+ return (sigmodes[sig] & SIG_HARD_IGNORE);
+}
+
void
set_signal_ignored (sig)
int sig;
{
+itrace("set_signal_ignored: %d set to SIG_HARD_IGNORE", sig);
sigmodes[sig] |= SIG_HARD_IGNORE;
original_signals[sig] = SIG_IGN;
}
diff --git a/trap.h b/trap.h
index 3a913afb..e507cbda 100644
--- a/trap.h
+++ b/trap.h
@@ -93,9 +93,10 @@ extern char *signal_name __P((int));
extern int decode_signal __P((char *, int));
extern void run_interrupt_trap __P((void));
extern int maybe_call_trap_handler __P((int));
+extern int signal_is_special __P((int));
extern int signal_is_trapped __P((int));
extern int signal_is_ignored __P((int));
-extern int signal_is_special __P((int));
+extern int signal_is_hard_ignored __P((int));
extern void set_signal_ignored __P((int));
extern int signal_in_progress __P((int));
diff --git a/trap.h~ b/trap.h~
index c34b7eb5..3a913afb 100644
--- a/trap.h~
+++ b/trap.h~
@@ -49,6 +49,9 @@
#define DSIG_SIGPREFIX 0x01 /* don't alllow `SIG' PREFIX */
#define DSIG_NOCASE 0x02 /* case-insensitive comparison */
+/* A value which can never be the target of a trap handler. */
+#define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps
+
#define signal_object_p(x,f) (decode_signal (x,f) != NO_SIG)
#define TRAP_STRING(s) \