5/8
---
-lib/readline/bind.c
-
lib/readline/complete.c
- add support for a third argument to fnprint and print_filename,
which supports replacing a specified portion of the pathnames
- in update_line, when fixing _rl_last_c_pos after drawing the first
line of the prompt, use the number of invisible chars on the first
line as the offset, instead of the total number of invisible chars
+ - use prompt_multibyte_characters, the number of multibyte chars in
+ the prompt string, to short-circuit some relatively expensive
+ multibyte text processing in rl_redisplay
5/21
----
bashline.h
- new extern declaration for bashline_reset()
+
+ 5/23
+ ----
+bashhist.c
+ - new function, bash_clear_history, clears the history and resets any
+ associated internal bash state
+
+bashhist.h
+ - extern declaration for bash_clear_history
+
+builtins/history.def
+ - call bash_clear_history instead of clear_history for `history -c'.
+ Fixes part of problem reported by Scott McDermott
+ <scott.m.mcdermott@gmail.com>
+ - decrement history_lines_this_session in delete_histent, called for
+ `history -d'
+
+builtins/history.def,bashhist.[ch]
+ - move delete_histent() to bashhist.c; rename to bash_delete_histent
+ - move delete_last_history() to bashhist.c; rename to
+ bash_delete_last_history()
+
+ 5/25
+ ----
+braces.c
+ - add another parameter to mkseq(), the number of digits to put into
+ each member of a numeric sequence (width), changes to determine
+ any zero-padding go into expand_seqterm
+ - changes to expand_seqterm to allow user-specified increments
+
+bashline.[ch],shell.c,sig.c
+ - switched names of bashline_reinitialize and bashline_reset to better
+ reflect their functions
+ - when searching $PATH for directories to use for command completion,
+ make sure to free `current_path' before going out of scope
+ - new bindable function `dabbrev-expand', which is more or less
+ menu completion using dynamic history completion as the generator
+ - changes to bash_execute_unix_command to set variables for the
+ executed command like programmable completion: READLINE_LINE
+ (rl_line_buffer) and READLINE_POINT (rl_point)
+ - change to bash_execute_unix_command to allow the executed command
+ to change the readline line buffer by modifying the value of
+ READLINE_LINE and to change rl_point by modifying the value of
+ READLINE_POINT
+
+common.h
+ - new SEVAL_ defines for later parse_string changes from 4.0-devel
+ branch
+
+command.h
+ - new defines for new &>> r_append_err_and_out redirection
+
+builtins/evalstring.c
+ - new function, parse_string, parses a command from a passed string
+ and returns the number of characters consumed. For satisfying
+ Posix rules when parsing command substitutions, from bash-4.0-devel
+ branch
+ - split out common prolog code from parse_string and
+ parse_and_execute into a separate function called from both
+
+parse.y
+ - small changes to add symbols needed for parse_string
+ - parser change to add `|&' as synonym for `2>&1 |'; translation is
+ performed at parse time so |& never shows up in output of
+ print_command, for instance. Picked up from zsh, merged in from
+ bash-4.0-devel branch
+
+parse.y,{redir,copy_cmd,dispose_cmd,make_cmd,print_cmd}.c
+ - implement new &>> r_append_err_and_out (like >>foo 2>&1); merged
+ in from bash-4.0-devel branch
+
+doc/{bash.1,bashref.texi},lib/readline/doc/rluser.texi
+ - document new optional increment in brace expansion
+ - document new zero-padded fixed-width integer brace expansion
+ - document new `dabbrev-expand' bindable readline command
+ - document new effects of `bind -x' setting and reading the values of
+ READLINE_LINE and READLINE_POINT
+ - document new |& synonym for `2>&1 |' pipeline operator
+
+ 5/26
+ ----
+parse.y
+ - recognize new ;& and ;;& case action list terminator tokens and
+ implement them in the grammar, setting CASEPAT_FALLTHROUGH and
+ CASEPAT_TESTNEXT flags as appropriate
+
+print_cmd.c
+ - print new ;& and ;;& case clause action list terminators as
+ appropriate
+
+execute_cmd.c
+ - implement new case clause action list terminators:
+ ;& - fall through to actions associated with next pattern list
+ ;;& - fall through to tests in next pattern list
+
+doc/{bash.1,bashref.texi}
+ - document new ;& and ;;& case clause action list terminators
5/8
---
-lib/readline/bind.c
-
lib/readline/complete.c
- add support for a third argument to fnprint and print_filename,
which supports replacing a specified portion of the pathnames
- in update_line, when fixing _rl_last_c_pos after drawing the first
line of the prompt, use the number of invisible chars on the first
line as the offset, instead of the total number of invisible chars
+ - use prompt_multibyte_characters, the number of multibyte chars in
+ the prompt string, to short-circuit some relatively expensive
+ multibyte text processing in rl_redisplay
5/21
----
shell.c
- shell_reinitialize now calls reinit_special_variables
+ - shell_reinitialize now calls bashline_reset
variables.h
- new extern declaration for reinit_special_variables
+
+bashline.c
+ - new function, bashline_reset(), called when the shell reinitializes
+ in shell_reinitialize. Right now, just resets
+ bash_readline_initialized to 0.
+
+bashline.h
+ - new extern declaration for bashline_reset()
+
+ 5/23
+ ----
+bashhist.c
+ - new function, bash_clear_history, clears the history and resets any
+ associated internal bash state
+
+bashhist.h
+ - extern declaration for bash_clear_history
+
+builtins/history.def
+ - call bash_clear_history instead of clear_history for `history -c'.
+ Fixes part of problem reported by Scott McDermott
+ <scott.m.mcdermott@gmail.com>
+ - decrement history_lines_this_session in delete_histent, called for
+ `history -d'
+
+builtins/history.def,bashhist.[ch]
+ - move delete_histent() to bashhist.c; rename to bash_delete_histent
+ - move delete_last_history() to bashhist.c; rename to
+ bash_delete_last_history()
+
+ 5/25
+ ----
+braces.c
+ - add another parameter to mkseq(), the number of digits to put into
+ each member of a numeric sequence (width), changes to determine
+ any zero-padding go into expand_seqterm
+ - changes to expand_seqterm to allow user-specified increments
+
+bashline.[ch],shell.c,sig.c
+ - switched names of bashline_reinitialize and bashline_reset to better
+ reflect their functions
+ - when searching $PATH for directories to use for command completion,
+ make sure to free `current_path' before going out of scope
+ - new bindable function `dabbrev-expand', which is more or less
+ menu completion using dynamic history completion as the generator
+ - changes to bash_execute_unix_command to set variables for the
+ executed command like programmable completion: READLINE_LINE
+ (rl_line_buffer) and READLINE_POINT (rl_point)
+ - change to bash_execute_unix_command to allow the executed command
+ to change the readline line buffer by modifying the value of
+ READLINE_LINE and to change rl_point by modifying the value of
+ READLINE_POINT
+
+common.h
+ - new SEVAL_ defines for later parse_string changes from 4.0-devel
+ branch
+
+command.h
+ - new defines for new &>> r_append_err_and_out redirection
+
+builtins/evalstring.c
+ - new function, parse_string, parses a command from a passed string
+ and returns the number of characters consumed. For satisfying
+ Posix rules when parsing command substitutions, from bash-4.0-devel
+ branch
+ - split out common prolog code from parse_string and
+ parse_and_execute into a separate function called from both
+
+parse.y
+ - small changes to add symbols needed for parse_string
+ - parser change to add `|&' as synonym for `2>&1 |'; translation is
+ performed at parse time so |& never shows up in output of
+ print_command, for instance. Picked up from zsh, merged in from
+ bash-4.0-devel branch
+
+parse.y,{redir,copy_cmd,dispose_cmd,make_cmd,print_cmd}.c
+ - implement new &>> r_append_err_and_out (like >>foo 2>&1); merged
+ in from bash-4.0-devel branch
+
+doc/{bash.1,bashref.texi},lib/readline/doc/rluser.texi
+ - document new optional increment in brace expansion
+ - document new zero-padded fixed-width integer brace expansion
+ - document new `dabbrev-expand' bindable readline command
+ - document new effects of `bind -x' setting and reading the values of
+ READLINE_LINE and READLINE_POINT
+ - document new |& synonym for `2>&1 |' pipeline operator
tests/source4.sub f
tests/source5.sub f
tests/source6.sub f
+tests/case.tests f
+tests/case.right f
tests/comsub.tests f
tests/comsub.right f
tests/cond.tests f
tests/redir6.sub f
tests/redir7.sub f
tests/redir8.sub f
+tests/redir9.sub f
tests/rhs-exp.tests f
tests/rhs-exp.right f
tests/rsh.tests f
tests/run-array2 f
tests/run-braces f
tests/run-builtins f
+tests/run-case f
tests/run-comsub f
tests/run-cond f
tests/run-cprint f
tests/trap2a.sub f 755
tests/type.tests f
tests/type.right f
+tests/type1.sub f
tests/varenv.right f
tests/varenv.sh f
tests/varenv1.sub f
doc/fdl.txt f
support/Makefile.in f
support/bashversion.c f
+support/checkbashisms f 755
support/config.guess f
support/config.rpath f 755
support/config.sub f
tests/redir6.sub f
tests/redir7.sub f
tests/redir8.sub f
+tests/redir9.sub f
tests/rhs-exp.tests f
tests/rhs-exp.right f
tests/rsh.tests f
tests/trap2a.sub f 755
tests/type.tests f
tests/type.right f
+tests/type1.sub f
tests/varenv.right f
tests/varenv.sh f
tests/varenv1.sub f
}
}
+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
void
bash_history_enable ()
{
- remember_on_history = enable_history_list = 1;
+ remember_on_history = 1;
#if defined (BANG_HISTORY)
history_expansion_inhibited = 0;
#endif
}
}
+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;
+}
+
#ifdef INCLUDE_UNUSED
/* Write the existing history out to the history file. */
void
extern void bash_history_reinit __P((int));
extern void bash_history_disable __P((void));
extern void bash_history_enable __P((void));
+extern void bash_clear_history __P((void));
+extern int bash_delete_histent __P((int));
+extern int bash_delete_last_history __P((void));
extern void load_history __P((void));
extern void save_history __P((void));
extern int maybe_append_history __P((char *));
--- /dev/null
+/* bashhist.h -- interface to the bash history functions in bashhist.c. */
+
+/* Copyright (C) 1993 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 2, 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; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if !defined (_BASHHIST_H_)
+#define _BASHHIST_H_
+
+#include "stdc.h"
+
+/* Flag values for history_control */
+#define HC_IGNSPACE 0x01
+#define HC_IGNDUPS 0x02
+#define HC_ERASEDUPS 0x04
+
+#define HC_IGNBOTH (HC_IGNSPACE|HC_IGNDUPS)
+
+extern int remember_on_history;
+extern int enable_history_list; /* value for `set -o history' */
+extern int history_lines_this_session;
+extern int history_lines_in_file;
+extern int history_expansion;
+extern int history_control;
+extern int command_oriented_history;
+extern int current_command_first_line_saved;
+extern int hist_last_line_added;
+extern int hist_last_line_pushed;
+
+# if defined (BANG_HISTORY)
+extern int history_expansion_inhibited;
+# endif /* BANG_HISTORY */
+
+extern void bash_initialize_history __P((void));
+extern void bash_history_reinit __P((int));
+extern void bash_history_disable __P((void));
+extern void bash_history_enable __P((void));
+extern void bash_clear_history __P((void));
+extern int bash_delete_histent __P((int));
+extern void load_history __P((void));
+extern void save_history __P((void));
+extern int maybe_append_history __P((char *));
+extern int maybe_save_shell_history __P((void));
+extern char *pre_process_line __P((char *, int, int));
+extern void maybe_add_history __P((char *));
+extern void bash_add_history __P((char *));
+extern int check_add_history __P((char *, int));
+extern int history_number __P((void));
+
+extern void setup_history_ignore __P((char *));
+
+extern char *last_history_line __P((void));
+
+#endif /* _BASHHIST_H_ */
static void build_history_completion_array __P((void));
static char *history_completion_generator __P((const char *, int));
static int dynamic_complete_history __P((int, int));
+static int bash_dabbrev_expand __P((int, int));
static void initialize_hostname_list __P((void));
static void add_host_name __P((char *));
#endif
extern int current_command_line_count, last_command_exit_value;
+extern int array_needs_making;
extern int posixly_correct, no_symbolic_links;
extern char *current_prompt_string, *ps1_prompt;
extern STRING_INT_ALIST word_token_alist[];
free (rl_completer_word_break_characters);
rl_completer_word_break_characters = nval;
}
-itrace("enable_hostname_completion: rl_completer_word_break_characters = %s", rl_completer_word_break_characters);
+
return (old_value);
}
#endif
rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
+ rl_add_defun ("dabbrev-expand", bash_dabbrev_expand, -1);
/* Bind defaults before binding our custom shell keybindings. */
if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
}
void
-bashline_reset ()
+bashline_reinitialize ()
{
bash_readline_initialized = 0;
}
word. This just resets all the completion functions to the right thing.
It's called from throw_to_top_level(). */
void
-bashline_reinitialize ()
+bashline_reset ()
{
tilde_initialize ();
rl_attempted_completion_function = attempt_shell_completion;
/* Get the next directory from the path. If there is none, then we
are all done. */
- if (!path || !path[path_index] ||
+ if (path == 0 || path[path_index] == 0 ||
(current_path = extract_colon_unit (path, &path_index)) == 0)
return ((char *)NULL);
free (filename_hint);
filename_hint = sh_makepath (current_path, hint, 0);
+ free (current_path); /* XXX */
}
inner:
*dirname = nd;
}
}
-
+
/* Handle symbolic link references and other directory name
expansions while hacking completion. */
static int
int count, key;
{
int r;
-
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
orig_func = rl_completion_entry_function;
orig_attempt_func = rl_attempted_completion_function;
+
rl_completion_entry_function = history_completion_generator;
rl_attempted_completion_function = (rl_completion_func_t *)NULL;
return r;
}
+static int
+bash_dabbrev_expand (count, key)
+ int count, key;
+{
+ int r;
+ rl_compentry_func_t *orig_func;
+ rl_completion_func_t *orig_attempt_func;
+
+ orig_func = rl_menu_completion_entry_function;
+ orig_attempt_func = rl_attempted_completion_function;
+
+ rl_menu_completion_entry_function = history_completion_generator;
+ rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+ rl_filename_completion_desired = 0;
+
+ /* XXX - use rl_completion_mode here? */
+ if (rl_last_func == bash_dabbrev_expand)
+ rl_last_func = rl_menu_complete;
+ r = rl_menu_complete (count, key);
+
+ rl_last_func = bash_dabbrev_expand;
+ rl_menu_completion_entry_function = orig_func;
+ rl_attempted_completion_function = orig_attempt_func;
+
+ return r;
+}
+
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int
bash_complete_username (ignore, ignore2)
Keymap ckmap; /* current keymap */
Keymap xkmap; /* unix command executing keymap */
register int i;
- char *cmd;
+ intmax_t mi;
+ int save_point;
sh_parser_state_t ps;
+ char *cmd, *value, *l;
+ SHELL_VAR *v;
+ char ibuf[INT_STRLEN_BOUND(int) + 1];
/* First, we need to find the right command to execute. This is tricky,
because we might have already indirected into another keymap. */
rl_crlf (); /* move to a new line */
+ v = bind_variable ("READLINE_LINE", rl_line_buffer, 0);
+ if (v)
+ VSETATTR (v, att_exported);
+ l = value_cell (v);
+ save_point = rl_point;
+ value = inttostr (rl_point, ibuf, sizeof (ibuf));
+ v = bind_int_variable ("READLINE_POINT", value);
+ if (v)
+ VSETATTR (v, att_exported);
+ array_needs_making = 1;
+
save_parser_state (&ps);
+ parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST|SEVAL_NOFREE);
+ restore_parser_state (&ps);
- cmd = savestring (cmd);
- parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST);
+ v = find_variable ("READLINE_LINE");
+ if (value_cell (v) != l)
+ maybe_make_readline_line (value_cell (v));
+ v = find_variable ("READLINE_POINT");
+ if (v && legal_number (value_cell (v), &mi))
+ {
+ i = mi;
+ if (i != save_point)
+ {
+ rl_point = i;
+ if (rl_point > rl_end)
+ rl_point = rl_end;
+ else if (rl_point < 0)
+ rl_point = 0;
+ }
+ }
- restore_parser_state (&ps);
+ unbind_variable ("READLINE_LINE");
+ unbind_variable ("READLINE_POINT");
+ array_needs_making = 1;
/* and restore the readline buffer and display after command execution. */
rl_forced_update_display ();
static void build_history_completion_array __P((void));
static char *history_completion_generator __P((const char *, int));
static int dynamic_complete_history __P((int, int));
+static int bash_dabbrev_expand __P((int, int));
static void initialize_hostname_list __P((void));
static void add_host_name __P((char *));
#endif
extern int current_command_line_count, last_command_exit_value;
+extern int array_needs_making;
extern int posixly_correct, no_symbolic_links;
extern char *current_prompt_string, *ps1_prompt;
extern STRING_INT_ALIST word_token_alist[];
free (rl_completer_word_break_characters);
rl_completer_word_break_characters = nval;
}
-itrace("enable_hostname_completion: rl_completer_word_break_characters = %s", rl_completer_word_break_characters);
+
return (old_value);
}
#endif
rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
+ rl_add_defun ("dabbrev-expand", bash_dabbrev_expand, -1);
/* Bind defaults before binding our custom shell keybindings. */
if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
bash_readline_initialized = 1;
}
+void
+bashline_reinitialize ()
+{
+ bash_readline_initialized = 0;
+}
+
/* On Sun systems at least, rl_attempted_completion_function can end up
getting set to NULL, and rl_completion_entry_function set to do command
word completion if Bash is interrupted while trying to complete a command
word. This just resets all the completion functions to the right thing.
It's called from throw_to_top_level(). */
void
-bashline_reinitialize ()
+bashline_reset ()
{
tilde_initialize ();
rl_attempted_completion_function = attempt_shell_completion;
/* Get the next directory from the path. If there is none, then we
are all done. */
- if (!path || !path[path_index] ||
+ if (path == 0 || path[path_index] == 0 ||
(current_path = extract_colon_unit (path, &path_index)) == 0)
return ((char *)NULL);
free (filename_hint);
filename_hint = sh_makepath (current_path, hint, 0);
+ free (current_path); /* XXX */
}
inner:
*dirname = nd;
}
}
-
+
/* Handle symbolic link references and other directory name
expansions while hacking completion. */
static int
int count, key;
{
int r;
-
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
orig_func = rl_completion_entry_function;
orig_attempt_func = rl_attempted_completion_function;
+
rl_completion_entry_function = history_completion_generator;
rl_attempted_completion_function = (rl_completion_func_t *)NULL;
return r;
}
+static int
+bash_dabbrev_expand (count, key)
+ int count, key;
+{
+ int r;
+ rl_compentry_func_t *orig_func;
+ rl_completion_func_t *orig_attempt_func;
+
+ orig_func = rl_menu_completion_entry_function;
+ orig_attempt_func = rl_attempted_completion_function;
+
+ rl_menu_completion_entry_function = history_completion_generator;
+ rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+ rl_filename_completion_desired = 0;
+
+ /* XXX - use rl_completion_mode here? */
+ if (rl_last_func == bash_dabbrev_expand)
+ rl_last_func = rl_menu_complete;
+ r = rl_menu_complete (count, key);
+
+ rl_last_func = bash_dabbrev_expand;
+ rl_menu_completion_entry_function = orig_func;
+ rl_attempted_completion_function = orig_attempt_func;
+
+ return r;
+}
+
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int
bash_complete_username (ignore, ignore2)
Keymap ckmap; /* current keymap */
Keymap xkmap; /* unix command executing keymap */
register int i;
- char *cmd;
sh_parser_state_t ps;
+ char *cmd, *value, *l;
+ SHELL_VAR *v;
+ char ibuf[INT_STRLEN_BOUND(int) + 1];
/* First, we need to find the right command to execute. This is tricky,
because we might have already indirected into another keymap. */
rl_crlf (); /* move to a new line */
+ v = bind_variable ("READLINE_LINE", rl_line_buffer, 0);
+ if (v)
+ VSETATTR (v, att_exported);
+ l = value_cell (v);
+ value = inttostr (rl_point, ibuf, sizeof (ibuf));
+ v = bind_int_variable ("READLINE_POINT", value);
+ if (v)
+ VSETATTR (v, att_exported);
+ array_needs_making = 1;
+
save_parser_state (&ps);
+ parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST|SEVAL_NOFREE);
+ restore_parser_state (&ps);
- cmd = savestring (cmd);
- parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST);
+ v = find_variable ("READLINE_LINE");
+ if (value_cell (v) != l)
+ maybe_make_readline_line (value_cell (v));
- restore_parser_state (&ps);
+ unbind_variable ("READLINE_LINE");
+ unbind_variable ("READLINE_POINT");
+ array_needs_making = 1;
/* and restore the readline buffer and display after command execution. */
rl_forced_update_display ();
/* bashline.h -- interface to the bash readline functions in bashline.c. */
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
extern void posix_readline_initialize __P((int));
extern int enable_hostname_completion __P((int));
extern void initialize_readline __P((void));
+extern void bashline_reset __P((void));
extern void bashline_reinitialize __P((void));
extern int bash_re_edit __P((char *));
/* braces.c -- code for doing word expansion in curly braces. */
-/* Copyright (C) 1987-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
static int brace_gobbler __P((char *, size_t, int *, int));
static char **expand_amble __P((char *, size_t, int));
static char **expand_seqterm __P((char *, size_t));
-static char **mkseq __P((int, int, int, int));
+static char **mkseq __P((int, int, int, int, int));
static char **array_concat __P((char **, char **));
#else
static int brace_gobbler ();
#define ST_BAD 0
#define ST_INT 1
#define ST_CHAR 2
+#define ST_ZINT 3
static char **
-mkseq (start, end, incr, type)
- int start, end, incr, type;
+mkseq (start, end, incr, type, width)
+ int start, end, incr, type, width;
{
int n, i;
char **result, *t;
#endif
if (type == ST_INT)
result[i++] = itos (n);
+ else if (type == ST_ZINT)
+ {
+ int len;
+ len = asprintf (&t, "%0*d", width, n);
+ result[i++] = t;
+ }
else
{
t = (char *)xmalloc (2);
t[1] = '\0';
result[i++] = t;
}
- if (n == end)
- break;
n += incr;
+ if ((incr < 0 && n < end) || (incr > 0 && n > end))
+ break;
}
while (1);
size_t tlen;
{
char *t, *lhs, *rhs;
- int i, lhs_t, rhs_t, lhs_v, rhs_v;
+ int i, lhs_t, rhs_t, lhs_v, rhs_v, incr, lhs_l, rhs_l, width;
intmax_t tl, tr;
- char **result;
+ char **result, *ep;
t = strstr (text, BRACE_SEQ_SPECIFIER);
if (t == 0)
return ((char **)NULL);
- i = t - text; /* index of start of BRACE_SEQ_SPECIFIER */
- lhs = substring (text, 0, i);
- rhs = substring (text, i + sizeof(BRACE_SEQ_SPECIFIER) - 1, tlen);
+ lhs_l = t - text; /* index of start of BRACE_SEQ_SPECIFIER */
+ lhs = substring (text, 0, lhs_l);
+ rhs = substring (text, lhs_l + sizeof(BRACE_SEQ_SPECIFIER) - 1, tlen);
if (lhs[0] == 0 || rhs[0] == 0)
{
sides have to match. */
lhs_t = (legal_number (lhs, &tl)) ? ST_INT :
((ISALPHA (lhs[0]) && lhs[1] == 0) ? ST_CHAR : ST_BAD);
- rhs_t = (legal_number (rhs, &tr)) ? ST_INT :
- ((ISALPHA (rhs[0]) && rhs[1] == 0) ? ST_CHAR : ST_BAD);
+
+ /* Decide on rhs and whether or not it looks like the user specified
+ an increment */
+ ep = 0;
+ if (ISDIGIT (rhs[0]) || ((rhs[0] == '+' || rhs[0] == '-') && ISDIGIT (rhs[1])))
+ {
+ rhs_t = ST_INT;
+ tr = strtoimax (rhs, &ep, 10);
+ if (ep && *ep != 0 && *ep != '.')
+ rhs_t = ST_BAD; /* invalid */
+ }
+ else if (ISALPHA (rhs[0]) && (rhs[1] == 0 || rhs[1] == '.'))
+ {
+ rhs_t = ST_CHAR;
+ ep = rhs + 1;
+ }
+ else
+ {
+ rhs_t = ST_BAD;
+ ep = 0;
+ }
+
+ incr = 1;
+ if (rhs_t != ST_BAD)
+ {
+ if (ep && *ep == '.' && ep[1] == '.' && ep[2])
+ incr = strtoimax (ep + 2, &ep, 10);
+ if (*ep != 0)
+ rhs_t = ST_BAD; /* invalid incr */
+ }
if (lhs_t != rhs_t || lhs_t == ST_BAD || rhs_t == ST_BAD)
{
{
lhs_v = (unsigned char)lhs[0];
rhs_v = (unsigned char)rhs[0];
+ width = 1;
}
else
{
lhs_v = tl; /* integer truncation */
rhs_v = tr;
+
+ /* Decide whether or not the terms need zero-padding */
+ rhs_l = tlen - lhs_l - sizeof (BRACE_SEQ_SPECIFIER) + 1;
+ width = 0;
+ if (lhs_l > 1 && lhs[0] == '0')
+ width = lhs_l, lhs_t = ST_ZINT;
+ if (lhs_l > 2 && lhs[0] == '-' && lhs[1] == '0')
+ width = lhs_l, lhs_t = ST_ZINT;
+ if (rhs_l > 1 && rhs[0] == '0' && width < rhs_l)
+ width = rhs_l, lhs_t = ST_ZINT;
+ if (rhs_l > 2 && rhs[0] == '-' && rhs[1] == '0' && width < rhs_l)
+ width = rhs_l, lhs_t = ST_ZINT;
}
- result = mkseq (lhs_v, rhs_v, 1, lhs_t);
+ result = mkseq (lhs_v, rhs_v, incr, lhs_t, width);
free (lhs);
free (rhs);
/* braces.c -- code for doing word expansion in curly braces. */
-/* Copyright (C) 1987-2003 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
static int brace_gobbler __P((char *, size_t, int *, int));
static char **expand_amble __P((char *, size_t, int));
static char **expand_seqterm __P((char *, size_t));
-static char **mkseq __P((int, int, int, int));
+static char **mkseq __P((int, int, int, int, int));
static char **array_concat __P((char **, char **));
#else
static int brace_gobbler ();
#define ST_BAD 0
#define ST_INT 1
#define ST_CHAR 2
+#define ST_ZINT 3
static char **
-mkseq (start, end, incr, type)
- int start, end, incr, type;
+mkseq (start, end, incr, type, width)
+ int start, end, incr, type, width;
{
int n, i;
char **result, *t;
#endif
if (type == ST_INT)
result[i++] = itos (n);
+ else if (type == ST_ZINT)
+ {
+ int len;
+ len = asprintf (&t, "%0*d", width, n);
+ result[i++] = t;
+ }
else
{
t = (char *)xmalloc (2);
t[1] = '\0';
result[i++] = t;
}
- if (n == end)
- break;
n += incr;
+ if ((incr < 0 && n < end) || (incr > 0 && n > end))
+ break;
}
while (1);
size_t tlen;
{
char *t, *lhs, *rhs;
- int i, lhs_t, rhs_t, lhs_v, rhs_v;
+ int i, lhs_t, rhs_t, lhs_v, rhs_v, incr, lhs_l, rhs_l, width;
intmax_t tl, tr;
- char **result;
+ char **result, *ep;
t = strstr (text, BRACE_SEQ_SPECIFIER);
if (t == 0)
return ((char **)NULL);
- i = t - text; /* index of start of BRACE_SEQ_SPECIFIER */
- lhs = substring (text, 0, i);
- rhs = substring (text, i + sizeof(BRACE_SEQ_SPECIFIER) - 1, tlen);
+ lhs_l = t - text; /* index of start of BRACE_SEQ_SPECIFIER */
+ lhs = substring (text, 0, lhs_l);
+ rhs = substring (text, lhs_l + sizeof(BRACE_SEQ_SPECIFIER) - 1, tlen);
if (lhs[0] == 0 || rhs[0] == 0)
{
sides have to match. */
lhs_t = (legal_number (lhs, &tl)) ? ST_INT :
((ISALPHA (lhs[0]) && lhs[1] == 0) ? ST_CHAR : ST_BAD);
- rhs_t = (legal_number (rhs, &tr)) ? ST_INT :
- ((ISALPHA (rhs[0]) && rhs[1] == 0) ? ST_CHAR : ST_BAD);
+
+ /* Decide on rhs and whether or not it looks like the user specified
+ an increment */
+ ep = 0;
+ if (ISDIGIT (rhs[0]) || ((rhs[0] == '+' || rhs[0] == '-') && ISDIGIT (rhs[1])))
+ {
+ rhs_t = ST_INT;
+ tr = strtoimax (rhs, &ep, 10);
+ if (ep && *ep != 0 && *ep != '.')
+ rhs_t = ST_BAD; /* invalid */
+ }
+ else if (ISALPHA (rhs[0]) && (rhs[1] == 0 || rhs[1] == '.'))
+ {
+ rhs_t = ST_CHAR;
+ ep = rhs + 1;
+ }
+ else
+ {
+ rhs_t = ST_BAD;
+ ep = 0;
+ }
+
+ incr = 1;
+ if (rhs_t != ST_BAD)
+ {
+ if (ep && *ep == '.' && ep[1] == '.' && ep[2])
+ incr = strtoimax (ep + 2, &ep, 10);
+ if (*ep != 0)
+ rhs_t = ST_BAD; /* invalid incr */
+ }
+
if (lhs_t != rhs_t || lhs_t == ST_BAD || rhs_t == ST_BAD)
{
{
lhs_v = (unsigned char)lhs[0];
rhs_v = (unsigned char)rhs[0];
+ width = 1;
}
else
{
lhs_v = tl; /* integer truncation */
rhs_v = tr;
+
+ /* Decide whether or not the terms need zero-padding */
+ rhs_l = tlen - lhs_l - sizeof (BRACE_SEQ_SPECIFIER) + 1;
+ width = 0;
+ if (lhs_l > 1 && lhs[0] == '0')
+ width = lhs_l, lhs_t = ST_ZINT;
+ if (lhs > 2 && lhs[0] == '-' && lhs[1] == '0')
+ width = lhs_l, lhs_t = ST_ZINT;
+ if (rhs_l > 1 && rhs[0] == '0' && width < rhs_l)
+ width = rhs_l, lhs_t = ST_ZINT;
+ if (rhs_l > 2 && rhs[0] == '-' && rhs[1] == '0' && width < rhs_l)
+ width = rhs_l, lhs_t = ST_ZINT;
}
- result = mkseq (lhs_v, rhs_v, 1, lhs_t);
+ result = mkseq (lhs_v, rhs_v, 1, lhs_t, width);
free (lhs);
free (rhs);
}
#if defined (SHELL)
- /* Pass new-style command substitutions through unchanged. */
- if (c == '$' && text[i+1] == '(') /* ) */
+ /* Pass new-style command and process substitutions through unchanged. */
+ if ((c == '$' || c == '<' || c == '>') && text[i+1] == '(') /* ) */
{
si = i + 2;
t = extract_command_subst (text, &si);
#define SEVAL_NOHIST 0x004
#define SEVAL_NOFREE 0x008
#define SEVAL_RESETLINE 0x010
+#define SEVAL_PARSEONLY 0x020
+#define SEVAL_NOLONGJMP 0x040
/* Flags for describe_command, shared between type.def and command.def */
#define CDESC_ALL 0x001 /* type -a */
/* Functions from setattr.def */
extern int set_or_show_attributes __P((WORD_LIST *, int, int));
+extern int show_all_var_attributes __P((int, int));
extern int show_var_attributes __P((SHELL_VAR *, int, int));
extern int show_name_attributes __P((char *, int));
extern void set_var_attribute __P((char *, int, int));
/* Evaluate a string as one or more shell commands.
- Copyright (C) 1996-2005 Free Software Foundation, Inc.
+ Copyright (C) 1996-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "../redir.h"
#include "../trap.h"
+#include <y.tab.h>
+
#if defined (HISTORY)
# include "../bashhist.h"
#endif
extern int indirection_level, subshell_environment;
extern int line_number;
+extern int current_token, shell_eof_token;
extern int last_command_exit_value;
extern int running_trap;
extern int loop_level;
static int cat_file __P((REDIRECT *));
+#define PE_TAG "parse_and_execute top"
+#define PS_TAG "parse_string top"
+
+#if defined (HISTORY)
+static void
+set_history_remembering ()
+{
+ remember_on_history = enable_history_list;
+}
+#endif
+
/* How to force parse_and_execute () to clean up after itself. */
void
parse_and_execute_cleanup ()
run_trap_cleanup (running_trap - 1);
unfreeze_jobs_list ();
}
- run_unwind_frame ("parse_and_execute_top");
+ run_unwind_frame (PE_TAG);
}
-#if defined (HISTORY)
static void
-set_history_remembering ()
-{
- remember_on_history = enable_history_list;
-}
-#endif
-
-/* Parse and execute the commands in STRING. Returns whatever
- execute_command () returns. This frees STRING. FLAGS is a
- flags word; look in common.h for the possible values. Actions
- are:
- (flags & SEVAL_NONINT) -> interactive = 0;
- (flags & SEVAL_INTERACT) -> interactive = 1;
- (flags & SEVAL_NOHIST) -> call bash_history_disable ()
- (flags & SEVAL_NOFREE) -> don't free STRING when finished
- (flags & SEVAL_RESETLINE) -> reset line_number to 1
-*/
-
-int
-parse_and_execute (string, from_file, flags)
+parse_prologue (string, flags, tag)
char *string;
- const char *from_file;
int flags;
+ char *tag;
{
- int code, x, lreset;
- volatile int should_jump_to_top_level, last_result;
char *orig_string;
- COMMAND *volatile command;
+ int x;
orig_string = string;
/* Unwind protect this invocation of parse_and_execute (). */
- begin_unwind_frame ("parse_and_execute_top");
+ begin_unwind_frame (tag);
unwind_protect_int (parse_and_execute_level);
unwind_protect_jmp_buf (top_level);
unwind_protect_int (indirection_level);
if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
unwind_protect_int (interactive);
- lreset = flags & SEVAL_RESETLINE;
-
#if defined (HISTORY)
if (parse_and_execute_level == 0)
add_unwind_protect (set_history_remembering, (char *)NULL);
else
- unwind_protect_int (remember_on_history);
+ unwind_protect_int (remember_on_history); /* can be used in scripts */
# if defined (BANG_HISTORY)
if (interactive_shell)
- {
- unwind_protect_int (history_expansion_inhibited);
- }
+ unwind_protect_int (history_expansion_inhibited);
# endif /* BANG_HISTORY */
#endif /* HISTORY */
add_unwind_protect (xfree, orig_string);
end_unwind_frame ();
+ if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
+ interactive = (flags & SEVAL_NONINT) ? 0 : 1;
+
+#if defined (HISTORY)
+ if (flags & SEVAL_NOHIST)
+ bash_history_disable ();
+#endif /* HISTORY */
+}
+
+/* Parse and execute the commands in STRING. Returns whatever
+ execute_command () returns. This frees STRING. FLAGS is a
+ flags word; look in common.h for the possible values. Actions
+ are:
+ (flags & SEVAL_NONINT) -> interactive = 0;
+ (flags & SEVAL_INTERACT) -> interactive = 1;
+ (flags & SEVAL_NOHIST) -> call bash_history_disable ()
+ (flags & SEVAL_NOFREE) -> don't free STRING when finished
+ (flags & SEVAL_RESETLINE) -> reset line_number to 1
+*/
+
+int
+parse_and_execute (string, from_file, flags)
+ char *string;
+ const char *from_file;
+ int flags;
+{
+ int code, lreset;
+ volatile int should_jump_to_top_level, last_result;
+ COMMAND *volatile command;
+
+ parse_prologue (string, flags, PE_TAG);
+
parse_and_execute_level++;
+ lreset = flags & SEVAL_RESETLINE;
+
/* Reset the line number if the caller wants us to. If we don't reset the
line number, we have to subtract one, because we will add one just
before executing the next command (resetting the line number sets it to
line_number--;
indirection_level++;
- if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
- interactive = (flags & SEVAL_NONINT) ? 0 : 1;
-
-#if defined (HISTORY)
- if (flags & SEVAL_NOHIST)
- bash_history_disable ();
-#endif /* HISTORY */
code = should_jump_to_top_level = 0;
last_result = EXECUTION_SUCCESS;
if (parse_command () == 0)
{
- if (interactive_shell == 0 && read_but_dont_execute)
+ if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute))
{
last_result = EXECUTION_SUCCESS;
dispose_command (global_command);
out:
- run_unwind_frame ("parse_and_execute_top");
+ run_unwind_frame (PE_TAG);
if (interrupt_state && parse_and_execute_level == 0)
{
return (last_result);
}
+/* Parse a command contained in STRING according to FLAGS and return the
+ number of characters consumed from the string. If non-NULL, set *ENDP
+ to the position in the string where the parse ended. Used to validate
+ command substitutions during parsing to obey Posix rules about finding
+ the end of the command and balancing parens. */
+int
+parse_string (string, from_file, flags, endp)
+ char *string;
+ const char *from_file;
+ int flags;
+ char **endp;
+{
+ int code, nc;
+ volatile int should_jump_to_top_level;
+ COMMAND *volatile command, *oglobal;
+ char *ostring;
+
+ parse_prologue (string, flags, PS_TAG);
+
+ /* Reset the line number if the caller wants us to. If we don't reset the
+ line number, we have to subtract one, because we will add one just
+ before executing the next command (resetting the line number sets it to
+ 0; the first line number is 1). */
+ push_stream (0);
+
+ code = should_jump_to_top_level = 0;
+ oglobal = global_command;
+
+ with_input_from_string (string, from_file);
+ while (*(bash_input.location.string))
+ {
+ command = (COMMAND *)NULL;
+
+#if 0
+ if (interrupt_state)
+ break;
+#endif
+
+ /* Provide a location for functions which `longjmp (top_level)' to
+ jump to. */
+ code = setjmp (top_level);
+
+ if (code)
+ {
+#if defined (DEBUG)
+itrace("parse_string: longjmp executed: code = %d", code);
+#endif
+ should_jump_to_top_level = 0;
+ switch (code)
+ {
+ case FORCE_EOF:
+ case ERREXIT:
+ case EXITPROG:
+ case DISCARD: /* XXX */
+ if (command)
+ dispose_command (command);
+ /* Remember to call longjmp (top_level) after the old
+ value for it is restored. */
+ should_jump_to_top_level = 1;
+ goto out;
+
+ default:
+ command_error ("parse_string", CMDERR_BADJUMP, code, 0);
+ break;
+ }
+ }
+
+ if (parse_command () == 0)
+ {
+ dispose_command (global_command);
+ global_command = (COMMAND *)NULL;
+ }
+ else
+ {
+ if ((flags & SEVAL_NOLONGJMP) == 0)
+ {
+ should_jump_to_top_level = 1;
+ code = DISCARD;
+ }
+ else
+ reset_parser (); /* XXX - sets token_to_read */
+ break;
+ }
+
+ if (current_token == yacc_EOF || current_token == shell_eof_token)
+ break;
+ }
+
+ out:
+
+ global_command = oglobal;
+ nc = bash_input.location.string - ostring;
+ if (endp)
+ *endp = bash_input.location.string;
+
+ run_unwind_frame (PS_TAG);
+
+ if (should_jump_to_top_level)
+ jump_to_top_level (code);
+
+ return (nc);
+}
+
/* Handle a $( < file ) command substitution. This expands the filename,
returning errors as appropriate, then just cats the file to the standard
output. */
/* Evaluate a string as one or more shell commands.
- Copyright (C) 1996-2005 Free Software Foundation, Inc.
+ Copyright (C) 1996-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "../redir.h"
#include "../trap.h"
+#include <y.tab.h>
+
#if defined (HISTORY)
# include "../bashhist.h"
#endif
extern int indirection_level, subshell_environment;
extern int line_number;
+extern int current_token, shell_eof_token;
extern int last_command_exit_value;
extern int running_trap;
extern int loop_level;
static int cat_file __P((REDIRECT *));
+#define PE_TAG "parse_and_execute top"
+#define PS_TAG "parse_string top"
+
+#if defined (HISTORY)
+static void
+set_history_remembering ()
+{
+ remember_on_history = enable_history_list;
+}
+#endif
+
/* How to force parse_and_execute () to clean up after itself. */
void
parse_and_execute_cleanup ()
run_trap_cleanup (running_trap - 1);
unfreeze_jobs_list ();
}
- run_unwind_frame ("parse_and_execute_top");
+ run_unwind_frame (PE_TAG);
}
static void
-set_history_remembering ()
-{
- remember_on_history = enable_history_list;
-}
-
-/* Parse and execute the commands in STRING. Returns whatever
- execute_command () returns. This frees STRING. FLAGS is a
- flags word; look in common.h for the possible values. Actions
- are:
- (flags & SEVAL_NONINT) -> interactive = 0;
- (flags & SEVAL_INTERACT) -> interactive = 1;
- (flags & SEVAL_NOHIST) -> call bash_history_disable ()
- (flags & SEVAL_NOFREE) -> don't free STRING when finished
- (flags & SEVAL_RESETLINE) -> reset line_number to 1
-*/
-
-int
-parse_and_execute (string, from_file, flags)
+parse_prologue (string, flags, tag)
char *string;
- const char *from_file;
int flags;
+ char *tag;
{
- int code, x, lreset;
- volatile int should_jump_to_top_level, last_result;
char *orig_string;
- COMMAND *volatile command;
+ int x;
orig_string = string;
/* Unwind protect this invocation of parse_and_execute (). */
- begin_unwind_frame ("parse_and_execute_top");
+ begin_unwind_frame (tag);
unwind_protect_int (parse_and_execute_level);
unwind_protect_jmp_buf (top_level);
unwind_protect_int (indirection_level);
if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
unwind_protect_int (interactive);
- lreset = flags & SEVAL_RESETLINE;
-
#if defined (HISTORY)
- add_unwind_protect (set_history_remembering, (char *)NULL);
+ if (parse_and_execute_level == 0)
+ add_unwind_protect (set_history_remembering, (char *)NULL);
+ else
+ unwind_protect_int (remember_on_history); /* can be used in scripts */
# if defined (BANG_HISTORY)
if (interactive_shell)
- {
- unwind_protect_int (history_expansion_inhibited);
- }
+ unwind_protect_int (history_expansion_inhibited);
# endif /* BANG_HISTORY */
#endif /* HISTORY */
add_unwind_protect (xfree, orig_string);
end_unwind_frame ();
+ if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
+ interactive = (flags & SEVAL_NONINT) ? 0 : 1;
+
+#if defined (HISTORY)
+ if (flags & SEVAL_NOHIST)
+ bash_history_disable ();
+#endif /* HISTORY */
+}
+
+/* Parse and execute the commands in STRING. Returns whatever
+ execute_command () returns. This frees STRING. FLAGS is a
+ flags word; look in common.h for the possible values. Actions
+ are:
+ (flags & SEVAL_NONINT) -> interactive = 0;
+ (flags & SEVAL_INTERACT) -> interactive = 1;
+ (flags & SEVAL_NOHIST) -> call bash_history_disable ()
+ (flags & SEVAL_NOFREE) -> don't free STRING when finished
+ (flags & SEVAL_RESETLINE) -> reset line_number to 1
+*/
+
+int
+parse_and_execute (string, from_file, flags)
+ char *string;
+ const char *from_file;
+ int flags;
+{
+ int code, lreset;
+ volatile int should_jump_to_top_level, last_result;
+ COMMAND *volatile command;
+
+ parse_prologue (string, flags, PE_TAG);
+
parse_and_execute_level++;
+ lreset = flags & SEVAL_RESETLINE;
+
/* Reset the line number if the caller wants us to. If we don't reset the
line number, we have to subtract one, because we will add one just
before executing the next command (resetting the line number sets it to
line_number--;
indirection_level++;
- if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
- interactive = (flags & SEVAL_NONINT) ? 0 : 1;
-
-#if defined (HISTORY)
- if (flags & SEVAL_NOHIST)
- bash_history_disable ();
-#endif /* HISTORY */
code = should_jump_to_top_level = 0;
last_result = EXECUTION_SUCCESS;
if (parse_command () == 0)
{
- if (interactive_shell == 0 && read_but_dont_execute)
+ if ((flags & SEVAL_PARSEONLY) || (interactive_shell == 0 && read_but_dont_execute))
{
last_result = EXECUTION_SUCCESS;
dispose_command (global_command);
out:
- run_unwind_frame ("parse_and_execute_top");
+ run_unwind_frame (PE_TAG);
if (interrupt_state && parse_and_execute_level == 0)
{
extern int unlink __P((const char *));
extern FILE *sh_mktmpfp __P((char *, int, char **));
-extern int delete_last_history __P((void));
/* **************************************************************** */
/* */
entered into the history list." */
if (listing == 0 && hist_last_line_added)
{
- delete_last_history ();
+ bash_delete_last_history ();
/* If we're editing a single command -- the last command in the
history -- and we just removed the dummy command added by
edit_and_execute_command (), we need to check whether or not we
if (command && *command)
{
- delete_last_history ();
+ bash_delete_last_history ();
maybe_add_history (command); /* Obeys HISTCONTROL setting. */
}
}
last_hist = histbeg = --histend;
}
-itrace ("fc: last_hist = %d histbeg = %d histend = %d", last_hist, histbeg, histend);
-
/* We print error messages for line specifications out of range. */
if ((histbeg < 0) || (histend < 0))
{
extern int current_command_line_count;
extern int force_append_history; /* shopt -s histappend */
-int delete_last_history __P((void));
-
static char *histtime __P((HIST_ENTRY *, const char *));
static void display_history __P((WORD_LIST *));
-static int delete_histent __P((int));
static void push_history __P((WORD_LIST *));
static int expand_and_print_history __P((WORD_LIST *));
/* clear the history, but allow other arguments to add to it again. */
if (flags & CFLAG)
{
- clear_history ();
+ bash_clear_history ();
if (list == 0)
return (EXECUTION_SUCCESS);
}
return (EXECUTION_FAILURE);
}
opt = delete_offset;
- result = delete_histent (opt - history_base);
+ result = bash_delete_histent (opt - history_base);
/* Since remove_history changes history_length, this can happen if
we delete the last history entry. */
if (where_history () > history_length)
}
}
-/* Delete and free the history list entry at offset I. */
-static int
-delete_histent (i)
- int i;
-{
- HIST_ENTRY *discard;
-
- discard = remove_history (i);
- if (discard)
- free_history_entry (discard);
-
- return 1;
-}
-
-int
-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 = delete_histent (i);
-
- if (where_history () > history_length)
- history_set_pos (history_length);
-
- return r;
-}
-
/* Remove the last entry in the history list and add each argument in
LIST to the history. */
static void
If you don't want history -s to remove the compound command from the
history, change #if 0 to #if 1 below. */
#if 0
- if (hist_last_line_pushed == 0 && hist_last_line_added && delete_last_history () == 0)
+ if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0)
#else
if (hist_last_line_pushed == 0 &&
(hist_last_line_added ||
(current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history))
- && delete_last_history () == 0)
+ && bash_delete_last_history () == 0)
#endif
return;
char *s;
int r, result;
- if (hist_last_line_pushed == 0 && hist_last_line_added && delete_last_history () == 0)
+ if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0)
return EXECUTION_FAILURE;
result = EXECUTION_SUCCESS;
while (list)
If the $HISTTIMEFORMAT variable is set and not null, its value is used
as a format string for strftime(3) to print the time stamp associated
with each displayed history entry. No time stamps are printed otherwise.
+
+Exit Status:
+Returns success unless an invalid option is given or an error occurs.
$END
#include <config.h>
static char *histtime __P((HIST_ENTRY *, const char *));
static void display_history __P((WORD_LIST *));
-static int delete_histent __P((int));
static void push_history __P((WORD_LIST *));
static int expand_and_print_history __P((WORD_LIST *));
/* clear the history, but allow other arguments to add to it again. */
if (flags & CFLAG)
{
- clear_history ();
+ bash_clear_history ();
if (list == 0)
return (EXECUTION_SUCCESS);
}
return (EXECUTION_FAILURE);
}
opt = delete_offset;
- result = delete_histent (opt - history_base);
+ result = bash_delete_histent (opt - history_base);
/* Since remove_history changes history_length, this can happen if
we delete the last history entry. */
if (where_history () > history_length)
}
}
-/* Delete and free the history list entry at offset I. */
-static int
-delete_histent (i)
- int i;
-{
- HIST_ENTRY *discard;
-
- discard = remove_history (i);
- if (discard)
- free_history_entry (discard);
-
- return 1;
-}
-
int
delete_last_history ()
{
if (histent == NULL)
return 0;
- r = delete_histent (i);
+ r = bash_delete_histent (i);
if (where_history () > history_length)
history_set_pos (history_length);
r_duplicating_input, r_duplicating_output, r_deblank_reading_until,
r_close_this, r_err_and_out, r_input_output, r_output_force,
r_duplicating_input_word, r_duplicating_output_word,
- r_move_input, r_move_output, r_move_input_word, r_move_output_word
+ r_move_input, r_move_output, r_move_input_word, r_move_output_word,
+ r_append_err_and_out
};
/* Redirection errors. */
(ri == r_output_direction || ri == r_err_and_out)
#define OUTPUT_REDIRECT(ri) \
- (ri == r_output_direction || ri == r_input_output || ri == r_err_and_out)
+ (ri == r_output_direction || ri == r_input_output || ri == r_err_and_out || ri == r_append_err_and_out)
#define INPUT_REDIRECT(ri) \
(ri == r_input_direction || ri == r_inputa_direction || ri == r_input_output)
ri == r_input_output || \
ri == r_err_and_out || \
ri == r_appending_to || \
+ ri == r_append_err_and_out || \
ri == r_output_force)
/* redirection needs translation */
#define W_DQUOTE 0x080000 /* word should be treated as if double-quoted */
#define W_NOPROCSUB 0x100000 /* don't perform process substitution */
#define W_HASCTLESC 0x200000 /* word contains literal CTLESC characters */
+#define W_ASSIGNASSOC 0x400000 /* word looks like associative array assignment */
/* Possible values for subshell_environment */
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
#define SUBSHELL_FORK 0x08 /* subshell caused by executing a disk command */
#define SUBSHELL_PIPE 0x10 /* subshell from a pipeline element */
#define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */
+#define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */
/* A structure which represents a word. */
typedef struct word_desc {
/* Structures used to represent the CASE command. */
+/* Values for FLAGS word in a PATTERN_LIST */
+#define CASEPAT_FALLTHROUGH 0x01
+#define CASEPAT_TESTNEXT 0x02
+
/* Pattern/action structure for CASE_COM. */
typedef struct pattern_list {
struct pattern_list *next; /* Clause to try in case this one failed. */
--- /dev/null
+/* command.h -- The structures used internally to represent commands, and
+ the extern declarations of the functions used to create them. */
+
+/* Copyright (C) 1993-2005 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 2, 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; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#if !defined (_COMMAND_H_)
+#define _COMMAND_H_
+
+#include "stdc.h"
+
+/* Instructions describing what kind of thing to do for a redirection. */
+enum r_instruction {
+ r_output_direction, r_input_direction, r_inputa_direction,
+ r_appending_to, r_reading_until, r_reading_string,
+ r_duplicating_input, r_duplicating_output, r_deblank_reading_until,
+ r_close_this, r_err_and_out, r_input_output, r_output_force,
+ r_duplicating_input_word, r_duplicating_output_word,
+ r_move_input, r_move_output, r_move_input_word, r_move_output_word,
+ r_append_err_and_out
+};
+
+/* Redirection errors. */
+#define AMBIGUOUS_REDIRECT -1
+#define NOCLOBBER_REDIRECT -2
+#define RESTRICTED_REDIRECT -3 /* can only happen in restricted shells. */
+#define HEREDOC_REDIRECT -4 /* here-doc temp file can't be created */
+
+#define CLOBBERING_REDIRECT(ri) \
+ (ri == r_output_direction || ri == r_err_and_out)
+
+#define OUTPUT_REDIRECT(ri) \
+ (ri == r_output_direction || ri == r_input_output || ri == r_err_and_out || ri == r_append_err_and_out)
+
+#define INPUT_REDIRECT(ri) \
+ (ri == r_input_direction || ri == r_inputa_direction || ri == r_input_output)
+
+#define WRITE_REDIRECT(ri) \
+ (ri == r_output_direction || \
+ ri == r_input_output || \
+ ri == r_err_and_out || \
+ ri == r_appending_to || \
+ ri == r_append_err_and_out || \
+ ri == r_output_force)
+
+/* redirection needs translation */
+#define TRANSLATE_REDIRECT(ri) \
+ (ri == r_duplicating_input_word || ri == r_duplicating_output_word || \
+ ri == r_move_input_word || ri == r_move_output_word)
+
+/* Command Types: */
+enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
+ cm_connection, cm_function_def, cm_until, cm_group,
+ cm_arith, cm_cond, cm_arith_for, cm_subshell };
+
+/* Possible values for the `flags' field of a WORD_DESC. */
+#define W_HASDOLLAR 0x000001 /* Dollar sign present. */
+#define W_QUOTED 0x000002 /* Some form of quote character is present. */
+#define W_ASSIGNMENT 0x000004 /* This word is a variable assignment. */
+#define W_GLOBEXP 0x000008 /* This word is the result of a glob expansion. */
+#define W_NOSPLIT 0x000010 /* Do not perform word splitting on this word. */
+#define W_NOGLOB 0x000020 /* Do not perform globbing on this word. */
+#define W_NOSPLIT2 0x000040 /* Don't split word except for $@ expansion. */
+#define W_TILDEEXP 0x000080 /* Tilde expand this assignment word */
+#define W_DOLLARAT 0x000100 /* $@ and its special handling */
+#define W_DOLLARSTAR 0x000200 /* $* and its special handling */
+#define W_NOCOMSUB 0x000400 /* Don't perform command substitution on this word */
+#define W_ASSIGNRHS 0x000800 /* Word is rhs of an assignment statement */
+#define W_NOTILDE 0x001000 /* Don't perform tilde expansion on this word */
+#define W_ITILDE 0x002000 /* Internal flag for word expansion */
+#define W_NOEXPAND 0x004000 /* Don't expand at all -- do quote removal */
+#define W_COMPASSIGN 0x008000 /* Compound assignment */
+#define W_ASSNBLTIN 0x010000 /* word is a builtin command that takes assignments */
+#define W_ASSIGNARG 0x020000 /* word is assignment argument to command */
+#define W_HASQUOTEDNULL 0x040000 /* word contains a quoted null character */
+#define W_DQUOTE 0x080000 /* word should be treated as if double-quoted */
+#define W_NOPROCSUB 0x100000 /* don't perform process substitution */
+#define W_HASCTLESC 0x200000 /* word contains literal CTLESC characters */
+#define W_ASSIGNASSOC 0x400000 /* word looks like associative array assignment */
+
+/* Possible values for subshell_environment */
+#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
+#define SUBSHELL_PAREN 0x02 /* subshell caused by ( ... ) */
+#define SUBSHELL_COMSUB 0x04 /* subshell caused by `command` or $(command) */
+#define SUBSHELL_FORK 0x08 /* subshell caused by executing a disk command */
+#define SUBSHELL_PIPE 0x10 /* subshell from a pipeline element */
+#define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */
+#define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */
+
+/* A structure which represents a word. */
+typedef struct word_desc {
+ char *word; /* Zero terminated string. */
+ int flags; /* Flags associated with this word. */
+} WORD_DESC;
+
+/* A linked list of words. */
+typedef struct word_list {
+ struct word_list *next;
+ WORD_DESC *word;
+} WORD_LIST;
+
+
+/* **************************************************************** */
+/* */
+/* Shell Command Structs */
+/* */
+/* **************************************************************** */
+
+/* What a redirection descriptor looks like. If the redirection instruction
+ is ri_duplicating_input or ri_duplicating_output, use DEST, otherwise
+ use the file in FILENAME. Out-of-range descriptors are identified by a
+ negative DEST. */
+
+typedef union {
+ int dest; /* Place to redirect REDIRECTOR to, or ... */
+ WORD_DESC *filename; /* filename to redirect to. */
+} REDIRECTEE;
+
+/* Structure describing a redirection. If REDIRECTOR is negative, the parser
+ (or translator in redir.c) encountered an out-of-range file descriptor. */
+typedef struct redirect {
+ struct redirect *next; /* Next element, or NULL. */
+ int redirector; /* Descriptor to be redirected. */
+ int flags; /* Flag value for `open'. */
+ enum r_instruction instruction; /* What to do with the information. */
+ REDIRECTEE redirectee; /* File descriptor or filename */
+ char *here_doc_eof; /* The word that appeared in <<foo. */
+} REDIRECT;
+
+/* An element used in parsing. A single word or a single redirection.
+ This is an ephemeral construct. */
+typedef struct element {
+ WORD_DESC *word;
+ REDIRECT *redirect;
+} ELEMENT;
+
+/* Possible values for command->flags. */
+#define CMD_WANT_SUBSHELL 0x01 /* User wants a subshell: ( command ) */
+#define CMD_FORCE_SUBSHELL 0x02 /* Shell needs to force a subshell. */
+#define CMD_INVERT_RETURN 0x04 /* Invert the exit value. */
+#define CMD_IGNORE_RETURN 0x08 /* Ignore the exit value. For set -e. */
+#define CMD_NO_FUNCTIONS 0x10 /* Ignore functions during command lookup. */
+#define CMD_INHIBIT_EXPANSION 0x20 /* Do not expand the command words. */
+#define CMD_NO_FORK 0x40 /* Don't fork; just call execve */
+#define CMD_TIME_PIPELINE 0x80 /* Time a pipeline */
+#define CMD_TIME_POSIX 0x100 /* time -p; use POSIX.2 time output spec. */
+#define CMD_AMPERSAND 0x200 /* command & */
+#define CMD_STDIN_REDIR 0x400 /* async command needs implicit </dev/null */
+#define CMD_COMMAND_BUILTIN 0x0800 /* command executed by `command' builtin */
+
+/* What a command looks like. */
+typedef struct command {
+ enum command_type type; /* FOR CASE WHILE IF CONNECTION or SIMPLE. */
+ int flags; /* Flags controlling execution environment. */
+ int line; /* line number the command starts on */
+ REDIRECT *redirects; /* Special redirects for FOR CASE, etc. */
+ union {
+ struct for_com *For;
+ struct case_com *Case;
+ struct while_com *While;
+ struct if_com *If;
+ struct connection *Connection;
+ struct simple_com *Simple;
+ struct function_def *Function_def;
+ struct group_com *Group;
+#if defined (SELECT_COMMAND)
+ struct select_com *Select;
+#endif
+#if defined (DPAREN_ARITHMETIC)
+ struct arith_com *Arith;
+#endif
+#if defined (COND_COMMAND)
+ struct cond_com *Cond;
+#endif
+#if defined (ARITH_FOR_COMMAND)
+ struct arith_for_com *ArithFor;
+#endif
+ struct subshell_com *Subshell;
+ } value;
+} COMMAND;
+
+/* Structure used to represent the CONNECTION type. */
+typedef struct connection {
+ int ignore; /* Unused; simplifies make_command (). */
+ COMMAND *first; /* Pointer to the first command. */
+ COMMAND *second; /* Pointer to the second command. */
+ int connector; /* What separates this command from others. */
+} CONNECTION;
+
+/* Structures used to represent the CASE command. */
+
+/* Values for FLAGS word in a PATTERN_LIST */
+#define CASEPAT_FALLTHROUGH 0x01
+#define CASEPAT_TESTNEXT 0x02
+
+/* Pattern/action structure for CASE_COM. */
+typedef struct pattern_list {
+ struct pattern_list *next; /* Clause to try in case this one failed. */
+ WORD_LIST *patterns; /* Linked list of patterns to test. */
+ COMMAND *action; /* Thing to execute if a pattern matches. */
+ int flags;
+} PATTERN_LIST;
+
+/* The CASE command. */
+typedef struct case_com {
+ int flags; /* See description of CMD flags. */
+ int line; /* line number the `case' keyword appears on */
+ WORD_DESC *word; /* The thing to test. */
+ PATTERN_LIST *clauses; /* The clauses to test against, or NULL. */
+} CASE_COM;
+
+/* FOR command. */
+typedef struct for_com {
+ int flags; /* See description of CMD flags. */
+ int line; /* line number the `for' keyword appears on */
+ WORD_DESC *name; /* The variable name to get mapped over. */
+ WORD_LIST *map_list; /* The things to map over. This is never NULL. */
+ COMMAND *action; /* The action to execute.
+ During execution, NAME is bound to successive
+ members of MAP_LIST. */
+} FOR_COM;
+
+#if defined (ARITH_FOR_COMMAND)
+typedef struct arith_for_com {
+ int flags;
+ int line; /* generally used for error messages */
+ WORD_LIST *init;
+ WORD_LIST *test;
+ WORD_LIST *step;
+ COMMAND *action;
+} ARITH_FOR_COM;
+#endif
+
+#if defined (SELECT_COMMAND)
+/* KSH SELECT command. */
+typedef struct select_com {
+ int flags; /* See description of CMD flags. */
+ int line; /* line number the `select' keyword appears on */
+ WORD_DESC *name; /* The variable name to get mapped over. */
+ WORD_LIST *map_list; /* The things to map over. This is never NULL. */
+ COMMAND *action; /* The action to execute.
+ During execution, NAME is bound to the member of
+ MAP_LIST chosen by the user. */
+} SELECT_COM;
+#endif /* SELECT_COMMAND */
+
+/* IF command. */
+typedef struct if_com {
+ int flags; /* See description of CMD flags. */
+ COMMAND *test; /* Thing to test. */
+ COMMAND *true_case; /* What to do if the test returned non-zero. */
+ COMMAND *false_case; /* What to do if the test returned zero. */
+} IF_COM;
+
+/* WHILE command. */
+typedef struct while_com {
+ int flags; /* See description of CMD flags. */
+ COMMAND *test; /* Thing to test. */
+ COMMAND *action; /* Thing to do while test is non-zero. */
+} WHILE_COM;
+
+#if defined (DPAREN_ARITHMETIC)
+/* The arithmetic evaluation command, ((...)). Just a set of flags and
+ a WORD_LIST, of which the first element is the only one used, for the
+ time being. */
+typedef struct arith_com {
+ int flags;
+ int line;
+ WORD_LIST *exp;
+} ARITH_COM;
+#endif /* DPAREN_ARITHMETIC */
+
+/* The conditional command, [[...]]. This is a binary tree -- we slippped
+ a recursive-descent parser into the YACC grammar to parse it. */
+#define COND_AND 1
+#define COND_OR 2
+#define COND_UNARY 3
+#define COND_BINARY 4
+#define COND_TERM 5
+#define COND_EXPR 6
+
+typedef struct cond_com {
+ int flags;
+ int line;
+ int type;
+ WORD_DESC *op;
+ struct cond_com *left, *right;
+} COND_COM;
+
+/* The "simple" command. Just a collection of words and redirects. */
+typedef struct simple_com {
+ int flags; /* See description of CMD flags. */
+ int line; /* line number the command starts on */
+ WORD_LIST *words; /* The program name, the arguments,
+ variable assignments, etc. */
+ REDIRECT *redirects; /* Redirections to perform. */
+} SIMPLE_COM;
+
+/* The "function definition" command. */
+typedef struct function_def {
+ int flags; /* See description of CMD flags. */
+ int line; /* Line number the function def starts on. */
+ WORD_DESC *name; /* The name of the function. */
+ COMMAND *command; /* The parsed execution tree. */
+ char *source_file; /* file in which function was defined, if any */
+} FUNCTION_DEF;
+
+/* A command that is `grouped' allows pipes and redirections to affect all
+ commands in the group. */
+typedef struct group_com {
+ int ignore; /* See description of CMD flags. */
+ COMMAND *command;
+} GROUP_COM;
+
+typedef struct subshell_com {
+ int flags;
+ COMMAND *command;
+} SUBSHELL_COM;
+
+extern COMMAND *global_command;
+
+/* Possible command errors */
+#define CMDERR_DEFAULT 0
+#define CMDERR_BADTYPE 1
+#define CMDERR_BADCONN 2
+#define CMDERR_BADJUMP 3
+
+#define CMDERR_LAST 3
+
+/* Forward declarations of functions declared in copy_cmd.c. */
+
+extern FUNCTION_DEF *copy_function_def_contents __P((FUNCTION_DEF *, FUNCTION_DEF *));
+extern FUNCTION_DEF *copy_function_def __P((FUNCTION_DEF *));
+
+extern WORD_DESC *copy_word __P((WORD_DESC *));
+extern WORD_LIST *copy_word_list __P((WORD_LIST *));
+extern REDIRECT *copy_redirect __P((REDIRECT *));
+extern REDIRECT *copy_redirects __P((REDIRECT *));
+extern COMMAND *copy_command __P((COMMAND *));
+
+#endif /* _COMMAND_H_ */
new_clause = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
new_clause->patterns = copy_word_list (clause->patterns);
new_clause->action = copy_command (clause->action);
+ new_clause->flags = clause->flags;
return (new_clause);
}
case r_input_direction:
case r_inputa_direction:
case r_err_and_out:
+ case r_append_err_and_out:
case r_input_output:
case r_output_force:
case r_duplicating_input_word:
--- /dev/null
+/* copy_command.c -- copy a COMMAND structure. This is needed
+ primarily for making function definitions, but I'm not sure
+ that anyone else will need it. */
+
+/* Copyright (C) 1987,1991 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 2, 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; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+
+#include "shell.h"
+
+static PATTERN_LIST *copy_case_clause __P((PATTERN_LIST *));
+static PATTERN_LIST *copy_case_clauses __P((PATTERN_LIST *));
+static FOR_COM *copy_for_command __P((FOR_COM *));
+#if defined (ARITH_FOR_COMMAND)
+static ARITH_FOR_COM *copy_arith_for_command __P((ARITH_FOR_COM *));
+#endif
+static GROUP_COM *copy_group_command __P((GROUP_COM *));
+static SUBSHELL_COM *copy_subshell_command __P((SUBSHELL_COM *));
+static CASE_COM *copy_case_command __P((CASE_COM *));
+static WHILE_COM *copy_while_command __P((WHILE_COM *));
+static IF_COM *copy_if_command __P((IF_COM *));
+#if defined (DPAREN_ARITHMETIC)
+static ARITH_COM *copy_arith_command __P((ARITH_COM *));
+#endif
+#if defined (COND_COMMAND)
+static COND_COM *copy_cond_command __P((COND_COM *));
+#endif
+static SIMPLE_COM *copy_simple_command __P((SIMPLE_COM *));
+
+WORD_DESC *
+copy_word (w)
+ WORD_DESC *w;
+{
+ WORD_DESC *new_word;
+
+ new_word = make_bare_word (w->word);
+ new_word->flags = w->flags;
+ return (new_word);
+}
+
+/* Copy the chain of words in LIST. Return a pointer to
+ the new chain. */
+WORD_LIST *
+copy_word_list (list)
+ WORD_LIST *list;
+{
+ WORD_LIST *new_list;
+
+ for (new_list = (WORD_LIST *)NULL; list; list = list->next)
+ new_list = make_word_list (copy_word (list->word), new_list);
+
+ return (REVERSE_LIST (new_list, WORD_LIST *));
+}
+
+static PATTERN_LIST *
+copy_case_clause (clause)
+ PATTERN_LIST *clause;
+{
+ PATTERN_LIST *new_clause;
+
+ new_clause = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
+ new_clause->patterns = copy_word_list (clause->patterns);
+ new_clause->action = copy_command (clause->action);
+ return (new_clause);
+}
+
+static PATTERN_LIST *
+copy_case_clauses (clauses)
+ PATTERN_LIST *clauses;
+{
+ PATTERN_LIST *new_list, *new_clause;
+
+ for (new_list = (PATTERN_LIST *)NULL; clauses; clauses = clauses->next)
+ {
+ new_clause = copy_case_clause (clauses);
+ new_clause->next = new_list;
+ new_list = new_clause;
+ }
+ return (REVERSE_LIST (new_list, PATTERN_LIST *));
+}
+
+/* Copy a single redirect. */
+REDIRECT *
+copy_redirect (redirect)
+ REDIRECT *redirect;
+{
+ REDIRECT *new_redirect;
+
+ new_redirect = (REDIRECT *)xmalloc (sizeof (REDIRECT));
+ FASTCOPY ((char *)redirect, (char *)new_redirect, (sizeof (REDIRECT)));
+ switch (redirect->instruction)
+ {
+ case r_reading_until:
+ case r_deblank_reading_until:
+ new_redirect->here_doc_eof = savestring (redirect->here_doc_eof);
+ /*FALLTHROUGH*/
+ case r_reading_string:
+ case r_appending_to:
+ case r_output_direction:
+ case r_input_direction:
+ case r_inputa_direction:
+ case r_err_and_out:
+ case r_input_output:
+ case r_output_force:
+ case r_duplicating_input_word:
+ case r_duplicating_output_word:
+ case r_move_input_word:
+ case r_move_output_word:
+ new_redirect->redirectee.filename = copy_word (redirect->redirectee.filename);
+ break;
+ case r_duplicating_input:
+ case r_duplicating_output:
+ case r_move_input:
+ case r_move_output:
+ case r_close_this:
+ break;
+ }
+ return (new_redirect);
+}
+
+REDIRECT *
+copy_redirects (list)
+ REDIRECT *list;
+{
+ REDIRECT *new_list, *temp;
+
+ for (new_list = (REDIRECT *)NULL; list; list = list->next)
+ {
+ temp = copy_redirect (list);
+ temp->next = new_list;
+ new_list = temp;
+ }
+ return (REVERSE_LIST (new_list, REDIRECT *));
+}
+
+static FOR_COM *
+copy_for_command (com)
+ FOR_COM *com;
+{
+ FOR_COM *new_for;
+
+ new_for = (FOR_COM *)xmalloc (sizeof (FOR_COM));
+ new_for->flags = com->flags;
+ new_for->line = com->line;
+ new_for->name = copy_word (com->name);
+ new_for->map_list = copy_word_list (com->map_list);
+ new_for->action = copy_command (com->action);
+ return (new_for);
+}
+
+#if defined (ARITH_FOR_COMMAND)
+static ARITH_FOR_COM *
+copy_arith_for_command (com)
+ ARITH_FOR_COM *com;
+{
+ ARITH_FOR_COM *new_arith_for;
+
+ new_arith_for = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM));
+ new_arith_for->flags = com->flags;
+ new_arith_for->line = com->line;
+ new_arith_for->init = copy_word_list (com->init);
+ new_arith_for->test = copy_word_list (com->test);
+ new_arith_for->step = copy_word_list (com->step);
+ new_arith_for->action = copy_command (com->action);
+ return (new_arith_for);
+}
+#endif /* ARITH_FOR_COMMAND */
+
+static GROUP_COM *
+copy_group_command (com)
+ GROUP_COM *com;
+{
+ GROUP_COM *new_group;
+
+ new_group = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
+ new_group->command = copy_command (com->command);
+ return (new_group);
+}
+
+static SUBSHELL_COM *
+copy_subshell_command (com)
+ SUBSHELL_COM *com;
+{
+ SUBSHELL_COM *new_subshell;
+
+ new_subshell = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM));
+ new_subshell->command = copy_command (com->command);
+ new_subshell->flags = com->flags;
+ return (new_subshell);
+}
+
+static CASE_COM *
+copy_case_command (com)
+ CASE_COM *com;
+{
+ CASE_COM *new_case;
+
+ new_case = (CASE_COM *)xmalloc (sizeof (CASE_COM));
+ new_case->flags = com->flags;
+ new_case->line = com->line;
+ new_case->word = copy_word (com->word);
+ new_case->clauses = copy_case_clauses (com->clauses);
+ return (new_case);
+}
+
+static WHILE_COM *
+copy_while_command (com)
+ WHILE_COM *com;
+{
+ WHILE_COM *new_while;
+
+ new_while = (WHILE_COM *)xmalloc (sizeof (WHILE_COM));
+ new_while->flags = com->flags;
+ new_while->test = copy_command (com->test);
+ new_while->action = copy_command (com->action);
+ return (new_while);
+}
+
+static IF_COM *
+copy_if_command (com)
+ IF_COM *com;
+{
+ IF_COM *new_if;
+
+ new_if = (IF_COM *)xmalloc (sizeof (IF_COM));
+ new_if->flags = com->flags;
+ new_if->test = copy_command (com->test);
+ new_if->true_case = copy_command (com->true_case);
+ new_if->false_case = com->false_case ? copy_command (com->false_case) : com->false_case;
+ return (new_if);
+}
+
+#if defined (DPAREN_ARITHMETIC)
+static ARITH_COM *
+copy_arith_command (com)
+ ARITH_COM *com;
+{
+ ARITH_COM *new_arith;
+
+ new_arith = (ARITH_COM *)xmalloc (sizeof (ARITH_COM));
+ new_arith->flags = com->flags;
+ new_arith->exp = copy_word_list (com->exp);
+ new_arith->line = com->line;
+
+ return (new_arith);
+}
+#endif
+
+#if defined (COND_COMMAND)
+static COND_COM *
+copy_cond_command (com)
+ COND_COM *com;
+{
+ COND_COM *new_cond;
+
+ new_cond = (COND_COM *)xmalloc (sizeof (COND_COM));
+ new_cond->flags = com->flags;
+ new_cond->line = com->line;
+ new_cond->type = com->type;
+ new_cond->op = com->op ? copy_word (com->op) : com->op;
+ new_cond->left = com->left ? copy_cond_command (com->left) : (COND_COM *)NULL;
+ new_cond->right = com->right ? copy_cond_command (com->right) : (COND_COM *)NULL;
+
+ return (new_cond);
+}
+#endif
+
+static SIMPLE_COM *
+copy_simple_command (com)
+ SIMPLE_COM *com;
+{
+ SIMPLE_COM *new_simple;
+
+ new_simple = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
+ new_simple->flags = com->flags;
+ new_simple->words = copy_word_list (com->words);
+ new_simple->redirects = com->redirects ? copy_redirects (com->redirects) : (REDIRECT *)NULL;
+ new_simple->line = com->line;
+ return (new_simple);
+}
+
+FUNCTION_DEF *
+copy_function_def_contents (old, new_def)
+ FUNCTION_DEF *old, *new_def;
+{
+ new_def->name = copy_word (old->name);
+ new_def->command = old->command ? copy_command (old->command) : old->command;
+ new_def->flags = old->flags;
+ new_def->line = old->line;
+ new_def->source_file = old->source_file ? savestring (old->source_file) : old->source_file;
+ return (new_def);
+}
+
+FUNCTION_DEF *
+copy_function_def (com)
+ FUNCTION_DEF *com;
+{
+ FUNCTION_DEF *new_def;
+
+ new_def = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
+ new_def = copy_function_def_contents (com, new_def);
+ return (new_def);
+}
+
+/* Copy the command structure in COMMAND. Return a pointer to the
+ copy. Don't you forget to dispose_command () on this pointer
+ later! */
+COMMAND *
+copy_command (command)
+ COMMAND *command;
+{
+ COMMAND *new_command;
+
+ if (command == NULL)
+ return (command);
+
+ new_command = (COMMAND *)xmalloc (sizeof (COMMAND));
+ FASTCOPY ((char *)command, (char *)new_command, sizeof (COMMAND));
+ new_command->flags = command->flags;
+ new_command->line = command->line;
+
+ if (command->redirects)
+ new_command->redirects = copy_redirects (command->redirects);
+
+ switch (command->type)
+ {
+ case cm_for:
+ new_command->value.For = copy_for_command (command->value.For);
+ break;
+
+#if defined (ARITH_FOR_COMMAND)
+ case cm_arith_for:
+ new_command->value.ArithFor = copy_arith_for_command (command->value.ArithFor);
+ break;
+#endif
+
+#if defined (SELECT_COMMAND)
+ case cm_select:
+ new_command->value.Select =
+ (SELECT_COM *)copy_for_command ((FOR_COM *)command->value.Select);
+ break;
+#endif
+
+ case cm_group:
+ new_command->value.Group = copy_group_command (command->value.Group);
+ break;
+
+ case cm_subshell:
+ new_command->value.Subshell = copy_subshell_command (command->value.Subshell);
+ break;
+
+ case cm_case:
+ new_command->value.Case = copy_case_command (command->value.Case);
+ break;
+
+ case cm_until:
+ case cm_while:
+ new_command->value.While = copy_while_command (command->value.While);
+ break;
+
+ case cm_if:
+ new_command->value.If = copy_if_command (command->value.If);
+ break;
+
+#if defined (DPAREN_ARITHMETIC)
+ case cm_arith:
+ new_command->value.Arith = copy_arith_command (command->value.Arith);
+ break;
+#endif
+
+#if defined (COND_COMMAND)
+ case cm_cond:
+ new_command->value.Cond = copy_cond_command (command->value.Cond);
+ break;
+#endif
+
+ case cm_simple:
+ new_command->value.Simple = copy_simple_command (command->value.Simple);
+ break;
+
+ case cm_connection:
+ {
+ CONNECTION *new_connection;
+
+ new_connection = (CONNECTION *)xmalloc (sizeof (CONNECTION));
+ new_connection->connector = command->value.Connection->connector;
+ new_connection->first = copy_command (command->value.Connection->first);
+ new_connection->second = copy_command (command->value.Connection->second);
+ new_command->value.Connection = new_connection;
+ break;
+ }
+
+ case cm_function_def:
+ new_command->value.Function_def = copy_function_def (command->value.Function_def);
+ break;
+ }
+ return (new_command);
+}
/* dispose_command.c -- dispose of a COMMAND structure. */
-/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
case r_inputa_direction:
case r_appending_to:
case r_err_and_out:
+ case r_append_err_and_out:
case r_input_output:
case r_output_force:
case r_duplicating_input_word:
--- /dev/null
+/* dispose_command.c -- dispose of a COMMAND structure. */
+
+/* Copyright (C) 1987-2005 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 2, 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; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+#include "shell.h"
+
+extern sh_obj_cache_t wdcache, wlcache;
+
+/* Dispose of the command structure passed. */
+void
+dispose_command (command)
+ COMMAND *command;
+{
+ if (command == 0)
+ return;
+
+ if (command->redirects)
+ dispose_redirects (command->redirects);
+
+ switch (command->type)
+ {
+ case cm_for:
+#if defined (SELECT_COMMAND)
+ case cm_select:
+#endif
+ {
+ register FOR_COM *c;
+#if defined (SELECT_COMMAND)
+ if (command->type == cm_select)
+ c = (FOR_COM *)command->value.Select;
+ else
+#endif
+ c = command->value.For;
+ dispose_word (c->name);
+ dispose_words (c->map_list);
+ dispose_command (c->action);
+ free (c);
+ break;
+ }
+
+#if defined (ARITH_FOR_COMMAND)
+ case cm_arith_for:
+ {
+ register ARITH_FOR_COM *c;
+
+ c = command->value.ArithFor;
+ dispose_words (c->init);
+ dispose_words (c->test);
+ dispose_words (c->step);
+ dispose_command (c->action);
+ free (c);
+ break;
+ }
+#endif /* ARITH_FOR_COMMAND */
+
+ case cm_group:
+ {
+ dispose_command (command->value.Group->command);
+ free (command->value.Group);
+ break;
+ }
+
+ case cm_subshell:
+ {
+ dispose_command (command->value.Subshell->command);
+ free (command->value.Subshell);
+ break;
+ }
+
+ case cm_case:
+ {
+ register CASE_COM *c;
+ PATTERN_LIST *t, *p;
+
+ c = command->value.Case;
+ dispose_word (c->word);
+
+ for (p = c->clauses; p; )
+ {
+ dispose_words (p->patterns);
+ dispose_command (p->action);
+ t = p;
+ p = p->next;
+ free (t);
+ }
+ free (c);
+ break;
+ }
+
+ case cm_until:
+ case cm_while:
+ {
+ register WHILE_COM *c;
+
+ c = command->value.While;
+ dispose_command (c->test);
+ dispose_command (c->action);
+ free (c);
+ break;
+ }
+
+ case cm_if:
+ {
+ register IF_COM *c;
+
+ c = command->value.If;
+ dispose_command (c->test);
+ dispose_command (c->true_case);
+ dispose_command (c->false_case);
+ free (c);
+ break;
+ }
+
+ case cm_simple:
+ {
+ register SIMPLE_COM *c;
+
+ c = command->value.Simple;
+ dispose_words (c->words);
+ dispose_redirects (c->redirects);
+ free (c);
+ break;
+ }
+
+ case cm_connection:
+ {
+ register CONNECTION *c;
+
+ c = command->value.Connection;
+ dispose_command (c->first);
+ dispose_command (c->second);
+ free (c);
+ break;
+ }
+
+#if defined (DPAREN_ARITHMETIC)
+ case cm_arith:
+ {
+ register ARITH_COM *c;
+
+ c = command->value.Arith;
+ dispose_words (c->exp);
+ free (c);
+ break;
+ }
+#endif /* DPAREN_ARITHMETIC */
+
+#if defined (COND_COMMAND)
+ case cm_cond:
+ {
+ register COND_COM *c;
+
+ c = command->value.Cond;
+ dispose_cond_node (c);
+ break;
+ }
+#endif /* COND_COMMAND */
+
+ case cm_function_def:
+ {
+ register FUNCTION_DEF *c;
+
+ c = command->value.Function_def;
+ dispose_function_def (c);
+ break;
+ }
+
+ default:
+ command_error ("dispose_command", CMDERR_BADTYPE, command->type, 0);
+ break;
+ }
+ free (command);
+}
+
+#if defined (COND_COMMAND)
+/* How to free a node in a conditional command. */
+void
+dispose_cond_node (cond)
+ COND_COM *cond;
+{
+ if (cond)
+ {
+ if (cond->left)
+ dispose_cond_node (cond->left);
+ if (cond->right)
+ dispose_cond_node (cond->right);
+ if (cond->op)
+ dispose_word (cond->op);
+ free (cond);
+ }
+}
+#endif /* COND_COMMAND */
+
+void
+dispose_function_def_contents (c)
+ FUNCTION_DEF *c;
+{
+ dispose_word (c->name);
+ dispose_command (c->command);
+ FREE (c->source_file);
+}
+
+void
+dispose_function_def (c)
+ FUNCTION_DEF *c;
+{
+ dispose_function_def_contents (c);
+ free (c);
+}
+
+/* How to free a WORD_DESC. */
+void
+dispose_word (w)
+ WORD_DESC *w;
+{
+ FREE (w->word);
+ ocache_free (wdcache, WORD_DESC, w);
+}
+
+/* Free a WORD_DESC, but not the word contained within. */
+void
+dispose_word_desc (w)
+ WORD_DESC *w;
+{
+ w->word = 0;
+ ocache_free (wdcache, WORD_DESC, w);
+}
+
+/* How to get rid of a linked list of words. A WORD_LIST. */
+void
+dispose_words (list)
+ WORD_LIST *list;
+{
+ WORD_LIST *t;
+
+ while (list)
+ {
+ t = list;
+ list = list->next;
+ dispose_word (t->word);
+#if 0
+ free (t);
+#else
+ ocache_free (wlcache, WORD_LIST, t);
+#endif
+ }
+}
+
+#ifdef INCLUDE_UNUSED
+/* How to dispose of an array of pointers to char. This is identical to
+ free_array in stringlib.c. */
+void
+dispose_word_array (array)
+ char **array;
+{
+ register int count;
+
+ if (array == 0)
+ return;
+
+ for (count = 0; array[count]; count++)
+ free (array[count]);
+
+ free (array);
+}
+#endif
+
+/* How to dispose of an list of redirections. A REDIRECT. */
+void
+dispose_redirects (list)
+ REDIRECT *list;
+{
+ register REDIRECT *t;
+
+ while (list)
+ {
+ t = list;
+ list = list->next;
+ switch (t->instruction)
+ {
+ case r_reading_until:
+ case r_deblank_reading_until:
+ free (t->here_doc_eof);
+ /*FALLTHROUGH*/
+ case r_reading_string:
+ case r_output_direction:
+ case r_input_direction:
+ case r_inputa_direction:
+ case r_appending_to:
+ case r_err_and_out:
+ case r_append_err_and_out:
+ case r_input_output:
+ case r_output_force:
+ case r_duplicating_input_word:
+ case r_duplicating_output_word:
+ case r_move_input_word:
+ case r_move_output_word:
+ dispose_word (t->redirectee.filename);
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ free (t);
+ }
+}
.\" Case Western Reserve University
.\" chet@po.cwru.edu
.\"
-.\" Last Change: Thu May 8 09:32:34 EDT 2008
+.\" Last Change: Sun May 25 10:41:29 EDT 2008
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
-.TH BASH 1 "2008 May 8" "GNU Bash-4.0"
+.TH BASH 1 "2008 May 25" "GNU Bash-4.0"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
symbols:
.RS
.PP
-.if t \fB\(bv\(bv & && ; ;; ( ) | <newline>\fP
-.if n \fB|| & && ; ;; ( ) | <newline>\fP
+.if t \fB\(bv\(bv & && ; ;; ( ) | |& <newline>\fP
+.if n \fB|| & && ; ;; ( ) | |& <newline>\fP
.RE
.PD
.SH "RESERVED WORDS"
.SS Pipelines
.PP
A \fIpipeline\fP is a sequence of one or more commands separated by
-the character
-.BR | .
+one of the control operators
+.B |
+or \fB|&\fP.
The format for a pipeline is:
.RS
.PP
-[\fBtime\fP [\fB\-p\fP]] [ ! ] \fIcommand\fP [ \fB|\fP \fIcommand2\fP ... ]
+[\fBtime\fP [\fB\-p\fP]] [ ! ] \fIcommand\fP [ [\fB|\fP\(bv\fB|&\fP] \fIcommand2\fP ... ]
.RE
.PP
The standard output of
.SM
.B REDIRECTION
below).
+If \fB|&\fP is used, the standard error of \fIcommand\fP is connected to
+\fIcommand2\fP's standard input through the pipe; it is shorthand for
+\fB2>&1 |\fP.
+This implicit redirection of the standard error is performed after any
+redirections specified by the command.
.PP
The return status of a pipeline is the exit status of the last
command, unless the \fBpipefail\fP option is enabled.
.B nocasematch
is enabled, the match is performed without regard to the case
of alphabetic characters.
-When a match is found, the
-corresponding \fIlist\fP is executed. After the first match, no
-subsequent matches are attempted. The exit status is zero if no
+When a match is found, the corresponding \fIlist\fP is executed.
+If the \fB;;\fP operator is used, no subsequent matches are attempted after
+the first pattern match.
+Using \fB;&\fP in place of \fB;;\fP causes execution to continue with
+the \fIlist\fP associated with the next set of patterns.
+Using \fB;;&\fP in place of \fB;;\fP causes the shell to test the next
+pattern list in the statement, if any, and execute any associated \fIlist\fP
+on a successful match.
+The exit status is zero if no
pattern matches. Otherwise, it is the exit status of the
last command executed in \fIlist\fP.
.TP
string are not sorted; left to right order is preserved.
For example, a\fB{\fPd,c,b\fB}\fPe expands into `ade ace abe'.
.PP
-A sequence expression takes the form \fB{\fP\fIx\fP\fB..\fP\fIy\fP\fB}\fP,
-where \fIx\fP and \fIy\fP are either integers or single characters.
+A sequence expression takes the form
+\fB{\fP\fIx\fP\fB..\fP\fIy\fP\fB[..\fIincr\fP]}\fP,
+where \fIx\fP and \fIy\fP are either integers or single characters,
+and \fIincr\fP, an optional increment, is an integer.
When integers are supplied, the expression expands to each number between
\fIx\fP and \fIy\fP, inclusive.
+Supplied integers may be prefixed with \fI0\fP to force each term to have the
+same width. When either \fIx\fP or \fPy\fP begins with a zero, the shell
+attempts to force all generated terms to contain the same number of digits,
+zero-padding where necessary.
When characters are supplied, the expression expands to each character
lexicographically between \fIx\fP and \fIy\fP, inclusive. Note that
both \fIx\fP and \fIy\fP must be of the same type.
+When the increment is supplied, it is used as the difference between
+each term. The default increment is 1 or -1 as appropriate.
.PP
Brace expansion is performed before any other expansions,
and any characters special to other expansions are preserved
.PP
.SS Redirecting Standard Output and Standard Error
.PP
-.B Bash
-allows both the
+This construct allows both the
standard output (file descriptor 1) and
the standard error output (file descriptor 2)
to be redirected to the file whose name is the
expansion of
-.I word
-with this construct.
+.IR word .
.PP
There are two formats for redirecting standard output and
standard error:
.PP
\fB>\fP\fIword\fP 2\fB>&\fP1
.RE
+.PP
+.SS Appending Standard Output and Standard Error
+.PP
+This construct allows both the
+standard output (file descriptor 1) and
+the standard error output (file descriptor 2)
+to be appended to the file whose name is the
+expansion of
+.IR word .
+.PP
+The format for appending standard output and standard error is:
+.RS
+.PP
+\fB&>>\fP\fIword\fP
+.RE
+.PP
+This is semantically equivalent to
+.RS
+.PP
+\fB>>\fP\fIword\fP 2\fB>&\fP1
+.RE
.SS Here Documents
.PP
This type of redirection instructs the shell to read input from the
the text against lines from the history list for possible
completion matches.
.TP
+.B dabbrev\-expand
+Attempt menu completion on the text before point, comparing
+the text against lines from the history list for possible
+completion matches.
+.TP
.B complete\-into\-braces (M\-{)
Perform filename completion and insert the list of possible completions
enclosed within braces so the list is available to the shell (see
.B \-x \fIkeyseq\fP:\fIshell\-command\fP
Cause \fIshell\-command\fP to be executed whenever \fIkeyseq\fP is
entered.
+When \fIshell\-command\fP is executed, the shell sets the
+.B READLINE_LINE
+variable to the contents of the \fBreadline\fP line buffer and the
+.B READLINE_POINT
+variable to the current location of the insertion point.
+If the executed command changes the value of
+.B READLINE_LINE
+or
+.BR READLINE_POINT ,
+those new values will be reflected in the editing state.
.PD
.PP
The return value is 0 unless an unrecognized option is given or an
.\" Case Western Reserve University
.\" chet@po.cwru.edu
.\"
-.\" Last Change: Thu May 8 09:32:34 EDT 2008
+.\" Last Change: Sun May 25 10:41:29 EDT 2008
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
-.TH BASH 1 "2008 May 8" "GNU Bash-4.0"
+.TH BASH 1 "2008 May 25" "GNU Bash-4.0"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
symbols:
.RS
.PP
-.if t \fB\(bv\(bv & && ; ;; ( ) | <newline>\fP
-.if n \fB|| & && ; ;; ( ) | <newline>\fP
+.if t \fB\(bv\(bv & && ; ;; ( ) | |& <newline>\fP
+.if n \fB|| & && ; ;; ( ) | |& <newline>\fP
.RE
.PD
.SH "RESERVED WORDS"
.SS Pipelines
.PP
A \fIpipeline\fP is a sequence of one or more commands separated by
-the character
-.BR | .
+one of the control operators
+.B |
+or \fB|&\fP.
The format for a pipeline is:
.RS
.PP
-[\fBtime\fP [\fB\-p\fP]] [ ! ] \fIcommand\fP [ \fB|\fP \fIcommand2\fP ... ]
+[\fBtime\fP [\fB\-p\fP]] [ ! ] \fIcommand\fP [ [\fB|\fP\(bv\fB|&\fP] \fIcommand2\fP ... ]
.RE
.PP
The standard output of
.SM
.B REDIRECTION
below).
+If \fB|&\fP is used, the standard error of \fIcommand\fP is connected to
+\fIcommand2\fP's standard input through the pipe; it is shorthand for
+\fB2>&1 |\fP.
+This implicit redirection of the standard error is performed after any
+redirections specified by the command.
.PP
The return status of a pipeline is the exit status of the last
command, unless the \fBpipefail\fP option is enabled.
.B nocasematch
is enabled, the match is performed without regard to the case
of alphabetic characters.
-When a match is found, the
-corresponding \fIlist\fP is executed. After the first match, no
-subsequent matches are attempted. The exit status is zero if no
+When a match is found, the corresponding \fIlist\fP is executed.
+If the \fB;;\fP operator is used, no subsequent matches are attempted after
+the first pattern match.
+Using \fB;&\fP in place of \fB;;\fP causes execution to continue with
+the \fIlist\fP associated with the next set of patterns.
+Using \fB;;\fP in place of \fB;;\fP causes the shell to test the next
+pattern list in the statement, if any, and execute any associated \fIlist\fP
+on a successful match.
+The exit status is zero if no
pattern matches. Otherwise, it is the exit status of the
last command executed in \fIlist\fP.
.TP
string are not sorted; left to right order is preserved.
For example, a\fB{\fPd,c,b\fB}\fPe expands into `ade ace abe'.
.PP
-A sequence expression takes the form \fB{\fP\fIx\fP\fB..\fP\fIy\fP\fB}\fP,
-where \fIx\fP and \fIy\fP are either integers or single characters.
+A sequence expression takes the form
+\fB{\fP\fIx\fP\fB..\fP\fIy\fP\fB[..\fIincr\fP]}\fP,
+where \fIx\fP and \fIy\fP are either integers or single characters,
+and \fIincr\fP, an optional increment, is an integer.
When integers are supplied, the expression expands to each number between
\fIx\fP and \fIy\fP, inclusive.
+Supplied integers may be prefixed with \fI0\fP to force each term to have the
+same width. When either \fIx\fP or \fPy\fP begins with a zero, the shell
+attempts to force all generated terms to contain the same number of digits,
+zero-padding where necessary.
When characters are supplied, the expression expands to each character
lexicographically between \fIx\fP and \fIy\fP, inclusive. Note that
both \fIx\fP and \fIy\fP must be of the same type.
+When the increment is supplied, it is used as the difference between
+each term. The default increment is 1 or -1 as appropriate.
.PP
Brace expansion is performed before any other expansions,
and any characters special to other expansions are preserved
.PP
.SS Redirecting Standard Output and Standard Error
.PP
-.B Bash
-allows both the
+This construct allows both the
standard output (file descriptor 1) and
the standard error output (file descriptor 2)
to be redirected to the file whose name is the
expansion of
-.I word
-with this construct.
+.IR word .
.PP
There are two formats for redirecting standard output and
standard error:
.PP
\fB>\fP\fIword\fP 2\fB>&\fP1
.RE
+.PP
+.SS Appending Standard Output and Standard Error
+.PP
+This construct allows both the
+standard output (file descriptor 1) and
+the standard error output (file descriptor 2)
+to be appended to the file whose name is the
+expansion of
+.IR word .
+.PP
+The format for appending standard output and standard error is:
+.RS
+.PP
+\fB&>>\fP\fIword\fP
+.RE
+.PP
+This is semantically equivalent to
+.RS
+.PP
+\fB>>\fP\fIword\fP 2\fB>&\fP1
+.RE
.SS Here Documents
.PP
This type of redirection instructs the shell to read input from the
the text against lines from the history list for possible
completion matches.
.TP
+.B dabbrev\-expand
+Attempt menu completion on the text before point, comparing
+the text against lines from the history list for possible
+completion matches.
+.TP
.B complete\-into\-braces (M\-{)
Perform filename completion and insert the list of possible completions
enclosed within braces so the list is available to the shell (see
.B \-x \fIkeyseq\fP:\fIshell\-command\fP
Cause \fIshell\-command\fP to be executed whenever \fIkeyseq\fP is
entered.
+When \fIshell\-command\fP is executed, the shell sets the
+.B READLINE_LINE
+variable to the contents of the \fBreadline\fP line buffer and the
+.B READLINE_POINT
+variable to the current location of the insertion point.
+If the executed command changes the value of
+.B READLINE_LINE
+or
+.BR READLINE_POINT ,
+those new values will be reflected in the editing state.
.PD
.PP
The return value is 0 unless an unrecognized option is given or an
must be \(>= 1. If
.I n
is greater than the number of enclosing loops, all enclosing loops
-are exited. The return value is 0 unless the shell is not executing
-a loop when
-.B break
-is executed.
+are exited.
+The return value is 0 unless \fIn\fP is not greater than or equal to 1.
.TP
\fBbuiltin\fP \fIshell\-builtin\fP [\fIarguments\fP]
Execute the specified shell builtin, passing it
must be \(>= 1. If
.I n
is greater than the number of enclosing loops, the last enclosing loop
-(the ``top-level'' loop) is resumed. The return value is 0 unless the
-shell is not executing a loop when
-.B continue
-is executed.
+(the ``top-level'' loop) is resumed.
+The return value is 0 unless \fIn\fP is not greater than or equal to 1.
.TP
\fBdeclare\fP [\fB\-afFirtx\fP] [\fB\-p\fP] [\fIname\fP[=\fIvalue\fP] ...]
.PD 0
A @code{token} that performs a control function. It is a @code{newline}
or one of the following:
@samp{||}, @samp{&&}, @samp{&}, @samp{;}, @samp{;;},
-@samp{|}, @samp{(}, or @samp{)}.
+@samp{|}, @samp{|&}, @samp{(}, or @samp{)}.
@item exit status
@cindex exit status
@cindex pipeline
@cindex commands, pipelines
-A @code{pipeline} is a sequence of simple commands separated by
-@samp{|}.
+A @code{pipeline} is a sequence of simple commands separated by one of
+the control operators @samp{|} or @samp{|&}.
@rwindex time
@rwindex !
@cindex command timing
The format for a pipeline is
@example
-[@code{time} [@code{-p}]] [@code{!}] @var{command1} [@code{|} @var{command2} @dots{}]
+[@code{time} [@code{-p}]] [@code{!}] @var{command1} [ [@code{|} or @code{|&}] @var{command2} @dots{}]
@end example
@noindent
The output of each command in the pipeline is connected via a pipe
to the input of the next command.
-That is, each command reads the previous command's output.
+That is, each command reads the previous command's output. This
+connection is performed before any redirections specified by the
+command.
+
+If @samp{|&} is used, the standard error of @var{command1} is connected to
+@var{command2}'s standard input through the pipe; it is shorthand for
+@code{2>&1 |}. This implicit redirection of the standard error is
+performed after any redirections specified by the command.
The reserved word @code{time} causes timing statistics
to be printed for the pipeline once it finishes.
The @samp{|} is used to separate multiple patterns, and the @samp{)}
operator terminates a pattern list.
A list of patterns and an associated command-list is known
-as a @var{clause}. Each clause must be terminated with @samp{;;}.
+as a @var{clause}.
+
+Each clause must be terminated with @samp{;;}, @samp{,&}, or @samp{;;&}.
The @var{word} undergoes tilde expansion, parameter expansion, command
substitution, arithmetic expansion, and quote removal before matching is
attempted. Each @var{pattern} undergoes tilde expansion, parameter
expansion, command substitution, and arithmetic expansion.
There may be an arbitrary number of @code{case} clauses, each terminated
-by a @samp{;;}. The first pattern that matches determines the
+by a @samp{;;}, @samp{;&}, or @samp{;;&}.
+The first pattern that matches determines the
command-list that is executed.
Here is an example using @code{case} in a script that could be used to
@end example
@noindent
+
+If the @samp{;;} operator is used, no subsequent matches are attempted after
+the first pattern match.
+Using @samp{;&} in place of @samp{;;} causes execution to continue with
+the @var{command-list} associated with the next clause, if any.
+Using @samp{;;&} in place of @samp{;;} causes the shell to test the patterns
+in the next clause, if any, and execute any associated @var{command-list}
+on a successful match.
+
The return status is zero if no @var{pattern} is matched. Otherwise, the
return status is the exit status of the @var{command-list} executed.
ade ace abe
@end example
-A sequence expression takes the form @code{@{@var{x}..@var{y}@}},
-where @var{x} and @var{y} are either integers or single characters.
+A sequence expression takes the form @code{@{@var{x}..@var{y}[@var{incr}]@}},
+where @var{x} and @var{y} are either integers or single characters,
+and @var{incr}, an optional increment, is an integer.
When integers are supplied, the expression expands to each number between
@var{x} and @var{y}, inclusive.
+Supplied integers may be prefixed with @samp{0} to force each term to have the
+same width. When either @var{x} or @var{y} begins with a zero, the shell
+attempts to force all generated terms to contain the same number of digits,
+zero-padding where necessary.
When characters are supplied, the expression expands to each character
lexicographically between @var{x} and @var{y}, inclusive. Note that
both @var{x} and @var{y} must be of the same type.
+When the increment is supplied, it is used as the difference between
+each term. The default increment is 1 or -1 as appropriate.
Brace expansion is performed before any other expansions,
and any characters special to other expansions are preserved
@end example
@subsection Redirecting Standard Output and Standard Error
-Bash allows both the
+This construct allows both the
standard output (file descriptor 1) and
the standard error output (file descriptor 2)
to be redirected to the file whose name is the
-expansion of @var{word} with this construct.
+expansion of @var{word}.
There are two formats for redirecting standard output and
standard error:
>@var{word} 2>&1
@end example
+@subsection Appending Standard Output and Standard Error
+This construct allows both the
+standard output (file descriptor 1) and
+the standard error output (file descriptor 2)
+to be appended to the file whose name is the
+expansion of @var{word}.
+
+The format for appending standard output and standard error is:
+@example
+&>>@var{word}
+@end example
+@noindent
+This is semantically equivalent to
+@example
+>>@var{word} 2>&1
+@end example
+
@subsection Here Documents
This type of redirection instructs the shell to read input from the
current source until a line containing only @var{word}
@item -x @var{keyseq:shell-command}
Cause @var{shell-command} to be executed whenever @var{keyseq} is
entered.
-
+When @var{shell-command} is executed, the shell sets the
+@code{READLINE_LINE} variable to the contents of the Readline line
+buffer and the @code{READLINE_POINT} variable to the current location
+of the insertion point.
+If the executed command changes the value of @code{READLINE_LINE} or
+@code{READLINE_POINT}, those new values will be reflected in the
+editing state.
@end table
@noindent
A @code{token} that performs a control function. It is a @code{newline}
or one of the following:
@samp{||}, @samp{&&}, @samp{&}, @samp{;}, @samp{;;},
-@samp{|}, @samp{(}, or @samp{)}.
+@samp{|}, @samp{|&}, @samp{(}, or @samp{)}.
@item exit status
@cindex exit status
@cindex pipeline
@cindex commands, pipelines
-A @code{pipeline} is a sequence of simple commands separated by
-@samp{|}.
+A @code{pipeline} is a sequence of simple commands separated by one of
+the control operators @samp{|} or @samp{|&}.
@rwindex time
@rwindex !
@cindex command timing
The format for a pipeline is
@example
-[@code{time} [@code{-p}]] [@code{!}] @var{command1} [@code{|} @var{command2} @dots{}]
+[@code{time} [@code{-p}]] [@code{!}] @var{command1} [ [@code{|} or @code{|&}] @var{command2} @dots{}]
@end example
@noindent
The output of each command in the pipeline is connected via a pipe
to the input of the next command.
-That is, each command reads the previous command's output.
+That is, each command reads the previous command's output. This
+connection is performed before any redirections specified by the
+command.
+
+If @samp{|&} is used, the standard error of @var{command1} is connected to
+@var{command2}'s standard input through the pipe; it is shorthand for
+@code{2>&1 |}. This implicit redirection of the standard error is
+performed after any redirections specified by the command.
The reserved word @code{time} causes timing statistics
to be printed for the pipeline once it finishes.
ade ace abe
@end example
-A sequence expression takes the form @code{@{@var{x}..@var{y}@}},
-where @var{x} and @var{y} are either integers or single characters.
+A sequence expression takes the form @code{@{@var{x}..@var{y}[@var{incr}]@}},
+where @var{x} and @var{y} are either integers or single characters,
+and @var{incr}, an optional increment, is an integer.
When integers are supplied, the expression expands to each number between
@var{x} and @var{y}, inclusive.
+Supplied integers may be prefixed with @samp{0} to force each term to have the
+same width. When either @var{x} or @var{y} begins with a zero, the shell
+attempts to force all generated terms to contain the same number of digits,
+zero-padding where necessary.
When characters are supplied, the expression expands to each character
lexicographically between @var{x} and @var{y}, inclusive. Note that
both @var{x} and @var{y} must be of the same type.
+When the increment is supplied, it is used as the difference between
+each term. The default increment is 1 or -1 as appropriate.
Brace expansion is performed before any other expansions,
and any characters special to other expansions are preserved
@end example
@subsection Redirecting Standard Output and Standard Error
-Bash allows both the
+This construct allows both the
standard output (file descriptor 1) and
the standard error output (file descriptor 2)
to be redirected to the file whose name is the
-expansion of @var{word} with this construct.
+expansion of @var{word}.
There are two formats for redirecting standard output and
standard error:
>@var{word} 2>&1
@end example
+@subsection Appending Standard Output and Standard Error
+This construct allows both the
+standard output (file descriptor 1) and
+the standard error output (file descriptor 2)
+to be appended to the file whose name is the
+expansion of @var{word}.
+
+The format for appending standard output and standard error is:
+@example
+&>>@var{word}
+@end example
+@noindent
+This is semantically equivalent to
+@example
+>>@var{word} 2>&1
+@end example
+
@subsection Here Documents
This type of redirection instructs the shell to read input from the
current source until a line containing only @var{word}
@item -x @var{keyseq:shell-command}
Cause @var{shell-command} to be executed whenever @var{keyseq} is
entered.
-
+When @var{shell-command} is executed, the shell sets the
+@code{READLINE_LINE} variable to the contents of the Readline line
+buffer and the @code{READLINE_POINT} variable to the current location
+of the insertion point.
+If the executed command changes the value of @code{READLINE_LINE} or
+@code{READLINE_POINT}, those new values will be reflected in the
+editing state.
@end table
@noindent
@item -t @var{timeout}
Cause @code{read} to time out and return failure if a complete line of
input is not read within @var{timeout} seconds.
+@var{timeout} may be a decimal number with a fractional portion following
+the decimal point.
This option has no effect if @code{read} is not reading input from the
terminal or a pipe.
Copyright (C) 1988-2008 Free Software Foundation, Inc.
@end ignore
-@set LASTCHANGE Sun May 4 22:23:58 EDT 2008
+@set LASTCHANGE Sun May 25 10:48:26 EDT 2008
@set EDITION 4.0
@set VERSION 4.0
-@set UPDATED 4 May 2008
+@set UPDATED 25 May 2008
@set UPDATED-MONTH May 2008
Copyright (C) 1988-2008 Free Software Foundation, Inc.
@end ignore
-@set LASTCHANGE Fri Apr 25 12:33:01 EDT 2008
+@set LASTCHANGE Sun May 4 22:23:58 EDT 2008
@set EDITION 4.0
@set VERSION 4.0
-@set UPDATED 25 April 2008
-@set UPDATED-MONTH April 2008
+@set UPDATED 4 May 2008
+@set UPDATED-MONTH May 2008
if (match)
{
- if (clauses->action && ignore_return)
- clauses->action->flags |= CMD_IGNORE_RETURN;
- retval = execute_command (clauses->action);
- EXIT_CASE ();
+ do
+ {
+ if (clauses->action && ignore_return)
+ clauses->action->flags |= CMD_IGNORE_RETURN;
+ retval = execute_command (clauses->action);
+ }
+ while ((clauses->flags & CASEPAT_FALLTHROUGH) && (clauses = clauses->next));
+ if ((clauses->flags & CASEPAT_TESTNEXT) == 0)
+ EXIT_CASE ();
+ else
+ break;
}
QUIT;
eval builtin is being called, and we're supposed to ignore the exit
value of the command, we turn the -e flag off ourselves, then
restore it when the command completes. This is also a problem (as
- below) for the command and source builtins. */
+ below) for the command and source/. builtins. */
if (subshell == 0 && (flags & CMD_IGNORE_RETURN) &&
(builtin == eval_builtin || builtin == command_builtin || builtin == source_builtin))
{
the text against lines from the history list for possible
completion matches.
+@item dabbrev-expand ()
+Attempt menu completion on the text before point, comparing
+the text against lines from the history list for possible
+completion matches.
+
@item complete-into-braces (M-@{)
Perform filename completion and insert the list of possible completions
enclosed within braces so the list is available to the shell
@vindex revert-all-at-newline
If set to @samp{on}, Readline will undo all changes to history lines
before returning when @code{accept-line} is executed. By default,
-history lines may be edited and retain individual undo lists across
+history lines may be modified and retain individual undo lists across
calls to @code{readline}. The default is @samp{off}.
@item show-all-if-ambiguous
@set EDITION 5.2
@set VERSION 5.2
-@set UPDATED 8 May 2008
+@set UPDATED 25 May 2008
@set UPDATED-MONTH May 2008
-@set LASTCHANGE Thu May 8 09:29:33 EDT 2008
+@set LASTCHANGE Sun May 25 12:00:28 EDT 2008
@set EDITION 5.2
@set VERSION 5.2
-@set UPDATED 7 April 2008
-@set UPDATED-MONTH April 2008
+@set UPDATED 8 May 2008
+@set UPDATED-MONTH May 2008
-@set LASTCHANGE Mon Apr 7 23:00:49 EDT 2008
+@set LASTCHANGE Thu May 8 09:29:33 EDT 2008
/* make_cmd.c -- Functions for making instances of the various
parser constructs. */
-/* Copyright (C) 1989-2007 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
break;
case r_appending_to: /* >>foo */
+ case r_append_err_and_out: /* &>> filename */
temp->flags = O_APPEND | O_WRONLY | O_CREAT;
break;
case r_output_direction: /* >foo */
case r_output_force: /* >| foo */
- case r_err_and_out: /* command &>filename */
+ case r_err_and_out: /* &>filename */
temp->flags = O_TRUNC | O_WRONLY | O_CREAT;
break;
/* Yacc grammar for bash. */
-/* Copyright (C) 1989-2007 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
/* 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;
+
/* 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];
/* The line number in a script at which an arithmetic for command starts. */
static int arith_for_lineno;
+/* The current parser state. */
+static int parser_state;
+
+/* 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;
+
/* 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. */
%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 LESS_LESS_MINUS AND_GREATER LESS_GREATER
-%token GREATER_BAR
+%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. */
%left '&' ';' '\n' yacc_EOF
%left AND_AND OR_OR
-%right '|'
+%right '|' BAR_AND
%%
inputunit: simple_list simple_list_terminator
redir.filename = $2;
$$ = make_redirection (1, r_err_and_out, redir);
}
+ | AND_GREATER_GREATER WORD
+ {
+ redir.filename = $2;
+ $$ = make_redirection (1, r_append_err_and_out, redir);
+ }
| NUMBER LESS_GREATER WORD
{
redir.filename = $3;
;
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
;
-pipeline:
- pipeline '|' newline_list pipeline
+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;
+ REDIRECT *r;
+
+ tc = $1;
+ rd.dest = 1;
+ r = make_redirection (2, r_duplicating_output, rd);
+ 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; }
;
# define expanding_alias() 0
#endif
-/* The token currently being read. */
-static int current_token;
-
-/* 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;
-
-/* The current parser state. */
-static int parser_state;
-
/* Global var is non-zero when end of file has been reached. */
int EOF_Reached = 0;
{ "<&", 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 */
{ ">", '>' },
#define command_token_position(token) \
(((token) == ASSIGNMENT_WORD) || \
- ((token) != SEMI_SEMI && reserved_word_acceptable(token)))
+ ((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))
#if defined (ALIAS)
parser_state &= ~PST_ALEXPNEXT;
#endif /* ALIAS */
-
- return (SEMI_SEMI);
+ 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);
return (LESS_GREATER);
else if MBTEST(character == '>' && peek_char == '|')
return (GREATER_BAR);
- else if MBTEST(peek_char == '>' && character == '&')
- return (AND_GREATER);
+ 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);
#define P_COMMAND 0x08 /* parsing a command, so look for comments */
#define P_BACKQUOTE 0x10 /* parsing a backquoted command substitution */
+/* 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 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 */
case IF:
case OR_OR:
case SEMI_SEMI:
+ case SEMI_AND:
+ case SEMI_SEMI_AND:
case THEN:
case TIME:
case TIMEOPT:
newline separator for such tokens is replaced with a space. */
static const int no_semi_successors[] = {
'\n', '{', '(', ')', ';', '&', '|',
- CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN,
+ CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
+ WHILE, AND_AND, OR_OR, IN,
0
};
/* Yacc grammar for bash. */
-/* Copyright (C) 1989-2007 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
/* 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;
+
/* 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];
/* The line number in a script at which an arithmetic for command starts. */
static int arith_for_lineno;
+/* The current parser state. */
+static int parser_state;
+
+/* 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;
+
/* 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. */
%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 LESS_LESS_MINUS AND_GREATER LESS_GREATER
-%token GREATER_BAR
+%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. */
%left '&' ';' '\n' yacc_EOF
%left AND_AND OR_OR
-%right '|'
+%right '|' BAR_AND
%%
inputunit: simple_list simple_list_terminator
redir.filename = $2;
$$ = make_redirection (1, r_err_and_out, redir);
}
+ | AND_GREATER_GREATER WORD
+ {
+ redir.filename = $2;
+ $$ = make_redirection (1, r_append_err_and_out, redir);
+ }
| NUMBER LESS_GREATER WORD
{
redir.filename = $3;
;
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
;
-pipeline:
- pipeline '|' newline_list pipeline
+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;
+ REDIRECT *r;
+
+ tc = $1;
+ rd.dest = 1;
+ r = make_redirection (2, r_duplicating_output, rd);
+ 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; }
;
;
%%
-/* Possible states for the parser that require it to do special things. */
-#define PST_CASEPAT 0x0001 /* in a case pattern list */
-#define PST_ALEXPNEXT 0x0002 /* expand next word for aliases */
-#define PST_ALLOWOPNBRC 0x0004 /* allow open brace for function def */
-#define PST_NEEDCLOSBRC 0x0008 /* need close brace */
-#define PST_DBLPAREN 0x0010 /* double-paren parsing */
-#define PST_SUBSHELL 0x0020 /* ( ... ) subshell */
-#define PST_CMDSUBST 0x0040 /* $( ... ) command substitution */
-#define PST_CASESTMT 0x0080 /* parsing a case statement */
-#define PST_CONDCMD 0x0100 /* parsing a [[...]] command */
-#define PST_CONDEXPR 0x0200 /* parsing the guts of [[...]] */
-#define PST_ARITHFOR 0x0400 /* parsing an arithmetic for command */
-#define PST_ALEXPAND 0x0800 /* OK to expand aliases - unused */
-#define PST_CMDTOKEN 0x1000 /* command token OK - unused */
-#define PST_COMPASSIGN 0x2000 /* parsing x=(...) compound assignment */
-#define PST_ASSIGNOK 0x4000 /* assignment statement ok in this context */
-#define PST_REGEXP 0x8000 /* parsing an ERE/BRE as a single word */
-
/* Initial size to allocate for tokens, and the
amount to grow them by. */
#define TOKEN_DEFAULT_INITIAL_SIZE 496
# define expanding_alias() 0
#endif
-/* The token currently being read. */
-static int current_token;
-
-/* 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;
-
-/* The current parser state. */
-static int parser_state;
-
/* Global var is non-zero when end of file has been reached. */
int EOF_Reached = 0;
{ "<&", 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 */
{ ">", '>' },
#define command_token_position(token) \
(((token) == ASSIGNMENT_WORD) || \
- ((token) != SEMI_SEMI && reserved_word_acceptable(token)))
+ ((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))
#if defined (ALIAS)
parser_state &= ~PST_ALEXPNEXT;
#endif /* ALIAS */
-
- return (SEMI_SEMI);
+ 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);
return (LESS_GREATER);
else if MBTEST(character == '>' && peek_char == '|')
return (GREATER_BAR);
- else if MBTEST(peek_char == '>' && character == '&')
- return (AND_GREATER);
+ 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);
#define P_COMMAND 0x08 /* parsing a command, so look for comments */
#define P_BACKQUOTE 0x10 /* parsing a backquoted command substitution */
+/* 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 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 */
case IF:
case OR_OR:
case SEMI_SEMI:
+ case SEMI_AND:
+ case SEMI_SEMI_AND:
case THEN:
case TIME:
case TIMEOPT:
newline separator for such tokens is replaced with a space. */
static const int no_semi_successors[] = {
'\n', '{', '(', ')', ';', '&', '|',
- CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN,
+ CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
+ WHILE, AND_AND, OR_OR, IN,
0
};
/* print_command -- A way to make readable commands from a command tree. */
-/* Copyright (C) 1989-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
indentation += indentation_amount;
make_command_string_internal (clauses->action);
indentation -= indentation_amount;
- newline (";;");
+ if (clauses->flags & CASEPAT_FALLTHROUGH)
+ newline (";&");
+ else if (clauses->flags & CASEPAT_TESTNEXT)
+ newline (";;&");
+ else
+ newline (";;");
clauses = clauses->next;
}
indentation -= indentation_amount;
cprintf ("&>%s", redirectee->word);
break;
+ case r_append_err_and_out:
+ cprintf ("&>>%s", redirectee->word);
+ break;
+
case r_input_output:
if (redirector != 1)
cprintf ("%d", redirector);
/* print_command -- A way to make readable commands from a command tree. */
-/* Copyright (C) 1989-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
break;
case r_err_and_out:
- cprintf (">&%s", redirectee->word);
+ cprintf ("&>%s", redirectee->word);
+ break;
+
+ case r_append_err_and_out:
+ cprintf ("&>>%s", redirectee->word);
break;
case r_input_output:
/* redir.c -- Functions to perform input and output redirection. */
-/* Copyright (C) 1997-2007 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2008 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
case r_input_direction:
case r_inputa_direction:
case r_err_and_out:
+ case r_append_err_and_out:
case r_input_output:
case r_output_force:
case r_duplicating_input_word:
case r_input_direction:
case r_inputa_direction:
case r_err_and_out: /* command &>filename */
+ case r_append_err_and_out: /* command &>> filename */
case r_input_output:
case r_output_force:
if (posixly_correct && interactive_shell == 0)
/* If we are hacking both stdout and stderr, do the stderr
redirection here. */
- if (ri == r_err_and_out)
+ if (ri == r_err_and_out || ri == r_append_err_and_out)
{
if (flags & RX_ACTIVE)
{
case r_appending_to:
case r_duplicating_output:
case r_err_and_out:
+ case r_append_err_and_out:
case r_output_force:
case r_duplicating_output_word:
return (0);
#endif /* RESTRICTED_SHELL */
case HEREDOC_REDIRECT:
- internal_error (_("cannot create temp file for here document: %s"), strerror (heredoc_errno));
+ internal_error (_("cannot create temp file for here-document: %s"), strerror (heredoc_errno));
break;
default:
reinit_special_variables ();
#if defined (READLINE)
- bashline_reset ();
+ bashline_reinitialize ();
#endif
shell_reinitialized = 1;
reinit_special_variables ();
#if defined (READLINE)
- bash_readline_initialized = 0;
+ bashline_reset ();
#endif
shell_reinitialized = 1;
#if defined (READLINE)
if (interactive)
- bashline_reinitialize ();
+ bashline_reset ();
#endif /* READLINE */
#if defined (PROCESS_SUBSTITUTION)
/* sig.c - interface for shell signal handlers and signal initialization. */
-/* Copyright (C) 1994-2006 Free Software Foundation, Inc.
+/* Copyright (C) 1994-2007 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#undef XSIG
#undef XHANDLER
+/* Run some of the cleanups that should be performed when we run
+ jump_to_top_level from a builtin command context. XXX - might want to
+ also call reset_parser here. */
+void
+top_level_cleanup ()
+{
+ /* Clean up string parser environment. */
+ while (parse_and_execute_level)
+ parse_and_execute_cleanup ();
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ run_unwind_protects ();
+ loop_level = continuing = breaking = 0;
+ return_catch_flag = 0;
+}
+
/* What to do when we've been interrupted, and it is safe to handle it. */
void
throw_to_top_level ()
/* Run any traps set on SIGINT. */
run_interrupt_trap ();
- /* Cleanup string parser environment. */
+ /* Clean up string parser environment. */
while (parse_and_execute_level)
parse_and_execute_cleanup ();
-BUILD_DIR=/usr/local/build/chet/bash/bash-current
+BUILD_DIR=/usr/local/build/bash/bash-current
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
-20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0
a-{bd}-c a-{be}-c
a-{bdef-g-c a-{bdef-i-c
+{klklkl}1 {klklkl}2 {klklkl}3
+{x,x}
+1 3 5 7 9
+-1 -3 -5 -7 -9
+-1 -3 -5 -7 -9
+10 8 6 4 2
+10 8 6 4 2
+1 3 5 7 9 11 13 15 17 19
+1
+0100 0095 0090 0085 0080 0075 0070 0065 0060 0055 0050 0045 0040 0035 0030 0025 0020 0015 0010 0005 0000
+00100 00095 00090 00085 00080 00075 00070 00065 00060 00055 00050 00045 00040 00035 00030 00025 00020 00015 00010 00005 00000
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+a c e g i k m o q s u w y
+z x v t r p n l j h f d b
+{1..10.f}
+{1..ff}
+{1..10..ff}
+{1.20..2}
+{1..20..f2}
+{1..20..2f}
+{1..2f..2}
+{1..ff..2}
+{1..ff}
+{1..f}
+{1..0f}
+{1..10f}
+{1..10.f}
+{1..10.f}
echo a-{b{d,e}}-c
echo a-{bdef-{g,i}-c
+
+echo {"klklkl"}{1,2,3}
+echo {"x,x"}
+
+echo {1..10..2}
+echo {-1..-10..2}
+echo {-1..-10..-2}
+
+echo {10..1..-2}
+echo {10..1..2}
+
+echo {1..20..2}
+echo {1..20..20}
+
+echo {100..0..5}
+echo {100..0..-5}
+
+echo {a..z}
+echo {a..z..2}
+echo {z..a..-2}
+
+# bad
+echo {1..10.f}
+echo {1..ff}
+echo {1..10..ff}
+echo {1.20..2}
+echo {1..20..f2}
+echo {1..20..2f}
+echo {1..2f..2}
+echo {1..ff..2}
+echo {1..ff}
+echo {1..f}
+echo {1..0f}
+echo {1..10f}
+echo {1..10.f}
+echo {1..10.f}
--- /dev/null
+echo ff{c,b,a}
+echo f{d,e,f}g
+echo {l,n,m}xyz
+echo {abc\,def}
+echo {abc}
+
+echo \{a,b,c,d,e}
+echo {x,y,\{a,b,c}}
+echo {x\,y,\{abc\},trie}
+
+echo /usr/{ucb/{ex,edit},lib/{ex,how_ex}}
+
+echo XXXX\{`echo a b c | tr ' ' ','`\}
+eval echo XXXX\{`echo a b c | tr ' ' ','`\}
+
+echo {}
+echo { }
+echo }
+echo {
+echo abcd{efgh
+
+echo foo {1,2} bar
+echo `zecho foo {1,2} bar`
+echo $(zecho foo {1,2} bar)
+
+var=baz
+varx=vx
+vary=vy
+
+echo foo{bar,${var}.}
+echo foo{bar,${var}}
+
+echo "${var}"{x,y}
+echo $var{x,y}
+echo ${var}{x,y}
+
+unset var varx vary
+
+# new sequence brace operators
+echo {1..10}
+
+# this doesn't work yet
+echo {0..10,braces}
+# but this does
+echo {{0..10},braces}
+echo x{{0..10},braces}y
+
+echo {3..3}
+echo x{3..3}y
+echo {10..1}
+echo {10..1}y
+echo x{10..1}y
+
+echo {a..f}
+echo {f..a}
+
+echo {a..A}
+echo {A..a}
+
+echo {f..f}
+
+# mixes are incorrectly-formed brace expansions
+echo {1..f}
+echo {f..1}
+
+echo 0{1..9} {10..20}
+
+# do negative numbers work?
+echo {-1..-10}
+echo {-20..0}
+
+# weirdly-formed brace expansions -- fixed in post-bash-3.1
+echo a-{b{d,e}}-c
+
+echo a-{bdef-{g,i}-c
+
+echo {"klklkl"}{1,2,3}
+echo {"x,x"}
--- /dev/null
+fallthrough
+to here
+and here
+retest
+and match
--- /dev/null
+case foo in
+bar) echo skip ;;
+foo) echo fallthrough ;&
+bax) echo to here ;&
+qux) echo and here;;
+fop) echo but not here;;
+esac
+
+case foobar in
+bar) echo skip ;;
+foo*) echo retest ;;&
+*bar) echo and match ;;&
+qux) echo but not this ;;
+esac
returns: 0
ok
jbig2dec
+
found 1
libc
found 2
[[ jbig2dec-0.9-i586-001.tgz =~ ([^-]+)-([^-]+)-([^-]+)-0*([1-9][0-9]*)\.tgz ]]
echo ${BASH_REMATCH[1]}
+# this shouldn't echo anything
+[[ jbig2dec-0.9-i586-001.tgz =~ \([^-]+\)-\([^-]+\)-\([^-]+\)-0*\([1-9][0-9]*\)\.tgz ]]
+echo ${BASH_REMATCH[1]}
+
LDD_BASH=" linux-gate.so.1 => (0xffffe000)
libreadline.so.5 => /lib/libreadline.so.5 (0xb7f91000)
libhistory.so.5 => /lib/libhistory.so.5 (0xb7f8a000)
[[ jbig2dec-0.9-i586-001.tgz =~ ([^-]+)-([^-]+)-([^-]+)-0*([1-9][0-9]*)\.tgz ]]
echo ${BASH_REMATCH[1]}
+LDD_BASH=" linux-gate.so.1 => (0xffffe000)
+ libreadline.so.5 => /lib/libreadline.so.5 (0xb7f91000)
+ libhistory.so.5 => /lib/libhistory.so.5 (0xb7f8a000)
+ libncurses.so.5 => /lib/libncurses.so.5 (0xb7f55000)
+ libdl.so.2 => /lib/libdl.so.2 (0xb7f51000)
+ libc.so.6 => /lib/libc.so.6 (0xb7e34000)
+ /lib/ld-linux.so.2 (0xb7fd0000)"
+
+[[ "$LDD_BASH" =~ "libc" ]] && echo "found 1"
+echo ${BASH_REMATCH[@]}
+
+[[ "$LDD_BASH" =~ libc ]] && echo "found 2"
+echo ${BASH_REMATCH[@]}
+
# bug in all versions up to and including bash-2.05b
if [[ "123abc" == *?(a)bc ]]; then echo ok 42; else echo bad 42; fi
if [[ "123abc" == *?(a)bc ]]; then echo ok 43; else echo bad 43; fi
+
+${THIS_SH} ./cond-regexp.sub
abc
-./redir.tests: line 13: /tmp/redir-test: cannot overwrite existing file
+./redir.tests: line 15: /tmp/redir-test: cannot overwrite existing file
abc
def
def
-./redir.tests: line 29: $z: ambiguous redirect
+./redir.tests: line 31: $z: ambiguous redirect
Point 1
Point 2
to a
ab
cd
cd
-./redir.tests: line 152: redir1.*: No such file or directory
+./redir.tests: line 154: redir1.*: No such file or directory
# tests of ksh93-like dup-and-close redirection operators
exec 9<$0
4
cat /tmp/foo
whatsis
+hey
+to stdout
+to stderr
+
+to stdout
+to stderr
+
+to stderr
+to stdout
+
+to stderr
+hey
+to stdout
+logfunc is a function
+logfunc ()
+{
+ echo "$@" &>>$TMPDIR/log
+}
+foo
+bix is a function
+bix ()
+{
+ echo foo 2>&1 | cat
+}
+foo
+: ${TMPDIR:=/tmp}
+
export LC_ALL=C
export LANG=C
echo "Point 1"
exec 3</etc/passwd
-exec 4>/tmp/bash-a
-exec 5>/tmp/bash-b
+exec 4>$TMPDIR/bash-a
+exec 5>$TMPDIR/bash-b
echo "Point 2"
echo to a 1>&4
echo to b 1>&5
-cat /tmp/bash-a
-cat /tmp/bash-b
+cat $TMPDIR/bash-a
+cat $TMPDIR/bash-b
exec 11</dev/null
echo "Point 3"
echo to a 1>&4
echo to b 1>&5
-cat /tmp/bash-a
-cat /tmp/bash-b
+cat $TMPDIR/bash-a
+cat $TMPDIR/bash-b
exec 11<&-
echo "Point 4"
-exec 6<>/tmp/bash-c
+exec 6<>$TMPDIR/bash-c
echo to c 1>&6
-cat /tmp/bash-c
+cat $TMPDIR/bash-c
echo "Point 5"
-rm -f /tmp/bash-a /tmp/bash-b /tmp/bash-c
+rm -f $TMPDIR/bash-a $TMPDIR/bash-b $TMPDIR/bash-c
#
# Test the effect of input buffering on the shell's input
fi
}
-> /tmp/null-redir-a
-testf /tmp/null-redir-a
+> $TMPDIR/null-redir-a
+testf $TMPDIR/null-redir-a
-$EXIT > /tmp/null-redir-b
-testf /tmp/null-redir-b
+$EXIT > $TMPDIR/null-redir-b
+testf $TMPDIR/null-redir-b
-( > /tmp/null-redir-c )
-testf /tmp/null-redir-c
+( > $TMPDIR/null-redir-c )
+testf $TMPDIR/null-redir-c
-$EXIT > /tmp/null-redir-d &
+$EXIT > $TMPDIR/null-redir-d &
wait
-testf /tmp/null-redir-d
+testf $TMPDIR/null-redir-d
-exit 3 | $EXIT > /tmp/null-redir-e
+exit 3 | $EXIT > $TMPDIR/null-redir-e
echo $? -- ${PIPESTATUS[@]}
-testf /tmp/null-redir-e
+testf $TMPDIR/null-redir-e
-exit 4 | > /tmp/null-redir-f
+exit 4 | > $TMPDIR/null-redir-f
echo $? -- ${PIPESTATUS[@]}
-testf /tmp/null-redir-f
+testf $TMPDIR/null-redir-f
-> /tmp/null-redir-g &
+> $TMPDIR/null-redir-g &
wait
-testf /tmp/null-redir-g
+testf $TMPDIR/null-redir-g
-exec >/tmp/null-redir-h &
+exec >$TMPDIR/null-redir-h &
wait
-testf /tmp/null-redir-h
+testf $TMPDIR/null-redir-h
# make sure async commands don't get /dev/null as stdin when an explicit
# input redirection is supplied
${THIS_SH} ./redir6.sub
# problem with redirections using fds bash uses internally
-: ${TMPDIR:=/tmp}
+: ${TMPDIR:=$TMPDIR}
trap 'rm -f $TMPDIR/bash-redir-$$' 0 1 2 3 6 15
${THIS_SH} ./redir8.sub
exec 9>&2
-command exec 2>/tmp/foo
+command exec 2>$TMPDIR/foo
echo whatsis >&2
echo cat /tmp/foo
-cat /tmp/foo
-rm -f /tmp/foo
+cat $TMPDIR/foo
+rm -f $TMPDIR/foo
exec 2>&9
exec 9>&-
+
+${THIS_SH} ./redir9.sub
+: ${TMPDIR:=/tmp}
+
export LC_ALL=C
export LANG=C
echo "Point 1"
exec 3</etc/passwd
-exec 4>/tmp/bash-a
-exec 5>/tmp/bash-b
+exec 4>$TMPDIR/bash-a
+exec 5>$TMPDIR/bash-b
echo "Point 2"
echo to a 1>&4
echo to b 1>&5
-cat /tmp/bash-a
-cat /tmp/bash-b
+cat $TMPDIR/bash-a
+cat $TMPDIR/bash-b
exec 11</dev/null
echo "Point 3"
echo to a 1>&4
echo to b 1>&5
-cat /tmp/bash-a
-cat /tmp/bash-b
+cat $TMPDIR/bash-a
+cat $TMPDIR/bash-b
exec 11<&-
echo "Point 4"
-exec 6<>/tmp/bash-c
+exec 6<>$TMPDIR/bash-c
echo to c 1>&6
-cat /tmp/bash-c
+cat $TMPDIR/bash-c
echo "Point 5"
-rm -f /tmp/bash-a /tmp/bash-b /tmp/bash-c
+rm -f $TMPDIR/bash-a $TMPDIR/bash-b $TMPDIR/bash-c
#
# Test the effect of input buffering on the shell's input
fi
}
-> /tmp/null-redir-a
-testf /tmp/null-redir-a
+> $TMPDIR/null-redir-a
+testf $TMPDIR/null-redir-a
-$EXIT > /tmp/null-redir-b
-testf /tmp/null-redir-b
+$EXIT > $TMPDIR/null-redir-b
+testf $TMPDIR/null-redir-b
-( > /tmp/null-redir-c )
-testf /tmp/null-redir-c
+( > $TMPDIR/null-redir-c )
+testf $TMPDIR/null-redir-c
-$EXIT > /tmp/null-redir-d &
+$EXIT > $TMPDIR/null-redir-d &
wait
-testf /tmp/null-redir-d
+testf $TMPDIR/null-redir-d
-exit 3 | $EXIT > /tmp/null-redir-e
+exit 3 | $EXIT > $TMPDIR/null-redir-e
echo $? -- ${PIPESTATUS[@]}
-testf /tmp/null-redir-e
+testf $TMPDIR/null-redir-e
-exit 4 | > /tmp/null-redir-f
+exit 4 | > $TMPDIR/null-redir-f
echo $? -- ${PIPESTATUS[@]}
-testf /tmp/null-redir-f
+testf $TMPDIR/null-redir-f
-> /tmp/null-redir-g &
+> $TMPDIR/null-redir-g &
wait
-testf /tmp/null-redir-g
+testf $TMPDIR/null-redir-g
-exec >/tmp/null-redir-h &
+exec >$TMPDIR/null-redir-h &
wait
-testf /tmp/null-redir-h
+testf $TMPDIR/null-redir-h
# make sure async commands don't get /dev/null as stdin when an explicit
# input redirection is supplied
${THIS_SH} ./redir6.sub
# problem with redirections using fds bash uses internally
-: ${TMPDIR:=/tmp}
+: ${TMPDIR:=$TMPDIR}
trap 'rm -f $TMPDIR/bash-redir-$$' 0 1 2 3 6 15
${THIS_SH} ./redir7.sub
${THIS_SH} ./redir8.sub
+
+exec 9>&2
+command exec 2>$TMPDIR/foo
+echo whatsis >&2
+echo cat $TMPDIR/foo
+cat $TMPDIR/foo
+rm -f $TMPDIR/foo
+exec 2>&9
+exec 9>&-
+
+${THIS_SH} ./redir9.sub
--- /dev/null
+rm -f u
+
+${THIS_SH} -c 'exec 10>&1; echo fd 10 >&10' 10>u
+cat u
+rm -f u
+
+${THIS_SH} -c 'exec 8>&1; echo fd 8 >&8' 8>u
+cat u
+rm -f u
+
+exec 10>u
+exec 10>&1; echo 'fd 10' >&10
+cat u
+rm -f u
+exec 10>&-
+
+exec 8>u
+exec 8>&1; echo 'fd 8' >&8
+cat u
+rm -f u
+exec 8>&-
+
+rm -f infile
+cat > infile <<EOF
+1
+2
+3
+4
+EOF
+
+exec 7<&0
+exec 10<infile
+exec 0<&10; cat <&10
+exec 0<&7
+exec 7<&-
+
+exec 7<&0
+exec 8<infile
+exec 0<&8 ; cat <&8
+exec 0<&7
+exec 7<&-
+
+exec 7<&0
+exec 0</dev/null
+exec 10<infile
+exec 10<&0; cat <&10
+exec 0<&7
+exec 7<&-
+
+exec 7<&0
+exec 0</dev/null
+exec 8<infile
+exec 8<&0; cat <&8
+exec 0<&7
+exec 7<&-
+
+exit 0
+
+
+
--- /dev/null
+: ${TMPDIR:=/tmp}
+
+func()
+{
+ echo "to stdout"
+ echo "to stderr" >&2
+}
+
+rm -f $TMPDIR/foo
+
+echo hey > $TMPDIR/foo
+func &>> $TMPDIR/foo
+
+cat $TMPDIR/foo
+
+echo
+func &> $TMPDIR/foo
+
+cat $TMPDIR/foo
+
+echo
+func >$TMPDIR/foo
+cat $TMPDIR/foo
+
+echo
+echo hey > $TMPDIR/foo
+func >> $TMPDIR/foo
+cat $TMPDIR/foo
+
+rm -f $TMPDIR/foo
+
+logfunc()
+{
+ echo "$@" &>> $TMPDIR/log
+}
+
+type logfunc
+
+echo foo 2>&1
+
+bix()
+{
+echo foo |& cat
+}
+
+type bix
+
+bix
--- /dev/null
+: ${TMPDIR:=/tmp}
+
+func()
+{
+ echo "to stdout"
+ echo "to stderr" >&2
+}
+
+rm -f $TMPDIR/foo
+
+echo hey > $TMPDIR/foo
+func &>> $TMPDIR/foo
+
+cat $TMPDIR/foo
+
+echo
+func &> $TMPDIR/foo
+
+cat $TMPDIR/foo
+
+echo
+func >$TMPDIR/foo
+cat $TMPDIR/foo
+
+echo
+echo hey > $TMPDIR/foo
+func >> $TMPDIR/foo
+cat $TMPDIR/foo
+
+rm -f $TMPDIR/foo
+
+logfunc()
+{
+ echo "$@" &>> $TMPDIR/log
+}
+
+type logfunc
--- /dev/null
+${THIS_SH} ./case.tests > /tmp/xx 2>&1
+diff /tmp/xx case.right && rm -f /tmp/xx
--- /dev/null
+echo "warning: all of these tests will fail if arrays have not" >&2
+echo "warning: been compiled into the shell" >&2
+echo "warning: the BASH_ARGC and BASH_ARGV tests will fail if debugging support" >&2
+echo "warning: has not been compiled into the shell" >&2
+${THIS_SH} ./array.tests > /tmp/xx 2>&1
+diff /tmp/xx array.right && rm -f /tmp/xx
{
v='^A'
}
+foo is a function
+foo ()
+{
+ echo $(<x1)
+}
+bar is a function
+bar ()
+{
+ echo $(<x1)
+}
}
type f | cat -v
+
+${THIS_SH} type1.sub
# make sure the hash table looks right
hash
+
+# bug in versions of bash up to and including bash-3.2
+f() {
+ v=$'\001'
+ }
+
+type f | cat -v
--- /dev/null
+foo()
+{
+ echo $(<x1)
+}
+
+type foo
+
+eval "$(declare -f foo | sed 's:foo:bar:')"
+
+type bar