From 8943768b87f00cb5ce36f5aa494471c9a9e09652 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Wed, 7 Dec 2011 09:24:08 -0500 Subject: [PATCH] commit bash-20080522 snapshot --- CWRU/CWRU.chlog | 102 +++++++- CWRU/CWRU.chlog~ | 92 ++++++- MANIFEST | 5 + MANIFEST~ | 3 + bashhist.c | 50 ++++ bashhist.c~ | 24 +- bashhist.h | 3 + bashhist.h~ | 68 ++++++ bashline.c | 84 ++++++- bashline.c~ | 72 +++++- bashline.h | 2 +- bashline.h~ | 1 + braces.c | 76 ++++-- braces.c~ | 81 +++++-- builtins/common.h | 2 + builtins/common.h~ | 1 + builtins/evalstring.c | 203 ++++++++++++---- builtins/evalstring.c~ | 101 ++++---- builtins/fc.def | 5 +- builtins/fc.def~ | 2 - builtins/history.def | 55 +---- builtins/history.def~ | 24 +- command.h | 12 +- command.h~ | 356 +++++++++++++++++++++++++++ copy_cmd.c | 2 + copy_cmd.c~ | 422 +++++++++++++++++++++++++++++++++ dispose_cmd.c | 3 +- dispose_cmd.c~ | 330 ++++++++++++++++++++++++++ doc/bash.1 | 86 +++++-- doc/bash.1~ | 98 ++++++-- doc/bashref.texi | 73 +++++- doc/bashref.texi~ | 59 ++++- doc/version.texi | 4 +- doc/version.texi~ | 6 +- execute_cmd.c | 15 +- execute_cmd.c~ | 2 +- lib/readline/doc/rluser.texi | 5 + lib/readline/doc/rluser.texi~ | 2 +- lib/readline/doc/version.texi | 4 +- lib/readline/doc/version.texi~ | 6 +- make_cmd.c | 3 +- make_cmd.c~ | 2 +- parse.y | 161 ++++++++++--- parse.y~ | 179 ++++++++++---- print_cmd.c | 13 +- print_cmd.c~ | 8 +- redir.c | 7 +- redir.c~ | 2 +- shell.c | 2 +- shell.c~ | 2 +- sig.c | 2 +- sig.c~ | 23 +- tests/RUN-ONE-TEST | 2 +- tests/braces.right | 28 +++ tests/braces.tests | 36 +++ tests/braces.tests~ | 78 ++++++ tests/case.right | 5 + tests/case.tests | 14 ++ tests/cond.right | 1 + tests/cond.tests | 4 + tests/cond.tests~ | 16 ++ tests/redir.right | 31 ++- tests/redir.tests | 62 ++--- tests/redir.tests~ | 65 +++-- tests/redir8.sub | 3 - tests/redir8.sub~ | 60 +++++ tests/redir9.sub | 48 ++++ tests/redir9.sub~ | 37 +++ tests/run-case | 2 + tests/run-case~ | 6 + tests/type.right | 10 + tests/type.tests | 2 + tests/type.tests~ | 7 + tests/type1.sub | 10 + 74 files changed, 3031 insertions(+), 441 deletions(-) create mode 100644 bashhist.h~ create mode 100644 command.h~ create mode 100644 copy_cmd.c~ create mode 100644 dispose_cmd.c~ create mode 100644 tests/braces.tests~ create mode 100644 tests/case.right create mode 100644 tests/case.tests create mode 100644 tests/redir8.sub~ create mode 100644 tests/redir9.sub create mode 100644 tests/redir9.sub~ create mode 100644 tests/run-case create mode 100644 tests/run-case~ create mode 100644 tests/type1.sub diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 1f867449f..1e2214d4a 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -15738,8 +15738,6 @@ lib/readline/readline.h 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 @@ -15812,6 +15810,9 @@ lib/readline/display.c - 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 ---- @@ -15834,3 +15835,100 @@ bashline.c 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 + + - 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 diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index ff2d394f7..64c42ca2b 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -15738,8 +15738,6 @@ lib/readline/readline.h 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 @@ -15812,6 +15810,9 @@ lib/readline/display.c - 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 ---- @@ -15822,6 +15823,93 @@ variables.c 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 + + - 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 diff --git a/MANIFEST b/MANIFEST index df394de4e..6eb4ec727 100644 --- a/MANIFEST +++ b/MANIFEST @@ -759,6 +759,8 @@ tests/source3.sub f 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 @@ -906,6 +908,7 @@ tests/redir5.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 @@ -920,6 +923,7 @@ tests/run-array 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 @@ -996,6 +1000,7 @@ tests/trap2.sub f 755 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 diff --git a/MANIFEST~ b/MANIFEST~ index 8be34a8a3..2e09b62bd 100644 --- a/MANIFEST~ +++ b/MANIFEST~ @@ -552,6 +552,7 @@ doc/fdl.texi 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 @@ -905,6 +906,7 @@ tests/redir5.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 @@ -995,6 +997,7 @@ tests/trap2.sub f 755 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 diff --git a/bashhist.c b/bashhist.c index 9e77bc591..51aa6bc09 100644 --- a/bashhist.c +++ b/bashhist.c @@ -288,6 +288,56 @@ load_history () } } +void +bash_clear_history () +{ + clear_history (); + history_lines_this_session = 0; +} + +/* Delete and free the history list entry at offset I. */ +int +bash_delete_histent (i) + int i; +{ + HIST_ENTRY *discard; + + discard = remove_history (i); + if (discard) + free_history_entry (discard); + history_lines_this_session--; + + return 1; +} + +int +bash_delete_last_history () +{ + register int i; + HIST_ENTRY **hlist, *histent; + int r; + + hlist = history_list (); + if (hlist == NULL) + return 0; + + for (i = 0; hlist[i]; i++) + ; + i--; + + /* History_get () takes a parameter that must be offset by history_base. */ + histent = history_get (history_base + i); /* Don't free this */ + if (histent == NULL) + return 0; + + r = bash_delete_histent (i); + + if (where_history () > history_length) + history_set_pos (history_length); + + return r; +} + #ifdef INCLUDE_UNUSED /* Write the existing history out to the history file. */ void diff --git a/bashhist.c~ b/bashhist.c~ index bb7df07ba..5d15c518a 100644 --- a/bashhist.c~ +++ b/bashhist.c~ @@ -251,7 +251,7 @@ bash_history_disable () void bash_history_enable () { - remember_on_history = enable_history_list = 1; + remember_on_history = 1; #if defined (BANG_HISTORY) history_expansion_inhibited = 0; #endif @@ -288,6 +288,28 @@ load_history () } } +void +bash_clear_history () +{ + clear_history (); + history_lines_this_session = 0; +} + +/* Delete and free the history list entry at offset I. */ +int +bash_delete_histent (i) + int i; +{ + HIST_ENTRY *discard; + + discard = remove_history (i); + if (discard) + free_history_entry (discard); + history_lines_this_session--; + + return 1; +} + #ifdef INCLUDE_UNUSED /* Write the existing history out to the history file. */ void diff --git a/bashhist.h b/bashhist.h index 35929aca4..39813e31b 100644 --- a/bashhist.h +++ b/bashhist.h @@ -49,6 +49,9 @@ 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 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 *)); diff --git a/bashhist.h~ b/bashhist.h~ new file mode 100644 index 000000000..dece490c9 --- /dev/null +++ b/bashhist.h~ @@ -0,0 +1,68 @@ +/* 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_ */ diff --git a/bashline.c b/bashline.c index 4ff359098..1d45bb0a4 100644 --- a/bashline.c +++ b/bashline.c @@ -122,6 +122,7 @@ static char *command_subst_completion_function __P((const char *, int)); 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 *)); @@ -162,6 +163,7 @@ extern int hist_verify; #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[]; @@ -331,7 +333,7 @@ enable_hostname_completion (on_or_off) 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); } @@ -395,6 +397,7 @@ initialize_readline () #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) @@ -514,7 +517,7 @@ initialize_readline () } void -bashline_reset () +bashline_reinitialize () { bash_readline_initialized = 0; } @@ -525,7 +528,7 @@ bashline_reset () 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; @@ -1508,7 +1511,7 @@ globword: /* 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); @@ -1535,6 +1538,7 @@ globword: free (filename_hint); filename_hint = sh_makepath (current_path, hint, 0); + free (current_path); /* XXX */ } inner: @@ -2427,7 +2431,7 @@ bash_directory_expansion (dirname) *dirname = nd; } } - + /* Handle symbolic link references and other directory name expansions while hacking completion. */ static int @@ -2614,12 +2618,12 @@ dynamic_complete_history (count, key) 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; @@ -2634,6 +2638,33 @@ dynamic_complete_history (count, key) 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) @@ -3133,8 +3164,12 @@ bash_execute_unix_command (count, key) 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. */ @@ -3170,12 +3205,41 @@ bash_execute_unix_command (count, key) 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 (); diff --git a/bashline.c~ b/bashline.c~ index 798315446..28dd18c63 100644 --- a/bashline.c~ +++ b/bashline.c~ @@ -122,6 +122,7 @@ static char *command_subst_completion_function __P((const char *, int)); 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 *)); @@ -162,6 +163,7 @@ extern int hist_verify; #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[]; @@ -331,7 +333,7 @@ enable_hostname_completion (on_or_off) 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); } @@ -395,6 +397,7 @@ initialize_readline () #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) @@ -513,13 +516,19 @@ initialize_readline () 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; @@ -1502,7 +1511,7 @@ globword: /* 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); @@ -1529,6 +1538,7 @@ globword: free (filename_hint); filename_hint = sh_makepath (current_path, hint, 0); + free (current_path); /* XXX */ } inner: @@ -2421,7 +2431,7 @@ bash_directory_expansion (dirname) *dirname = nd; } } - + /* Handle symbolic link references and other directory name expansions while hacking completion. */ static int @@ -2608,12 +2618,12 @@ dynamic_complete_history (count, key) 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; @@ -2628,6 +2638,33 @@ dynamic_complete_history (count, key) 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) @@ -3127,8 +3164,10 @@ bash_execute_unix_command (count, key) 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. */ @@ -3164,12 +3203,27 @@ bash_execute_unix_command (count, key) 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 (); diff --git a/bashline.h b/bashline.h index cd84d879f..8cf22ff48 100644 --- a/bashline.h +++ b/bashline.h @@ -1,6 +1,6 @@ /* 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. diff --git a/bashline.h~ b/bashline.h~ index eac5d3533..cd84d879f 100644 --- a/bashline.h~ +++ b/bashline.h~ @@ -28,6 +28,7 @@ extern int bash_readline_initialized; 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 *)); diff --git a/braces.c b/braces.c index 13270d9b4..5810d0f5e 100644 --- a/braces.c +++ b/braces.c @@ -1,6 +1,6 @@ /* 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. @@ -61,7 +61,7 @@ static const int brace_arg_separator = ','; 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 (); @@ -301,10 +301,11 @@ expand_amble (text, tlen, flags) #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; @@ -330,6 +331,12 @@ mkseq (start, end, incr, type) #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); @@ -337,9 +344,9 @@ mkseq (start, end, incr, type) t[1] = '\0'; result[i++] = t; } - if (n == end) - break; n += incr; + if ((incr < 0 && n < end) || (incr > 0 && n > end)) + break; } while (1); @@ -353,17 +360,17 @@ expand_seqterm (text, tlen) 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) { @@ -376,8 +383,36 @@ expand_seqterm (text, tlen) 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) { @@ -394,14 +429,27 @@ expand_seqterm (text, tlen) { 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); diff --git a/braces.c~ b/braces.c~ index 34a72be43..53498da26 100644 --- a/braces.c~ +++ b/braces.c~ @@ -1,6 +1,6 @@ /* 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. @@ -61,7 +61,7 @@ static const int brace_arg_separator = ','; 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 (); @@ -301,10 +301,11 @@ expand_amble (text, tlen, flags) #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; @@ -330,6 +331,12 @@ mkseq (start, end, incr, type) #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); @@ -337,9 +344,9 @@ mkseq (start, end, incr, type) t[1] = '\0'; result[i++] = t; } - if (n == end) - break; n += incr; + if ((incr < 0 && n < end) || (incr > 0 && n > end)) + break; } while (1); @@ -353,17 +360,17 @@ expand_seqterm (text, tlen) 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) { @@ -376,8 +383,37 @@ expand_seqterm (text, tlen) 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) { @@ -394,14 +430,27 @@ expand_seqterm (text, tlen) { 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); @@ -486,8 +535,8 @@ brace_gobbler (text, tlen, indx, satisfy) } #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); diff --git a/builtins/common.h b/builtins/common.h index 3a817cc25..ec1200b5f 100644 --- a/builtins/common.h +++ b/builtins/common.h @@ -31,6 +31,8 @@ #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 */ diff --git a/builtins/common.h~ b/builtins/common.h~ index 0a46d2001..3a817cc25 100644 --- a/builtins/common.h~ +++ b/builtins/common.h~ @@ -142,6 +142,7 @@ extern int describe_command __P((char *, int)); /* 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)); diff --git a/builtins/evalstring.c b/builtins/evalstring.c index beffa3667..490fd22a7 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -1,6 +1,6 @@ /* 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. @@ -44,6 +44,8 @@ #include "../redir.h" #include "../trap.h" +#include + #if defined (HISTORY) # include "../bashhist.h" #endif @@ -58,6 +60,7 @@ extern int errno; 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; @@ -67,6 +70,17 @@ int parse_and_execute_level = 0; 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 () @@ -76,42 +90,21 @@ 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); @@ -120,18 +113,14 @@ parse_and_execute (string, from_file, flags) 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 */ @@ -146,8 +135,42 @@ parse_and_execute (string, from_file, flags) 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 @@ -157,13 +180,6 @@ parse_and_execute (string, from_file, flags) 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; @@ -224,7 +240,7 @@ parse_and_execute (string, from_file, flags) 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); @@ -303,7 +319,7 @@ parse_and_execute (string, from_file, flags) out: - run_unwind_frame ("parse_and_execute_top"); + run_unwind_frame (PE_TAG); if (interrupt_state && parse_and_execute_level == 0) { @@ -320,6 +336,109 @@ parse_and_execute (string, from_file, flags) 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. */ diff --git a/builtins/evalstring.c~ b/builtins/evalstring.c~ index a8976930d..190b8658e 100644 --- a/builtins/evalstring.c~ +++ b/builtins/evalstring.c~ @@ -1,6 +1,6 @@ /* 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. @@ -44,6 +44,8 @@ #include "../redir.h" #include "../trap.h" +#include + #if defined (HISTORY) # include "../bashhist.h" #endif @@ -58,6 +60,7 @@ extern int errno; 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; @@ -67,6 +70,17 @@ int parse_and_execute_level = 0; 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 () @@ -76,40 +90,21 @@ 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); @@ -118,15 +113,14 @@ parse_and_execute (string, from_file, flags) 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 */ @@ -141,8 +135,42 @@ parse_and_execute (string, from_file, flags) 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 @@ -152,13 +180,6 @@ parse_and_execute (string, from_file, flags) 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; @@ -219,7 +240,7 @@ parse_and_execute (string, from_file, flags) 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); @@ -298,7 +319,7 @@ parse_and_execute (string, from_file, flags) out: - run_unwind_frame ("parse_and_execute_top"); + run_unwind_frame (PE_TAG); if (interrupt_state && parse_and_execute_level == 0) { diff --git a/builtins/fc.def b/builtins/fc.def index 32cd57acb..e1d038be1 100644 --- a/builtins/fc.def +++ b/builtins/fc.def @@ -93,7 +93,6 @@ extern int posixly_correct; extern int unlink __P((const char *)); extern FILE *sh_mktmpfp __P((char *, int, char **)); -extern int delete_last_history __P((void)); /* **************************************************************** */ /* */ @@ -326,7 +325,7 @@ fc_builtin (list) 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 @@ -625,7 +624,7 @@ fc_replhist (command) if (command && *command) { - delete_last_history (); + bash_delete_last_history (); maybe_add_history (command); /* Obeys HISTCONTROL setting. */ } } diff --git a/builtins/fc.def~ b/builtins/fc.def~ index 078e24106..32cd57acb 100644 --- a/builtins/fc.def~ +++ b/builtins/fc.def~ @@ -337,8 +337,6 @@ fc_builtin (list) 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)) { diff --git a/builtins/history.def b/builtins/history.def index 1c888c55a..f21e7c9e3 100644 --- a/builtins/history.def +++ b/builtins/history.def @@ -87,11 +87,8 @@ extern int errno; 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 *)); @@ -162,7 +159,7 @@ history_builtin (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); } @@ -191,7 +188,7 @@ history_builtin (list) 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) @@ -310,48 +307,6 @@ display_history (list) } } -/* 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 @@ -367,12 +322,12 @@ push_history (list) 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; @@ -397,7 +352,7 @@ expand_and_print_history (list) 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) diff --git a/builtins/history.def~ b/builtins/history.def~ index f3008244e..31f72e70b 100644 --- a/builtins/history.def~ +++ b/builtins/history.def~ @@ -51,6 +51,9 @@ if $HISTFILE has a value, that is used, else ~/.bash_history. 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 @@ -88,7 +91,6 @@ 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 *)); @@ -159,7 +161,7 @@ history_builtin (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); } @@ -188,7 +190,7 @@ history_builtin (list) 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) @@ -307,20 +309,6 @@ display_history (list) } } -/* 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 () { @@ -341,7 +329,7 @@ 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); diff --git a/command.h b/command.h index d90a2eac5..2cbae8e7d 100644 --- a/command.h +++ b/command.h @@ -31,7 +31,8 @@ enum r_instruction { 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. */ @@ -44,7 +45,7 @@ enum r_instruction { (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) @@ -54,6 +55,7 @@ enum r_instruction { 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 */ @@ -89,6 +91,7 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, #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 &' */ @@ -97,6 +100,7 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, #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 { @@ -200,6 +204,10 @@ typedef struct 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. */ diff --git a/command.h~ b/command.h~ new file mode 100644 index 000000000..46e5bda92 --- /dev/null +++ b/command.h~ @@ -0,0 +1,356 @@ +/* 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 <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 patterns = copy_word_list (clause->patterns); new_clause->action = copy_command (clause->action); + new_clause->flags = clause->flags; return (new_clause); } @@ -124,6 +125,7 @@ copy_redirect (redirect) 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: diff --git a/copy_cmd.c~ b/copy_cmd.c~ new file mode 100644 index 000000000..d36436c9c --- /dev/null +++ b/copy_cmd.c~ @@ -0,0 +1,422 @@ +/* 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 +#endif + +#include + +#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); +} diff --git a/dispose_cmd.c b/dispose_cmd.c index ee2e68cdf..36c195f9f 100644 --- a/dispose_cmd.c +++ b/dispose_cmd.c @@ -1,6 +1,6 @@ /* 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. @@ -313,6 +313,7 @@ dispose_redirects (list) 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: diff --git a/dispose_cmd.c~ b/dispose_cmd.c~ new file mode 100644 index 000000000..8b8790ddf --- /dev/null +++ b/dispose_cmd.c~ @@ -0,0 +1,330 @@ +/* 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 +#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); + } +} diff --git a/doc/bash.1 b/doc/bash.1 index f5207ff75..ea532b4ee 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,12 +5,12 @@ .\" 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. @@ -471,8 +471,8 @@ A \fItoken\fP that performs a control function. It is one of the following symbols: .RS .PP -.if t \fB\(bv\(bv & && ; ;; ( ) | \fP -.if n \fB|| & && ; ;; ( ) | \fP +.if t \fB\(bv\(bv & && ; ;; ( ) | |& \fP +.if n \fB|| & && ; ;; ( ) | |& \fP .RE .PD .SH "RESERVED WORDS" @@ -507,12 +507,13 @@ The return value of a \fIsimple command\fP is its exit status, or .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 @@ -524,6 +525,11 @@ command (see .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. @@ -820,9 +826,15 @@ If the shell option .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 @@ -2224,13 +2236,21 @@ Brace expansions may be nested. The results of each expanded 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 @@ -3141,14 +3161,12 @@ The general format for appending output is: .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: @@ -3167,6 +3185,27 @@ This is semantically equivalent to .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 @@ -5338,6 +5377,11 @@ Attempt completion on the text before point, comparing 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 @@ -6175,6 +6219,16 @@ Remove any current binding for \fIkeyseq\fP. .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 diff --git a/doc/bash.1~ b/doc/bash.1~ index dafaf84a3..d0e0bed6d 100644 --- a/doc/bash.1~ +++ b/doc/bash.1~ @@ -5,12 +5,12 @@ .\" 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. @@ -471,8 +471,8 @@ A \fItoken\fP that performs a control function. It is one of the following symbols: .RS .PP -.if t \fB\(bv\(bv & && ; ;; ( ) | \fP -.if n \fB|| & && ; ;; ( ) | \fP +.if t \fB\(bv\(bv & && ; ;; ( ) | |& \fP +.if n \fB|| & && ; ;; ( ) | |& \fP .RE .PD .SH "RESERVED WORDS" @@ -507,12 +507,13 @@ The return value of a \fIsimple command\fP is its exit status, or .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 @@ -524,6 +525,11 @@ command (see .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. @@ -820,9 +826,15 @@ If the shell option .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 @@ -2224,13 +2236,21 @@ Brace expansions may be nested. The results of each expanded 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 @@ -3141,14 +3161,12 @@ The general format for appending output is: .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: @@ -3167,6 +3185,27 @@ This is semantically equivalent to .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 @@ -5338,6 +5377,11 @@ Attempt completion on the text before point, comparing 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 @@ -6175,6 +6219,16 @@ Remove any current binding for \fIkeyseq\fP. .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 @@ -6193,10 +6247,8 @@ loop. If \fIn\fP is specified, break \fIn\fP levels. 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 @@ -6552,10 +6604,8 @@ is specified, resume at the \fIn\fPth enclosing loop. 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 diff --git a/doc/bashref.texi b/doc/bashref.texi index f90cc23b2..fdd47c173 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -229,7 +229,7 @@ than by an executable program somewhere in the file system. 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 @@ -606,21 +606,28 @@ the command was terminated by signal @var{n}. @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. @@ -852,14 +859,17 @@ of alphabetic characters. 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 @@ -878,6 +888,15 @@ echo " legs." @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. @@ -1395,13 +1414,20 @@ bash$ echo a@{d,c,b@}e 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 @@ -2094,11 +2120,11 @@ The general format for appending output is: @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: @@ -2117,6 +2143,23 @@ This is semantically equivalent to >@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} @@ -3202,7 +3245,13 @@ Remove any current binding for @var{keyseq}. @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 diff --git a/doc/bashref.texi~ b/doc/bashref.texi~ index 5664da2b7..857b260e5 100644 --- a/doc/bashref.texi~ +++ b/doc/bashref.texi~ @@ -229,7 +229,7 @@ than by an executable program somewhere in the file system. 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 @@ -606,21 +606,28 @@ the command was terminated by signal @var{n}. @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. @@ -1395,13 +1402,20 @@ bash$ echo a@{d,c,b@}e 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 @@ -2094,11 +2108,11 @@ The general format for appending output is: @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: @@ -2117,6 +2131,23 @@ This is semantically equivalent to >@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} @@ -3202,7 +3233,13 @@ Remove any current binding for @var{keyseq}. @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 @@ -3610,6 +3647,8 @@ not echoed. @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. diff --git a/doc/version.texi b/doc/version.texi index ba448d24b..27ea810a9 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,9 +2,9 @@ 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 diff --git a/doc/version.texi~ b/doc/version.texi~ index 314c1efc7..ba448d24b 100644 --- a/doc/version.texi~ +++ b/doc/version.texi~ @@ -2,9 +2,9 @@ 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 diff --git a/execute_cmd.c b/execute_cmd.c index ee6429fd4..ca27b081a 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -2292,10 +2292,17 @@ execute_case_command (case_command) 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; diff --git a/execute_cmd.c~ b/execute_cmd.c~ index 3f29e1676..ee6429fd4 100644 --- a/execute_cmd.c~ +++ b/execute_cmd.c~ @@ -3138,7 +3138,7 @@ execute_builtin (builtin, words, flags, subshell) 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)) { diff --git a/lib/readline/doc/rluser.texi b/lib/readline/doc/rluser.texi index f096775c2..81a92ebc3 100644 --- a/lib/readline/doc/rluser.texi +++ b/lib/readline/doc/rluser.texi @@ -1316,6 +1316,11 @@ Attempt completion on the text before point, comparing 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 diff --git a/lib/readline/doc/rluser.texi~ b/lib/readline/doc/rluser.texi~ index 4301beb24..f096775c2 100644 --- a/lib/readline/doc/rluser.texi~ +++ b/lib/readline/doc/rluser.texi~ @@ -574,7 +574,7 @@ The default is @samp{off}. @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 diff --git a/lib/readline/doc/version.texi b/lib/readline/doc/version.texi index 75322efc5..dc4618ac6 100644 --- a/lib/readline/doc/version.texi +++ b/lib/readline/doc/version.texi @@ -4,7 +4,7 @@ Copyright (C) 1988-2008 Free Software Foundation, Inc. @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 diff --git a/lib/readline/doc/version.texi~ b/lib/readline/doc/version.texi~ index c2044522d..75322efc5 100644 --- a/lib/readline/doc/version.texi~ +++ b/lib/readline/doc/version.texi~ @@ -4,7 +4,7 @@ Copyright (C) 1988-2008 Free Software Foundation, Inc. @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 diff --git a/make_cmd.c b/make_cmd.c index 48dba0887..ec914fdea 100644 --- a/make_cmd.c +++ b/make_cmd.c @@ -1,7 +1,7 @@ /* 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. @@ -690,6 +690,7 @@ make_redirection (source, instruction, dest_and_filename) break; case r_appending_to: /* >>foo */ + case r_append_err_and_out: /* &>> filename */ temp->flags = O_APPEND | O_WRONLY | O_CREAT; break; diff --git a/make_cmd.c~ b/make_cmd.c~ index 08eed18a8..48dba0887 100644 --- a/make_cmd.c~ +++ b/make_cmd.c~ @@ -685,7 +685,7 @@ make_redirection (source, instruction, dest_and_filename) 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; diff --git a/parse.y b/parse.y index 53b09435f..3bc2863bb 100644 --- a/parse.y +++ b/parse.y @@ -1,6 +1,6 @@ /* 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. @@ -251,6 +251,12 @@ static char *current_decoded_prompt; /* 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]; @@ -275,6 +281,19 @@ static int function_bstart; /* 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. */ @@ -316,8 +335,9 @@ static REDIRECTEE redir; %token ARITH_CMD ARITH_FOR_EXPRS %token 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. */ @@ -340,7 +360,7 @@ static REDIRECTEE redir; %left '&' ';' '\n' yacc_EOF %left AND_AND OR_OR -%right '|' +%right '|' BAR_AND %% inputunit: simple_list simple_list_terminator @@ -521,6 +541,11 @@ redirection: '>' WORD 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; @@ -829,8 +854,17 @@ pattern_list: newline_list pattern ')' compound_list ; 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 @@ -999,9 +1033,30 @@ pipeline_command: pipeline ; -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; } ; @@ -1028,22 +1083,6 @@ timespec: TIME # 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; @@ -1779,11 +1818,15 @@ STRING_INT_ALIST other_token_alist[] = { { "<&", 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 */ { ">", '>' }, @@ -2212,7 +2255,7 @@ static int open_brace_count; #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)) @@ -2616,8 +2659,14 @@ read_token (command) #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); @@ -2643,8 +2692,27 @@ read_token (command) 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); @@ -2707,7 +2775,41 @@ tokword: #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 */ @@ -3785,6 +3887,8 @@ reserved_word_acceptable (toksym) case IF: case OR_OR: case SEMI_SEMI: + case SEMI_AND: + case SEMI_SEMI_AND: case THEN: case TIME: case TIMEOPT: @@ -3845,7 +3949,8 @@ reset_readline_prompt () 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 }; diff --git a/parse.y~ b/parse.y~ index 2b710a6de..3bc2863bb 100644 --- a/parse.y~ +++ b/parse.y~ @@ -1,6 +1,6 @@ /* 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. @@ -251,6 +251,12 @@ static char *current_decoded_prompt; /* 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]; @@ -275,6 +281,19 @@ static int function_bstart; /* 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. */ @@ -316,8 +335,9 @@ static REDIRECTEE redir; %token ARITH_CMD ARITH_FOR_EXPRS %token 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. */ @@ -340,7 +360,7 @@ static REDIRECTEE redir; %left '&' ';' '\n' yacc_EOF %left AND_AND OR_OR -%right '|' +%right '|' BAR_AND %% inputunit: simple_list simple_list_terminator @@ -521,6 +541,11 @@ redirection: '>' WORD 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; @@ -829,8 +854,17 @@ pattern_list: newline_list pattern ')' compound_list ; 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 @@ -999,9 +1033,30 @@ pipeline_command: pipeline ; -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; } ; @@ -1013,24 +1068,6 @@ timespec: TIME ; %% -/* 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 @@ -1046,22 +1083,6 @@ timespec: TIME # 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; @@ -1797,11 +1818,15 @@ STRING_INT_ALIST other_token_alist[] = { { "<&", 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 */ { ">", '>' }, @@ -2230,7 +2255,7 @@ static int open_brace_count; #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)) @@ -2634,8 +2659,14 @@ read_token (command) #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); @@ -2661,8 +2692,27 @@ read_token (command) 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); @@ -2725,7 +2775,41 @@ tokword: #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 */ @@ -3803,6 +3887,8 @@ reserved_word_acceptable (toksym) case IF: case OR_OR: case SEMI_SEMI: + case SEMI_AND: + case SEMI_SEMI_AND: case THEN: case TIME: case TIMEOPT: @@ -3863,7 +3949,8 @@ reset_readline_prompt () 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 }; diff --git a/print_cmd.c b/print_cmd.c index d3cf08098..05bb7a838 100644 --- a/print_cmd.c +++ b/print_cmd.c @@ -1,6 +1,6 @@ /* 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. @@ -609,7 +609,12 @@ print_case_clauses (clauses) 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; @@ -972,6 +977,10 @@ print_redirection (redirect) 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); diff --git a/print_cmd.c~ b/print_cmd.c~ index 0386a78a3..b35ecbc46 100644 --- a/print_cmd.c~ +++ b/print_cmd.c~ @@ -1,6 +1,6 @@ /* 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. @@ -969,7 +969,11 @@ print_redirection (redirect) 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: diff --git a/redir.c b/redir.c index 2baf5041b..ca10d572a 100644 --- a/redir.c +++ b/redir.c @@ -1,6 +1,6 @@ /* 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. @@ -217,6 +217,7 @@ expandable_redirection_filename (redirect) 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: @@ -724,6 +725,7 @@ do_redirection_internal (redirect, flags) 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) @@ -821,7 +823,7 @@ do_redirection_internal (redirect, flags) /* 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) { @@ -1081,6 +1083,7 @@ stdin_redirection (ri, redirector) 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); diff --git a/redir.c~ b/redir.c~ index 47a6097c8..2baf5041b 100644 --- a/redir.c~ +++ b/redir.c~ @@ -156,7 +156,7 @@ redirection_error (temp, error) #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: diff --git a/shell.c b/shell.c index fac78dcad..a64da180d 100644 --- a/shell.c +++ b/shell.c @@ -1747,7 +1747,7 @@ shell_reinitialize () reinit_special_variables (); #if defined (READLINE) - bashline_reset (); + bashline_reinitialize (); #endif shell_reinitialized = 1; diff --git a/shell.c~ b/shell.c~ index b69204f71..fac78dcad 100644 --- a/shell.c~ +++ b/shell.c~ @@ -1747,7 +1747,7 @@ shell_reinitialize () reinit_special_variables (); #if defined (READLINE) - bash_readline_initialized = 0; + bashline_reset (); #endif shell_reinitialized = 1; diff --git a/sig.c b/sig.c index f6c4b42c9..3f08f457d 100644 --- a/sig.c +++ b/sig.c @@ -408,7 +408,7 @@ throw_to_top_level () #if defined (READLINE) if (interactive) - bashline_reinitialize (); + bashline_reset (); #endif /* READLINE */ #if defined (PROCESS_SUBSTITUTION) diff --git a/sig.c~ b/sig.c~ index 2bd6cae60..f6c4b42c9 100644 --- a/sig.c~ +++ b/sig.c~ @@ -1,6 +1,6 @@ /* 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. @@ -350,6 +350,25 @@ reset_terminating_signals () #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 () @@ -372,7 +391,7 @@ 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 (); diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 3efcf32d6..72ec06a2c 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/braces.right b/tests/braces.right index f00d39a76..5369a7e3a 100644 --- a/tests/braces.right +++ b/tests/braces.right @@ -43,3 +43,31 @@ f -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} diff --git a/tests/braces.tests b/tests/braces.tests index 5a57f2844..25b07b6a2 100644 --- a/tests/braces.tests +++ b/tests/braces.tests @@ -73,3 +73,39 @@ echo {-20..0} 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} diff --git a/tests/braces.tests~ b/tests/braces.tests~ new file mode 100644 index 000000000..5f15f5f3b --- /dev/null +++ b/tests/braces.tests~ @@ -0,0 +1,78 @@ +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"} diff --git a/tests/case.right b/tests/case.right new file mode 100644 index 000000000..b82ad9a78 --- /dev/null +++ b/tests/case.right @@ -0,0 +1,5 @@ +fallthrough +to here +and here +retest +and match diff --git a/tests/case.tests b/tests/case.tests new file mode 100644 index 000000000..d85dc3ed6 --- /dev/null +++ b/tests/case.tests @@ -0,0 +1,14 @@ +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 diff --git a/tests/cond.right b/tests/cond.right index 39fac2fcb..377523080 100644 --- a/tests/cond.right +++ b/tests/cond.right @@ -34,6 +34,7 @@ returns: 1 returns: 0 ok jbig2dec + found 1 libc found 2 diff --git a/tests/cond.tests b/tests/cond.tests index 5ba18561c..b053e4822 100755 --- a/tests/cond.tests +++ b/tests/cond.tests @@ -158,6 +158,10 @@ fi [[ 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) diff --git a/tests/cond.tests~ b/tests/cond.tests~ index c04b0a250..5ba18561c 100755 --- a/tests/cond.tests~ +++ b/tests/cond.tests~ @@ -158,6 +158,22 @@ fi [[ 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 diff --git a/tests/redir.right b/tests/redir.right index 45fb384c0..f816c63b7 100644 --- a/tests/redir.right +++ b/tests/redir.right @@ -1,9 +1,9 @@ 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 @@ -44,7 +44,7 @@ kl 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 @@ -114,3 +114,28 @@ fd 8 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 diff --git a/tests/redir.tests b/tests/redir.tests index 08b485479..2091a1f53 100644 --- a/tests/redir.tests +++ b/tests/redir.tests @@ -1,3 +1,5 @@ +: ${TMPDIR:=/tmp} + export LC_ALL=C export LANG=C @@ -31,31 +33,31 @@ cat < $z echo "Point 1" exec 3/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&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 @@ -78,34 +80,34 @@ testf() 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 @@ -158,7 +160,7 @@ ${THIS_SH} ./redir5.sub ${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 @@ -176,10 +178,12 @@ ${THIS_SH} ./redir7.sub ${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 diff --git a/tests/redir.tests~ b/tests/redir.tests~ index 644e435ca..5d88357c2 100644 --- a/tests/redir.tests~ +++ b/tests/redir.tests~ @@ -1,3 +1,5 @@ +: ${TMPDIR:=/tmp} + export LC_ALL=C export LANG=C @@ -31,31 +33,31 @@ cat < $z echo "Point 1" exec 3/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&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 @@ -78,34 +80,34 @@ testf() 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 @@ -158,7 +160,7 @@ ${THIS_SH} ./redir5.sub ${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 @@ -174,3 +176,14 @@ echo after block ${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 diff --git a/tests/redir8.sub b/tests/redir8.sub index da12cc766..cf8862ae9 100644 --- a/tests/redir8.sub +++ b/tests/redir8.sub @@ -55,6 +55,3 @@ exec 0<&7 exec 7<&- exit 0 - - - diff --git a/tests/redir8.sub~ b/tests/redir8.sub~ new file mode 100644 index 000000000..da12cc766 --- /dev/null +++ b/tests/redir8.sub~ @@ -0,0 +1,60 @@ +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 <&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 diff --git a/tests/redir9.sub~ b/tests/redir9.sub~ new file mode 100644 index 000000000..a60d5c9f0 --- /dev/null +++ b/tests/redir9.sub~ @@ -0,0 +1,37 @@ +: ${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 diff --git a/tests/run-case b/tests/run-case new file mode 100644 index 000000000..71d9b83d2 --- /dev/null +++ b/tests/run-case @@ -0,0 +1,2 @@ +${THIS_SH} ./case.tests > /tmp/xx 2>&1 +diff /tmp/xx case.right && rm -f /tmp/xx diff --git a/tests/run-case~ b/tests/run-case~ new file mode 100644 index 000000000..6fdbae494 --- /dev/null +++ b/tests/run-case~ @@ -0,0 +1,6 @@ +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 diff --git a/tests/type.right b/tests/type.right index abdd02fbf..18208d132 100644 --- a/tests/type.right +++ b/tests/type.right @@ -51,3 +51,13 @@ f () { v='^A' } +foo is a function +foo () +{ + echo $(