diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 16479fde..309c7457 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -13316,3 +13316,47 @@ doc/bash.1,doc/bashref.texi,lib/readline/doc/hsuser.texi maximum in the file to avoid partial history entries or history entries without a timestamp (if the file has them) From https://savannah.gnu.org/bugs/?68650 + + 8/28 + ---- + +eval.c + - alrm_catcher: change to only be active if readline isn't being used; + all it does now is set a flag (input_timeout_seen) + - read_command: use rl_set_timeout and rl_timeout_event_hook to + implement the timeout specified by $TMOUT if readline is being used; + use SIGALRM only if readline is not being used (--nolineediting) + - alrm_handler: renamed alrm_catcher to this; add call to tcflush to + make sure tty input buffer is empty if we time out reading input + - input_timeout_hook: new function, used by both readline and non- + readline paths, just calls alrm_handler + +shell.h + - extern declaration for input_timeout_seen + +externs.h + - extern declaration for input_timeout_hook() + +quit.h + - CHECK_INPUT_TIMEOUT: new signal macro, calls input_timeout_hook + if we have caught SIGALRM and set input_timeout_seen + +parse.y + - yy_stream_get: add calls to CHECK_INPUT_TIMEOUT around call to + stream_getc + +input.c + - stream_getc: if we get SIGALRM and set input_timeout_seen, reset + the local buffer variables and return EOF. Caller will handle + checking for input_timeout_seen and calling input_timeout_hook() + +parse.y + - no_semi_successors: add BAR_AND to list of tokens that should not + be converted from newline to semicolon + Report from Alberto Millán + + 8/31 + ---- +builtins/history.def + - history_builtin: history -r should increase history_lines_this_session + only if *not* reading from $HISTFILE diff --git a/MANIFEST b/MANIFEST index f9f052a4..c785c379 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1037,6 +1037,7 @@ tests/assoc16.sub f tests/assoc17.sub f tests/assoc18.sub f tests/assoc19.sub f +tests/assoc20.sub f tests/attr.tests f tests/attr.right f tests/attr1.sub f diff --git a/builtins/history.def b/builtins/history.def index 1c35d5b9..ee8bc3db 100644 --- a/builtins/history.def +++ b/builtins/history.def @@ -293,7 +293,7 @@ history_builtin (WORD_LIST *list) if (flags & AFLAG) /* Append session's history to file. */ { result = maybe_append_history (filename, using_histfile); - if (using_histfile) + if (result == 0 && using_histfile) history_lines_this_session = 0; } else if (flags & WFLAG) /* Write entire history. */ @@ -317,7 +317,9 @@ history_builtin (WORD_LIST *list) I/O and permission errors. */ if (result > 0) history_error (filename, result, 1); - if (result == 0) + /* Only act like these are new history entries if we are not reading + from $HISTFILE */ + if (result == 0 && using_histfile == 0) history_lines_this_session += history_lines_read_from_file; } else if (flags & NFLAG) /* Read `new' history from file. */ diff --git a/doc/bash.1 b/doc/bash.1 index 225ede3d..ce34545d 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -8939,7 +8939,7 @@ Several shell options settable with the builtin will modify history expansion behavior (see the description of the .B shopt -builtin below).and +builtin below). If the .B histverify shell option is enabled, and diff --git a/eval.c b/eval.c index 5e01cc56..77a25b42 100644 --- a/eval.c +++ b/eval.c @@ -32,6 +32,10 @@ #include +#if defined (HAVE_TERMIOS_H) +# include +#endif + #include "bashintl.h" #include "shell.h" @@ -44,6 +48,12 @@ #include "input.h" #include "execute_cmd.h" +/* If we are using readline timeouts and timeout hooks */ +#if defined (READLINE) +# include "bashline.h" +# include +#endif /* READLINE */ + #if defined (HISTORY) # include "bashhist.h" #endif @@ -53,8 +63,12 @@ #endif static void send_pwd_to_eterm (void); +static void alrm_handler (int); static sighandler alrm_catcher (int); +int input_timeout_set = 0; +volatile sig_atomic_t input_timeout_seen = 0; + /* Read and execute commands until EOF is reached. This assumes that the input source has already been initialized. */ int @@ -254,16 +268,29 @@ pretty_print_loop (void) } static sighandler -alrm_catcher(int i) +alrm_catcher (int i) { - char *msg; + input_timeout_seen = 1; + SIGRETURN (0); +} - msg = _("\007timed out waiting for input: auto-logout\n"); - write (1, msg, strlen (msg)); +static void +alrm_handler(int i) +{ + printf ("\007%s\n", _("timed out waiting for input: auto-logout")); + fflush (stdout); + tcflush (fileno (stdin), TCIFLUSH); bash_logout (); /* run ~/.bash_logout if this is a login shell */ jump_to_top_level (EXITPROG); - SIGRETURN (0); +} + +/* We can use this for both readline and non-readline cases */ +int +input_timeout_hook (void) +{ + alrm_handler (SIGALRM); + return 0; } /* Send an escape sequence to emacs term mode to tell it the @@ -405,8 +432,19 @@ read_command (void) tmout_len = (int)strtol (t, &e, 10); if (e != t && *e == '\0' && tmout_len > 0) { - old_alrm = set_signal_handler (SIGALRM, alrm_catcher); - alarm (tmout_len); + input_timeout_set = 1; + if (no_line_editing) + { + old_alrm = set_signal_handler (SIGALRM, alrm_catcher); + alarm (tmout_len); + } +#if defined (READLINE) + else + { + rl_timeout_event_hook = input_timeout_hook; + rl_set_timeout (tmout_len, 0); + } +#endif } } } @@ -416,10 +454,21 @@ read_command (void) current_command_line_count = 0; result = parse_command (); - if (interactive && tmout_var && (tmout_len > 0)) + if (interactive && input_timeout_set) { - alarm(0); - set_signal_handler (SIGALRM, old_alrm); + if (no_line_editing) + { + alarm(0); + set_signal_handler (SIGALRM, old_alrm); + } +#if defined (READLINE) + else + { + rl_clear_timeout (); + rl_timeout_event_hook = NULL; + } +#endif + input_timeout_set = 0; } return (result); diff --git a/externs.h b/externs.h index 9739e34d..a21b4f9d 100644 --- a/externs.h +++ b/externs.h @@ -102,6 +102,7 @@ extern int reader_loop (void); extern int pretty_print_loop (void); extern int parse_command (void); extern int read_command (void); +extern int input_timeout_hook (void); /* Functions from braces.c. */ #if defined (BRACE_EXPANSION) diff --git a/input.c b/input.c index 6d038f2c..0fff833c 100644 --- a/input.c +++ b/input.c @@ -1,6 +1,6 @@ /* input.c -- functions to perform buffered input with synchronization. */ -/* Copyright (C) 1992-2023 Free Software Foundation, Inc. +/* Copyright (C) 1992-2026 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -114,6 +114,11 @@ stream_getc (FILE *stream) } else if (interrupt_state || terminating_signal) /* QUIT; */ local_index = local_bufused = 0; + else if (input_timeout_seen) /* CHECK_INPUT_TIMEOUT; */ + { + local_index = local_bufused = 0; + return EOF; + } } local_index = 0; } diff --git a/parse.y b/parse.y index cc3e55fe..e094bb6e 100644 --- a/parse.y +++ b/parse.y @@ -1844,12 +1844,14 @@ yy_stream_get (void) int result; result = EOF; + CHECK_INPUT_TIMEOUT; if (bash_input.location.file) { /* XXX - don't need terminate_immediately; stream_getc checks for terminating signals itself if read returns < 0 */ result = stream_getc (bash_input.location.file); } + CHECK_INPUT_TIMEOUT; return (result); } @@ -6024,7 +6026,7 @@ reset_readline_prompt (void) semi-colons. When concatenating multiple lines of history, the newline separator for such tokens is replaced with a space. */ static const int no_semi_successors[] = { - '\n', '{', '(', ')', ';', '&', '|', + '\n', '{', '(', ')', ';', '&', '|', BAR_AND, CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN, DOLPAREN, DOLBRACE, 0 diff --git a/quit.h b/quit.h index 318d6686..0a3ab996 100644 --- a/quit.h +++ b/quit.h @@ -88,4 +88,10 @@ do { \ if (interrupt_state) zreset (); \ } while (0) +#define CHECK_INPUT_TIMEOUT \ +do { \ + if (input_timeout_seen) \ + input_timeout_hook (); \ +} while (0) + #endif /* _QUIT_H_ */ diff --git a/shell.h b/shell.h index 445a9f61..59a3555e 100644 --- a/shell.h +++ b/shell.h @@ -1,6 +1,6 @@ /* shell.h -- The data structures used by the shell */ -/* Copyright (C) 1993-2024 Free Software Foundation, Inc. +/* Copyright (C) 1993-2026 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -114,6 +114,8 @@ extern int indirection_level; extern int shell_compatibility_level; extern const int default_compatibility_level; extern int running_under_emacs; +extern int input_timeout_set; +extern volatile sig_atomic_t input_timeout_seen; extern int pretty_print_mode; diff --git a/tests/assoc.right b/tests/assoc.right index f0c3df7f..234b37a4 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -430,3 +430,4 @@ declare -A aa=([/homes/cj/key]="/homes/cj/Desktop" ) declare -A aa=([/homes/cj/Documents]="/homes/cj/Library" [/homes/cj/key]="/homes/cj/Desktop" ) declare -A aa=([/homes/cj/Documents]="/homes/cj/Library" [/homes/cj/key]="/homes/cj/Desktop:/homes/cj/Documents:/homes/cj/Applications" ) declare -A aa=([/homes/cj/Documents]="/homes/cj/Library" [/homes/cj/key]="/homes/cj/Desktop:/homes/cj/Documents:/homes/cj/Applications" ) + assoc20.sub diff --git a/tests/assoc.tests b/tests/assoc.tests index 1e38b9a4..84f84b7b 100644 --- a/tests/assoc.tests +++ b/tests/assoc.tests @@ -275,3 +275,6 @@ test_runsub ./assoc18.sub # tests with tilde expansion in keys and values post-bash-5.2 test_runsub ./assoc19.sub + +# new bash-5.4 kv-pair compound assignment word splitting behavior +test_runsub ./assoc20.sub diff --git a/tests/assoc20.sub b/tests/assoc20.sub new file mode 100644 index 00000000..ca3d28f6 --- /dev/null +++ b/tests/assoc20.sub @@ -0,0 +1,129 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . +# +# Originally by Zachary Santer +# Modified by Chet Ramey for the Bash test suite +# + +: ${THIS_SH:=./bash} + +set -o nounset -o noglob +o braceexpand +shopt -s lastpipe +unset CDPATH TMOUT +declare -r -x LC_ALL='C' + +main () { + + local declaration + if (( ${#} == 0 )) then + declaration='declare -A assoc + ' + else + declaration="${1} -A " + fi + + while (( test_count < 512 )); do + (( ++test_count )) + + # Create an associative array compound assignment containing random-length + # quoted arrays, unquoted scalars, and literals + + local -i array_count=0 + local -i scalar_count=0 + + local script="\ +set -o nounset -o noglob +o braceexpand +shopt -s lastpipe +unset CDPATH TMOUT +declare -r -x LC_ALL='C' + +main () { + +" + + local test_case='' + + local assignment_contents='' + + while (( ( SRANDOM & 7 ) > 0 )) do + + local -i type_selector="$(( SRANDOM & 32767 ))" + if (( type_selector < 10923 )) then + assignment_contents+=" '${SRANDOM//[01]/& }'" + else + local -a list=() + local -i element_count + for (( element_count = SRANDOM & 7; element_count > 0; --element_count )) do + list+=( "${SRANDOM}" ) + done + if (( type_selector < 21845 )) then + local array_name="array_$(( array_count++ ))" + list=( "${list[@]//[01]/& }" ) + test_case+=" local -a ${array_name}=( ${list[*]@Q} )"$'\n' + assignment_contents+=" \"\${${array_name}[@]}\"" + else + local scalar_name="scalar_$(( scalar_count++ ))" + test_case+=" local ${scalar_name}='${list[*]}'"$'\n' + assignment_contents+=" \${${scalar_name}}" + fi + fi + + done + + test_case+="\ + + ${declaration}assoc=(${assignment_contents} ) + set --${assignment_contents}" + + script+="${test_case}"' + + breakage="false" + if (( ( ( ${#} + 1 ) / 2 ) != ${#assoc[@]} )) then + breakage="true" + fi + while [[ ${breakage} == "false" && ${#} -gt 1 ]] do + if [[ ${assoc[${1}]} != "${2}" ]] then + breakage="true" + fi + shift 2 + done + if [[ ${breakage} == "false" && ${#} -eq 1 && -n ${assoc[${1}]} ]] then + breakage="true" + fi + [[ ${breakage} == "false" ]] + exit + +} + +main +' + + if ! ${THIS_SH} -c "${script}"; then + (( ++error_count )) + printf '========\n' + printf '%s\n' "${test_case}" + printf '========\n' + fi + + done + +} + +declare -i test_count=0 +declare -i error_count=0 + + +main # local +main export + +exit ${error_count}