Submitted By: Pierre Labastie Date: 2019-12-01 Initial Package Version: 5.0 Upstream Status: Already in upstream patch repo Origin: Upstream Description: This patch contains upstream patch numbers 001 thru 011 diff -Naur bash-5.0/bashhist.c bash-5.0.11/bashhist.c --- bash-5.0/bashhist.c 2018-07-06 04:41:14.000000000 +0200 +++ bash-5.0.11/bashhist.c 2019-11-28 16:11:49.667000000 +0100 @@ -560,15 +560,18 @@ add that line to the history if ADDIT is non-zero. */ if (!history_expansion_inhibited && history_expansion && history_expansion_p (line)) { + int old_len; + /* If we are expanding the second or later line of a multi-line command, decrease history_length so references to history expansions in these lines refer to the previous history entry and not the current command. */ + old_len = history_length; if (history_length > 0 && command_oriented_history && current_command_first_line_saved && current_command_line_count > 1) history_length--; expanded = history_expand (line, &history_value); if (history_length >= 0 && command_oriented_history && current_command_first_line_saved && current_command_line_count > 1) - history_length++; + history_length = old_len; if (expanded) { diff -Naur bash-5.0/bashline.c bash-5.0.11/bashline.c --- bash-5.0/bashline.c 2018-11-27 19:20:16.000000000 +0100 +++ bash-5.0.11/bashline.c 2019-11-28 16:11:33.994000000 +0100 @@ -231,6 +231,7 @@ static int bash_complete_command __P((int, int)); static int bash_possible_command_completions __P((int, int)); +static int completion_glob_pattern __P((char *)); static char *glob_complete_word __P((const char *, int)); static int bash_glob_completion_internal __P((int)); static int bash_glob_complete_word __P((int, int)); @@ -1741,7 +1742,7 @@ /* This could be a globbing pattern, so try to expand it using pathname expansion. */ - if (!matches && glob_pattern_p (text)) + if (!matches && completion_glob_pattern ((char *)text)) { matches = rl_completion_matches (text, glob_complete_word); /* A glob expression that matches more than one filename is problematic. @@ -1850,7 +1851,7 @@ glob_matches = (char **)NULL; } - globpat = glob_pattern_p (hint_text); + globpat = completion_glob_pattern ((char *)hint_text); /* If this is an absolute program name, do not check it against aliases, reserved words, functions or builtins. We must check @@ -3713,6 +3714,61 @@ return bash_specific_completion (what_to_do, command_word_completion_function); } +static int +completion_glob_pattern (string) + char *string; +{ + register int c; + char *send; + int open; + + DECLARE_MBSTATE; + + open = 0; + send = string + strlen (string); + + while (c = *string++) + { + switch (c) + { + case '?': + case '*': + return (1); + + case '[': + open++; + continue; + + case ']': + if (open) + return (1); + continue; + + case '+': + case '@': + case '!': + if (*string == '(') /*)*/ + return (1); + continue; + + case '\\': + if (*string++ == 0) + return (0); + } + + /* Advance one fewer byte than an entire multibyte character to + account for the auto-increment in the loop above. */ +#ifdef HANDLE_MULTIBYTE + string--; + ADVANCE_CHAR_P (string, send - string); + string++; +#else + ADVANCE_CHAR_P (string, send - string); +#endif + } + return (0); +} + static char *globtext; static char *globorig; @@ -3877,7 +3933,7 @@ t = substring (rl_line_buffer, p, rl_point); } - if (t && glob_pattern_p (t) == 0) + if (t && completion_glob_pattern (t) == 0) rl_explicit_arg = 1; /* XXX - force glob_complete_word to append `*' */ FREE (t); diff -Naur bash-5.0/builtins/evalstring.c bash-5.0.11/builtins/evalstring.c --- bash-5.0/builtins/evalstring.c 2018-12-26 17:19:21.000000000 +0100 +++ bash-5.0.11/builtins/evalstring.c 2019-11-28 16:11:40.813000000 +0100 @@ -100,12 +100,22 @@ ((command->flags & CMD_INVERT_RETURN) == 0)); } +int +can_optimize_connection (command) + COMMAND *command; +{ + return (*bash_input.location.string == '\0' && + (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') && + command->value.Connection->second->type == cm_simple); +} + void optimize_fork (command) COMMAND *command; { if (command->type == cm_connection && - (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR) && + (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') && + (command->value.Connection->second->flags & CMD_TRY_OPTIMIZING) && should_suppress_fork (command->value.Connection->second)) { command->value.Connection->second->flags |= CMD_NO_FORK; @@ -412,8 +422,18 @@ command->flags |= CMD_NO_FORK; command->value.Simple->flags |= CMD_NO_FORK; } - else if (command->type == cm_connection) - optimize_fork (command); + + /* Can't optimize forks out here execept for simple commands. + This knows that the parser sets up commands as left-side heavy + (&& and || are left-associative) and after the single parse, + if we are at the end of the command string, the last in a + series of connection commands is + command->value.Connection->second. */ + else if (command->type == cm_connection && can_optimize_connection (command)) + { + command->value.Connection->second->flags |= CMD_TRY_OPTIMIZING; + command->value.Connection->second->value.Simple->flags |= CMD_TRY_OPTIMIZING; + } #endif /* ONESHOT */ /* See if this is a candidate for $( value.Connection->connector == OR_OR) && (exec_result != EXECUTION_SUCCESS))) { + optimize_fork (command); + second = command->value.Connection->second; if (ignore_return && second) second->flags |= CMD_IGNORE_RETURN; diff -Naur bash-5.0/jobs.c bash-5.0.11/jobs.c --- bash-5.0/jobs.c 2018-12-06 17:44:34.000000000 +0100 +++ bash-5.0.11/jobs.c 2019-11-28 16:11:46.415000000 +0100 @@ -2488,10 +2488,8 @@ r = wait_for (last_procsub_child->pid); wait_procsubs (); reap_procsubs (); -#if 1 +#if 0 /* We don't want to wait indefinitely if we have stopped children. */ - /* XXX - should add a loop that goes through the list of process - substitutions and waits for each proc in turn before this code. */ if (any_stopped == 0) { /* Check whether or not we have any unreaped children. */ @@ -4839,15 +4837,13 @@ end_job_control () { if (job_control) - { - terminate_stopped_jobs (); + terminate_stopped_jobs (); - if (original_pgrp >= 0) - give_terminal_to (original_pgrp, 1); - } + if (original_pgrp >= 0 && terminal_pgrp != original_pgrp) + give_terminal_to (original_pgrp, 1); - if (original_pgrp >= 0) - setpgid (0, original_pgrp); + if (original_pgrp >= 0 && setpgid (0, original_pgrp) == 0) + shell_pgrp = original_pgrp; } /* Restart job control by closing shell tty and reinitializing. This is diff -Naur bash-5.0/lib/glob/glob.c bash-5.0.11/lib/glob/glob.c --- bash-5.0/lib/glob/glob.c 2018-09-20 16:53:23.000000000 +0200 +++ bash-5.0.11/lib/glob/glob.c 2019-11-28 16:11:33.990000000 +0100 @@ -1061,7 +1061,7 @@ char *directory_name, *filename, *dname, *fn; unsigned int directory_len; int free_dirname; /* flag */ - int dflags; + int dflags, hasglob; result = (char **) malloc (sizeof (char *)); result_size = 1; @@ -1110,9 +1110,12 @@ free_dirname = 1; } + hasglob = 0; /* If directory_name contains globbing characters, then we - have to expand the previous levels. Just recurse. */ - if (directory_len > 0 && glob_pattern_p (directory_name)) + have to expand the previous levels. Just recurse. + If glob_pattern_p returns != [0,1] we have a pattern that has backslash + quotes but no unquoted glob pattern characters. We dequote it below. */ + if (directory_len > 0 && (hasglob = glob_pattern_p (directory_name)) == 1) { char **directories, *d, *p; register unsigned int i; @@ -1175,7 +1178,7 @@ if (d[directory_len - 1] == '/') d[directory_len - 1] = '\0'; - directories = glob_filename (d, dflags); + directories = glob_filename (d, dflags|GX_RECURSE); if (free_dirname) { @@ -1332,6 +1335,20 @@ free (directory_name); return (NULL); } + /* If we have a directory name with quoted characters, and we are + being called recursively to glob the directory portion of a pathname, + we need to dequote the directory name before returning it so the + caller can read the directory */ + if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) != 0) + { + dequote_pathname (directory_name); + directory_len = strlen (directory_name); + } + + /* We could check whether or not the dequoted directory_name is a + directory and return it here, returning the original directory_name + if not, but we don't do that yet. I'm not sure it matters. */ + /* Handle GX_MARKDIRS here. */ result[0] = (char *) malloc (directory_len + 1); if (result[0] == NULL) diff -Naur bash-5.0/lib/glob/glob.h bash-5.0.11/lib/glob/glob.h --- bash-5.0/lib/glob/glob.h 2013-10-28 19:46:12.000000000 +0100 +++ bash-5.0.11/lib/glob/glob.h 2019-11-28 16:11:33.989000000 +0100 @@ -30,6 +30,7 @@ #define GX_NULLDIR 0x100 /* internal -- no directory preceding pattern */ #define GX_ADDCURDIR 0x200 /* internal -- add passed directory name */ #define GX_GLOBSTAR 0x400 /* turn on special handling of ** */ +#define GX_RECURSE 0x800 /* internal -- glob_filename called recursively */ extern int glob_pattern_p __P((const char *)); extern char **glob_vector __P((char *, char *, int)); diff -Naur bash-5.0/lib/glob/glob_loop.c bash-5.0.11/lib/glob/glob_loop.c --- bash-5.0/lib/glob/glob_loop.c 2018-12-31 19:35:15.000000000 +0100 +++ bash-5.0.11/lib/glob/glob_loop.c 2019-11-28 16:11:33.987000000 +0100 @@ -26,10 +26,10 @@ { register const GCHAR *p; register GCHAR c; - int bopen; + int bopen, bsquote; p = pattern; - bopen = 0; + bopen = bsquote = 0; while ((c = *p++) != L('\0')) switch (c) @@ -54,20 +54,23 @@ continue; case L('\\'): -#if 0 /* Don't let the pattern end in a backslash (GMATCH returns no match - if the pattern ends in a backslash anyway), but otherwise return 1, - since the matching engine uses backslash as an escape character - and it can be removed. */ - return (*p != L('\0')); -#else - /* The pattern may not end with a backslash. */ - if (*p++ == L('\0')) + if the pattern ends in a backslash anyway), but otherwise note that + we have seen this, since the matching engine uses backslash as an + escape character and it can be removed. We return 2 later if we + have seen only backslash-escaped characters, so interested callers + know they can shortcut and just dequote the pathname. */ + if (*p != L('\0')) + { + p++; + bsquote = 1; + continue; + } + else /* (*p == L('\0')) */ return 0; -#endif } - return 0; + return bsquote ? 2 : 0; } #undef INTERNAL_GLOB_PATTERN_P diff -Naur bash-5.0/lib/readline/histfile.c bash-5.0.11/lib/readline/histfile.c --- bash-5.0/lib/readline/histfile.c 2018-06-11 15:14:52.000000000 +0200 +++ bash-5.0.11/lib/readline/histfile.c 2019-11-28 16:11:52.648000000 +0100 @@ -305,6 +305,7 @@ if (file_size == 0) { free (input); + close (file); return 0; /* don't waste time if we don't have to */ } diff -Naur bash-5.0/parser.h bash-5.0.11/parser.h --- bash-5.0/parser.h 2018-12-29 01:11:18.000000000 +0100 +++ bash-5.0.11/parser.h 2019-11-28 16:11:29.603000000 +0100 @@ -47,6 +47,7 @@ #define PST_REPARSE 0x040000 /* re-parsing in parse_string_to_word_list */ #define PST_REDIRLIST 0x080000 /* parsing a list of redirections preceding a simple command name */ #define PST_COMMENT 0x100000 /* parsing a shell comment; used by aliases */ +#define PST_ENDALIAS 0x200000 /* just finished expanding and consuming an alias */ /* Definition of the delimiter stack. Needed by parse.y and bashhist.c. */ struct dstack { diff -Naur bash-5.0/parse.y bash-5.0.11/parse.y --- bash-5.0/parse.y 2019-01-02 19:57:34.000000000 +0100 +++ bash-5.0.11/parse.y 2019-11-28 16:11:29.607000000 +0100 @@ -2557,12 +2557,14 @@ if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE && pushed_string_list->flags != PSH_DPAREN && (parser_state & PST_COMMENT) == 0 && + (parser_state & PST_ENDALIAS) == 0 && /* only once */ shell_input_line_index > 0 && - shell_input_line[shell_input_line_index-1] != ' ' && + shellblank (shell_input_line[shell_input_line_index-1]) == 0 && shell_input_line[shell_input_line_index-1] != '\n' && shellmeta (shell_input_line[shell_input_line_index-1]) == 0 && (current_delimiter (dstack) != '\'' && current_delimiter (dstack) != '"')) { + parser_state |= PST_ENDALIAS; return ' '; /* END_ALIAS */ } #endif @@ -2571,6 +2573,7 @@ /* This case works for PSH_DPAREN as well */ if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE) { + parser_state &= ~PST_ENDALIAS; pop_string (); uc = shell_input_line[shell_input_line_index]; if (uc) diff -Naur bash-5.0/patchlevel.h bash-5.0.11/patchlevel.h --- bash-5.0/patchlevel.h 2018-02-21 21:47:15.000000000 +0100 +++ bash-5.0.11/patchlevel.h 2019-11-28 16:11:58.589000000 +0100 @@ -25,6 +25,6 @@ regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh looks for to find the patch level (for the sccs version string). */ -#define PATCHLEVEL 0 +#define PATCHLEVEL 11 #endif /* _PATCHLEVEL_H_ */ diff -Naur bash-5.0/pathexp.c bash-5.0.11/pathexp.c --- bash-5.0/pathexp.c 2018-04-29 23:44:48.000000000 +0200 +++ bash-5.0.11/pathexp.c 2019-11-28 16:11:33.991000000 +0100 @@ -65,11 +65,11 @@ { register int c; char *send; - int open; + int open, bsquote; DECLARE_MBSTATE; - open = 0; + open = bsquote = 0; send = string + strlen (string); while (c = *string++) @@ -100,7 +100,14 @@ can be removed by the matching engine, so we have to run it through globbing. */ case '\\': - return (*string != 0); + if (*string != '\0' && *string != '/') + { + bsquote = 1; + string++; + continue; + } + else if (*string == 0) + return (0); case CTLESC: if (*string++ == '\0') @@ -117,7 +124,8 @@ ADVANCE_CHAR_P (string, send - string); #endif } - return (0); + + return (bsquote ? 2 : 0); } /* Return 1 if C is a character that is `special' in a POSIX ERE and needs to diff -Naur bash-5.0/subst.c bash-5.0.11/subst.c --- bash-5.0/subst.c 2018-12-22 23:43:37.000000000 +0100 +++ bash-5.0.11/subst.c 2019-11-28 16:11:58.588000000 +0100 @@ -3625,7 +3625,9 @@ this case, we quote the string specially for the globbing code. If SPECIAL is 2, this is an rhs argument for the =~ operator, and should be quoted appropriately for regcomp/regexec. The caller is responsible - for removing the backslashes if the unquoted word is needed later. */ + for removing the backslashes if the unquoted word is needed later. In + any case, since we don't perform word splitting, we need to do quoted + null character removal. */ char * cond_expand_word (w, special) WORD_DESC *w; @@ -3646,6 +3648,8 @@ { if (special == 0) /* LHS */ { + if (l->word) + word_list_remove_quoted_nulls (l); dequote_list (l); r = string_list (l); } diff -Naur bash-5.0/tests/varenv.right bash-5.0.11/tests/varenv.right --- bash-5.0/tests/varenv.right 2018-12-17 21:39:48.000000000 +0100 +++ bash-5.0.11/tests/varenv.right 2019-11-28 16:11:55.854000000 +0100 @@ -146,9 +146,9 @@ inside: declare -x var="value" outside: declare -- var="one" inside: declare -x var="value" -outside: declare -x var="value" -inside: declare -- var="local" -outside: declare -x var="global" +outside: declare -- var="outside" +inside: declare -x var="global" +outside: declare -- var="outside" foo= environment foo= foo=foo environment foo=foo foo=foo environment foo=foo diff -Naur bash-5.0/variables.c bash-5.0.11/variables.c --- bash-5.0/variables.c 2018-12-18 17:07:21.000000000 +0100 +++ bash-5.0.11/variables.c 2019-11-28 16:11:55.853000000 +0100 @@ -4460,9 +4460,9 @@ int tvlist_ind; /* Take a variable from an assignment statement preceding a posix special - builtin (including `return') and create a global variable from it. This - is called from merge_temporary_env, which is only called when in posix - mode. */ + builtin (including `return') and create a variable from it as if a + standalone assignment statement had been performed. This is called from + merge_temporary_env, which is only called when in posix mode. */ static void push_posix_temp_var (data) PTR_T data; @@ -4472,16 +4472,27 @@ var = (SHELL_VAR *)data; - binding_table = global_variables->table; - if (binding_table == 0) - binding_table = global_variables->table = hash_create (VARIABLES_HASH_BUCKETS); - - v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, ASS_FORCE|ASS_NOLONGJMP); + /* Just like do_assignment_internal(). This makes assignments preceding + special builtins act like standalone assignment statements when in + posix mode, satisfying the posix requirement that this affect the + "current execution environment." */ + v = bind_variable (var->name, value_cell (var), ASS_FORCE|ASS_NOLONGJMP); + + /* If this modifies an existing local variable, v->context will be non-zero. + If it comes back with v->context == 0, we bound at the global context. + Set binding_table appropriately. It doesn't matter whether it's correct + if the variable is local, only that it's not global_variables->table */ + binding_table = v->context ? shell_variables->table : global_variables->table; /* global variables are no longer temporary and don't need propagating. */ - var->attributes &= ~(att_tempvar|att_propagate); + if (binding_table == global_variables->table) + var->attributes &= ~(att_tempvar|att_propagate); + if (v) - v->attributes |= var->attributes; + { + v->attributes |= var->attributes; + v->attributes &= ~att_tempvar; /* not a temp var now */ + } if (find_special_var (var->name) >= 0) tempvar_list[tvlist_ind++] = savestring (var->name); @@ -4575,14 +4586,17 @@ sh_free_func_t *pushf; { int i; + HASH_TABLE *disposer; tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1); tempvar_list[tvlist_ind = 0] = 0; - - hash_flush (temporary_env, pushf); - hash_dispose (temporary_env); + + disposer = temporary_env; temporary_env = (HASH_TABLE *)NULL; + hash_flush (disposer, pushf); + hash_dispose (disposer); + tempvar_list[tvlist_ind] = 0; array_needs_making = 1; diff -Naur bash-5.0/y.tab.c bash-5.0.11/y.tab.c --- bash-5.0/y.tab.c 2019-01-02 19:57:43.000000000 +0100 +++ bash-5.0.11/y.tab.c 2019-11-28 16:11:29.611000000 +0100 @@ -4873,12 +4873,14 @@ if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE && pushed_string_list->flags != PSH_DPAREN && (parser_state & PST_COMMENT) == 0 && + (parser_state & PST_ENDALIAS) == 0 && /* only once */ shell_input_line_index > 0 && - shell_input_line[shell_input_line_index-1] != ' ' && + shellblank (shell_input_line[shell_input_line_index-1]) == 0 && shell_input_line[shell_input_line_index-1] != '\n' && shellmeta (shell_input_line[shell_input_line_index-1]) == 0 && (current_delimiter (dstack) != '\'' && current_delimiter (dstack) != '"')) { + parser_state |= PST_ENDALIAS; return ' '; /* END_ALIAS */ } #endif @@ -4887,6 +4889,7 @@ /* This case works for PSH_DPAREN as well */ if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE) { + parser_state &= ~PST_ENDALIAS; pop_string (); uc = shell_input_line[shell_input_line_index]; if (uc)