diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index a0c7cb26..54b55a50 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -5661,3 +5661,49 @@ doc/{bash.1,bashref.texi} doc/bash.1,lib/readline/doc/rluser.texi - shell-expand-line: enumerate the specific expansions performed. From a request by Alex Bochannek + +findcmd.c + - path_value: new function, returns normalized version of $PATH. Takes + a single argument saying whether or not to look in the temporary + environment first. Normalizes null $PATH (PATH=) into ".". From a + bug-bash discussion started by Moshe Looks + - _find_user_command_in_path: use path_value() + - user_command_matches: use path_value() + - search_for_command: if PATH[0] == 0, set PATH = "." like path_value(). + This has the side effect of calling command_not_found_handle when + PATH is the empty string and the command isn't in the current + directory + - search_for_command: if path == 0, but we found an executable in the + current directory, make sure to set dot_found_in_search before adding + it to the command hash table + +findcmd.h + - path_value: extern declaration + +bashline.c + - command_word_completion_function: use path_value() + +builtins/enable.def + - dyn_load_builtin: use path_value for BASH_LOADABLES_PATH; it's cleaner + though it doesn't change the behavior + + 3/17 + ---- +bashline.c + - bashline_reset: reset rl_filename_quoting_function, since there is a + SIGINT code path where it could remain reset by glob completion. + Reported by Grisha Levit + - command_word_completion_function,command_subst_completion_function, + glob_complete_word: set some static variables to NULL + after freeing them so they don't potentially get freed twice after + a SIGINT during completion + Reported by Grisha Levit + +parse.y + - read_token_word: when deciding how to quote translated strings, don't + free ttok before comparing it to ttrans + Reported by Grisha Levit + - parse_matched_pair: same thing for freeing nestret + - read_token: if we return a newline, make sure we call set_word_top() + if necessary. + Fixes underflow reported by Grisha Levit diff --git a/bashline.c b/bashline.c index 2745c4dd..0047caef 100644 --- a/bashline.c +++ b/bashline.c @@ -686,6 +686,8 @@ bashline_reset (void) rl_filename_quote_characters = default_filename_quote_characters; set_filename_bstab (rl_filename_quote_characters); + rl_filename_quoting_function = bash_quote_filename; + set_directory_hook (); rl_filename_stat_hook = bash_filename_stat_hook; @@ -1922,12 +1924,8 @@ executable_completion (const char *filename, int searching_path) /* This gets an unquoted filename, so we need to quote special characters in the filename before the completion hook gets it. */ -#if 0 - f = savestring (filename); -#else c = 0; f = bash_quote_filename ((char *)filename, SINGLE_MATCH, &c); -#endif bash_directory_completion_hook (&f); r = searching_path ? executable_file (f) : executable_or_directory (f); @@ -1970,6 +1968,7 @@ command_word_completion_function (const char *hint_text, int state) free (dequoted_hint); if (hint) free (hint); + dequoted_hint = hint = (char *)NULL; mapping_over = searching_path = 0; hint_is_dir = CMD_IS_DIR (hint_text); @@ -2064,7 +2063,7 @@ command_word_completion_function (const char *hint_text, int state) if (rl_completion_found_quote && rl_completion_quote_character == 0) dequoted_hint = bash_dequote_filename (hint, 0); - path = get_string_value ("PATH"); + path = path_value ("PATH", 0); path_index = dot_in_path = 0; /* Initialize the variables for each type of command word. */ @@ -2252,6 +2251,7 @@ globword: free (fnhint); if (filename_hint) free (filename_hint); + fnhint = filename_hint = (char *)NULL; filename_hint = sh_makepath (current_path, hint, 0); /* Need a quoted version (though it doesn't matter much in most @@ -2347,6 +2347,7 @@ globword: t1 = make_absolute (val, t); free (t); cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); + free (t1); } else #endif @@ -2397,7 +2398,10 @@ command_subst_completion_function (const char *text, int state) start_len = text - orig_start; filename_text = savestring (text); if (matches) - free (matches); + { + free (matches); + matches = (char **)NULL; + } /* * At this point we can entertain the idea of re-parsing @@ -3873,9 +3877,11 @@ glob_complete_word (const char *text, int state) { rl_filename_completion_desired = 1; FREE (matches); + matches = (char **)NULL; if (globorig != globtext) FREE (globorig); FREE (globtext); + globorig = globtext = (char *)NULL; ttext = bash_tilde_expand (text, 0); diff --git a/builtins/cd.def b/builtins/cd.def index 08f18da9..35d95f67 100644 --- a/builtins/cd.def +++ b/builtins/cd.def @@ -1,7 +1,7 @@ This file is cd.def, from which is created cd.c. It implements the builtins "cd" and "pwd" in Bash. -Copyright (C) 1987-2022 Free Software Foundation, Inc. +Copyright (C) 1987-2023 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -390,20 +390,6 @@ cd_builtin (WORD_LIST *list) else free (temp); } - -#if 0 - /* changed for bash-4.2 Posix cd description steps 5-6 */ - /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't - try the current directory, so we just punt now with an error - message if POSIXLY_CORRECT is non-zero. The check for cdpath[0] - is so we don't mistakenly treat a CDPATH value of "" as not - specifying the current directory. */ - if (posixly_correct && cdpath[0]) - { - builtin_error ("%s: %s", dirname, strerror (ENOENT)); - return (EXECUTION_FAILURE); - } -#endif } else dirname = list->word->word; diff --git a/builtins/enable.def b/builtins/enable.def index b6d6a8fe..e6948bac 100644 --- a/builtins/enable.def +++ b/builtins/enable.def @@ -336,7 +336,7 @@ dyn_load_builtin (WORD_LIST *list, int flags, char *filename) handle = 0; if (absolute_program (filename) == 0) { - loadables_path = get_string_value ("BASH_LOADABLES_PATH"); + loadables_path = path_value ("BASH_LOADABLES_PATH", 1); if (loadables_path) { load_path = find_in_path (filename, loadables_path, FS_NODIRS|FS_EXEC_PREFERRED); diff --git a/config-top.h b/config-top.h index 5a322706..6e87a7ce 100644 --- a/config-top.h +++ b/config-top.h @@ -139,6 +139,7 @@ /* Define as 1 if you want to enable code that implements multiple coprocs executing simultaneously */ +/* TAG: bash-5.3 */ #ifndef MULTIPLE_COPROCS # define MULTIPLE_COPROCS 0 #endif diff --git a/findcmd.c b/findcmd.c index 186871ac..bbb8ae92 100644 --- a/findcmd.c +++ b/findcmd.c @@ -248,6 +248,23 @@ find_path_file (const char *name) return (find_user_command_internal (name, FS_READABLE)); } +/* Get $PATH and normalize it. USE_TEMPENV, if non-zero, says to look in the + temporary environment first. Normalizing means converting PATH= into ".". */ +char * +path_value (const char *pathvar, int use_tempenv) +{ + SHELL_VAR *var; + char *path; + + var = use_tempenv ? find_variable_tempenv (pathvar) : find_variable (pathvar); + path = var ? value_cell (var) : (char *)NULL; + + if (path == 0 || *path) + return (path); + else /* *path == '\0' */ + return "."; +} + static char * _find_user_command_internal (const char *name, int flags) { @@ -256,12 +273,9 @@ _find_user_command_internal (const char *name, int flags) /* Search for the value of PATH in both the temporary environments and in the regular list of variables. */ - if (var = find_variable_tempenv ("PATH")) /* XXX could be array? */ - path_list = value_cell (var); - else - path_list = (char *)NULL; + path_list = path_value ("PATH", 1); - if (path_list == 0 || *path_list == '\0') + if (path_list == 0) return (savestring (name)); cmd = find_user_command_in_path (name, path_list, flags, (int *)0); @@ -364,7 +378,11 @@ search_for_command (const char *pathname, int flags) if (flags & CMDSRCH_STDPATH) path_list = conf_standard_path (); else if (temp_path || path) - path_list = value_cell (path); + { + path_list = value_cell (path); + if (path_list && *path_list == '\0') + path_list = "."; + } else path_list = 0; @@ -377,6 +395,11 @@ search_for_command (const char *pathname, int flags) table unless it's an executable file in the current directory. */ if (STREQ (command, pathname)) { + if (path_list == 0) + { + dot_found_in_search = 1; + st = file_status (pathname); + } if (st & FS_EXECABLE) phash_insert ((char *)pathname, command, dot_found_in_search, 1); } @@ -439,8 +462,8 @@ user_command_matches (const char *name, int flags, int state) dot_found_in_search = 0; if (stat (".", &dotinfo) < 0) dotinfo.st_dev = dotinfo.st_ino = 0; /* so same_file won't match */ - path_list = get_string_value ("PATH"); - path_index = 0; + path_list = path_value ("PATH", 0); + path_index = 0; } while (path_list && path_list[path_index]) @@ -573,7 +596,9 @@ find_in_path_element (const char *name, char *path, int flags, size_t name_len, /* This does the dirty work for find_user_command_internal () and user_command_matches (). NAME is the name of the file to search for. - PATH_LIST is a colon separated list of directories to search. + PATH_LIST is a colon separated list of directories to search. It is the + caller's responsibility to pass a non-empty path if they want an empty + path to be treated specially. FLAGS contains bit fields which control the files which are eligible. Some values are: FS_EXEC_ONLY: The file must be an executable to be found. diff --git a/findcmd.h b/findcmd.h index b0d80d11..6dbded4c 100644 --- a/findcmd.h +++ b/findcmd.h @@ -35,6 +35,7 @@ extern int executable_or_directory (const char *); extern char *find_user_command (const char *); extern char *find_in_path (const char *, char *, int); extern char *find_path_file (const char *); +extern char *path_value (const char *, int); extern char *search_for_command (const char *, int); extern char *user_command_matches (const char *, int, int); extern void setup_exec_ignore (const char *); diff --git a/parse.y b/parse.y index e3516e2d..33a4ddfd 100644 --- a/parse.y +++ b/parse.y @@ -3470,6 +3470,9 @@ read_token (int command) parser_state &= ~PST_ASSIGNOK; parser_state &= ~PST_CMDBLTIN; + if (last_read_token == IF || last_read_token == WHILE || last_read_token == UNTIL) + set_word_top (last_read_token); + return (character); } @@ -3906,7 +3909,6 @@ parse_matched_pair (int qc, int open, int close, size_t *lenp, int flags) /* Locale expand $"..." here. */ /* PST_NOEXPAND */ ttrans = locale_expand (nestret, 0, nestlen - 1, start_lineno, &ttranslen); - free (nestret); /* If we're supposed to single-quote translated strings, check whether the translated result is different from @@ -3914,6 +3916,7 @@ parse_matched_pair (int qc, int open, int close, size_t *lenp, int flags) if (singlequote_translations && ((nestlen - 1) != ttranslen || STREQN (nestret, ttrans, ttranslen) == 0)) { + free (nestret); if ((rflags & P_DQUOTE) == 0) nestret = sh_single_quote (ttrans); else if ((rflags & P_DQUOTE) && (dolbrace_state == DOLBRACE_QUOTE2) && (flags & P_DOLBRACE)) @@ -3923,7 +3926,10 @@ parse_matched_pair (int qc, int open, int close, size_t *lenp, int flags) nestret = sh_backslash_quote_for_double_quotes (ttrans, 0); } else - nestret = sh_mkdoublequoted (ttrans, ttranslen, 0); + { + free (nestret); + nestret = sh_mkdoublequoted (ttrans, ttranslen, 0); + } free (ttrans); nestlen = strlen (nestret); retind -= 2; /* back up before the $" */ @@ -5228,15 +5234,20 @@ read_token_word (int character) /* PST_NOEXPAND */ /* Try to locale-expand the converted string. */ ttrans = locale_expand (ttok, 0, ttoklen - 1, first_line, &ttranslen); - free (ttok); /* Add the double quotes back (or single quotes if the user has set that option). */ if (singlequote_translations && ((ttoklen - 1) != ttranslen || STREQN (ttok, ttrans, ttranslen) == 0)) - ttok = sh_single_quote (ttrans); + { + free (ttok); + ttok = sh_single_quote (ttrans); + } else - ttok = sh_mkdoublequoted (ttrans, ttranslen, 0); + { + free (ttok); + ttok = sh_mkdoublequoted (ttrans, ttranslen, 0); + } free (ttrans); ttrans = ttok; diff --git a/tests/exec.right b/tests/exec.right index f7e8a7fb..09204f1b 100644 --- a/tests/exec.right +++ b/tests/exec.right @@ -31,11 +31,17 @@ trap -- '' SIGTERM trap -- 'echo USR1' SIGUSR1 USR1 EXIT -./execscript: line 71: notthere: No such file or directory +./execscript: line 71: notthere: command not found 127 -./execscript: line 74: notthere: No such file or directory +./execscript: line 73: notthere: command not found 127 -./execscript: line 77: notthere: command not found +./execscript: line 75: notthere: command not found +127 +./execscript: line 81: notthere: command not found +127 +./execscript: line 83: notthere: command not found +127 +./execscript: line 85: notthere: command not found 127 this is sh this is sh diff --git a/tests/execscript b/tests/execscript index 706c1e13..f8ecf1ed 100644 --- a/tests/execscript +++ b/tests/execscript @@ -65,15 +65,23 @@ ${THIS_SH} ./exec3.sub rm -f $TMPDIR/bashenv unset BASH_ENV +# these results should be the same as with an empty PATH +PATH=. + +notthere +echo $? +command notthere +echo $? +command -p notthere +echo $? + # we're resetting the $PATH to empty, so this should be last PATH= notthere echo $? - command notthere echo $? - command -p notthere echo $?